diff --git a/Gopkg.lock b/Gopkg.lock index 730f74d467..c4ee14e827 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,6 +1,12 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. +[[projects]] + name = "cloud.google.com/go" + packages = ["compute/metadata"] + revision = "aad3f485ee528456e0768f20397b4d9dd941e755" + version = "v0.25.0" + [[projects]] branch = "master" name = "github.com/beorn7/perks" @@ -118,6 +124,18 @@ packages = ["."] revision = "da5fdee475fb09de87c5d4a50da233b7d28767b1" +[[projects]] + branch = "master" + name = "github.com/howeyc/gopass" + packages = ["."] + revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8" + +[[projects]] + name = "github.com/imdario/mergo" + packages = ["."] + revision = "9316a62528ac99aaecb4e47eadd6dc8aa6533d58" + version = "v0.3.5" + [[projects]] name = "github.com/json-iterator/go" packages = ["."] @@ -255,6 +273,7 @@ name = "golang.org/x/net" packages = [ "context", + "context/ctxhttp", "http2", "http2/hpack", "idna", @@ -264,6 +283,18 @@ ] revision = "c73622c77280266305273cb545f54516ced95b93" +[[projects]] + branch = "master" + name = "golang.org/x/oauth2" + packages = [ + ".", + "google", + "internal", + "jws", + "jwt" + ] + revision = "ef147856a6ddbb60760db74283d2424e98c87bff" + [[projects]] branch = "master" name = "golang.org/x/sys" @@ -300,6 +331,23 @@ packages = ["rate"] revision = "fbb02b2291d28baffd63558aa44b4b56f178d650" +[[projects]] + name = "google.golang.org/appengine" + packages = [ + ".", + "internal", + "internal/app_identity", + "internal/base", + "internal/datastore", + "internal/log", + "internal/modules", + "internal/remote_api", + "internal/urlfetch", + "urlfetch" + ] + revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" + version = "v1.1.0" + [[projects]] branch = "master" name = "google.golang.org/genproto" @@ -582,11 +630,17 @@ "pkg/apis/clientauthentication/v1alpha1", "pkg/version", "plugin/pkg/client/auth/exec", + "plugin/pkg/client/auth/gcp", "rest", "rest/watch", "testing", + "third_party/forked/golang/template", + "tools/auth", "tools/cache", + "tools/clientcmd", "tools/clientcmd/api", + "tools/clientcmd/api/latest", + "tools/clientcmd/api/v1", "tools/metrics", "tools/pager", "tools/record", @@ -595,7 +649,9 @@ "util/buffer", "util/cert", "util/flowcontrol", + "util/homedir", "util/integer", + "util/jsonpath", "util/retry", "util/workqueue" ] @@ -611,6 +667,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5f39542c1eb4cd19061ae02f9ab9edcec2fba90025d6bb435f9eb973d966261c" + inputs-digest = "e56feb85cb2a8dd31674bc6642ef125441c4de39dbf7455d6eaf0b5eb386329b" solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/cloud.google.com/go/AUTHORS b/vendor/cloud.google.com/go/AUTHORS new file mode 100644 index 0000000000..c364af1da0 --- /dev/null +++ b/vendor/cloud.google.com/go/AUTHORS @@ -0,0 +1,15 @@ +# This is the official list of cloud authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as: +# Name or Organization +# The email address is not required for organizations. + +Filippo Valsorda +Google Inc. +Ingo Oeser +Palm Stone Games, Inc. +Paweł Knap +Péter Szilágyi +Tyler Treat diff --git a/vendor/cloud.google.com/go/CODE_OF_CONDUCT.md b/vendor/cloud.google.com/go/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..8fd1bc9c22 --- /dev/null +++ b/vendor/cloud.google.com/go/CODE_OF_CONDUCT.md @@ -0,0 +1,44 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, +and in the interest of fostering an open and welcoming community, +we pledge to respect all people who contribute through reporting issues, +posting feature requests, updating documentation, +submitting pull requests or patches, and other activities. + +We are committed to making participation in this project +a harassment-free experience for everyone, +regardless of level of experience, gender, gender identity and expression, +sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, +such as physical or electronic +addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. +By adopting this Code of Conduct, +project maintainers commit themselves to fairly and consistently +applying these principles to every aspect of managing this project. +Project maintainers who do not follow or enforce the Code of Conduct +may be permanently removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior +may be reported by opening an issue +or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) + diff --git a/vendor/cloud.google.com/go/CONTRIBUTING.md b/vendor/cloud.google.com/go/CONTRIBUTING.md new file mode 100644 index 0000000000..9db3f83198 --- /dev/null +++ b/vendor/cloud.google.com/go/CONTRIBUTING.md @@ -0,0 +1,174 @@ +# Contributing + +1. Sign one of the contributor license agreements below. +1. `go get golang.org/x/review/git-codereview` to install the code reviewing tool. + 1. You will need to ensure that your `GOBIN` directory (by default + `$GOPATH/bin`) is in your `PATH` so that git can find the command. + 1. If you would like, you may want to set up aliases for git-codereview, + such that `git codereview change` becomes `git change`. See the + [godoc](https://godoc.org/golang.org/x/review/git-codereview) for details. + 1. Should you run into issues with the git-codereview tool, please note + that all error messages will assume that you have set up these + aliases. +1. Get the cloud package by running `go get -d cloud.google.com/go`. + 1. If you have already checked out the source, make sure that the remote git + origin is https://code.googlesource.com/gocloud: + + git remote set-url origin https://code.googlesource.com/gocloud +1. Make sure your auth is configured correctly by visiting + https://code.googlesource.com, clicking "Generate Password", and following + the directions. +1. Make changes and create a change by running `git codereview change `, +provide a commit message, and use `git codereview mail` to create a Gerrit CL. +1. Keep amending to the change with `git codereview change` and mail as your receive +feedback. Each new mailed amendment will create a new patch set for your change in Gerrit. + +## Integration Tests + +In addition to the unit tests, you may run the integration test suite. + +To run the integrations tests, creating and configuration of a project in the +Google Developers Console is required. + +After creating a project, you must [create a service account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount). +Ensure the project-level **Owner** +[IAM role](console.cloud.google.com/iam-admin/iam/project) role is added to the +service account. Alternatively, the account can be granted all of the following roles: +- **Editor** +- **Logs Configuration Writer** +- **PubSub Admin** + +Once you create a project, set the following environment variables to be able to +run the against the actual APIs. + +- **GCLOUD_TESTS_GOLANG_PROJECT_ID**: Developers Console project's ID (e.g. bamboo-shift-455) +- **GCLOUD_TESTS_GOLANG_KEY**: The path to the JSON key file. + +Some packages require additional environment variables to be set: + +- firestore + - **GCLOUD_TESTS_GOLANG_FIRESTORE_PROJECT_ID**: project ID for Firestore. + - **GCLOUD_TESTS_GOLANG_FIRESTORE_KEY**: The path to the JSON key file. +- storage + - **GCLOUD_TESTS_GOLANG_KEYRING**: The full name of the keyring for the tests, in the + form "projects/P/locations/L/keyRings/R". +- translate + - **GCLOUD_TESTS_API_KEY**: API key for using the Translate API. +- profiler + - **GCLOUD_TESTS_GOLANG_ZONE**: Compute Engine zone. + +Some packages can record the RPCs during integration tests to a file for +subsequent replay. To record, pass the `-record` flag to `go test`. The +recording will be saved to the _package_`.replay` file. To replay integration +tests from a saved recording, the replay file must be present, the `-short` flag +must be passed to `go test`, and the **GCLOUD_TESTS_GOLANG_ENABLE_REPLAY** +environment variable must have a non-empty value. + +Install the [gcloud command-line tool][gcloudcli] to your machine and use it +to create some resources used in integration tests. + +From the project's root directory: + +``` sh +# Set the default project in your env. +$ gcloud config set project $GCLOUD_TESTS_GOLANG_PROJECT_ID + +# Authenticate the gcloud tool with your account. +$ gcloud auth login + +# Create the indexes used in the datastore integration tests. +$ gcloud preview datastore create-indexes datastore/testdata/index.yaml + +# Create a Google Cloud storage bucket with the same name as your test project, +# and with the Stackdriver Logging service account as owner, for the sink +# integration tests in logging. +$ gsutil mb gs://$GCLOUD_TESTS_GOLANG_PROJECT_ID +$ gsutil acl ch -g cloud-logs@google.com:O gs://$GCLOUD_TESTS_GOLANG_PROJECT_ID + +# Create a PubSub topic for integration tests of storage notifications. +$ gcloud beta pubsub topics create go-storage-notification-test + +# Create a Spanner instance for the spanner integration tests. +$ gcloud beta spanner instances create go-integration-test --config regional-us-central1 --nodes 1 --description 'Instance for go client test' +# NOTE: Spanner instances are priced by the node-hour, so you may want to delete +# the instance after testing with 'gcloud beta spanner instances delete'. + +# For Storage integration tests: +# Enable KMS for your project in the Cloud Console. +# Create a KMS keyring, in the same location as the default location for your project's buckets. +$ gcloud kms keyrings create MY_KEYRING --location MY_LOCATION +# Create two keys in the keyring, named key1 and key2. +$ gcloud kms keys create key1 --keyring MY_KEYRING --location MY_LOCATION --purpose encryption +$ gcloud kms keys create key2 --keyring MY_KEYRING --location MY_LOCATION --purpose encryption +# As mentioned above, set the GCLOUD_TESTS_GOLANG_KEYRING environment variable. +$ export GCLOUD_TESTS_GOLANG_KEYRING=projects/$GCLOUD_TESTS_GOLANG_PROJECT_ID/locations/MY_LOCATION/keyRings/MY_KEYRING +# Authorize Google Cloud Storage to encrypt and decrypt using key1. +gsutil kms authorize -p $GCLOUD_TESTS_GOLANG_PROJECT_ID -k $GCLOUD_TESTS_GOLANG_KEYRING/cryptoKeys/key1 +``` + +Once you've done the necessary setup, you can run the integration tests by running: + +``` sh +$ go test -v cloud.google.com/go/... +``` + +## Contributor License Agreements + +Before we can accept your pull requests you'll need to sign a Contributor +License Agreement (CLA): + +- **If you are an individual writing original source code** and **you own the +intellectual property**, then you'll need to sign an [individual CLA][indvcla]. +- **If you work for a company that wants to allow you to contribute your +work**, then you'll need to sign a [corporate CLA][corpcla]. + +You can sign these electronically (just scroll to the bottom). After that, +we'll be able to accept your pull requests. + +## Contributor Code of Conduct + +As contributors and maintainers of this project, +and in the interest of fostering an open and welcoming community, +we pledge to respect all people who contribute through reporting issues, +posting feature requests, updating documentation, +submitting pull requests or patches, and other activities. + +We are committed to making participation in this project +a harassment-free experience for everyone, +regardless of level of experience, gender, gender identity and expression, +sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, +such as physical or electronic +addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. +By adopting this Code of Conduct, +project maintainers commit themselves to fairly and consistently +applying these principles to every aspect of managing this project. +Project maintainers who do not follow or enforce the Code of Conduct +may be permanently removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior +may be reported by opening an issue +or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) + +[gcloudcli]: https://developers.google.com/cloud/sdk/gcloud/ +[indvcla]: https://developers.google.com/open-source/cla/individual +[corpcla]: https://developers.google.com/open-source/cla/corporate diff --git a/vendor/cloud.google.com/go/CONTRIBUTORS b/vendor/cloud.google.com/go/CONTRIBUTORS new file mode 100644 index 0000000000..3b3cbed98e --- /dev/null +++ b/vendor/cloud.google.com/go/CONTRIBUTORS @@ -0,0 +1,40 @@ +# People who have agreed to one of the CLAs and can contribute patches. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# https://developers.google.com/open-source/cla/individual +# https://developers.google.com/open-source/cla/corporate +# +# Names should be added to this file as: +# Name + +# Keep the list alphabetically sorted. + +Alexis Hunt +Andreas Litt +Andrew Gerrand +Brad Fitzpatrick +Burcu Dogan +Dave Day +David Sansome +David Symonds +Filippo Valsorda +Glenn Lewis +Ingo Oeser +James Hall +Johan Euphrosine +Jonathan Amsterdam +Kunpei Sakai +Luna Duclos +Magnus Hiie +Mario Castro +Michael McGreevy +Omar Jarjur +Paweł Knap +Péter Szilágyi +Sarah Adams +Thanatat Tamtan +Toby Burress +Tuo Shan +Tyler Treat diff --git a/vendor/cloud.google.com/go/LICENSE b/vendor/cloud.google.com/go/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/cloud.google.com/go/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/cloud.google.com/go/MIGRATION.md b/vendor/cloud.google.com/go/MIGRATION.md new file mode 100644 index 0000000000..791210de19 --- /dev/null +++ b/vendor/cloud.google.com/go/MIGRATION.md @@ -0,0 +1,54 @@ +# Code Changes + +## v0.10.0 + +- pubsub: Replace + + ``` + sub.ModifyPushConfig(ctx, pubsub.PushConfig{Endpoint: "https://example.com/push"}) + ``` + + with + + ``` + sub.Update(ctx, pubsub.SubscriptionConfigToUpdate{ + PushConfig: &pubsub.PushConfig{Endpoint: "https://example.com/push"}, + }) + ``` + +- trace: traceGRPCServerInterceptor will be provided from *trace.Client. +Given an initialized `*trace.Client` named `tc`, instead of + + ``` + s := grpc.NewServer(grpc.UnaryInterceptor(trace.GRPCServerInterceptor(tc))) + ``` + + write + + ``` + s := grpc.NewServer(grpc.UnaryInterceptor(tc.GRPCServerInterceptor())) + ``` + +- trace trace.GRPCClientInterceptor will also provided from *trace.Client. +Instead of + + ``` + conn, err := grpc.Dial(srv.Addr, grpc.WithUnaryInterceptor(trace.GRPCClientInterceptor())) + ``` + + write + + ``` + conn, err := grpc.Dial(srv.Addr, grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor())) + ``` + +- trace: We removed the deprecated `trace.EnableGRPCTracing`. Use the gRPC +interceptor as a dial option as shown below when initializing Cloud package +clients: + + ``` + c, err := pubsub.NewClient(ctx, "project-id", option.WithGRPCDialOption(grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor()))) + if err != nil { + ... + } + ``` diff --git a/vendor/cloud.google.com/go/README.md b/vendor/cloud.google.com/go/README.md new file mode 100644 index 0000000000..a8abfac436 --- /dev/null +++ b/vendor/cloud.google.com/go/README.md @@ -0,0 +1,592 @@ +# Google Cloud Client Libraries for Go + +[![GoDoc](https://godoc.org/cloud.google.com/go?status.svg)](https://godoc.org/cloud.google.com/go) + +Go packages for [Google Cloud Platform](https://cloud.google.com) services. + +``` go +import "cloud.google.com/go" +``` + +To install the packages on your system, *do not clone the repo*. Instead use + +``` +$ go get -u cloud.google.com/go/... +``` + +**NOTE:** Some of these packages are under development, and may occasionally +make backwards-incompatible changes. + +**NOTE:** Github repo is a mirror of [https://code.googlesource.com/gocloud](https://code.googlesource.com/gocloud). + + * [News](#news) + * [Supported APIs](#supported-apis) + * [Go Versions Supported](#go-versions-supported) + * [Authorization](#authorization) + * [Cloud Datastore](#cloud-datastore-) + * [Cloud Storage](#cloud-storage-) + * [Cloud Pub/Sub](#cloud-pub-sub-) + * [Cloud BigQuery](#cloud-bigquery-) + * [Stackdriver Logging](#stackdriver-logging-) + * [Cloud Spanner](#cloud-spanner-) + + +## News + +_July 11, 2018_ + +*v0.25.0* + +- Added [Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CODE_OF_CONDUCT.md) +- bigtable: + - cbt: Support a GC policy of "never". +- errorreporting: + - Support User. + - Close now calls Flush. + - Use OnError (previously ignored). + - Pass through the RPC error as-is to OnError. +- httpreplay: A tool for recording and replaying HTTP requests + (for the bigquery and storage clients in this repo). +- kms: v1 client added +- logging: add SourceLocation to Entry. +- storage: improve CRC checking on read. + + +_June 18, 2018_ + +*v0.24.0* + +- bigquery: Support for the NUMERIC type. +- bigtable: + - cbt: Optionally specify columns for read/lookup + - Support instance-level administration. +- oslogin: New client for the OS Login API. +- pubsub: + - The package is now stable. There will be no further breaking changes. + - Internal changes to improve Subscription.Receive behavior. +- storage: Support updating bucket lifecycle config. +- spanner: Support struct-typed parameter bindings. +- texttospeech: New client for the Text-to-Speech API. + +_May 18, 2018_ + +*v0.23.0* + +- bigquery: Add DDL stats to query statistics. +- bigtable: + - cbt: Add cells-per-column limit for row lookup. + - cbt: Make it possible to combine read filters. +- dlp: v2beta2 client removed. Use the v2 client instead. +- firestore, spanner: Fix compilation errors due to protobuf changes. + +_May 8, 2018_ + +*v0.22.0* + +- bigtable: + - cbt: Support cells per column limit for row read. + - bttest: Correctly handle empty RowSet. + - Fix ReadModifyWrite operation in emulator. + - Fix API path in GetCluster. + +- bigquery: + - BEHAVIOR CHANGE: Retry on 503 status code. + - Add dataset.DeleteWithContents. + - Add SchemaUpdateOptions for query jobs. + - Add Timeline to QueryStatistics. + - Add more stats to ExplainQueryStage. + - Support Parquet data format. + +- datastore: + - Support omitempty for times. + +- dlp: + - **BREAKING CHANGE:** Remove v1beta1 client. Please migrate to the v2 client, + which is now out of beta. + - Add v2 client. + +- firestore: + - BEHAVIOR CHANGE: Treat set({}, MergeAll) as valid. + +- iam: + - Support JWT signing via SignJwt callopt. + +- profiler: + - BEHAVIOR CHANGE: PollForSerialOutput returns an error when context.Done. + - BEHAVIOR CHANGE: Increase the initial backoff to 1 minute. + - Avoid returning empty serial port output. + +- pubsub: + - BEHAVIOR CHANGE: Don't backoff during next retryable error once stream is healthy. + - BEHAVIOR CHANGE: Don't backoff on EOF. + - pstest: Support Acknowledge and ModifyAckDeadline RPCs. + +- redis: + - Add v1 beta Redis client. + +- spanner: + - Support SessionLabels. + +- speech: + - Add api v1 beta1 client. + +- storage: + - BEHAVIOR CHANGE: Retry reads when retryable error occurs. + - Fix delete of object in requester-pays bucket. + - Support KMS integration. + +_April 9, 2018_ + +*v0.21.0* + +- bigquery: + - Add OpenCensus tracing. + +- firestore: + - **BREAKING CHANGE:** If a document does not exist, return a DocumentSnapshot + whose Exists method returns false. DocumentRef.Get and Transaction.Get + return the non-nil DocumentSnapshot in addition to a NotFound error. + **DocumentRef.GetAll and Transaction.GetAll return a non-nil + DocumentSnapshot instead of nil.** + - Add DocumentIterator.Stop. **Call Stop whenever you are done with a + DocumentIterator.** + - Added Query.Snapshots and DocumentRef.Snapshots, which provide realtime + notification of updates. See https://cloud.google.com/firestore/docs/query-data/listen. + - Canceling an RPC now always returns a grpc.Status with codes.Canceled. + +- spanner: + - Add `CommitTimestamp`, which supports inserting the commit timestamp of a + transaction into a column. + +_March 22, 2018_ + +*v0.20.0* + +- bigquery: Support SchemaUpdateOptions for load jobs. + +- bigtable: + - Add SampleRowKeys. + - cbt: Support union, intersection GCPolicy. + - Retry admin RPCS. + - Add trace spans to retries. + +- datastore: Add OpenCensus tracing. + +- firestore: + - Fix queries involving Null and NaN. + - Allow Timestamp protobuffers for time values. + +- logging: Add a WriteTimeout option. + +- spanner: Support Batch API. + +- storage: Add OpenCensus tracing. + +[Older news](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/old-news.md) + +## Supported APIs + +Google API | Status | Package +---------------------------------|--------------|----------------------------------------------------------- +[BigQuery][cloud-bigquery] | stable | [`cloud.google.com/go/bigquery`][cloud-bigquery-ref] +[Bigtable][cloud-bigtable] | stable | [`cloud.google.com/go/bigtable`][cloud-bigtable-ref] +[Container][cloud-container] | alpha | [`cloud.google.com/go/container/apiv1`][cloud-container-ref] +[Data Loss Prevention][cloud-dlp]| alpha | [`cloud.google.com/go/dlp/apiv2beta1`][cloud-dlp-ref] +[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`][cloud-datastore-ref] +[Debugger][cloud-debugger] | alpha | [`cloud.google.com/go/debugger/apiv2`][cloud-debugger-ref] +[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`][cloud-errors-ref] +[Firestore][cloud-firestore] | beta | [`cloud.google.com/go/firestore`][cloud-firestore-ref] +[Language][cloud-language] | stable | [`cloud.google.com/go/language/apiv1`][cloud-language-ref] +[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`][cloud-logging-ref] +[Monitoring][cloud-monitoring] | beta | [`cloud.google.com/go/monitoring/apiv3`][cloud-monitoring-ref] +[OS Login][cloud-oslogin] | alpha | [`cloud.google.com/compute/docs/oslogin/rest`][cloud-oslogin-ref] +[Pub/Sub][cloud-pubsub] | stable | [`cloud.google.com/go/pubsub`][cloud-pubsub-ref] +[Spanner][cloud-spanner] | stable | [`cloud.google.com/go/spanner`][cloud-spanner-ref] +[Speech][cloud-speech] | stable | [`cloud.google.com/go/speech/apiv1`][cloud-speech-ref] +[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`][cloud-storage-ref] +[Translation][cloud-translation] | stable | [`cloud.google.com/go/translate`][cloud-translation-ref] +[Video Intelligence][cloud-video]| beta | [`cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref] +[Vision][cloud-vision] | stable | [`cloud.google.com/go/vision/apiv1`][cloud-vision-ref] + + +> **Alpha status**: the API is still being actively developed. As a +> result, it might change in backward-incompatible ways and is not recommended +> for production use. +> +> **Beta status**: the API is largely complete, but still has outstanding +> features and bugs to be addressed. There may be minor backwards-incompatible +> changes where necessary. +> +> **Stable status**: the API is mature and ready for production use. We will +> continue addressing bugs and feature requests. + +Documentation and examples are available at +https://godoc.org/cloud.google.com/go + +Visit or join the +[google-api-go-announce group](https://groups.google.com/forum/#!forum/google-api-go-announce) +for updates on these packages. + +## Go Versions Supported + +We support the two most recent major versions of Go. If Google App Engine uses +an older version, we support that as well. You can see which versions are +currently supported by looking at the lines following `go:` in +[`.travis.yml`](.travis.yml). + +## Authorization + +By default, each API will use [Google Application Default Credentials][default-creds] +for authorization credentials used in calling the API endpoints. This will allow your +application to run in many environments without requiring explicit configuration. + +[snip]:# (auth) +```go +client, err := storage.NewClient(ctx) +``` + +To authorize using a +[JSON key file](https://cloud.google.com/iam/docs/managing-service-account-keys), +pass +[`option.WithServiceAccountFile`](https://godoc.org/google.golang.org/api/option#WithServiceAccountFile) +to the `NewClient` function of the desired package. For example: + +[snip]:# (auth-JSON) +```go +client, err := storage.NewClient(ctx, option.WithServiceAccountFile("path/to/keyfile.json")) +``` + +You can exert more control over authorization by using the +[`golang.org/x/oauth2`](https://godoc.org/golang.org/x/oauth2) package to +create an `oauth2.TokenSource`. Then pass +[`option.WithTokenSource`](https://godoc.org/google.golang.org/api/option#WithTokenSource) +to the `NewClient` function: +[snip]:# (auth-ts) +```go +tokenSource := ... +client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource)) +``` + +## Cloud Datastore [![GoDoc](https://godoc.org/cloud.google.com/go/datastore?status.svg)](https://godoc.org/cloud.google.com/go/datastore) + +- [About Cloud Datastore][cloud-datastore] +- [Activating the API for your project][cloud-datastore-activation] +- [API documentation][cloud-datastore-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/datastore) +- [Complete sample program](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/datastore/tasks) + +### Example Usage + +First create a `datastore.Client` to use throughout your application: + +[snip]:# (datastore-1) +```go +client, err := datastore.NewClient(ctx, "my-project-id") +if err != nil { + log.Fatal(err) +} +``` + +Then use that client to interact with the API: + +[snip]:# (datastore-2) +```go +type Post struct { + Title string + Body string `datastore:",noindex"` + PublishedAt time.Time +} +keys := []*datastore.Key{ + datastore.NameKey("Post", "post1", nil), + datastore.NameKey("Post", "post2", nil), +} +posts := []*Post{ + {Title: "Post 1", Body: "...", PublishedAt: time.Now()}, + {Title: "Post 2", Body: "...", PublishedAt: time.Now()}, +} +if _, err := client.PutMulti(ctx, keys, posts); err != nil { + log.Fatal(err) +} +``` + +## Cloud Storage [![GoDoc](https://godoc.org/cloud.google.com/go/storage?status.svg)](https://godoc.org/cloud.google.com/go/storage) + +- [About Cloud Storage][cloud-storage] +- [API documentation][cloud-storage-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/storage) +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/storage) + +### Example Usage + +First create a `storage.Client` to use throughout your application: + +[snip]:# (storage-1) +```go +client, err := storage.NewClient(ctx) +if err != nil { + log.Fatal(err) +} +``` + +[snip]:# (storage-2) +```go +// Read the object1 from bucket. +rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx) +if err != nil { + log.Fatal(err) +} +defer rc.Close() +body, err := ioutil.ReadAll(rc) +if err != nil { + log.Fatal(err) +} +``` + +## Cloud Pub/Sub [![GoDoc](https://godoc.org/cloud.google.com/go/pubsub?status.svg)](https://godoc.org/cloud.google.com/go/pubsub) + +- [About Cloud Pubsub][cloud-pubsub] +- [API documentation][cloud-pubsub-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/pubsub) +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/pubsub) + +### Example Usage + +First create a `pubsub.Client` to use throughout your application: + +[snip]:# (pubsub-1) +```go +client, err := pubsub.NewClient(ctx, "project-id") +if err != nil { + log.Fatal(err) +} +``` + +Then use the client to publish and subscribe: + +[snip]:# (pubsub-2) +```go +// Publish "hello world" on topic1. +topic := client.Topic("topic1") +res := topic.Publish(ctx, &pubsub.Message{ + Data: []byte("hello world"), +}) +// The publish happens asynchronously. +// Later, you can get the result from res: +... +msgID, err := res.Get(ctx) +if err != nil { + log.Fatal(err) +} + +// Use a callback to receive messages via subscription1. +sub := client.Subscription("subscription1") +err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { + fmt.Println(m.Data) + m.Ack() // Acknowledge that we've consumed the message. +}) +if err != nil { + log.Println(err) +} +``` + +## Cloud BigQuery [![GoDoc](https://godoc.org/cloud.google.com/go/bigquery?status.svg)](https://godoc.org/cloud.google.com/go/bigquery) + +- [About Cloud BigQuery][cloud-bigquery] +- [API documentation][cloud-bigquery-docs] +- [Go client documentation][cloud-bigquery-ref] +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/bigquery) + +### Example Usage + +First create a `bigquery.Client` to use throughout your application: +[snip]:# (bq-1) +```go +c, err := bigquery.NewClient(ctx, "my-project-ID") +if err != nil { + // TODO: Handle error. +} +``` + +Then use that client to interact with the API: +[snip]:# (bq-2) +```go +// Construct a query. +q := c.Query(` + SELECT year, SUM(number) + FROM [bigquery-public-data:usa_names.usa_1910_2013] + WHERE name = "William" + GROUP BY year + ORDER BY year +`) +// Execute the query. +it, err := q.Read(ctx) +if err != nil { + // TODO: Handle error. +} +// Iterate through the results. +for { + var values []bigquery.Value + err := it.Next(&values) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(values) +} +``` + + +## Stackdriver Logging [![GoDoc](https://godoc.org/cloud.google.com/go/logging?status.svg)](https://godoc.org/cloud.google.com/go/logging) + +- [About Stackdriver Logging][cloud-logging] +- [API documentation][cloud-logging-docs] +- [Go client documentation][cloud-logging-ref] +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/logging) + +### Example Usage + +First create a `logging.Client` to use throughout your application: +[snip]:# (logging-1) +```go +ctx := context.Background() +client, err := logging.NewClient(ctx, "my-project") +if err != nil { + // TODO: Handle error. +} +``` + +Usually, you'll want to add log entries to a buffer to be periodically flushed +(automatically and asynchronously) to the Stackdriver Logging service. +[snip]:# (logging-2) +```go +logger := client.Logger("my-log") +logger.Log(logging.Entry{Payload: "something happened!"}) +``` + +Close your client before your program exits, to flush any buffered log entries. +[snip]:# (logging-3) +```go +err = client.Close() +if err != nil { + // TODO: Handle error. +} +``` + +## Cloud Spanner [![GoDoc](https://godoc.org/cloud.google.com/go/spanner?status.svg)](https://godoc.org/cloud.google.com/go/spanner) + +- [About Cloud Spanner][cloud-spanner] +- [API documentation][cloud-spanner-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/spanner) + +### Example Usage + +First create a `spanner.Client` to use throughout your application: + +[snip]:# (spanner-1) +```go +client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D") +if err != nil { + log.Fatal(err) +} +``` + +[snip]:# (spanner-2) +```go +// Simple Reads And Writes +_, err = client.Apply(ctx, []*spanner.Mutation{ + spanner.Insert("Users", + []string{"name", "email"}, + []interface{}{"alice", "a@example.com"})}) +if err != nil { + log.Fatal(err) +} +row, err := client.Single().ReadRow(ctx, "Users", + spanner.Key{"alice"}, []string{"email"}) +if err != nil { + log.Fatal(err) +} +``` + + +## Contributing + +Contributions are welcome. Please, see the +[CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md) +document for details. We're using Gerrit for our code reviews. Please don't open pull +requests against this repo, new pull requests will be automatically closed. + +Please note that this project is released with a Contributor Code of Conduct. +By participating in this project you agree to abide by its terms. +See [Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md#contributor-code-of-conduct) +for more information. + +[cloud-datastore]: https://cloud.google.com/datastore/ +[cloud-datastore-ref]: https://godoc.org/cloud.google.com/go/datastore +[cloud-datastore-docs]: https://cloud.google.com/datastore/docs +[cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate + +[cloud-firestore]: https://cloud.google.com/firestore/ +[cloud-firestore-ref]: https://godoc.org/cloud.google.com/go/firestore +[cloud-firestore-docs]: https://cloud.google.com/firestore/docs +[cloud-firestore-activation]: https://cloud.google.com/firestore/docs/activate + +[cloud-pubsub]: https://cloud.google.com/pubsub/ +[cloud-pubsub-ref]: https://godoc.org/cloud.google.com/go/pubsub +[cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs + +[cloud-storage]: https://cloud.google.com/storage/ +[cloud-storage-ref]: https://godoc.org/cloud.google.com/go/storage +[cloud-storage-docs]: https://cloud.google.com/storage/docs +[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets + +[cloud-bigtable]: https://cloud.google.com/bigtable/ +[cloud-bigtable-ref]: https://godoc.org/cloud.google.com/go/bigtable + +[cloud-bigquery]: https://cloud.google.com/bigquery/ +[cloud-bigquery-docs]: https://cloud.google.com/bigquery/docs +[cloud-bigquery-ref]: https://godoc.org/cloud.google.com/go/bigquery + +[cloud-logging]: https://cloud.google.com/logging/ +[cloud-logging-docs]: https://cloud.google.com/logging/docs +[cloud-logging-ref]: https://godoc.org/cloud.google.com/go/logging + +[cloud-monitoring]: https://cloud.google.com/monitoring/ +[cloud-monitoring-ref]: https://godoc.org/cloud.google.com/go/monitoring/apiv3 + +[cloud-vision]: https://cloud.google.com/vision +[cloud-vision-ref]: https://godoc.org/cloud.google.com/go/vision/apiv1 + +[cloud-language]: https://cloud.google.com/natural-language +[cloud-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1 + +[cloud-oslogin]: https://cloud.google.com/compute/docs/oslogin/rest +[cloud-oslogin-ref]: https://cloud.google.com/compute/docs/oslogin/rest + +[cloud-speech]: https://cloud.google.com/speech +[cloud-speech-ref]: https://godoc.org/cloud.google.com/go/speech/apiv1 + +[cloud-spanner]: https://cloud.google.com/spanner/ +[cloud-spanner-ref]: https://godoc.org/cloud.google.com/go/spanner +[cloud-spanner-docs]: https://cloud.google.com/spanner/docs + +[cloud-translation]: https://cloud.google.com/translation +[cloud-translation-ref]: https://godoc.org/cloud.google.com/go/translation + +[cloud-video]: https://cloud.google.com/video-intelligence/ +[cloud-video-ref]: https://godoc.org/cloud.google.com/go/videointelligence/apiv1beta1 + +[cloud-errors]: https://cloud.google.com/error-reporting/ +[cloud-errors-ref]: https://godoc.org/cloud.google.com/go/errorreporting + +[cloud-container]: https://cloud.google.com/containers/ +[cloud-container-ref]: https://godoc.org/cloud.google.com/go/container/apiv1 + +[cloud-debugger]: https://cloud.google.com/debugger/ +[cloud-debugger-ref]: https://godoc.org/cloud.google.com/go/debugger/apiv2 + +[cloud-dlp]: https://cloud.google.com/dlp/ +[cloud-dlp-ref]: https://godoc.org/cloud.google.com/go/dlp/apiv2beta1 + +[default-creds]: https://developers.google.com/identity/protocols/application-default-credentials diff --git a/vendor/cloud.google.com/go/RELEASING.md b/vendor/cloud.google.com/go/RELEASING.md new file mode 100644 index 0000000000..c2735c53b3 --- /dev/null +++ b/vendor/cloud.google.com/go/RELEASING.md @@ -0,0 +1,13 @@ +# How to Release this Repo + +1. Determine the current release version with `git tag -l`. It should look + something like `vX.Y.Z`. We'll call the current + version `$CV` and the new version `$NV`. +1. On master, run `git log $CV..` to list all the changes since the last + release. +1. Edit the News section of `README.md` to include a summary of the changes. +1. Mail the CL containing the `README.md` changes. When the CL is approved, submit it. +1. Without submitting any other CLs: + a. Switch to master. + b. Tag the repo with the next version: `git tag $NV`. + c. Push the tag: `git push origin $NV`. diff --git a/vendor/cloud.google.com/go/authexample_test.go b/vendor/cloud.google.com/go/authexample_test.go new file mode 100644 index 0000000000..66a1b8f330 --- /dev/null +++ b/vendor/cloud.google.com/go/authexample_test.go @@ -0,0 +1,72 @@ +// 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 cloud_test + +import ( + "cloud.google.com/go/datastore" + "cloud.google.com/go/pubsub" + "golang.org/x/net/context" + "golang.org/x/oauth2/google" + "google.golang.org/api/option" +) + +// Google Application Default Credentials is the recommended way to authorize +// and authenticate clients. +// +// For information on how to create and obtain Application Default Credentials, see +// https://developers.google.com/identity/protocols/application-default-credentials. +func Example_applicationDefaultCredentials() { + client, err := datastore.NewClient(context.Background(), "project-id") + if err != nil { + // TODO: handle error. + } + _ = client // Use the client. +} + +// You can use a file with credentials to authenticate and authorize, such as a JSON +// key file associated with a Google service account. Service Account keys can be +// created and downloaded from +// https://console.developers.google.com/permissions/serviceaccounts. +// +// This example uses the Datastore client, but the same steps apply to +// the other client libraries underneath this package. +func Example_credentialsFile() { + client, err := datastore.NewClient(context.Background(), + "project-id", option.WithCredentialsFile("/path/to/service-account-key.json")) + if err != nil { + // TODO: handle error. + } + _ = client // Use the client. +} + +// In some cases (for instance, you don't want to store secrets on disk), you can +// create credentials from in-memory JSON and use the WithCredentials option. +// +// The google package in this example is at golang.org/x/oauth2/google. +// +// This example uses the PubSub client, but the same steps apply to +// the other client libraries underneath this package. +func Example_credentialsFromJSON() { + ctx := context.Background() + creds, err := google.CredentialsFromJSON(ctx, []byte("JSON creds"), pubsub.ScopePubSub) + if err != nil { + // TODO: handle error. + } + client, err := pubsub.NewClient(ctx, "project-id", option.WithCredentials(creds)) + if err != nil { + // TODO: handle error. + } + _ = client // Use the client. +} diff --git a/vendor/cloud.google.com/go/bigquery/benchmarks/README.md b/vendor/cloud.google.com/go/bigquery/benchmarks/README.md new file mode 100644 index 0000000000..c97f9d8607 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/benchmarks/README.md @@ -0,0 +1,8 @@ +# BigQuery Benchmark +This directory contains benchmarks for BigQuery client. + +## Usage +`go run bench.go -- queries.json` + +BigQuery service caches requests so the benchmark should be run +at least twice, disregarding the first result. diff --git a/vendor/cloud.google.com/go/bigquery/benchmarks/bench.go b/vendor/cloud.google.com/go/bigquery/benchmarks/bench.go new file mode 100644 index 0000000000..021092ce8d --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/benchmarks/bench.go @@ -0,0 +1,85 @@ +// Copyright 2017 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 ignore + +package main + +import ( + "encoding/json" + "flag" + "io/ioutil" + "log" + "time" + + "cloud.google.com/go/bigquery" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func main() { + flag.Parse() + + ctx := context.Background() + c, err := bigquery.NewClient(ctx, flag.Arg(0)) + if err != nil { + log.Fatal(err) + } + + queriesJSON, err := ioutil.ReadFile(flag.Arg(1)) + if err != nil { + log.Fatal(err) + } + + var queries []string + if err := json.Unmarshal(queriesJSON, &queries); err != nil { + log.Fatal(err) + } + + for _, q := range queries { + doQuery(ctx, c, q) + } +} + +func doQuery(ctx context.Context, c *bigquery.Client, qt string) { + startTime := time.Now() + q := c.Query(qt) + it, err := q.Read(ctx) + if err != nil { + log.Fatal(err) + } + + numRows, numCols := 0, 0 + var firstByte time.Duration + + for { + var values []bigquery.Value + err := it.Next(&values) + if err == iterator.Done { + break + } + if err != nil { + log.Fatal(err) + } + if numRows == 0 { + numCols = len(values) + firstByte = time.Since(startTime) + } else if numCols != len(values) { + log.Fatalf("got %d columns, want %d", len(values), numCols) + } + numRows++ + } + log.Printf("query %q: %d rows, %d cols, first byte %f sec, total %f sec", + qt, numRows, numCols, firstByte.Seconds(), time.Since(startTime).Seconds()) +} diff --git a/vendor/cloud.google.com/go/bigquery/benchmarks/queries.json b/vendor/cloud.google.com/go/bigquery/benchmarks/queries.json new file mode 100644 index 0000000000..13fed38b52 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/benchmarks/queries.json @@ -0,0 +1,10 @@ +[ + "SELECT * FROM `nyc-tlc.yellow.trips` LIMIT 10000", + "SELECT * FROM `nyc-tlc.yellow.trips` LIMIT 100000", + "SELECT * FROM `nyc-tlc.yellow.trips` LIMIT 1000000", + "SELECT title FROM `bigquery-public-data.samples.wikipedia` ORDER BY title LIMIT 1000", + "SELECT title, id, timestamp, contributor_ip FROM `bigquery-public-data.samples.wikipedia` WHERE title like 'Blo%' ORDER BY id", + "SELECT * FROM `bigquery-public-data.baseball.games_post_wide` ORDER BY gameId", + "SELECT * FROM `bigquery-public-data.samples.github_nested` WHERE repository.has_downloads ORDER BY repository.created_at LIMIT 10000", + "SELECT repo_name, path FROM `bigquery-public-data.github_repos.files` WHERE path LIKE '%.java' ORDER BY id LIMIT 1000000" +] diff --git a/vendor/cloud.google.com/go/bigquery/bigquery.go b/vendor/cloud.google.com/go/bigquery/bigquery.go new file mode 100644 index 0000000000..ceb0228182 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/bigquery.go @@ -0,0 +1,164 @@ +// Copyright 2015 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 bigquery + +import ( + "fmt" + "io" + "net/http" + "time" + + gax "github.com/googleapis/gax-go" + + "cloud.google.com/go/internal" + "cloud.google.com/go/internal/version" + + "google.golang.org/api/googleapi" + "google.golang.org/api/option" + htransport "google.golang.org/api/transport/http" + + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" +) + +const ( + prodAddr = "https://www.googleapis.com/bigquery/v2/" + Scope = "https://www.googleapis.com/auth/bigquery" + userAgent = "gcloud-golang-bigquery/20160429" +) + +var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo) + +func setClientHeader(headers http.Header) { + headers.Set("x-goog-api-client", xGoogHeader) +} + +// Client may be used to perform BigQuery operations. +type Client struct { + // Location, if set, will be used as the default location for all subsequent + // dataset creation and job operations. A location specified directly in one of + // those operations will override this value. + Location string + + projectID string + bqs *bq.Service +} + +// NewClient constructs a new Client which can perform BigQuery operations. +// Operations performed via the client are billed to the specified GCP project. +func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) { + o := []option.ClientOption{ + option.WithEndpoint(prodAddr), + option.WithScopes(Scope), + option.WithUserAgent(userAgent), + } + o = append(o, opts...) + httpClient, endpoint, err := htransport.NewClient(ctx, o...) + if err != nil { + return nil, fmt.Errorf("bigquery: dialing: %v", err) + } + bqs, err := bq.New(httpClient) + if err != nil { + return nil, fmt.Errorf("bigquery: constructing client: %v", err) + } + bqs.BasePath = endpoint + c := &Client{ + projectID: projectID, + bqs: bqs, + } + return c, nil +} + +// Close closes any resources held by the client. +// Close should be called when the client is no longer needed. +// It need not be called at program exit. +func (c *Client) Close() error { + return nil +} + +// Calls the Jobs.Insert RPC and returns a Job. +func (c *Client) insertJob(ctx context.Context, job *bq.Job, media io.Reader) (*Job, error) { + call := c.bqs.Jobs.Insert(c.projectID, job).Context(ctx) + setClientHeader(call.Header()) + if media != nil { + call.Media(media) + } + var res *bq.Job + var err error + invoke := func() error { + res, err = call.Do() + return err + } + // A job with a client-generated ID can be retried; the presence of the + // ID makes the insert operation idempotent. + // We don't retry if there is media, because it is an io.Reader. We'd + // have to read the contents and keep it in memory, and that could be expensive. + // TODO(jba): Look into retrying if media != nil. + if job.JobReference != nil && media == nil { + err = runWithRetry(ctx, invoke) + } else { + err = invoke() + } + if err != nil { + return nil, err + } + return bqToJob(res, c) +} + +// Convert a number of milliseconds since the Unix epoch to a time.Time. +// Treat an input of zero specially: convert it to the zero time, +// rather than the start of the epoch. +func unixMillisToTime(m int64) time.Time { + if m == 0 { + return time.Time{} + } + return time.Unix(0, m*1e6) +} + +// runWithRetry calls the function until it returns nil or a non-retryable error, or +// the context is done. +// See the similar function in ../storage/invoke.go. The main difference is the +// reason for retrying. +func runWithRetry(ctx context.Context, call func() error) error { + // These parameters match the suggestions in https://cloud.google.com/bigquery/sla. + backoff := gax.Backoff{ + Initial: 1 * time.Second, + Max: 32 * time.Second, + Multiplier: 2, + } + return internal.Retry(ctx, backoff, func() (stop bool, err error) { + err = call() + if err == nil { + return true, nil + } + return !retryableError(err), err + }) +} + +// This is the correct definition of retryable according to the BigQuery team. It +// also considers 502 ("Bad Gateway") and 503 ("Service Unavailable") errors +// retryable; these are returned by systems between the client and the BigQuery +// service. +func retryableError(err error) bool { + e, ok := err.(*googleapi.Error) + if !ok { + return false + } + var reason string + if len(e.Errors) > 0 { + reason = e.Errors[0].Reason + } + return e.Code == http.StatusServiceUnavailable || e.Code == http.StatusBadGateway || reason == "backendError" || reason == "rateLimitExceeded" +} diff --git a/vendor/cloud.google.com/go/bigquery/bigquery.replay b/vendor/cloud.google.com/go/bigquery/bigquery.replay new file mode 100644 index 0000000000..e499fa261a --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/bigquery.replay @@ -0,0 +1,53499 @@ +{ + "Initial": "IjIwMTgtMDctMDRUMTE6MTg6MTIuOTgxNTQ3MjcxWiI=", + "Version": "0.1", + "Entries": [ + { + "ID": "9dbcd44b9867b124", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "74" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8a4f561224ca352c40d6445684bff8b2/12563758415832524460;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkYXRhc2V0UmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:13 GMT" + ], + "Etag": [ + "gdkGeCWv06Kx9xOvuZE4PQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbt65:4165,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/153,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9aw8W-GMG8TYqgWC8ZqABg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/153,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/153" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2Ry27bMBBF9/oKgd3G1oOyHgYCJGmNwGgbt0oKAy0Cg6bGMitalElKThDk30OqShdBHuaSd+bcOzMPjosqVhdo6qI1K/ctyPtPBdFEgUYnRgRNSiuWRXUJn5edH3+9y+4WXft7Fv34eXraF7G+v2g5BT1qhNSjJA6nA2YV+kHqJ360ivw4C7M0mERJmAQr37y+XQHffGN1ZSFbrRs19bzD4TAuhSg5kIapMRU77zmf14VeI8VfoFp5L0y9wVR5R7sPhTlsQEJNwaR4cNz///N/sx1Jc9GQbP7aSpDjPlpLQikoZQr+mA5r5iIpuHVGy3x+M8t7lFlMA5QRfilF21hxYC8l0yCVofW4F4TF8uojwOJQH9ffKpAX97MdYdxKOI5xlmEcRJPROki1H4idzkiYZBXFJS3ZfruveLoW6217VkAHXDQgx6WhdIyCGVu0tbbHfMM7n51/+Sh8DqR4Tu+4t3afVALRTNQ3bNdjggk2R8J+hlMc9UfmROnvomAbBsU7VYL2HKv+ukbOo/MEcvD6JSADAAA=" + } + }, + { + "ID": "78c23f15902f0b56", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "206" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e53b9046e1de9e41dd0025b1cec6358f/10955696979841739733;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM5OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoicmVjIiwidHlwZSI6IlJFQ09SRCJ9XX0sInRhYmxlUmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0X2JhZCJ9fQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:14 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:14 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlt11:4411,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9aw8W_H6Os_sqgXhhojQAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2cW2/aSBTH3/MpLMQDKyGLO0kkHgiQDVLTZknSfWirarAn4MXMeMfjNNko333PXAzGGS5pmpZGo0rgufh/5nJm5vhH6ocDp4AZo6xw7DwcODoRQ+oTpGQWZPp0jgICmYVJSMcoLJRVPsMopjI/ILcoDPy0YI7jGE2wKDkNcOg7DHtOEDv8PsLOaND7MOo744Q7UxQ7hDqxN8VzlN7s43EyGZIbKm7/NHz/sftu2P86fH9xffWl7IykzWPHoz7urBQ62mpnd5OOtNUhSRg6sucXjHLaUf09dj4XvJAmvjvFYXDnDkSFviz6XPhMRANElbQN8Hk9EAWITZI5JlwU7t4UcSf844hD1+buhNJJiN2sfcieU+IO7jwc8YCSuHiRjMPAc/XgD0mU8NKy2P0H3aLjaqP5x6voNhtbdGPMbjFL5S9lL0+SIPQxK8px0QnXAz/iuLRSQxupbWv8BiMbdOuHldfRbVefpftRjC/ilLm36gor0dKyQOrWGoe1FwrnJduN9o9WrO/oEYzG9+6ZyLlC4xCPIFmUVwYbq9X0aLR2HGWDIXBqKORG3fbSK+KEuAzfhNjj7p+YYAZt8c8xn1K/63mwz1BWbTfbYonQGS5dkxmh34hzSRPmYZNIH4d4gnhAJqsqw3kUpiob68gWNuqptki6ISKThQV1T6qlU+quo8PdtgA5XmI8RvjfBMccdsO7+2LV9VAYlvL5Wru6bSWt0e6jKMLsjNLZKfJgysEFGPKg77pclDwx/OQm1Yiti2Nj/2ob+1evf5/2R8T+y/bO2J9cJb2JbN1ZX7M72aWT0egmfsCz/XnqFfkq6Va7f3PTY9iPN3cmX0XvENs2nl8wMz1KOL7bMjdPKymrrW2HwC+YnXd0srkzqxWUteYeupnS0BuyWbvZbmW1xYkRydKirFRvpOeV+YRZ055IRLHqs4aiIHMEFpeXl+BOgYeL+vsCMTTHHLO4eOROEfFDfBJSb7bckTNnpo7OWpWjbfHZT2yKaZYI5i6LvLobiDNUr6BR5KV63SiCSBeJuPZMGmLaYGpoc10Vjphcb53hS3k1hNXICAr1snRZQqBsSDImSmsra8cxrdzNVnvQNYJDWFFwjIb3pXyBPkzbR6+lbN5t1OJJeBDCNfESxuDhye2OYw4BAYeogMQ3lM1PE54wXMylXZ8uckpr7kljaGOk8rOs119uXbjJZivVqmmEuQqsXBFgYe1FxWxilBAi1xIYGBKdWcrW0Dth07Tcd5YH9xA9MOi2TAGcUTfT/UVmD/buMfJmqvlDMsUsgHBdl76n1wRiZIPZes20ZfwgsyZ71Z3trZsd46SY1usazzqnDA/usJfAyRkXm3pGVnK1J1WarbfnSj01DJltd9nGXJn2ENOq/QmOaYozf2PHlE+reV+8mjKM/AtKw9T3hNDflM0wKxkK9Q7XeK5qUUmqVq6RbdUaK6ryyVpVztyXkhN54JPMUInIZoFLYDM+RUnIj53hfJ5wMUIKHeKbgATibH8Yo3gJLy+6o+754GowKjtw9OMJxLSd68vB6OtgNPogMxOoLRhl2Vmw0Y4FoxaMWjBqwagFoxaMWjBqwagFoxaMWjC6PzTSglELRi0YtWDUgtE94E8WjFowupeO+QbBaNlRlLCj/la07EC/MfGxD6FJpDHmlPPoDO6DSKbz8KjSl2AjiTtj5OsQo+wE+oBXaLOjvh5SjKhuzdJRiT7XkEl3BUdCXcmq/JP7E3AIaF+HswRrwHqueallrJaxWsZqGatlrJaxWsZqGatlrJaxWsa6P2DTMlbLWC1jtYzVMtY9QFmWsVrGupeO+SYZ6xIY9rIsUsPNZalChuugaNm5CUI44AVeUPhSgVQIOmQcoJPzlUIx8HPsdxcY9tPuoPHL41rx3UXKjnrxQEczQkhHnhyGRqXy6OwudOxYwmsJryW8lvBawmsJryW8lvBawmsJryW8+4NVLeG1hNcSXkt4LeHdA5BmCa8lvHvpmG+Q8D4dr/w7BiQv7NFQPJ2DHU7lSwdKuWx1mpue+DJ6DEIRF/ko4tkHAK1EoIbITw0YitbH+luN6FAnGyKvRj/GyPvlssbnxvwQy8kX7i7cMHNI5rL0FH/vNtkPGMzVwmGwvMCmXbNReYaRdOnqRmrZd0HMBXAq5Yr1rlx7xmafU4BaUQhhtVH50Pxou5NwjLlRs3W0xa3tHNo5fCLI1VardlxxqszEg2AmKR4DV06elZrpDwe/IHrJdEJ/9eTYn1M/gZBlcTozdg+tSN0uffit5sKRzK16ehsv6pMNmX6rkCmjOomxm/5PG7cfxBHi3vSvBCdY/DKgw5tMsPKkSrqxiSYXxDvcH+Hji3ibe0H8ilk4dhqVikx+33vhD0Dw8eB/S02c+KteAAA=" + } + }, + { + "ID": "81a2860f42cd0b20", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a80542ae2ad807dceb91bff7f2b5bcd4/9419692038377255166;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:14 GMT" + ], + "Etag": [ + "5y1PMWIz+mWfW9rhXZUg7g==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmm62:4449,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9qw8W8CsBsOUqwXjz4WoBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2T3W7iMBCF7/MUUfZyKc4PkAapF7CNKiQKVZqK1VYVMskQ3CZxaptl2Yp3X8dOJBbRitw4PjNn5hs7+TBM642UqTU0rRXJ3rfA9t8EXuVgdWQIBM7qUH/vPNwvJn+/F4v1ImCbn7+eMj+7uVFJRLnTbZ6AuKooE1f+wB2mWGAOYunazrXt271lzx4EbnDt9Hu+6ztLWz5d1enLFNWBQ76ekvKt7rMRouJDhHa7XTejNMsBV4R3E1qgdgD020UVo6+QCI5OuFDDxdElgEgBcnQhp0qLYA0MygQk7YdhmlaDMjl3TLVNihqlybiAS9tUO226ANAwD+oskw0UuGVbE8hTLnfPcqckKZa4qOH12tGa2FdKe4yjyezOqsVD55xnW/BTz2QWh3dh1MoFTZUchQ/hKA5vPy/GIDmtFYU/5tFtq/6P3/qPKqwozZvkoyLj+XwajmaW1g9qeTGa15fmoOQk472Aurqlr1cqU1pmMbAzkYjujoSEARaEljHRHE7fkxfj2UHPsz39a/2pCDuXMwi89nvKMRf3NCVyzPTzSu1U8Wg8DbWPJqpyrT49WsbB+AcFgMnL6AMAAA==" + } + }, + { + "ID": "0e95272190010a12", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "303" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4441aeb86c9c68bed922103e1298cde0/7811631701898163238;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF92aWV3X3N0YW5kYXJkc3FsIn0sInZpZXciOnsicXVlcnkiOiJTRUxFQ1QgQVBQUk9YX0NPVU5UX0RJU1RJTkNUKG5hbWUpIEZST00gYGR1bGNldC1wb3J0LTc2Mi5kYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAudGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMGAiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:14 GMT" + ], + "Etag": [ + "TeLIsCrBWBLP3VDqelX6Hg==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnku10:4450,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9qw8W-i_F4LLqQWIlamoAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SXW+bMBSG7/kViN1s0hoTQpMmUi+alG1INIkIaStVE3XhQL0anGCnKKry32sbkKYqrcIFkt/z9fg9fjNM64WUqTUxrSeSb3dQ7b8J/ETB+ilDIHCuQhEEPp9V07tpsBzcXm+B3g//5JeXOono6nRHExBnG1aJs9HQmaRYYA4iduz+hT2y3di1h2NnfNE/d0fOqB/b8uuJ+JVAHXOByxRXKd9S3ZEDzQJSvqi+z0Js+AShuq57OWM5BbwhvJewAnXA6NVBm4r9g0Rw9IEDtRwcnQKE9NU5+oRLR0PIoIIyAUn3Zpim1Y72j9mgyqTYjG4zTuBoyvS4pugIkGEetFfJMxS4Y8kI0JTL04M8aUmKJS4UrJXZsW6sWu83WvLnkffbCzu5YKmW5+sguJoGnqXkg/z9baeVu2K6F6AmWJpSKQEr8wiqI5GQ1f8JSQVYEFZGpOHpnw+kAQN77A7dgc6gmIsblhJ5jfTzrA7+1vfutKC86RzQL0JFV17gzSLzarkMF/fxbLGeR/G1v4r8+Sz6riz5Yf4KFzfm44eV9U57uWo3X6Y8NkvccQggx8l+JXc2MTNMObRmUpZoPxTtemUZB+MdRgoYGI8DAAA=" + } + }, + { + "ID": "62b56346ad71102f", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_standardsql?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c369ead1895e846306b4b3326003b4e8/6203570270202280271;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_standardsql?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:14 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnam66:4165,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9qw8W-mALMaqqQWRtJWYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "2229dc31dca46f11", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "14e9c13a62846c1680cc0698a5e2aa16/4667566428232646264;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:15 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vni65:4255,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9qw8W5CjOIT0qgXmtqDQBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "10d19a81d0b8e3b5", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c591a17125416e3d80f037223ca07fa9/3059504992241861537;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMSJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:15 GMT" + ], + "Etag": [ + "zJAb5aFX01KaL35ycMiu9Q==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneq9:4200,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=96w8W8CpB5eAqgXe7Ipo" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TXW/aMBSG7/MrIu+W4nwQaJB6AW1asaXQppk0aaqQcQ6p1yTOYjNGK/57HSeRGKITuXH8+rznPOdYfjdM9MqKBI1NtGLp7w1Uuy+SrDJAPXUEkqT10dvXycojtz8s+xsJXW9H79nGf7y60kFMu5NNRkFelLySF6OhM06IJALk0rHsS2tkDZYDa+g7/qXtDUbOyF5a6uvrSv8LsXUFAdk6ZMVrXedFylKMMd5ut/2U8zQDUjLRpzzHXQP4j4PLiv8CKgU+4sItl8DnAGINKPCZnDosgjVUUFBQtO+GaaIWZXZqTLVNiQ1KG3EGV2PT5RrTGYCGudezpC+Qk45tzSBLhNr9VDstKbEgeQ3frL1Gk7tSa09xNJvfoVrc9055Nrk49szmcXAXRJ2c80TLUfAQTOLg5vNkFdDjXFFwvYhuOvVf/M5/kGHFedYGHySZLhZhMJmjRt/r5dlof5/bQalOpjsJdXakB14rIS/SGKoTJxHfHgi0AiIZL2LWcNieqy7GtXzPcYfN0/pbsupUzNB32xtGGRHynidMtZl8nqnrKp5Mw6Dxcaoz1+r3J2TsjQ/B3U3K6AMAAA==" + } + }, + { + "ID": "e8eab8ddf78c3cdd", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0001?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "edcd9db4dfffa04fd3c26f51248db909/1451443556267853770;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0001?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:15 GMT" + ], + "Etag": [ + "zJAb5aFX01KaL35ycMiu9Q==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:15 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndx193:4244,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=96w8W6mNE8TtqwWv4qKADg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TXW/aMBSG7/MrIu+W4nwQaJB6AW1asaXQppk0aaqQcQ6p1yTOYjNGK/57HSeRGKITuXH8+rznPOdYfjdM9MqKBI1NtGLp7w1Uuy+SrDJAPXUEkqT10dvXycojtz8s+xsJXW9H79nGf7y60kFMu5NNRkFelLySF6OhM06IJALk0rHsS2tkDZYDa+g7/qXtDUbOyF5a6uvrSv8LsXUFAdk6ZMVrXedFylKMMd5ut/2U8zQDUjLRpzzHXQP4j4PLiv8CKgU+4sItl8DnAGINKPCZnDosgjVUUFBQtO+GaaIWZXZqTLVNiQ1KG3EGV2PT5RrTGYCGudezpC+Qk45tzSBLhNr9VDstKbEgeQ3frL1Gk7tSa09xNJvfoVrc9055Nrk49szmcXAXRJ2c80TLUfAQTOLg5vNkFdDjXFFwvYhuOvVf/M5/kGHFedYGHySZLhZhMJmjRt/r5dlof5/bQalOpjsJdXakB14rIS/SGKoTJxHfHgi0AiIZL2LWcNieqy7GtXzPcYfN0/pbsupUzNB32xtGGRHynidMtZl8nqnrKp5Mw6Dxcaoz1+r3J2TsjQ/B3U3K6AMAAA==" + } + }, + { + "ID": "a073bcd05b4bb6ce", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "288" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c79f42a7329804a535936f0c7b5c573a/18361902313031126258;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJkYXRlIiwidHlwZSI6IkRBVEUifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF9tZXRhZGF0YV9wYXJ0aXRpb25fMCJ9LCJ0aW1lUGFydGl0aW9uaW5nIjp7InR5cGUiOiJEQVkifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:15 GMT" + ], + "Etag": [ + "JXtGtkSPTc3Rgs3HlK7T2A==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfs12:4294,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=96w8W_j7HYKHqwWJpq2YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2S3W6bQBCF73kKtLl1vBhssC3lwlaiJK1bRTaVWkURWsOYbAwsZcd1Lcvv3v0BtWqcKFws2jNzZj6OODou2fIqI1OXrHn+cwfN4QLZugDSUyVAluvSp+94i9vVQ5wGy1wGd8XnKPZnV1emiRt3titSwMtaNHgZhf40Y8gkYOJ7g7EXecNk6IUTfzIejIaRHw0STz19TEq1QrcmNWuQIxdV4pmpEorNgldbPfsZsZZTSvf7fT8XIi+A1Vz2U1HSDpr+8mndiBdIUdL/WGjLIulHoKj5fEnfYTMdS9hAA1UKivDouC5p19+fi0PblGjXtx0fYLE2s86a3oBy3JPJLH2GknU8Gw5FJtXtUd2MpMSKlRrYvntWw0NttFW8vP96S7R46p3xqLWvPNez+MY61PHUciAv4aHD41XeEf01/eiQq105PyBoTGLDVcpCVHkMzZnKUuz/EdIGmF4Rcws4GAUqycCbjMJhaH/g3zVvzvWEk6DNlxRM4heRcZVX9vakjj2ezRc31idSM1mr31bEOTl/AM3d++dOAwAA" + } + }, + { + "ID": "1815281eb8754f27", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_0?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f4f5b366f95855c9596b70b87acfde00/16753840877040341275;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_0?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:15 GMT" + ], + "Etag": [ + "JXtGtkSPTc3Rgs3HlK7T2A==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:15 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncw1:4438,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=96w8W8G3LJXaqgWP8K_QAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2S3W6bQBCF73kKtLl1vBhssC3lwlaiJK1bRTaVWkURWsOYbAwsZcd1Lcvv3v0BtWqcKFws2jNzZj6OODou2fIqI1OXrHn+cwfN4QLZugDSUyVAluvSp+94i9vVQ5wGy1wGd8XnKPZnV1emiRt3titSwMtaNHgZhf40Y8gkYOJ7g7EXecNk6IUTfzIejIaRHw0STz19TEq1QrcmNWuQIxdV4pmpEorNgldbPfsZsZZTSvf7fT8XIi+A1Vz2U1HSDpr+8mndiBdIUdL/WGjLIulHoKj5fEnfYTMdS9hAA1UKivDouC5p19+fi0PblGjXtx0fYLE2s86a3oBy3JPJLH2GknU8Gw5FJtXtUd2MpMSKlRrYvntWw0NttFW8vP96S7R46p3xqLWvPNez+MY61PHUciAv4aHD41XeEf01/eiQq105PyBoTGLDVcpCVHkMzZnKUuz/EdIGmF4Rcws4GAUqycCbjMJhaH/g3zVvzvWEk6DNlxRM4heRcZVX9vakjj2ezRc31idSM1mr31bEOTl/AM3d++dOAwAA" + } + }, + { + "ID": "ec9c7bc00af40700", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "310" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "db6691a98897592b32d97f82434777e3/15145779445344458308;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJkYXRlIiwidHlwZSI6IkRBVEUifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF9tZXRhZGF0YV9wYXJ0aXRpb25fMSJ9LCJ0aW1lUGFydGl0aW9uaW5nIjp7ImV4cGlyYXRpb25NcyI6IjEwMDAiLCJ0eXBlIjoiREFZIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:16 GMT" + ], + "Etag": [ + "XmoCUMxtqmqJzx0jcCF8/Q==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbq70:4228,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=96w8W-nfNoL8qwXf556wDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SW0/jMBCF3/MrIvNa6iQtvUl94Lpi1a6gBAm0WkUmmQZDEqfxdNuC+t/xJQHEtivykMjHc2a+Ocqr45JnXiRk5JIHni6WUG0OkD1kQFrqCpCl+uouF6e30zUu8sXPl7X3FJ9eDOj1eGyKuHEnyywGPCxFhYf9XjBKGDIJGAWeP/D6Xjfqer1hMBz4R91+0PcjTz1tjHI1QpdGJauQIxdF5JuuErL5hBfPuvcjYilHlK5Wq3YqRJoBK7lsxyKnDTT9G9CyEk8Qo6RfWGjNIul3oKhZX9L/sJmKGcyhgiIGRfjquC6px1/uikPblGjH1xXfYLE2M86a9kA57tZkFj9CzhqeOYcsker0W52MpMSC5RrYfltWw01ptJtwdvnrB9HitrXDo8b+4zk7Ds+tQ73+1BzIc7hq8HiRNkQfpnu7GaxLXjFdNdWgxNc7102KZX6yQTC6CUIrE1GkIVQ7bmZi9UmIKzBtQ27R/aOOyrjjDXsqXvtrv4/+UtMbdurkScYkTkXCVZLJ/k7NVuHxyeTc+kRsOmv19oY4W+cN6eurm2gDAAA=" + } + }, + { + "ID": "8f4bb8839cf3ba84", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f1297038a2fdc2d620c63f9bcb1c21ea/13609775603391601261;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:16 GMT" + ], + "Etag": [ + "XmoCUMxtqmqJzx0jcCF8/Q==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:16 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnax142:4112,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/147,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-Kw8W5CkDoefqgW1kLW4Dw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/147,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/147" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SW0/jMBCF3/MrIvNa6iQtvUl94Lpi1a6gBAm0WkUmmQZDEqfxdNuC+t/xJQHEtivykMjHc2a+Ocqr45JnXiRk5JIHni6WUG0OkD1kQFrqCpCl+uouF6e30zUu8sXPl7X3FJ9eDOj1eGyKuHEnyywGPCxFhYf9XjBKGDIJGAWeP/D6Xjfqer1hMBz4R91+0PcjTz1tjHI1QpdGJauQIxdF5JuuErL5hBfPuvcjYilHlK5Wq3YqRJoBK7lsxyKnDTT9G9CyEk8Qo6RfWGjNIul3oKhZX9L/sJmKGcyhgiIGRfjquC6px1/uikPblGjH1xXfYLE2M86a9kA57tZkFj9CzhqeOYcsker0W52MpMSC5RrYfltWw01ptJtwdvnrB9HitrXDo8b+4zk7Ds+tQ73+1BzIc7hq8HiRNkQfpnu7GaxLXjFdNdWgxNc7102KZX6yQTC6CUIrE1GkIVQ7bmZi9UmIKzBtQ27R/aOOyrjjDXsqXvtrv4/+UtMbdurkScYkTkXCVZLJ/k7NVuHxyeTc+kRsOmv19oY4W+cN6eurm2gDAAA=" + } + }, + { + "ID": "0502943bfde71269", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "325" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f2b3dae25a7fae35e62808eef4eb2a69/12001714167400882069;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJkYXRlIiwidHlwZSI6IkRBVEUifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF9tZXRhZGF0YV9wYXJ0aXRpb25fMiJ9LCJ0aW1lUGFydGl0aW9uaW5nIjp7ImV4cGlyYXRpb25NcyI6IjEwMDAiLCJmaWVsZCI6ImRhdGUiLCJ0eXBlIjoiREFZIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:16 GMT" + ], + "Etag": [ + "jja5ujSKsZr/Ns0LdUYjuA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncf127:4229,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-Kw8W4GgH5DiqAW31KGwBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SW0+rQBSF3/kVZHytHaAtvSQ+1GiMsRrT4oOenJARdnEQGJwZrI3pf3cuUE881cgDZNastfe3d3h3XPRMqxTNXPRIs5cG+PZIkscCUE9dgSSZvspzMmry1ZV44PhGeIv07j5v5icnxkRNOm2KBORxzbg8HofBLCWSCJBx4PkTb+wN46EXToPpxB8Nx8HYjz319GVcqhbaGteESyopq+LAVBVQrBe0eta1n6SsxQzjzWbTzxjLCiA1Ff2ElbiDxq8BrjnLIZECf2HBLYvAv4HCZnyBf2AzjiWsgUOVgCJ8d1wXte0vD61Dx5Ro27eOX7DYmGlnQ99AOe7O7Cx5gpJ0PGsKRSrU6Y86GUmJFSk1sP32rCa3tdFW0fLy5gJpcdc7kFFt/8uczaNzm1Cvvy2HpCXcdni0yjqiz9C9nQzeasqJdl1rUOTvZzbw+6Zt3aopT7cSjNX4tLJgVRYBP3CzZJt/hISD6RRRO40/Gqi1D7xpGA4n9m/f03zxhNNBC4YKIuQ1S6niS7+v1A0azU8X5zbHElNZq3cr5OycD+UzMXh7AwAA" + } + }, + { + "ID": "4e848b0f502a7f4f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8dd7d5f137ce482a77d530ea817a6dfb/10393652731410097342;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:16 GMT" + ], + "Etag": [ + "jja5ujSKsZr/Ns0LdUYjuA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:16 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbh141:4349,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/137,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-Kw8W6i_LIjXqAWdypTYDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/137,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/137" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SW0+rQBSF3/kVZHytHaAtvSQ+1GiMsRrT4oOenJARdnEQGJwZrI3pf3cuUE881cgDZNastfe3d3h3XPRMqxTNXPRIs5cG+PZIkscCUE9dgSSZvspzMmry1ZV44PhGeIv07j5v5icnxkRNOm2KBORxzbg8HofBLCWSCJBx4PkTb+wN46EXToPpxB8Nx8HYjz319GVcqhbaGteESyopq+LAVBVQrBe0eta1n6SsxQzjzWbTzxjLCiA1Ff2ElbiDxq8BrjnLIZECf2HBLYvAv4HCZnyBf2AzjiWsgUOVgCJ8d1wXte0vD61Dx5Ro27eOX7DYmGlnQ99AOe7O7Cx5gpJ0PGsKRSrU6Y86GUmJFSk1sP32rCa3tdFW0fLy5gJpcdc7kFFt/8uczaNzm1Cvvy2HpCXcdni0yjqiz9C9nQzeasqJdl1rUOTvZzbw+6Zt3aopT7cSjNX4tLJgVRYBP3CzZJt/hISD6RRRO40/Gqi1D7xpGA4n9m/f03zxhNNBC4YKIuQ1S6niS7+v1A0azU8X5zbHElNZq3cr5OycD+UzMXh7AwAA" + } + }, + { + "ID": "e9f21351e5775b89", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "caf635527e4ced167609756c60ce824a/8857648889440463335;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:17 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlf1:4425,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-Kw8W6HlPNLtqgWJopWwDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "39368b14164762c9", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2ac0251e45ac95e9157dbeb996c1d866/7249587453466455312;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:17 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnoa21:4387,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-aw8W_mcE8r0qAWI7IHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "4822c8ce73da37b9", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_0?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f3fe514b9942193135375655b5ac9de0/5641526021770637880;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_metadata_partition_0?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:17 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlq25:4400,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-aw8W5HxHoHdqAWszJ3oDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "b8394e01db7fc671", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0001?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8847aaefcba1e9378433e8946c9a32f0/4105522179801003873;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0001?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:17 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnau23:4246,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-aw8W6mtMoreqgXKwp3ICQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "b88f0e02c2eddb7f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "112" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "163ccc7743f0b85d60d35b9da7f68753/2497460743810219146;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkYXRhc2V0UmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMSJ9LCJmcmllbmRseU5hbWUiOiJuYW1lIiwibG9jYXRpb24iOiJFVSJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:18 GMT" + ], + "Etag": [ + "2Oby67IajER07P0ddj4gjg==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnll65:4019,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-qw8W_hVkv6pBa2YmWg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RX2vbMBTF3/0pjPfaxLLl+E+gsJWZEdiaYTryMEaQpRtFiWI5kpwQSr/7LNfdQ1nbPAl07j2/o6NHzw/2omHB3A9qwY8d6MsnRiwxYIObXgRLuBPjZX1JswXZlRXKfiLGdgnf8dvbYUgM+6yTFOykVdpOsjSejzbrGEU5ylCyTlBaxEUezZIszqI1Qiga1g3IzXfR7J3J1trWzMPwfD5PuVJcAmmFmVJ1CF/yhac4bLXaAbUmfAUNR6gJr6aPgxVsQENDoU/x6Pn/7hfPb7vSzQ/GZIv/VRJ4/pNDbrSAhsnLPTk4XNC40wmEUjCmv/rdW7kUfqCVHGZW1eKhrAZG31gLVBD5TauudeIIXWlhQZseM3BeOSxX9x8ZLM/NdfudAX13KQ9ESCfhNMVFgXGUzCZ1lFsUqYMtSJwVe4o55eK4Pe5lXqt6231mcAKpWtBT3rucBIX+2aprrPvlN9hV+eXrR+ErIOwlvef/cX1SDcQK1TyI56KjGe5/D6MixxgPjUti7A/FxEYAe2dK0cHHqeWvwHvy/gL7CS07OQMAAA==" + } + }, + { + "ID": "87c5b76ed564c57b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0001?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "50d7cd8e4b7c9ac670923d8c6f2d2b25/889400407314284979;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0001?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:18 GMT" + ], + "Etag": [ + "2Oby67IajER07P0ddj4gjg==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:18 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnh126:4324,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-qw8W7CZHIW5qgWBiYYg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RX2vbMBTF3/0pjPfaxLLl+E+gsJWZEdiaYTryMEaQpRtFiWI5kpwQSr/7LNfdQ1nbPAl07j2/o6NHzw/2omHB3A9qwY8d6MsnRiwxYIObXgRLuBPjZX1JswXZlRXKfiLGdgnf8dvbYUgM+6yTFOykVdpOsjSejzbrGEU5ylCyTlBaxEUezZIszqI1Qiga1g3IzXfR7J3J1trWzMPwfD5PuVJcAmmFmVJ1CF/yhac4bLXaAbUmfAUNR6gJr6aPgxVsQENDoU/x6Pn/7hfPb7vSzQ/GZIv/VRJ4/pNDbrSAhsnLPTk4XNC40wmEUjCmv/rdW7kUfqCVHGZW1eKhrAZG31gLVBD5TauudeIIXWlhQZseM3BeOSxX9x8ZLM/NdfudAX13KQ9ESCfhNMVFgXGUzCZ1lFsUqYMtSJwVe4o55eK4Pe5lXqt6231mcAKpWtBT3rucBIX+2aprrPvlN9hV+eXrR+ErIOwlvef/cX1SDcQK1TyI56KjGe5/D6MixxgPjUti7A/FxEYAe2dK0cHHqeWvwHvy/gL7CS07OQMAAA==" + } + }, + { + "ID": "f735719534a515c9", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0001?alt=json\u0026deleteContents=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1ae7ded7bc783c56aa2f08ada723bef1/17727801570073118172;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0001?alt=json\u0026deleteContents=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:19 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncp68:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-qw8W9mpLtD5qQWHgJDABA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "b88d6f82c0423ae6", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "9d9b6774865b27db1e2a2acffe9c08ce/16191796632903600644;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:19 GMT" + ], + "Etag": [ + "gdkGeCWv06Kx9xOvuZE4PQ==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:19 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vna15:4288,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/38,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-6w8W9D9BYeLqwXIz7qQDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/38,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/38" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2Ry27bMBBF9/oKgd3G1oOyHgYCJGmNwGgbt0oKAy0Cg6bGMitalElKThDk30OqShdBHuaSd+bcOzMPjosqVhdo6qI1K/ctyPtPBdFEgUYnRgRNSiuWRXUJn5edH3+9y+4WXft7Fv34eXraF7G+v2g5BT1qhNSjJA6nA2YV+kHqJ360ivw4C7M0mERJmAQr37y+XQHffGN1ZSFbrRs19bzD4TAuhSg5kIapMRU77zmf14VeI8VfoFp5L0y9wVR5R7sPhTlsQEJNwaR4cNz///N/sx1Jc9GQbP7aSpDjPlpLQikoZQr+mA5r5iIpuHVGy3x+M8t7lFlMA5QRfilF21hxYC8l0yCVofW4F4TF8uojwOJQH9ffKpAX97MdYdxKOI5xlmEcRJPROki1H4idzkiYZBXFJS3ZfruveLoW6217VkAHXDQgx6WhdIyCGVu0tbbHfMM7n51/+Sh8DqR4Tu+4t3afVALRTNQ3bNdjggk2R8J+hlMc9UfmROnvomAbBsU7VYL2HKv+ukbOo/MEcvD6JSADAAA=" + } + }, + { + "ID": "0f06927f594aa367", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/does_not_exist?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "10fbb6bed22ffbbdd94df7db9cb84ce0/14583736296407666477;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/does_not_exist?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:19 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:19 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmr22:4414,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-6w8W4j3D8OHqQXd0p2oDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2cX08bORDA3/kUq2gfclK6SkISIFIeaAAVqdBeoLqHtkLO7hD22NiR10uJEN/9xmtvulmcP0BRq9OoUok93hnbM3ZmfpA87Hg1kFLIWt972PFsI8XWV2zlXdgZiSmLOXbWJokYs6TWMP0SWCryfi7Uich4VEimkKZsAlp0LpR3rWV974gploLyoiwJQb2bCane7fXa/UhAeoUqruA+TlWhI4JxNjnl10Jr+Xr+6fLq5NOX86PvDW+U2+17oYhgsBB41uhgOzMeLybm5ZYGPEsSL1//ZymUGJhV971vtTARWRTcQBLfB8d6wFEu+lb7xvUU9JDFLHQnk5NsClxpgZ1MtXv93PRo/KeYwjVOg4kQkwSC8jSweyp4cHwfwkzFgqf+52ycxGEgIRWZDOHceqT+c0jwL7tj/Xar+9fb6d7foDsFeQcymIJiEe5MYLfnUrLwFuTFjHGO4oRNxxHzJ6Cs/DCd89Bv1d3Dc9utbtNl26wlU3GCr3mYSYkeCA7HqUIdWhFPr4WcnmQqk+DndiqdQSQWPfUVDxbLb//2KTRbr55CIDO+3kqrtbe9lTMh4fgewkzhzeIfxRJCVbQDyF9AfWmQMdJpvmA37Syt2o94lgAjpF4Rm0U02wcvNoCjZgngxF2a9/de4ASrAKPbqbN30Hl9bFWdicZGkGaJ2hBUHeeV8auDqvcMf1NQ/bagGjIV3sR84vBrRZTbONglr/4JXk3tm2WMswlG4scwk6mQ5rnUv0CvJYDdVtEN41ECR4KDGVevPmE3puPy7npTxZQrgiDMG5jbRfMV1jq9TcYuMfoWzwYhS5IxZgqjTI+oLwuNxo5r+9eoZGGIWabfbDZd6nquKFmtzW/p03PKh4IruFcujbsHrqtX7x86LNB5ENin/XJDr5iNEzAW6mVRsZWuE1PoHZqAK55YmmRFZmbZduVezlmW3gIWnUPrJ2PolN+AjBVEVnouvnAJ145F7LZd3vtFZl32Wq5o39oZq1zRdUUNBxUwnQoGpzyJOTy5BivdZoKuYFkTzTG/E7eQH7liM1xBiKfc5d81imeZM5pb3fYzj7Dg+g5yT+rA5X29b3IW7gbob2DTYDQLz0xdWKzQP8XNl5wl/mHEZviyMPJ06OL2X2fI3HKz8CI3WCgvAqGXe32V2BrY33fd2NtZCIBHRrDJSnd3VaBtYcWESrExaSHeZPPA6fCX2dy4i21X+G9py17se3ixrzfTe7kRv7tNMOw5MyZtAe7w4p0yjhEqK8nAJUtv/Qt9XcT6KI1KSdnPATab77kO8xb6l1K9qtaW8y5+ovVYN85M41TvFerUSupPBLnWrjOV2aw1tjuL+/CPkLcgPwoxW2Gj1XSW7VtNfaPy/YOtdBs9lzcYFZFGXhXdT8S2YHrRlvtlbblPV+zL/l5e5fOScjaLC4QTYhoenDAsHvve6XSaKf0WZ+gYXMc81ljoYczSEqFreCFTMBFyPvhycTy6Oh6NPo10Z4ajNH5reAvoNyDiR8SPiB8RPyrjifhRUBHxI68S8SPiR8SPiB8RPyJ+RPyI+BHxI+L3FsSv4RkQNjB/7dfwMFLxyoHoAyQzy+lulJp9QG0g08HDo2lf4LyydFD8LWDDW/gkp3cD8+OhgGLmwTL8y+neCvQWlCkirgPftt/P32soxKOBkhlYdmjvc8KHhA8JHxI+JCZA+JCCivAheZXwIeFDwoeEDwkfEj4kfEj4kPAh4cM3woc/mdhwCbiVJYaIraJ9De86TtAjEBU8zxDCRIRM4yPbnC4J9bvmFKLDBV/8aulPYwPZ+/64UvOzP73c8MynoksYFM95vg+dZufRe7bCvkcok1AmoUxCmcQnCGVSUBHKJK8SyiSUSSiTUCahTEKZhDIJZRLKJJT5FijzqYXqB6BzJDYUSYLZOt7OSuSfiK5Xuo0/Xe4s6cNUD4+ivZalSOeYPqZzq4njCN1fGHCIzAl1wYmNRj7kGa/023naWC912WPTdbGt16vd3aA23+K8EtAXoc4jSoVXpcssn8roP6LgIh+SD9cpVOYCNvewJdqpX2piUbFcOiyNLN52XfnVs0qSUv3pqE1c2U5pEfbHMN/7MxFlWHMuyisp5ziLIuwucGgcQrXoLT9q3eus86nm/d/XvJMUFplbcBRjWajCm78zyMCdtzwZUlxseso1/Q3Kj/jfd/1dyjX9i7pa3+s0O3nzVV/OvIN6H3f+A+vb/bk4WgAA" + } + }, + { + "ID": "376b309fc7e24651", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "74" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a5963e792901a15e47f5c02d298cb89f/12975674860416881750;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkYXRhc2V0UmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:19 GMT" + ], + "Etag": [ + "uHM2rcKg+0nxVNEu9su6BA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnid10:4261,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-6w8W9ncHYnsqwXJ4IaoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RX2/TMBTF3/MpIvMIbZy4TeJKk9hEBRVsk8KgDwhVrnObmrpx6j8t07Tvjh0yHiZgffW553eO732IYrQTbY1mMVqL5uBA37+qmWUGLHrjRbCsCaL7cJ1p/rF5jdufX2/mjhqXX11eXPRDovfXTnKwo05pOyrybDZgVhlOS1zgyWqCc5rRMp1OiqxIVxjjrLcbkJtPot0FyNbazsyS5HQ6jRulGgmsE2bM1T556pccs6TT6gdwa5JnockQapKz04fBCjagoeXgWzxE8Z/3xe+/nUmL0dBs8beVoCh+DJGMczDGD3zzjhAWI61kSEbLanE3r3qUX0wHXDD5XivXBXFgL7WwoI2n9bhnhNvlzUuA21N7nt8Z0Ff38z0TMkgkzwmlhKST6Widlhanam8pywq646ThjThsDztZrtV6697WcASpOtDjxlOOgoP/tnKtDcf8R3Y1v3z3UvkKWP3UPoq/h31yDcwK1d6JfY9Jp8QfiWBKS0L6I0tm7LWqxUZA/Z8pxXtOUL98RtFj9AuromjvIAMAAA==" + } + }, + { + "ID": "db72b0d5d4c3540e", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0002?alt=json\u0026deleteContents=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "91d1d71df5c3de2d74eb1e3c412ff786/11439669918969174143;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0002?alt=json\u0026deleteContents=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:20 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnq142:4003,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/33,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-6w8W-rTOsK9qgXdkLCQDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/33,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/33" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "2bf249801d72af85", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "74" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "707c92e41753420a57bbfb8252180f15/9831609582473305511;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkYXRhc2V0UmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMyJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:20 GMT" + ], + "Etag": [ + "Hkt83JfMhkbOb07CeE0E3w==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnl184:4023,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/103,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_Kw8W6mQEpHfqAX2jqjIAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/103,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/103" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RUW+bMBSF3/kVyHttgsFJgEiVtm5oy7Q2Em2Vh2mKjLkhHg4mtgmqqv732YzuoWrXvPrc853jex89H9W8KdHSRwWvjh2ohw8lNVSDQRdWBEMrJ36rTUK+7673dbEucPwZMpyR/vJyGOKDv+wEAzNppTKTeBEtR8w2wmGCYzzbzvAijdIknM/iKA63GGMy2DWI3Q/e1A6yN6bVyyDo+35aSVkJoC3XUyYPwXO/4BQFrZK/gRkdvAgNxlAdnJ0+DuawAwUNA9vi0fP/va/+/u1Mmo/GZqvXVoI8/8lFUsZAazvw0zpcmI+UFC4ZbfLVXZYPKLuYFhin4quSXevEkb1R3IDSljbgXhDWm5v3AOu+Oc/faVBXD9mBcuEksliQNCUknM0nRZgYHMqDSWkUpzUjFav4cX+sRVLIYt99LOEEQragppWlnDgD+23ZNcYd843sPPv05b3yOdDyub3n/3L7ZAqo4bK544cBE86JPRIJMY7JfDiyoNpcy5LvOJT/mZJs4Dj1/hZ5T94fdRWiGiADAAA=" + } + }, + { + "ID": "bd0e27b7fc996802", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0003/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "149" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cdb685a003fbb533d0da3ab17192baa1/8223548146482520784;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0003/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDMiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:21 GMT" + ], + "Etag": [ + "mBc2jVHDQn3SQCLUEb5Dlw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlq25:4400,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/174,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_Kw8W-G-NMPvqwXStL2oCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/174,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/174" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2QTW+CMBjH73wK0l2nLUVFTTzMabIl7ODbrgbLI6tCy2gdMcbvPlowWRa3cP2/9P97enFcdOQiRmMX7XjyeYLi/KCjXQrosbJAR4mxsimjh/eX2UL4q8VzuJnv+rO0nExsiNt2fEoZ6E4uC90JBnQcRzpSoLeUeEMSkN62RwYjOhp6/V5AA29LCPG7dum/CLULCtJ9yMXR7HxonasxxmVZdhMpkxSinKsukxm+HYC/KM4LeQCmFf7FhRsuhdsAYguocEtOG1vCHgoQDCrai+O6qEF5vfdNplaJNUqTaMFV1+xcXWoB6LhXwyhO2fSsQZkWQY0SSpGsobjjLGX5Q2AFRJpLseaZOQ95fb/a8z3iEX9kE2mk9JuM+Z5D/HdKn3PrrJ+m4bzuSWZfNupmhZyr8w0+QYdvmwIAAA==" + } + }, + { + "ID": "6e56c853c8b12f19", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0003?alt=json\u0026deleteContents=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "895a00e956cd2fc75955b74e3118266f/6687543209312937977;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0003?alt=json\u0026deleteContents=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:21 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:21 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnce185:4121,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_aw8W-jvB8SNqgXr0IDoBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dW2/buBJ+768QDD/4AF5BvsROAvghTdxTA0036yS7OGiLgJYYWycyqaWonuQE+e87vMiRZfqSNmlTYBAgFjmjGQ5nOBzqS+T7N16NCsFF7dC7f+PZRgatT9DSXdAZ8TmJGXTWpgmfkKTWNP2CkozrfkEznouQjthlRgvynGYZmVJFPyGSZFR6UZ6EVP6WciF/6/fah5Hpv2oHrf2gH3SvukHvoH2w39rr9tv91lUQBB0vzrxMxknixczLH8VHdJJPR+yaKwWfPv5+cTU8Pbv4z5emN9bjOvRCHtHBguDZ8QxYDrL03eZS23wmuOQDY+mh97kWJjyP/BlN4lt/qBhONOlz7TNTYhXLQrLqJGKazymTimCtrXZ/i/FKBvxIIsGauT/lfJpQvzw46J5z5g9vQ5rKmLOsfpZPkjj0GZfDeSrvGo8k/7/kKzns7nX/9ewyey8hs79FZkbFVyr8OZVETaZvJ/5CkPCGivOUMAbkiCZUUktruHm0wk5rmxFVhadwsSywos3BYH3wRE1nyS2E6O2dlZLVl6UW5Ir66l1Geeug90Tt58CbjenfOc3kBz6dxmy6GIhtV6xMyHwSkfrScOr9xgZB1gdB6xnHZmkhFxHcIvTF9jHAcvz5Y9h7uTGQMIRUWO8EwQ7jaLdfPFaWY3brkNoHO+YFwbO7IiecaB3uuBwxoDCS1INGhVvrOzhwqTPpLCGTDK5ZmAsBad4fUynuYMTHJAGS4oOLxkqvDfVty9AqGafhJWyAPr2lYS7pX7GcaYmNgmA9tWPEuKbFOR/u2dhxfW7V4pTefYpr36seK2WsOoz8xkq/Ft17XFFZzmAhXic0lP6/KeR/ImkEqXrGoyO9NrhodzotP2Zf+Q1tXLIbxv/HvHNd47iEKBumRIKLl6WM5mlSSNnIY4zvFLJVE2KLTRcazD2FLNsydx3s7xZGes7UfNjFpXeGesuGaKXfym5ti6k1sk9ImlLxnvObdySUXNzV1RoG2y1dUVYUr9xkBrG/LQNttK+90b5O59tk/0nE/8vWOe2pMNlVv2v19SLmlJdPScZRHsWybM9qVFRZrDXfFXsv45tjQaNsszFVFqOxvy21/QTPHHNIxrdbfLPKZFPetmz6E7wDW/pmY5YZjLa9VxhmRoZNyG7Ze/2lHV7tGKmm1jVTp1PsWO4dZs14UnVUNb/bJI2XtsF6uXEOIRWHtG4/z4ggc7XVZvVW4M8IixL6NuHhzWNaXto6bVnRawXbwujHj8h1fGLAINKw48dqR7XrCUqkQuJRmsJ5k6gT5nutSliVharNvKb2dAXiOsXn+qqopuwi9UXOgDZiJRWNtcw2jFwO2Kz1eKYOtgmsL9hUk7tGlWC31r6rtn0Wye7cY5ZSrorWUtV8NMkklAfqQMCyay7m73KZC1qvtP2IL3oaa+6xbuo665Yfpb3z/dpVmGzW0mq5ZliaMstX5Ra1UVQvN8Y5Y/oMAgpGzHY2yhzFEwrXGttZPISHssAht+cq55xyS+YvOtURagKnQTP8EZtREUPxbqkf+SWDitmhttN2pflnUuvS19pZ3zrvOJ2y4SxajaxTLuhQHxk5JNg965GlXhtJwZ7rJPqLh9KxmYZS2n0cY4VmI8S1an9AYLqqzl84MPXZtRqLFzNBSXTGeVLEnhL0Fxc3VDQcRJvhuk+VWjcizSjXiO21u0tS9TnbMJfus0eCrt7wWWmqVIFj98QQ1pL/juSJPPRG83ku1QwZtIBexyxWe/v9BCqXwejjn0cfRidXZ0fjo9PhxXDc9GDrp1OocAeX58Px1XA8/l135plBKJreAuAYILqB6AaiG4huILqB6AaiG4huILqB6AaiG4huILrx2ryD6AaiG4huILqB6AaiG4huILqB6AaiG68d3Wh6BhgYmP/uaHpgN2URjaA4SS0WMZMyfQ/3QTEzuH8wbfUoK88GExLZEqPpxXaDNxjFwHzcFzCBubUMcWgMYw0Y4S8QCOCDMYPf3969VY/KWDSQIqcWITm1wAeCJAiSIEiCIAmCJAiSIEiCIAmCJAiSIEiCIAmCJK/NOwiSIEiCIAmCJAiSIEiCIAmCJAiSIEjy+kGSR4zgeAlUKFMMQrAO0Wh613ECm7t60PCIQzQ9KDh0DWCb8yWimvQ5jY4WGMonWwk1v+ltWl8e1up7zpd0NT3zcrDB0pvBoDcN9ex1g+DBe06Fhx7iP4j/IP6D+A/iP4j/IP6D+A/iP4j/IP6D+A/iP6/NO4j/IP6D+A/iP4j/IP6D+A/iP4j/IP7z2vGf1fmqvgdMQwTHPFFnddAjuX4xWKPSbXZz1/mvJE9AKeKTiKSyfBywkhhwqP5CgYO0vvLfqsSWOuWCebn6cdbh3y/WeYqsTrF2fvEErbRJVrqsi781TZ7EAny1CBj7yM2VNbvBE5QUS9cO0or9EGdSPX5qVMg2K7efkOwrEoAr1ScLl+R990F3J8HqwZ1LZu9gS1ijD9GHKwKlSbUm4y5QglJTHQSXdp4lzuLB90+oXkpG2I9jPfenPMqhZFnszkI/+C/Crjj/tirlSOlW697ud9mEJdMvVTKVpE4z6hf/SOufxFlKZDj7I6c5VTiBLW9KxcoKS5HY1JBr6svSHuDXF/W1aTX1hwu1Q68bBLr5Ul/R9gZUPrz5B3LVmYVebgAA" + } + }, + { + "ID": "d5e3e2340b667d62", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0003?alt=json\u0026deleteContents=true", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1fb672052e9251947b4930f7c2d0b365/5079482872833780514;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0003?alt=json\u0026deleteContents=true" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:21 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnh126:4324,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/98,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_aw8W-GqEYmRqwX_oKSIAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/98,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/98" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "4d8a01bed7c00caa", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "217db240453baca8d8a2a4711ff06d8f/3471421436843061322;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:21 GMT" + ], + "Etag": [ + "gdkGeCWv06Kx9xOvuZE4PQ==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:21 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmd16:4448,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/145,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_aw8W4mmKpXqqgX76JDIDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/145,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/145" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2Ry27bMBBF9/oKgd3G1oOyHgYCJGmNwGgbt0oKAy0Cg6bGMitalElKThDk30OqShdBHuaSd+bcOzMPjosqVhdo6qI1K/ctyPtPBdFEgUYnRgRNSiuWRXUJn5edH3+9y+4WXft7Fv34eXraF7G+v2g5BT1qhNSjJA6nA2YV+kHqJ360ivw4C7M0mERJmAQr37y+XQHffGN1ZSFbrRs19bzD4TAuhSg5kIapMRU77zmf14VeI8VfoFp5L0y9wVR5R7sPhTlsQEJNwaR4cNz///N/sx1Jc9GQbP7aSpDjPlpLQikoZQr+mA5r5iIpuHVGy3x+M8t7lFlMA5QRfilF21hxYC8l0yCVofW4F4TF8uojwOJQH9ffKpAX97MdYdxKOI5xlmEcRJPROki1H4idzkiYZBXFJS3ZfruveLoW6217VkAHXDQgx6WhdIyCGVu0tbbHfMM7n51/+Sh8DqR4Tu+4t3afVALRTNQ3bNdjggk2R8J+hlMc9UfmROnvomAbBsU7VYL2HKv+ukbOo/MEcvD6JSADAAA=" + } + }, + { + "ID": "0cab470448b3e46c", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "41" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "18b4376d90adb9b9911cc7ba63859833/1935416495378576755;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXNjcmlwdGlvbiI6ImQyIiwiZnJpZW5kbHlOYW1lIjoibjIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:22 GMT" + ], + "Etag": [ + "ATh2tJlA/WCqLf8NoxzlWA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnap199:4017,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/120,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_aw8W_m2NIrUqgXGyaXoBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/120,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/120" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1RTY/aMBC951dE6bWQxA4kQVqptEUV1ZaVUqocqgoZZxJcTBxsB0pX+99rJ6EH1GrxcWbeh997dlxvz+rCm7nellXHFuTlTUE0UaC9t2YJmlR2OV/vkP7M537+4fhYJivx6zfP5w8P3RHr8EXLKehRI6QexVM0G2g2KAiTIA6iTRRMU5Qm4SSKURxuAvM6uAJePrJ6b0l2Wjdq5vvn83lcCVFxIA1TYyoO/tWff0J+I8VPoFr5N6L+IKr8u9WHwwxKkFBTMC6eHffvfNn/7U421xucLf8Viee4L1aylAzqgl9W5GDlvBr1TkBRyRrNRN2B+ymhFJQyg++G3jpzPSl4h8uz5XqRdbomxQYoI/yTFG1jl4ORXDINUhnpTvuG4SlfvUbwdK7vw7cK5PvL4kAYtys8neI0xTiMJqNtmOggFAedEhSne4orWrHj7rjnyVZsd+27Ak7ARQNyXBmWE6Ngvi3aWtvm/6OdLeYfXzOfASmu7h33h82TSiA24jXrww8n2DSKgxQnOOoS50TpL6JgJYPi5ioMUIT7Xrig5FrVt6+e8+L8AQcpzhhNAwAA" + } + }, + { + "ID": "e292967c5e21521a", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "39" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "gdkGeCWv06Kx9xOvuZE4PQ==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "61f379c5f4c91ba38eb74f5aaea362fb/327074688200899228;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXNjcmlwdGlvbiI6ImQiLCJmcmllbmRseU5hbWUiOiJuIn0K" + }, + "Response": { + "StatusCode": 412, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:22 GMT" + ], + "Etag": [ + "ATh2tJlA/WCqLf8NoxzlWA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:22 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnas187:4257,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_qw8W5HlIofVqwXB_qHIDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1X3U8iMRB/96/YEB64hGsAUU+TfQDEHIlft2J8UGNKd1j2XNpN2zUa4/9+0+6ut2BPIF7u4eILMNPp7zdfLZ3nLa8GUgpZO/Cet7xCUChdo2RVqAzFnMYclbUoEROa1Jq5XgJVwuqZ4GGsY8FPhT4BXRrMQSkagbE4l/Bq5B3ROIGwtEoEo0Y9fkqt6QxoCHJ51ayMpl9PqGazci2ESRaN+FTkPsxJJESUAKFpTBTIB5CECQnkiGaJPvD6WZwg8vOEKvDPg+Hg7PRwNB6dnd4d9UbHw8Omh1QQCfnkX14Mg7thEJwFTS8P389jb3ozrdPv1kXlP7/k8oWmOlN+Wgkyj7HpxVyD5DQJbLL80geGydMQ9p/6lN0DD/0pTRQ0vYxzOoewJ6NsDlwr//oWOcok+HluFCkz0fSKHPuOBDe9vEL+UnlQn7KBCMHvtjsvnmPnDb/BiLx3UmrDYpBqAppGZFACEDYDdt/4Lf+kD/SgvbP3ZWPM4ZhicQuVkDly1VnVWDaxZDut7Y+TpVIwzGwAKkUicDN1O+2NmHpSZDyswFxJmqbwlu2Phpa2032fVeGSIqYnVV8IrTRurlfA7EqAeZShg3txX8G4v79RoK9kpkr1pWhMVyyTLmwoemZvs55ZyUmZoay3Wi0n3YZds4qu3iaMJomTqtv5Z1Tb6yQx03FCBghBJwkcZTpDncx4Y0llEXddvuPPOWbY4uDxZJmUeHmREwQfPgLLsOlU/TDGs6tLmYD9AY0Fo+JctTYg6U1MozJdOFnAHsdKAwfZWFouat1x9fN6BGiVJoCOu5C/7bluhPWAFWgn5u7+ivP+WcPPGn7W8H+pYQVQz/ABF5Kx/RpL81LE+lXEmEeBeTEiriVZtCyS3Nl1sJjocDcxtoCvNQ2Pul4VqrgjXigbVYuiR3Z2VrxG8q+Bzf2JCLME6gXCgEr5hF6UbXeBpjED/D9bIK1uLcrb/VBMOYErGGdFStxB3i/ljkUfF9cs2nanta6XZb9VlaZjJljJnGjEZyBjHBmK1VNxySVMHUFsO1+lf4nWxdd2vZw2ajBnX7muhkgVDyOcqMhhrFIzBP3IIIMR3gb1KyGx8fNTYHHfmJQXm3G5ZqbIF/y4NfMkDpChmT9xJrLiiuF1C3e+bP0C0rGXpkEPAAA=" + } + }, + { + "ID": "2496fa334be5ce30", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "37" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "ATh2tJlA/WCqLf8NoxzlWA==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "70afea5f483ce3e3d30d27a73961247c/17165475850942955461;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXNjcmlwdGlvbiI6IiIsImZyaWVuZGx5TmFtZSI6IiJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:23 GMT" + ], + "Etag": [ + "7/5GhaMhwLsPmEbXKUyftg==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnww67:4387,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/124,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_qw8W6m5LtD8qQWEjabYCA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/124,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/124" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RXWvbMBSG7/0rjHu7xB9yYjtQ6MZCCevH8FoyKCXI8rGtRbYcSY4Jpf99kuP0Imw0upPOOe/z6j1vlu1saZM7C9vJaLnrQByucqywBOV80UVQuDTFyJ3dVvi+6u/kz3qZ/f7xfChUeX09NNFhPu8YATVpuVCTaB4sRplN4PmxF3nhJvTmSZDE/iyMgsjfePoM4xJYcUebrRGplGrlwnX7vp+WnJcMcEvllPDaPflz94HbCv4HiJLuGdQdodK9mD42plCAgIaAdvFm2R/vq+PfLlSzndHZ6l+ROJb9bpCFoNDk7PCAa4Nzjj5AEkFbRXnz8YYJASn19UVLG1e2IzgbZtbp6mmZDkydYAuEYnYreNea4mhiLagCITV24J4pPK4fPhN47JvL5jsJ4tthWWPKTAnN5yhJEPLD2STzY+X5vFYJDqJkS1BJSrqrdlsWZzyrupsc9sB4C2JaapU9JaC/zbtGma3/h50uv37/zHwKOD+5t+xXkycRgE3AT/QYvD9DepvIS1CMwiFxhqW65zktKORnXb6H9Dl2cYJPi3r+5Vjv1l9OW3CISQMAAA==" + } + }, + { + "ID": "5fafe6d4515f4033", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "dce20ed56e82066f1b7b73d18414a05d/15629472013285000174;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:23 GMT" + ], + "Etag": [ + "7/5GhaMhwLsPmEbXKUyftg==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:23 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnak188:4287,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_6w8W9G4HM_mqgWKjIewAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RXWvbMBSG7/0rjHu7xB9yYjtQ6MZCCevH8FoyKCXI8rGtRbYcSY4Jpf99kuP0Imw0upPOOe/z6j1vlu1saZM7C9vJaLnrQByucqywBOV80UVQuDTFyJ3dVvi+6u/kz3qZ/f7xfChUeX09NNFhPu8YATVpuVCTaB4sRplN4PmxF3nhJvTmSZDE/iyMgsjfePoM4xJYcUebrRGplGrlwnX7vp+WnJcMcEvllPDaPflz94HbCv4HiJLuGdQdodK9mD42plCAgIaAdvFm2R/vq+PfLlSzndHZ6l+ROJb9bpCFoNDk7PCAa4Nzjj5AEkFbRXnz8YYJASn19UVLG1e2IzgbZtbp6mmZDkydYAuEYnYreNea4mhiLagCITV24J4pPK4fPhN47JvL5jsJ4tthWWPKTAnN5yhJEPLD2STzY+X5vFYJDqJkS1BJSrqrdlsWZzyrupsc9sB4C2JaapU9JaC/zbtGma3/h50uv37/zHwKOD+5t+xXkycRgE3AT/QYvD9DepvIS1CMwiFxhqW65zktKORnXb6H9Dl2cYJPi3r+5Vjv1l9OW3CISQMAAA==" + } + }, + { + "ID": "0e7d81c7c237b723", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "39" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b9197197f7231d6a0ee2182c88e307f1/14021410577294280726;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZWZhdWx0VGFibGVFeHBpcmF0aW9uTXMiOiIzNjAwMDAwIn0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:24 GMT" + ], + "Etag": [ + "377T+LSJFlu62WX+UYCWOA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnnw13:4388,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/24,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_6w8W4isKIaGrAW35L-ICg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/24,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/24" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2Sa2vbMBSGv/tXGO9jl/gix5dAYd2WjYy2ATfFG2MEWT52tCiWI8lJQ+l/n2U7ZYSNRh/P5X2fc46eDdPa0Cq3pqaV0XLXgDi+y7HCEpT1vk2CwqVOojBcXt0+fPvCmsBLv189/viULm6ur7si2vXnDSOgRjUXahQG3nSQWXmOGzmh4698J4i9OHInfuiF7sppX9cugRW3tNpokbVStZza9uFwGJeclwxwTeWY8K194rP3nl0L/huIkvaZqT2YSvti96EwgQIEVARaimfDfI3P+9kuVDOtgWz+r5VYhvmiLQtBocrZ8R5vtZ3Vc4AkgtaK8uqvWIEbppY4YzB7qqnAOn0nu4sEzusMmBCQOvqzRdD0piU467TTZL6cJR1bu+kaCMXsq+BNrZMDbCqoAiFbvI7vTGGR3r8lsDhUl/U3EsTH42yLKetnCFAcI+T6k1HmRspx+VbF2AvjDUElKeluvduwKOPZuvmQwx4Yr0GMy1ZlTwm0Y/OmUvp3/Mc7md18fgs+AZyf6A3zl94nEdBtekn7A7kT1F4dOTGKkN9tnGGp7nhOCwr5WZXr+B5CfRUn+HTQxwfLeDH+AA6QZ7dxAwAA" + } + }, + { + "ID": "fbbfcfb386ecadff", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "23" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a27aa7d05a95c0bc9378f2bc9315079a/12413349141303495999;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJmcmllbmRseU5hbWUiOiJ4eXoifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:25 GMT" + ], + "Etag": [ + "D3xe5487gj+mZfjqzHuRtg==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmc19:4319,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=AK08W9HKFtW7qQXv7o-ICw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SXW+bMBSG7/kViF5uCR8mASJV2qZGW6S1lVimSJ2myJgDceJgYpt8tOp/rw2kF9Gmhstzjt/3ec/hxbKdDa1yZ2I7GS13DYjTTY4VlqCcz7oJCpemeYeOMArjqFx/2j4V693zjyZV5e1tO0Tb93nDCKhBzYUaRONg0sssA8+PvcgLl6E3ToIk9kdhFET+0tNf+1wCK37SamNEVkrVcuK6h8NhWHJeMsA1lUPCt+6Zz90Hbi34GoiS7oWp25tK92r3fjCFAgRUBDTFi2W/12ddtivVbKcnm/1rJY5lvxrLQlCocnZ6wFtj5xxPzx0KSCJorSivTLmvFbhhao4zBtNjTQU27XtpBtDYe4+BCQFpqn80hQlgO4KzVn6RzubTtMXTy66BUMy+C97UptnzLgRVIKQmbBEvFB4XDx8JPB6q6943EsS303SLKesyjFGSIOSHo0Hmx8rz+VYlOIiSDUElKelutduwOOPZqvmSwx4Yr0EMS62ypwR0bN5Uyvwg//FOp1/vPoJPAednesv+a/ZJBLSbntPuRv4I6cMjL0ExCtuNMyzVPc9pQSG/mPK9UEfqpjjB54P+/uVYr9YbwiVrTHQDAAA=" + } + }, + { + "ID": "1b96d9bd1b9eff5c", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "34" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7b66b8181f2878720195d41057eeeff4/10877345299333861992;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZWZhdWx0VGFibGVFeHBpcmF0aW9uTXMiOm51bGx9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:25 GMT" + ], + "Etag": [ + "MElvmTt0ZG23oWFWgDgdlw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnap199:4017,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Aa08W6CTBJTrqgWAr42YDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RXWvbMBSG7/0rjHu7xB9y/BEobKNZCawteBmGlhEU+cTRIluOJMfNSv/7JMfpRWhpfOfz8T6v3vNi2c6W1oUztZ0VLXctiMNVgRWWoJwvugkKl6Z5N2P7aqG8x9sA8fxHXt6UBeuur/sh2u8XLSOgRg0XahRHwXSQWQaen3ixFy5DL0qDNPEnYRzE/tLTX78uga1/0nprRDZKNXLqul3XjUvOSwa4oXJMeOWe/Ln7wG0E/wtESfcM6g5Q6V5MHwYzWIOAmoB28WLZb/X58W0XqtnO4Gz+XiSOZb8a5FpQqAt2uMeVwTnPh39HKyCJoI2ivDblvoYJASn175NWN8ZsR3DWr+XZfDHLeqwOsQFCMbsVvG1Mc/CRC6pASE3u0WcKD/n9ZwIPXX3ZfitBfD/MKkyZaaEoQmmKkB9ORis/UZ7PK5XiIE63BJWkpLvNbsuSFV9t2q8F7IHxBsS41Cp7SkA/m7e1Mof/gJ3Nvt18Zj4DXJzcW/YfkycRgE3AC3rM3p8gfVDkpShBYZ84w1Ld8YKuKRRnU743iU5TnODToX7/cqxX6z9Es8suTAMAAA==" + } + }, + { + "ID": "2830c63bce4bf33a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8e5e7c2a79c5533915bc2ae817eaf3a4/9269283863359854225;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:26 GMT" + ], + "Etag": [ + "MElvmTt0ZG23oWFWgDgdlw==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:26 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vng62:4036,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Aa08W7iHL4iOqQWKlL3gDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RXWvbMBSG7/0rjHu7xB9y/BEobKNZCawteBmGlhEU+cTRIluOJMfNSv/7JMfpRWhpfOfz8T6v3vNi2c6W1oUztZ0VLXctiMNVgRWWoJwvugkKl6Z5N2P7aqG8x9sA8fxHXt6UBeuur/sh2u8XLSOgRg0XahRHwXSQWQaen3ixFy5DL0qDNPEnYRzE/tLTX78uga1/0nprRDZKNXLqul3XjUvOSwa4oXJMeOWe/Ln7wG0E/wtESfcM6g5Q6V5MHwYzWIOAmoB28WLZb/X58W0XqtnO4Gz+XiSOZb8a5FpQqAt2uMeVwTnPh39HKyCJoI2ivDblvoYJASn175NWN8ZsR3DWr+XZfDHLeqwOsQFCMbsVvG1Mc/CRC6pASE3u0WcKD/n9ZwIPXX3ZfitBfD/MKkyZaaEoQmmKkB9ORis/UZ7PK5XiIE63BJWkpLvNbsuSFV9t2q8F7IHxBsS41Cp7SkA/m7e1Mof/gJ3Nvt18Zj4DXJzcW/YfkycRgE3AC3rM3p8gfVDkpShBYZ84w1Ld8YKuKRRnU743iU5TnODToX7/cqxX6z9Es8suTAMAAA==" + } + }, + { + "ID": "4dfc5c5729e3df66", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "319" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "MElvmTt0ZG23oWFWgDgdlw==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "77d30c7fd138fb4ed6e6d1d4c4c48739/7661222431664036793;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJhY2Nlc3MiOlt7InJvbGUiOiJXUklURVIiLCJzcGVjaWFsR3JvdXAiOiJwcm9qZWN0V3JpdGVycyJ9LHsicm9sZSI6Ik9XTkVSIiwic3BlY2lhbEdyb3VwIjoicHJvamVjdE93bmVycyJ9LHsicm9sZSI6Ik9XTkVSIiwidXNlckJ5RW1haWwiOiIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSJ9LHsicm9sZSI6IlJFQURFUiIsInNwZWNpYWxHcm91cCI6InByb2plY3RSZWFkZXJzIn0seyJyb2xlIjoiUkVBREVSIiwidXNlckJ5RW1haWwiOiJKb2VAZXhhbXBsZS5jb20ifV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:26 GMT" + ], + "Etag": [ + "BDPTI1iDh6yj37Ca10++dw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnqq6:4031,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Aq08W-DUA4-NqwXt2aDwDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SXW/aMBSG7/MrovSyg3w45AOpUteBJqa1nTIqLqYJGecQXJw42A40q/rfZ4e00tCqkjv7nPO+j8+bZ8t2trTKnbHtrGixa0C0FzlWWIJyPukiKFyY4s3kx3zm08kmah9R/AX73uVlfri66ppoN583jIAa1FyoQRwF415mGXh+4sVeuAy9KA3SxB+FcRD7S09/3bgEtv5Oq60R2ShVy7HrHg6HYcF5wQDXVA4JL91XPncfuLXgj0CUdE9M3d5Uume7940ZrEFARUBTPFv22/3s+LYz1WynJ5v9byWOZb8Yy7WgUOWsvcOlsXOe2j9HFJBE0FpRXpnr7g4TAlLq4y+tbsBsR3DWjS2y2XyadbZ6iTUQitlXwZvaFHuOhaAKhNTOnfWJwv3i7iOB+0N13nwjQdy00xJTZkooilCaIuSHo8HKT5Tn81KlOIjTLUEFKehus9uyZMVXm+Y6hz0wXoMYFlplTwnoZ/OmUib4d7yz6efJR/AZ4Px9+n8UTvC/cbiGJ1zWDN4YLPu3SYQIwCaiOT2m54+Q/iWQl6IEhV1mDEt1y3O6ppCfdPme3kt87OIEv0b98NOxXqy/66bKMI4DAAA=" + } + }, + { + "ID": "7cfa9bbe8d41f56c", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "BDPTI1iDh6yj37Ca10++dw==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7d5cf48402bbe81b3f22fa8473b78e45/6053162095168102626;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJhY2Nlc3MiOlt7InJvbGUiOiJXUklURVIiLCJzcGVjaWFsR3JvdXAiOiJwcm9qZWN0V3JpdGVycyJ9LHsicm9sZSI6Ik9XTkVSIiwic3BlY2lhbEdyb3VwIjoicHJvamVjdE93bmVycyJ9LHsicm9sZSI6Ik9XTkVSIiwidXNlckJ5RW1haWwiOiIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSJ9LHsicm9sZSI6IlJFQURFUiIsInNwZWNpYWxHcm91cCI6InByb2plY3RSZWFkZXJzIn1dfQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:27 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:27 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnqq6:4031,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Aq08W8jYLoaKqgXTyqTwBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1c628aORD/nr/CQnzgJG4THklaJD6kQC5ISR8kvYfaKjK7Duyx2Fuvtw0X5X+/8WPJsjGEHNeURFYkwOPxjMczHo9/ZLnZQSXCOeOlFrrZQaaRQOsTtBQJiAGb4pACsTSK2BBHpaqmc4ITpugcC3IaTkPRu/YJCUiQsUxJkuARkTxZF5LMKJLcSQv9xVKOAixwQgQiGcvXlAmMrthd15QILD+jNA7keBYTkBMymnjoGPimjBMUUhgyVeQqSghBYyHipLW760csDbwRY6OIeD6b7g7D0deU8Nmu4CwdRiQZMyZCOvrVLICZfsR8Je1iFisbmBgTXuyUHWaa3nceCpIxBGSYjvowJ8nx6cPHdxdHl93e236v+6WKBmrxWshnAWnn+5BZszZNowgpGfqjmtp7zgRra4+00OeStmxMovDa60mGrur6XPpMpWTJMji66F2e9s/6F5e9Pzu9XrfXld2Yj9IpoUKyPLzKS0ZIcrYOlwJWqYXeXZz0BnfUHLNeHTkE/gQWYPt07pScHUCeMurhVIy9DzISzjCFBeH9aRyVFeFcyCAazTx/TPyJIg1IEsM8SaU4wvsbf8Ot5uu9XzbQeh7CKxmA0rnqb4SHV7Ml6g4Oa5uoU4TfOKZitZr6/qsN1HhXaXQVRtES4bWDxibCffAGhMsS4a/2H5CdEA6me5wlM+9EUro6iAaSoMOzco+u1/51M5OdpNTj5CoivvB+I1QGMwnOiBiz4Mj3YZ8xXq/v73kh/cYmpPKRTij7TtE5JCWf2IR0SURGWGaKRSnKMCNlJY8OxvnCyqYXYTqaa9BjMlmmZUJ4TWerNZPrMSCQ5BIBOeN6Vq55PgZfF+lGdm3N7VGU3cUx5IgTxibH2BeMz8oXHPtgu+mXPfcU3xtkYqK+iX31lfY11ozlouzfMf8nb53VngKTVtnYb/5Ec/LbJyfjKA1CkbfnflQUWYw1G8Xej/FNh5MgWW1MkUVrXDc5P6VnOowKcv2Ab+4zZcfN9nnnlI1WG7PIoLWte549pSFahknIdtn7hwd52fLEiFVvWTE1GtmJZT9hlswnlrWefq3jOFw4Bsv5xjmEVOiTsnl/jzmGSo7wpFyreWNMg4i8gXpscpeWF47O7Liv1R46lJ9+RjafUWDgsd/wZFGW7adB7GcSj+I4CnX1eaJUcaMyU7WaV1dWtkBcpvhcferD3uQUR2aTejyl0NenORWVpcwmjGz7eLXWDphGSQT7Cw7VaFYpdpij9fD1j5Jszz16K6UiVIWgn3IOFwfvaJhA+ewLqBFoIu9qx6lIOSkX2l7A5pTKkjHGTU1r3fJU2huba5dhslpLrWZbYaHLLE+WW8REUTnfGKSUYrjaSgV9aoiVPIfJi/u2Xb+2eAgPaYFF7oGtnLPKzZk/J3Ygkw+xP9HT71O4eMPlMTC9b9lHChWzRW2jbksZ/5Namz5rinqUd6xOse3XJZF1xjjpXRM/hXM0Ke8bjyxQTSTt7R+8vFDq6GXIpd27ORb6TITYdu0TBKat6nzGganursVYvBhzgoP3jEVZ7ElBfzA+Ibxi6TQZrvlYqWUtUs9yidiDenNBqrpna+bcOHMlaKoDn+aWShY45kz0YS95xziNRAv1p9NUyBXScBu5Cmkoz/abIVQubQvYVkW+RIygxjU4X28weDeQ1DTRKF8VzaHCtsMJHU7ocEKHEzqc0OGEDid0OKHDCR1OuHWonMMJHU7ocEKHEzqccHvgGIcTOpxwKwPzBeKEVaSRtbb+h8gqArsJDUgAxUlsID35v4cnMA6KmfbNrW6fg440aUPmHoZBQEBOaM53DfO19dtNhqDpkXmgUMGAS8A8zw4+wuQhAN7M3kBUwCTbgqfEII5nBkJ0oKMDHR3o6EBHBzo60NGBjg50dKCjAx23DuJzoKMDHR3o6EBHBzpuD7bjQEcHOm5lYL5I0PEOZOtIYM4K993xaLBtGVhYRVdhBMe8hBzuIL0qyqCxtnrW2VtAx6oL8F8VSW9MSXA0Rys/PYzVVbNHur/crqvsOT05XkX6qfj2vUfioSf2ld+atcNb9JyMaiEHEDuA2AHEDiB2ALEDiB1A7ABiBxA7gHjr4FgHEDuA2AHEDiB2APH24HAOIHYA8VYG5gsEiO+vV/ERdoWxdVgk7+qgRzD1THulQNanue3+l5PHoRTxcIBjkb8OGEkUOCQ9U2DpWl75P6jElDr5gnmx+rHW4ZuLtd4ii0usnC/DXYZh7pAskIyL/2ua7IYcfDUPGKI+EFvWbO49Qkm2dc0kjdjTMBESfqoUuk1Wrj8i2RckAFccQWVtlfzKftFdSzBU4VaZOXTN+dD5cJUPcwKFTrU648pTZSLvgrmmvAgunDwLnGaR6z+heskZYd46au3PWJBCyTI/nTmfwSyysMvuv7VCOZIbatzb3MgmVzI9q5IpJ3WUEC97dMXrhkmMhT/+kJKUqG+AdHmTK1busWSJTU65JH8u+xZevsgfzi7Jb/5KLQR9qvlcf0J8B0y63fkXQ9oulWJdAAA=" + } + }, + { + "ID": "7f698bfa85f385cf", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "BDPTI1iDh6yj37Ca10++dw==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7d5cf48402bbe81b3f22fa8473b78e45/14508531109404082935;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJhY2Nlc3MiOlt7InJvbGUiOiJXUklURVIiLCJzcGVjaWFsR3JvdXAiOiJwcm9qZWN0V3JpdGVycyJ9LHsicm9sZSI6Ik9XTkVSIiwic3BlY2lhbEdyb3VwIjoicHJvamVjdE93bmVycyJ9LHsicm9sZSI6Ik9XTkVSIiwidXNlckJ5RW1haWwiOiIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSJ9LHsicm9sZSI6IlJFQURFUiIsInNwZWNpYWxHcm91cCI6InByb2plY3RSZWFkZXJzIn1dfQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:27 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:27 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndf4:4033,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=A608W-GYF8TNqAXfvonACQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1c628aORD/nr/CQnzgJG5THkkaJD6kQC5ISR8kvYfaKjK7Duyx2Fuvtw0X5X+/8WPJsjGEHNeURFYkwOPxjMczHo9/ZLnZQSXCOeOlFrrZQaaRQOsTtBQJiAGb4pACsTSK2BBHpaqmc4ITpugcC3IaTkPRu/YJCUiQsUxJkuARkTxZF5LMKJLcSQv9xVKOAixwQgQiGcvXlAmMrthd15QILD+jNA7keBYTkBMymnjoGPimjBMUUhgyVeQqSghBYyHipLW760csDbwRY6OIeD6b7g7D0deU8Nmu4CwdRiQZMyZCOvrVLICZfsR8Je1iFisbmBgTXuyUHWaa3nceCpIxBGSYjvowJ8nx6cPHdxdHl93e236v+6WKBmrxWshnAWnn+5BZszZNowgpGfqjmtp7zgRra4+00OeStmxMovDa60mGrur6XPpMpWTJMji66F2e9s/6F5e9Pzu9XrfXld2Yj9IpoUKyPLzKS0ZIcrYOlwJWqYXeXZz0BnfUHLNeHTkE/gQWYPt07pScHUCeMurhVIy9DzISzjCFBeH9aRyVFeFcyCAazTx/TPyJIg1IEsM8SaU4wvsbf8Ot5uGrXzbQeh7CKxmA0rnqb4SHV7Ml6vYPapuoU4TfOKZitZr63usN1HhXaXQVRtES4bX9xibCffAGhMsS4a/3HpCdEA6me5wlM+9EUro6iAaSoMOzco+u1/6wmclOUupxchURX3i/ESqDmQRnRIxZcOT7sM8Yr9f2Dr2QfmMTUvlIJ5R9p+gckpJPbEK6JCIjLDPFohRlmJGykkcH43xhZdOLMB3NNegxmSzTMiG8prPVmsn1GBBIcomAnHE9K9c8H4Ovi3Qju7bm9ijK7uIYcsQJY5Nj7AvGZ+ULjn2w3fTLnnuK7w0yMVHfxL76Svsaa8ZyUfbvmP+Tt85qT4FJq2zsNX+iOfntk5NxlAahyNtzPyqKLMaajWLvx/imw0mQrDamyKI1rpucn9IzHUYFuX7AN/eZsuNm+7xzykarjVlk0NrWPc+e0hAtwyRku+y9g/28bHlixKq3rJgajezEsp8wS+YTy1pPv9ZxHC4cg+V84xxCKvRJ2by/xxxDJUd4Uq7VvDGmQUTeQD02uUvLC0dndtzXag8dyk8/I5vPKDDw2G94sijL9tMg9jOJR3Echbr6PFGquFGZqVrNqysrWyAuU3yuPvVhb3KKI7NJPZ5S6OvTnIrKUmYTRrZ9vFprB0yjJIL9BYdqNKsUO8zRenD4oyTbc4/eSqkIVSHop5zDxcE7GiZQPvsCagSayLvacSpSTsqFthewOaWyZIxxU9NatzyV9sbm2mWYrNZSq9lWWOgyy5PlFjFRVM43BimlGK62UkGfGmIlz2Hy4p5t168tHsJDWmCRu28r56xyc+bPiR3I5EPsT/T0+xQu3nB5DEzvW/aRQsVsUduo21LG/6TWps+aoh7lHatTbPt1SWSdMU5618RP4RxNynvGIwtUE0mv9vZfXih19DLk0u7dHAt9JkJsu/YJAtNWdT7jwFR312IsXow5wcF7xqIs9qSgPxifEF6xdJoM13ys1LIWqWe5ROx+vbkgVd2zNXNunLkSNNWBT3NLJQsccyb6sJe8Y5xGooX602kq5AppuI1chTSUZ/vNECqXtgVsqyJfIkZQ4xqcrzcYvBtIappolK+K5lBh2+GEDid0OKHDCR1O6HBChxM6nNDhhA4n3DpUzuGEDid0OKHDCR1OuD1wjMMJHU64lYH5AnHCKtLIWlv/Q2QVgd2EBiSA4iQ2kJ7838MTGAfFTPvmVrfPQUeatCFzD8MgICAnNOe7hvna+u0mQ9D0yDxQqGDAJWCeZwcfYfIQAG9mbyAqYJJtwVNiEMczAyE60NGBjg50dKCjAx0d6OhARwc6OtDRgY5bB/E50NGBjg50dKCjAx23B9txoKMDHbcyMF8k6HgHsnUkMGeF++54NNi2DCysoqswgmNeQg53kF4VZdBYWz3r7C2gY9UF+K+KpDemJDiao5WfHsbqqtkj3V9u11X2nJ4cryL9VHz73iPx0BP7ym/N2sEtek5GtZADiB1A7ABiBxA7gNgBxA4gdgCxA4gdQLx1cKwDiB1A7ABiBxA7gHh7cDgHEDuAeCsD8wUCxPfXq/gIu8LYOiySd3XQI5h6pr1SIOvT3Hb/y8njUIp4OMCxyF8HjCQKHJKeKbB0La/8H1RiSp18wbxY/Vjr8M3FWm+RxSVWzpfhLsMwd0gWSMbF/zVNdkMOvpoHDFEfiC1rNl89Qkm2dc0kjdjTMBESfqoUuk1Wrj8i2RckAFccQWVtlfzaftFdSzBU4VaZOXTN+dD5cJUPcwKFTrU648pTZSLvgrmmvAgunDwLnGaR6z+heskZYd46au3PWJBCyTI/nTmfwSyysMvuv7VCOZIbatzb3MgmVzI9q5IpJ3WUEC97dMXrhkmMhT/+kJKUqG+AdHmTK1busWSJTU65JH8u+xZevsgfzi7Jb/5KLQR9qvlcf0J8B0y63fkXbJGPq2JdAAA=" + } + }, + { + "ID": "6a8858fe230188de", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "BDPTI1iDh6yj37Ca10++dw==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7d5cf48402bbe81b3f22fa8473b78e45/4517157153703617803;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJhY2Nlc3MiOlt7InJvbGUiOiJXUklURVIiLCJzcGVjaWFsR3JvdXAiOiJwcm9qZWN0V3JpdGVycyJ9LHsicm9sZSI6Ik9XTkVSIiwic3BlY2lhbEdyb3VwIjoicHJvamVjdE93bmVycyJ9LHsicm9sZSI6Ik9XTkVSIiwidXNlckJ5RW1haWwiOiIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSJ9LHsicm9sZSI6IlJFQURFUiIsInNwZWNpYWxHcm91cCI6InByb2plY3RSZWFkZXJzIn1dfQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:28 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:28 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnuu132:4009,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=BK08W8mdGImeqQW954roBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1c628aORD/nr/CQnzgJG4THklaJD6kQC5ISR8kvYfaKjK7Duxlsbdebxsuyv9+48eSZTGEHNeURFYkwOPxjMczHo9/ZLndQSXCOeOlFrrdQaaRQOsTtBQJiAGb4JACsTSK2BBHpaqmc4ITpugcC3IaTkLRu/EJCUiQsUxIkuARkTxZF5LMKJLcSQv9xVKOAixwQgQiGcvXlAmMrth914QILD+jNA7keBYTkBMymnjoGPgmjBMUUhgyUeQqSghBYyHipLW760csDbwRY6OIeD6b7A7D0deU8Omu4CwdRiQZMyZCOvrVLICZfsR8Je1iGisbmBgTXuyUHWaa3nceCpIxBGSYjvowJ8nx6cPHdxdHl93e236v+6WKBmrxWshnAWnn+5BZszZNowgpGfqjmtp7zgRra4+00OeStmxMovDG60mGrur6XPpMpWTJMji66F2e9s/6F5e9Pzu9XrfXld2Yj9IJoUKyPLzKS0ZIcrYOlwJWqYXeXZz0BvfUHLNeHTkE/gQWYPtk5pScHUCeMOrhVIy9DzISzjCFBeH9SRyVFeFcyCAaTT1/TPxrRRqQJIZ5kkpxhPc3/oZbzdd7v2yg9TyEVzIApTPV3wgPr6ZL1B0c1jZRpwi/cUzFajX1/VcbqPGu0ugqjKIlwmsHjU2E++ANCJclwl/tPyA7IRxM9zhLpt6JpHR1EA0kQYdnZYGu1/51M5OdpNTj5CoivvB+I1QGMwnOiBiz4Mj3YZ8xXt+r1b2QfmPXpPKRXlP2naJzSEo+sQnpkoiMsMwU81KUYUbKSh4djLOFlU0vwnQ006DHZLJMy4Twms5WaybXY0AgySUCcsbNtFzzfAy+LtKN7Nqa26Mou4tjyBEnjF0fY18wPi1fcOyD7aZf9iwoXhhkYqK+iX31lfY11ozlouzfMf8nb53VngKTVtnYb/5Ec/LbJyfjKA1CkbdnMSqKLMaajWLvx/imw0mQrDamyKI1rpucn9IzHUYFuXnAN4tM2XGzfd45ZaPVxswzaG3rnmdPaYiWYRKyXfb+4UFetjwxYtVbVkyNRnZi2U+YJfOJZa2nX+s4DueOwXK+cQ4hFfqkbN7fY46hkiM8Kddq3hjTICJvoB67vk/Lc0dndtzXag8dyk8/I5vPKDDw2G94sijL9tMg9jOJR3Echbr6PFGquFGZqVrNqysrWyAuU3yuPvVhb3KKI7NJPZ5S6OvTnIrKUmYTRrZ9vFprB0yjJIL9BYdqNK0UO8zRevj6R0m25x69lVIRqkLQTzmHi4N3NEygfPYF1Ag0kXe141SknJQLbS9gM0plyRjjpqa1bnkq7Y3NtcswWa2lVrOtsNBllifLLWKiqJxvDFJKMVxtpYI+NcRKnsPkxX3brl9bPISHtMAi98BWzlnl5syfETuQyYfYv9bT71O4eMPlMTC9b9lHChWzRW2jbksZ/5Namz5rinqUd6xOse3XJZF1xjjp3RA/hXM0Ke8bj8xRTSTt7R+8vFDq6GXIpd37ORb6TITYdu0TBKat6nzGganursVYvBhzgoP3jEVZ7ElBfzB+TXjF0mkyXPOxUstapJ7lErEH9eacVHXP1sy5ceZK0FQHPs0tlSxwzJnow17yjnEaiRbqTyapkCuk4TZyFdJQnu23Q6hc2hawrYp8iRhBjWtwvt5g8G4gqWmiUb4qmkGFbYcTOpzQ4YQOJ3Q4ocMJHU7ocEKHEzqccOtQOYcTOpzQ4YQOJ3Q44fbAMQ4ndDjhVgbmC8QJq0gja239D5FVBHYTGpAAipPYQHryfw9PYBwUM+3bO90+Bx1p0obMPQyDgICc0JzvGuZr67fbDEHTI/NAoYIBl4B5nh18hMlDALyZvoGogEm2BU+JQRzPDIToQEcHOjrQ0YGODnR0oKMDHR3o6EBHBzpuHcTnQEcHOjrQ0YGODnTcHmzHgY4OdNzKwHyRoOM9yNaRwJwV7rvn0WDbMrCwiq7CCI55CTncQ3pVlEFjbfWsszeHjlXn4L8qkt6YkOBohlZ+ehirq2aPdH+5W1fZc3pyvIr0U/HthUfioSf2ld+atcM79JyMaiEHEDuA2AHEDiB2ALEDiB1A7ABiBxA7gHjr4FgHEDuA2AHEDiB2APH24HAOIHYA8VYG5gsEiBfXq/gIu8LYOiySd3XQI5h6pr1SIOvT3Hb/y8njUIp4OMCxyF8HjCQKHJKeKbB0La/8H1RiSp18wTxf/Vjr8M3FWm+RxSVWzpfhLsMwd0gWSMbF/zVNdkMOvpoFDFEfiC1rNvceoSTbumaSRuxpmAgJP1UK3SYr1x+R7AsSgCuOoLK2Sn5lv+iuJRiqcKvMHLrmfOh8uMqHOYFCp1qdceWpci3vgrmmvAjOnTxznGaR6z+heskZYd46au3PWJBCyTI7nTmfwiyysMvuv7VCOZIbatzb3MgmVzI9q5IpJ3WUEC97dMXrhkmMhT/+kJKUqG+AdHmTK1YWWLLEJqdckj+XfQcvX+QPZ5fkN3+lFoI+1XyuPyG+Aybd7fwLlsynMmJdAAA=" + } + }, + { + "ID": "d320dc84b4303d6a", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "BDPTI1iDh6yj37Ca10++dw==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7d5cf48402bbe81b3f22fa8473b78e45/12900470772908214047;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJhY2Nlc3MiOlt7InJvbGUiOiJXUklURVIiLCJzcGVjaWFsR3JvdXAiOiJwcm9qZWN0V3JpdGVycyJ9LHsicm9sZSI6Ik9XTkVSIiwic3BlY2lhbEdyb3VwIjoicHJvamVjdE93bmVycyJ9LHsicm9sZSI6Ik9XTkVSIiwidXNlckJ5RW1haWwiOiIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSJ9LHsicm9sZSI6IlJFQURFUiIsInNwZWNpYWxHcm91cCI6InByb2plY3RSZWFkZXJzIn1dfQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:32 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:32 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfd11:4363,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=CK08W8HkHYuYqgWVibugAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1c628aORD/nr/CQnzgJG4THklaJD6kQC5ISR8kvYfaKjK7Duxlsbdebxsuyv9+48eSZTGEHNeURFYkwOPxjMczHo9/ZLndQSXCOeOlFrrdQaaRQOsTtBQJiAGb4JACsTSK2BBHpaqmc4ITpugcC3IaTkLRu/EJCUiQsUxIkuARkTxZF5LMKJLcSQv9xVKOAixwQgQiGcvXlAmMrth914QILD+jNA7keBYTkBMymnjoGPgmjBMUUhgyUeQqSghBYyHipLW760csDbwRY6OIeD6b7A7D0deU8Omu4CwdRiQZMyZCOvrVLICZfsR8Je1iGisbmBgTXuyUHWaa3nceCpIxBGSYjvowJ8nx6cPHdxdHl93e236v+6WKBmrxWshnAWnn+5BZszZNowgpGfqjmtp7zgRra4+00OeStmxMovDG60mGrur6XPpMpWTJMji66F2e9s/6F5e9Pzu9XrfXld2Yj9IJoUKyPLzKS0ZIcrYOlwJWqYXeXZz0BvfUHLNeHTkE/gQWYPtk5pScHUCeMOrhVIy9DzISzjCFBeH9SRyVFeFcyCAaTT1/TPxrRRqQJIZ5kkpxhPc3/oZbzdd7v2yg9TyEVzIApTPV3wgPr6ZL1B0c1jZRpwi/cUzFajX1/VcbqPGu0ugqjKIlwmsHjU2E++ANCJclwl/tPyA7IRxM9zhLpt6JpHR1EA0kQYdnZYGu1/51M5OdpNTj5CoivvB+I1QGMwnOiBiz4Mj3YZ8xXt87PPBC+o1dk8pHek3Zd4rOISn5xCakSyIywjJTzEtRhhkpK3l0MM4WVja9CNPRTIMek8kyLRPCazpbrZlcjwGBJJcIyBk303LN8zH4ukg3smtrbo+i7C6OIUecMHZ9jH3B+LR8wbEPtpt+2bOgeGGQiYn6JvbVV9rXWDOWi7J/x/yfvHVWewpMWmVjv/kTzclvn5yMozQIRd6exagoshhrNoq9H+ObDidBstqYIovWuG5yfkrPdBgV5OYB3ywyZcfN9nnnlI1WGzPPoLWte549pSFahknIdtn7hwd52fLEiFVvWTE1GtmJZT9hlswnlrWefq3jOJw7Bsv5xjmEVOiTsnl/jzmGSo7wpFyreWNMg4i8gXrs+j4tzx2d2XFfqz10KD/9jGw+o8DAY7/hyaIs20+D2M8kHsVxFOrq80Sp4kZlpmo1r66sbIG4TPG5+tSHvckpjswm9XhKoa9PcyoqS5lNGNn28WqtHTCNkgj2Fxyq0bRS7DBH6+HrHyXZnnv0VkpFqApBP+UcLg7e0TCB8tkXUCPQRN7VjlORclIutL2AzSiVJWOMm5rWuuWptDc21y7DZLWWWs22wkKXWZ4st4iJonK+MUgpxXC1lQr61BAreQ6TF/dtu35t8RAe0gKL3ANbOWeVmzN/RuxAJh9i/1pPv0/h4g2Xx8D0vmUfKVTMFrWNui1l/E9qbfqsKepR3rE6xbZfl0TWGeOkd0P8FM7RpLxvPDJHNZG0t3/w8kKpo5chl3bv51joMxFi27VPEJi2qvMZB6a6uxZj8WLMCQ7eMxZlsScF/cH4NeEVS6fJcM3HSi1rkXqWS8Qe1JtzUtU9WzPnxpkrQVMd+DS3VLLAMWeiD3vJO8ZpJFqoP5mkQq6QhtvIVUhDebbfDqFyaVvAtiryJWIENa7B+XqDwbuBpKaJRvmqaAYVth1O6HBChxM6nNDhhA4ndDihwwkdTuhwwq1D5RxO6HBChxM6nNDhhNsDxzic0OGEWxmYLxAnrCKNrLX1P0RWEdhNaEACKE5iA+nJ/z08gXFQzLRv73T7HHSkSRsy9zAMAgJyQnO+a5ivrd9uMwRNj8wDhQoGXALmeXbwESYPAfBm+gaiAibZFjwlBnE8MxCiAx0d6OhARwc6OtDRgY4OdHSgowMdHei4dRCfAx0d6OhARwc6OtBxe7AdBzo60HErA/NFgo73IFtHAnNWuO+eR4Nty8DCKroKIzjmJeRwD+lVUQaNtdWzzt4cOladg/+qSHpjQoKjGVr56WGsrpo90v3lbl1lz+nJ8SrST8W3Fx6Jh57YV35r1g7v0HMyqoUcQOwAYgcQO4DYAcQOIHYAsQOIHUDsAOKtg2MdQOwAYgcQO4DYAcTbg8M5gNgBxFsZmC8QIF5cr+Ij7Apj67BI3tVBj2DqmfZKgaxPc9v9LyePQyni4QDHIn8dMJIocEh6psDStbzyf1CJKXXyBfN89WOtwzcXa71FFpdYOV+GuwzD3CFZIBkX/9c02Q05+GoWMER9ILas2dx7hJJs65pJGrGnYSIk/FQpdJusXH9Esi9IAK44gsraKvmV/aK7lmCowq0yc+ia86Hz4Sof5gQKnWp1xpWnyrW8C+aa8iI4d/LMcZpFrv+E6iVnhHnrqLU/Y0EKJcvsdOZ8CrPIwi67/9YK5UhuqHFvcyObXMn0rEqmnNRRQrzs0RWvGyYxFv74Q0pSor4B0uVNrlhZYMkSm5xySf5c9h28fJE/nF2S3/yVWgj6VPO5/oT4Dph0t/MvN6jnAmJdAAA=" + } + }, + { + "ID": "bdfefd5f043726fa", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "BDPTI1iDh6yj37Ca10++dw==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7d5cf48402bbe81b3f22fa8473b78e45/2909095717729610036;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJhY2Nlc3MiOlt7InJvbGUiOiJXUklURVIiLCJzcGVjaWFsR3JvdXAiOiJwcm9qZWN0V3JpdGVycyJ9LHsicm9sZSI6Ik9XTkVSIiwic3BlY2lhbEdyb3VwIjoicHJvamVjdE93bmVycyJ9LHsicm9sZSI6Ik9XTkVSIiwidXNlckJ5RW1haWwiOiIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSJ9LHsicm9sZSI6IlJFQURFUiIsInNwZWNpYWxHcm91cCI6InByb2plY3RSZWFkZXJzIn1dfQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:38 GMT" + ], + "Etag": [ + "MK63K5c+KsurFkN6WRdspw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnnw13:4388,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/167,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Dq08W6miDILUqAXF5oC4Ag" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/167,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/167" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RS4/aMBSF9/kVkbtsIQ+HPJBGaqvSCtFhpHQqFlWFjHMJbkwcbIcMHc1/rx1CF6jVkF3u43zH5z47LqpYXaCpizasPLQgT28KookCjd6ZJmhS2ub9IsaLCX27UK38XC3jVV6opru764dYv1+0nIIeNULqURKH00FmHfpB6id+tI78OAuzNJhESZgEa998/boCvv3K6sqK7LRu1NTzuq4bl0KUHEjD1JiKvXfx5x1Dr5HiF1CtvCuoN0CVdzN9GMxhCxJqCsbFs+P+rc/Pb7tRzUWDs/m/IkGO+2KRW8mgLvhpSfYWh55Ov89WQFHJGs1Ebct9jVAKSpnfH0bdGnORFLxfW+Xzx1neY02IDVBG+Bcp2sY2Bx8ryTRIZcg9+krhYbV8TeChq2/bbxXIj6fZnjBuWziOcZZhHEST0SZItR+Ivc5ImGQVxSUt2WF3qHi6EZtd+76AI3DRgByXRuXIKJhni7bW9vD/YeezD59eM58DKS7uHfenzZNKIDbgR3bOPphgc1DsZzjFUZ84J0rfi4JtGRRXU0GQJjg8TwlKLof6/g05L84fdVDfSkwDAAA=" + } + }, + { + "ID": "9ece4bde0419cff9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "31b8281d566e00dd4dbebb2be1d4a240/1301035381233741404;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:39 GMT" + ], + "Etag": [ + "MK63K5c+KsurFkN6WRdspw==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:39 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnaa198:4415,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/188,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Dq08W5GpNoTWqwWR4qfoBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/188,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/188" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RS4/aMBSF9/kVkbtsIQ+HPJBGaqvSCtFhpHQqFlWFjHMJbkwcbIcMHc1/rx1CF6jVkF3u43zH5z47LqpYXaCpizasPLQgT28KookCjd6ZJmhS2ub9IsaLCX27UK38XC3jVV6opru764dYv1+0nIIeNULqURKH00FmHfpB6id+tI78OAuzNJhESZgEa998/boCvv3K6sqK7LRu1NTzuq4bl0KUHEjD1JiKvXfx5x1Dr5HiF1CtvCuoN0CVdzN9GMxhCxJqCsbFs+P+rc/Pb7tRzUWDs/m/IkGO+2KRW8mgLvhpSfYWh55Ov89WQFHJGs1Ebct9jVAKSpnfH0bdGnORFLxfW+Xzx1neY02IDVBG+Bcp2sY2Bx8ryTRIZcg9+krhYbV8TeChq2/bbxXIj6fZnjBuWziOcZZhHEST0SZItR+Ivc5ImGQVxSUt2WF3qHi6EZtd+76AI3DRgByXRuXIKJhni7bW9vD/YeezD59eM58DKS7uHfenzZNKIDbgR3bOPphgc1DsZzjFUZ84J0rfi4JtGRRXU0GQJjg8TwlKLof6/g05L84fdVDfSkwDAAA=" + } + }, + { + "ID": "c11cd494fa6b14ac", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "29" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a15c1c51a547915af53d875fa307a198/18211493038502097797;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsYWJlbHMiOnsibGFiZWwiOiJ2YWx1ZSJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:39 GMT" + ], + "Etag": [ + "KJkJDh9lga7zt2/uiTBvYg==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnw69:4198,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/46,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D608W-LUA4XFqgWj67DYDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/46,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/46" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1SXY+bMBB851cg+trEgAkfkU5qTxdVubZ3Ek0VVVUVGbMhLg4mtiGXO91/LybQh6jVhTfvzs7M7vBi2U7JqtyZ207GikMD8vQuJ5oo0M77rgmaFKb5+b68v9slvCDRs/ZRw1a37Y/i5qYHsX4+bzgFPamF1JMo9OcDzcZ3vdiN3GATuGHiJ7E3CyI/8jZu9/XjCvj2C6tKQ7LTulZzhI7H47QQouBAaqamVOzR6A+1Pqql+A1UK3QhigZRha5WH4ApbEFCRaFz8WLZf+vL825XstnO4Gz5r5M4lv1qJLeSQZXz0wPZGznn6fR8tgKKSlZrJipT7mucZMDVaKp/mV5LeAMjH6EUlMH87DAGZztS8J56nS5Xi7S31h26BsoI/yRFU5vm4HUtmQapOrae7oLhcf3wFsHjsbpuvlEgb0+LPWH9EjgMcZJg7AWzSebF2vXEXifEj5KS4oIW7LA7lDzORLZrPuTQAhc1yGnRsbSMQre2aCptfo7/aKeLj3dvmU+B5KN7y/5l7kklEBPCip3z8Wa4Cx27CY5xMKSi9FeRsy2D/ALleUmI/TNKUDKG+f2bY71afwAubDvucAMAAA==" + } + }, + { + "ID": "f000889fe8789a54", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "26" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8c0729297711f62caffb77cb34d840dc/16603431606806214830;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsYWJlbHMiOnsibGFiZWwiOm51bGx9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:40 GMT" + ], + "Etag": [ + "VxiB7HKKGADkIqOmxTd6Gw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnam66:4165,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/94,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D608W8jYLsrHqwXYmpmIDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/94,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/94" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RS4/aMBSF9/kVUbot5OFAEqSROqNBFE07SCkti6pCxrkEN04cbIfAjOa/1w6hC9RqyC73cb7jc18t2ylolTkT29nQfN+AOH3IsMISlPNRN0Hh3DR/HOlD9PnpaXb/WMz3i/K4zMaz9u6uG6LdftYwAmpQc6EG0TiY9DLrwPNjL/LCdeiNkyCJ/VEYBZG/9vTXrUtg2y+0KozITqlaTly3bdthznnOANdUDgkv3Ys/9xC4teC/gSjpXkHdHirdm+n9YApbEFAR0C5eLftvfX5+241qttM7m/8rEsey3wxyKyhUGTs949LgnOPp5WwFJBG0VpRXptzVMCEgpf79qdWNMdsRnHVrq3S+nKYdVodYA6GYzQRvatPsfawEVSCkJnfoK4XF6vk9gUVb3bbfSBAPp2mJKTMtNB6jJEHID0eDjR8rz+elSnAQJQVBOcnpfrcvWLzhm13zKYMDMF6DGOZa5UAJ6GfzplLm8P9hp9P7x/fMp4Czi3vL/mXyJAKwCXhJz9n7I6QPirwExSjsEmdYqq88o1sK2dWUH3gIBecpTvDlUN+/Odab9Qe7qz5aTAMAAA==" + } + }, + { + "ID": "29ee1ccaf38bf434", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "48a75b6b2701e0b22d96d548b605e301/14995371270327057623;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMyJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:40 GMT" + ], + "Etag": [ + "jV3IK2YtJ6QrTUcFUILiLw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnma19:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=EK08W4CpHNbHqQWl3oHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TXW/aMBSG7/MrIu+W4nzQUJB6AWtWZUthS8OkaaqQcUzqNokz2yxDFf99jp1IDNGJ3Dh+fd5znnMsv1k2eKVVBqY22ND8147w/QeJNgUBA3VEJMrbo5fvfvTF+yE/B994usKfVlFM4+b2VgdR7c52BSbyqmZcXo0Db5ohiQSRa89xb5yxM1qPnGDiTW7c69HYG7trR31DXel/Ib6uIEixjWn12tZ5lrIWUwibphnmjOUFQTUVQ8xK2DcAf3uw5uyFYCngCRfsuAS8BBBqQAEv5NRhCdkSTipMFO2bZdugQ4nOjam1KdGgdBEXcBmbLmdMFwBa9kHPEj+TEvVsW0qKTKjdT7XTkhIrVLbwZh0YTe5rrT2mSbS4B614GJzz7Epx6okWaXgfJr1cskzLSfg1nKXh3fvJOMGnuZLw4zK569V/8Xv/UYYNY0UXfJRkvlzG4WwBjH7Qy5PV/T51g1KdzPeStNmBHnirxKzKU8LPnCSsORIwJ0hSVqXUcLjXvroY3/WcYBSYp/WnpvxcTDDxuxsGBRLygWVUtZm9n6nvKp3N49D4GNaZW3X1CKyD9ReWqwfT6AMAAA==" + } + }, + { + "ID": "999ca34098798dc8", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0003?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "29" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "482d765124b7257f0e8a90dea81bcd7f/13459366328862572800;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0003?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsYWJlbHMiOnsibGFiZWwiOiJ2YWx1ZSJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:41 GMT" + ], + "Etag": [ + "hc2nAUCVp38xvse04CiJXQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnak188:4287,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=EK08W-HWLIqgqQXumri4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TS2+jMBSF9/wKxGzTmFfJQ+oiSVGVKpN0KB1VGlWRAzfULWAGO0mjKv99/AApk0lHYWN8fM/xd234NEzrnZSpNTStFcl+b6Def+N4lYPVEUvAcSaXXhO3HD1NflZe/2PLwPYn5P75x82NKiLKnW7yBPhVRWt+1QvcYYo5ZsCXru307Z7tL307GLiDvnPt99yes7TF01U7/a/EUzswyNczUr4rFM4rNkRot9t1M0qzHHBFWDehBWobQFsXVTV9g4QzdMKFGi6GLgFECpChCzlVWQRrqKFMQNB+GqZpNSjTc8ckbULUKE3FBVzaprbTpgsADfMgGXO8gpy1bGomA7Y430Bbw5JXKHBbsyaQp9LxS8yUJMQSF7JBPXa0xveV0h7jaDq/s6R46JzzbAp26pnO4/AujFq5oKmSo/AhHMXh7ddhNSSnWVE4WUS3rfo3fus/SlhRmjfFRyHjxWIWjuaW1g9qeDGa15fmoEQn4z0HmW6pS5HKjJZZDPWZlYjujoSkBswJLWOiOZxrT1ye57h24Af69/uoSH2uJhh4zVcgbpDx7zQlos30nyTHDfr6y2y6ikfjWah9NFHJUn16tIyD8QduBI+9DAQAAA==" + } + }, + { + "ID": "c3f502fbcf393b73", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0003?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "26" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5145bf30b953736993d0c40dbfc2ef11/11851304892871853608;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0003?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsYWJlbHMiOnsibGFiZWwiOm51bGx9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:41 GMT" + ], + "Etag": [ + "GLui6M1Kc96i0n3x6pK6lA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlf1:4425,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ea08W_DsFYv1qQXaw6mACw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TTW+jMBCG7/wK5L2mMV91PqQekhZFUWlSUXqqqogYh7oFzGJn06jKf6+xjZRms6twMX4978wzY/iybPBBqwyMbbCm+e8tafa/RLouCOjJIyLSvD2aRVuKHtx7PELUqfxPVN+jYnJzo4KocmfbAhNxVbNGXA2QN85SkXIiVp7jDp2BE6wCB4280dC9DgbewF058umrSv8L8VUFTopNRKuPts6bEDUfQ7jb7fo5Y3lB0pryPmYl7BqAfzxYN+ydYMHhCRc0XBxeAggVIIcXcqqwmGxIQypMJO2XZdvAoMzPjam1SVGjmIgLuLRNldOmCwAt+6Bmid9ImXZsG0qKjMvdi9wpSYpVWrbweu1pTexrpT0l8XwxA6146J3zbEt+6pkvknAWxp1cskzJcfgYTpLw7t/JGoJPc8Xh7TK+69Sf+J3/KMOascIEHyWZLpdROFkArR/U8mqZ11czKNnJdC9Imx2ogbdKxKo8Ic2Zk5jtjgTckFRQViVUc7jXvrwY3/UcFCD9a33WtDkXg0a+uWFQpFw8sIzKNrO/MrnDwHx1pqtkMo1C7WNYZW7V5ydgHaxvMm6eWegDAAA=" + } + }, + { + "ID": "5d6751072423e5c1", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0003?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "9237b689849257d6563284bf3aeca61c/10243244556375919441;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0003?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:42 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndx193:4244,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ea08W8m6ONb-qAX8y7zwDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "2284cd2a80d093fb", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ab43a601951f7c709ae870ff33fa8d58/8707239619206336634;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwNCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:42 GMT" + ], + "Etag": [ + "C1K5ijfm2kPwrWSRXUPg9g==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnab123:4382,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/4,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Eq08W9HoDM2ZqwWqw42ABQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/4,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/4" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1R0W7aMBR9z1dE3ivFSQhQkPoAbVShMUAh1SZNFTLJJXVJ4sw2y1DFv8+xE4khOpEXx+eec+651x+Wjfa0SNDYRlua/joAP36RZJsB6qgSSJLWpUf3a5++73Jvv6r493X442WVjtKHB02iWp0cshjkXcm4vBsOvHFCJBEgN57j3jtDx9/4zmDkje7dvj/0hu7GUV9Xd/ofxdcdBGS7OS32dZ83KUsxxriqqm7KWJoBKanoxizH7QD4t4dLzt4hlgJf5MJNLoFvCYh1QIFvzKlpIeyAQxGDSvth2TZqosyuramWKdBEaRg35DIy3c6Ibgho2Se9y/gNctJm21HIEqFuP9VNQwosSF6HN2fHYPJYamwdhbPFM6rBU+ea5pCLS81sEQXPQdjCOUs0HAarYBIFT5+bcYgvvcLgcRk+tei/8Vv9mcOWsawhn5lMl8t5MFkgg5/08Wo1v6/NotQk06OE2h3phdfInBVpBPxKJWTVGRBzIJKyIqImh9vvqYfpuZ7n9w0D/pSUX+MMRr3mhVFGhPzGEqrGTD53aqeKJtN5YHQs1s41+rJG1sn6CxU2npXoAwAA" + } + }, + { + "ID": "37c510fd2584ebf7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json\u0026pageToken=", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6b32fd8d4f347ec01136eac4c88d36c8/17090553238410932878;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json\u0026pageToken=" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:42 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/uEjjVAJ6PF6dHuxffcql1XOdPGs\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:42 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncp68:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/86,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Eq08W-ikIMfxqwWB3a6ABA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/86,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/86" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2RUW+CMBSF3/kVpHudW1sKiG8uMudiMmPcYjYXgnDRIgJCyWTG/75S2JtbbNKXnnPP+Zp70nS042mIBjpa882hgqK+Ef46gSkvBbqVMgh/08grZNOdPfqGGtyEldtkO47S95H3+rXE7L5y4/ht+GzNHq3wqTpGUXBIyPIlnI3LFVI5KrWUSR+arp/k/au5cUuRKymskgBEL88K0bMtOgh94ZcgPIpJH9uYeQxbDnX6xGQ2tYmH5blTMf9ZWNehjHOIoIA0ANmnuHSUF1kMgZhcQmhH5XNL0nmuwPodVKXt2BWgzcy5o63zhhEthg9Tt/tBUIAveJYu+F5pxDRklEEoZWbXiOCY8+KSy3KMhkuazpr+qZaUCT+ZCNg3iyLaWfsBOWK2JyECAAA=" + } + }, + { + "ID": "58745850c0020bc5", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0004?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ea5419d1ccfc6ae75d1892bb730bcedf/15554548296946448311;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0004?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:42 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmh22:4372,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Eq08W9igMcbRqAWE-JmIBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "37dae5f8d58cd171", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f5ffa43d54ec2740092c6c4653fcddbe/13946487960467291104;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwNSJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:43 GMT" + ], + "Etag": [ + "oLLRM/oyn2xZqxsRGaEBbA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnid10:4261,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/220,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E608W6CcAYTXqwXor6K4BQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/220,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/220" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TTW/iMBCG7/kVkfdKcT6AFKQeYBshpBQqN3vZVYVMMqTeJnEamwVU8d/XsROJRXRFLo5fzzvzzFj+tGz0zsoUTWy0YdnHDurjN0k3OaCeOgJJs+aIRxF5wvxYeoefHwdB5jScbaYPDzqIaXe6yxOQdxWv5V0w8iYplVSAXHuOe+8EzmA9cEZjb3zvDgeBF7hrR319Xel/IUNdQUC+jVj53tR5k7ISE4z3+30/4zzLgVZM9BNe4K4B/MfDVc1/QyIFvuDCLZfAtwBiDSjwjZw6jMAWaigTULSflm2jFmVxbUyNTYkGpY24gcvYdDljugHQsk96lskbFLRj2zLIU6F2v9ROS0osadHAm7VnNHmstPYSk8Vyjhrx1Lvm2RXi0rNYxuE8JJ1c8FTLJHwOp3H4+HWyGpLLXCT8viKPnfovfuc/y7DhPG+Dz5LMVqsonC6R0U96ebXa39d2UKqT2VFCkx3pgTdKxMsshvrKCeH7MyGpgUrGy5gZDnfoq4vxXc/3At88rUPF6msxo7Hf3jDKqZBPPGWqzfTrTF1X8XQWhcbHE525UX+8IOtk/QUZ2nSH6AMAAA==" + } + }, + { + "ID": "163df1b46c35fc44", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/insertAll?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "215" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d82e0c6752b08d9c247b30d4c80f92a4/12338426524476506121;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/insertAll?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyb3dzIjpbeyJpbnNlcnRJZCI6ImEiLCJqc29uIjp7Im5hbWUiOiJhIiwibnVtcyI6WzBdLCJyZWMiOnsiYm9vbCI6dHJ1ZX19fSx7Imluc2VydElkIjoiYiIsImpzb24iOnsibmFtZSI6ImIiLCJudW1zIjpbMV0sInJlYyI6eyJib29sIjp0cnVlfX19LHsiaW5zZXJ0SWQiOiJjIiwianNvbiI6eyJuYW1lIjoiYyIsIm51bXMiOlsyXSwicmVjIjp7ImJvb2wiOnRydWV9fX1dfQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:43 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/180.esf,ybn130-v6:9887,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/63,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-streaming-frontend-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-streaming-frontend-thermostat\" pick_cluster_params { allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1983::]:4159 had response of OK with response time of 0.028064727783203125 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybn130-v6:9887,/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/180.esf,ybn130-v6:9887,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-streaming-frontend-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/180" + ], + "X-Google-Service": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0zCtOLSpxzMkJSi0uyAdylLhquQAsgTGpNAAAAA==" + } + }, + { + "ID": "8a283447da836b9b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0074a0c137bc3f3a148ffca7573fc2ce/2347052568776041245;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:43 GMT" + ], + "Etag": [ + "xEeYVBhYYBwIglByLbz8Iw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4319,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14.esf,ybpj18-v6:9842,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/70,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"jl\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e708::]:4095 had response of OK with response time of 0.012238025665283203 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpj18-v6:9842,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14.esf,ybpj18-v6:9842,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqalwTY0Mc8qIjHQq90zPcar0Saqy8Cy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Je4BmOuMBLpl7gGQ39wOMCsWq5AIzY7z0oBAAA" + } + }, + { + "ID": "79ec652ca42dfbf3", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0fad692243ea1e0e31fca9b9fba72547/10802421583012087089;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:43 GMT" + ], + "Etag": [ + "oLLRM/oyn2xZqxsRGaEBbA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:43 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vned124:4463,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E608W4H8KpTrqgWAr42YDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYNgwrn5KWDhINcAV8cQVxfchhWlJqObFeTq7B/kAhNFdT5MP5IJSfn5OVDFSIY4+fv7uDr6KUHEa8FULBeUCWTUctVyAQDcRh1+LAEAAA==" + } + }, + { + "ID": "b8af68351330c76b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7b6b49dcaca5f79abb3be97b177acda3/738991132802033478;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:44 GMT" + ], + "Etag": [ + "xEeYVBhYYBwIglByLbz8Iw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4319,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14.esf,ybl207-v6:9854,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/88,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"na\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f0d7::]:4151 had response of OK with response time of 0.013895034790039062 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybl207-v6:9854,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14.esf,ybl207-v6:9854,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqalwTY0Mc8qIjHQq90zPcar0Saqy8Cy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Je4BmOuMBLpl7gGQ39wOMCsWq5AIzY7z0oBAAA" + } + }, + { + "ID": "09938a7521a12747", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7be58ce82f28fc73d86f028f02802100/9194361246516152922;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:44 GMT" + ], + "Etag": [ + "oLLRM/oyn2xZqxsRGaEBbA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:44 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vni65:4255,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/96,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E608W6DrOovKqgXfmLagDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/96,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/96" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYNgwrn5KWDhINcAV8cQVxfchhWlJqObFeTq7B/kAhNFdT5MP5IJSfn5OVDFSIY4+fv7uDr6KUHEa8FULBeUCWTUctVyAQDcRh1+LAEAAA==" + } + }, + { + "ID": "4c24e917d5ec2115", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "309" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cf6b5cb907771d4131c75b17742160f5/7586299814837046915;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7ImRlZmF1bHREYXRhc2V0Ijp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9LCJxdWVyeSI6InNlbGVjdCBuYW1lLCBudW1zLCByZWMgZnJvbSB0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDA1IiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJqZEZkajEyTTE2THVibHpOR0l3WDJYdUJWWUQiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:44 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/huGHoNA6igXprDrMrAslxnA5_ck\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnb21:4037,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FK08W7GZHNbRqwWDg5e4Cg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U226bQBB991cg+pqE5Y4jVa1rqGvJ8YMvqVtFQssy4LWBxewS14n8712wyaWp0j6uZs6cy4z2saeoW1rE6rWiRjTd1VAdPmxYpF7IAgicNoU71TW2rv8ABwgyi6+z9Sgpfvrhcr9ClrauR9/YdODQdFVWfnVTDXj2qxjYIdneqe0c2o6P64yAuCxZJS5dx7hezq828dd4oxs3ujOpo+xhOhrvV8aq/nL7w2+BHLJkQottA18LUfJrTdvv91cpY2kGuKT8irBc64Rr94ZWVmwDRHDtDzpNmuLaO4SfMkawoKz4uJy35BIwgwQqKAhIAY89RVHP08d/M9SAWtSp+g9vitrxNc2SsqccG1bCioSmddWVWtrW3fnx/GrikWKUAudwoRR1zi+UCoiSVCxXBI4yCA2ke8hFVmghp2/0Pd22XMPVQ4SQ3aqQLoALWrR0iwbT0fyH2QaNBeZw7gmJl3goQv1I96IEGQkktmsRQDbywEYWJE7suY6DO3Qr8oTFBStc4nmxbZMQJbEZWrbrhhExUYhM17JRYtnSgtogjyfppAIswKe8ZJx2UQ5nwWARhOOv4TQI/MA/29xX9E3r99lYdi5my+lQQp7ySHCdCf9k7DmNV07Pj/fyRZ3Jd2N84aasKJMi282Op4tgNhguxredrJrDBFJMDvNdJjsSnHGQheP5bLjAoubdvTSvZpGqtDYdT0fqiy4q102eOtsIm9XTvAXotin9mLoh03dOdypBlXhb95B1qr8+zpY6h0IsDmWLmAeTYLhQX2iVVqoQckwbI6rpOGa/b5q6ZV/KuxFIZ7noY8Ptb4mZkpTu1rtt5kUsWtefY7iHjJVQXaVyyD0lgAlhdSGaf0DtHXu/AQct+zjQBAAA" + } + }, + { + "ID": "df665ebe421be96d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jdFdj12M16LublzNGIwX2XuBVYD?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e747476cee07da69848e298c65d16695/6050294873372562348;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jdFdj12M16LublzNGIwX2XuBVYD?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:45 GMT" + ], + "Etag": [ + "7wmnb7B1HqepRvm9CHqoSw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4215,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/45.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/45.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/45" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41SXU/CMBR9369Y6iskgglEEx8YVFgyBxYwGOPD1l1ws1vH2rkg4b+7lq+hSHxqzzm359zedm2YJvoIkwDdmcgPF8scstXVAuST2hAQOZOiXFKeCEA1VQ3SW6jqdhEnfttqDJaQks/4tjtY8nFxf7+tEvQdYq+sW5eoxPMQWCBK/KqxueO1lngxKEe91o68XKWaH0+I7farSswDrbhTx+lYDkY7aVO74J7H4py77U5wH5Nz9gSPcGeCe/+xz4Cecye4OyS9i71XtF9TOg07CfQ5Z5WzJ6nWcOjgjvtT/mts+m6H/ZtRZRTS90YR9wnMIYOEwvFh04xHQKWtf1CQMwqynvJM1tut5i5fndzqUfAQRI3mY6Pl5D77cvt2MWvOcuv5ZT8ixDj1ZMgTVT4do0O65NJjhBdqOugGHTlrJUGMMk5BCNAp12jfb5fHKQOp2pVZDpqmXvk1B6EsubnHBBgb4xuKyt5FBgMAAA==" + } + }, + { + "ID": "d9348c011a61a78c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon7c88d55c_0fd3_4577_bc30_037450f45929/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e747476cee07da69848e298c65d16695/14433327021878638272;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon7c88d55c_0fd3_4577_bc30_037450f45929/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:46 GMT" + ], + "Etag": [ + "MZtgdqKEjBUE4Gn9nPFxsw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4246,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybna17-v6:9807,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/106,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e1b1::]:4420 had response of OK with response time of 0.014366626739501953 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybna17-v6:9807,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybna17-v6:9807,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfGNKklPKfR2zXIKdTVxz7PMC3CrKC63tYWoKskvScwJyi8vBik1hogVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Je4BmOuMBLpl7gGQ39wOMCsWq5ALCfG2QoBAAA" + } + }, + { + "ID": "b886e5f31b914f47", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "309" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c7070bf8e6f0f271de1d987c4c2b4e93/12897322080430930665;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7ImRlZmF1bHREYXRhc2V0Ijp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9LCJxdWVyeSI6InNlbGVjdCBuYW1lLCBudW1zLCByZWMgZnJvbSB0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDA1IiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJKQ09IaTY0S3hncjh5UkZvNnZCM0RoQ09OUEEiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:46 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/XfnzSI5LKkT27DsoUf_KF6m_erg\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbr70:4356,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Fq08W7CxFIqcqwXBmbTYBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2UXW+bMBSG7/MrELtti/mGStPWBbpljdIpIVs1VULGORA3gAk2ydIq/32GhH6sU7dL65zX7/scH/lhoKgrWi7Uc0VNaLZuoN69u2OJeiILIHDWFm5V11i5wT3sIMwtvsyXn9PyZxDPtzfI0m7S8n42ssdXq8hwA87maXx16RQx1Nmt2t1Du+sXTU5AnFasFqeuY5zPZ2dfh9dfqGNd/cpqbze9ZM7mkxksh9eTbxedkEOejmm5auVLISp+rmnb7fYsYyzLAVeUnxFWaH1wbWNoVc3ugAiu/WGnSSiuvWH4IWcEC8rK9/NZZy4FU0ihhpKADPAwUBT1ePvob0CtqFMdqv9gU9Ter22WlgNl37oSVqY0a+q+1Nl2dMfD06kdjwyjlLiAE6VsCn6i1ECUtGaFInCSQ2wg3UMusmILOb7he7ptuYarxwghu0shKYALWnZ2Uavpbf4DtlVjgTkce2LipR5KkJ/oXpIiI4XUdi0CyEYe2MiC1Fl4ruPgXt2FPGhxyUo3ReD5rhOnvuXFFsFW7JuepDANAMPA2POR2ir3h+ikBiwgoLxinPajHE7DiyiMR5fxJAyDMDhibmv6qvXHdCQ7o+l8MpSSx3mkuMlFcAB7msYL0uPhrfmiHvLNMT6jqWrKZMjuZUeTKJxeDKPR9z5Ww2EMGSa72TqXHSnOOcjC/rg2XGDR8H5f2lP7kKpEm4wmn9VnXVQ+N3ns7EbYPj0tOoFum5LH1A3HspzDnkpRLV7XPcM91F8uZ2ddQCmiXdUpZuE4HEbqs6wSpY6hwLQFUU3HMX3fNHXLPpV7I5DOCuFjw/VXxMxIRtfL9Sr3EpYsm48L2EDOKqjPMnnJhhLAhLCmFO0/oA72g9/oUnGG0AQAAA==" + } + }, + { + "ID": "334ad1dfbe07c6ac", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/JCOHi64Kxgr8yRFo6vB3DhCONPA?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "649f8a6332958fb1040fd209806df65f/11289261743935061777;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/JCOHi64Kxgr8yRFo6vB3DhCONPA?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:47 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/XfnzSI5LKkT27DsoUf_KF6m_erg\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:47 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfl7:4300,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/28,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Fq08W6D3NsjUqwWA67fYCw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/28,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/28" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1STW+cMBC9769APm8k4zVgetsCaWkjUrGkPSLjHW+JWLwxpu0q2v8e85UPRYp6nJn3NO+9mceVg+5VlYMEDa0A9Ml5XDkOOml1D8Kke9tA+74RYK5OSpurwCdoPSAsa5p+i26/1j79/u+g2Tm/Vv6fz5v4d3Sb/dhOyEYJbmrVDuC7HVo5F9tGQrWyPvR6GY1rH3rQ57l4qVAHjRXjtPwIa6ftj93a0SAcqdXRMbxqoCTYZTjAtKTYD0nIXI8GJHBLjLE3qrAuoDN1O64rBs6y5j/MDmxueAczphRMMlzhsHJZJTGRIL2ACsAeZuBhCtLfs8D3+cIeRU5c3qo2kBhYGPilDCkrqeC0DDfMutgQAEI4ZyFGA/MySRcauIG47k6qq5coozzZFkmZXpdZksRJPNv8q+t30F95apFFfpdFlvKch+R9Y+LJ2Esab5zOxUf54sXkhzG+cnPStbIix8umWZHk26hIfy6y+g5u4MDFeffQWITkTQd2cJnfpjPc9N3yL0M1HBJZa1mafUGvULU9t3hGjhEOp6+PI8H1NtbPxiU+pf70p5akzfs5I8E0f/uc4+ojtKY4n0bGLrlJogLNWleX1RMtaoSTXAMAAA==" + } + }, + { + "ID": "b668ba57a7babd7d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/JCOHi64Kxgr8yRFo6vB3DhCONPA?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d980570df21f870531faea525c356e61/9681200307944277050;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/JCOHi64Kxgr8yRFo6vB3DhCONPA?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:48 GMT" + ], + "Etag": [ + "TolNtj17H7waXnj86nPX5g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4351,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/1.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/1.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/1" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41SXW+CMBR951eQ7lWTbW5olvgAyNTNgKuYmCx7QLgirFBGyxwx/vfR+oWbM3tq7zmn59zedq2oKnqP0gA9qGgehR8F5OVVCPxFbDCwgnBWLRlNGaCGUAP3QqF2KbF5fNMetFfeLI07Wjqe3Yfd7lbF/CUkXqVbV1VVLyIgAavqV1mrO1xyqZeAcJRr44jzMpP4xMVDu19nEhpIxp6ORroxstCO2jQuuBcJO+c+tF2rb+Fz9tgaW7pr9f5jn4N/zh1bpoN7F3uvcb+mdBp2EjinlNTOnqQajjOydPsn/dfY5N0O+zeljohK3hvFdI5hATmkPhwfNstpDD4fyh8UFMQH3sxozptt7XaXL05u+SfTGUTa3fNXmHdK/Ei1T6PVW5qOPdb3WkJ9j0c0FfLpBB3SOeUewXQlpoNa6IgZJQc2zqkPjIFMuUb7fk2aZAS4aJfnBUjY96qvOYh4hS08wkDZKN9jFvj5BgMAAA==" + } + }, + { + "ID": "6696b8f7c0f1f679", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon7f0e8976_f948_4ca4_938e_232ee22aa890/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d980570df21f870531faea525c356e61/18136288955288266319;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon7f0e8976_f948_4ca4_938e_232ee22aa890/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:48 GMT" + ], + "Etag": [ + "RrZX8rhoDmI7JwU9YX39ug==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4233,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybqv4-v6:9825,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/33,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:5e01::]:4429 had response of OK with response time of 0.012795686721801758 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybqv4-v6:9825,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybqv4-v6:9825,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQkqioqwKMrId8n1NPcqD7WMjDC2LE23tYWoKskvScwJyi8vBik1hogVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Je4BmOuMBLpl7gGQ39wOMCsWq5APRoXosoBAAA" + } + }, + { + "ID": "5032dc7bb4aa05ba", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/JCOHi64Kxgr8yRFo6vB3DhCONPA?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5c4fbe926fddb2bcc51ebed0496676ae/16528227519297547127;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/JCOHi64Kxgr8yRFo6vB3DhCONPA?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:48 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/J6kGeRLb1PCdON5KT4DH93MMh8o\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:48 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnll65:4019,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/180,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GK08W4CFKMaTqgW0_IGADg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/180,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/180" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1XyW7bMBC9+ysEIYcWSAKSojYDPWRx0QBxHTgOcigKQ5HoRK0sqSKVNAjy7x1qXyzZt1zqg2HOvJl5IjlP47eJonLhiJSrU+VtouQrBgv1cvF9pk6U9+MC4nPhuxXMTRiYonDlbzM01jVkIg0Tg1JDPS4yJaLvt4iZ+1no9bymbWu5V0TCCc5fBeM3SeQyzpknkSj3/klZ8lpwKVY3gROC5Ye05HbwhE5e4BahqXIVxqnI4qXLb+RTSrpz3qaDKK4AwLfrJnod/+L4Yin35Oz5EVDoFAHIJti2dAvrBiKWpjexc54DVdxPMXf+HpYiBzZSwLF4bRZNT1UT9QOqmu2AokId4EZb2EbWLqLp1EC2TWxkWghT0gFXdfWdaarSe9MUbOo0L4nf5WJoFrFNyyQaRZToegtaMbF3pKh47ElRsKhT8Kd0swnYIhVAM7u00m/REcBt7AcB87qH4UaJx5ew89KhdR33UF+wsO2LncSBVEF2vXn7OsiNC5hg3s0IqBIA9WIxv7merWYNF4t51VVVX4Hntx9mHJezs8sSLgPSh24MWI/wVHbisXJEpmG65fBDm8IznR75fB1GYh2mQQBGmhkfoiioUyrq1+VirgjnIWBrgrAFPUHXFBlwTaArqElMvEYI6WoZ8bP48X48yPp+eVU/5QhtyViSldyalFYLZb2GfXtkCK2j7FT75SeNZcGlr0t4quS3oiNMeFyYKLLHhEkvRDbLJ4/8VnJtPh7cuoLfgIDhWgnackUHBasXUsoT/WiBwho1dWyaGqK2QXU6pE/YHhWovWnKB96lLiUXo/MZ0Cc6IlB7UpT6NKY/mYYY/wUqtw53eiY++3t9WGok5btDxIYgZfpF+XREJY3PH6Rm+DA1k9+5cKgM5sItzIze0LAGsxyMeYEfst54xgInBmyuXpZea1Y2/d0GUaF7BNU3NQa588PHu9AfvEs7fI4r/GfWcOxS5RYdDFPIEB+N4EE+aIQP2censa9ZSegK4cs5e2Bjx4bkpvu828vqA1iA9spniVTuRkD9mNgs0a7jPrFvvgDrxgk4y60J27CEhS7zVvLtzHvnGyfRL+aKq6yylwYuEydxlIgT06i3wnOEw1kJyhdjL/rGqciqedyh40Fzh7O/OlsWitVrnL+NZ9ezi5UEAux98j75By6pwZUfDQAA" + } + }, + { + "ID": "3ecc8f8490f372f7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b85a446e49d638c79a97f3854b51ada6/6537135034295537036;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Etag": [ + "xEeYVBhYYBwIglByLbz8Iw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4042,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybfk130-v6:9855,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/118,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"jl\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e341::]:4447 had response of OK with response time of 0.014356851577758789 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybfk130-v6:9855,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybfk130-v6:9855,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqalwTY0Mc8qIjHQq90zPcar0Saqy8Cy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Je4BmOuMBLpl7gGQ39wOMCsWq5AIzY7z0oBAAA" + } + }, + { + "ID": "00ef2bfa8678d534", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cd1ad811a5d5335b84d1965c462d145d/14920166083306762400;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Etag": [ + "oLLRM/oyn2xZqxsRGaEBbA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncw1:4438,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/51,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GK08W8jJM5HGqAWElonYAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/51,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/51" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYNgwrn5KWDhINcAV8cQVxfchhWlJqObFeTq7B/kAhNFdT5MP5IJSfn5OVDFSIY4+fv7uDr6KUHEa8FULBeUCWTUctVyAQDcRh1+LAEAAA==" + } + }, + { + "ID": "c4444343b4849217", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6197eb5b56dc4077e84d64b17a355fba/4929073598304752309;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Etag": [ + "xEeYVBhYYBwIglByLbz8Iw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4115,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/27.esf,ybfd126-v6:9887,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/57,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"el\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:643::]:4099 had response of OK with response time of 0.013188838958740234 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybfd126-v6:9887,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/27.esf,ybfd126-v6:9887,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/27" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqalwTY0Mc8qIjHQq90zPcar0Saqy8Cy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Je4BmOuMBLpl7gGQ39wOMCsWq5AIzY7z0oBAAA" + } + }, + { + "ID": "43b5e2b4e8c61f2d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d718f0b1492038f999d5f30be8b34718/13384162241337128393;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Etag": [ + "oLLRM/oyn2xZqxsRGaEBbA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlx6:4349,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/150,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ga08W4HNEMTbqQXC2IDoDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/150,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/150" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYNgwrn5KWDhINcAV8cQVxfchhWlJqObFeTq7B/kAhNFdT5MP5IJSfn5OVDFSIY4+fv7uDr6KUHEa8FULBeUCWTUctVyAQDcRh1+LAEAAA==" + } + }, + { + "ID": "38f91409045f5a51", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3a2a0d1e0dfae8b47eee3c23c08826f4/3321013261808883677;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:50 GMT" + ], + "Etag": [ + "xEeYVBhYYBwIglByLbz8Iw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4139,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26.esf,ybx5-v6:9804,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/114,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"oj\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"ws\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:2705::]:4309 had response of OK with response time of 0.01762700080871582 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybx5-v6:9804,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26.esf,ybx5-v6:9804,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqalwTY0Mc8qIjHQq90zPcar0Saqy8Cy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Je4BmOuMBLpl7gGQ39wOMCsWq5AIzY7z0oBAAA" + } + }, + { + "ID": "4869b8d54cc81e06", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "9ef747cfb1572060b54cec468841f74e/11776100805363120626;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Etag": [ + "oLLRM/oyn2xZqxsRGaEBbA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:49 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnt135:4087,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/172,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ga08W7mZKcPXqQXFuI3QBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/172,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/172" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYNgwrn5KWDhINcAV8cQVxfchhWlJqObFeTq7B/kAhNFdT5MP5IJSfn5OVDFSIY4+fv7uDr6KUHEa8FULBeUCWTUctVyAQDcRh1+LAEAAA==" + } + }, + { + "ID": "c274b694acf2ce18", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "37ae502bb960f9fc89d9111e330aaea7/10168040468867251738;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0005?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:50 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnp127:4499,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/110,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Gq08W5GzCcO_qgWW7LrADA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/110,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/110" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "d33d6cb6e2fdd594", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "1958" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "90d670538f1f985f66540177f30ac137/8632035531697668931;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJtb2RlIjoiUkVRVUlSRUQiLCJuYW1lIjoiTmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJCeXRlcyIsInR5cGUiOiJCWVRFUyJ9LHsibW9kZSI6IlJFUVVJUkVEIiwibmFtZSI6IkludGVnZXIiLCJ0eXBlIjoiSU5URUdFUiJ9LHsibW9kZSI6IlJFUVVJUkVEIiwibmFtZSI6IkZsb2F0IiwidHlwZSI6IkZMT0FUIn0seyJtb2RlIjoiUkVRVUlSRUQiLCJuYW1lIjoiQm9vbGVhbiIsInR5cGUiOiJCT09MRUFOIn0seyJtb2RlIjoiUkVRVUlSRUQiLCJuYW1lIjoiVGltZXN0YW1wIiwidHlwZSI6IlRJTUVTVEFNUCJ9LHsibW9kZSI6IlJFUVVJUkVEIiwibmFtZSI6IkRhdGUiLCJ0eXBlIjoiREFURSJ9LHsibW9kZSI6IlJFUVVJUkVEIiwibmFtZSI6IlRpbWUiLCJ0eXBlIjoiVElNRSJ9LHsibW9kZSI6IlJFUVVJUkVEIiwibmFtZSI6IkRhdGVUaW1lIiwidHlwZSI6IkRBVEVUSU1FIn0seyJtb2RlIjoiUkVRVUlSRUQiLCJuYW1lIjoiTnVtZXJpYyIsInR5cGUiOiJOVU1FUklDIn0seyJtb2RlIjoiUkVQRUFURUQiLCJuYW1lIjoiU3RyaW5nQXJyYXkiLCJ0eXBlIjoiU1RSSU5HIn0seyJtb2RlIjoiUkVQRUFURUQiLCJuYW1lIjoiSW50ZWdlckFycmF5IiwidHlwZSI6IklOVEVHRVIifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJGbG9hdEFycmF5IiwidHlwZSI6IkZMT0FUIn0seyJtb2RlIjoiUkVQRUFURUQiLCJuYW1lIjoiQm9vbGVhbkFycmF5IiwidHlwZSI6IkJPT0xFQU4ifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJUaW1lc3RhbXBBcnJheSIsInR5cGUiOiJUSU1FU1RBTVAifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJEYXRlQXJyYXkiLCJ0eXBlIjoiREFURSJ9LHsibW9kZSI6IlJFUEVBVEVEIiwibmFtZSI6IlRpbWVBcnJheSIsInR5cGUiOiJUSU1FIn0seyJtb2RlIjoiUkVQRUFURUQiLCJuYW1lIjoiRGF0ZVRpbWVBcnJheSIsInR5cGUiOiJEQVRFVElNRSJ9LHsibW9kZSI6IlJFUEVBVEVEIiwibmFtZSI6Ik51bWVyaWNBcnJheSIsInR5cGUiOiJOVU1FUklDIn0seyJmaWVsZHMiOlt7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJTdHJpbmciLCJ0eXBlIjoiU1RSSU5HIn0seyJmaWVsZHMiOlt7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJJbnRlZ2VyIiwidHlwZSI6IklOVEVHRVIifV0sIm1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJSZWNvcmQiLCJ0eXBlIjoiUkVDT1JEIn0seyJmaWVsZHMiOlt7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJJbnRlZ2VyIiwidHlwZSI6IklOVEVHRVIifV0sIm1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJSZWNvcmRBcnJheSIsInR5cGUiOiJSRUNPUkQifV0sIm1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJSZWNvcmQiLCJ0eXBlIjoiUkVDT1JEIn0seyJmaWVsZHMiOlt7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJTdHJpbmciLCJ0eXBlIjoiU1RSSU5HIn0seyJmaWVsZHMiOlt7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJJbnRlZ2VyIiwidHlwZSI6IklOVEVHRVIifV0sIm1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJSZWNvcmQiLCJ0eXBlIjoiUkVDT1JEIn0seyJmaWVsZHMiOlt7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJJbnRlZ2VyIiwidHlwZSI6IklOVEVHRVIifV0sIm1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJSZWNvcmRBcnJheSIsInR5cGUiOiJSRUNPUkQifV0sIm1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJSZWNvcmRBcnJheSIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwNiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:50 GMT" + ], + "Etag": [ + "pDANmbJ2TK8DRjAWzlKWeg==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnj66:4151,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/59,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Gq08W7CiGY3_qgWMk5KQBA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/59,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/59" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2XT3OiMBjG73wKh71awT9V60wPWGmHrWIXcTo7OztOCimlBcKGWNft9LtvCOhWTAQ97aFehIfkl+cNTybhTarJL37kyoOa/OB7v5YQr78Q8BBAuU4fQQK89FE80szw4WvLvu2PrGft/k9wew+9y0vWyGe93WXgQHIWI0zOet3WwAUEJJAsWmqzr/bUzqKjdi9aF/3meafX6jUXKv012EiHmnTZCAkMHsd+9JKO80RInAwUZbVaNTyEvACC2E8aDgqVTQHKa0uJMXqGDkmUgi8l95UoVQwqzGCiVPTJmlnwEWIYOZC6fZNqdPIyKwZvmtJuVMys5C0q+Mq6seGyThUMSrV3NpfOEwzBxtujDwM3oXc/6B2TqBiBMDUvm+l/PdPIOmbazLYM82ajhshlqqV/mxuWPpJT+b3OYQ3XBCZF2PC7rc+OZxkRgR7ERZph2vqNbh3Puw4QIEXa9Xiq2SfUiRBNZLRX6XQ61jXzeJ7thzAhIIyLRNuY6DNbm9wdzxwBsvdeR5qtn+aOZ+w0Tzxa6us0orkMIfadItCcT3TLuDqeNyPYjzwNY7CuuijudOq+PMhcpjDNZVCWZi5SEOkyYB5pLlKY6zLoNtdc7IFwl4HTIHGZ/IRX8Sm0eJo7IVKc9TJsnnUuVRj4MqgFHYTdIs7Sr6bWSLh8cn13X9mg91ZT3ly0lETrc2OYw90xLbItNs6x/m8Q4RZ0YOEeKoKWkV/8rFTVx9dbqbT8Df8XpUnbmwNh4wZYlLid6j4T95k4ceIkdsHOv9EyzM6iFMHO0akyRpFnQ8x5YqHVB8HBEBAfReykQtXmeZuet9vNttrtNrMvpt+xj3ltuhft/OAuByAhE+T6dHJcMWm70WjDsZ71Qw4jp+p8Jkvv0l8cSL51vw0AAA==" + } + }, + { + "ID": "6433a67cdffa1c52", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006/insertAll?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "1210" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "244986d689203291304671eb5192c38d/7023974095706884204;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006/insertAll?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyb3dzIjpbeyJpbnNlcnRJZCI6Ik51RzRrTUV0ZXkwbUNhdVhiek5Bc1NYVHh6UyIsImpzb24iOnsiQm9vbGVhbiI6dHJ1ZSwiQm9vbGVhbkFycmF5IjpbdHJ1ZSxmYWxzZV0sIkJ5dGVzIjoiWW5sMFpRPT0iLCJEYXRlIjoiMjAxNi0wMy0yMCIsIkRhdGVBcnJheSI6WyIyMDE2LTAzLTIwIiwiMTk5NC0wNS0xNSJdLCJEYXRlVGltZSI6IjIwMTYtMDMtMjAgMTU6MDQ6MDUuMDAwMDA2IiwiRGF0ZVRpbWVBcnJheSI6WyIyMDE2LTAzLTIwIDE1OjA0OjA1LjAwMDAwNiIsIjE5OTQtMDUtMTUgMDE6MDI6MDQiXSwiRmxvYXQiOjMuMTQsIkZsb2F0QXJyYXkiOlsxLDEuNDFdLCJJbnRlZ2VyIjo0MiwiSW50ZWdlckFycmF5IjpbMSwyXSwiTmFtZSI6ImEiLCJOdW1lcmljIjoiMC41NzAwMDAwMDAiLCJOdW1lcmljQXJyYXkiOlsiMC41MDAwMDAwMDAiLCIwLjYwMDAwMDAwMCJdLCJSZWNvcmQiOnsiUmVjb3JkIjp7IkludGVnZXIiOjI0fSwiUmVjb3JkQXJyYXkiOlt7IkludGVnZXIiOjF9LHsiSW50ZWdlciI6Mn1dLCJTdHJpbmciOiJzdHJpbmcifSwiUmVjb3JkQXJyYXkiOlt7IlJlY29yZCI6eyJJbnRlZ2VyIjowfSwiU3RyaW5nIjoiZW1wdHkifSx7IlJlY29yZCI6eyJJbnRlZ2VyIjoxfSwiUmVjb3JkQXJyYXkiOlt7IkludGVnZXIiOjF9LHsiSW50ZWdlciI6Mn1dLCJTdHJpbmciOiJmdWxsIn1dLCJTdHJpbmdBcnJheSI6WyJhIiwiYiJdLCJUaW1lIjoiMTU6MDQ6MDUuMDAwMDA2IiwiVGltZUFycmF5IjpbIjE1OjA0OjA1LjAwMDAwNiIsIjAxOjAyOjA0Il0sIlRpbWVzdGFtcCI6IjIwMTYtMDMtMjBUMTU6MDQ6MDUuMDAwMDA2WiIsIlRpbWVzdGFtcEFycmF5IjpbIjIwMTYtMDMtMjBUMTU6MDQ6MDUuMDAwMDA2WiIsIjE5OTQtMDUtMTVUMDE6MDI6MDRaIl19fSx7Imluc2VydElkIjoiclVyT2JrbUE4NzNEV1M2VmtPZFJDU1ZTZjBEIiwianNvbiI6eyJCb29sZWFuIjpmYWxzZSwiQnl0ZXMiOiJZbmwwWlRJPSIsIkRhdGUiOiIyMDE2LTAzLTIwIiwiRGF0ZVRpbWUiOiIyMDE2LTAzLTIwIDE1OjA0OjA1LjAwMDAwNiIsIkZsb2F0Ijo0LjEzLCJJbnRlZ2VyIjoyNCwiTmFtZSI6ImIiLCJOdW1lcmljIjoiMC40NDk5MDAwMDAiLCJSZWNvcmQiOnsiUmVjb3JkIjp7IkludGVnZXIiOjB9LCJTdHJpbmciOiIifSwiVGltZSI6IjE1OjA0OjA1LjAwMDAwNiIsIlRpbWVzdGFtcCI6IjIwMTYtMDMtMjBUMTU6MDQ6MDUuMDAwMDA2WiJ9fV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:50 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/122.esf,ybky3-v6:9837,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/89,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-streaming-frontend-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-streaming-frontend-thermostat\" pick_cluster_params { allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"wa\" allowed_cluster: \"jl\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:d16::]:4411 had response of OK with response time of 0.012339353561401367 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybky3-v6:9837,/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/122.esf,ybky3-v6:9837,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-streaming-frontend-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/122" + ], + "X-Google-Service": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0zCtOLSpxzMkJSi0uyAdylLhquQAsgTGpNAAAAA==" + } + }, + { + "ID": "75014096dc0beb33", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a4a33d4017165980414b018dea6954f2/15479344209437780608;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:51 GMT" + ], + "Etag": [ + "SmB04Q6NVIMDa87sDZsq8g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4357,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/20.esf,ybbf184-v6:9885,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/82,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jx\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:2e46::]:4246 had response of OK with response time of 0.013756036758422852 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybbf184-v6:9885,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/20.esf,ybbf184-v6:9885,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/20" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/+1YUU+DMBB+368g+LqRtisMlvhi9mKiJupiosseWGTLIm46Oo0x/ncpU9eVHrkhLCTaBwLX767ffS0H7XvLsuyH+eLe7lv2ZD57XkertyMRTuJoEIrwbJ4Iuy0xkQhnEnP9eEL4pXdxc3o+CP1eMrhLnv3Z8fEGJZYijK+Wr4mEko1ttXkcpfeW9Z5dU+v0x6Sas64X6RzaP6aPdgHudhGTu8t0fBycMySw61COhIrVOkJCKXd97nuMuw6RzUP6MUK9Dul2GMEO5PYJ75PSwwzLBSCO28NBR4pht3sbTVkDWjjYZ6L5KE/jyojREsTYQYg5pAQ16nA9o1rY7b4oaHrTME50v3rUg99ONNme5wdet8d4biZqoWwqDGiuNAh4h7gd6h5EXaiioPkS2icsDXJYYeFSiCa+FXp4yBzSalxGZUdPcW9yu4G1b7157O/RE7GaL2a21qsRB93zZmB4KIpKhXGdxhcZg3Wcs+kobAompmaeYMZZJ5w1HFGJq3/j1GYSQLa8CBA6JwbMqd4sWa1Z5my67y5C7d32/LIKGJYZJFrBBNjR45N4M4nVmKkkTVqwI1PofdcDplxXNL3TdRw3enYbVY6gROA0CsXJAMUCFUdXxigSSjZILNnMghV5GYWTrQlKFNR22SpWwmivpAqYf79aquVrHtCnKxPkfj47XRmeYk9XGPbIhDu0i4Rqu78/frzCeRAgD1iQ+4h/WCnYL7Y4jdjcAH9LNe9tKv//BQtiS959tD4B8lfpaOAWAAA=" + } + }, + { + "ID": "c926d0742770b684", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7b056985dbfd1e6384d55bf9ba042635/5415913759227726997;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:51 GMT" + ], + "Etag": [ + "pDANmbJ2TK8DRjAWzlKWeg==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:51 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlu9:4362,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=G608W-FdjtipBcy2qagC" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2VvW6DMBRGd54CMecJupHgREgJpMYZqqqDRdwUiZ8I3AFFvHuBQlrgXgxMGTIFf0In35XPFTdNNzL/S0TceNFvmq4bn4EIz1l5ei9PdVSGMY9EGRlO9bv6zWR+rTOPUdvZtWmUnOuUkteTTYllVHGxAljrXIqsD1u/MeLNZ9mxFBeR9mm2w8iO0Pm8bZhw2adt967JFsyZJKHg8WBS190T05nPY0EkMsmja5/I7APxmHk4zmdaXA7u1TIZWdYOKrasE0Srei0jOt+RSAO/D3ROB0LtzXyeJ9MgvphpyvOpS3EkZXu1yCATtVkFrW0GkYjSKmCjNIhEvVZB716D2BG5VeBKJJAJGz6lJ1pxWTsUibuuwjaug1RUeBWUCj9Jz30cJRuXWuj6NHn3u9KiB9vUvI6tErafbWGA2ymN1caLA9X//gT9BI0s7tgQ5RjNw8ekqf5f76TRmht+iNG0+2FENlBgzLjOdE/jnsbhxmn1Q6EV2g8HguP1AwsAAA==" + } + }, + { + "ID": "19b73c786284f6d3", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f483ccc9bd601882c57b2898667b63ea/13871282773446995881;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:51 GMT" + ], + "Etag": [ + "SmB04Q6NVIMDa87sDZsq8g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4287,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/9.esf,ybpm14-v6:9841,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/121,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:1119::]:4002 had response of OK with response time of 0.013013124465942383 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpm14-v6:9841,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/9.esf,ybpm14-v6:9841,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/9" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/+1YUU+DMBB+368g+LqRtisMlvhi9mKiJupiosseWGTLIm46Oo0x/ncpU9eVHrkhLCTaBwLX767ffS0H7XvLsuyH+eLe7lv2ZD57XkertyMRTuJoEIrwbJ4Iuy0xkQhnEnP9eEL4pXdxc3o+CP1eMrhLnv3Z8fEGJZYijK+Wr4mEko1ttXkcpfeW9Z5dU+v0x6Sas64X6RzaP6aPdgHudhGTu8t0fBycMySw61COhIrVOkJCKXd97nuMuw6RzUP6MUK9Dul2GMEO5PYJ75PSwwzLBSCO28NBR4pht3sbTVkDWjjYZ6L5KE/jyojREsTYQYg5pAQ16nA9o1rY7b4oaHrTME50v3rUg99ONNme5wdet8d4biZqoWwqDGiuNAh4h7gd6h5EXaiioPkS2icsDXJYYeFSiCa+FXp4yBzSalxGZUdPcW9yu4G1b7157O/RE7GaL2a21qsRB93zZmB4KIpKhXGdxhcZg3Wcs+kobAompmaeYMZZJ5w1HFGJq3/j1GYSQLa8CBA6JwbMqd4sWa1Z5my67y5C7d32/LIKGJYZJFrBBNjR45N4M4nVmKkkTVqwI1PofdcDplxXNL3TdRw3enYbVY6gROA0CsXJAMUCFUdXxigSSjZILNnMghV5GYWTrQlKFNR22SpWwmivpAqYf79aquVrHtCnKxPkfj47XRmeYk9XGPbIhDu0i4Rqu78/frzCeRAgD1iQ+4h/WCnYL7Y4jdjcAH9LNe9tKv//BQtiS959tD4B8lfpaOAWAAA=" + } + }, + { + "ID": "c7e1f30e19a336ae", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "11b92eca7cc212b5b7a4dcab2d459fca/3879908817763242430;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:51 GMT" + ], + "Etag": [ + "pDANmbJ2TK8DRjAWzlKWeg==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:51 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbf4:4286,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=G608W6mHFcTNqAXfvonACQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2VvW6DMBRGd54CMecJupHgREgJpMYZqqqDRdwUiZ8I3AFFvHuBQlrgXgxMGTIFf0In35XPFTdNNzL/S0TceNFvmq4bn4EIz1l5ei9PdVSGMY9EGRlO9bv6zWR+rTOPUdvZtWmUnOuUkteTTYllVHGxAljrXIqsD1u/MeLNZ9mxFBeR9mm2w8iO0Pm8bZhw2adt967JFsyZJKHg8WBS190T05nPY0EkMsmja5/I7APxmHk4zmdaXA7u1TIZWdYOKrasE0Srei0jOt+RSAO/D3ROB0LtzXyeJ9MgvphpyvOpS3EkZXu1yCATtVkFrW0GkYjSKmCjNIhEvVZB716D2BG5VeBKJJAJGz6lJ1pxWTsUibuuwjaug1RUeBWUCj9Jz30cJRuXWuj6NHn3u9KiB9vUvI6tErafbWGA2ymN1caLA9X//gT9BI0s7tgQ5RjNw8ekqf5f76TRmht+iNG0+2FENlBgzLjOdE/jnsbhxmn1Q6EV2g8HguP1AwsAAA==" + } + }, + { + "ID": "05b92f5c7dcb05a2", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f8e0cd97d7f8c0e0bf66578760a042a3/2271847381772523238;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0006?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:51 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnzz206:4433,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/9,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=G608W9mpLpH3qQWluJzwAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/9,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/9" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "3677d68da5982718", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "623" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e98ec5f37690c23a93caff3260873c63/663787045276588815;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoiU3RyaW5nIiwidHlwZSI6IlNUUklORyJ9LHsibmFtZSI6IkJ5dGVzIiwidHlwZSI6IkJZVEVTIn0seyJuYW1lIjoiSW50ZWdlciIsInR5cGUiOiJJTlRFR0VSIn0seyJuYW1lIjoiRmxvYXQiLCJ0eXBlIjoiRkxPQVQifSx7Im5hbWUiOiJCb29sZWFuIiwidHlwZSI6IkJPT0xFQU4ifSx7Im5hbWUiOiJUaW1lc3RhbXAiLCJ0eXBlIjoiVElNRVNUQU1QIn0seyJuYW1lIjoiRGF0ZSIsInR5cGUiOiJEQVRFIn0seyJuYW1lIjoiVGltZSIsInR5cGUiOiJUSU1FIn0seyJuYW1lIjoiRGF0ZVRpbWUiLCJ0eXBlIjoiREFURVRJTUUifSx7Im5hbWUiOiJOdW1lcmljIiwidHlwZSI6Ik5VTUVSSUMifSx7ImZpZWxkcyI6W3sibmFtZSI6IlgiLCJ0eXBlIjoiSU5URUdFUiJ9XSwibmFtZSI6IlJlY29yZCIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwNyJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:52 GMT" + ], + "Etag": [ + "BKQSpbgoCV9oiQuwu+A2yw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnds123:4337,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/177,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=HK08W-iEAZGKqwWO7Yq4DQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/177,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/177" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2UW2+bMBiG7/MrEL1cGw45NZF6QRJaoeWwEjptmqrIgS/UK2Bmm7Goyn+fMaTqGEThBvz6e/y+PvHWUdRXnATqRFF3OPyVAT1ccbSLQL0WXcBRWHRNPz9u0l1IZl/HBD9mefbJMg/53Z0swpIOssgHfpMSym9GQ3MSII4Y8K2pG7f6SO9v+/pwbI5vjUF/ZI6MrS6ernQ6VzKSDgyi/QInr4XPC+cpm2hanufdkJAwApRi1vVJrJ0moP02tZSSn+BzptVyaVUupl0SUJMBmXZhTlnmwh4oJD6ItG8dRVGrKE7TMhWYEMsoVcUFuUpM2pXQBQE7ylGupf8CMTpl22OIAiZaP0RLSkJMUFyEVzec4iSUXoXbIS1Vz3VWD2ohHq8bqOmBA6tD0++evWlnnIRDCLROOSvPfrDddu4+IojXqfvF2vLO5CNEnJnkv4Tr9cK2Vu2ch2NgHMVpnfScpb3xrOWXdnaOONSxueXZ592ajM57NFGFz3lylcVAsV8HV09L23Vm7ZwLPqFBHXPt2dqdn9R/D9hpiA+DfKsqW7ZdWMvXc6f6fK7OcZLF5UkTgLwPhbIgSegBbehxSf5B8Ckgjkki10uoxqAn7k3P6JnGwCj/fH9STJtqhuNedQHVCDG+JAEWcwzaR3rfPWu6sEuO+HLkQn0St+LY+QtIjEewhwUAAA==" + } + }, + { + "ID": "fa14a6e9eed0681c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/insertAll?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "190" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f50a894bbc204ff220db42f70c2cbd29/17574244706856623928;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/insertAll?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyb3dzIjpbeyJpbnNlcnRJZCI6InFGVzlwcUpmTkJvZDJHVW13Ujh5allNdVVZMiIsImpzb24iOnsiQm9vbGVhbiI6bnVsbCwiQnl0ZXMiOm51bGwsIkRhdGUiOm51bGwsIkRhdGVUaW1lIjpudWxsLCJGbG9hdCI6bnVsbCwiSW50ZWdlciI6bnVsbCwiU3RyaW5nIjpudWxsLCJUaW1lIjpudWxsLCJUaW1lc3RhbXAiOm51bGx9fV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:52 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/109.esf,ybny4-v6:9814,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/6,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-streaming-frontend-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-streaming-frontend-thermostat\" pick_cluster_params { allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jl\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:5b10::]:4485 had response of OK with response time of 0.013238430023193359 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybny4-v6:9814,/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/109.esf,ybny4-v6:9814,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-streaming-frontend-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/109" + ], + "X-Google-Service": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0zCtOLSpxzMkJSi0uyAdylLhquQAsgTGpNAAAAA==" + } + }, + { + "ID": "19a69bc3e977b025", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f632cd59b51dbf2c6d92d071a5daaf6c/7511095727311602252;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:52 GMT" + ], + "Etag": [ + "5L3l6qh/PQ0Zu/w/3I8sUQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4217,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/8.esf,ybr7-v6:9887,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/46,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"ws\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1002::]:4048 had response of OK with response time of 0.014569282531738281 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybr7-v6:9887,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/8.esf,ybr7-v6:9887,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/8" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTH1Mc4xK8zQDwg0iCrVL9c39rQoDg20tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZUCpvNKcHLhYrc6owkGqEMqK5YLxYrlquQAHu11gVQIAAA==" + } + }, + { + "ID": "d617afa99ad4f901", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cb41fe5dbea1ef5528aff44a3dd4adf4/15966183270865839201;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:52 GMT" + ], + "Etag": [ + "BKQSpbgoCV9oiQuwu+A2yw==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:52 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnds123:4337,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/107,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=HK08W-H6IMShqwXKiJjwBA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/107,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/107" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAH2S3wqCMBSH7/cUsmufoDvLFQOdMRcU0cWwUwlORdeFiO/e+iPUcl7tnI9953dg65GH2+wGSuKF1yPPw5ccinNruqPpXsjAUiowCKe6ycsr9t9Ud/WbCk7ZBj/h4E9Yy05Da0vLgyCp26Glhis0tkWZIBvC3d66qKS2rXWUBGJmv6oqQJZ/GyZJRALm9kSuoNVS1bYpaExSEcRbtxtKDbYWBoLMp00FzWdMWc+ceZPdFTR5ZotsFxNOV26PQ1Y1Z1vjZJXwcKS/H2wc8TVk/7npeHYT/TpO6FOaYkADegDOtV6KywIAAA==" + } + }, + { + "ID": "63b5979c1306755b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "237617f7e8a1e551343f38f520811b4f/5975090785847117685;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:53 GMT" + ], + "Etag": [ + "5L3l6qh/PQ0Zu/w/3I8sUQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4387,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0.esf,ybag144-v6:9889,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/79,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"jc\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"wo\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:c70f::]:4070 had response of OK with response time of 0.013023138046264648 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybag144-v6:9889,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0.esf,ybag144-v6:9889,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTH1Mc4xK8zQDwg0iCrVL9c39rQoDg20tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZUCpvNKcHLhYrc6owkGqEMqK5YLxYrlquQAHu11gVQIAAA==" + } + }, + { + "ID": "03692ea499ff4528", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "38687097eea77904d6397e29f1cd19e0/14358122934369970569;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:53 GMT" + ], + "Etag": [ + "BKQSpbgoCV9oiQuwu+A2yw==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:53 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vny124:4241,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=HK08W9HtNIL8qwXf556wDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAH2S3wqCMBSH7/cUsmufoDvLFQOdMRcU0cWwUwlORdeFiO/e+iPUcl7tnI9953dg65GH2+wGSuKF1yPPw5ccinNruqPpXsjAUiowCKe6ycsr9t9Ud/WbCk7ZBj/h4E9Yy05Da0vLgyCp26Glhis0tkWZIBvC3d66qKS2rXWUBGJmv6oqQJZ/GyZJRALm9kSuoNVS1bYpaExSEcRbtxtKDbYWBoLMp00FzWdMWc+ceZPdFTR5ZotsFxNOV26PQ1Y1Z1vjZJXwcKS/H2wc8TVk/7npeHYT/TpO6FOaYkADegDOtV6KywIAAA==" + } + }, + { + "ID": "ed772558cc822ff4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "84bc38db5d7675cdd273d93b2d4f8ee4/4367029349873109918;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:53 GMT" + ], + "Etag": [ + "5L3l6qh/PQ0Zu/w/3I8sUQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4042,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybar66-v6:9843,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/110,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1207::]:4374 had response of OK with response time of 0.012819766998291016 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybar66-v6:9843,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybar66-v6:9843,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTH1Mc4xK8zQDwg0iCrVL9c39rQoDg20tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZUCpvNKcHLhYrc6owkGqEMqK5YLxYrlquQAHu11gVQIAAA==" + } + }, + { + "ID": "17aa4933b47f42b9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "559a0f7bdd259b252789df419d1184c8/12822117992905486002;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:53 GMT" + ], + "Etag": [ + "BKQSpbgoCV9oiQuwu+A2yw==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:53 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnds123:4337,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/133,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ha08W9j0FcGqqwWOt5CYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/133,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/133" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAH2S3wqCMBSH7/cUsmufoDvLFQOdMRcU0cWwUwlORdeFiO/e+iPUcl7tnI9953dg65GH2+wGSuKF1yPPw5ccinNruqPpXsjAUiowCKe6ycsr9t9Ud/WbCk7ZBj/h4E9Yy05Da0vLgyCp26Glhis0tkWZIBvC3d66qKS2rXWUBGJmv6oqQJZ/GyZJRALm9kSuoNVS1bYpaExSEcRbtxtKDbYWBoLMp00FzWdMWc+ceZPdFTR5ZotsFxNOV26PQ1Y1Z1vjZJXwcKS/H2wc8TVk/7npeHYT/TpO6FOaYkADegDOtV6KywIAAA==" + } + }, + { + "ID": "9a22b65229fe3037", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "513230454f25b8494430d4f0d80230b2/11214056556914701275;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0007?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:54 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbv126:4011,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/161,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ha08W_GMNdCIqwWyt4GQDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/161,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/161" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "a56ae36ffcc9bd6c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "623" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "49a248d373923cf26d4dfca8c2584fd6/9605996224730445572;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoiU3RyaW5nIiwidHlwZSI6IlNUUklORyJ9LHsibmFtZSI6IkJ5dGVzIiwidHlwZSI6IkJZVEVTIn0seyJuYW1lIjoiSW50ZWdlciIsInR5cGUiOiJJTlRFR0VSIn0seyJuYW1lIjoiRmxvYXQiLCJ0eXBlIjoiRkxPQVQifSx7Im5hbWUiOiJCb29sZWFuIiwidHlwZSI6IkJPT0xFQU4ifSx7Im5hbWUiOiJUaW1lc3RhbXAiLCJ0eXBlIjoiVElNRVNUQU1QIn0seyJuYW1lIjoiRGF0ZSIsInR5cGUiOiJEQVRFIn0seyJuYW1lIjoiVGltZSIsInR5cGUiOiJUSU1FIn0seyJuYW1lIjoiRGF0ZVRpbWUiLCJ0eXBlIjoiREFURVRJTUUifSx7Im5hbWUiOiJOdW1lcmljIiwidHlwZSI6Ik5VTUVSSUMifSx7ImZpZWxkcyI6W3sibmFtZSI6IlgiLCJ0eXBlIjoiSU5URUdFUiJ9XSwibmFtZSI6IlJlY29yZCIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwOCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:54 GMT" + ], + "Etag": [ + "nN+lGOajekm8NVYCHn8K0Q==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlx6:4349,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/55,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Hq08W5G8BovMqAXxlrL4Ag" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/55,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/55" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2UW2+bMBiG7/MrIna5NkDOidSLHGiGRshG6LRqqiIXvlA3YDPbWRZV+e8zhkwdgyjcgF9/j9/XJ94aTW2HSaiNm9ozjn7ugR0/CPQcg3Yju0CgKOsi7sd4sUKvsEuG7rfH2Scy/Gx8vbtTRVjR4T4OQNymlInbQb89DpFAHMSmbZhDY2B0N12jP2qPhmavO2gPzI0hn5ZyulQyVA4c4q2DyS7zeREi5WNdPxwOrYjSKAaUYt4KaKKfJ6D/auspo68QCK6XculFLq5fE1BXAbl+ZU5V5sEWGJAAZNq3RrOpFVHsqmXKMCnmUYqKK3LlmLLLoSsCNpontZbBCyTonG2LIQ65bP2QLSVJkaAkC6+tBcMkUl6Z2zHNVd+z3YWWiaebCmp6FMDL0PTRt9b1jE0ERMDKlO361sLy6rn7mCJRpu6d1cS/kI9SeWbIfwlXK8eauPWcjxPgAiVpmfTtpbX2J8sv9ewcCShj84lvXXarMrrsUUVlPpdJd58Aw0EZdB+WlmfP6jkPAsrCMuZZs5U3P6v/HrDzEO8G+V5U1my7tFavp0bx+VScY7JP8pMmAXUfMsWhJPKBVfR49PBOCBgggSlR6yVVs9eR96Zjdrqdnpn/+X6nmFXV9Eed4gJqMeJiSUMs5xjWj/R39yZTx8o5GqiRM/VB3opT4w8tvOlEhwUAAA==" + } + }, + { + "ID": "ce38ef72556e846f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/insertAll?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "301" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "136b91acf2fb4acbf8c6467bb745b770/8069991283266026540;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/insertAll?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyb3dzIjpbeyJpbnNlcnRJZCI6InFBUjhtV2lDVFR2b0ZBUTNOQlFDUnZaVWRFdiIsImpzb24iOnsiQm9vbGVhbiI6dHJ1ZSwiQnl0ZXMiOiJBUUlEIiwiRGF0ZSI6IjIwMTYtMTEtMDUiLCJEYXRlVGltZSI6IjIwMTYtMTEtMDUgMTU6MDQ6MDUuMDAwMDA2IiwiRmxvYXQiOjIuMywiSW50ZWdlciI6MSwiTnVtZXJpYyI6IjAuMzMwMDAwMDAwIiwiUmVjb3JkIjp7IlgiOjR9LCJTdHJpbmciOiJ4IiwiVGltZSI6IjE1OjA0OjA1LjAwMDAwNiIsIlRpbWVzdGFtcCI6IjIwMTYtMTEtMDVUMDc6NTA6MjIuMDAwMDAwMDA4WiJ9fV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:54 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/134.esf,ybkg14-v6:9820,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/76,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-streaming-frontend-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-streaming-frontend-thermostat\" pick_cluster_params { allowed_cluster: \"ej\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"os\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:4786::]:4145 had response of OK with response time of 0.013673543930053711 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybkg14-v6:9820,/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/134.esf,ybkg14-v6:9820,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-streaming-frontend-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/134" + ], + "X-Google-Service": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0zCtOLSpxzMkJSi0uyAdylLhquQAsgTGpNAAAAA==" + } + }, + { + "ID": "47b584504269272d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "09b4128076ffbc014ef75adfc9d4a398/16453304902470557249;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:55 GMT" + ], + "Etag": [ + "LfhOV8Kio0uUysy78j+caw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4233,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybmg12-v6:9843,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/30,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"wo\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:cd57::]:4174 had response of OK with response time of 0.014644622802734375 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,ybmg12-v6:9843,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybmg12-v6:9843,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfFJy/APs/DOzDcoDa0srjS3yNJOTiy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNFUpwoVodPOocAz1diFRqSKQ6Iz1jIlWWFJWmEmu5ibmFsbEREOgZEOsOA0MzXUNDXQNTYu0wtTIwsTIw1TMAATOSrQkhzwADPWMiAwxZACMNYOpBtsRECU2iFoUfy4VNBsaCyIJ4sVy1XABsjyn49AIAAA==" + } + }, + { + "ID": "1a5d759c9ecb2499", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1a59910fb82fc78ebf959d5442c7de4c/6461930946770092373;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:54 GMT" + ], + "Etag": [ + "nN+lGOajekm8NVYCHn8K0Q==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:54 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnhr7:4465,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/74,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Hq08W7iKLpGJqQXrmaWgBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/74,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/74" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAH2S3wqCMBSH7/cUsmufoDvLFQOdMRcU0cWwUwlORdeFiO/e+iPUcl7tnI9953dg65GH2+wGSuKF1yPPw5ccinNruqPpXsjAUiowCKe6ycsr9t9Ud/WbCk7ZBj/h4E9Yy05Da0vLgyCp26Glhis0tkWZIBvC3d66qKS2rXWUBGJmv6oqQJZ/GyZJRALm9kSuoNVS1bYpaExSEcRbtxtKDbYWBoLMp00FzWdMWc+ceZPdFTR5ZotsFxNOV26PQ1Y1Z1vjZJXwcKS/H2wc8TVk/7npeHYT/TpO6FOaYkADegDOtV6KywIAAA==" + } + }, + { + "ID": "3dbd11e2c56e2853", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "61c57e9c6c83b12ade5019a81c25e9ad/14845243466479772522;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:55 GMT" + ], + "Etag": [ + "LfhOV8Kio0uUysy78j+caw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4139,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26.esf,ybao19-v6:9897,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/120,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"wo\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:d04b::]:4077 had response of OK with response time of 0.01387786865234375 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,ybao19-v6:9897,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26.esf,ybao19-v6:9897,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfFJy/APs/DOzDcoDa0srjS3yNJOTiy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNFUpwoVodPOocAz1diFRqSKQ6Iz1jIlWWFJWmEmu5ibmFsbEREOgZEOsOA0MzXUNDXQNTYu0wtTIwsTIw1TMAATOSrQkhzwADPWMiAwxZACMNYOpBtsRECU2iFoUfy4VNBsaCyIJ4sVy1XABsjyn49AIAAA==" + } + }, + { + "ID": "5495a047ba8b9642", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d3c11b2de1126c65c655e498a77eb6d5/4853869510779307646;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:55 GMT" + ], + "Etag": [ + "nN+lGOajekm8NVYCHn8K0Q==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndd73:4003,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=H608W6KECdLtqgWJopWwDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAH2S3wqCMBSH7/cUsmufoDvLFQOdMRcU0cWwUwlORdeFiO/e+iPUcl7tnI9953dg65GH2+wGSuKF1yPPw5ccinNruqPpXsjAUiowCKe6ycsr9t9Ud/WbCk7ZBj/h4E9Yy05Da0vLgyCp26Glhis0tkWZIBvC3d66qKS2rXWUBGJmv6oqQJZ/GyZJRALm9kSuoNVS1bYpaExSEcRbtxtKDbYWBoLMp00FzWdMWc+ceZPdFTR5ZotsFxNOV26PQ1Y1Z1vjZJXwcKS/H2wc8TVk/7npeHYT/TpO6FOaYkADegDOtV6KywIAAA==" + } + }, + { + "ID": "79d2dec49a4c0126", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e112adb1719a26712068261e4ba944e2/13309239624510204050;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:56 GMT" + ], + "Etag": [ + "LfhOV8Kio0uUysy78j+caw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4355,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/21.esf,ybbt9-v6:9811,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/23,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"jl\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:3c6::]:4005 had response of OK with response time of 0.013697147369384766 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybbt9-v6:9811,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/21.esf,ybbt9-v6:9811,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/21" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfFJy/APs/DOzDcoDa0srjS3yNJOTiy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNFUpwoVodPOocAz1diFRqSKQ6Iz1jIlWWFJWmEmu5ibmFsbEREOgZEOsOA0MzXUNDXQNTYu0wtTIwsTIw1TMAATOSrQkhzwADPWMiAwxZACMNYOpBtsRECU2iFoUfy4VNBsaCyIJ4sVy1XABsjyn49AIAAA==" + } + }, + { + "ID": "75b521a3f517ded6", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "69796cb2a6c0f463fe89401c295eafe7/3245808074805299879;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:55 GMT" + ], + "Etag": [ + "nN+lGOajekm8NVYCHn8K0Q==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlw9:4453,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/14,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=H608W5n9J8fUqQW_tYXYBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/14,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/14" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAH2S3wqCMBSH7/cUsmufoDvLFQOdMRcU0cWwUwlORdeFiO/e+iPUcl7tnI9953dg65GH2+wGSuKF1yPPw5ccinNruqPpXsjAUiowCKe6ycsr9t9Ud/WbCk7ZBj/h4E9Yy05Da0vLgyCp26Glhis0tkWZIBvC3d66qKS2rXWUBGJmv6oqQJZ/GyZJRALm9kSuoNVS1bYpaExSEcRbtxtKDbYWBoLMp00FzWdMWc+ceZPdFTR5ZotsFxNOV26PQ1Y1Z1vjZJXwcKS/H2wc8TVk/7npeHYT/TpO6FOaYkADegDOtV6KywIAAA==" + } + }, + { + "ID": "6a35963d4bc711b0", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4d22b6dd3cb47a4ec7f38005af3fc2e2/1709804232835665872;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0008?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:56 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vned124:4463,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/81,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=IK08W9itA4ekqQWBpIe4DA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/81,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/81" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "f0cb353480948a97", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0754565b9bd6dbf9b420faf3c2b5a580/101742801139848440;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwOSJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:56 GMT" + ], + "Etag": [ + "QaEZDAW6u3USx0mTrfRhgw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbf4:4286,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=IK08W4HEE47YqQXMtqmoAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TXW/aMBSG7/MrIu+W4nw1FKRewLAqJAZrSDVpVYVMYoLXJM5ssxRV/Pc6diIxRCty4/j1ec95zrH8btnglZYpGNlgQ7O/e8IP3yTe5AT01BGROGuOHjH6PR3/Cvf+0+rNKWK+jXZZfX+vg6h2p/s8IfKmYlzeDEJvlGKJBZFrz3HvnIETrAMnHHrDO/c2GHgDd+2or68rfRUy1BUEybdzWr42dXZSVmIEYV3X/YyxLCe4oqKfsAJ2DcB/Hqw4+0MSKeAZF2y5BLwGEGpAAa/k1GER2RJOyoQo2nfLtkGLMrs0psamRIPSRlzBZWy6nDFdAWjZRz3LZEcK3LFtKclToXbPaqclJZa4aODN2jOaPFRaW8XRbPEAGvHYu+TZF+LcM1vE6AFFnVywVMsR+onGMZp+noyT5DxXhL4vo2mn/o/f+U8ybBjL2+CTJJPlco7GC2D0o15erPb3pR2U6mRykKTJDvTAG2XOyiwm/MJJxOoTIeEES8rKmBoO99ZXF+O7fhgErnlabxXll2LCod/eMMixkD9YSlWb6eeZuq7i8WSOjI8lOnOjPq2AdbQ+AKleCLXoAwAA" + } + }, + { + "ID": "9c0240cc6fe2ef4f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "149d8045a34ea450e0619cdca26c13ef/16940143963881904417;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:56 GMT" + ], + "Etag": [ + "QaEZDAW6u3USx0mTrfRhgw==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:56 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlr11:4372,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/77,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=IK08W9CsIILqqQXwq7agCg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/77,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/77" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TXW/aMBSG7/MrIu+W4nw1FKRewLAqJAZrSDVpVYVMYoLXJM5ssxRV/Pc6diIxRCty4/j1ec95zrH8btnglZYpGNlgQ7O/e8IP3yTe5AT01BGROGuOHjH6PR3/Cvf+0+rNKWK+jXZZfX+vg6h2p/s8IfKmYlzeDEJvlGKJBZFrz3HvnIETrAMnHHrDO/c2GHgDd+2or68rfRUy1BUEybdzWr42dXZSVmIEYV3X/YyxLCe4oqKfsAJ2DcB/Hqw4+0MSKeAZF2y5BLwGEGpAAa/k1GER2RJOyoQo2nfLtkGLMrs0psamRIPSRlzBZWy6nDFdAWjZRz3LZEcK3LFtKclToXbPaqclJZa4aODN2jOaPFRaW8XRbPEAGvHYu+TZF+LcM1vE6AFFnVywVMsR+onGMZp+noyT5DxXhL4vo2mn/o/f+U8ybBjL2+CTJJPlco7GC2D0o15erPb3pR2U6mRykKTJDvTAG2XOyiwm/MJJxOoTIeEES8rKmBoO99ZXF+O7fhgErnlabxXll2LCod/eMMixkD9YSlWb6eeZuq7i8WSOjI8lOnOjPq2AdbQ+AKleCLXoAwAA" + } + }, + { + "ID": "d88c2db6d0b16e2b", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "78" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "QaEZDAW6u3USx0mTrfRhgw==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "9b51dcdc8609bee702e7ada65c8589bf/15404140121929047370;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXNjcmlwdGlvbiI6Im1vcmUiLCJleHBpcmF0aW9uVGltZSI6IjE1MzA3OTAwOTMwMDAiLCJmcmllbmRseU5hbWUiOiJtb3JlIn0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:57 GMT" + ], + "Etag": [ + "MH5R/iRTs9P03tsDqg8JqQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnas187:4257,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=IK08W4mmKsTtqwWv4qKADg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TTW/iMBCG7/kVkXulOF98BKkHWKIuKwrdNHuqKhSSIfU2iYNtyqKK/76OnUiUsitycfx63plnPMmHYaI3UqZoZKI1ybY7YIcbEa9zQB15BCLO6qOH770QkzDi/qPlCj7dZsMf2593dyqIKHe6yxMQtxVl4nbQd0ZpLGIOYuVY9tAaWN7Ks/q+4w/tnjdwBvbKkk9XVfpfiK8qcMg3c1K+1XVehaj4COP9ft/NKM1yiCvCuwktcNsAfndwxehvSATHZ1y44eL4GkCsADm+klOFhbABBmUCkvbDME3UoMwuXVNtk6JGaSKu4NI2VU6brgA0zGPNuGEEyjQ/LOKiJkQFZXrWKfCEkUoQWn7SefIKRdw2syGQp1zunuVOSVIsm1xq7WhNHCqlPUXhbHGPavHYueTZFfzcM1tEwX0QtnJBUyWHwWMwjoLpv5MxSM5zhcG3ZTht1c/4rf8kw5rSvAk+STJZLufBeIG0flTLi9G8vjQ3KzuZHATU2ZGaUK3MaZlFwC6chHR/IiQM4vrqI6I57J4rJ+nabt/zbP0v/qkI+xrjy9m6zSeB8piLB5oS2Wb6JdPA9ob6M226isaTeaB9NInbwf96QsbR+AurBxXvGQQAAA==" + } + }, + { + "ID": "74a05c0208a6a1ea", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "21" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "77308ca4df6abe58c63d1db8a1c03f50/13796078685938262643;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJmcmllbmRseU5hbWUiOiJ4In0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:57 GMT" + ], + "Etag": [ + "2Ghs0dLVf++8dqtoXTyhbQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnj66:4151,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/144,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ia08W8CUDpLbqwWA56-YDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/144,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/144" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2T0W6bMBSG73kK5F02jYGQkETqRbKiKBJLNsqqSVMVOdgQr4Cp7TRFVd59xoCUptkUbsC/z3/Od3zMu2GCZ1pgMDXBlqYve8KrLxJtMwJ6aotIlNZbzmInLBw8Jjc3Y/wi2a+o2m1/3N3pIKrdeJ/FRN6WjMtbb+RMMZJIELlxLHtseZa7ca3RxJmM7aHrOZ69sdTT15X+FzLRFQTJkoAWz3WdnZSlmEJ4OBz6KWNpRlBJRT9mOewagK8OLDn7Q2Ip4BkXbLkEvAYQakABr+TUYSFJCCdFTBTtu2GaoEVZXjqm2qbEBqWNuIKrselyjekKQMM81owJp6TAWbVCeU0I3jQ5JiLmtJSUFbWYM95cABHvSI66ThJKMizU6rdaaUmJRZtIv3uNJqtSaw9RuFwtQC0ee5c8+1yce5aryF/4YSfnDGs59L/7s8i//3cyTuLzXKH/dR3ed+pH/M5/kmHLWNYGnySZr9eBP1uBRj/q15PRfj61x6o6mVeS1NmBHk+tBKxII8Iv7ITscCLEnKD66CPacNjDgRrjwB6MXNdufsS3kvLPMRM12EF7H0CGhPzGMFVt4k+ZvNHQae5o21U0mwd+42Mx6gb/8wEYR+Mv7Y83cBYEAAA=" + } + }, + { + "ID": "3e3056fa3bb8275a", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "21" + ], + "Content-Type": [ + "application/json" + ], + "If-Match": [ + "MH5R/iRTs9P03tsDqg8JqQ==" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a361a02f70f685a127ba6f8f32ffc806/12188017249947543451;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJmcmllbmRseU5hbWUiOiJ5In0K" + }, + "Response": { + "StatusCode": 412, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:57 GMT" + ], + "Etag": [ + "2Ghs0dLVf++8dqtoXTyhbQ==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:57 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnez28:4339,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ia08W7HmLMr0qAWI7IHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1X3U8iMRB/96/YEB64hGsAUU+TfQDEHIlft2J8UGNKd1j2XNpN2zUa4/9+0+6ut2BPIF7u4eILMNPp7zdfLZ3nLa8GUgpZO/Cet7xCUChdo2RVqAzFnMYclbUoEROa1Jq5XgJVwuqZ4GGsY8FPhT4BXRrMQSkagbE4l/Bq5B3ROIGwtEoEo0Y9fkqt6QxoCHJ51ayMpl9PqGazci2ESRaN+FTkPsxJJESUAKFpTBTIB5CECQnkiGaJPvD6WZwg8vOEKvDPg+Hg7PRwNB6dnd4d9UbHw8Omh1QQCfnkX14Mg7thEJwFTS8P389jb3ozrdPv1kXlP7/k8oWmOlN+Wgkyj7HpxVyD5DQJbLL80geGydMQ9p/6lN0DD/0pTRQ0vYxzOoewJ6NsDlwr//oWOcok+HluFCkz0fSKHPuOBDe9vEL+UnlQn7KBCMHvtjsvnmPnDb/BiLx3UmrDYpBqAppGZFACEDYDdt/4Lf+kD/SgvbP3ZWPM4ZhicQuVkDly1VnVWDaxZDut7Y+TpVIwzGwAKkUicDN1O+2NmHpSZDyswFxJmqbwlu2Phpa2032fVeGSIqYnVV8IrTRurlfA7EqAeZShg3txX8G4v79RoK9kpkr1pWhMVyyTLmwoemZvs55ZyUmZoay3Wi0n3YZds4qu3iaMJomTqtv5Z1Tb6yQx03FCBghBJwkcZTpDncx4Y0llEXddvuPPOWbY4uDxZJmUeHmREwQfPgLLsOlU/TDGs6tLmYD9AY0Fo+JctTYg6U1MozJdOFnAHsdKAwfZWFouat1x9fN6BGiVJoCOu5C/7bluhPWAFWgn5u7+ivP+WcPPGn7W8H+pYQVQz/ABF5Kx/RpL81LE+lXEmEeBeTEiriVZtCyS3Nl1sJjocDcxtoCvNQ2Pul4VqrgjXigbVYuiR3Z2VrxG8q+Bzf2JCLME6gXCgEr5hF6UbXeBpjED/D9bIK1uLcrb/VBMOYErGGdFStxB3i/ljkUfF9cs2nanta6XZb9VlaZjJljJnGjEZyBjHBmK1VNxySVMHUFsO1+lf4nWxdd2vZw2ajBnX7muhkgVDyOcqMhhrFIzBP3IIIMR3gb1KyGx8fNTYHHfmJQXm3G5ZqbIF/y4NfMkDpChmT9xJrLiiuF1C3e+bP0C0rGXpkEPAAA=" + } + }, + { + "ID": "0cd6abc95bea3698", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "293" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4a747849fb8b0c2c44ea6fef0a273ed3/10652013412272811204;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7ImZpZWxkcyI6W3sibmFtZSI6Im5lc3RlZCIsInR5cGUiOiJCT09MRUFOIn0seyJuYW1lIjoib3RoZXIiLCJ0eXBlIjoiU1RSSU5HIn1dLCJuYW1lIjoicmVjMiIsInR5cGUiOiJSRUNPUkQifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:58 GMT" + ], + "Etag": [ + "BwIbuLUKfDzELCKiOom1VQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlv21:4338,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/44,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ia08W4DYNovCqgXjnZbwDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/44,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/44" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKWUXW+bMBSG7/kVyLtNQ/hovqReJA2qorJko7Q30xQROBCvgJntjGZV/nttA1KapF2kcWP8+rznPLYPvGo6esZFjMY6WuP09xbo7gsP1xmgjlgCHqZyaVrN11vv8T6Z/XW923u8JLn59P3mRgVh5Y63WQT8qiSUXw361jgOeciAr6yeOewNes7K6fVH1mhoXjsDa2CueuLpqkqfhYxUBQZZ4uHiWdbZcF6ysWFUVdVNCUkzCEvMuhHJjXYDxh/LKCn5BRFnxhGX0XAx4xJAQwEy40JOFeZDAhSKCATtq6brqEGZnzsmaRNijdJEXMBV21S52nQBoKbvJWNCMRRxtluEuSREL4o8BhZRXHJMCinmhNYNwKIN5GG7kwRDFjMx+yFmShJi0SRSY6fW+K5U2kPgzxd3SIr7zjnPNmfHnvkicO9cv5VzEivZd7+5k8CdfZyMQnScy3dvl/6sVd/jt/6DDGtCsib4IMl0ufTcyQLV+l4NPz+jsP4TowDGIf4nSOcDO+EboKfug7t4vwtNvajmEPcx3XGQcEg1mVQ8UqQB0DMrPqkOhIhCKBsowDWHeW2LZrRNu+84Zv07eSkxPY0Zifa0m65GWcj4VxJjcUrxSaah7TRfWrOpYDL13NpHorBt38cHpO21N7v5A33cBAAA" + } + }, + { + "ID": "3a9bfc8f0cbef384", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "14" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "dd1fc69c091e04fd4c3b4162c9bffbe2/9043951976298803437;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzY2hlbWEiOnt9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:59 GMT" + ], + "Etag": [ + "BwIbuLUKfDzELCKiOom1VQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vna15:4288,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/114,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Iq08W4GvGpT7qAXMo7g4" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/114,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/114" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKWUXW+bMBSG7/kVyLtNQ/hovqReJA2qorJko7Q30xQROBCvgJntjGZV/nttA1KapF2kcWP8+rznPLYPvGo6esZFjMY6WuP09xbo7gsP1xmgjlgCHqZyaVrN11vv8T6Z/XW923u8JLn59P3mRgVh5Y63WQT8qiSUXw361jgOeciAr6yeOewNes7K6fVH1mhoXjsDa2CueuLpqkqfhYxUBQZZ4uHiWdbZcF6ysWFUVdVNCUkzCEvMuhHJjXYDxh/LKCn5BRFnxhGX0XAx4xJAQwEy40JOFeZDAhSKCATtq6brqEGZnzsmaRNijdJEXMBV21S52nQBoKbvJWNCMRRxtluEuSREL4o8BhZRXHJMCinmhNYNwKIN5GG7kwRDFjMx+yFmShJi0SRSY6fW+K5U2kPgzxd3SIr7zjnPNmfHnvkicO9cv5VzEivZd7+5k8CdfZyMQnScy3dvl/6sVd/jt/6DDGtCsib4IMl0ufTcyQLV+l4NPz+jsP4TowDGIf4nSOcDO+EboKfug7t4vwtNvajmEPcx3XGQcEg1mVQ8UqQB0DMrPqkOhIhCKBsowDWHeW2LZrRNu+84Zv07eSkxPY0Zifa0m65GWcj4VxJjcUrxSaah7TRfWrOpYDL13NpHorBt38cHpO21N7v5A33cBAAA" + } + }, + { + "ID": "c7e5e3f2f6a21b28", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "311" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f55b5c2c1274bac8a2f33a92f395e34c/7435890540308018454;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzY2hlbWEiOnsiZmllbGRzIjpbeyJtb2RlIjoiUkVRVUlSRUQiLCJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifSx7ImZpZWxkcyI6W3sibmFtZSI6Im5lc3RlZCIsInR5cGUiOiJCT09MRUFOIn0seyJuYW1lIjoib3RoZXIiLCJ0eXBlIjoiU1RSSU5HIn1dLCJuYW1lIjoicmVjMiIsInR5cGUiOiJSRUNPUkQifV19fQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:18:59 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:18:59 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnaj129:4167,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/52,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=I608W5K5B8KbqgWKtKHoAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/52,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/52" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1d3U8bORB/719hRXnISekqGxK+pDyEEI5IQGmA9qGtkLNrkj127T2vl4ND/O83/tiwCU4C5UoBmUok/tgZz3g8M/5NSW4/oArhnPHKNrr9gEwjg9Y3aKku6AxZgiMKnZVxzEY4rtR1Pyc4Y6o/olc4jsJiICFZhsdEjhxzdhWFJEQnwYQkGIWMZIgygRIsggk6xaOYoDCPAyI+poyLjxvrze0QC5wRcd5s+JuNjUbrvNVY32pubfrt1kZzwz9vwI8n5KPLpmx5aC8icYgoTgia4AwFE0zHsJaEhQRdcJago7ODg+7OQR8Jhob9z2eDYX+3kCIko3w8oBdMyvHtpLffP+yeD456nw6Pu6cDeOhHHQ2VCrZRABQ7linIqKJD8zhGiqJ+qxQNyhGso9W7jb5XgpjloTchcXTt9eWEXTX0vfKdSgZyioWHHMZ8nCeECjnldWlTrg7+CSxASYk3ZmwcE68sKXQnjHr964CkImI0qx7nozgKvEyZzABkT1IsIlhg7X6S9xe+wtvtxsYfy6lnhF8RXjDRVriTR3EInZxIJSlN12ZHFPFWa/0ZxHEQwN5X/UbDSrvx86SraiMKcmMizlLYYxIewkbYeDW31v4vZrniZGPib/rPUNZiuuvtrV9Ct+03fwnd1mb7cXQ5y268fdmjnOBQNqWl54IcEg4HSxOvzU3RSnmssm1MokzbyxGRBor5jZXFhv8MOWKcjEJcBQGCyyNCQs1wL6eBPLtV3y7UY4+bXXM0Aw/4lUeCfM6ZwIOL5fK1tzZflNszNswYm42u316bipHlFDzaRUwC4f1JKOHKJxAxYWFX+SLG/VZzzYNgzS5J7YxeUvYPRScs5wGxEdklMRmD46XjWSqDJI0LKkvn6AMxdT6yCaZBx1MO+pmClmnpp1ZujzmXSmFSIUPyd04yATH1+qbqewGO49p8v6Htr3K+C2jv4jQlfJ+xyz0cCMZvqqccByC7GZcjDxg/eEgvYnOV+1kqX3OpfGur/P0C2l8w/7csnVWeuUma5Vq79RvFKZ+dEo1uHkaiLM9Dq5ifYqR5lu39mr3pcRJmy4WZn1I48te3Mz1GBblesTcPJ5k4sSrl+w27c8DGy4WZnWCCwis0M03DOGQ77fbGTJyWESNVo1U1aa1VBCx7hFmwnlTehfTvJk6jUgys3r89AXOKAlI1r8eYw2VEEA5ptu/BdSSMyU7Mgst7l1yKmiZirvv+qhPxgmtp2LJbSoTH0wBCtYyi5gwN06Cg101TuB9hmUvtK0bcMCwYLZ+rLwU241vE+ES9G8B55BTH5mB6PKcwNqAlFrWFk43p2M7ucq49EI2SGM4UBNL4pjY/YMLpxlMU+STKdn+jj08uolhmg0HOOVy/ve4oE5ASCMgLaHbBeLKXi5yT6lzbC9m0p7bgGbNN9oviS3Ffez53aSbLufi+TcNCp1aeTLGIsaJquTHMKVVnCRgMqOmslWcYX9i23WQeTR7MQ0pgobtuS+GsdEviTzt74L1HOLjUyx/QCZG3idCMHrEzClmyhe1a0+ba/ye2Nn7+o/kt2h3rpliv9XbLOmSc9K9JkEPszKptsyMzvcaSGm3bHfKNm1JPq6Hkdu/XODdmLMR2al/AMG1x9Q0bprqvztvi6YQTHB4zFhe2Jwl9ZfyS8Jpl0Hi41lOpVjVJvcoFZNebrRmq6m6tJ5eeM9eAlgr4tKQqmdpMQS5wxns4j8U2GiRJruBgDUGTi4hGMrbfjnBGOoOjL92Dwe75cXfYPeyf9od1BKGfjCGr7Zyd9Ifn/eHwk+rMMw1719EUSe84GN3B6A5GdzC6g9EdjO5gdAejOxjdwegORncwuoPRHYzuYHQHozsY3cHoDkZ3MLqD0d+PYb5DGL2ONNrc0f8hvo5AbkJDEkJqkhrQeyJEug/PQSrTub3T7RPgkWedEQ5NilFHkQnwGgLv6JfbAojWj5axdAWRL0C4PQusDU/A6sECdm52wCxglR3Bc2JA+UODqztc3uHyDpd3uLzD5R0u73B5h8s7XN7h8g6Xd7i8w+UdLu9weYfLO1ze4fIOl3e4vMPl349hvktc/h547i3ArMtzNAC9CE6vo4sohjAvYYZ7mLuOIPVQ2YBpJjODUv0JCbtTAP/b6wKsf9wtFOANflZPHekPIeqYTyCCdhqonW81GnfoDUq0jVwtxtViXC3G1WJcLcbVYlwtxtViXC3G1WJcLcbVYlwtxtViXC3G1WJcLcbVYlwtxtViXC3m/RjmO6zFPNTX/OcNKTS0x2J5Pwc+gqkPIKrNdetobrvzlehxSEU8HOJUlK8AhhKFGbK/YGAZWpztr2RiUp1ykjyb/Vhz7+eTtd4c51WsNl+auzTDUpCc6zJb/LNucjfisFdTgyHqDbF5zVbjCUyKo2sWacgeRJmQkFNtbth45eYTnP0cBQmJxpBXWylv2i+3jyKcEWGlub61wqzdHro9fEBQaFerPa6MKpfyJlhqymvgTOSZmWmU3PwN2UtJCPPSU7o/ZGEOKcs0OnN+A6sozK64/fpz6UjpUbO9VlTfpUzvM2UqUR1nxCv+jtLbjbJUVvc/5yQnsjZg0ptSsvJgSuHY5JIr8tt/7uDXD/k9QBVZDa9so1ajoZpv/KuNPoBkdx/+AwuEgmH3aQAA" + } + }, + { + "ID": "997858e6f516ca8e", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "342" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0bfc15531186a483579512b9d32d7a4e/5899886698338449982;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifSx7ImZpZWxkcyI6W3sibmFtZSI6Im5lc3RlZCIsInR5cGUiOiJCT09MRUFOIn0seyJuYW1lIjoib3RoZXIiLCJ0eXBlIjoiU1RSSU5HIn1dLCJuYW1lIjoicmVjMiIsInR5cGUiOiJSRUNPUkQifSx7Im1vZGUiOiJSRVFVSVJFRCIsIm5hbWUiOiJyZXEiLCJ0eXBlIjoiU1RSSU5HIn1dfX0K" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:00 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:00 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnr135:4219,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/142,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=I608W8jzJYHMqwWCx6ToDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/142,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/142" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dW2/buBJ+768gDD94Aa9g+ZKLAT84jntioMlmnXT3oS0CWpo42sikDkX1JCfIf1+SohxZoe2kabrpYlogNsnRDIcczgy/aZW7d6QGQnBR65O7d8Q2UtX6pFqmS3WGfEEjpjpr85jPaFxr5v0CaMpNf8S+0jgKi4EFpCmdgx45FfxrFEJIzoIrWFASckgJ45IsqAyuyDmdxUDCLA5A/ppwIX/d3Wn3QyppCvKi3fL3Wrut7kW3tbPf3t/ze93d9q5/0VJ/PKkf3USy75ERZVoWDUMi4L9ZJNRELiOIw5RITigjcBOlMmJzkprpeaRhhvua/JdCnRBm2XzCLrlW6NPZ6Gh8PLyYnIx+Oz4dnk8OPoy/NMnUrEWfBDyEgYOE2DUZsCyOieGYfzUrrlZJ8kG+zn3yuRbEPAu9K4ijG2+sCQ7N0OfaZ6YFaBKHDD1MxTxbAJOa5I0uq56m+iupVKu18Oacz2Pwyiqr7gVn3vgmgERGnKX102wWR4GXs5uoRVgkVEZqpo0HIu8v+pX2e63dXzZzT0F8BVEIye3yIIviUHUK0KtllryxOmKYd7s7r8i803kB8yxR2wsuvr297uvw9duvwre713saX8HTW+9I9xg3MtVNbRmZhGMQcwhz5o0KiRGys+d/u5Ao/WhmfwKBPtXi1ili13+BHjFdzEJaVwoE1ycAYS7wfcYCbet1363UU83TvXIsVa7jTxFJ+D3jkk4uN+vX29/7odJesGHW2Fx8/V5nqUaaMXVIL2MIpPcfYCDUQ+ExyCseDgM9OS78dmffU+GOX0PjI7tm/H+MnPFMBOBicggxzKn2hKtcJoskLrhspLGOoeCtm8o02HwpIX+m4GVb+VNbt8eeS7NgekGmypdDKlUwurmt+15A47hR7be8/da38T6kSQLiiPPr9zSQXNzWzwUNlO52XI88EvzooXwSe9vcz0b92hv12+qK1/D+g4r/l7Vz6lMhykV2etu89GuqUz47JR7DLIxkWZ/HVlElsdq8yPZeZ29GKk1JNytTJSkc+dvbmRFnEm627M1jIhsntqVI/8DufODzzcqsEhSpzdtTJOdhHbKbd293JU7riJGY0boh6nSLgOWOMGvmk+hLRP6zTZOoFAPrD1/PlDlFAdTt5ykVdAESRFr3fe+KsjCGg5gH1w8uuRQ1bcTc8f1tJ+IHzqW175gLA+mJJOh4kY6i9gxNk6DgN0wSdZ+gOpc6MoKEFVgI2kxrZLddxrdO8Jn5NlHnUTAa24PpiYypsQkriWisJbam4zq7m6WOlGoMYnWmVCCNbxvVARtOd5+zkM/i7PY3+fHJZBTrbDDIhFD3Vm84S6VKCaTKC1h6ycXifSYzAfVK2wv5sqex5hm7TV1nrvKjpHdeLl2byWYpvu9aYZmnVp5OscBaUb3cmGaMmbOkBEyY7WyUKawv7LluMk9mr8xDa+Dgu+NK4Zx8S+ovO0fKe89ocJ1Pf8KuQN8mQjt6wj8ylSU7xHbaLtf+ncS65PlPlrdud5yb4jqvayzrmAsY30CQqdiZ1nt2R1Z6rSW1eq475E9uSqN8GUpu92GOlTFrIa5T+wMM0xVXf2LDNPfVqi2eXwmg4SnncWF7mtGfXFyDaDgGrYfrPpdrPWeZz3IN2512d4WruVvnxKXn7DWgawI+Ky2VTm2WIJdyxu9pFss+mSwWmcFRc+wWLiMW6dh+N6MpDCYnfww/TA4vTofT4fH4fDxtEhX6Ya6y2sHHs/H0Yjyd/mY6szTHi5tkCUEPEH9G/BnxZ8SfEX9G/BnxZ8SfEX9G/BnxZ8SfEX9G/BnxZ8SfEX9G/Bnx5zdmSog/I/78nfDnJslh2kH+b7GbROkNLIRQpSaJRYuvpEyO1HMqlRnc3eftMyUjSwczGtoUo0kiG+Bz7HiQf9wVCG7+aBmENtjyGmjYc+DB6gk1e2UBB7cHyizULAdSZGDR7GMLSCOgjYA2AtoIaCOgjYA2AtoIaCOgjYA2AtoIaCOgjYA2AtoIaCOgjYA2AtpvzJQQ0EZA+7sB2g+I7WgN2FumyZHbdTh0k1xGsQrzGmZ4wIebRKUeJhuwzcXKoF7+BYTDJfL96Y0ivV/u12ryM79opUnyV8kM7HtkVDsJjC10W6178jOr1idY38D6BtY3sL6B9Q2sb2B9A+sbWN/A+gbWN7C+gfUNrG9gfQPrG1jfwPoG1jfemClhfQPrG9/thTHV9aq+NcYAfCMe6/u5kiO5eY1Mo9KdR3PXna/ET6hUxKMhTWT5CmA5MUWh+wsBjqH12f5WITbVKSfJq9mPM/d+OVvnzbG6xGbztblrMywFyUqX3eJvdZOHkVB7tTQYMF/A5TW7rWcIKY6unaRl+yFKpYacGpVh65Xbz3D2FQ4aEo1VXu3kvOe+3D6JcQrSyXNnf4tZ4x7iHj5iKHNXm3tcHVWu9U2w1NTXwJXIs0JpF7n9D2QvJSXsx8is/TEPM5WyLKOzELdqFoXZFbdfv5KOlB612+tE9TFl+nemTCWu8xS84j/1eYdRmuj6+O8ZZKBrAza9KSUrj0gKx6anXNO//ORe/fiifw1KTZeRa33SbbVM89/yK17eKRXv3/0Nm/Fa2whnAAA=" + } + }, + { + "ID": "c7c049b571448f26", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "182" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ec4e3a027b537a108b678f0f22afaf58/4291825262347665255;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19fQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:00 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:00 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vna15:4288,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=JK08W9H3B4KHqwWJpq2YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1d3U8bORB/719hRXnISekqmy8+pDyEEI5IhdJAew9thZzdIeyxa+95vS0c4n8/f23YBCdAuUJAphKJ7dkZjz2eGf+mJNfvUAUYo6yyja7fIdPIROuraKku0RnSBEdEdFamMZ3guFLX/QxwRlV/RH7gOAqLgQSyDE9Bjhwx+iMKIUTHwTkkGIUUMkQoRwnmwTk6wZMYUJjHAfD3KWX8/Ua3uR1ijjPgp82Gv9nYaLRP243uVnNr0++0N5ob/mlD/HhcPrqKZMtDexHEIWIQNFGUoSTKsohMUUQQgZ8oUzMq5hzCJJ+OyBmVs/56PNgfHvRPR4eDjwdH/ZPRzofh9zoaK4W3UUBD6FlIkFG8R/I4RoqjfquWVSwFpz29mNvoWyWIaR565xBHl95QEuyqoW+Vb0QKkCQWGXIYs2meAOGS5CXXTs5F/OOYiyVJvCml0xi8sl6iO6HEG14GkPKIkqx6lE/iKPA0g5HQNEkxj8R0ardE3t/4B97uNDb+WM09A/YDWCFEW9hOHsWh6GQgl0Sta21+RDFvt7u/kXmr9QTmeSr2EGx8O62nTHoFX7/5W/i2NzsP48toduXtyx7lEMayKS0j53AAbAqhZl5bIFFCupv+rwuJss9q9ocQyKPLrqwiNvwn6BHjZBLiqlAguDgECLXAvZwE0tarvl2ph5qnfeVIJvzDXyzi8CmnHI/OVuvX2dp8VmlP2DBjbDa+fqc1UyPLiTikZzEE3PsTCDDxUHgA/JyG/UBOjjK/2W15InDRC6h9JheE/iTomOYsABuTXYhhKhwVmc5zGSVpXHBZSWMcQ8FbNoVpkOlMgn6m4GVa+ql7t8ecS7VgckHG8E8OGRcR5/Kq6nsBjuPaYr/h7Td+jfcuTlNg+5Re7OGAU3ZVPWE4ELqbcTlyR/Cdh/QkNu9zPyv1a67U715XvIT3F8z+LWtn1WeBSItsddovqE757JR49PMw4mV97lrFIonR5km293v2ZsAgzFYrs0hSOPL125kBJRwu79mbu0QmTtyXIr3A7nyg09XKzBOYoLCGZqZ5GIds593ZmIvTMmKkarSqiFrtImDZI8yS+aTypqB/N3EalWJg9fbtsTCnKICqeT3CDCfAgWVV3/fOMQlj2IlpcHHrkktR00TMru/fdyKecS6NLctcCHCPpYEI1TKKmjM0ToOCXz9NxX0Cy1xqXwliRmAhaDWtkt20Gd8ywcfq3UicR0ZwbA6mx3IixkakJKK2lNiYju3srpY6EKoRiMWZEoE0vqotDphwuvGYhXwUZ7u/0ccn51Ess8EgZ0xcTr3+JOMiJeAiLyDZGWXJXs5zBtWFthfSWU9tyTNmm9rWXOW5pLeeLl2ayWopvm9bYa5TK0+mWGCsqFpujHNC1FkSAkbEdNbKFMYXdmw3mQezF+YhNbDw7dpSOCvfkvqzzoHw3hMcXOjpj8g5yNtEaEYP6WcismSL2FbT5tr/J7E2ef6D5S3bHeum2M7rEss6oAyGlxDkInZm1Y7ZkbleY0mNju0O+cpNaaCXoeR2b+e4MGYsxHZqn8EwbXH1FRumuq8u2uLJOQMcHlEaF7YnGf1F2QWwmmXQeLj2Y7lWNUs9yyVsu832HFd1t9bEpefMNaCtAj4pLZVMbWYgl3DGeziP+TYaJUmuwFIN0MJZRCIZ268nOIPe6PBL/8No9/SoP+4fDE+G4zoSoR+mIqvtfT4ejk+H4/FH1ZlnGhSuoxnO3HMgswOZHcjsQGYHMjuQ2YHMDmR2IPNa7Y0DmR3I7EBmBzI7kNmBzA5kXn9k0IHMDmR2IPOrNsw3CDLXkcZie/q/TteR0BtICKFITVIDCZ9znu6L50Qq07u+0e1jISPPehMcmhSjjiIT4DVA3NMv1wVMqx8tI80KQF6C/3oW0Fc8IWYvLGDnakeYhZhlj7McDGR9YFBnh1o71Nqh1g61dqi1Q60dau1Qa4dar9XeONTaodYOtXaotUOtHWrtUOv1hxodau1Qa4dav2rDfJOo9S0sO1iC6JZpNDy7DGyuo7MoFmFewgy3IHAdidRDZQOmmcwNyuVPIOzP4O2vLwnnfr9ZOt21/4STOtIf1NIzn9Ii2mmgdrXdaNygtZ//NnI1B1dzcDUHV3NwNQdXc3A1B1dzcDWHtdobV3NwNQdXc3A1B1dzcDUHV3NYf6DY1RxczcHVHF61Yb7BmsPd9Vr8TBYF8A1oLO/nQg6n6kNaagvdOprb7nwlfkykIh4OccrLVwDDiQgK2V8IsAwtz/bvFWJSnXKSPJ/9WHPvp7O13hwXl1htvjR3aYalILnQZbb4V93kbsTEXs0MBtQbsHnNduMRQoqjayZp2H6IMi4hp9rCsPHKzUc4+wUOEhKNRV5t5bxpv9w+iHEG3Mqzu3WPWbs9dHt4hyHXrlZ7XBlVLuRNsNSU18C5yDNHaRa5+QLZS0kJ8zJQa39Aw1ykLLPozNiVmEVhdsXt119IR0qPmu21ovouZXqbKVOJ6zQDr/hrOm83ylJZ6f6UQw6yNmDSm1KycoekcGxyyhX5/SE34td3+U0iFVkrrmyjdqOhmq/qq1DeCT1u3v0HFm9XlhVmAAA=" + } + }, + { + "ID": "f26061c65a11ed2c", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "260" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3b577c11ecbab07031db6f9a62d5cdb9/2683764925851731088;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifSx7ImZpZWxkcyI6W3sibmFtZSI6Im5lc3RlZCIsInR5cGUiOiJCT09MRUFOIn1dLCJuYW1lIjoicmVjMiIsInR5cGUiOiJSRUNPUkQifV19fQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:01 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:01 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfr1:4352,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=JK08W6GqJpeAqgXe7Ipo" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1d7U/jOBP/vn9FVPVDH6kXNX2hgNQPpZSj0sJxhb37sLtCbjK0eUjsnOPswSH+97Njp6TBbWHZFzjNItHansx47PHM+Ddse/fOqQHnjNf2nbt3jmmksvVRtvIu2RmwmIRUdtbmEZuRqNbU/RxIyvL+kH4hURgUAzGkKZmDGjnj7EsYQOCc+wuIiRMwSB3KhBMT4S+cCzKLwAmyyAfxS8K4+KW/094PiCApiMt2y9tt9Vvdy25rZ6+9t+v1uv1237tsyX+uUI9uItlznaMQosDh4LddJhbAnTB14jBNQzp3QupQ+NtJ83kVMw9gls0n9IqpuX88Hx2PT4aXk9PRbydnw4vJwfvx56YzzdXed3wWwMBC4hj1BzSLIifnqN/miysXRLCBXtJ951PNj1gWuAuIwht3rAgO86FPtU9UCVAkFhlqmPB5FgMViuTnr6CakfwRRMiFid05Y/MI3LJ2sjtm1B3f+JCIkNG0fpbNotB3NYOJ1DdOiAjlpBoPRO7/yRey32v1/7eZewr8C/BCiLa2gyyMAtnJQS1MvrqN1ZGcebe78x2ZdzovYJ4lcifBxrfXecmkN/D12l/Pt56by3YhXr/7XSa/09t7fYuygW93t/c0vpylt+6x6sk95lQ11XHJBJwAn0OgmTcqJHpRdr2vFxKmH/LZn4KvvBq/tYroey/QIyLxLCB1qYB/fQoQaIFHGfWVA6h7dqWeembtK0dT6Tr/5KGA3zMmyORqs369vd0fKu0FG2aMzcbX63WWaqQZlZ7rKgJfuL8CBS4fCk5ALFgw9NXkGPc6nufKyM6uofGBXlP2N3XOWcZ9sDE5hAjm0nvT+SqXSZxEBZeNNMZbFrxVU5oGnS8l6GcKXqaln9q6PeZc5gumFmQKf2WQChmMb27rnuuTKGpU+w1vr/V1vA9JkgA/Zuz6iPiC8dv6BSe+1N2Mq5FHgh89pCexu839bNSvvVG/rfFpDe8/CP+nrJ1VnwqRFtnpbfP+31Od8tkp8RhmQSjK+jy2iiqJ0eZFtvd99mbEIUg3K1MlKRz569uZEaMCbrbszWMiEye25Y0/YXfes/lmZVYJTFB4hWameRiHbOfd66/EaRUxkny0nhN1ukXAskeYNfNJ1CVK/26TJCzFwPrD23NpTqEPdfN6RjiJQQBP6zKuLQgNIjiImH/94JJLUdNEzB3P23YifuBcWrbsloJweeJ33FBFUXOGpolf8BsmibxkEZVLHeeCuBFYCNpMm8tu24xvneDz/N1EnkdOSWQOpsszKscmtCSisZbYmI7t7G6WOpKqUYjkmZKBNLptVAdMOO0/ZyGfxdnub/TxyUQYqWzQzziX93Z3OEuFTAmEzAtoesV4fJSJjEO90nYDtuxprHnGbFPXmqv8KOmdl0tXZrJZiufZVljo1MpVKRYYK6qXG9OM0vwsSQETajobZQrjC3u2m8yT2UvzUBpY+O7YUjgr35L6y86R9N4z4l/r6U/oAtRtIjCjp+wDlVmyRWynbXPt30isTZ73ZHnrdse6KdZrvd2yThiH8Q34mYydab1ndmSl11hSq2e7Q75xUxrpZSi53Yc5VsaMhdhO7Q8wTFtcfcOGmd9Xq7Z4seBAgjPGosL2FKM/Gb8G3rAMGg/XfS7XumapZ7mG7U67u8I1v1tr4tJz5hrQzQM+LS2VSm2WIJd0xkcki8S+M4njLMeRNXYNVyENVWy/m5EUBpPTP4bvJ4eXZ8Pp8GR8MZ42HRn6YS6z2sGH8/H0cjyd/pZ3ZqnGy5vOEoIfIP6O+Dvi74i/I/6O+Dvi74i/I/6O+Dvi74i/I/6O+Dvi74i/I/6O+Dvi74i/vypTQvwd8fdvhL83HQ1TD/Sf3TcdqTfQAAKZmiQGLV8IkRzL52QqM7i71+1zKSNLBzMSmBSj6YQmwGvsfKBf7goEWz9aBuFzbH0NNO5a8HD5hJy9tICD2wNpFnKWA8EzMGj+iQHkEdBHQB8BfQT0EdBHQB8BfQT0EdBHQB8BfQT0EdBHQB8BfQT0EdBHQB8BfQT0X5UpIaCPgP43A/QfEOvRGrC7TKOR63U4fNO5CiMZ5hXM8ICPNx2ZeuTZgGnGK4Nq+WMIhkvk/+PPR7o/36+d9Bv5+KCmoz8LaWA+CEm2Ez/f4W6rde+8ES32HSzQYIEGCzRYoMECDRZosECDBRos0GCBBgs0WKDBAg0WaLBAgwUaLNBggQYLNFigeVWmhAUaLNB8s088qq5X9WOPctRzxCJ1P5dyBMs/B6lR6dbR3HbnK/HjMhVxSUASUb4CGE5UUqj+QoBlaH22v1WISXXKSfJq9mPNvV/O1npzrC5xvvnK3JUZloJkpcts8de6ycOQy71aGgzkb8DmNbutZwgpjq6ZpGH7PkyFgpwalWHjldvPcPYVDgoSjWRebeW8a7/cPolxCsLKc2dvi1njHuIePmIotKvVHldFlWt1Eyw11TVwJfKsUJpFbv+E7KWkhHkZ5Wt/woJMpizL6Mz5rZxFYXbF7derpCOlR832WlF9TJn+mylTies8Bbf4X5nuYZgm6k8Bfs8gA1UbMOlNKVl5RFI4NjXlmvr2onv567P6HqOaKqPX9p1uq5U33+DXMb2T2ty/+xcwG/S8n2oAAA==" + } + }, + { + "ID": "d840215af30da35f", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "214" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "9b3119860a4a42591eb57280b1091bd3/1147759988698925241;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifSx7Im5hbWUiOiJyZWMyIiwidHlwZSI6IlJFQ09SRCJ9XX19Cg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:01 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:01 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbh141:4349,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/22,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ja08W8GUDoLgqgXl05b4Cg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/22,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/22" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2cW28aORTH3/MpRogHVkIjhkwujcQDIWSD1KRZknQf2qoyMycwG2PPejzZZKN89/VtYJgYCM22TSOrEuDL/I8vx/bxL4WHLa8GjFFWO/AetjyTyETqk0ipLJEZ0ylKiMisjTEdIVxr6nwGKKMqPyG3CCdxUTCFLENjkCXHCeDYYxC1vSTz+H0K3rDf+zA88kY59yYo8wj1smgCU1Q8HcMoHw/INZXPfxqcfey+Hxx9HZydX11+aXpDZfTAi2gMnYVCz5jtbGDTU8Y6JMfYU30/Z5TTju7xgfe5FmGax/4EcHLn92WFI1X0ufaZyBbIKkUjxOtVXxYgNs6nQLgs3KAt8lHxjyMuOjf1x5SOMfjlBojsKSV+/y6ClCeUZPXzfISTyDfjPyBpzhvzYv8vdIsOgnDnt++iuxOu0c2A3QIr5C9ULw/zBMfA6mpgTMKPhCtxaCzUMEba6xq/wsgK3e391vfR3Qs20v0oxxdxyvxb/Qm0aGNeoHTb4X77hcJVyb1w73mKjGb3/onMuUQjDEORrKtPFhuL1cyYBNvfbMjP06W6QTuYjUmWE5/BNYaI+78DASYeik+BT2jcjSKxMVAWhGEoPZreQOOK3BD6D/EuaM4isIkcAYYx4gkZL6oMpikuVFbWUU0MZz2XSR8jMp5Z0M8UWialn3q3/7wVqwZMDsgQ/s4h42L3uruvB36EMG5U8412sM7xl2gfoTQFdkLpzTGKxJwLH2AoEn035bLkieEnD+lGrPXllf1rr+zf9jpnW6L9EbF/y72z9qdSyfj32o3we3anvHZKGt08Tni5P0+9olql2Blf39z0GMTZ6s5Uq5g9bt1u/BNmpkcJh7s1c/O0kra6u27P/gmz856OV3dmsYK2tvMK3UxrmA3Zrr2zt1vWlidGqkrrqtJ2WBxY9hNmSXtSGXXq1zZKk9IZWJ9/vBDulERQN+/niKEpcGBZPQj8CSIxhkNMo5v5llw6Nc2JuRsE61bED2xL652lLQS4z9Jo20/kKWrW0DCNCr1umorQFMlA9EQZYsZgYWh1XR1N2ZxvmeEL9Wkg1iMjCJuF6bOciLIBKZloLK1sXMe2dldb7YmuEcBiTYmDFN83qgXmON3bZCA3UrbvN3r55DzB4jOJcsbEdcfvjjIuQgIu4gKSXVM2Pc55zqBeSfsxneU0ljxTBL3WWOVHWd9+uXXpJqutBIFthLkOrXwZYoHxono5McwJUWtJGBgQk9ko1zB74Y7t/vRseeEesgcW3V1bCGfVLXV/ltkTu/cIRTe6+QMyAZaIgN2UntErIqJki9nttm1r/5/M2uxZry4bzY51UmzrdYlnnVIG/TuIcnF2ZvUdMyMLucaTWju7b8+VenoYStvuvI2VMuMhtlX7AxzTdq7+wo6p7qtVX7ycMEDxOaW48D0p9CdlN8AalkKzw4Wbqta1pG7lEtnddrigqu7WunLpuQJ1qAOflIZKhjYzYiI242OUY37gDabTnMsR0rAPrhOSyLP9YYSyOW887w67p/3L/rDpiaMfxiKq7Vxd9Idf+8PhB5WZi9qSKja9Gc7sOJbpWKZjmY5lOpbpWKZjmY5lOpb5+mbHsUzHMh3LdCzTsUzHMh3LdCzTscw34JhvkGU2Pc31Ovq/ZDY90W8gMcQiNEkNeZxwnp6I50Qo03l41OkLYSPPOiMUmxCj6SXmgNc0sqPfHgrwpx8tA01FK5ewRH8BIIq6Ci7Fh/eHwiFE+zqc5WCY6KlBnA6LOizqsKjDog6LOizqsKjDog6Lvr7ZcVjUYVGHRR0WdVjUYVGHRR0WdVj0DTjmm8Sic8TXK+NDwyPnpRryLeOYTe86weKAl4BBE0fNPkXQoeIAk5wuFMqBn0LcnZHTTxugwS+PS9U3UGl6+kv5HYP1RDqN1ECErdajt4HSgee4rOOyjss6Luu4rOOyjss6Luu47OubHcdlHZd1XNZxWcdlHZd1XNZxWcdl34BjvkEu+3S8qt+/V4SvR7G8nws7nKov5Dcq2fo0t935SnpMhCI+ilHKy1cAo0REDZlfGLAULY/21xoxoU45SF6Mfqyx98tlrTfH6hCryZfuLt2wdEhWsswUf+s2eZQwMVczhwH1AWy7ZtjawEixdE0jjez7JOMSOTUqxWZXbm+w2VcURK0Ui7jaqrxvv9w+SzgDbtXcfbfGrd0cujl8Isj1Vqt3XHmq3MibYCkpr4ELJ89CzYLm/oTopdQJ89ZTY39K41yELLPTmbF70YrC7Yrbb1AJR0qPmukNX9QnFzL9UiFTSXWcgV98pcU/SrIU8WjyRw45yL8NmPCmFKw8qVJsbLLJNfmT5I/i5Yv8cfKa/Ltj7cALWy2V/MbfOd8Sio9b/wGXVRAFfF0AAA==" + } + }, + { + "ID": "93087d48a6a2259f", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5ed80282e2526be35e76bee65935fe02/17986161151440981474;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:01 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnp127:4499,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/64,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ja08W6nOJ8jpqAWo5bOoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/64,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/64" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "2bf72a027e8d2df4", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4805c2d263f1b674d9b803995703b7d2/16378100814945112586;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxMCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:02 GMT" + ], + "Etag": [ + "bFVp9CnqlbG6CD74hInD0A==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndc65:4052,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/104,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ja08W6H0N4n2qAWImZK4Bg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/104,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/104" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SX2+CMBTF3/kUpHtVC4iiJj74b8bE+YBsL8tiKlyxEyjSOmeM332lxWQxbpEHSM+9p+d3bzgbJtrRLEI9E61pvD9AcXoSZJ0AqskSCBKr0vNb3h1l+2Q9bY/GnrudZWNr0O+rJqrc0SEJQdRzVoi613Z6ERGEg1g5lt2xPMtduVa763Q7dsv1HM9eWfJpqKR/WmxLJXBINnOa7cqcrRA572F8PB4bMWNxAiSnvBGyFF8HwF8Ozgv2CaHg+IYLV1wcPwKIFSDHD3KqNh82UEAWgqQ9G6aJKpTZvTWVNilqlKrjAS5tU3Ha9ACgYV7ULsMtpOTKtqGQRFye3uVJSVLMSFrC629Na+KUK20Z+LPFFJXipXbPc0j5rWe2CCbTia9N8vVRocje4UlAGY/0AqUyZ1kcQHGn4rPjLyEsgAjKsoDqYLvVlKM3bdexXFv/vN85Le71tLvNaocoIVy8sIjKPUR/33QdJBgM5xPtY6G6uVRfl8i4GD9rF5lTSgMAAA==" + } + }, + { + "ID": "648126d3e910672e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/bigquery/v2/projects/dulcet-port-762/jobs?alt=json\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=d4e6b01fab4161b52c3535d1c11f7d5c89da8d9001a95b946a7d324d2cd3" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "86896158aad5cdcec29c97c5ce09e11c/14842095873480628019;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/bigquery/v2/projects/dulcet-port-762/jobs?alt=json\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1kNGU2YjAxZmFiNDE2MWI1MmMzNTM1ZDFjMTFmN2Q1Yzg5ZGE4ZDkwMDFhOTViOTQ2YTdkMzI0ZDJjZDMNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImNvbmZpZ3VyYXRpb24iOnsibGFiZWxzIjp7InRlc3QiOiJnbyJ9LCJsb2FkIjp7ImRlc3RpbmF0aW9uVGFibGUiOnsiZGF0YXNldElkIjoiZGF0YXNldF8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDAwIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIiwidGFibGVJZCI6InRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTAifSwid3JpdGVEaXNwb3NpdGlvbiI6IldSSVRFX1RSVU5DQVRFIn19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJ0c1Q1RmltWFE4Yzl3cHpNSHZ6WWU5Tzh1aDEiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0KDQotLWQ0ZTZiMDFmYWI0MTYxYjUyYzM1MzVkMWMxMWY3ZDVjODlkYThkOTAwMWE5NWI5NDZhN2QzMjRkMmNkMw0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCmEsMApiLDEKYywyCg0KLS1kNGU2YjAxZmFiNDE2MWI1MmMzNTM1ZDFjMTFmN2Q1Yzg5ZGE4ZDkwMDFhOTViOTQ2YTdkMzI0ZDJjZDMtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "1072" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:03 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Ef8T_NNGbIXMj6qklVSU2hJveuM\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncg5:4019,/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/25,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Jq08W8PeB4vBlAHE37ngAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/25,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apps-upload" + ], + "X-Google-Netmon-Label": [ + "/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/25" + ], + "X-Google-Service": [ + "apps-upload" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrShg6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur_c0-FVx3T_A45lx9KTgRAmp1eXGEQu8QhxzRm3_-aWJc0xl96sAcQTmw22wiA4zVtDFhD6NfB9BBCPK3tdtIliMFkxA" + ] + }, + "Body": "ewogImtpbmQiOiAiYmlncXVlcnkjam9iIiwKICJldGFnIjogIlwiNzJrN0R6ZXllRWw0c2hsaEdmblpEX1V3WDA0L0VmOFRfTk5HYklYTWo2cWtsVlNVMmhKdmV1TVwiIiwKICJpZCI6ICJkdWxjZXQtcG9ydC03NjI6VVMudHNUNUZpbVhROGM5d3B6TUh2elllOU84dWgxIiwKICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9iaWdxdWVyeS92Mi9wcm9qZWN0cy9kdWxjZXQtcG9ydC03NjIvam9icy90c1Q1RmltWFE4Yzl3cHpNSHZ6WWU5Tzh1aDE/bG9jYXRpb249VVMiLAogImpvYlJlZmVyZW5jZSI6IHsKICAicHJvamVjdElkIjogImR1bGNldC1wb3J0LTc2MiIsCiAgImpvYklkIjogInRzVDVGaW1YUThjOXdwek1IdnpZZTlPOHVoMSIsCiAgImxvY2F0aW9uIjogIlVTIgogfSwKICJjb25maWd1cmF0aW9uIjogewogICJsb2FkIjogewogICAic2NoZW1hIjogewogICAgImZpZWxkcyI6IFsKICAgICB7CiAgICAgICJuYW1lIjogIm5hbWUiLAogICAgICAidHlwZSI6ICJTVFJJTkciCiAgICAgfSwKICAgICB7CiAgICAgICJuYW1lIjogIm51bXMiLAogICAgICAidHlwZSI6ICJJTlRFR0VSIgogICAgIH0KICAgIF0KICAgfSwKICAgImRlc3RpbmF0aW9uVGFibGUiOiB7CiAgICAicHJvamVjdElkIjogImR1bGNldC1wb3J0LTc2MiIsCiAgICAiZGF0YXNldElkIjogImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCIsCiAgICAidGFibGVJZCI6ICJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDEwIgogICB9LAogICAid3JpdGVEaXNwb3NpdGlvbiI6ICJXUklURV9UUlVOQ0FURSIKICB9LAogICJsYWJlbHMiOiB7CiAgICJ0ZXN0IjogImdvIgogIH0KIH0sCiAic3RhdHVzIjogewogICJzdGF0ZSI6ICJSVU5OSU5HIgogfSwKICJzdGF0aXN0aWNzIjogewogICJjcmVhdGlvblRpbWUiOiAiMTUzMDcwMzE0MzA2NSIsCiAgInN0YXJ0VGltZSI6ICIxNTMwNzAzMTQzNzQ1IgogfSwKICJ1c2VyX2VtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgp9Cg==" + } + }, + { + "ID": "700cb8c63c78e8a9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/tsT5FimXQ8c9wpzMHvzYe9O8uh1?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6fbe66d9991e7d3fc88b150ac8ec2c1b/3170603987279855216;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/tsT5FimXQ8c9wpzMHvzYe9O8uh1?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:04 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Ef8T_NNGbIXMj6qklVSU2hJveuM\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:04 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnh126:4324,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=J608W8jJM4L8qwXf556wDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqLkksKS1WslKo5lKA8FKBHKWgUD8/Tz93JS6FWh2oqsziksxkuMrkolSgUH5eSGYuWIOhqbGBuYGxoYmxgZmpkg7UsKISTHlzE1OQsVy1XABll05wgAAAAA==" + } + }, + { + "ID": "a2e8448865da961d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/tsT5FimXQ8c9wpzMHvzYe9O8uh1?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6fbe66d9991e7d3fc88b150ac8ec2c1b/1634599050110272409;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/tsT5FimXQ8c9wpzMHvzYe9O8uh1?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:04 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vQNwUmNHljxAfdL9W5rmRGUdqR0\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:04 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnme13:4459,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=KK08W6CMMIT0qgXmtqDQBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqLkksKS1WslKo5lKA8FKBHKWgUD8/Tz93JS6FWh2oqsziksxkuMrkolSgUH5eSGYuWIOhqbGBuYGxoYmxgZmpkg7UsKISTHlzE6h8cn5uQU4qyJAgkFFAVYZ6BkALuWq5AKwBnoGaAAAA" + } + }, + { + "ID": "885270e153c78d31", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/tsT5FimXQ8c9wpzMHvzYe9O8uh1?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6fbe66d9991e7d3fc88b150ac8ec2c1b/26538713631115202;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/tsT5FimXQ8c9wpzMHvzYe9O8uh1?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:05 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/TNRK_mom_aSV0UksR_zEUfwYLvA\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:05 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncp68:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/132,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ka08W7r2CsS3qwXk3o6oAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/132,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/132" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAGWQTQrCMBBG9zlFyLpIalJLXYq6VAheoCZZBNqkNFNESu9umsaidDnvffPDjAgTDzUMnhzxiPBS6VCQ8/12IQhPWYoYD0auMdnrgJx9mDam84LRkrKcM3ooSJYm9bD1JU9eW7WxvKqqxUrXdo2eV4h5UUjlOxpN42qVzsDE2G6Aq2m0j3Ni7w89vSGZfVJugOCEe0XM/uiaZl/+rJXQ0vUqYhr+gafwEzShDzcNRHY6AQAA" + } + }, + { + "ID": "3aa67a496e2fa75a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0010/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e2080646498d31c63e96d7198e9e9ce5/8481907727850384086;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0010/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:05 GMT" + ], + "Etag": [ + "M1Pqw8MdoTG1OJhZ0TaXgQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4184,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29.esf,ybco5-v6:9873,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/18,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:4043::]:4324 had response of OK with response time of 0.014394044876098633 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybco5-v6:9873,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29.esf,ybco5-v6:9873,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfE1DCgst/BNyQ9xN/T3yogyCEmMSA+0tYWoKskvScwJyi8vBik1hogVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVodPOoMkNRBWbFcSLqItjCJSAsNqWVhMpEWGuG0kAvEquUCADhrKMTEAQAA" + } + }, + { + "ID": "b2492035242ab2c8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0010?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4c39785cc1206e946e3bde35caea2059/16864939876373171435;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0010?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:05 GMT" + ], + "Etag": [ + "Ni0+chkjYIWVl0tzrqSu2w==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:05 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnam66:4165,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/75,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ka08W8mgF9bBqgXSz4PACA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/75,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/75" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYMgmoBELBeQquUCAH8xZICOAAAA" + } + }, + { + "ID": "1966a64aa1a653e2", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0010?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8fad7c4f5d314adde9ec4a65fee0b613/15328934934908752147;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0010?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:05 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbg127:4174,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ka08W7miJoLLqQWIlamoAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "fc996b6cd9e31172", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4a3bb0f37be92dea1c578e85e9e61864/13720874598412817980;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxMSJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:05 GMT" + ], + "Etag": [ + "Co7wlv25klV3moyhBD8BlA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnal63:4409,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ka08W9idMs_sqgXhhojQAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TTW/iMBCG7/kVkfdKcT6AEKQeSIkqJBaqNO1lVSGTmODFibOxaYoq/nsdO5Eooitycfx63plnxvKnYYI9KVIwMcGGZP8OuDr+EmhDMejJIyxQ1hw9MK+m785wT1/dnB13wWwc0On9vQoiyp0eaILFXckqceeNnEmKBOJYrB3LHlueNVgPrJHv+GN7OPAcz15b8uurSv8JsW1VgWO6XZBi39TZCVHyCYR1XfczxjKKUUl4P2E57BqA7w4sK/YXJ4LDCy7YcnF4CyBUgBzeyKnCIrzFFS4SLGk/DdMELcr82pgamxQ1ShtxA5e2qXLadAOgYZ7ULJMdzlHHtiWYplzu/sidkqRYoLyB12tPa+JYKu05jubLR9CIp941zyHnl575Mg4fw6iTc5YqOQqfwmkczn5OVuHkMlcUPqyiWad+x+/8Zxk2jNE2+CxJsFotwukSaP2kljej/X1rByU7CY4CN9mBGnijLFiRxbi6chKx+kxIKowEYUVMNIc9dOXFuPZg6Lu+flofJamuxYx8t71hQBEXv1lKZJvpz5m6ruJpsAi1jyUqc6O+PAPjZHwByZcd2egDAAA=" + } + }, + { + "ID": "fc4b70e92ad6e809", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "425" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b0c104373148d3d18ac9e597af474f71/12112813162438810213;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiSU5TRVJUIGRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMC50YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDExIChuYW1lLCBudW1zLCByZWMpXG5cdFx0XHRcdFx0XHRWQUxVRVMgKCdhJywgWzBdLCBTVFJVQ1RcdTAwM2NCT09MXHUwMDNlKFRSVUUpKSxcblx0XHRcdFx0XHRcdFx0ICAgKCdiJywgWzFdLCBTVFJVQ1RcdTAwM2NCT09MXHUwMDNlKEZBTFNFKSksXG5cdFx0XHRcdFx0XHRcdCAgICgnYycsIFsyXSwgU1RSVUNUXHUwMDNjQk9PTFx1MDAzZShUUlVFKSkiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IndnSG91c3NLVVhqYjBRaWhUSjZKN3A0V3J1NiIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:06 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/A9NcSiWg-EAzK5oScMMXO7wU5Dc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlk1:4163,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/51,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Kq08W9qzAZHGqAWElonYAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/51,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/51" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1UXW/aQBB851dY14cQCfAnNo5UtTShKQkhKraTqKVC9rGYC8ZnfOcgEvHfe2dMk7TNh3nxMTs7O3MrP9QUtCDpFB0pKCLxqoB88+GWRqghAOBhLIExcoyFc3IPG+glFpsn89NZ+uNkEqxvNEvtukPskeu42even7ephy8ubi6dddA+wWNU9iFl+2mRYODNjOa86djGUeC11vE3WjB2HtzcRtp3MvfP7DMns67zwi6JDJLZgKQLSZ9znrEjVV2v162Y0jiBMCOshelS3Q+u3hlqltNbwJypf8mpwhRTXxH8lFAcckLTj4FXigvCCGaQQ4pBDPBQUxRUde//z5Aklawd+oY3Be31ZLGQrClbqYppOiNxke+hUrZ0Vx0eT6g/9HojX5mGPGTAJ4amdzRHsyaWZruG29HblmM4+kQTT4uHUQKvlOi6Uk/DJTSUtFiyhpIDPhynY/74u+oOgp6n1A/Cg4byU/vVUDx/FBz740LTTPzl8nJQvkFd/Ns7PGw8Z4+5GL1+EEmu/jL3a3fgvUjGkmy8JVymK24HGCdpGaMvve/je8clSvYu06rmHQHviWXOO9rbkSPJ2e7mzXJCc8Kri/V7o+6x37/qVW4KBgOIQ7zxVomomIUJAwFsq61hPOQF26+LPEm/aBQMh/3hKXpSRUQq+E8lzmGXEFmWBL1tillN3bJ1fWdJknL+L246Ff58N0vpJaTc32TwuKPoyazCSj6BZUikEWTatum6pmjZbkZ6h2s6XXI3NBx3gc0Yx2Q1Xy2STkSjefF5CneQ0AzyViya3BEMIca0SLn8DKDatvYbRinZ/M8EAAA=" + } + }, + { + "ID": "6e27e4e15cc0f532", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/wgHoussKUXjb0QihTJ6J7p4Wru6?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "34b97dbeabb4425bda73595500f29e85/10576808225269227406;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/wgHoussKUXjb0QihTJ6J7p4Wru6?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:07 GMT" + ], + "Etag": [ + "lPtGZzM5P29U6/v6G2XdVA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4064,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/48.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/48.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/48" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41SW0/CMBR+369Y6itEgjqjCQ8DlgEqjDGUaHzY5WwMu3W2nQQJ/9223EYkxqf2u/Q7pz1da7qOPtI8Qvc6CtLkswS6ukiAj+XGBVZizsRSkJwBqkk3cD+Rbuxw+/X76cZp3k2Nyy/Dbs6iZ7PV2rpYOIfMF761QALHKeCICfymsL7jlZb7GchEtdaOPF8Vip94bn9oo52wqf2RUGbsXEJ/6Fm25ValjERKci3HMj2r+594CuG5dNfqjNxuVfl129PAk9CAEFw5e5LcHo0eLXOIKurmsH/XqoxEqne0IIELMVDIQzgOoKBkASHvq0lHJQ6B1wtCef3WaO7Ky5NbfZn0SMnYw3S2CBrjdO4NjMFtcf1CS2PvxST0eUpyaZ9O0KE6J9zH7RUH5lASAmOgEhto31uHZAUGLlvjtARFh774Lr2UCy72MduSYpjdDJtxLNqGyCVL+aDoCmkb7QcWFZGstwIAAA==" + } + }, + { + "ID": "d80b3940b4329c91", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/wgHoussKUXjb0QihTJ6J7p4Wru6?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "34b97dbeabb4425bda73595500f29e85/8968747888773358774;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/wgHoussKUXjb0QihTJ6J7p4Wru6?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:07 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/jUZsCknefa7MJr9SIbd948EKqQg\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:07 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnr135:4219,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/40,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=K608W8jbLcKVqwXP5SY" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/40,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/40" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAMVYW4+bRhh9969AKFJaabOaKwOW+uBkXXWlOl7ZjqIqiSwWxhtaDBSGbFbR/vfOAIbBXOy2m+wLYr7rYZhzhuHbxDAz4Yo8M6fGt4lRjrgcmFfLt3NzYjxeVCFBJgKvDvNSLk1xtAn2RTSkGDCAIbEgBOZFVSkVXT9mlZ9HfsfLCGWlV8TCDV8/CJ7dpLHHs4z7KrLK/Tvn6UOFpRrdhG4kLR+UpbRLT+SWDdYATI3rKMlFka9cgVbPOMBdZG2wFmgCJN6Om1q1+94NxErNyezLnYwClwBRSBnCiAFC1IDpoYusjDNJt8LC/XpWhTJOqyBfit/GoHvqlqCbULdsJ1QdmgQv3stJ5O0mJbga6VFs3Zb1Vqk7n6pSYWmq3KfBERJtqso6rdAaCO6rccBxqkYFQ6uRfc53u5AvcyGBFktWBTjWSMA6CcKQ+8cvw4tTP1vJme91vJcABI+K7rUvcVNXlgqLxV0uz9Y0h1xw/2YkqKa/+Wa5uPl9vplrLp5kNadqVknPX0HkH1LeNRkqJ789TpPWFxgY01+Ml+7LJlRZobLOVqvZHx9zALB3eXlZ3PB2GFJh0DyYPlU3jxf/Hxgpgd22gZHzgJEKWMuGlQ18F7C0BOu1wdLzwNJ/P4vvV9fnwELgwniBoLogdcHqQtSF6gg2S2O7lcvtjgOwjQsydMFMtGGFrCvmcGq8id2QZx4/0nM4rucM2WN6bkOnqae4slZo9cc1Dy/206DuY8dCyCKQAodgOaC9sm/hYd0nCGIbW4Qxx2ZyYPXKvsWeW/ehbVvMQYQiGxeDIeXH48qPGcPUJg6gcsOVgyHpt0ak/zzlp2hE+ZvnKB4K9ws/Q99b+PF/FH4KzlF+Cp5G+lfz2dVJafh1tVw8FenR1Cgn8ojyaJzydiNCfZR3CD1FeXiC8rBhT4vikDpDHO+kVItLT3kmUlvIdjCwIbMgkANniNOwv07d+2SdwyODEVZbNqWYUAdChzhy0M9qCJwRWp+oUcNwTvAaPBOtf+T33FmkPne/b5EfDpL/Cb6LYPFd9JPsXyD5+Ud84wx82aDzRE5dSz0xuTxY7+Wh2x867crDsDwnh0HEO+dbHrqJjF1UG0+zhIvj8zqMKznEmDYrJJEyGER376JAZF1BKBZY7dM2TNcTwReuZfXJdQsQxBA9AyJtboumki0iUD8rBiZ37E+D7n59zHHzVlok7k3AUyXqWoL2oJBVrDA91/vMfwuENO/cMOOlNeU7nvLI4/7GvQ21recwp0ka/8k9cV209vPQ4+JVEqfiFbOaufBd4Wb8EFQOtkh+zch9jmwJsBzk2JAShhjcAqD91xCqa5lX3I5kQdid4ijfX+3D2W4nIXJ/Fd9nmqCVv5P2PBKbh6TYx6/fruerjSojizxOHif/AJcZQOyDEgAA" + } + }, + { + "ID": "96f5eb51a308363e", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0011/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6b9441c00968fa1ab2f3891c558bf9b7/17424116903009339083;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0011/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Etag": [ + "lOPs8IL8R9aclyR5KoG6bA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4335,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybra5-v6:9838,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/4,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jx\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"jc\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e1c2::]:4062 had response of OK with response time of 0.013840198516845703 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybra5-v6:9838,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybra5-v6:9838,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqcnxDyi28PSxCLJMTM6pDDL1znc3S3K0tYWoKskvScwJyi8vBik1hogVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVod3OqikQRQpRFGGSihCNci8WKJsgTVWDSnY7cZZndJUWmqEppcLQo/lgubDIwFkYU6jujAS6Ze4BmNuMBLol7gGQ5g4KUl5hRTI/S4QKxaLgBfaON0KQQAAA==" + } + }, + { + "ID": "eb32391ca7a273e3", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0011?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "eb04a78eb8935b19c751ab7a14df7269/7360686452782574047;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0011?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Etag": [ + "Co7wlv25klV3moyhBD8BlA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnds123:4337,/bns/yk/borg/yk/bns/apiserving/canary_hightraffic_api_frontend.server/1,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=K608W8m9N4L8qgX936ToBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/canary_hightraffic_api_frontend.server/1,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/canary_hightraffic_api_frontend.server/1" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYNgwrn5KWDhINcAV8cQVxfchhWlJqObFeTq7B/kAhNFdT5MP5IJSfn5OVDFSIY4+fv7uDr6KUHEa8FULBeUCWTUctVyAQDcRh1+LAEAAA==" + } + }, + { + "ID": "ce5aa57a09971cd4", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0011?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "17518becbcc261b56fea321e9903d1e1/5824682610829716744;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0011?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnab123:4382,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=LK08W5C5B8aqqQWRtJWYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "7a00094f8d06f151", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "323" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b4e460d6e9ad88fb08f7e593fc8a9625/4216621174838932017;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoiZCIsInR5cGUiOiJEQVRFIn0seyJuYW1lIjoidCIsInR5cGUiOiJUSU1FIn0seyJuYW1lIjoiZHQiLCJ0eXBlIjoiREFURVRJTUUifSx7Im5hbWUiOiJ0cyIsInR5cGUiOiJUSU1FU1RBTVAifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxMiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Etag": [ + "J14GU+9/Hn301VlkIlNdYQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnkt1:4240,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/65,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=LK08W4jrE5HHqAX2ypcY" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/65,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/65" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXU/CMBR9369Y6qNK98VnwgNGohgwfgwTYwyp22VUtnWuRSSE/27bDWNwkvVhSc89555z79KtYaIlTUPUM9EbjT5WkG9OBHmLAZ3JEggSqdKN7V1NT7v4OnUt+ylejuLb8Pm+39ckqtXhKg5AnGcsF+ftltMLiSAcxMyx7I7VtryZZ7W6TrdjN72207ZnljwN7XSEYjvagUM8H9N0qXwWQmS8h/F6vW5EjEUxkIzyRsASvB8Afzo4y9k7BILjg1y4zMVxnYBYB+S4Zk5Ne4A55JAGINNuDdNEZZRR1ZqUTIJFlJJRI1ch03aFqEZAw9zpXQYLSMg+25xCHHJ5e5E3DUkwJQnoJNpGGW0yDVwO/CFS0O6sgi4O6f5ocoQe/uGr9sc1gld5PPqDyV0hkp/XctB0lVxsBKjhkF6YQsYsjXzIKyoPbP0LCHIggrLUp4Wx3XTlYl3b63hNt3gaXxnNqzitrlv+IRQTLiYspHLL4f+dfkYZXIyHhY4FurNCp4/I2Bnf/4phwagDAAA=" + } + }, + { + "ID": "fc8119d943f49926", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012/insertAll?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "164" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "18a3c43b95e58cd17bcabcd43a20585c/2608559738848212825;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012/insertAll?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyb3dzIjpbeyJpbnNlcnRJZCI6IllmMzM1OXJrY3U2c1gyb01DM2tReTZBZDRLTyIsImpzb24iOnsiZCI6IjIwMTYtMDMtMjAiLCJkdCI6IjIwMTYtMDMtMjAgMTI6MzA6MDAuMDAwMDA2IiwidCI6IjEyOjMwOjAwLjAwMDAwNiIsInRzIjoiMjAxNi0wMy0yMFQxNTowNDowNVoifX1dfQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/213.esf,ybcx185-v6:9811,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/84,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-streaming-frontend-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-streaming-frontend-thermostat\" pick_cluster_params { allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:d889::]:4393 had response of OK with response time of 0.013442754745483398 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybcx185-v6:9811,/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/213.esf,ybcx185-v6:9811,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-streaming-frontend-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/213" + ], + "X-Google-Service": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0zCtOLSpxzMkJSi0uyAdylLhquQAsgTGpNAAAAA==" + } + }, + { + "ID": "7c4002c734f944de", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "93adff824de3a080026cfec6d0b1d005/11063929856873945454;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:09 GMT" + ], + "Etag": [ + "LSxhB5GuEJiKIGQ/p+tM1g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4143,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48.esf,ybid10-v6:9812,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/128,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"wo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:12d6::]:4012 had response of OK with response time of 0.01437687873840332 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybid10-v6:9812,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48.esf,ybid10-v6:9812,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfEJrshwMnUvdfXK9PZ0D9Qv0C7xNUy3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBkpwuVodPBoMjayMDawMDPQMQMCMSF0Ia0LIM8DQxNTCxMLMyMRUD9mlUFYsF4wXy1XLBQAlkoTQbQEAAA==" + } + }, + { + "ID": "0ebed818fa6da984", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "979322c3f0d0b6107f43237868f63d24/1072555901173480578;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Etag": [ + "J14GU+9/Hn301VlkIlNdYQ==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:08 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnw69:4198,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/54,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=LK08W5nxK5DZqAW9lInwAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/54,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/54" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUopJSipAMRKKksAAu4OIa4KoGEanWwKC9BVx7i6YtHeQqGepDx+PWUFGOzIzjE0TcAoglIxHIBqVouAMtZJH3sAAAA" + } + }, + { + "ID": "8e0b822b0148ae68", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "334" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "dcac8e0d5cdeda89988edcea1bd90266/17910957063932313771;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiSU5TRVJUIGRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMC50YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDEyIChkLCB0LCBkdCwgdHMpIFZBTFVFUyAoJzIwMTYtMDMtMjAnLCAnMTI6MzA6MDAuMDAwMDA2JywgJzIwMTYtMDMtMjAgMTI6MzA6MDAuMDAwMDA2JywgJzIwMTYtMDMtMjAgMTU6MDQ6MDUnKSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiMTV4cGFyNW4yT3NBM2pGTFdPSnF6MWFQb2V3IiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:09 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/z4PTQoHHqBVKRnDXet0Kxq9_jHc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnax142:4112,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=La08W6i3BcOUqwXjz4WoBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ta2/aMBT9zq+wvA+0EhDnQUKQpq1bWUuLaMejq6ZKyJhLMIQ4iZ1SWvHf5ySwdg+1jZQPzrnnnnuOb54qCK94NMNthKc8SDJItx+WYoprGgBFgxy4w5618k4fYQud0JGLcHE2j36eTsabW+IYj8716Ls4P0++3FwOotNbUOTyIfEny3N2h4s+vGg/y0IGqh6LVNU912qPhw2z+RDTtBlZV/LEXn7r/bi6SB5Nei1gUxAlhPMej1Y5faFULNuGsdlsGoEQQQg05rLBxNo4DG7cW0aciiUwJY2/5AxtShqvCH4KBaOKi+jjeFiIa8IA5pBCxEAP8FRBCO+7d/9nKCcVrBJ9wxvCB728WEtW0C5XZSKa8yBLD1AhW7jbH55PuNsfdgYjNKOKSlATi5gt4hFn4hDXt/yW2XQ8yzMnRD8NRachvFJiWuhoVkOqhmb6VfIY3Zz0xp0hOqpqklsndt0i1RqqmlbbJm3dMW9L3PzTcwF6A222idMmzepxEYGOEKTiUeF1lA948PiOpHN2aXxf844UDsQijJL2di445+zKeeOUi5SrffqjzuDk66h709m7yST0IKBsO0xCXTGnoQQN7PZXKxVVmTzcaX7K/eLBuN/v9s/wiyquU2G/K1kKZUJ8DeVm2XpW23R802+Vu6RJqfoXd1ynxP9coEJ6DZEabWN4XiT8YlZtJZ3AmvLcCLZd1/Z9W7ds1qdmSxFTrJVPLc9fMTtgAU8WySpsTcV0kX2ewT2EIoa0Eegm95wBZUxkkcr/VVzZVX4BxlBDV3QEAAA=" + } + }, + { + "ID": "797ec66df01adb87", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/15xpar5n2OsA3jFLWOJqz1aPoew?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8ce2368761d24bc0fdd0f63865306562/16302895627941529044;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/15xpar5n2OsA3jFLWOJqz1aPoew?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:11 GMT" + ], + "Etag": [ + "BI0y6V17865YvVW4VV+0+w==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4125,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/27.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/27.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/27" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41R30/CMBB+31+x1EcwARQwJjwM0YiRgIAjxvhQuhsOu3a0N3ES/nfbMvAFEp6u993349LbeL5PvhIRkVufzJPFKgdVXCwAX+xjDDrnqE3JpNBAqpYNSBeW3e3XilZYb9+0mm/f4ew6DCu1yrrT2bE0+4SUGt7GdKaPE+CRNv276/0SdzNBU7COkZOWIBaZA3vB9J6U8LZ6WozHxNP+4CxxdFRto891QH0qfzINBqODhasfXmlGlnI+hhgUCAb/n5UpuQSGfXeVKOcM8DKTCi/brUYZY5W7eb35k1HVFI2hDq6WD8+z4dPqt05HEtZ7LpeMYiKFpb9OyCEdJVLeLRD0SEkGWoNzrJH9bncyzTigXQ1VDg5m1Jz2MUGDxZTrHSjytJfyII7N2hCN5Vq71Yi39f4A17Fmg2MCAAA=" + } + }, + { + "ID": "2a521f18885565e6", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/15xpar5n2OsA3jFLWOJqz1aPoew?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8ce2368761d24bc0fdd0f63865306562/14766891785971895037;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/15xpar5n2OsA3jFLWOJqz1aPoew?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:11 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Mbvsw20q-LE4usUsP6VqrLqEOmU\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:11 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneg123:4118,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=L608W5ngB4fVqwXB_qHIDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAMVYW2/bNhh9968QhD4mA0mREmVgD2njYQHmOrBd9GEoDEWiU3Wy5El0s6DIf99HXamrjaJtBNiw+N0OP/Eciv42M8xMevKUmXPj28wo7gTcmLer9wtzZrxclS5hJkO/dvNTAUNJvA0PuTdmFnKQhamLXW5elZlS2bdTmxZ2EQddK0OOXUbLRHrR22cpsvs08UWWiUB5osL670mkzyWW8u4+8mIY+VuNFONgib2iwAahuXEXH08yj1emUMtnVHCXWRusg3ntAHh7ZhfX5icvlGvVk5uvj+CFfkMYEUQRx7ZFbAixdc9lVriZpJ9g6f13SYLCTUsAjyRoI9AtdUXUD6grtgPKCk2AnxyghaJdpI0Rk45zXZcOpqlLn01TomnSPKVhB4vFRhqmPGsgtjuQosJxJkUJQkuRfT7t95FYnSTAzBescrDIhMPmGEaRCLoPw0/SIFtD5wcNHwGAFHG+AGvb0Us9SBXlSztr21TjIiFFcD/hVJPffLda3v+12C40kzhmNaNqToHlnzAOqpAPTYSKOT10w2D0DTbmvxvY5hw1vjBM8mEyt9AcwQpQl91ysJQDQdi+RtY1QZO+NE9GGafcJpQVLuoyK6dP5Y+Xq9EZfVzfXTSfK+MNgY8FH6qj2K6M3Q56+igQ2iX5E++Xn2m3JZa+XuG58S7xIpH5oiNZeFqyOHOmJMtlrMmnFsRGodUnaFYd+zQqbYQrhhJKOUggc/GwtGmVetpGACamzfegtDH7tbUNF1rUzHRM3KxpcbPas+2nKeE4U+KmiSMAckfEjUyJG9cScJcPi5vDfra44e8UN4YuUTeGfoy8rRc3t2e14I/1avmjOE/mRtHIDuPJNOPhGmc8g42Vn2M8PsN43LCnxXDs2mMU74WUi0sPeSVS28M86lIan+H0uTTVhK0JTruYW7ZjUdu1uOMwNsxp3OybA6Q+k6OCoeUYZvWU/WeS+le+sVxE6fHtvUV2PEr2X/J+QS7TGvVd0NoUcII7wOkuGDtWwakLDmRRGIveQUpE3hF8S21BqFnU+UFtEyWlLFk2b+TqCHIUxo8f4lBmfWLmj7q2sSbM82X4VWhRQ7LZRsQoeQVEWnPzorBuZaiOxSPdnTrT6ua3XbaZDzACuLehSJW4agH6RGm5c5u+538Wf4YShvdelIliNBV7kYrYF8HWe4i0LaDq6TFNvghf3uWlg1PkC3l9TFJ57WivFYEnvUxUTsXNDl7SOew3dEeR7RKXwwNxiIN3+Qt4/WBU1SIu/zkRhUm/xfHpcHuIbvZ7gCiCdfKkK0Pxx8VBxHL7fMz307v3m8V6q9JAkpfZy+x/fmHK5O0QAAA=" + } + }, + { + "ID": "1aaee5c28e402afa", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "906101190873a160033c4a3feead77fa/4703460236267056145;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:12 GMT" + ], + "Etag": [ + "b/pF+vpEL53Tr92BziwJUQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4092,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31.esf,ybcu11-v6:9864,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/69,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"iw\" allowed_cluster: \"wa\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:5483::]:4465 had response of OK with response time of 0.014473915100097656 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybcu11-v6:9864,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31.esf,ybcu11-v6:9864,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHq9EvcNMuK3D1MTUOKbI0cqrKLPcKDbS1hagqyS9JzAnKLy8GKTWAiBVBuNFAtoJCNZgEiqbBhZCFwVJlIM1GBoZmugbGukYGSnC5Wh08GgyNrIwNrAwM9AxAwIxIXQhrQsgzwNDE1MLEwszIxFQP2aVQViwXkgEj0+9cIFYtFwA9s4iYaQIAAA==" + } + }, + { + "ID": "68f5941751b0224b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "06f56a512ffdbde28c0a0b8e833f7946/13158830349981175589;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:11 GMT" + ], + "Etag": [ + "J14GU+9/Hn301VlkIlNdYQ==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:11 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703395000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneb206:4483,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=L608W9HZEc_mqgWKjIewAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUopJSipAMRKKksAAu4OIa4KoGEanWwKC9BVx7i6YtHeQqGepDx+PWUFGOzIzjE0TcAoglIxHIBqVouAMtZJH3sAAAA" + } + }, + { + "ID": "e1fdf7380b3c6cd4", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e3223587acfc4c1831ef1f81725afb5b/11550768918285292622;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0012?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:12 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703393000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndf4:4033,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/151,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=MK08W7nnD8PZqgWa6peIAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/151,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/151" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "68c8ddd198f23b3e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "155" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ec24c62212bf9dfb7dd212c3e7606d84/10014765076332435575;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIDEiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6InI3bjRCeHF5azJOZjVVWkFvdnNPR1g4QU41dCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:12 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/CcpH4fpxDHAI2zAjlvmNsCxxE6k\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnm125:4095,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/91,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=MK08W7KQH4vSqgWbubnQCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/91,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/91" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T2W7bMBB891cI7Gtj3VeAonVtNRFgqICPNggCGBS9lBnLoixSPhL430vJVpM2RdtHYmZ2dnbA556G1qxYomsNpSzb1lAd3z3yFL1XAEicNcAD8q21P3qCI0S5I1b56oYW96PFfH9nOPqQlLcOLQ+j20FsPQ0e890mEcPDIfLWD6idw9rxyzonIK9KXskr37Ou59N+5RfO58P2uLYS6s7vB3wnvt7cBYPEla1QQE7HrFg38pWUpbjW9f1+3884z3LAJRN9wjd6t7i+s/Sy4o9ApNB/s9NVKKH/xfBjzgmWjBcf5tPWXAkmQKGCgoBa4LmnaegyPf5ToEbUqs7oP7JpqPNryMqyp50aV8ILyrK66qDWtk13eby80DQaR8OZZrbz1D4gJCta4Qynebf0/6zdqLHEAi6cBQloYKRGmJpBSg2LAnV9h4DhGgG4hgPUWwa+5+FOLRvDsxYXvPBClxDfDEwDLx3iumlq+NaSeuBh0/dCg1IrtX2LokZ9Oq9PKsASRkyUXLDuMMNJNJhFi/jLIomiUTS6RN1X7A31+yRWzNlkngyV5EIsK8YVtz1XnMyiyWA4i791aC1gDBkmx+k2VwyKcwEKOF26EBLLWnQlNK/mpkg5JHFyg16xmLo8+clskzQtsE0rMF3b8A3bdC3f8s7lK1El3+JBEJ7xXxtvrTdQyNmxhJfm0atdVZRqARvMmiDI9jw7DG3bdNwrVaE0TL6RIbb8cE3sjGRsu9qu8yDl6ar+tIQd5LyEqp+pITtGABPC60I2nwv1Tr0fpEWUrCUEAAA=" + } + }, + { + "ID": "f8feafb1dda850f8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/r7n4Bxqyk2Nf5UZAovsOGX8AN5t?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "277501e4a1f1395dbd931b76c9927c0a/8406703640341650848;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/r7n4Bxqyk2Nf5UZAovsOGX8AN5t?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:13 GMT" + ], + "Etag": [ + "1MEXWV/4Hw8yzKxockHE9Q==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4107,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/88.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/88.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/88" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QbU/CMBD+zq9Y6leJSEDQhA9gFiDilOmUaIwZ3Q3HXm60N2GS/XfbMsQvvT4v13t6+4ZlsTjKAnZjsWW02hQgyrMV0FxfXJBFQlKVHDMJ7Fy7gfyVdl/e24vXl4vOZNsvf+52yOOJfT0fDA4uyb8g9ZVvr5DCYQRJIBV+N9iqeaNlfgr6xbD1aZprmsrc0FPn2R7b7n8pxcBIjjebDUczm9VSZeqHOiuTYo1LF0IQkHE4ZckFroHT1Hw6KBIO1MxRULN31a6n6M6DLnpZZ7TblHHbCbve2xC/5cN40R86XTp6E+Q+RZhpu/fE/qYTkp+4uJVmXezEjUoC+SiQg5RgprTYMe8tpnkCpOOSKMDQ3FfbnERUc42q8QtgU59cuAEAAA==" + } + }, + { + "ID": "98f5252f303397d1", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon695cc71810ad4c55bb072df6e6a17690ff2b372f/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "277501e4a1f1395dbd931b76c9927c0a/16789735788864503732;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon695cc71810ad4c55bb072df6e6a17690ff2b372f/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:13 GMT" + ], + "Etag": [ + "vL1Yeyg2XmdGPwLaQVKTGw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4080,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/1.esf,ybes23-v6:9831,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/31,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:70f::]:4005 had response of OK with response time of 0.013710260391235352 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybes23-v6:9831,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/1.esf,ybes23-v6:9831,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/1" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqSnzMYxMrUw3ishNcQ8o90kMDPMOcS+3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZRDNcKFaKCuWC8aL5arlAgBxXpLYugAAAA==" + } + }, + { + "ID": "86d416e8e7432d63", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "157" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3c8afd2e3b118f26dddbb8439a5c6dc9/15253730847400019165;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIDEuMyIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiUFl1UGtZeXBORDZBb1FvMjN1b3NDWVhCcjhVIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:13 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/CHbCOQiiJggkr8iSwzJcsanOVaM\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnf186:4399,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ma08W6LmEpXaqgWP8K_QAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T207bQBR8z1dY29cS3y9BqlrquDQIAiQOhQop2t0cO4sdr+NdEwWUf+/aiQstVdsnazUzZ2bOkZ97GspYsUDHGiIsXddQbd89cILeKwAkThvgHvlW5g+fYAtR7ohlvjxNiu/D+Wxzazh6+JWEl9eMnaVpVgVsunk6owIXlzf44h61c1g7flHnFORRySt55HvW8Wzav7qrr7K7bTkeeif8mlt2zUV4d/u5CmatUECenLMia+RLKUtxrOubzaafcp7mgEsm+pSv9C64/mjpZcUfgEqh/2anq1JC/4vhx5xTLBkvPsymrbkSTCCBCgoKKsBzT9PQYfroT4UaUavao//opqHOryEry562a1wpLxKW1lUHtbZtu8Pj5YWm0XkUxprZt9uJKhEIyYpWGmOSd7H/J3ijxhILOHDmNEgCgxgDYgYkMawEEtd3KBiuEYBrOJB4i8D3PNypZWO41+KCFyZ1qUGTgGAzWRBKHDMA23MN1ycDy8YGWShnz0lQo97t49MKsIQhEyUXrFtNOIlO4mg++jIfR9EwGh6qbir2hvptMlLMeDIbh0pyIJYV44rbLmw0jqPJSRiPbjq0FnAOKabb6TpXjATnAhSwO1xDSCxr0Z2heTU7RcphPBqfolcspjZPfzLbJs0V2KoVmK5t+IatPo7t7M+vRJV8i7u+scd/vXlrvYJCxtsSXm6PXmVVVao5rDBriiDb8+zBwLZNxz1SJ5SGyVdygC1/kFE7pSlbL9dZHhBOlvWnBTxCzkuo+qka8sgoYEp5Xcjm90K9Xe8HcT+QZycEAAA=" + } + }, + { + "ID": "f97212e2bf382c8a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/PYuPkYypND6AoQo23uosCYXBr8U?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "02bbe9ece049f0d9ee71f94a4d1f362b/13645669411409234182;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/PYuPkYypND6AoQo23uosCYXBr8U?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:13 GMT" + ], + "Etag": [ + "Lr/3xOx+uV5A0upa4uroQg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4488,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/52.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/52.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/52" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QbU/CMBD+zq9Y6kclIigaEz5sqNFk4U1nJMaY0h1zsO1qe40shP9uW4b4pdfn5XrPddsKArbOq5TdBmyRZ98GVH2SAU3dZQbaFKRtkVhpYGfODcQz547VeW8z3pya16uwYyS/NAqn2WCwd2nxBSW3vq1FFi9zKFJt8bvHQcN7reIluBeXnU/f3NBUS08/xOPw5b9QYuqFURLHYRTfs0ba+fphz53PsMLFDJagoBJwTCIVrkDQk185NYUAaktU1L7ud5sprnOvT+Zmsp7XcnTXD3GK3Z5BPZy/ReomOXgLFJxyrJw9eWZ/0wmJFzP8cUuzC3bkoppATxQK0Br8lA475B1iKQsgF5eUAU8Lbv/yMaeGa+1avw0rNaG2AQAA" + } + }, + { + "ID": "0697ab099a345656", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon1c5c0cf8ba1fdbcb418e365057b923a0bd76264f/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "02bbe9ece049f0d9ee71f94a4d1f362b/3654576926407289626;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon1c5c0cf8ba1fdbcb418e365057b923a0bd76264f/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:14 GMT" + ], + "Etag": [ + "O+IrsIPi+GBkoDyNJkYpeA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4140,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/33.esf,ybkn3-v6:9842,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/104,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"wo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:ccc3::]:4126 had response of OK with response time of 0.013982534408569336 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybkn3-v6:9842,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/33.esf,ybkn3-v6:9842,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/33" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfHX9iwq9gzI1HZ3ys53qfTzyo4sSHW0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNesZKcMFaKCuWC8aL5arlAgBTRGY6vAAAAA==" + } + }, + { + "ID": "e1d7d457095b6a4c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "175" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b632c61449f14e09c5d4cf16e1d674ab/2046516594206257219;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIENBU1QoMS4zICBBUyBOVU1FUklDKSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiY1lTZnI0YVVnbUFRWWdveG1HY3ZlTHpwdUQ5IiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:14 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/uQsQ5FQX56Hd6cYkCXcpiqKELAc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnez28:4339,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/137,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Mq08W_CZB4jXqAWdypTYDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/137,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/137" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/TQBB8z6+wjheQaGzH35UQRI5bLEKkxA60qFJ0vqydaxyf4zsnpFX+O2cnpoUi4PE0Mzs7u3uPPQWtabFElwpKaLatoTq8umcJeisBEDhrgDvkDNbO6AEOEOQmX+Wr67T4NlrM9zeaqdZTPrWupjeW/XFpk9u1f0NKuv0UjIfkDrV1aFt+WecExEXJKnHh2IPLedQnt1FamXiebYbT24x931yTHYwfynrktUIOeTqmxbqRr4Qo+aWq7vf7fsZYlgMuKe8TtlG7xtXdQC0rdg9EcPU3O1WG4upfDN/njGBBWfFuHrXmUjCDFCooCMgGHnuKgs7Vwz8FakSt6oT+I5uCOr+GLC17yrFxJaxIaVZXHdTatunOj6cXioJx4MeKP4zi13rfUJRhpEzmn4NZ6L9pPWSPwAUt2mIxTvIuyP9EadRYYA5nzoK4qaslmpfobpJqgxRSyzEJaJbmgqWZkNpL17Ft3KlFY3jS4oIVOPHwAOuGYemOmySWa6dWqpmObXokBXMAS09zdddBjfp4ap9UgAWMKC8Zp92w/FkwjINFeLWYBMEoGJ2j7iv6gvp1FkpmPJtPfCk5E8uKMsltRxhO4mA29OPwS4fWHMaQYXKItrlkpDjnIIHjeT9cYFHzbjHNq5kpkg6TcHKNnrGonDz5yWyTNFugm1agW4bmaIZumQPXOB2EFFXiJW52p/XrFbTWGyhEfCjh6RrQs15llGoBG0ybIMiwbcPzDEM3rQu5QqHpbCPkShxvTYyMZHS72q5zN2HJqv6whB3krISqn8kiO0oAE8LqQjQfDvWOvR83Xu73OQQAAA==" + } + }, + { + "ID": "4febb66e14290efd", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/cYSfr4aUgmAQYgoxmGcveLzpuD9?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "51a9f98ef5fb0f9713dbf13f14295ba3/438455158215538027;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/cYSfr4aUgmAQYgoxmGcveLzpuD9?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:14 GMT" + ], + "Etag": [ + "PlcOBi5qZwODohJ0XMEEGQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4065,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/24.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/24.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/24" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+51cs9VUSNF6iCQ9cFsQMgZElojGmdGdj2O2M9kxAwn+3LUN86el3OT3f6b7heewrK2L26LFFlq4rULuLFGhqLyHoSpI2pcRCA7u0biCeWvdEinE3u12/bcZ9XD63Xke+P5i220eXFkvIufHtDTI4yUDG2uB3h72ad1rBc7AvJq1P11zTtCsd/RKN/HDY+y/lGNdSEHS6gc9q6eDqhzkPLsUKFyEkoKAQcM5SKlyBoKFbOq6kAGqWqKh5f3ddT7GdR13MZ4m64VGad6bzFLf5QHxD8FNW/YeTV6LglGFh7dGM/U0nJC5D3Ni12RU7c90dgZ4oFKA1uCktdsrbw7yUQDYuqQocLbj5zaeMaq5xaPwCRcr4FbgBAAA=" + } + }, + { + "ID": "870af47f4d30f35b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonab9a2a1335178bb586f5f047649cfe42ed908187/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "51a9f98ef5fb0f9713dbf13f14295ba3/8893543801264625536;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonab9a2a1335178bb586f5f047649cfe42ed908187/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:14 GMT" + ], + "Etag": [ + "MrZLiYvFhgqwHda83wj1bA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4194,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/41.esf,ybba187-v6:9866,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/93,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e492::]:4204 had response of OK with response time of 0.013524055480957031 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybba187-v6:9866,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/41.esf,ybba187-v6:9866,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/41" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfEtivLJjCxzy0gvLPdISbQwLs8yTHK0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNesZKcMFaKCuWC8aL5arlAgCB3gHOvAAAAA==" + } + }, + { + "ID": "88187513a4d17f20", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "168" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "29f85c1ac450714fffe97857c84aae2f/7285482365273840809;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIE5VTUVSSUMgJzAuMjUnIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJhc0tOd29EV2w2UUwwajhNVWg3anIzU0Zybm4iLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:15 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/LnVCyPD0g5yL5bL9_xA1jBKVZ84\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncp68:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Mq08W9n_O8SNqgXr0IDoBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ty27bMBC8+ysE9pBLY1FvKUDRprYSGFGM1o+kCAIYFLWSacuiLNJx3MD/XkqRmrQp2h4XM7Ozs0s+9TS0ZkWCzjQUs2y7g+rwbsVj9F4BIElWA/fIM9fe8DscIMxtscyXl2lxN1zM99+wrUfFzeDwZYgz5xA5cRQsHs+N1eermzvfvkdNH9a0T3Y5BXla8kqeeq55Np/2ibga7/nwNne/RnjlX8+X3qqyphdVUTRCAXkasWJdy5dSluJM1/f7fT/jPMuBlEz0Kd/o3eD6g6mXFV8BlUL/zU5XoYT+F8OPOadEMl58mE8bcyWYQAoVFBTUAE89TUNt99GfAtWiRvWM/iObhjq/mqwse9qxdqW8SFm2qzqosW3StcVLhaZhFA5m2nh+HU5GA+0E903npGmuhgMhWdF0mZE47xL8T4ZaTSQR0HIW1E99HOMgNvw4xWYKqePZFLCDfXCwDamb+J7rkk4ta8N2C4XK4yY0oeA7vhEbqUG9wIwNL6ExMS3bxLZpBEEMCaBafXwen1ZAJAyZKLlg3ZYGk/B8Fi5GF4txGA7DYRt1X7E31NvJSDFnk/l4oCQtsawYV9xmd6PxLJycD2ajmw7dCYggI/Qw3eaKkZJcgAKO7WGEJHInuovUVb1TpBzGo/ElesViavP0J7NJUl+BbRqB4VjYw5bhODhwnl+CElXyLW74Lf7r+RvrDRRydijh5RmgV7OqKNUCNoTVQZDlulYQWJZhO6fqhBIbfCMDYnrBmloZzdh2uV3nfszj5e5TAg+Q8xKqfqaaPDAKhFK+K2T901Dv2PsBKxZvSDIEAAA=" + } + }, + { + "ID": "446ff4bf11d48dae", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/asKNwoDWl6QL0j8MUh7jr3SFrnn?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "45822db850e45adcce2180e3c931d378/5749477423809421777;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/asKNwoDWl6QL0j8MUh7jr3SFrnn?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:15 GMT" + ], + "Etag": [ + "KRD/LW180LW1o4sWkKvdrA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4084,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/72.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/72.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/72" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QS2/CMAy+8yuq7Dq0sk2AJnHgNQ1R0ChCHKZpCqmBljTuEncIof73JaGMXeJ8D8efc24EATukKmEvAduku+8S9OluB7RwlxhMKcnYUqAywO6dG4jvnHsajx6idasb2gOfzfow/Ul0v9e7uIzYQ86t72yRxdsUZGIs/vA4qHmvKZ6De3EbfvnmmqZT4en5ajaOJ8P/Uo5JLUVRfxCNWS1Vvn7as/IpMtzEsAUNSsAtS6ExA0ETv3RSSgHULFBTs9N+rKe4zovOzXR+xNFathdRmHVnq30n00/LV63U1StRcEpROftqyf6mExKXMR7d2qzFbtzgRGDeNQowBvyUkF3zDjEvJJCLS7oETwtuf/MtpZprVI1fim4PO7gBAAA=" + } + }, + { + "ID": "5c049fdf7df82cd8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonf6dcdce8581b1f1c792b17dcba2342042199bede/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "45822db850e45adcce2180e3c931d378/14132791043013952486;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonf6dcdce8581b1f1c792b17dcba2342042199bede/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:15 GMT" + ], + "Etag": [ + "t4Ml6hL3KkzeegmQY67JQA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4092,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31.esf,ybpn14-v6:9870,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/71,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:5b47::]:4042 had response of OK with response time of 0.013962507247924805 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpn14-v6:9870,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31.esf,ybpn14-v6:9870,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqSkx8c0xy/Ax9s6uSk1Nzw2MNDP3CnS0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNBnpGpkpw0VooK5YLxovlquUCALpzzxS9AAAA" + } + }, + { + "ID": "f6b719e482d1650f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "158" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b201602488b1be08f0507c1db92a20f2/12596786105844369423;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFRSVUUiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6Im9UOG85T0V1OEMzNkF3SHhxd1ZiTkVhd3hYbyIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:16 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/gK1a62x0p8vuU7OUfO4dsWyNZpw\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnr135:4219,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=M608W9mvLND5qQWHgJDABA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T226bQBR891eg7Wtj7rdIVRvZNLUaEcnGSRRFspblgDfGLGYXEyfyv3fB0KRN1fZxNTNnzpyBl5GCNrRI0LmCYprtaqgOHx5ZjD5KAATOWuABucbGnT7DAYLc4ut8fZkW99PVsrnTLDX7rmPHeNJKb18v3etlem0l/PYQ3pfNA+rm0G58UucExFnJKnHmOsb5cjFmkcf866D2JqZz0Xx72jU3cRjg5umOdUIOeXpFi00rXwtR8nNVbZpmnDGW5YBLyseEbdVhcXVvqGXFHoEIrv5mp8pQXP2L4eecESwoKz4tF525FMwhhQoKAnKBl5GioH767E+BWlGnOqH/yKagwa8lS8uRcmxdCStSmtXVAHW2Xbr+8fpCi+AqmERKNF8G3Ui5EnBBi04b4Tgf9v6fzVs1FphDz1kRL/W0WPNj3YtTzUghtV2LgGZrHtiaBamTeK7j4EEtWsOTFhesMHXDMX3bBDPxE0hM7IMeO5YOtkssP/Us20mT1IhRqz6e1icVYAFTykvG6XCbyTy4iILV7OsqDIJpMO2jNhV9R72dzyRT3iOcSElPLCvKJLe72CyMgvnFJJrdDGjN4QoyTA6LXS4ZKc45SODY18EFFjUfemhf7U2RdAhn4SV6w6Ly8uQns0vStkC3nUC3Tc3VTN22fd079S9FlXiHO5rZf0m/lt5Zb6EQ0aGE1/LRm11llGoFW0zbIMh05Pl909Qt+0xWKDSdbYWPDdffEDMjGd2td5vci1m8rr8ksIeclVCNMzlkTwlgQlhdiPb/QqPj6AfA3aRgKAQAAA==" + } + }, + { + "ID": "57747d3601958261", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/oT8o9OEu8C36AwHxqwVbNEawxXo?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b67057b278adde471361de2c9b93c3cd/10988725769348500791;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/oT8o9OEu8C36AwHxqwVbNEawxXo?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:16 GMT" + ], + "Etag": [ + "bBM5dvP7kSW6RBXqAt7M/g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4020,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/106.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/106.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/106" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QbU/CMBD+zq9Y6leJqHGgCR82sgST8eIQJTHGlO6Yg2031ptjIfvvtmWIX3p9Xq73XI8dy2K7OAvZk8XWcbQvoaivIqAXfQlAlglJVXLMJLBr7QbikXG7k4fwZ97fLd7twF3tHepPbqLh8OSS4htSrnxHhRTexJCEUuEPg62WN1rGU9Avbnpfprmlqc4N7c5mvudM/0sphkaaLn3fcX2PtVJj6qc6G5Nii+sANlBAJuCSJS9wC4KezdJhmQigbo4Fdfv2XTtFd550fB3g48wrB6N726nGh331tp56vDqs8OxNUHCKMdP25YL9TSckngRY6bXZLbtwbk0g5wUKkBLMlB475x1hmidAOi4VJRhacPWb45hartN0fgFhVIJRuAEAAA==" + } + }, + { + "ID": "cbe2fbc688daadff", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon31263953e3d9ded3a9e1b641e57c49f8456fdf2b/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b67057b278adde471361de2c9b93c3cd/997350714169896780;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon31263953e3d9ded3a9e1b641e57c49f8456fdf2b/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:16 GMT" + ], + "Etag": [ + "552b6oqAxoLcF+5TiUtrkw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4193,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/28.esf,ybkq18-v6:9843,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/78,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:7c5::]:4143 had response of OK with response time of 0.014002084732055664 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybkq18-v6:9843,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/28.esf,ybkq18-v6:9843,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/28" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTE1NUoyyy90rMj3SXbTNg3JDC0pyi63tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNJUWlqUpw0VooK5YLxovlquUCAMp6cai9AAAA" + } + }, + { + "ID": "602fc7e4b25bb5e8", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "159" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "274a89dd246a15e675ada94eb239d048/17835752976406869108;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICdBQkMnIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJqTlgyOVVUcmdrdUhqQkdwRXBRQnJZMW00WUwiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:16 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vRE-Rob6aOWZXWPap3vP9ZoQMZ4\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnf186:4399,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=NK08W_HXHIHdqAWszJ3oDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T227aQBR85yvQ9iEvBd9vkao2ATdFIjQxpklQJLRezpoF4zX2GkQj/r1rx27Spmor8bKamTMz55inThdtWLpE510UsXhXQn58t+YRei8BEDiugEfk6Btn+B2O4CdmsUpWVzSdDxezw71qKvvA7wU8svHXu/n93Q3OjP2NN+e313PzEdVzWD1+WSYERC/jueg5tn4+m/bXk3vdm4V5vCm/rC+vMj+7vcwftK35MK6FBSR0zNJNJV8JkRXninI4HPox53ECOGNFn/Ct0gZX9rqS5XwNRBTKb3aKLFUofzH8mHCCBePph9m0NpeCACjkkBKQAZ463S5qpo/+VKgS1apn9B/duqj1q8jSstM9Va6Ep5TFZd5CtW3drnm8vNDUH/uDsHt2cTk4q2fKTFAIltbiEEdJG/x/oldqLHABDWdBXOqqkepFmhtRVadALcckoFqqC5ZqArWXrmPbuFWLyvBZi1OeqpFjG0tMiSd/4ERaZJuOgYFSXSNgLXXqudSKDFSpT8/xSQ5YwJAVGS9Yu5xB4F+E/mL0eTHx/aE/bKoecvaGeheMJDMMZpOBlDTELGdccuuVjSahH1wMwtG3Fi0LGEOMyXG6SySD4qQACZyaexQCi7JoD1G9qp0i6TAZTa7QKxaTmyc/mXWT6gpsWws0y1Ad1dAs21abT0WKcvEWd1r816vX1ltIRXjM4OX66FVWWSVfwBazqggybNvwPMPQTKsnTyhUjW+Fh3XH2xAjJjHbrXabxI14tCo/LWEPCc8g78dyyJ4RwITwMhXVHwx1Tp0f/xSYECkEAAA=" + } + }, + { + "ID": "f2992f5ba3a1c7db", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jNX29UTrgkuHjBGpEpQBrY1m4YL?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e8b3bdeb83a5416cf590d5add7d34ed0/16227691540416084381;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jNX29UTrgkuHjBGpEpQBrY1m4YL?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:16 GMT" + ], + "Etag": [ + "DbbHDWZUj7sudI9GDbstIg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4338,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/14.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/14.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/14" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q32uDMBB+718h2esKbRkrHfRhzqKCyGorWzfG0Hg6rZosORlS/N+XpHbdSy7fj8t9l9PEssixbDPyYJG0LL47EP1NAbjVlwhkV6NUhbNWArnVbsCk0G4nTT3n5S2ulrLL/JXrpBL9Yr0+uyT9giZRvpNCCucl1JlU+N1ga+SN1iYN6Bfz2adpHmnsuaF3+8gP3f9KwzKjhHEQPNrBhozSYOqHOgcTomJpBDkIaClco3DBKqDom52zrqaAU84ETpf3i3GK7jzrVfi6WMV7URw7r7JdvuFbWxzmzd0huHhrRhMsWavt8Y78TUeGSR2xH701mZMrZ/cI8lkwClKCmTIjl7xPrOE1oI6LogND00R9plfiyE2GyS8X0xWRtwEAAA==" + } + }, + { + "ID": "0a1a6760f7514168", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0b763dafc9fc9e7b1b6473aeff21ce5d2f98f5b3/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e8b3bdeb83a5416cf590d5add7d34ed0/6236599055414074290;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0b763dafc9fc9e7b1b6473aeff21ce5d2f98f5b3/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:17 GMT" + ], + "Etag": [ + "fqEWaGU8v48YdTHoiH8TLQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4236,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/10.esf,ybmg12-v6:9843,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/30,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jd\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:378c::]:4353 had response of OK with response time of 0.014425754547119141 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybmg12-v6:9843,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/10.esf,ybmg12-v6:9843,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/10" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqUkrdA1PdA+1KDOxiEwJ8cjP9LAI8Qm0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNjk7OSnDBWigrlgvGi+Wq5QIA1NiicLwAAAA=" + } + }, + { + "ID": "367534088bdb0f54", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "174" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "88001df1b5af1103b2a6fe1429fcab40/4628537619423355098;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIENBU1QoJ2ZvbycgQVMgQllURVMpIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJHdWpvVmFmUHV6V0RyM2xENHVmSnJCMmM0Z3oiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:17 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/eXDqw1XRiX4BZ6rdVt7ygxjrAwQ\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnaf190:4081,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Na08W8jKCYreqgXKwp3ICQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY14e2UoO/P4hUtQS7EVWEWjAJjSKh87FnDozPnM+hJOK/92xwkzZV28fTzOzs7O49djS0ZvkCnWsoYem2ArF/teIJeqcAkDitgTvkW2s/fIA9RJlTLrPlJc1vw/l0NzMcHWbhdmfOxmzmXNx6YnEt/X36fSX6u693qKnDmvKLKiMgzwou5JnvWefTSfeyWvFrTL9UDzehsLPQqehncWERJ31ohCVk9Irl61q+lLIoz3V9t9t1U87TDHDByi7hG71tXL+39ELwFRBZ6r/Z6SpUqf/F8EPGCZaM5++nk8ZcCcZAQUBOQDXw2NE0dKo+/FOgWtSojug/smmo9avJyrKjHWpXwnPK0kq0UGPbpDs9nl5oEl1Fg1gb9Cfxm9eU89daf6JdfIujydvGQrUIpWR5UyvGSdbm+J8ktRpLXMKJMycBDYzE6CVmkFDDokBd3yFguEYAruEA9RaB73m4Vcva8KjFuUpFgoWlKri9xHMtMHzHCUxqBdiniwRbVuITi/bARLX6cGyfCMASQlYWvGTtrAbjqB9H8+Gn+SiKwig8Rd0J9oJ6Mx4qZjyejgZKciIWgnHFbSY4HMXRuD+Ih9ctWpVwBSkm+8k2UwyKsxIUcDitp5RYVmW7l/pVzxQph9FwdImesZiaPPnJbJLUW2CbRmC6tuEbtun6VmAc70GJhHyJ24F5xH89gsZ6A7mM9wU8HQN61quKIuawwawOgmzPs3s92zYd90ytUBom38getvzemtgpSdl2uV1nQcKTZfVxAfeQ8QJEN1VF7hkBTAivcln/N9Q5dH4AcKs72zgEAAA=" + } + }, + { + "ID": "ae65ef5938fb28e0", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/GujoVafPuzWDr3lD4ufJrB2c4gz?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bee7b4470a715a7caf22694adccfc379/3092532682253772035;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/GujoVafPuzWDr3lD4ufJrB2c4gz?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:17 GMT" + ], + "Etag": [ + "FZfNpm7z7ZOfQVw38F7Ykg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4069,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/2.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/2.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/2" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+51cs9VUSBOKMCQ9OwEsIchEMGGNKdzoH3Trb0xAg+++0ZYgvPf0up+c7PdSCgGzSPCb3AVmlya8BtbtKAMfuMgFtBGpbCplrINfODUgT5+4v+bDIwn24fOPj+bZ11w8Xm6TTObk0+4GMWt/BIot5CiLWFn96HFS813KagXuRN759c0XjrvB0tHjvTf8LmYy9MJwNBg/RoEcqqfT1y56lz7CWqwlwUJAzuCQplFwDwxe/cmwEA6wXUmE9vG1WU1znSX8yazmnfGT2H13VEt224a8qarJ2sj97hWQUU5k7+2xK/qajRComcuuWJjfkwkU7BD1SkoHW4Kc0yDnvo8wKAejiojLgaUbtXz6nWHG1snYEHeymG7YBAAA=" + } + }, + { + "ID": "551726665c426572", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonfc8d2f8059b652e074481f28a7fdba22b7c2f9e1/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bee7b4470a715a7caf22694adccfc379/11475564830776559384;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonfc8d2f8059b652e074481f28a7fdba22b7c2f9e1/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:17 GMT" + ], + "Etag": [ + "nYE7y6mF77ps4xVNa1V22w==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4131,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/2.esf,ybcf2-v6:9847,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/109,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1101::]:4358 had response of OK with response time of 0.014118432998657227 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybcf2-v6:9847,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/2.esf,ybcf2-v6:9847,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/2" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqcmLdDWvNMt1MzcvKDapCPNLNAwzMiq3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNUbmWZUpw0VooK5YLxovlquUCAFeJ1/a9AAAA" + } + }, + { + "ID": "9365700910e83c73", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "185" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c0df7abe73758b626a9366118e877067/9939559889312140352;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFRJTUVTVEFNUCAnMjAxNi0wMy0yMCAxNTowNDowNSciLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlVmVGNtdzNYM01ObjFNVlY4aU1TSXFyNGZtRCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:18 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/G800zjnSWLxU3cDT0omz6r8E5aQ\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnd8:4477,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Na08W_m8MpTrqgWAr42YDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY14e8FHz2+Qukqo3AjSwF1GKTRFUkdD7W5oLtM/YRSiL+e88GN2lTtX08zczOzu7ec09DG16s0EhDMU+3O6gO7x5EjN4rACRNG+AeuebGnTzBAfzMqtfZ+iopvk2Wi/0dtvQrD+OnhyK8vf6+IGwSYZE/OZXn2/TrPWrr8Lb8apcxkP1SVLLvOuZoEQ4WScTyPbkj01lhTG9uPD4Ng21lJfmkFdaQJde82DTytZRlPdL1/X4/SIVIM6AlrwdM5HrXuP5o6mUlHoDJWv/NTlehav0vhh8zwajkoviwCFtzJZhDAhUUDFQDzz1NQ+fqwZ8CNaJWdUL/kU1DnV9LDlFPOzauTBQJT3dVB7W2bbrz4+WFQv/aH0daFEz9MLqcftEuTGw4fUz6JtYMe4StEbYvWjfVLdSSF23ZiMZZF+l/QjVqKmkNZ86SeYmHYzyMDS9OsJlAYrsWA2xjD2xsQeKsPNdxaKeWjeFJSwtRrFaMOEMA0yY0AY8x2zIwthzHTejKJThmjMSG5aBGfTy1zyqgEia8LkXNu7GN5/5l5C+Dz8uZ70/8yTnqvuJvqLfzQDGj+WI2VpIzsay4UNx2mMEs8ueX4yi46dBdDdeQUnYIt5liJDSrQQHH86ZqSeWu7lbUvJqZIuUwC2ZX6BWLq8mzn8w2SbMFnrcCwybYxcSw3aHlnk5DiSr5Bvdwd2S/3kNrnUMho0MJL3eBXvWqolRLyClvgiDiOGQ4JMSw7L5aocSGyOWQmu5ww0jKUr5dbzeZF4t4vfu0gkfIRAnVIFVFHjkDypjYFbL5eqh37P0AdsC92EMEAAA=" + } + }, + { + "ID": "335dfc702c5bb998", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/UfTcmw3X3MNn1MVV8iMSIqr4fmD?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a4b2052fd873b4e3065b618c5252b090/8331499552832983145;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/UfTcmw3X3MNn1MVV8iMSIqr4fmD?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:18 GMT" + ], + "Etag": [ + "U5qcVo3g3ASZY3siix7b3Q==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4296,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/78.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/78.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/78" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+369Y6qsk4FSMCQ9DSSRhBHaLlxgzurNZ3NatPQsuhP9OW4b40tPvcnq+071l2+SHVSl5tMmG5U0LorvKAdf64oNsC5Sq1LySQK61GzDJtTu6a2jMndxxg/c3RzL2O94468nk5JL0G8pE+fYKKZwxKFKp8IfBds8brUpK0C9mwy/T3NPY1YYO594sCF1v9V8seWrEZbRYuNPFjPTSwdRPdR5Mji3f+JCBgIrCJU0t+BYozs3aaVtQwEHNBQ7G9zf9FN150qMspOXOeXW8ZTXy4viBecG8EbdZ+Xz2FpwmyHhl7AH5m44ck8LnO704GZELN+0Q5EpwClKCmTIk57xPvKwLQB0XRQuGpon6zxeGPWcdrCMtnvlpugEAAA==" + } + }, + { + "ID": "c9ab317839ecc4b8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonddc369ee253afe8cc541004667fad730bcc3b146/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a4b2052fd873b4e3065b618c5252b090/16786868567052252029;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonddc369ee253afe8cc541004667fad730bcc3b146/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:18 GMT" + ], + "Etag": [ + "u1BQpXI6/OxVGg5vsVa50g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4246,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybki18-v6:9816,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/40,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f10d::]:4158 had response of OK with response time of 0.013144731521606445 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybki18-v6:9816,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybki18-v6:9816,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqSk1dAosiPA00/evCHNPNy0rDks0NUi3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNJqYWJhZmRiamegZKcNlaKCuWC8aL5arlAgCKOAmwxQAAAA==" + } + }, + { + "ID": "f6bd7e824144f346", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "220" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4c089be878a080dea94941e04b38a30f/15178808230556317862;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFtUSU1FU1RBTVAgJzIwMTYtMDMtMjAgMTU6MDQ6MDUnLCBUSU1FU1RBTVAgJzIwMTYtMDMtMjAgMTU6MDQ6MDUnXSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiZHVsdVk5eGVQMzNJa0xYVHk1RGtwYWZsZG9GIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:18 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/E5Ed5D30jzKH6BISxiXEZdAOMGA\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnb21:4037,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/103,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Nq08W4jHH5HfqAX2jqjIAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/103,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/103" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXXOaQBR991cw9CEvURaQL2c6rRWSMjU2o9imbTrOslxwI7IIS4zJ+N+7oCRp06/H3XPOPffcu/vQkeQVzSJ5IMkhTTYVFLtXNyyUTwUAHCc1cC1b2spy72EHXtovl+nyPM6+uov59gr1Fc/wIsPV0c39h/fmO392R6+8r9Hw48X58Fpu6tCmfFSlBHg3ZwXvWqY2mM964qr64tzBpa77q/FVsDPcVY7jNGJnjbCENB7TbFXLl5zn5UBRttttL2EsSQHntOwRtlbaxpVbTckLdgOEl8ovdooI1Vz+yfBNygjmlGWv57PGXAimEEMBGQHRwENHkuRjdf93gWpRo3pE/5ZNklu/miwsO9K+diUsi2lSFS3U2Dbpjoenkzzzxt4okL4F/oU3C4YXl9KJhlSzi/SuhiTVGKD+ABknp9I/CN+bhkTLUHKaNc4BDtM29f/krtWY4xKOnAWxYxuFyAlVO4yRFkNsWH0CyEA2GKgPsRnZlmniVs1rw4MWZywjjuFgB5uObuC+aekRiXRHDW0cItPR4kgNgegxCeVavT+0TwrAHFxa5qyk7WRHU28YeAv/bDHxPNdzj1G3BX1B/Tz1BTOYzicjITkS84IywW3m7U8CbzocBf6nFq1KGEOCyW62SQUjxmkJAtgfl1lyzKuy3WJ9qmcqC4eJPzmXn7GomDx5ZDZJ6i3QdSNQDR1ZSFcN2zTtw+sRooK/xC3TOeA/P5nGeg0ZD3Y5PD0d+VmvIkqxgDWmdRBZN03dcXRd7RtdsUKOVLbmDtYsZ0X0hCR0s9ysUjtk4bJ6G8EtpCyHopeIIreUACaEVRmvf6fc2Xd+APH1fQNmBAAA" + } + }, + { + "ID": "5358610d32bb9884", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/duluY9xeP33IkLXTy5DkpafldoF?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7c5ed240b1ee606c8491a8798aa7eec7/13570746794582310095;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/duluY9xeP33IkLXTy5DkpafldoF?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:19 GMT" + ], + "Etag": [ + "Mq02X8JGlUe/4APoov9lkA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4406,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/26.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/26.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/26" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QbU/CMBD+zq9Y6leJCL5hwocpUzGSzDESjDGmdDcc63ZjvaEL2X+3rQP80uvzcr3nuus4DkuTPGK3Dlsmq00FZX2yAno1lwBUJUnpUmCugJ0aNxBfGfd00+svbp4f5RzOLlwfcTuUqTsa/bmU+IKMa99OI43jBGSkNH632Gl5q+U8A/Ni3Pu0zS1NdWHpcDL1ZqE79f+LGUZWDDzfc0NvzFqpsfVDn43NscZlADGUkAs4pilKXIOgiV07qqQA6hZYUvf6qt9OMZ0HvXob/oA/GEzSl0VYX47Tgscywoe9V6LglGBu7PMZO0wnJC4D/DaLs3N25O5qAuWXKEApsFN6bJ/3HrNCApm4VFZgacH1fz4l1HKdpvMLmiX62roBAAA=" + } + }, + { + "ID": "adad667a07c75387", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc959a9a6935a4673dcd391b8ab0692fd1bec3fcb/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7c5ed240b1ee606c8491a8798aa7eec7/3579372843176746979;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc959a9a6935a4673dcd391b8ab0692fd1bec3fcb/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:19 GMT" + ], + "Etag": [ + "HD6g9rBjDHPikenVQxdQRA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4328,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/46.esf,ybmv16-v6:9863,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/117,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:2e46::]:4246 had response of OK with response time of 0.014238357543945312 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybmv16-v6:9863,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/46.esf,ybmv16-v6:9863,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/46" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfFwMUu3LHLKcvEIyMxOzQsLrEgJDHK0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZShS6NJwJUqGJqYWJhZmRiamegZKKCpqdSjTjsSL5UIXhYiAeLFctVwAt3mDCkEBAAA=" + } + }, + { + "ID": "70d6e503691016b2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "196" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "38e16a4726d99a40c4dbd2c48e3bb68d/1971311407202738956;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICgnaGVsbG8nLCBUSU1FU1RBTVAgJzIwMTYtMDMtMjAgMTU6MDQ6MDUnKSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiT0FXUU5rdkNsVW4zOFozd0ZZUTBXQmpmRVB0IiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:19 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/9xwoy-oiNa1qFBF2fTL5P_iC7Cc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vned124:4463,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/38,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=N608W6iEFoeLqwXIz7qQDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/38,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/38" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY7gOtFPD520aqWgImQiI0AdO0USR0PvbMBeMz9gGlEf+9Z4ObtKnaPp5mZmdnd++poagrli7UjqJGLN5sIT+8eeSReiEBEDgugQfVNVZu/zscIEisYpksr2h635/P9l+Qpfnf9vzQ4myM9c3gcmDQcGTfzFnP7ZEHtarDqvKLbUJAtDKei5brGJ3ZtP2pe3c7Xu16ySw1vXtzP/h6i+4uH2lwIyphAQkdsXRVypdCZEVH0/b7fTvmPE4AZ6xoE77W6sa1naFlOX8EIgrtNztNhiq0vxh+SDjBgvH0/WxamUvBBCjkkBKQDTw1FEU9Vx/+KVApqlQn9B/ZFLX2K8nSsqEcS1fCU8ribV5DlW2V7vx4fqnTYBT0QuVtcwlJwpsXSji8DqZh9/pGaRpId1rIbBlI0e0OsjrIbr6rjGXjUAiWVg4hjpI63f/kK9VY4ALOnDnxqIci5Ee6F1FkUKC2axFANvLARhZQZ+G5joNrtSgNT1qc8jSK/GhBie/YkYUdSunCIqZDse1FyPGR7hFk2y5Caqk+ntonOWABfVZkvGD1BHuToBsG8+FgPg6CftA/R93n7BX1bjKUzHAyG/ek5EzMcsYlt5rrcBwGk24vHH6u0W0BI4gxOUw3iWRQnBQggeN5aYXAYlvU2ypf5UxV6TAejq/UFywmJ09+Mqsk5RbYuhLotolcZOq2bzn26UqkKBevcdtyTvivp1FZryEV4SGD5xNRX/Qqo+RzWGNWBlFNxzF93zR1y27JFQqk87XwseH6K2LGJGab5WaVeBGPltuPC9hBwjPI27EssmMEMCF8m4ryF6qNY+MHf9yH+E4EAAA=" + } + }, + { + "ID": "712b5ca96894ffe8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/OAWQNkvClUn38Z3wFYQ0WBjfEPt?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3d62199104d1a05a27732404c9f8917a/363251070706804789;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/OAWQNkvClUn38Z3wFYQ0WBjfEPt?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:19 GMT" + ], + "Etag": [ + "lt2bLIDi/ZC1JerCKUrJ4A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4496,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/118.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/118.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/118" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/5WSXU/CMBSG7/crlnoLkQ+jxoSLMaYOJx+DhYgxZGxnOOjW2Z1JCOG/u3aAQ2KiV+153/f0OWm7VVSVrMLYJ3cqmYeLjwz45mIBOBQbG9KMYpovCYtTIBWRBnQXIk2xMbfMTng51etd4PqTw7tXWqtVpFLvHSI3z23zKq+DEKif5vWrrNW9Lr3YjUCcGNRmsnkv4yaRsm3ofbtTdiLmS6fnWJbWtoyydwY6hZ0AZzI8q5f6T8ijsW32Hn66Z/SSvav8mdr4jTo2n43RWHse/At83L8pZUVUciiyZHMbAuAQe/D9LglnS/DQlB/Az6gHWE0Yx+rN9WFA0Vn4fW0y7K0+derEzdtpc33/MqxN2svAGOAhS5nnYshiEXdG5EhHhi612Vq8DCkuvNDaG4R0wJkHaQqSUiOHeXUWJRRQjIs8Ayl7bv6zHkPca8pO+QKyGDqKxAIAAA==" + } + }, + { + "ID": "87b3714f8d92aa9e", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonbb9bdfc965b4a6fffd4c36fa58b069018c055700/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3d62199104d1a05a27732404c9f8917a/8818338614244330313;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonbb9bdfc965b4a6fffd4c36fa58b069018c055700/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:20 GMT" + ], + "Etag": [ + "FumO7MDkim7vI1Dh//66UQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4132,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44.esf,ybnf10-v6:9874,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/54,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jl\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"wa\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:c217::]:4291 had response of OK with response time of 0.014681577682495117 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybnf10-v6:9874,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44.esf,ybnf10-v6:9874,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXErzfU393XJzsw1L/M0dMnQ1zczCw20tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZUApZAEM1Zh6kHQqZaTm5OQroUnW6hCp29DE1MLEwszIxFTPAMMQFH4sFzYZGAsiC+LFctVyAQC4vnrnZwEAAA==" + } + }, + { + "ID": "d07af4cdf9fbb563", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "195" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "fa713241b684cc21ad155831ebd1e8f3/7210278277765173106;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIERBVEVUSU1FKFRJTUVTVEFNUCAnMjAxNi0wMy0yMCAxNTowNDowNScpIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJsRVhwYnpJdW5idmRYSEVDT2VsQlJPV2dVbHQiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:20 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/6DSYah63YGsaPmZDQXtYPEbe8Mc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnno25:4168,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=OK08W5C5B4W5qgWBiYYg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB8969A9CGtVJvjGyxVbWqoi5Q4qY0bJ4pkHecFX4w5DIctJ/J/74FxkzZV2xek08zs7OwuTx1JXtFsIfclOaLJpoJi/+aBRfJ7AQDHSQ3cy7a2sr1H2IOfGuUyXQ7j7M6bT3czZCiWN7nFS0u/HZb4en3nfZvx22s/AueS3MtNHdqUX1QpAd7NWcG7tqX1p5Ne6s/y6DGosmi7mH31B1eQfh5f3STTlDfCEtL4gmarWr7kPC/7irLb7XoJY0kKOKdlj7C1cmpc2WpKXrAHILxUfrNTRKhS+Yvhx5QRzCnLPkwnjbkQjCGGAjICooGnjiTJbfXgT4FqUaM6ov/IJgitX00Wlh3pULsSlsU0qYoT1Ng26drH80ue+Bf+IJS889APg0v/bf2ZhOeX19KZhlSri/SuhiTV7COjj8yzd42v6BtKTrPGIMRRegr3P/FqNea4hJYzJ07soAi5kepEMdJiiE3bIIBM5ICJDIithWNbFj6peW141OKMZch2RWknXti2Q2zVJpqlIc21YlVF1gIbsYlcl7iOXKsPx/ZJAZiDR8uclfQ0wMHYF1OYB1/mI9/3fK+NuivoK+rNOBDMcDwdDYSkJeYFZYLbjDUYhf74fBAG309oVcIFJJjsJ5tUMGKcliCAQ7uzkmNeladl1a96prJwGAWjofyCRcXkyU9mk6TeAl03AtXUkY101UI60o9HIkQFf40bRntuv15GY72GjIf7HJ4vRH7Rq4hSzGGNaR1E1i1Ld11dVw2zK1bIkcrW3MWa7a6InpCEbpabVepELFpWnxawhZTlUPQSUWRLCWBCWJXx+ieUO4fOD44Uy79NBAAA" + } + }, + { + "ID": "61f9c1e59b425f69", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/lEXpbzIunbvdXHECOelBROWgUlt?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "34c8c44ea62c3f855d934e1e88c13a26/5674273336300688539;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/lEXpbzIunbvdXHECOelBROWgUlt?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:20 GMT" + ], + "Etag": [ + "p9RMLfDlRAwwb7Fck5QLzg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4414,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/4.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/4.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/4" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30+DMBB+568g9dUl00QXTfYAG2YkzDkccYkxBsqBbIVWekjYwv9u2zHnS6/fj+t916Nl22RfVCl5tElS5N8N1N1VDrjWlxBkw1CqInglgVxrN2Cca7d4CJdBNmeh07bJ5Inu79bBIZ9OTy5Jv6CMle+okMJZASyVCr8bbA+80aq4BP1iNv40zQONnTD03Nl4G3/p/ddKnhrtOQoCxw08Mki9qR/q7E2MHU9CyKCGisIljKj5Dij6Zuu0YRRwJHiNo8n97TBFd5505m1FcvCbKvlJtwtvtgLmhqu3PGJ49jJOYyx4pe3RK/mbjhxjFvJW701uyIVzOwT5UnMKUoKZMibnvDNeCgao42LdgKFprL5zUeDAWb31C2MKTaG5AQAA" + } + }, + { + "ID": "da784ecaec168516", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0797628fd778c717c2620296f1106da4f5099c98/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "34c8c44ea62c3f855d934e1e88c13a26/14057586955488507823;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0797628fd778c717c2620296f1106da4f5099c98/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:20 GMT" + ], + "Etag": [ + "yDZCcCzsUejLJltuXDJ7jg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4131,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/2.esf,ybkp12-v6:9832,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/83,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:3646::]:4241 had response of OK with response time of 0.014756202697753906 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybkp12-v6:9832,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/2.esf,ybkp12-v6:9832,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/2" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqal0iXJOdq4qDk3N8vHKKSmNcPEyz0q3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBiGGplYGJlYGpkpwRbVQViwXjBfLVcsFAND6LGPMAAAA" + } + }, + { + "ID": "02eb02ebaf67a237", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "191" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3d226959712157d09efb7ffc04e5a751/12521582018335701976;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIERBVEUoVElNRVNUQU1QICcyMDE2LTAzLTIwIDE1OjA0OjA1JykiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6ImxUdXYwS1JPbm1kaVNoaHE4TnlYeWVuVW1wUSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:21 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/KqbeSH8AVkrVBk40GRGRJd4Xzy4\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnff78:4058,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=OK08W-GvOdbRqwWDg5e4Cg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXY/SQBR951c09WE1ETr9LiRGESrisqilrBuzCZlO75RZSqe0Uwi74b87LeCurlEfJ+ece+65985DS1FXLIvVnqJGLNlUUOxf3PFIfS0BEDipgVvVNVbu8B724KdWuUyXI5p9Hy7muxtkaZebCGYfvf71qrh+v7LQKBgFn2Lr5n5v3apNHdaUj6uUgGjnvBBt1zF681knDastugw+Z+uYzZbLjTfd3+whm6/zr42whJROWLaq5Ush8rKnabvdrpNwnqSAc1Z2CF9r58a1raHlBb8DIkrtNztNhiq1vxi+TTnBgvHszXzWmEtBABQKyAjIBh5aiqKeqo//FKgWNaoj+o9sknDyq8nSsqUcalfCM8qSqjhDjW2T7vR4fKkzf+IPQmXYD/2X4fjKn4X9qy/KhYF0p43MtoEU3e4hq4fsi1eNp+wZSsGypniIo/Qc7H+i1WoscAknzoJ41EMR6ka6F1FkUKC2axFANvLARhZQJ/Zcx8FntagNj1qc8cw1ozj2EDhgmJatU+ramFCkA4mpSS2iey6x7a6t1urDsX1SABYwZGXOS3Ye3iDw5QQW4w+Lqe8P/eEp6q5gz6jfgrFkhsF8OpCSEzEvGJfcZqTjaegH/UE4vj6jVQkTSDDZzzapZFCcliCBw2lfpcCiKs+Lql/1TFXpMB1PR+oTFpOTJz+ZTZJ6C2zdCHTbRC4ydUdHrns8ECkqxHPcQOYR//UqGus1ZCLc5/B4HeqTXmWUYgFrzOogquk4Zrdrmrplt+UKBdL5WnSx4XZXxExIwjbLzSr1Ih4tq3cxbCHlORSdRBbZMgKYEF5lov6AauvQ+gGotaPySQQAAA==" + } + }, + { + "ID": "dd31cee86acecbc6", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/lTuv0KROnmdiShhq8NyXyenUmpQ?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "93d933c5fb21515b67c4b2d0d61b090e/10913520582344916993;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/lTuv0KROnmdiShhq8NyXyenUmpQ?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:21 GMT" + ], + "Etag": [ + "lZzfvzJT+hTXdh34Br6wtA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4219,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/35.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/35.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/35" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/02Q207DMAyG7/cUVbiESeWggZB20cIkDtVgXStNIIS61D2MNOkSd1M39d1Jso5xE8eff8e/sx84DvkpeUruHbIs83UDsj3LAWfmEoJqGCodasEVkAujBkxyo2Yfu2yze4nOi2iRFtc3vhxt0RuPDypFC6gSrdvrTOdZCSxVOv+0udNzW+NJBebFzP22zT3Gtrb40Ysm/3klUsuncRB4fjAhfamz8UufnbWwEssQMpDAKZyM1FKsgOKz3ThtGAUc1kLi8HZ01U8xnYc6i5qN+xq+8Sot50Wxvpu2ixZ4XNWzo5YJmmApuJHHc/I3HQUmLBRbszO5JCfmtwjqXQoKSoGd4pKj3wdR1QzQ2EXZgMU00V/5VGLPBt3gFwUxDu61AQAA" + } + }, + { + "ID": "301686d527d2e040", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon73bdd80e6e23451ff75acf01ecdf3f4c187c5595/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "93d933c5fb21515b67c4b2d0d61b090e/922146626661229077;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon73bdd80e6e23451ff75acf01ecdf3f4c187c5595/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:21 GMT" + ], + "Etag": [ + "cEsYb3zCh8KmrIB7RByDrA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4176,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/32.esf,ybls5-v6:9850,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/20,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ws\" allowed_cluster: \"na\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e292::]:4464 had response of OK with response time of 0.014599323272705078 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybls5-v6:9850,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/32.esf,ybls5-v6:9850,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/32" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqUl2LY5MMq5yzrDwzi3ydDIPcqp0KXK0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBkpwuVooK5YLxovlquUCADPLlQzDAAAA" + } + }, + { + "ID": "4853f9d44c66e026", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "191" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a4ddada137fe5e80960efd015fcb1c16/17760547789403285310;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFRJTUUoVElNRVNUQU1QICcyMDE2LTAzLTIwIDE1OjA0OjA1JykiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlgxRUd4ZklXaGIyR0NqcmU5TW9mUVRybjZSUiIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:21 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/xnCDhhMY01iIMDhtCJIGjdtBrkc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vngh2:4427,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/33,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Oa08W_ndJ8K9qgXdkLCQDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/33,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/33" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXY+TQBR9768g+LCa2DIDlI8mRtcWG8y20Za6q9mkGYYLTEsZCtPt1k3/uwMt7uoa9YXk5pxzzz33Dg8dRV2zPFIHihqyZLuD8vBixUP1tQRAkKQGblVbX9uj73AALzOrNEvHcf5ttFzsb5Cp3efDUZpOviLM/MkoFcOP/ngVifflmt6qTR/WtI92GQXRLXgpuralDxbz3g32xvexf52G+ni4KsGd8PhzUObWbNYIK8jiK5ava3kqRFENNG2/3/cSzpMMSMGqHuUbrR1cu9O1ouQroKLSfrPTZKhK+4vh24xTIhjP3yzmjbkUzCCGEnIKcoCHjqKo5+7+nwLVokZ1Qv+RTVFbv5osLTvKsXalPI9ZsitbqLFt0p2Lx0qde1feMFACf+K9rD/z4HLySbnQEba6yOjqSMH9ATIHqH/xqvGUM0MlWN40D0iYtcH+J1qtJoJUcOYsqRM7KERuiJ0wRnoMcd82KaA+cqCPTIityLEti7RqURuetCTneehiF8WuY2FshpGDwTYhsqIIIYO6MTZsR49ckzhqrT6exqclEAEjVhW8Yu3yhjPvMvCW/ofl1PNG3ugcdV+yZ9TrmS+ZwWwxHUrJmViUjEtus1J/Gnizy2Hgf2nRXQVXkBB6mG8zyYhJVoEEjud7VYKIXdUeqq7qnarSYepPx+oTFpObpz+ZTZL6CmzTCHDfQDYysIUdbJ8eiBSV4jnuGu4J//VVNNYbyEVwKODxdahPZpVRyiVsCKuDqIZlGa5rGNjsd+UJBcJ8I1yi2+6aGglN2DbdrjMn5GG6exfBHWS8gLKXyCZ3jAKhlO9yUf+AaufY+QG2MWHVSQQAAA==" + } + }, + { + "ID": "347ad53e58e43637", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/X1EGxfIWhb2GCjre9MofQTrn6RR?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cf98efe0c97bcc9c0856eef827fcc2cf/16152487452907351143;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/X1EGxfIWhb2GCjre9MofQTrn6RR?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:22 GMT" + ], + "Etag": [ + "etAfhuSH4/sHMJOAZn257A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4022,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/37.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/37.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/37" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/02Qb0/CMBDG3/MplvpWIhCFaMKLQQib2VQGRKMxZnTX/XFrZ3uLLoTvbluG+KbX+91zvee67zkO+cx5Qu4cssvTrwZke5ECrswlAtWUqHSoBVdALo0aME6NGtBlWbP2rq+UF94/uq98dDNxp9OjStEMqljr9jrTOcuhTJTO32zudNzWeFyBeZENPmxzh7GtLd744eI/r0Ri+cM2CNxZsCBd6WDjuz4P1kIhdhEwkMApnI3UUhRA0bcbJ01JAfu1kNifjEfdFNN5rL8MF8sf5j9nu9FyXki4DQVbbSQfR9FJWwoaYy64kW/X5G86CozLSHybncmQnNmsRVBPUlBQCuyUATn5nYuqLgGNXZQNWExj/ZVejh3rHXq/bsicC7UBAAA=" + } + }, + { + "ID": "a6754dee045c3423", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonb9190f986114bd81e74ed6dd003c9f13782d94a8/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cf98efe0c97bcc9c0856eef827fcc2cf/6161393868410556027;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonb9190f986114bd81e74ed6dd003c9f13782d94a8/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:22 GMT" + ], + "Etag": [ + "IG+yD3eyq89gtd2Zj1CvpQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4495,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/34.esf,ybel10-v6:9856,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/17,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"wo\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f0d7::]:4151 had response of OK with response time of 0.013097524642944336 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybel10-v6:9856,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/34.esf,ybel10-v6:9856,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/34" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfF01650MU6tLLSwTC9JMYrKMnQuKwi0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNplYGJlYGpkpwmVooK5YLxovlquUCADEEpzfBAAAA" + } + }, + { + "ID": "b94296ec2a79b411", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "160" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "64d7edd820cdd472a46c1700d40c37d6/4553333531914621860;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICgxLCAyKSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoialJFaWNoR0hXRnE4OWFORXBlR01zc0ZYMkdYIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:22 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/aQF0fMuERDLDmsMvjRXagBjkL1Q\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnab123:4382,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/94,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Oq08W8idGMrHqwXYmpmIDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/94,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/94" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY15dWavD5245UtSk4FIkgxUCDqkjofKzNgfEZ3xlEI/57zwY3aVO1fTzNzM7Mrv3U0dCG5Ut0raGYpbsKyuObNY/RewWAJGkNPCLP3Hj973CEMLPFKlsNkvxbfzE7zLGtk/tbnNxVYdQf9bfibr+O5iT9vN6MjPtH1MxhzfhllVGQVwUv5ZXnmtezSXcdhYyuBl8ebnd+QMZhAYM7IW7n5mDeCAVkyYjlm1q+krIQ17p+OBy6KedpBqRgokv5Vm+D63tTL0q+BiqF/pudrkoJ/S+GHzNOiWQ8/zCbNOZKEEECJeQUVICnjqahy/ThnwrVokZ1Rv/RTUOtX01Wlh3tVLtSnicsrcoWamybdpfH8wtNwlHYm2pvjfea+a4ZqkKBkCxv1FMSZ23y/8leq4kkAi6cBfUTH8c4iA0/TrCZQOJ4NgXsYB8cbEPiLn3PdUmrlrXhWUtynhsutUw7cDzseobluNi0LWwnge06xPSBmo4X4zihqFafzvFpCURCn4mCC9ZupxeFN9NwMbxdjMOwH/YvVQ8le0V9iIaKOY1m456SXIhFybjiNjsbjqdhdNObDr+2aCVgBCmhx8kuU4yEZAIUcLocREgiK9Feon7VO0XKYTwcD9ALFlObpz+ZTZP6CmzbCAzHwh62DNdU6zt/AUpUyte4i90z/uvZG+st5HJ6LOD5/OhFVlWlXMCWsLoIslzXCgLLMmznSp1QYoNvZUBML9hQK6Up2612m8yPebyqPi1hDxkvoOymasieUSCU8iqX9R+GOqfODwMashcqBAAA" + } + }, + { + "ID": "e1257f85ac73f368", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jREichGHWFq89aNEpeGMssFX2GX?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "27c508b8d7824c78961ee905de0c8372/3017328594745039053;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jREichGHWFq89aNEpeGMssFX2GX?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:22 GMT" + ], + "Etag": [ + "Xr1UTA/OQVHavQNXMxMQwQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4218,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/18.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/18.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/18" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/61SO2/CMBDe8ysidwUVGPqSGICGgAShcaGNVFXIOAeEJnGIL6UI8d8bO0BDWTp08n2P83fyeWeYJvkIYp88mGQWLNYZpNurBaCrCgoyC1HmRyJiCaSi3IBsodxeWp+MW9cj96XHPl3HG34N3Y3bbBYuyZcQsdy3y1GO5wGEvszxm8bmgddazCJQN85rU918oHGbaJpanRF9LCuR8LXiTAaDVntglbWLoPOws8CpNk/rpf6z5L4ztmyL/pYv4kvyvvLn2MZ/xp7qd6PMKKRHIisxozCHFGIOP2tJUrECjn29fz8LOWA1ESlWb2+O46nOQl9RK+BLu/faXd/dM8dKwB5K2fUatnf0hoIzDESs7JNnckpHgSykYqMWQ4r3Lrj2FkE+pYKDlKBTauQ4b0dESQioxsU0A01zln+sXoAHztgb39esgbnDAgAA" + } + }, + { + "ID": "4d010129725e79f7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon16c3249570671356024304f9465a28ec257b0bfc/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "27c508b8d7824c78961ee905de0c8372/11400360743267891937;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon16c3249570671356024304f9465a28ec257b0bfc/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:23 GMT" + ], + "Etag": [ + "9dZuS5v/PqexuQBv6rq26Q==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4140,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/33.esf,ybdk16-v6:9870,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/9,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:312::]:4013 had response of OK with response time of 0.01313471794128418 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybdk16-v6:9870,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/33.esf,ybdk16-v6:9870,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/33" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqbFMiSoNNi3TDyhMrSgNdCozKyo0Mgu0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZUApZAEM1Zh6kHQCrUWTqNUhUqcRhk4UfiwXNhkYCyIL4sVy1XIBAEIt8k1YAQAA" + } + }, + { + "ID": "80743ce468855701", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "163" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "defec63e5190afaffdc8b1e293d71ae0/9864355801803407114;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFsxLCAyLCAzXSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoidVNqWVYxcGZPVDdPREVyRXBwN3ZnME9FcmJNIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:23 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/SpmVG65IseRiRvCaX4HZnzO_fbU\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnij21:4020,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=O608W-G8C8_mqgWKjIewAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB8969A19fEHN8QqWojm6ZIiS3ZOE3TVNZxXvDFmMPcYcuJ/N97YNOkTdX2Ca12Zmdm93juaWjFigW60FDCsk0N1f7dI0/QmWqAJFnTeECeufKGT7CHMLfFMl9epcX9cD7b3WFbn5br2yvXiQRM2GQ7IHf25/viaTxPk9kDauewdvyizinI85JX8txzzYvZtF9PH7/eGmU6jr3xMKzCsvS2GR6HVXLTEgXk6TUrVg19KWUpLnR9t9v1M86zHEjJRJ/ytd4Z17emXlb8EagU+m9yugol9L8Ifsg5JZLx4v1s2oorwgRSqKCgoAw89zQNnaZHfwrUkFrWsfuPbBrq9Bqwkuxph0aV8iJlWV11rVa2TXcqXio0Da/DQax9M84080yzvrdzlS8QkhXtgJgkeWf+f+w3bCKJgBNmTv3UxwkOEsNPUmymkDqeTQE72AcH25C6C99zXdKxZSN45JKCF4HtJq7hJL5pJMbCgsBNLfX1bGK7gG0XG46fWNRDDftwtE8rIBKGTJRcsG5Bg0l4GYfz6NN8FIbDcHiKuqvYG+iXSaSQ8WQ2GijKCVhWjCtsu7ZoFIeTy0Ec3XbdWsA1ZITup5tcIVKSC1CNw+kmQhJZi+4YTdXsFCmFUTS6Qq9QTG2e/kS2SZorsHVLMBwLe9gyXMvCwfERKFIl3/ZtfHpOv16+lV5DIeN9CS8vAL3yqqJUc1gT1gRBlutaQWBZhu2cqxNKbPC1DIjpBStqZTRjm+VmlfsJT5b1xwVsIeclVP1MDdkyCoRSXhey+clQ79D7AY++ljQtBAAA" + } + }, + { + "ID": "b29aa4bd2c653134", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/uSjYV1pfOT7ODErEpp7vg0OErbM?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bd7681aa30d508c2c9addd6bedb828bd/8256295465307472947;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/uSjYV1pfOT7ODErEpp7vg0OErbM?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:23 GMT" + ], + "Etag": [ + "5yWzQ8OMYtbgG1SBjTZPaw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4147,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/98.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/98.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/98" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qskYKIzJjyINMgDboypQWNM193m5rbW9iaZhP/dtgzxpdfvx/W+627geeSzaFJy45GkyL9aUN1ZDriylwh0W6E2RYpGAzm3bkCWW/dl9/yzug6WG0zy+Xg9LeOXkG0nk4NL8w+omfHtDDI4K6BKtcGvDns977SG1WBfzEbvrrmnsZOOXjzEdE6j/1ItUidFNKS3MZ2RXtq7+mbOvUtRiiSCDBQ0HE5ZpBIlcFy4pdO24oBDKRQO/auLfortPOjtutw8jWUWxH4wo4pK6X/no4CqZHn0VoIzLERj7Y9r8jcdBbIqElu7NhmTEzftEHSoBAetwU0ZkWPeO1HLCtDGRdWCozkzv3lfYM8N9oNfOTW3jrgBAAA=" + } + }, + { + "ID": "e9813008a1de52b5", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon946b615b821b1d3e96f31d374a46e0460158b3c7/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bd7681aa30d508c2c9addd6bedb828bd/16711664479543518791;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon946b615b821b1d3e96f31d374a46e0460158b3c7/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:23 GMT" + ], + "Etag": [ + "2HIFRXdFp9Mtt5I1sNwWcg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4042,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybrd10-v6:9828,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/67,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:d506::]:4294 had response of OK with response time of 0.013185024261474609 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybrd10-v6:9828,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybrd10-v6:9828,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTHy8HQLikhxK7D0LSkx9TQs9isPT063tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZShS6NJwJUDzUYRrdYjQY0SGHmM0PUi8WC50UYgIiBfLVcsFAEhCDgRfAQAA" + } + }, + { + "ID": "e4a2e84d856ed9f0", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "173" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8cc29b07e167a130789325b8ef66b2dd/15103604143047584624;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIChbMSwgMl0sIDMsIFs0LCA1XSkiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6Imh2RlhZV2xQc21jVzFhWk9QZk5vb002amNNMiIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:24 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/ohI46AbKkh7FQjO6d148mr7i6Ik\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbd207:4339,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=O608W9HbOpL-qQWtmJlo" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T227TQBB9z1dYywtIaXy/VUJQJS6yaNOSCy29KFqvx/YmjtfxbhKFKv/O2olpoQh4XJ1z5syZmX3qKGhBixidKiii6WoN1e7NnEWoKwEQOK2BB+QaC3fwHXYQ5BbP8uxTUtwNZtPtrWapLAst5yz6vMjc8y/zKyfWLW9ZudQJFw+oqUOb8vE6JyBOSlaJE9cxTqfjXrY5v/12k1/zJbnR8d3VdTJk7NKZk0ujEXLIkwtaLGp5JkTJT1V1u932UsbSHHBJeY+wpdo2rm4MtazYHIjg6m92qgzF1b8YfsgZwYKy4v103JhLwQgSqKAgIBt46igKOlYP/xSoFjWqA/qPbApq/WqytOwo+9qVsCKh6bpqoca2SXd8PL/QOLgI+hPl7b3eVYzHrmJ2lXurq9iP7xoH2SFwQYum1ARHeRvjf4LUaiwwhyNnRrzE0yLNj3QvSjQjgcR2LQKarXlgaxYkTuy5joNbtagND1pcsCL2NDsyLN+3cQQQ+76mmYYPRDd04vu6r8uCmhlbqFbvD+2TCrCAAeUl47QdVX8UnE2CWXg+GwbBIBgco24r+op6MwolczKaDvtSciSWFWWS2wwwHE6C0Vl/En5t0TWHC0gx2Y1XuWQkOOcggf1xO1xgsebtWupXPVMkHYbh8BN6waJy8uQns0lSb4EuG4Fum5qrmbpjaa59OAcpqsRrXHe9A/7rDTTWSyjEZFfC8y2gF73KKNUMlpjWQZDpOKbvm6Zu2SdyhULT2VL42HD9BTFTktJVtlrkXsSibP0xhg3krISql8oiG0oAE8LWhai/G+rsOz8ApAhy3TcEAAA=" + } + }, + { + "ID": "cff882d20d51f00a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hvFXYWlPsmcW1aZOPfNooM6jcM2?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "53e93b3efa02c5a9d3f0e48a8441c482/13495542711351767192;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hvFXYWlPsmcW1aZOPfNooM6jcM2?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:24 GMT" + ], + "Etag": [ + "291ZCCIkeaXsboQF+ddqrQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4168,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/124.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/124.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/124" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/62SX2+CMBTF3/kUpHucJuoSly3xQbFuJv5BptG5LKaWi6JAsS1bjPG7jxZluL0sZk/0nnMuv1u4B8M00daPXPRooqW/2iXA9zcrkCN1cEAkgRTpI2aRAFRSaZBkpdK1h+rcsrpbIDOxZKPOrevu+KjRyFKCriEkae6QVmnt+RC4Iq3fdG2edO1FJAT1Rq+y0M0nWe5jLTvYGjrtohMyVzuDSa/XbPVw0fsFuoRdABc6vKgW+i/I3cEYP2Hnp33GO9jGzTFuo4J9LP0ZW7sSm9/6Ouzdf942P78bRUVVeiS0YUsHPOAQUfjehpizDVDZ1WvnJgEFWY4Zl+X7+vmrqM7MX390Zq/TwBYhnVbJfGh7A8b69Q3t59mAUSJ9Fqn45AXldMkkCRz2qfYBZb8501p7CcLmjIIQoCkVdJ7XYmEcgFTjSp6AlilJ9/nZlyfNOBpfW9Zl6zoDAAA=" + } + }, + { + "ID": "c41113768c3f415e", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anond805b24995abeed9900329ec121c99191fef03d4/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "53e93b3efa02c5a9d3f0e48a8441c482/3504168755668013741;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anond805b24995abeed9900329ec121c99191fef03d4/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:24 GMT" + ], + "Etag": [ + "Z+F8F+Ov66/70nEEtxgH0A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4224,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/19.esf,ybpz16-v6:9829,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/98,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f10d::]:4158 had response of OK with response time of 0.013050079345703125 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpz16-v6:9829,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/19.esf,ybpz16-v6:9829,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/19" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqYnSdrNw0/YvMzPTNzfIc3UtqUj3MHC0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZUApZAEM1Zh6kHSiK8OuFK4c6EoskrU6JJpihNUUDLFYNBEMe3B4SskY3XxidZIaHCZUCQ5TMoODC5csQgbGgsiCeLFctVwA4A4zJMgCAAA=" + } + }, + { + "ID": "f76bc72716b9b356", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "176" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bc2a2cda586466e7bd517e8aa3e42041/1896107319677229014;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFsoMSwgMiwgMyksICg0LCA1LCA2KV0iLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IjBydlRNMXFtdHlzajNCR0MxUXJCTnJKVGN1aCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:24 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/TcORyK6DB8yIZU4GjaAHubvrx-4\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnu206:4129,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=PK08W_HCI5DiqAW31KGwBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXY/SQBR951c048tuAvSTfmxidBfqiq4YoahZ15DpcFsGSqfMTEHc8N+dFuqurlEfJ+ece+659859S0Mrms/RhYZimm5K4PtnSxajtgJA4rQC7pBnrbzBd9hDmDlikS2uk/x2MJvuPhuOHpH34/1bd3Dl74e3U+d6iS9fl/GWf+s4d6iuQ+vy8zIjIDsF47LjudbFdNI1+DZ6Z27Wci+W9tV13/zAr0b8TUTKRS0UkCU3NF9V8oWUhbjQ9d1u100ZSzPABRVdwtZ607i+tfSCsyUQKfTf7HQVSuh/MXyRMYIlZfnz6aQ2V4IxJMAhJ6AauG9pGjpVH/4pUCWqVUf0H9k01PhVZGXZ0g6VK2F5QtOSN1BtW6c7PR5eaBLehP1I+3JmtjWrrdnnbe3MaWu9tuaef61NVJMgJM3rahGOsybJ/2Sp1FhiASfOjPiJb8RGEJt+nBhWAknPcwgYPcOHnuFA4s59z3Vxo5aV4VGLc5a7phNYxLdN0zOSWIkC2/HAtDzbBwP7DgZVFPwEVerDsX3CAUsYUFEwQZtp9cfhZRTOhq9mozAchINT1B2nT6ifxkPFjMbTUV9JTsSCU6a49QyHoygcX/aj4ccGLQXcQIrJfrLJFCPBmQAFHE4LEhLLUjSbqV7VTJFyGA1H1+gRi6rJk5/MOkm1BbquBWbPNjzDNl3HDXrHi1AiLp/ivmEf8V/PoLZeQy6jfQEP54Ae9aqi8BmsMa2CINt17SCwbdPpddQKpWGytQyw5QUrYqckpZvFZpX5MYsX5cs5bCFjBfBuqopsKQFMCCtzWf041Dq0fgBAlKcZOgQAAA==" + } + }, + { + "ID": "266e35620fcfecfe", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/0rvTM1qmtysj3BGC1QrBNrJTcuh?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "16f7f09f9bf5f625456ccfca8cf74799/360102378212809982;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/0rvTM1qmtysj3BGC1QrBNrJTcuh?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:25 GMT" + ], + "Etag": [ + "9skudH3eb0gAHm0jckbL6A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4098,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/6.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/6.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/6" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/8WST0+DQBDF73wKsl7bhNqkRpMeoJK2BmuL9GRMA8uUQoGlu4OGNH53Wf5U0IsHE0+7894bfhN2zoqqkmOY+uROJV4YnHLgxVUAuJEXG0QeoyiPjKUCyECmAd1Apm/FMfcXY/C0QF8kWkSPnjXRp9M6JegBErfMncuqrPchxL4o65eqVhu98lI3AfnFvbarmhsZi6ySbXP2ZN93nYT5jbM2dcfseT9AfVgPuKvCu1Gnv0derhxzbtrf7Ra/2lqWblgm6dgfg19jr/8HO/5L7OX+qnQVWVUjkYh5NuyBQ0rhaxsyziKguKzWzs9jCjjMGMfhzaT9K7Kz9jX+5jyOTgkWIhob89low40Vf3BofmizMaMuhiyV8e0zudCRoRvb7F3uA6mfudaMAkGsOaMgBNQU0s47Y0kWA8pxkedQydQt93kRYqMpH8on0ffd0DoDAAA=" + } + }, + { + "ID": "b0a23c4dd848a62a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon61492c831170fb5089347e12738e0a84aef02e8f/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "16f7f09f9bf5f625456ccfca8cf74799/8743415997417340435;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon61492c831170fb5089347e12738e0a84aef02e8f/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:25 GMT" + ], + "Etag": [ + "ouAPoPFAaWa7Xo5VFjRJuw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4440,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/12.esf,ybfk130-v6:9855,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/118,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1002::]:4048 had response of OK with response time of 0.013931512832641602 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybfk130-v6:9855,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/12.esf,ybfk130-v6:9855,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/12" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqckvdQzID3BzTAxPNI/INw1zywryKi23tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZShS6NJwJeiCGIbi1o9kCtCVWCRrdUg0xYgqphhjNQVDLJYLnwo0W2kZdiZU8bUpVUwxo0rYcWFTCROFiIB4sVy1XABfc/01OwMAAA==" + } + }, + { + "ID": "c12a821e66dbfb42", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "183" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e5d1ec8e61c3daa6085e52de97d91875/7135354561426555708;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFsoWzEsIDIsIDNdLCA0KSwgKFs1LCA2XSwgNyldIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJNSmtsYkZDODZvcFFGMW1KSTdzZWtlajJVeDUiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:25 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/jNeKuAtgB1-hteP6PCQBxyuNOV8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnnz3:4297,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/188,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Pa08W9iGEITWqwWR4qfoBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/188,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/188" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY15dEAnz+OptIVZuAU5GmNOEjrZpE6HyszYHxGfscQiP+e88GN2lTtX1czczOzu7dU0NDS57M0ImGAh6tC8i2bxYiQE0FgKRRCdwh11y6ve+wBT+283k8/xAm33rTyeYrtvXFAD4WpzI6M1pzCVfkqnt99rgtBp9vvDtU9eFV+1kRM5CtVGSy5RLzZDJqf7pYxsF51yMivT43Vhd9N4clLMzJo1MJc4jDS54sS/lcyjQ/0fXNZtOOhIhioCnP20ys9Hpw/cHU00wsgMlc/81OV6Fy/S+G72LBqOQieTsZVeZKMIQQMkgYqAGeGpqGDt37fwpUiirVHv1HNg3VfiVZWTa0XenKRBLyqMhqqLKt0h2K5wqN/Eu/O9Zuj26NpmY2Neu+qdnHTe3o1mlqRBXu8X3lpWaFXPKkajqmQVwH+p9IpZpKmsOBM2Ve6OEAdwLDC0JshhA6rs0AO9gDB9sQkpnnEkJrtSwN91qaiMRhMzPENnEd07INDygJWchsg1geNkwjpJhQMAyMSvVuPz7LgEro8TwVOa+X1h36p2N/2j+fDny/5/cOUTcZf0X9Muwr5ng4GXSV5EBMMy4Ut1plfzD2h6fdcf+mRoscLiGibDtax4oR0jgHBewOd8ollUVeH6isyp0i5TDoDz6gFyyuNs9+Mqsk5RX4qhIYjoVdbBnEsTpk/zCUKJOvcQfjPf7ra6isV5DI8TaF51eBXsyqomRTWFFeBkEWIVanY1mG7bTUCSU2xEp2qOl2lsyKWMTX8/Uy9gIRzIv3M3iAWKSQtSPV5IEzoIyJIpHlx0ONXeMH1MUNWkEEAAA=" + } + }, + { + "ID": "77f0d848806eb0e7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/MJklbFC86opQF1mJI7sekej2Ux5?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4698f11e3b093469e46b7a8c50e2cf23/5599350719456987236;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/MJklbFC86opQF1mJI7sekej2Ux5?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:25 GMT" + ], + "Etag": [ + "ULleCIcBtMD9pJT2tsBXaQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4322,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/97.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/97.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/97" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/52SQU/CQBCF7/0VzXqFBEgENeFAS9GSglIhMTGGlO2AhW137U6jhPDf7W4LtnIxnrrz3pv9Jp09GKZJdlESkjuTrKLNRwbp/moDOFMHH2TGUOYfwRMJpKHSgMFGpRceA9ulFk6Gt2I876C0XoJZv1+kJH2HOMhzh7zK63UELJR5/aprs9S1lwQxqBvXraVuLmXcCy37jv3oD6tOzMPSeXIGc6fmXYDqsBpwqcPLdqW/Rnanc+fe8X/bF/iKfWz8Gdv5J3a68LyB5Tk17Pn8ZlQVVemRyJavfFhDCgmFn7WIlG+Boqv3H2aMAjYFT7HZ657GU52FPxnv2Gpk33S5mI3a8djtSdjBtrP4uj5lGacBRjzRD+SZnOnIMWA+/1SLIcX/LjRrjyCfUk5BStCUFjnNa/NYMEA1LqYZaJkG+cN6iLDUjKPxDRjNUmLDAgAA" + } + }, + { + "ID": "73ccf0a343aaeb0b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon5cd2f0467523418ea6fcfc416380121fa06ae110/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4698f11e3b093469e46b7a8c50e2cf23/13982381772779825785;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon5cd2f0467523418ea6fcfc416380121fa06ae110/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:25 GMT" + ], + "Etag": [ + "xeuNZPYf8CvMSRWnVvhPSA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4039,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/38.esf,ybqy14-v6:9847,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/27,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:fe94::]:4169 had response of OK with response time of 0.014435291290283203 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybqy14-v6:9847,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/38.esf,ybqy14-v6:9847,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/38" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqalILfWLCohMs3Au8w0OCs8LK8sICHa0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZShS6NJwJeiCGIbi1o/TIkJa4NqAnsOhoFaHTBONqG6iMU4TsYrHYhHFajee4FQywWYnpn3odqGqQLN1EEa/KdUjy2wAIsucKpHFhU0lTBQiAuLFctVyAQBDxIjcbwQAAA==" + } + }, + { + "ID": "a8444a37b59a56c0", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "182" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bcef47d8ee7ba6b9ef735c5a66376f64/12446377930810257313;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEFSUkFZKFNFTEVDVCBTVFJVQ1QoWzEsIDJdKSkiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6InRmT2M4bEJKWUg1U21rMTFHYzBaUVRqRlhSUSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:26 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Hl-o3gJmH9xNjwd8_BdetAltCSs\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnff78:4058,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Pa08W-GpO4uYqgWVibugAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY15dECvj7K1LVEnASoogqxrRJmwqdj7W5YHzGd4TSiP/es7GbtKnaPq5mZmdn9+6po6AlzefoVEExTdcbKHdvHliMTiQAAqcVcI9cY+kOv8MOgszii2xxkeSfh7Pp9laz1Musy8z0anXpfxs/bOfe7GwOop+JwYTfo7oPrdvPNxkB0S1YKbquY5xOJz2RfCBednZ1d2lPVktdvyDa55vo4fw2vKmFHLLkmubLSr4QouCnqrrdbnspY2kGuKC8R9hKbQdXHw21KNkDEMHV3+xUGYqrfzF8lzGCBWX52+mkNpeCEBIoIScgB3jqKApquo/+FKgS1aoD+o9sCmr9KrK07Cj7ypWwPKHppmyh2rZO1xTPFZoE18EgUvph2L87aopJFE4H0dEX/UQxvh4f11ZyVOCC5nXPCMdZm+d/ElVqLDCHhjMjXuJpsebHuhcnmpFAYrsWAc3WPLA1CxJn7rmOg1u1qAwPWpyzXLdsJ4ltBzzdNWzwDaz7iWFYpmXY/hxi1yC+i50EVer9YXxSAhYwpLxgnLY7G4RBPwpmo/PZOAiGwbCJui3pK+qncCSZcjPjgZQ0xKKkTHLrTY7GURD2B9HoY4tuOFxDisluss4kI8EZBwnsmzNxgcWGt/epqmqnSDqMR+ML9IJF5ebJT2adpLoCXdUC3TY1VzN1x9FM//AupKgUr3HdavBfH0NtvYJcRLsCnh8FejGrjFLOYIVpFQSZjmP6vmnKO3TlCYWms5XwseH6S2KmJKXrxXqZeTGLF5v3c3iEjBVQ9lLZ5JESwISwTS6qf4c6+84P6+DDbEAEAAA=" + } + }, + { + "ID": "183b456cc3b8b25a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/tfOc8lBJYH5Smk11Gc0ZQTjFXRQ?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d8feb354e465a3644f351f761c02cebe/10838316494836249546;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/tfOc8lBJYH5Smk11Gc0ZQTjFXRQ?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:26 GMT" + ], + "Etag": [ + "4kBnOkcraMMqhpPJzXjTmQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4080,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/59.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/59.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/59" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/2VRyW7CMBC95ysi91okqLqpEodCUxaJQkwq0VYVCs4EsjrYE1WA+PfGTqBJOXneMn7j8cEwTRIFqUeeTLIK1tscxO5qDWirgoLMY5TFkfFUArlWbkB3rdy3US+dRky4k8l2k83G+0XoJHa3W7ok20DiFr5DgQrsBxB7ssBfGpsVr7XUTUDd6LeXurmicZdpmlr9KX2pKwn3KmVmPTtWQ7sIaoY1ApfavOzU+hvJozfHGlj0v3wRX5OP5/rbqDMKHfVmQr6i4IOAlMHffjLBQ2A40h/h5TEDbGVcYOvh/qbKV52ljv6UPca98cfwbp5Enc6AtT9tJ3xdUPvkjTlzMeCpsr/PyTkdObox5T9qQ6R8eMn1dghyJjgDKUGntMlp3j5PshhQjYsiB00zt/jhYYAVZxyNX9nmIeVMAgAA" + } + }, + { + "ID": "41c08cf3b75122d4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon1456fb56e81725e92a19f22434259deb72c97a6f/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d8feb354e465a3644f351f761c02cebe/847224009817462495;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon1456fb56e81725e92a19f22434259deb72c97a6f/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:26 GMT" + ], + "Etag": [ + "WhhgA0r9rC1Pp3gNQo+oyQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4132,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44.esf,ybbe71-v6:9895,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/2,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:57c5::]:4178 had response of OK with response time of 0.014255762100219727 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybbe71-v6:9895,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44.esf,ybbe71-v6:9895,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQnPyEh3NCiyLHI2DCgwTvcLzNfOrwy0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZShS6NJwJeiCGIbi1o/TIkJa4NqAnsOhoFaHTBONcJqIVTwWiyimSnRVqCqQeQiVMFGICIgXy1XLBQCveJWwKgIAAA==" + } + }, + { + "ID": "179f224f54772124", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "154" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "66334dda1ebc44d0a2c1f5aaf2bd5bf5/17685625172559584007;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIDEiLCJ1c2VMZWdhY3lTcWwiOnRydWV9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiVHpOOHpzc0x4dXVJSllYRkJvY3hobE8zYmF0IiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:26 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/-DM5AoGN8BwxScHEPIrNAb_lpco\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnma19:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/192,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Pq08W8G6K4f2qQXu4J7QCA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/192,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/192" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T227aQBR85yus7WvBN/AlUtUScFJX1K3AtGkVCa2Xs2aD8RrvGkIi/r1rg5u0qdo+rmfmzMw58mNHQ2uWL9GFhhKWbisoD6/ueIJeKwAkTmvgFrnW2h0/wAGCrC9W2eqa5t/Hi/n+xujr3fHHwZBfR97l/n5G3gefwzIaJousIPwWNXNYM35ZZQRkt+Cl7LqOdTGf9eKHyHsQYnJfVeGHbzdXl5zcr7JPdoJlIxSQ0QnL17V8JWUhLnR9v9/3Us7TDHDBRI/wjd4G13eWXpT8DogU+m92uiol9L8Yvs04wZLx/M181pgrwRQolJATUAEeO5qGztPDPxWqRY3qhP6jm4Zav5qsLDvasXYlPKcsrcoWamybdufH0wvNgkkwijWzmafygJAsb4QxTrI29P/ErtVYYgFnzoJ41DMSw09ML6GGRYEO3D4BY2B4MDD6QJ2l5zoObtWyNjxpcc5zQhK3v8R9C1OL2D41CaUGtSzLtXzTBMtNXAsssFGtPp7ikxKwhDETBResXcxoGgzjYBFeLaIgGAfjc9V9yV5Qv05DxYyn82ikJGdiUTKuuM26wigOpsNRHH5p0UrABFJMDrNtphiyrEB9P55PISSWlWhvUL/qlSJlEIXRNXrGYmrx5CezKVIfgW0agTmwDdewTcfxTOd0eyUq5UvcN+wT/uvBG+sN5DI+FPB0ePQsq2pSLmCDWd0D2Y5j+75tm/1BV11QGibfSB9brr8mdkpStl1t15mX8GRVvVvCDjJeQNlL1ZAdI4AJ4VUu638LdY6dH9kUcCAkBAAA" + } + }, + { + "ID": "1d2999062a01f7e9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TzN8zssLxuuIJYXFBocxhlO3bat?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a2f58806db2af488b649c6a29ae2a94c/16077563736585576240;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TzN8zssLxuuIJYXFBocxhlO3bat?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:27 GMT" + ], + "Etag": [ + "tnhvTbMobCGfvlX7XqxNOg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4075,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/101.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/101.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/101" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qskqIkYEx6EIM7MoQMSjDGm625j2O3GekMG2f9uW4b40uv343rf9dBxHPad5hG7d1iYJpsKyvoiAXozlwBUJUnpUmCugF0aNxBPjJvy1XYevmA4msRbuewvNzt/mgwGR5cSK8i49h000jhOQUZK4w+LnZa3Ws4zMC/GvS/b3NJUF5Z2/fl4Mg7+SxlGVvIXnvcw9MaslRpbP/XZ2BRrDAOIoYRcwDlLUeIaBLl26aiSAqhbYEnd/u11O8V0HvX53r/bK+Xtqsp9fl8+DlHsVnJ6E3I6eSUKTinmxr6Ysb/phMRlgD9mbXbFztywJlCvJQpQCuyUHjvlHWFWSCATl8oKLC24/s2nlFqu03R+AQ9MNsS4AQAA" + } + }, + { + "ID": "75f251023fb32352", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonccb74da42af2c39f1cff0f22272911e27b72e2e3/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a2f58806db2af488b649c6a29ae2a94c/6086189780885045829;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonccb74da42af2c39f1cff0f22272911e27b72e2e3/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:27 GMT" + ], + "Etag": [ + "rwXFnR/ciLuALgfHGHVONA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4108,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/7.esf,ybfm7-v6:9865,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/45,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:cf01::]:4111 had response of OK with response time of 0.014280796051025391 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybfm7-v6:9865,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/7.esf,ybfm7-v6:9865,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/7" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqSkqj3DLC9JPzvQpdfRJT/Nw9wjz93O0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZRDNcKFaKCuWC8aL5arlAgCb7vOfugAAAA==" + } + }, + { + "ID": "82645d760b512012", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "156" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "005ff289288b9a361d71e8c2060e32c0/4478128349189228397;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIDEuMyIsInVzZUxlZ2FjeVNxbCI6dHJ1ZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJueEN1QmdrRXNlYVlZSDhPQThCNkVFdkw3dFoiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:27 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/6vCQMNen4Ues5JHgeyGY2havoJg\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnhr7:4465,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/93,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=P608W_HvFMOMqgXR8aHICg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/93,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/93" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY19cGn/F3pKpNwE2IKFXBtE0UCZ3Pa3PB+IzvDKUR/71nYzdpU7V9PO/MzswOPPY0tGZ5jM41FLF0W0F5ePXAI/RaDUCStB7cI3ewdkff4QBBZolVtrpK8rvRcrH/ii3d2Q0/fZhCbi1A2DfXKRyubgcrsuM36T1q9rBmfVxlFORZwUt55jqD88W8n38bVpfpOhBAbm+vvY8X3qUTBLuJK+8aooAsmbB8XdNXUhbiXNf3+30/5TzNgBRM9Cnf6J1xfTfQi5I/AJVC/01OV6GE/hfBtxmnRDKev1nMG3FFmEECJeQUlIHHnqahdvv4T4FqUsM6Tf+RTUOdXg1Wkj3tWKtSnicsrcpu1Mg26drH0wvNg0kwDDWjbzYblSMQkuUNNSRR1tn+H+M1m0gioMUsqZd4OMJ+ZHhRggcJJLZrUcA29sDGFiRO7LmOQzq2rAVPXJLzPAKIcAI49qhtuFaMXd833CRJSIKpi62IOgPsxxTV7OPJPi2BSBgxUXDButMMZ8FFGCzH75fTIBgFozbqvmQvoF9mY4UMZ4vpUFFaYFEyrrDNwcbTMJhdDMPx525aCZhASuhhvs0UQpYVqO/HtgwhiaxE10L9qk+KlMB0PL1Cz1BMHZ7+RDZB6hLYpiEYtoldbBqOa1nOqX1FKuXLuW20v6NfK2+kN5DL8FDAU/XomVeVpFzChrA6BzIdx/R90zQs+0w1KLHBN9InA9dfUzOlKduutuvMi3i0qt7FsIOMF1D2U7VkxygQSnmVy/rfhXrH3g/K/nS9JgQAAA==" + } + }, + { + "ID": "b72c3e321b854eb7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/nxCuBgkEseaYYH8OA8B6EEvL7tZ?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a9583f68c37bf79f5311cfe9cdaff373/2942124507236371350;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/nxCuBgkEseaYYH8OA8B6EEvL7tZ?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:27 GMT" + ], + "Etag": [ + "6jwZWrV8psvrVqeunaKfvw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4159,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/87.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/87.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/87" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qsk6AMsJjxsZAbjIoqCAWNM6W5zY2tHe2USsv/dtgzxpdfvx/W+67HneWSb84TceWSTZzsN8nCVAb7YyxyULlGZUguugFxbNyDNrHtYNOt3ufRrtZfLHWhOH9N9Mx6fXIp9Q0WN72iQwWkOZaIM/nDY63incVqBfTEdfLnmjsZD7ej7eBa8/RcqkTjhaRHHQRhHpJNaVz/N2boMhdjMIQUJnMElSS1FAQwf3MqJLhlgvxYS+6PhbTfFdp50/jPRYbaNFNDVaurPAj8cRtE+HuH67C0Fo5gLbu2LV/I3HQXSci4auzS5IRcuPCCoZykYKAVuyoCc805EVZeANi5KDY5m1PzlNMeO67W9X5M9f662AQAA" + } + }, + { + "ID": "5ce0a66c9198550d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonbeeb0fe0d8c5174d079917fffaf0c704bc6209dc/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a9583f68c37bf79f5311cfe9cdaff373/11325156655742447274;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonbeeb0fe0d8c5174d079917fffaf0c704bc6209dc/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:27 GMT" + ], + "Etag": [ + "5N2RMTGt0j72MpD3qI4UCA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4032,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/6.esf,yblj10-v6:9819,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/119,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"jl\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e708::]:4095 had response of OK with response time of 0.013932943344116211 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,yblj10-v6:9819,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/6.esf,yblj10-v6:9819,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/6" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTH1MwryDXEvMcgyN/ItcDEu9DQJdXa0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNesZKcMFaKCuWC8aL5arlAgDsmcu7vAAAAA==" + } + }, + { + "ID": "1d44916e5427dd4e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "157" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f292a28976af07442a7661c4f629132b/9789151714294739667;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFRSVUUiLCJ1c2VMZWdhY3lTcWwiOnRydWV9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiN3d2YzRCanhLdEh2elZlR002WDR6cWtlZGcyIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:28 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/R5_9Y8cU7pskrvfS6dcxgmRKkhw\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnez28:4339,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=QK08W4knw4epBd3SnagO" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T227TQBR8z1dEyyuN77dKCEpiStSSB8cpBVWK1utjZ2vH63jXcdMq/87asWmBCnhcn5kzM2eSp9EYZbSI0fkYRTTd1VAd3tyzCL2VAxA4bQd3yNEzZ/YIB/Bzk2/yzWVSfJ+tV82taiqBtfa+uWTllDyr9snSjslDug2usk1zh7o9tFsf1zkBcVaySpw5tn6+Wk6cZk/Mj/cPV+Lz/vEGLr/Yt+bjLoM41Tsihzy5pkXW0jdClPxcUZqmmaSMpTngkvIJYVtlMK7sdaWs2D0QwZXf5BQZiit/EXyfM4IFZcW71bITl4QAEqigICANPI3GY9Rvn78WqCV1rNP0H9nGaNBrwVJyND62qoQVCU3rahh1sl26/vH8Qkv/2p+G4zBY+d1KaQm4oEXHDXGUD77/x3nLxgJz6DFr4iauGqlepLlRouoJJJZjElAt1QVLNSGxY9exbTywRSt44uKCFboOrh1Zsa3bqqYR1YoJ6DaxNFM3iB4R3SN2HKkYtezjyT6pAAuYUV4yTofbTAP/IvTX80/rhe/P/FkftanoH9CvwVwi5T0WU0npgWVFmcR2F5svQj+4mIbzm2Fac7iGFJPDcpdLhKhqkN+PfRtcYFHzoYb21Z4USYHFfHGJXqCoPDz5ieyCtCXQbUfQLEN1VEOzXU2zTvVLUiVemTv9z+PXzjvpLRQiPJTw3D164VUmqdawxbTNgQzbNjzPMDTTOpMNClVjW+Fh3fEyYqQkpbvNLsvdiEWb+kMMe8hZCdUklUv2lAAmhNWFaP9eaHQc/QCxLYjyJwQAAA==" + } + }, + { + "ID": "91cec1ad19736d05", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/7wvc4BjxKtHvzVeGM6X4zqkedg2?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "9f8c3b83bf5b41661e399c2b25401eb7/8181090278303954940;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/7wvc4BjxKtHvzVeGM6X4zqkedg2?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:28 GMT" + ], + "Etag": [ + "2YyS/MEkLCOniWWKSVPLJQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4482,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/111.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/111.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/111" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW2+CMBR+51eQ7nVmzhhNlvgghsxNFIWoW5ZlwXJgXIv0oEPDf19bme6lp9/l9HynZ03XSRLlPnnSyS4K9xWU9V0IuJIXB3iVIhelYDkHci/dgF4o3b332n2Ym4k1sfNou525m6X1uhqNLi5OvyHzhO8skMBBBKnPBf5QWG95peVeBvLFoPulmlsa60LRhm1b5njxX8qYr6TF2rLGhmWSVmpU/RRno1LEbOdAACXkFG5ZipLFQPFFLe1XKQXsFKzEznDQa6fIzos+PB5o34h/Zjg9nDbwPB+89U/7BPzw6k0Z9TBiubSvXXKdjgy91GFHuTZ5JDfOqBH4smQUOAc1pUv+8k5YVqSAMi6WFSiaeuI3pxG2nNZov37U/jy4AQAA" + } + }, + { + "ID": "5162106399db60f6", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon22e86b5d626011c05dce26c51423c2bc29c6db0a/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "9f8c3b83bf5b41661e399c2b25401eb7/16636460392018074128;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon22e86b5d626011c05dce26c51423c2bc29c6db0a/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:28 GMT" + ], + "Etag": [ + "pI3jfm8G4H99q+tK2CNSQQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4335,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybga23-v6:9893,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/56,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:da13::]:4372 had response of OK with response time of 0.013057231903076172 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybga23-v6:9893,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybga23-v6:9893,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqSnwNM5Ky7VwN/GwtCzULvE2cvYLDgy0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNJUWlqUpw0VooK5YLxovlquUCAH+xHMG9AAAA" + } + }, + { + "ID": "1e9813db77f9f601", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "158" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "28fd55376932790429c8c1953b1292e8/15028398956044066361;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICdBQkMnIiwidXNlTGVnYWN5U3FsIjp0cnVlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6InB5bmw5dDVwNVZCQk9pRHBlc1JvQU5YZHpuOCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:28 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Bfa4cMqiB5ZUuVw5SoAR3mMYzQk\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnxx66:4237,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=QK08W-mbI9W7qQXv7o-ICw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T226bQBR891eg7UNeGoO5LpGq1hcaWUpcFeM0jSJZy3LAG2MWs0ssJ/K/d8GmSZuq7eMyM2dmzhHPPQ2tWZGgCw3FLNvWUO3fPfAYvVcASJI1wD3yzLU3eYI9BLktVvnqMi3uJsvF7taw9VFKbHq9ZSPnblHf7Jw5H4bW5vr709f1PWrnsHZ8UucU5HnJK3nuuebFYt4v90XuS6d0bkajL2xSggj5cHabPBW4FQrI0ytWrBv5SspSXOj6brfrZ5xnOZCSiT7lG70Lrj+aelnxB6BS6L/Z6aqU0P9i+DHnlEjGiw+LeWuuBCGkUEFBQQV47mkaOk2f/qlQI2pVR/Qf3TTU+TVkZdnTDo0r5UXKsrrqoNa2bXd6vLzQPLgKxpF2NhyNz9qZKhMIyYpWHJE474L/T/RGTSQRcOIsKU6xERt+PMBxapgppI5nUzAcA4Nj2JC6CfZcl3Rq2RgetaTgRRwbHh4kPqam6cRJnGCX+tiwMSa2paDEwCS2PUCN+nCMTysgEiZMlFywbjnjMBhGwXL6eTkLgkkwOVXdVewN9Vs4VcwoXMzGSnIilhXjituubDqLgnA4jqY3HVoLuIKM0P18myuGrGpQ3w+ncwhJZC26OzSvZqVIGcyms0v0isXU4ulPZlukOQLbtIKBoyob1sDFrm8c769ElXyLe651xH89emu9gUJG+xJejo9eZVVNqiVsCGt6IMt1Ld+3rIHtnKsLSmPAN9InpuevqZXRjG1X23WOYx6v6k8JPELOS6j6mRryyCgQSnldyOb/Qr1D7weeaPcsKAQAAA==" + } + }, + { + "ID": "8cc502d904ce2448", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/pynl9t5p5VBBOiDpesRoANXdzn8?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c92dd904f5d7544fe31c4f25cb1332e8/13420338623843033954;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/pynl9t5p5VBBOiDpesRoANXdzn8?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:29 GMT" + ], + "Etag": [ + "FtV2+B21iqMfJVCKC6yi2Q==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4095,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/31.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/31.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/31" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+51cs9VFJYAl4SXhgeEMRZchiYowZ3RkWu7a0ZyGT7L/bliG+9PS7nJ7vdNcKAvLNREauArJkq00JujpZAc7cJQZTcjS2KCkMkDPnBkxXzn2LSXgahV22ecofktHjqF+xcDYY7F2GfkGRWt/OIotzBjwzFr97HDS810RagHsx73z65obGSnl6/hqPp3f/lUJmXpkuJpNhNLkhjVT7+mHP2odYy2UMOWgQFI5RlJZroDj2O2clp4BtJTW2z/thM8V17nVVCX6JPdVLouiZXSswsRxO37IfcXHwcklTZFI4+2JO/qajxJTHcuu2Jl1y5KIKwbxoScEY8FM65JB3JAvFAV1c1CV4mqb2M+8ZNlyrbv0Cs9tKqrcBAAA=" + } + }, + { + "ID": "c2fe71f32b213d4c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonbb0781d98c225bdbd86c980488a43078d08ab47e/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c92dd904f5d7544fe31c4f25cb1332e8/3428963568647718518;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonbb0781d98c225bdbd86c980488a43078d08ab47e/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:29 GMT" + ], + "Etag": [ + "uMcvGOeLRKzt5TKlirirUA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4496,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/25.esf,ybw128-v6:9875,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/127,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:2cc3::]:4073 had response of OK with response time of 0.014798879623413086 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybw128-v6:9875,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/25.esf,ybw128-v6:9875,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/25" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqSn1TS5z90/1CfKuKjEN8c7JLMosCnW0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNjk7OSnDBWigrlgvGi+Wq5QIAAOl3q7wAAAA=" + } + }, + { + "ID": "842f22085f96d32c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "173" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2d7f156c08e1b39964fe72c6893ae3ad/1820903232168561311;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIENBU1QoJ2ZvbycgQVMgQllURVMpIiwidXNlTGVnYWN5U3FsIjp0cnVlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlZRM1Q5U3B5ZEhWUHczV3Nxd2RkSVJQbXF0bCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:29 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/57XaazJTVCxZHiBbfnO7bUZTrAk\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndd73:4003,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Qa08W9HZEcTNqAXfvonACQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/90" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB8969A14e0UmOObxOpah2bJkSRmxrspFEk6zgv+GLMYTjiOpH/ew9smrSp2j4eM7Ozs7s8dRS0ZNkcnSgoYsm6gmL75p5H6L0EQJCkBu6Qoy+d4SNswUvNcpEuzuLsdjibbG6wqVrODSGPF+F08P32nJ1GcfbFiSa3YdFf3qGmDmvKz6uUgjjOeSGOHVs/mQTd6VcjdIN8Oz+fXm2M63K9mc/98dVqLdJGWEIaX7JsWcsXQuTliapuNptuwnmSAslZ2aV8pbaNqw+6mhf8Hqgo1d/sVBmqVP9i+DHllAjGsw+ToDGXgjHEUEBGQTbw1FEUdKju/ylQLWpUe/Qf2RTU+tVkadlRdrUr5VnMkqpooca2SXd4PL9Q4F16g1AZ9IPw7VHM+ZHSD5TTb6EXvGssZItQCpY1tUISpW2O/0lSq4kgJRw4M9qLezjCbqT1ohjrMcSWY1LAFu6BhU2I7XnPsW3SqkVtuNeSjGdguBGhdg90l1LQHMdwIpu4kWtjU8cUExu7mubqqFbv9u3TAoiAIStzXrJ2VoOx1w+9mf95NvK8oTc8RN0U7BX1euxLZjiejAZSciDmBeOS20zQH4XeuD8I/WmLViVcQkLoNlinkiGKCuT33WE7pSCiKtu11K96pEgajPzRGXrBYnLw9CezCVIvga0agWYZ2MGGZrum5ezPQYoK8Rq3jMNh/XoDjfUKMhFuc3i+BfSiV5mkmMGKsDoHMmzbcF3D0EzrWG5QYI2vhEt0x11SI6EJWy/Wy7QX8WhRfZrDA6Q8h6KbyCIPjAKhlFeZqH831Nl1fgBWKYzfNwQAAA==" + } + }, + { + "ID": "5d562d22664c73c0", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/VQ3T9SpydHVPw3WsqwddIRPmqtl?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7bddacbb18e0284eca2f568b1720eb72/284898290704076744;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/VQ3T9SpydHVPw3WsqwddIRPmqtl?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:29 GMT" + ], + "Etag": [ + "e6ckao9hodT4+DrBlPOcaw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4495,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/105.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/105.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/105" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q32vCMBB+968o2eMmuDkcDnxYN0FBtlqrY4wxYnJqNe3V5Eop4v9uEuvcSy7fj8t9l0MrCNguzSV7DtgyXe9L0PXNGmjqLjGYUpGxpcDcALtzbiC+dm7oiR3H/gZl8nj7pkMVfQheDQZnlxEbyLj1HSyyeJWCksbib4+DhvdazjNwL646v765oakuPB1+JcPZfyFD6YX3+WTyEk6GrJGOvv7Y8+gzbHEZwwo05AKuSQqNWxA09ivLUgmgdoGa2k+9h2aK6zzri2k36c+KWo4WUdX9NPtKynEcZXtSF69CwSnF3NnnM/Y3nZC4irFyS7N7duXCmsBEGgUYA35Kh13yvmJWKCAXl3QJnhbc/uUopYZrHVsnSX5cv7YBAAA=" + } + }, + { + "ID": "1ed571ac926d3c60", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anone39bac68e29cce17737b6a9b960420c0a6091192/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7bddacbb18e0284eca2f568b1720eb72/8668211909908672988;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anone39bac68e29cce17737b6a9b960420c0a6091192/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:29 GMT" + ], + "Etag": [ + "aDhYfSWqoJfe3/Qf3LpEJg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4335,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybbj203-v6:9883,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/36,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"iw\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f4c3::]:4080 had response of OK with response time of 0.014663934707641602 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybbj203-v6:9883,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybbj203-v6:9883,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqUl0yYhMCw4vzPdKSzXWD0wz9ilw9Uq3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNUbmWZUpw0VooK5YLxovlquUCAKoChFa9AAAA" + } + }, + { + "ID": "7ba839ab21ff7701", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "185" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2653ba558a57a188a776862aac565564/7132206968444188165;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFRJTUVTVEFNUCgnMjAxNi0wMy0yMCAxNTowNDowNScpIiwidXNlTGVnYWN5U3FsIjp0cnVlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6ImY3UWU0UWFyUHFNTm5Za1VrWDdFQWNXZkVuayIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:30 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/8FY8inGwyEUfLKnuNQMQgBHXlzs\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vni65:4255,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/80,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Qq08W-DaAYWOqwWy47aYCw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/80,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/80" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY14e0UsHnbxupamlwUlRAAUyTVJHQ+VibC8Zn7HMQifjvPRvcpE3V9nE9Mzs7u+enloLWLF2iroJCFm9LyPdv7nmI3ksABIkr4A45+trpP8Ie/MQsVsnqMkq/9xfz3Q02Vffi1mXp5W7vz6Ph17QcT0aT+POXm+SxuEN1H1a3X5YJBdHOeC7ajq1357NO5EzAnJD8ajsap7fr+frG8Xv0OvLTdS0sIImGTBZSvhIiK7qqutvtOjHncQIkY0WH8o3aDK4+6GqW83ugolB/s1NlqEL9i+HHhFMiGE8/zGe1uRRMIYIcUgpygKeWoqBT98GfAlWiWnVE/5FNQY1fRZaWLeVQuVKeRiwu8waqbet0p+K5QjN/6J8HSjAY+bOgN7p6e6ZjzW5jo61jRbO62Oxi6+xdbSfHhUKwtO4bkDBpMv1PqkpNBCngxFlQN3JxiL1Qc8MI6xFElmNSwBZ2wcImRPbSdWybNGpRGR61JOUpwbZrhsQNQ83xTEJ0rDs0dFzL8PSlE4KBjZAasESV+nAcn+ZABPRZkfGCNXs7n/q9wF8MLhZj3+/7/VPUXc5eUa+nA8kMpvPxuZSciFnOuOTW2xyMA3/aOw8G3xq0LGAIMaH72TaRDJGXIL8fTpcqBBFl0ZyoqqqVImkwHowv0QsWk4unP5l1kOoIbFMLNMvADjY0B2ve6RFJUS5e47pjH/Ff30NtvYFUBPsMnt8FejGrTJIvYENYlQMZtm14nmFoptWWFxRY4xvhEd3x1tSIacy2q+06cUMerspPS3iAhGeQd2LZ5IFRIJTyMhXVr4dah9YPpoNYxUMEAAA=" + } + }, + { + "ID": "a0fad3908a56cfd2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/f7Qe4QarPqMNnYkUkX7EAcWfEnk?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5dd3e2dfcff7afb38396c2c76f14930c/5524145536748305198;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/f7Qe4QarPqMNnYkUkX7EAcWfEnk?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:30 GMT" + ], + "Etag": [ + "fg3h0jXqHDTTRqI1LChQ6A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4472,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/19.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/19.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/19" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW2vCMBR+91eU7HWCbkNh4EN1BYVWbK3MMcaI6WntLWmTU4aI/31JrHMvOfkuJ+c7OQ8ch5Q5T8irQw551nYgTw8ZYGguEaiuQqVLI7gC8mjcgDQz7jR7Po6Kfbt8i+OoXY39xTGcuLPZ1aXYEWqqfWeNNE5zqBKl8afFTs9bjdMa7Iujb9vc03hqLB2vAm8bu8Hmv1iLxIrrne+7c98jvXSx9UufF5ujEIcIUpDAGdzTNFIUwHBl1066igEOGyFxOJ089VNM51VPpyG8hFRu2mDNP8pduZ96LntPPV7evJVgFHPBjX23JX/TUSCtIvFjFidjcufmJwS1kYKBUmCnjMgt70LUTQVo4qLswNKM6v9c5thzg8vgFwqhWem6AQAA" + } + }, + { + "ID": "9194937211adf578", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anona0684ba8bb1794aa2027cb785392d7be303bc3ed/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5dd3e2dfcff7afb38396c2c76f14930c/13907177685271158082;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anona0684ba8bb1794aa2027cb785392d7be303bc3ed/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:30 GMT" + ], + "Etag": [ + "W0xTYAqjW6brY0lltz61AA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4355,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/21.esf,ybna17-v6:9807,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/106,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"na\" allowed_cluster: \"wo\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:c096::]:4177 had response of OK with response time of 0.015369892120361328 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybna17-v6:9807,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/21.esf,ybna17-v6:9807,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/21" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQk3qAiJdCzMCjdLKoo0yMkpqTIzdHS0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNJqYWJhZmRiamegZKcNlaKCuWC8aL5arlAgCjBYD9xQAAAA==" + } + }, + { + "ID": "8c3dde93a1555150", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "191" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6df9b9ad7eccd4780f7df31d2a1090c0/12371172743806673515;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIERBVEUoVElNRVNUQU1QKCcyMDE2LTAzLTIwIDE1OjA0OjA1JykpIiwidXNlTGVnYWN5U3FsIjp0cnVlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlRPemxOeTFrTFJBd2swQ0JvTTJUenpqM1l3SiIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:31 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/qozevucEJwaxg1gdyuV08QTPU0Q\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vns132:4112,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/60,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Qq08W_jUKoixqQW6x6ugCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/60,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/60" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB8969A9CGpVJvjGyxVbWrTyJHjJjZOPxTJOo4FX4w5DIepHfm/98CmSduoLS/omJmdnd3jsSPJK5qGcl+SAxpvSsh3rx5YIL8RAHAc18C9bGsre7iHHXiJUSyT5WWUfhsu5tUXZCgbtodtSbyrCn+P1TjclXfIufVv5uj2Xm7q0KZ8WCYEeDdjOe/altafz3r+p30y2amr8fSiWqHBB3at+fv9g/61umqEBSTRmKarWr7kPCv6ilJVVS9mLE4AZ7ToEbZW2saVraZkOXsAwgvlNztFhCqUvxi+SxjBnLL07XzWmAvBFCLIISUgGnjsSJJ8qj56KVAtalRH9B/ZJLn1q8nCsiMdalfC0ojGZd5CjW2T7nR4Oskzb+wNfGl44Xvn/ujam/kX1zfnZxpSrS7SuxqSVLOPjD4yz16/bkxF01BwmjbVfRwkbbL/yVarMccFnDgL4kQOCpAbqE4QIS2CyLQNAshEDpjIgMgKHduycKvmteFRi1OWGgEB24lQ6GiuGRqGG4mHuFjVXd0Sn8QrjAwcybX6cGyf5IA5DGmRsYK20xtMPTGCxejjYuJ5Q294ilrl9A/q5+lIMP3pfDIQkhMxyykT3Gamo4nvTS8G/uiuRcsCxhBjspttEsHgeQni++G0r4JjXhbtoupTPVJZGExGk0v5GYuKwZOfzCZIvQS6bgSqqSMb6aqNXNU6XhAhyvkLuGMf8V9vRWO9hpT7uwyebof8rFeRJF/AGtM6h6xblu66uq4aZldskCOVrbmLNdtdET0mMd0sN6vECViwLN+HsIWEZZD3YlFkSwlgQliZ8voHlDuHzg+yz1vzSQQAAA==" + } + }, + { + "ID": "7474ae45e84bda6c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TOzlNy1kLRAwk0CBoM2Tzzj3YwJ?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cb000cb0e31a4fc5bace15a9d8dd1cfb/10763112407310739348;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TOzlNy1kLRAwk0CBoM2Tzzj3YwJ?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:31 GMT" + ], + "Etag": [ + "bbIZ1OxOvI6xpDUcRcA/VQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4448,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/109.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/109.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/109" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qtEwAQTEx4YGp2ZTMpmosaYrTtwW7fO9SYMsv+dtgzxpdfvx/W+675nWSRLipjcWiRK1j81VM3FGnChLxRkzVGqUopCArnUbsBwbdyR8z70tt6vM96WdwGjbHr1uphMji7JviEPlW+vkMKrBHgsFf4w2Op4oxVhDvrF1eDLNHc0NqWhlz515g//lVzERpkHrju13XvSSa2pn+psTYhURBRWUEHB4BylrEQKDB2zc1xzBtgvRYX9m/Gom6I7j7rv7fi8GWYunW6ywcwWzyN/t0uv3zZPJy8XLMREFNoeLMnfdBQYcio2emsyJGfObhDkSyUYSAlmyoCc8s5EXnJAHRerGgzNQvWZjwl2XK/tHQCGQrf6twEAAA==" + } + }, + { + "ID": "705b45b788c555d9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon4bce78f0d8295d449ffffc9a13936295393df4af/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cb000cb0e31a4fc5bace15a9d8dd1cfb/772018822813944232;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon4bce78f0d8295d449ffffc9a13936295393df4af/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:31 GMT" + ], + "Etag": [ + "xXGiYCfHPWequO53xyJvPg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4184,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29.esf,ybhx92-v6:9864,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/22,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"wo\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e988::]:4130 had response of OK with response time of 0.012307405471801758 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybhx92-v6:9864,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29.esf,ybhx92-v6:9864,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqamIcM+MdE7zCAhPLSz1NzWuqPQqC0i3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBkpwuVooK5YLxovlquUCAK52FDvDAAAA" + } + }, + { + "ID": "de41e5b64b68be5b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "191" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6d473bc83890e3f92db8fb36b5faa031/17610421085050851025;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIFRJTUUoVElNRVNUQU1QKCcyMDE2LTAzLTIwIDE1OjA0OjA1JykpIiwidXNlTGVnYWN5U3FsIjp0cnVlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IllXS1ZtUzhGSXZUVVpYM3puRFE2QnFFSG9KRSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:31 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/CNez2B3Jej1Jm4IBhh0OLmGReuQ\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnap199:4017,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Q608W9jrGNLtqgWJopWwDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/130" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB8969A9CGJVJvj68CWqjaxSUrquI2NkzSKZMF5wRcDh+Gw5UT+7z2wadI2avuCtOzMzs7u3nNLkpc0ncs9SQ5otCoh3757ZIH8XiSA+1GVeJAtbWkNnmALTmwUi3hxEab3g9l0c4cMpT+CJ+1Mv4RH9TIx3LPFAn0dJhdjKK8f5LoOrcvPy5gAb2cs520La73ppPP99stNMrHP3bU3vb/Tn9LBNT5bOZ/ZpVMTC4jDIU2XFX3BeVb0FGWz2XQixqIY/IwWHcISpWlcWWtKlrNHILxQfpNThKlC+Yvgx5gRn1OWfphOanFBGEMIOaQERAPPLUmSD9XdtwxVpJq1z/7DmyQ3ehVYSLakXaVKWBrSqMybVC1buzsEL5E8cYZO35M898o5rj4T7/Tq2/GRhlTcRnpbQ5Jq9pDRQ+bRyUktKpqGgtO0ru75Qdw4+x9vFdvnfgEHzIzYoY0C1A1UOwiRFkJoWgYBZCIbTGRAiOe2hbHfsHkluOf6KUtJAMi2bIznYYjRHLBKbAObczF0GxOhGoh6vt6VK/Zu3z7JwecwoEXGCtpMrz92Tj1n5p7PRo4zcAYHq5uc/gG9HbsC6Y2no76gHIBZTpnA1jN1R54zPu177k2TLQsYQuST7WQVCwTPSxD/d4d9FdznZdEsqoqqkcpCYOSOLuRXKCoGT34iayPVEmhSE1RTRxbSVUvFCO8PRJBy/kbePpzar1dRSyeQcm+bwct1yK96FU7yGSQ+rXzIOsZ6t6vrqmG2xQY5UlnCu75mdZdEj0hEV4vVMrYDFizKT3NYQ8wyyDuRKLKmBHxCWJny6gHKrV3rByUZA/VJBAAA" + } + }, + { + "ID": "30049baff00b53f4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/YWKVmS8FIvTUZX3znDQ6BqEHoJE?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "30d7f032462ebd1a01972de9c80a86fd/16002359649060066298;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/YWKVmS8FIvTUZX3znDQ6BqEHoJE?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:31 GMT" + ], + "Etag": [ + "zxKqu8TziQv/VyXqKpn0jg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4108,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/33.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/33.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/33" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+369Y6qvEqQkSEx5EUXALcRcQNcaM7jA3t3Zbz9CN8N9tyxBfevpdTs93ujVMk3wlLCLXJlklcVlD1ZzEgK66eCDqDIUsBWcCyKlyA4axcrc/dlkPgjZxN2eLZlnaBbPSeDjcuwT9hDyUvq1EEq8TyCIh8ZvGZsdrjYU5qBfX1odu7mhsCk37gTedPfxXch5pZTZ3nJuRMyadtNP1XZ47HSLlKw/WUAGjcIxSVDwFilO9c1RnFLBX8Ap7V/2Lborq3Osvz/Yi9wf3000wf11etuzO7Y/K8YQ/jg/ejNMQE86Ufe6Tv+nIMcw8/q22JufkyI0aBPFUcQpCgJ5ikUPeW54XGaCKi1UNmqah/MxJgh1n7IxfI/Jk67cBAAA=" + } + }, + { + "ID": "c9bed6ad8d4d4da7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anoncbe087866dff60de61c8465defe86c762bf57a39/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "30d7f032462ebd1a01972de9c80a86fd/6010985697671279886;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anoncbe087866dff60de61c8465defe86c762bf57a39/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:32 GMT" + ], + "Etag": [ + "2JcltEdI8YDP4OgWd2zhlQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4367,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/24.esf,ybn130-v6:9887,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/63,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:1119::]:4002 had response of OK with response time of 0.014032125473022461 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybn130-v6:9887,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/24.esf,ybn130-v6:9887,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/24" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTHySs4pcU3xtIh0CTDxTw9PMarKyAm0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNplYGJlYGpkpwmVooK5YLxovlquUCAHJFy+7BAAAA" + } + }, + { + "ID": "e0db1ec0c9348b16", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "257" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c4ea58f47353dfbdc981cac805d587ba/4402924261680495159;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJJTlQ2NCJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJ2YWx1ZSI6IjEifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6ImhnTzdkQ3lnZnl1VVFQZWF1bGRhbk1DVGxINCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:32 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/qjFGb6OOa4bsGilpmIqxQt-yctI\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnkj10:4176,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/86,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=RK08W-C2DcfxqwWB3a6ABA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/86,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/86" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U226bQBB991eg7WscruYSqWoim7iWUiexcVq1qaxlGfDGa8Cw2HUj/3t3ueTSVG1fEMM5Z87M0YjHnoLWNI3QmYJCmmwrKA7vHrIQnQgAOE4kcI8cY+2MfsIBfGaVK7Yax+nX0XKx/6JZ6vbhchza19fYCssxZflmsv1xy/sHwif3qO5D6/ZRxQjwfp4VvO/YxtlifrpKrp1oeEjiQ7W4vQFcsQinn4YB+2jVwhJYfEXTtZSvOM/LM1Xd7/enSZYlDHBOy1OSbdRucHVnqHmRPQDhpfqbnSqWKtW/GH5gGcGcZun7xbw2F4IZxFBASkAM8NhTFNR2n/xpISmqVQ36j90U1PlJsrDsKUfpSrI0pklVdFBtW2/XFs8VmvtX/jBQzneY1S3FSFBymtbaAIesm/t/JpdqzHEJLWdJ3NjVQs0LdTeMNSOGeOBYBLSB5sJAsyC2I9exbdypuTRstDjNUht7RAcDO3YcmUasa54Zm6BFoWvblmUbAy/EjmZ7SKqPzfikAMxhRMs8K2mXzXDmXwT+cnK5nPr+yB+1q+4L+ob6eTYRzGC2mA6FpCXmBc0Et05sMg382cUwmNx1aFXCFSSYHOZbJhgxZiWcPMd8gwu8AQ5FKcBv9aJNoApKBSB7PqUvvTp6cMifw5fhNLUcwLZQ8/X4RnWHWfVKtms/IL3T9J6e33v1S301Jce8KrtzkVWtEkFMJ9MxesGi4kDIE7MOXB4LbXbRB6bmaKbuGKbbHrQQFfwtPtDsBn99m7X1BlLeBtDeKHoxq0i8WMIGU5k3Mm3b9DzT1K1BX1wa1/Rswz1sON6amAlJ6Ha1XTM3zMJVdR7BDliWQ3GaiCY7SgATklUpl78B1Dv2fgE7/MUhzwQAAA==" + } + }, + { + "ID": "aff2f1b81e1a3a09", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hgO7dCygfyuUQPeauldanMCTlH4?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f62307abf33c2a4fea9b573e0c77b802/2866920419710926687;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hgO7dCygfyuUQPeauldanMCTlH4?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:32 GMT" + ], + "Etag": [ + "RgJftbTBbIrrZhP/Wup7GA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4169,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/20.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/20.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/20" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30vDMBB+319R4qvDKWJB2MM6ylaZs6sdgiKSpteuM21qckHK6P++JOucL7l8Py73XQ4jzyPfVZOTR49kVfmjQXZXJeDGXhJQmqMypRWNAnJt3YC0tO6kfCowS4MskvJ9F9+86dZfzKbTk0uxHdTU+A4GGVxUwHNl8IfD3sA7raE12BeLyZdrHmjsWkdH6zRchMl/qRa5k9bb1WoWrEIySL2rn+bsXYq9yBIoQELD4JKllWIPDCO3dK45Axy3QuLYf7gbptjOk74rX/x83pVFp7ebGKjmOW2e5ylf3p+9XDCKlWisfftK/qajQMoT8WvXJrfkwgUdgoqlYKAUuCkTcs47F3XLAW1clBoczaj5zWWFAzfqR0dIqwSfuAEAAA==" + } + }, + { + "ID": "0faa71aab6c153ff", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon6a9c1e2a76fd32f1093f3e0db866446259ba7069/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f62307abf33c2a4fea9b573e0c77b802/11250232939420606836;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon6a9c1e2a76fd32f1093f3e0db866446259ba7069/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:33 GMT" + ], + "Etag": [ + "bq8ShEXKFYvN3M9OQMIAzA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4092,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31.esf,ybkg14-v6:9820,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/76,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f0d7::]:4151 had response of OK with response time of 0.014510631561279297 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybkg14-v6:9820,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31.esf,ybkg14-v6:9820,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/31" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqym0CM5wjfB2iyzzM/a19A/09XSscrS1hagqyS9JzAnKLy8GKTWEiBVBuNFAtoJCNZgEiqbBhZCFwVJlEM1woVooK5YLxovlquUCACJEGfm6AAAA" + } + }, + { + "ID": "afdc0edbc0969689", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "261" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a70d22babb19014cc71504fc02f12c37/9714229097450972829;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJGTE9BVDY0In0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoiMS4zIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJaYTdYdE5LV2RtQnVSQkNNT0xSa3NpT2xVUDkiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:33 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/AEqL5bTom26_5jOBFP1MTiJD-U0\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfj19:4381,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ra08W5jvAoaKqgXTyqTwBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U227aQBR85yus7WvA9wuRqiYBJ6IlEIFJojQVWq93zQbjNd41iEb8e9e3XJqq7Yvl45k5c87oyM8dBaxpGoFTBYQ03hY4P3x6YiE4kQAWMC6BR+Aaa3f4Ex+wn1h8layuSPowXC7295qlnvvbsR0GbGM4S/tpenF5o18H9Ouwu9AeQdWHVu2jIkFYdDOWi67rGKeLee8Buvdi8u0u2lwUs4vB9XQ8W3M6TRY3/UrIcULGNF2X8pUQGT9V1f1+34sZixMMM8p7iG3UdnB1Z6hZzp4wElz9zU6VS3H1L4ZfEoagoCz9vJhX5lIwwwTnOEVYDvDcURTQdB/9aaFSVKlq9B+7KaD1K8nSsqMcS1fEUkLjIm+hyrbarileKzD3x/4gUM52MKlaypEwFzSttAEMk3bu/5m8VEMBOW44S+QRTwu1fqh7IdEMgontWghrtuZhW7MwcSLPdRzYqkVpWGthKreIHOjKDkQjFtRDgi3LJa5hWroFI0QI8fqeFVk2KNXHenyUYyjwkPKMcdpmM5j554G/HF0uJ74/9IfNqvucfqDezUaSGcwWk4GUNMQsp0xyq8RGk8CfnQ+C0W2LFhyPcQzRYb5NJIPAhOOT15hvYA43WOCcS/B7tWgdqAJSCZQ9X9IvvVp6cMhewy/DqWtwOZ6eB44F6u/HD7pbmBTvhLvmA9B7ZqvqvDx/dKqX6nK4gKLg7cmUVaWTYUxGkyvwhkXlkaAXZhV6eTC03ke3Tc3VTN01dcetT1WKcvERNzytxt/fZ2W9waloQmjuFLyZVaaeL/EG0jJzYDqO2e+bpm7ZXXltQtPZRvSh4fbXyIxRTLer7TrxQhauirMI73DCMpz3YtlkRxGGCLEiFeWvAHSOnV/mOgTn0wQAAA==" + } + }, + { + "ID": "69c1114f02e34cef", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/Za7XtNKWdmBuRBCMOLRksiOlUP9?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8d6cd089b083544975eccda1aa322983/8106167661460253637;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/Za7XtNKWdmBuRBCMOLRksiOlUP9?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:33 GMT" + ], + "Etag": [ + "qnh5ZXdrOQG8Wp2Q6Fk5AQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4166,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/71.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/71.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/71" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+31+x1FdJkARQEx42ImqcjE0JBGNM6Q4YdGtpbzEL4X+nLUN86fX7cb3vevB8n+zyMiOPPlnm630Fqr5ZAyb2koKuOGpTpCg1kFvrBqRr696Xm+5inqk4eb6fyU7SG+26QTIYnF2abaCgxncwyOBVDjzTBn857De800pagH1x1f5xzQ2NtXT0KIqDz/9CITInjKdRFITRE2mko6vf5jy6DFuxTGEFCkoG1yRSiS0wfHUrZxVngC0pFLb6vU4zxXae9QXtz3H8NsuKsErD4XscpTudx3w6ebh4uWAUc1Fa+/SD/E1HgZSn4tcuTe7IlQtrBD1RgoHW4Ka0ySXvUBSSA9q4qCpwNKPmL19ybDjv6J0AFvhfjLYBAAA=" + } + }, + { + "ID": "c23b13e5765bf825", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonfd6a7f80f0f4a1bfe447f723414adcfff8984d45/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8d6cd089b083544975eccda1aa322983/16561256304509341146;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonfd6a7f80f0f4a1bfe447f723414adcfff8984d45/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:33 GMT" + ], + "Etag": [ + "xrJk0GebkxdpykUugJRu6g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4319,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14.esf,ybkf7-v6:9895,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/59,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"wo\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:312::]:4013 had response of OK with response time of 0.012949228286743164 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybkf7-v6:9895,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14.esf,ybkf7-v6:9895,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/14" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqako8so2cE9Nyq5IKajMDi1N9woqNUu3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNesZKcMFaKCuWC8aL5arlAgACFUO0vAAAAA==" + } + }, + { + "ID": "25aca3837116e1bf", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3235b333c0d9aba69451ad91bbd5b3bc/14953194872813457923;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJOVU1FUklDIn0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoiMS4zMDAwMDAwMDAifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IjhWSkJWVm56bEtFVWlDbWIxOTI1OVU4c01ERCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:34 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/PjRDDytrszaVXeYpEsZMtrDwclc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnq142:4003,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ra08W4mXL4-NqwXt2aDwDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ua2+bMBT9nl+BvK9tgPCuNK1dYFO2NqryWtd1ioxzIW4IJtgkSqv89xkH+linbXxA2Oece+49uuKxo6EVzRfoTEMxTTcVlPt39yxGJxIAgdMauENeb+WFD7CHKLP5Mlt+TvLbcD7d3Ri2fn0/CsO9KPkDnt3A9yLit1eiDHckI3dI1aGq/KLKCIjTgpXi1HN7Z9Nx1599+Tib5Q/Z12hK++vYDHpOMPX5VRgqIYcsuaT5qpYvhSj4ma7vdrtuyliaAS4o7xK21tvG9W1PL0p2D0Rw/Tc7XQ7F9b8YfsgYwYKy/P10rMylYAQJlJATkA08djQNNdUHfxqoFinVEf3HbBpq/WqytOxoh9qVsDyhaVW2kLJV0zWH5xMaR5dRf6Kdb3GmSsqWgAuaK+0Ex1nb9/90XquxwBwazpz4iW/ERhCbfpwYvQQSx7MJGI7hg2PYkLgL33Nd3KpFbXjU4pzlxAHLtnzPsmzbDjD2DNN2Tde3nATHiW06HiTECVxUqw/H9kkJWEBIecE4bbPpj6KLSTQffJoPoyiMwmbUXUnfUL+NBpI5GU2HfSlpiEVJmeSqxAbDSTS66E8GsxatOFxCisl+vMkkI8EZh5PnmK9xidcgoOQS/KEGPQaqoVwCdc2n9Guvlj7ZF8/h1+Ecz2g4vYpGgz463h/e6GY4q14Jt80FMruW0TytuvP0/tlRH2qDuMCi4u3q1Cell6EMB8PP6AWLymUhT0wVfr049DiX6ViGZ1imZ/m+f1xZKSrFWzzwGvz1nirrNeSiCaPZV/SiV5l+OYc1pnX2yHJdKwgsy7SdU7l1wjDZWgS45wUrYqUkpZvlZpX5MYuX1fkCtpCxAspuKotsKQFMCKtyUf8SUOfQ+QX8XobW2wQAAA==" + } + }, + { + "ID": "322b892288d0ad47", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/8VJBVVnzlKEUiCmb19259U8sMDD?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1214626592090eb6d05b7eff85d8c660/13345133436822738731;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/8VJBVVnzlKEUiCmb19259U8sMDD?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:34 GMT" + ], + "Etag": [ + "PQVVFs1UNNOXdxPGL9cN9w==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4076,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/74.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/74.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/74" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qskQKIyEx4cTEXHhJktJsaYrrvhsGvnegtOwv9uW4b40uv343rfdddzHPJZiIxcOyQt1l8N1O3ZGnBlLhGohqPSpZJCATk3bkC6Nu7lKklu1TAOw6eX7Ht5F7gsdLeTycGl2AeUVPt2GmmcF8AzpfGrxU7HW03QEsyL+eDdNnc0tpWlw3jhR/Ppf6mUWScFwY0X+KST9ra+6XNvU2xkGkEONQgGpyxVLTfAcG6XzhrOAPuVrLF/dTnqppjOgz5OHrwkET/80Y+LaZkO3dGFG4/VYjY7erlkFAspjD1+Jn/TUSLlkdyatcmQnDivRVDLWjJQCuyUATnmncqy4oAmLtYNWJpR/Zv3BXZcb9/7BXToxJm4AQAA" + } + }, + { + "ID": "8c55e31a0b8e1b0d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc5e34387334449aa7014616835fabf4157efc596/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1214626592090eb6d05b7eff85d8c660/3354040951820728640;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc5e34387334449aa7014616835fabf4157efc596/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:34 GMT" + ], + "Etag": [ + "a3MItnbUT0Fx5NCNuMyH8w==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4387,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0.esf,ybbt9-v6:9811,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/23,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f8d9::]:4080 had response of OK with response time of 0.013611078262329102 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybbt9-v6:9811,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0.esf,ybbt9-v6:9811,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqUk09vUsyUsKDTFwqzD1c/Yr9a30sCi3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNesZKcMFaKCuWC8aL5arlAgD2drQVvAAAAA==" + } + }, + { + "ID": "35a5c3e171b6cd69", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "259" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6c53a645c55cffdd53ba59633becec1b/1745979515830009448;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJCT09MIn0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoidHJ1ZSJ9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoidXRyTDhTMXJPZmlVZzNHOXpXTnljT3k1TFdYIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:34 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/mFmGcgmuk8VA-wVlmrCEoeMGB40\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbc206:4400,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Rq08W6n4GYiOqQWKlL3gDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ua2/aMBT9zq9A3tdCEhLyqDStLaQIicHEq9XWCTnmOrgkMTgOiFb89zmvPtZp25ck9jnnnnuPrvLcaKItS9bosokCFu4zEKdPjzxAFwoAicMceEBOZ+v0n+AEfmSlm2gzoMn3/mpxvNctLb6NBySMs627vG4dl1Esej6Hr4MbS39ARR1WlF9nEQHZ2nEhW47duVzM2pkUI3dmiAlli9AceE934xOZnLqju/tCmEJERyzZ5vKNlLv0UtOOx2M75DyMAO9Y2iY81urGtUNH2wn+CESm2m92mhoq1f5i+CXiBEvGk8+LWWGuBFOgICAhoBp4bjSbqKo+/NNAuahQleg/Zmui2i8nK8tG85y7Ep5QFmaihgrbYrrq8HpCM3/k9+bNqwOOipKqJUglSwrtHAdR3ff/dJ6rscQpVJwVcamrB7oXGG5A9Q4F2nUsAnpXd6GrW0DttevYNq7VMjcstTjhiUeJqVPwKAWr0w0sy7BcYtvEIBRTg3Rcy10HAbZQrj6X7RMBWEKfpTuesjqb3tS/nvur4e1q7Pt9v1+NehTsA/VuOlTM+XQx7ilJRdwJxhW3SGw4nvvT6958uKzRLIURhJicZvtIMSiOUrh4jfkbFjgGCSJV4I9i0DLQJkoUkNd8ST/3qunz0+41/Dyc8oxuJpMRKi/PH0RLHGXvVIfqAkmh3pWs8fL82Sg+ir1JJZZZWi9MfiqEKorxcDxAb1hMrQh5YRaR5+vCymmMrqk7umk4VtczykVVIiE/4rarl/j77SysY0hkFUG1pehNrypzsYIYszxxZNq26XmmaVjdlto1qRs8lh7uON6WmCEJ2X6z30ZuwINNdrWGA0R8B6IdqiIHRgATwrNE5j8C1Dg3fgHDx9vJ0QQAAA==" + } + }, + { + "ID": "b03b12570b264f54", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/utrL8S1rOfiUg3G9zWNycOy5LWX?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a27f34165b1b66929ee678095fd8cc91/209975673877152401;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/utrL8S1rOfiUg3G9zWNycOy5LWX?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:34 GMT" + ], + "Etag": [ + "E5/NxyEtHDlebyl/9HZGYA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4124,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/70.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/70.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/70" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0+DMBR+51eQ+uqyTTN1JnvYlAwTAspC5iXGQDkgs1BsD9G67L/bdsz50tPvcnq+063juuSjanJy7ZKsKj87EOqkBHwwlxhkx1Dq0vJGAjk1bsC0NG5vMgy/lYf+LYNMseHUf14+zWezvUvSd6hT7dtqpHFRAculxi8Wuz1vtSatwbxYjN5sc0+jai29iKLAm4f/pZrnVgqTIJgvAo/00s7WV33ubIoNz2IoQEBD4ZilFXwDFO/s0nnHKOCg5QIHlxdn/RTTudc7FMHVaiyiokrK8+X0Zx0qGqlJsH48eBmnKVa8MfZkRf6mI8eUxfzLrE3G5MgtFIK8F5yClGCnjMgh7w2vWwZo4qLowNI01b/pV9hzzs75BRR+u0y4AQAA" + } + }, + { + "ID": "09745d132fdaee31", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon9fc30fe9ffe425b44148c66c1cfaf1c2848dbba4/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a27f34165b1b66929ee678095fd8cc91/8593006722888312230;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon9fc30fe9ffe425b44148c66c1cfaf1c2848dbba4/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:35 GMT" + ], + "Etag": [ + "1sj8EtLTl+5iI9iUMS+g/A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4143,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48.esf,ybky3-v6:9837,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/89,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"jc\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jl\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:d16::]:4411 had response of OK with response time of 0.013666629791259766 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybky3-v6:9837,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48.esf,ybky3-v6:9837,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTEszrJwLfEJydE2zfS0zAz1DdZO13e0tYWoKskvScwJyi8vBiuFiBVBuNFAtoJCNZgEiqbBhZCFwVJlIM0lRaWpSnDRWigrlgvGi+Wq5QIAJBPbob0AAAA=" + } + }, + { + "ID": "5d45bd614ddc0f83", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "260" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2d7a2af53b45768e12de2af7a2510f69/7057002880918743758;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJTVFJJTkcifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiJBQkMifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlVCTDB6V0dTZUpPeHpVd0pqcHA2NjlQYmlpNCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:35 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/1Iy1iVMSAcKGIbwNr3-fUm4Tbnc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnhq127:4266,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=R608W4LuBdbHqQWl3oHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1UXW/aQBB851dY19eADf6OVDUEXERLaYRNUrWp0PlYmwvGZ+wzlET8957PmDRN1fbF8npmdnZHKz+1FLSm6RJdKiik8baE/PDmgYXoQgDAcVwB98jure3hIxzAS4xilaxGUfp1uJjvv2iG2h0fuvT2k98nH0fjcD/N9XY03xhBmJJ7JPtQ2X5ZJgR4O2M5b9tW73Lud+bXE+3xbuTDh88/Huf7Dw9ZZlnuTUipIYUFJNGEputKvuI8Ky5Vdb/fd2LG4gRwRosOYRu1GVzd9dQsZw9AeKH+ZqeKpQr1L4bvEkYwpyx9O/eluRDMIIIcUgJigKeWoqBT9/GfFqpEUlWj/9hNQY2fJPuopRwrV8LSiMZl3kDSVm53Kp4r5HsTbxAoVzucyJZiJCg4TaU2wGHSzP0/k1dqzHEBJ86COJGjhZobdp0w0noRRKZtENBMzQFTMyCylo5tWbhR88qw1uKUpdYSCz42dWwRMFwrdIxId11zafZcw8AOEC2E0DZQpT7W45McMIchLTJW0CabwczrB95i/H4x9byhNzytus/pK+rdbCyYwWw+HQjJiZjllAmuTGw8DbxZfxCMbxu0LGACMSYHf5sIRoSTAi6eY77BOd4Ah7wQ4De5aB2oglIBVD3P6VdeDT04ZM/hV+HUNfKD2Xg6QvXn4yvZLU7KF7rd6QPqXw8aVev8/N6SL/JwCo55WTQXU1VSJ7KY1o5nFhU3Qs5MmXl1L7Rep2vqmq3pXdvsmU59qUKU89e4bls1/vI8pfUGUh40O8szRb/MKkLPF7DBtIoc6ZYlDkPXu4bZFsfGtS7bcBf3bHdN9JjEdLvarhMnZOGqvFrCDhKWQd6JRZMdJYAJYWXKqz8Bah1bPwG3e12Y0gQAAA==" + } + }, + { + "ID": "9ec6a3fd8aae1e3c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/UBL0zWGSeJOxzUwJjpp669Pbii4?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "184dfd7b85f94dbd45422f0d60db5d79/5448941449239637751;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/UBL0zWGSeJOxzUwJjpp669Pbii4?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:35 GMT" + ], + "Etag": [ + "TPV0oOfwpEZzM5PdYiNoVw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4320,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/39.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/39.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/39" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QyU7DMBC99ysic6VSQFAEUg8UVaVVaEO6IEAIJc4kuCQZE0+ULsq/Y7sp5eLxW8bzxvuO47BvUcTszmGRSH8qKLdnKdCzuQSgqoyULhILBezcuIHC1LgX/srFWVLL4dvu6dqPX8UUV3W/f3Ap/gV5qH17jTROBGSx0vjdYqflrVaEOZgXE/fTNrc0baWl54tgPB39V3KMrTJdet79wBuyVmps/dBnY0OsMQoggRIKDqcossQ1cBrbneMq40BdiSV1b3qX7RTTedCXA8/dvYzmMJltdst6spay17v1IyGujt4MeUgCC2ufs7/phBRmAdZma3bBTtxgS6D8EjkoBXaKy455HzCXGZCJS2UFluah/sxHQS3XaTq/H4nRibcBAAA=" + } + }, + { + "ID": "1f21de97be964047", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon6da574a53a6ce496b84f3995d52944a8ec0beb74/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "184dfd7b85f94dbd45422f0d60db5d79/13904030092271948044;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon6da574a53a6ce496b84f3995d52944a8ec0beb74/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:35 GMT" + ], + "Etag": [ + "8iDuCAcnUyebmtn8Fh56aw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4108,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/7.esf,ybmq13-v6:9830,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/7,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:c9::]:4025 had response of OK with response time of 0.012531042098999023 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybmq13-v6:9830,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/7.esf,ybmq13-v6:9830,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/7" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqbHIdCl1dkzOC61MTcotybNwyzA1Syy3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNjk7OSnDBWigrlgvGi+Wq5QIAxqL+Y7wAAAA=" + } + }, + { + "ID": "c420099afe11324b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "260" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "61e7aa58e4040ea73d2b7484b241e337/12295968656298005812;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJCWVRFUyJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJ2YWx1ZSI6IlptOXYifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlhRN0NGZTc5UlpyS0NrRWpMZGRrYjNHdHFzbCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:36 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/SepAZdcKqiE0lHkqKbG7E0-6zrs\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmc19:4319,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=R608W9PeOYmeqQW954roBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U227aQBB95yus7WuIb+BLpKpJwaUoCLVg0pSmQuv12GwwXuNdg0jEv3d9y6Wp2r5YHp9z5swcjfzYUdCGpiG6UFBA410B+fHdPQvQmQRA4LgE7pBtbOzhAxzBS3p8naxHUbocrhaHW62nziG7Wobkekc9Lfm82V0HI9vTutZDzu9Q1YdW7cMiISC6GctF17aMi8X8/ParPfgEtjtb5teDjXc/CcNNYI7EjieVkEMSTWi6KeVrITJ+oaqHw+E8ZixOAGeUnxO2VdvB1b2hZjm7ByK4+pudKpfi6l8MPySMYEFZ+n4xr8ylYAYR5JASkAM8dhQFNd3Hf1qoFFWqGv3Hbgpq/UqytOwop9KVsDSicZG3UGVbbdcUzxWaexNv4CuXe1y3lCMBFzSttD4Oknbu/5m8VGOBOTScFXEiRws0N9CdINKMCKK+3SOg9TUH+loPIit0bMvCrVqUhrUWpyy1rdDuBf0AbNM1bMfVwzAA09JDJzJIFEZAXOJicFGpPtXjkxywgCHlGeO0zWYw8658bzX+tJp63tAbNqsecvqG+m02lkx/tpgOpKQhZjllklslNp763uxq4I9vWrTgMIEYk+N8l0hGhBMOZ88xf8E53oKAnEvwR7VoHaiCUgmUPZ/SL71aun/MnsMvw6lr9PG7781R/fX0RnWDk+KVbN98QMutu29lnafnz071Uh0OF1gUvL2YsqqEMovpeDpCL1hU3gh5YlaZl/dC63X0vqnZmqnblm5o9aVKUS7e4oZh1fjr86yst5CKJoPmTNGLWWXo+Qq2mJaRI9OyTNc1Tb3X78pjE5rOtsLFhu1uiBmTmO7Wu03iBCxYF5ch7CFhGeTnsWyypwQwIaxIRfknQJ1T5xcspkjO0gQAAA==" + } + }, + { + "ID": "9ece71e1594579d7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/XQ7CFe79RZrKCkEjLddkb3Gtqsl?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ad655245a6e21a3c65f4b2b25aba949d/10687908319802071645;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/XQ7CFe79RZrKCkEjLddkb3Gtqsl?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:36 GMT" + ], + "Etag": [ + "I+GNDbCiBZtSGQAaYRp5+w==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4106,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/50.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/50.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/50" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6iOSoEaJJjywOZG4ENkkEYwxXXfDbd062lsIIfvfacsQX3r9flzvux56jkOKrErIk0PibLNtQO6vNoALcwlBNRyVLrWoFJBr4wakG+Oe9afz59jL3DVG08WErsL6vr8bj08uxX6hpNp30EjjNAOeKI2/LHY63moVLcG8mA5/bHNH4762tLv68KP/QikSK8yXQTBxA590Umvrtz5bmyEXcQgpSKgYXJLUUuTAcGZXThrOAAe1kDgYPdx2U0znSf9cjLwXGD2Ga/nmFX4eJEkR301xq/jZywWjmInK2JcR+ZuOAikPxc4sTW7IhXP3COpdCgZKgZ0yJOe8nihrDmjiomzA0ozqv3zNsON6be8Ifl6qJrYBAAA=" + } + }, + { + "ID": "7a545d9de77fd7c8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon76d74b5be73927891ddbe361d8f2cfdfec9c9ae9/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ad655245a6e21a3c65f4b2b25aba949d/696814735288499569;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon76d74b5be73927891ddbe361d8f2cfdfec9c9ae9/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:36 GMT" + ], + "Etag": [ + "On+r6qzGZEgXU04oStJrTg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4335,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybx5-v6:9804,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/114,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:3056::]:4095 had response of OK with response time of 0.012925863265991211 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybx5-v6:9804,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30.esf,ybx5-v6:9804,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/30" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfHP0y4yK6xyj3JNjwg1MMkPLvEqCkm3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNUbmWZUpw0VooK5YLxovlquUCAHjeGSG9AAAA" + } + }, + { + "ID": "3c82377c9a7e2549", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "285" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6c2f07e2c4cd588f850b8e3ec0bfa6a7/17535216997542183322;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJUSU1FU1RBTVAifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiIyMDE2LTAzLTIwIDE1OjA0OjA1KzAwOjAwIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiIxbk54MTJqT25KSUR5NGpobk4zNTlFRW15aUoiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:36 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/rjTA1PS6T_kjPxlFazG_SCiyx0s\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vna15:4288,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=SK08W7mZKYqgqQXumri4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ua2/aMBT9zq+IvI8rxM47SNOKIK2oWoYgdNPWCTmuEwwhCbEDpRX/fc6rj3Xa9iWKc865597jqzx1FLBhyT3oKyBg0a6g+fHDOg3AmQSowFEJ3AFb29ijR3qkXmzwVby6DJPvo+Xi8A0aar72B2g6t/zlZj19iC/w4+VyPmTHB8jvQFWHVeXvi5hQ0c3SXHRtS+sv5j2UTB6Qtv6SXI1HR2O9Sia66Xre9siuKiGncXjNkk0pXwmR8b6qHg6HXpSmUUxxxniPpFu1bVzda2qWp2tKBFd/s1PlUFz9i+HnOCVYsDT5tJhX5lIwoyHNaUKobOCpoyigqT7+00ClqFLV6D9mU0DrV5KlZUc5la4kTUIWFXkLVbbVdM3h5QTm3rU39JXzPY6rkrIlygVLKq2Pg7jt+386L9VYYE4bzpI4oQMD6AbICUKohTQ0bYNQaEKHmtCgoXXv2JaFW7UoDWstTtLE0pFjWJpmUhvpBBrIwEhzXQdKjWkSghDWEXFDUKpPdfskp1jQEeNZylmbzXDmDXxvOb5YTjxv5I2aUQ85e0f9OhtLpj9bTIZS0hCznKWSWyU2nvjebDD0x7ctWnB6TSNMjvNdLBkhjjk9e4l5inO8pYLmXII/qkHrQBWQSKCs+Zx+6dXS/WP2En4ZTn0G/vjGm/uDmymokdM75S2OizfSffMBaBBZXah3Naggsw+NPjQ/QtiHsK3VeX7+7FQv1UZxgUXB21UqT1U1GdJkPLkEr1hMLg95ZlaXUS4Sq+dEpg5tqCPbsu1m2aUoF+9xx7Rr/O3eVtZbmogmnGZ/wate5W3kS7rFrLwLoFuW7rq6jgyzK7dQQJRuhYs1290QPSIR2612m9gJ0mBVnN/TPY3TjOa9SBbZM0IxIWmRiPIXATqnzi+OtYhc6wQAAA==" + } + }, + { + "ID": "eb2ea6558934f46a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/1nNx12jOnJIDy4jhnN359EEmyiJ?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bd64ed008544d422dfb088f9edb2f6aa/15927155565846300355;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/1nNx12jOnJIDy4jhnN359EEmyiJ?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:37 GMT" + ], + "Etag": [ + "LBFHp6en8FOq17KFSoNCzQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4321,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/100.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/100.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/100" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QS1PCMBC+8ys68SozFBXUGQ4U2wEs5X1yHKek29LaJiXZjlaG/24SinjJ5nvs7pccW5ZFPlMWkWeL7NLkUIGobxLApb6sQFY5SlVKziSQW+0GDBPt9h1vXPaAPXrzg91/9dY8GP0sB4OzS9I9FKHyHRVSOE4hj6TCbwZbDW80FhagJ8adD9Pc0FiXht5MZu56M5wt/osFj4wYbH1/6PguaaSTqe/qPJkcGd+tIAYBjMI1TSl4BhQn5tlRlVPAdskFtvu9brNFd551mwXfdjebs+nkpb7P9iy4e3hy3aJOpxdvzmmIKWfavl2Tv+3IMcxX/EuaOeTKOTWCXAhOQUowWzrkknfEizIH1HFRVGBoGqr/HKfYcK1T6xdNIsrFugEAAA==" + } + }, + { + "ID": "d3881ee50475bb00", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon631846225e713c0414a12998076655cc11a31c9f/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bd64ed008544d422dfb088f9edb2f6aa/5935781610145835479;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon631846225e713c0414a12998076655cc11a31c9f/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:37 GMT" + ], + "Etag": [ + "Gsb2sTEF0LPTqMSC0KQHJQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4078,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35.esf,ybny4-v6:9814,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/6,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:2001::]:4268 had response of OK with response time of 0.014473676681518555 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybny4-v6:9814,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35.esf,ybny4-v6:9814,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXEvTjIqDnF1M/AJCCn0DXY28A708Aq0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNJqYWJhZmRiamegZKcNlaKCuWC8aL5arlAgAmUGKkxQAAAA==" + } + }, + { + "ID": "b872dfed3db35caf", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "370" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a2a325864ff5f69e4c4b217efc7e242a/4327720174171827456;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7ImFycmF5VHlwZSI6eyJ0eXBlIjoiVElNRVNUQU1QIn0sInR5cGUiOiJBUlJBWSJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJhcnJheVZhbHVlcyI6W3sidmFsdWUiOiIyMDE2LTAzLTIwIDE1OjA0OjA1KzAwOjAwIn0seyJ2YWx1ZSI6IjIwMTYtMDMtMjAgMTU6MDQ6MDUrMDA6MDAifV19fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiT1lrcnZtUVoxdFI0N2tEcWVXenF1ZWYwaVlFIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:37 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/e66eRsaHlFnjtIv7TV1KGoTU0qU\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnx73:4170,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Sa08W9mAEov1qQXaw6mACw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAJ1UXY/aOBR951dE7mPLxPkgH0irLRrSWdQp2w1hutN2hRxzEzyEGBIDohX/vbaTDNPOarfqS8jNOeeee6+v+doz0JqVSzQ0UMry3R6q04sHnqJXEgBBcgV8Rr699sdf4ARR4darYnWTlR/Hi/nxb+ya4HkQ1+SP4k35ICYHP7mz3t7wZI53889I52E6/XJfUBD9La9E3/fs4Xx29ef9ujps/vpoidj11+MdfPgiK8gwu4+0sIYiu2XlWslXQmzroWkej8ernPO8ALJl9RXlG7Mr3DzY5rbiD0BFbf5gZ8qmavM/DH8vOCWC8fK3+UybS0EMGVRQUpAFfO0ZBmqzT/6tISXSqgb9n94M1PkpsrTsGWflSnmZsXxfdZC21d21wSVCs+g2uk6M1wdS6JSyJKgFK7U2IWnR1f0zlSs1EaSGlrOgQRbgFIepFaQZtjPIBr5LAQ9wAAPsQuYtA9/zSKcWyrDRkpKX1LdIiFMvoEvLBt8OXQfbge14ZOl6KQSW61rOMgOk1OemfFoBETBm9ZbXrJvNdRyNkmgxebOYRtE4GretHiv2jPohnkhmEs+n11LSErcV45KrJzaZJlE8uk4mdx26r+EWckJPs10hGRkpanh1GfN7UpENCKhqCX7SjTYDNVApAZXzcfrKq6Mnp+1l+Go4TYxGcTy67+hyUFVFTj9wL+Rk8i6aJaN371ELnZvf8zO7O1Lsv/PTifXXS+GX2hXj0EqQjS2vj52+jQ1rMMTuEA9eYjzEuHN99PtlffvyT+9JqJ/qy7nd/FoQsa+7lVeRTi8PczqZ3qAnLCaXnD4y9dKohWfNeVgDB/vYsXzfCZ3mqklRJZ7jbogb/Pv7pa03UIr2YNp7hp7UKremWsCGMLUzyPE8Jwwdx3IHfXlbBLb4RoTE9sM1dXKas91qty6ClKer/eslHKDgW6iucpnkwCgQSvm+FOqvDPXOvW/wTMFMkwUAAA==" + } + }, + { + "ID": "14fd379449de64ad", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/OYkrvmQZ1tR47kDqeWzquef0iYE?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8cb6691509397864ecf3a15edd4a7073/2791715232707342889;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/OYkrvmQZ1tR47kDqeWzquef0iYE?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:37 GMT" + ], + "Etag": [ + "J1/1R6kbPmo8aQvVrAP5oA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4002,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/102.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/102.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/102" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QbU/CMBD+zq9Y6leJzCgYEz5MWSImxDGmBo0xpbvNsZcb7Q2DZP/dtgzxS6/Py/We677nOCzPqpjdOmyVpZsG5O4sBZqbSwiqKUjpUmOlgJ0bNxBPjfvRvXDDYb4KSrzh8+2L9IJr9Mbjg0uJLyi59u010jjJoIiVxu8WOx1vtYqXYF5MBp+2uaNpV1s6ms78ReTNgv9iibEVQz/wvcifsE5qbf3QZ2tzrHEVQgISKgGnNLXENQia2rXjphBA/Rol9UfDy26K6TzoT8tcbsv5m0vh1SifbOD1R39TMsiW/tFboOCUYWXszwv2N52QeBHit1mcuezE3e0IVCBRgFJgpwzYMe89lnUBZOKSbMDSguv/fMio43pt7xfdWDW3ugEAAA==" + } + }, + { + "ID": "58f9bf8903b630de", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc71a90b68cd12e72943028236ad46be814413dfe/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8cb6691509397864ecf3a15edd4a7073/11175028851911939133;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc71a90b68cd12e72943028236ad46be814413dfe/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:37 GMT" + ], + "Etag": [ + "1mLKPW33Jqa8Qs6yEX2WuQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4233,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybr7-v6:9887,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/46,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ws\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e646::]:4136 had response of OK with response time of 0.012487649917602539 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybr7-v6:9887,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybr7-v6:9887,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTHM9fEOCDc29ipMtAgsNqt0jTAKLw20tYWoKskvScwJyi8vBiuFiBVBuNFAtoJCNZgEiqbBhZCFwVJlKFLo0nAlSoYmphYmFmZGJqZ6BkooKmp1KNOOxIvlQheFiIB4sVy1XACIq2gtQQEAAA==" + } + }, + { + "ID": "8ff35fdcf05a6692", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "285" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a2880586bb5179bb62430cff7526dd42/9639023910447454566;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJEQVRFVElNRSJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJ2YWx1ZSI6IjIwMTYtMDMtMjAgMTU6MDQ6MDUuMDAwMDAzIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJ3R2lmMVMyaGdmZ2JnaEJwWGNodVNOV2R4aFUiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:38 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/THE9XdNQIqgVxtrpBxiVD7CPBAI\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnez28:4339,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Sa08W7C5O9b-qAX8y7zwDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U227aQBR85yus7WvA67tBqpoE3NRSilIwJGpTofVybG8xtrHXEBrx713fcmmqtn5AXs/MmXNmj3jsSWjDkjUaSchn4a6E/PjuR+qjMwEAJ2EF3CNL3ViTn3AEJ9aLKI6uguTrZLU43GFd9j45w7v19Iu7C5cPPM8uH9hyYo1vLi/ce1TXYXX5dRlT4P0szXnfMtXRYj44XLFAmatRGIR+GF1mdzQq59Pb9UO0qIUFxME1SzaVPOI8K0ayfDgcBmGahjGQjBUDmm7lrnF5r8pZnv4Aygv5NztZDFXIfzH8EKeUcJYm7xfz2lwIZhBADgkF0cBjT5JQW93900CVqFY16D9mk1DnV5GFZU86Va40TQIWlnkH1bb1dO3h+YTmzrUz9qTzPYnrkqIlKDhLaq1H/Ljr+386r9SEkwJazoragY19PPQV2w+wGkBgWDoFbGAbDKxDYK5tyzRJp+aVYaMlSZpYeG1ppqWrhKrGehjouuGDKb4Q0/IBNNPXtIDaBqrUp6Z9mgPhMGFFlhasy2Y8cy48Z+V+XE0dZ+JM2lEPOXtDvZ25gunNFtOxkLTELGep4NaJuVPPmV2MPXfZoWUB1xASepzvYsEISFzA2XPMNyQnW+CQFwL8Vg/aBCqhRABVzaf0K6+O7h2z5/CrcJozmoi+PPezgxrg9Ea4JHH5SrlvPyAVK2Yfa30VS4oxwvoIGwNcPVpXrPf0+71Xv9QbVXDCy6JbpepUlxMhTd3pFXrBYmJ56BOzvoxqkVgzp2Jo2MKaYtkKbpddiHL+FldV3OCv97a23kLC23Da/UUvehW3ka9gS1h1F0gzTW041DRFN/piCzlW0i0fEtUabqgW0pDtot0mtv3Uj8rzNewhTjPIB6EosmcUCKVpmfDqLwL1Tr1fIbYWFusEAAA=" + } + }, + { + "ID": "8d1645c969112424", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/wGif1S2hgfgbghBpXchuSNWdxhU?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "08e233dbbaab181f3c7c6c2d3841b4c8/8030963573951520399;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/wGif1S2hgfgbghBpXchuSNWdxhU?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:38 GMT" + ], + "Etag": [ + "t7+bSmO/pd7yZEvy8Yyqaw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4071,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/22.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/22.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/22" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0+DMBR+368g9VEXtz2IMdnDUKJLcCqMeIsxpRwuE2hHDyJZ+O+2HXO+9PS7nJ7vdDeyLPKVVzG5skiUp9sG6u4kBXzSFx9kU6BURfBKAjnTbkCaajfap1FQPpyL2O7e3O/u8rXb0nY+37sky6CkyrdTSOEkhyKWCr8bbA280Spagn4xmXya5oHGThj6ZrF218t7979W8thoq9DzFo7nkkHqTf1QZ29ibHjkQwI1VAyOYUTNN8BwabaOm4IBjgWvcWxfzIYpunOvt7d5Mg1mWZqkUZo54oVlTbB6jn+y8OAtOKOY80rbw4D8TUeOtPB5q/cmU3LknA5BPtacgZRgpkzIIe81L0UBqONi3YChGVXfeZfjwI360S+yLc7uuQEAAA==" + } + }, + { + "ID": "8a6550348abcbccd", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon70d736742ac25d9f445be6367a67bee36b33fc85/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "08e233dbbaab181f3c7c6c2d3841b4c8/16486051121800724643;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon70d736742ac25d9f445be6367a67bee36b33fc85/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:38 GMT" + ], + "Etag": [ + "R8872kfnOaTo3FmGLPbFsQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4426,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/43.esf,ybe18-v6:9822,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/122,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f8d9::]:4080 had response of OK with response time of 0.014645576477050781 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybe18-v6:9822,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/43.esf,ybe18-v6:9822,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/43" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQmysDA3yk7L808MyTd2y3X3CUhyKw60tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBiGGplYGJlYGpnoGIGCsBFdbC2XFcsF4sVy1XAB0A5wZ0wAAAA==" + } + }, + { + "ID": "408a933ce19e1653", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "265" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "fb00e38bc0a6393e1f9955c0299ddb74/14877990785304790476;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJEQVRFIn0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoiMjAxNi0wMy0yMCJ9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoia3p5MWRYQ1ZZVDZPR0NrN3dNclNqZk43MklNIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:38 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/cq0HYFZ4sHrm2s3Ohh_mngrSA_s\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnaf190:4081,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Sq08W5uAJ4qcqwXBmbTYBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U227aQBB95yvQ9jXEN3yLVDUROClSQiowaZKmQuv12GwwXuNdg0jEv3fX2Lk0VdsXy+NzzpyZo5GfO120pHmMTroooum6gnL36ZFF6EgCIHCqgAfkmkt3+AQ7CLI+X2SLiyS/H85n21u9r5G1/vXu/L7Pv5Yrk1vXi8V8lafl9GzOH1Ddh9bt4yojIHoFK0XPdcyT2fR4+bQz4tvBzV3oXF8Mlu72qpw+JmPXHF3VQg5ZcknzpZIvhCj4iaZtt9vjlLE0A1xQfkzYSmsH1zamVpTsEYjg2m92mlyKa38x/JIxggVl+efZtDaXggkkUEJOQA7w3Ol2UdN99KeFlKhWHdB/7NZFrZ8iS8tOd69cCcsTmlZlC9W29XZN8VqhaXAZDMLu6QZndUs5EnBB81ob4ihr5/6fyZUaC8yh4cyJl3h6pPuR4UWJbiaQ2G6fgG7rHth6HxIn9lzHwa1aKMODFucsx4bjEdtLrLgPseVjN8a+70dgGzEmphMlxE5MJzGQUu8P45MSsIAh5QXjtM1mMAnOwmA+Op+Pg2AYDJtVtyX9QP0+GUlmOJmNB1LSEIuSMsmtExuNw2ByNghHNy1acbiEFJPddJ1JRoIzDkevMX/DJV6BgJJL8Ee96CHQLsoloHq+pK+8Wnq4K17DV+EcajRUcx0+7j+IbnBWvVNtmg/I1A2np1s9U2/FnZfnz079Ul8PF1hUvD0bVdVyGch4NL5Ab1hUHgp5YdbBq6Ohh50M29Jd3TJcz7Waw5aiUnzEPbvB399obb2CXDRBNLeK3swqky/nsMJU5Y4sx7F837KMvt2TFyd0g62Ej03XXxIrJSldL9bLzItYtKhOY9hAxgooj1PZZEMJYEJYlQv1O0CdfecXCniuGdcEAAA=" + } + }, + { + "ID": "8580e23ba5de11ce", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/kzy1dXCVYT6OGCk7wMrSjfN72IM?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1bb93eea27b12a82ba598edfd349c609/13269929349314005749;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/kzy1dXCVYT6OGCk7wMrSjfN72IM?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:39 GMT" + ], + "Etag": [ + "NNlL0IjXNZDr7uSlG5Ce4A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4496,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/119.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/119.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/119" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/02QUW+CMBDH3/0UpHudCZpNkiU+IBpngmwDXdyWZcFyOLBQ1h4xaPjuayvOvfR6v/tf73899SyL7LMyIQ8W2Wa7nxpEc7MDfNGXEGTNUKpQ8VICudVqwHin1UHAfHuRb4L3qXDqiM3vPbhzx+OzStJvKGKlO6lM5WkGLJEq/zC51XFTK+MC9Iup/WWaO4xNZfDUXc3+84Inhgdr33cn/ox0pdbET3W2xkLOtyGkIKCkcDVSCZ4DxYXZOKkZBexXXGDfGQ27KbrzXN8fm0Gy8V7fVqOnubd3DksR5WngDBfLi5ZxGmPGSy1fR+RvOnKMWcgPemcyIFc2aRDks+AUpAQzxSYXvx4vKgao7aKowWAaq698zLBjvbb3C9+6ktm1AQAA" + } + }, + { + "ID": "4993b1d3353c527a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anona168c58f3d4ed39a7da999be51dac26bfc5f26f1/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1bb93eea27b12a82ba598edfd349c609/3278836864312060937;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anona168c58f3d4ed39a7da999be51dac26bfc5f26f1/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:39 GMT" + ], + "Etag": [ + "gnFSdd7A0+l9vP6Swbgvkg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4233,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybca13-v6:9877,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/113,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"jc\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"wo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e942::]:4073 had response of OK with response time of 0.012927532196044922 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybca13-v6:9877,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4.esf,ybca13-v6:9877,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/4" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqUnPcwtOSTF3NNDOsSwLMAsuT0ovy063tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBkpwuVooK5YLxovlquUCAHvrB+zDAAAA" + } + }, + { + "ID": "8269e5a53ffe0f5c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "270" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8e40cd448684f97e5161c552f31198f5/1670775428321276210;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InR5cGUiOiJUSU1FIn0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoiMTU6MDQ6MDUuMDAwMDAzIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJXTXROZDdjU08xTDdCUkZVbjF3VzBLQUVnM3QiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:39 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/yFpmmbMokGK21orCvdBcXR_IRfo\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnvv3:4062,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=S608W7nhEcbRqAWE-JmIBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1UW2+bMBR+z69A3msbzC0kkaa1TWiFlmYTIW21dYqMcyBuABMwibKq/33m1ss6beMBYZ/vcs6nIx57CtqydI3GCgpYtCshP3544AE6kQUQJKoK98jWt/b0JxzBic1iE2+uwvTbdLU83GFTPV5mSRJc8+3VZ13j+WS/vqB33sr1Qn6Pah1Wy6/LmII4zXguTu2BPl4u+rfXYr626eKLNrMvvMtlqh1u8edzJzJETSwgDmcs3Vb0jRBZMVbVw+HQjziPYiAZK/qUJ2rXuLrX1SznD0BFof5mp8qhCvUvhp9iTolgPP24XNTmkuBBCDmkFGQDjz1FQa26+6eBKlLNaqr/mE1BnV8FlpY95alypTwNWVTmXam2radrDy8ntHBmzsRXzvYkriVlS1AIltZcnwRx1/f/dF6xiSAFtJgVHYZDHOBRoA2DEOshhJZtUsAWHoKFTQgH66E9GJCOLSrDhktSnto6mCYJIFgPQpPoOLC0QB8NjJFFBoTqUoYMA5OEqGI/Ne3THIiAKSsyXrAum4nnnPvOyr1czR1n6kzbUQ85ewe99VyJ9L3lfCIpLTDLGZfYOjF37jve+cR3b7pqWcAMIkKPi10sESGJCzh5ifkryUkCAvJCFr/XgzaBKiiVhUrzOf3Kq4P7x+wl/Cqc5ox899pBzeXTO9INics3rH17gTRrjM0xtvq4eoxOoff8/tGrP+oVKgQRZdHtTnWqNWQqc3d+hV6hmNwW+oys0682hyWtqYFtbGj2yMR2s7OSlIv3dQtrTf3totbWCaSiTaNdWPSqVxl/voKEsCp8ZAzkfowMQzOtU7l2Ams8ESOi26MtNSIasd1mt42HAQ825dka9hDzDPJ+JEX2jAKhlJepqP4JqPfU+wWun/wZ3AQAAA==" + } + }, + { + "ID": "153643e9621d5ff2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/WMtNd7cSO1L7BRFUn1wW0KAEg3t?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "153415487f82a346eb6e1cae5c4b95d9/134770486856791643;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/WMtNd7cSO1L7BRFUn1wW0KAEg3t?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:39 GMT" + ], + "Etag": [ + "r4R5Hl1CNp2iPgKiTb2tuw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4149,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/10.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/10.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/10" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/02QTW/CMAyG7/yKKrsOibIPpEkcAHUCURgrVBymaUpTtwtLky5xhRDiv5OEMnaJ48ev49c5doKA/HCZk5eAZLz8bUAf7krAd3dJwDQCjQ21kgbIvVMD0tKp9WPyNBXhZFn3+aqc803Wx2Y/HF5Uhn1DRa3uaDObFxxEbmz+4fOg5b4maQXuxaL35ZtbjIfa481sEf3nlco9X6ZxPBrHEWlLJx8/7XnyFnYqS6AADZLBzUit1Q4YzvzGeSMYYLdWGruD5347xXVe6tsFLvMBW7+F8WCcvKYy3G9781FUPuBVKxSjyJV08nRN/qajQioStXc7k5Dc2PiAYFZaMTAG/JQeufqdqKoWgM4u6gY8ZtR+5ZRjyzqnzhm+k5kZtQEAAA==" + } + }, + { + "ID": "d3fd34f5a2d21f15", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon72e44abebd6f4a20b51b296395a6ac2050a8b4af/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "153415487f82a346eb6e1cae5c4b95d9/8517802635379644527;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon72e44abebd6f4a20b51b296395a6ac2050a8b4af/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:39 GMT" + ], + "Etag": [ + "3C+6UmjX12kUkUE8Xtkf3g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4246,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybra5-v6:9838,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/4,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"iw\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:c01a::]:4208 had response of OK with response time of 0.014368534088134766 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybra5-v6:9838,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybra5-v6:9838,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTF21jYLzc2KMDTKDs0OdbWIKMlOM063tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNplYGJlYGpnoGIGCsBFdQC2XFcsF4sVy1XABl4EfsyAAAAA==" + } + }, + { + "ID": "38c5dcfe4b8a791c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "932" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0e9e42842def4c842ea32aabb06ef278/6981797698210061720;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwiLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InN0cnVjdFR5cGVzIjpbeyJuYW1lIjoiVGltZXN0YW1wIiwidHlwZSI6eyJ0eXBlIjoiVElNRVNUQU1QIn19LHsibmFtZSI6IlN0cmluZ0FycmF5IiwidHlwZSI6eyJhcnJheVR5cGUiOnsidHlwZSI6IlNUUklORyJ9LCJ0eXBlIjoiQVJSQVkifX0seyJuYW1lIjoiU3ViU3RydWN0IiwidHlwZSI6eyJzdHJ1Y3RUeXBlcyI6W3sibmFtZSI6IlN0cmluZyIsInR5cGUiOnsidHlwZSI6IlNUUklORyJ9fV0sInR5cGUiOiJTVFJVQ1QifX0seyJuYW1lIjoiU3ViU3RydWN0QXJyYXkiLCJ0eXBlIjp7ImFycmF5VHlwZSI6eyJzdHJ1Y3RUeXBlcyI6W3sibmFtZSI6IlN0cmluZyIsInR5cGUiOnsidHlwZSI6IlNUUklORyJ9fV0sInR5cGUiOiJTVFJVQ1QifSwidHlwZSI6IkFSUkFZIn19XSwidHlwZSI6IlNUUlVDVCJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJzdHJ1Y3RWYWx1ZXMiOnsiU3RyaW5nQXJyYXkiOnsiYXJyYXlWYWx1ZXMiOlt7InZhbHVlIjoiYSJ9LHsidmFsdWUiOiJiIn1dfSwiU3ViU3RydWN0Ijp7InN0cnVjdFZhbHVlcyI6eyJTdHJpbmciOnsidmFsdWUiOiJjIn19fSwiU3ViU3RydWN0QXJyYXkiOnsiYXJyYXlWYWx1ZXMiOlt7InN0cnVjdFZhbHVlcyI6eyJTdHJpbmciOnsidmFsdWUiOiJkIn19fSx7InN0cnVjdFZhbHVlcyI6eyJTdHJpbmciOnsidmFsdWUiOiJlIn19fV19LCJUaW1lc3RhbXAiOnsidmFsdWUiOiIyMDE2LTAzLTIwIDE1OjA0OjA1KzAwOjAwIn19fX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IjlWSjRvakM2S3V4b2UyY0NTT0MzeDlYYUlHbSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:40 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/RtiawRjQSZ2NZgb6vqsjaphuaiE\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneg123:4118,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/218,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=S608W6ieN46GqgW5-qagBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/218,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/218" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAALVWXZOiRhR991dY5DHj0HyDVamspWTKZNbsCjPZTHbKatoGWxEQGl13y/+eBuRLR9faVF7E9p57z7mHvnZ/63S5FQnmXL/LOcTbpDje/7QMHe6OBTCFXhb4zGniSht9xXts+nKy8BcPbvAymj3tPgGZn1ICd9PlR+tFnLx4jrrdJEsYLVJIzM9cXofk5eepjzDtRWFMe5oq9p+se+P5dzlcDtU/0i8hFtHQ+nMofTE+wfHDOk9MsO8+kmCVpS8ojZI+z+92u3svDD0fw4gk9yhc86VwfivyURwuMaIJf0LHs6YS/grhr36IICVh8MuTlZOzhCl2cYwDhJmAb51ulztWH7/VUJaUZxXR7/TW5Uq+DMwoO91DxorCwCVeGpehnDbv7rioV5xlPppDu/tuC/28JJOEE0qCPNeGjl/qvkV5lg0pTPARM0O6qwMHGI6gOy4QXewqmowwUICOFSBjV53rmqrCMptmhEUuDMLA0RTXFV1N1EWkyGguqBpU54ouK5rhYsmYQ1kRBFnjsuxDIR/FGFI8IkkUJqT0Zjg1B7Y5G/82m5jmyBwdW93F5Az613TMkPb0aTJkKUdgFJOQYXPHxhPbnA6G9vi5jKYJfsQeRHtr4zOEC/0E39U2f4AxXGOK44QF/8kbLQztcgELZDUr9zOuEm7vo9r8zJxizVlM3NAu8WyH0zhFNEPXBDVHk8Yma/Zy4TqqkuuyNbxmssfvTcsevP/AVcFD+e1wd4XIojEJvEEcw/2NVIPpdPB3A8s2QJZtn2W0fBhPHmpptbZbVaaOlXt3o8YT46943+I7N6ZZ4k3Kq202G219f/3R/v//F3Xi2zXjTn24Zt0F766a13KvtXi9vpU6LdThbF6foZ+2BrboMf85aWpsjUdTe2FlldBwpeXr9kjEwebmv/se2nlzVF7Pt0pjMFrqLvVTdXS6ASpqdOuUnu7JH3DnsspLOptK57cM2kWv/xs3von76pur/+FbzlUUIhDUHpB6IugKSh/IfaD8DEAfgIq6rH98Fo/8M+M7HK8YjIOmZYv5Ki/PTs1JMW8VirDbBKqQ+emc3SxIMdSCIgENSIIOgGAUdxqWFNPzuCDoRbx9kcmp1zigdjnx+YWGa2hlx3M8w2tIssOZk1RVMgxJEmSlx64lFAjhmhpQ1IwVkjzkkc1is/J1J3QW6bs53mI/jHB877EiW4IwRChMA5rdGbnOofMvWHEYV/wKAAA=" + } + }, + { + "ID": "b5cf937a611b3fc0", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/9VJ4ojC6Kuxoe2cCSOC3x9XaIGm?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "809e047eff8388b581c8ebb2c45f5200/5373737361714127553;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/9VJ4ojC6Kuxoe2cCSOC3x9XaIGm?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:40 GMT" + ], + "Etag": [ + "flQ/pvbSzoAgyQDJg4fHEQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4079,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/46.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/46.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/46" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/9VU2U7CQBR95yvI+AoRkWAw4aGUhkW2LhgTY8wwvdRi26mdqVIJ/247bKVs0Tef2nvOmXtuz01nkcvn0bvtmeg+jya29RFCEF1ZwNXkRQMWOpzFD596DFAhUQPHVqKeOuq1/znRv6lkRWqza1WmbUWt11cqRt7AxbFuEVdxPbXBMVlcP4s6v8YF52EXRMfSqzi8hnnkC1hT5KHWTDMuNQUzGPd6UqOnpLkDo32zPUPDdoFx7PqpBnvWRqev6IbUH2UFBxOk6GXhsrPOA9uzpCDA0Slv3dA6g9YpY00ZKZKhNH9tHE5i75DwU7YHaR//3gx/NPXDMY5kkOl0OYLj82Qky7365a8hnV3Q+aS2C/q/SeWy6ApJKpEimtGJBlMIwCOw+9X9gM6A8I64U8zQIcCLPg148a5aXk+YnFzxtcduhc7k6kM4p1Amsj6Ub+e1J9xpuRutQwnmNvUS+VhHW3dOOXY0+pWEiW7QDmtEHNgooAQYA+FSQpt5Zer6DvBk3HjDIGCC48uqbfM1llvmfgDqQ3AyFwUAAA==" + } + }, + { + "ID": "3257cca532f91ea5", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonb75ff2f7282c54cd167a6d584579fe39da451147/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "809e047eff8388b581c8ebb2c45f5200/13829107475445023957;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonb75ff2f7282c54cd167a6d584579fe39da451147/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:40 GMT" + ], + "Etag": [ + "iseY2ksfsAX5BXew1G30Qg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4042,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybpk9-v6:9827,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/72,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e306::]:4495 had response of OK with response time of 0.014226198196411133 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpk9-v6:9827,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36.esf,ybpk9-v6:9827,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/36" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqcksTo00yi5OK3aMMHWKSC03dDc2CEy3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZUApZAEM1Zh6kHQqGZqYWphYmBmZmOoZKKGpqdUhzhB0u7ArRdiZiG4RVssImZKE1RQMsVgyPYXNaqwhi8+lMLcmY3MrNtdiuhdTFY2iBacPcHsat4lI5iqlYPc8CGALABDADARcqklNNjTzZSpNfUk4WXPhkkXIwFgQWRAvlquWCwC/vdCv1QQAAA==" + } + }, + { + "ID": "d552a296178467e2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "823" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1581002d2941c89cab02fab3916d65b8/12221046039454239230;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUIEB2YWwuVGltZXN0YW1wLCBAdmFsLlN1YlN0cnVjdC5TdHJpbmciLCJxdWVyeVBhcmFtZXRlcnMiOlt7Im5hbWUiOiJ2YWwiLCJwYXJhbWV0ZXJUeXBlIjp7InN0cnVjdFR5cGVzIjpbeyJuYW1lIjoiVGltZXN0YW1wIiwidHlwZSI6eyJ0eXBlIjoiVElNRVNUQU1QIn19LHsibmFtZSI6IlN0cmluZ0FycmF5IiwidHlwZSI6eyJhcnJheVR5cGUiOnsidHlwZSI6IlNUUklORyJ9LCJ0eXBlIjoiQVJSQVkifX0seyJuYW1lIjoiU3ViU3RydWN0IiwidHlwZSI6eyJzdHJ1Y3RUeXBlcyI6W3sibmFtZSI6IlN0cmluZyIsInR5cGUiOnsidHlwZSI6IlNUUklORyJ9fV0sInR5cGUiOiJTVFJVQ1QifX0seyJuYW1lIjoiU3ViU3RydWN0QXJyYXkiLCJ0eXBlIjp7ImFycmF5VHlwZSI6eyJzdHJ1Y3RUeXBlcyI6W3sibmFtZSI6IlN0cmluZyIsInR5cGUiOnsidHlwZSI6IlNUUklORyJ9fV0sInR5cGUiOiJTVFJVQ1QifSwidHlwZSI6IkFSUkFZIn19XSwidHlwZSI6IlNUUlVDVCJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJzdHJ1Y3RWYWx1ZXMiOnsiU3RyaW5nQXJyYXkiOnt9LCJTdWJTdHJ1Y3QiOnsic3RydWN0VmFsdWVzIjp7IlN0cmluZyI6eyJ2YWx1ZSI6ImEifX19LCJTdWJTdHJ1Y3RBcnJheSI6e30sIlRpbWVzdGFtcCI6eyJ2YWx1ZSI6IjIwMTYtMDMtMjAgMTU6MDQ6MDUrMDA6MDAifX19fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiYVIwUERMUERlN1NVclJjSW9DZzM2eWpwa0RrIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:41 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/etHp7--an2TO2k_5_w6luX3OZgY\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfr1:4352,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=TK08W7DvKY7YqQXMtqmoAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/102" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAALVWba+aSBj97q8gsx/3KiPIiyab1ijbNbm1N4DdvsYM44BzReDCoLGN/32HAUS0tabJfhHG5zzPec6ZN753JLCh0QqMJODR4CUn6eGP59gDDzxAGAqKwBdgKBtj+o0ciBUOsnW4fuNHn6bLxf4DHMiE/ZMY3S6KFPedsllqy70e5h/Ud5+Cj1+AqENF+VUeYsK6SZyyrqEro4XTQzZ8mj4+TYnhLFIbz+JJoOqH52Qz3YjEjIT+I402RfqasSQbyfJ+v+8FcRyEBCU06+F4K9eNyztFTtL4mWCWyRd0MheVyTcIX4UxRozG0V8LR5DzBJv4JCURJryB7x1JAlX12Y8EFUkiq4z+QpsEar4CzCk70rFgxXHk0yBP65CgFeqqQTMCjvVoTVzp9Q6FPZduScbQNnkox07uOSzNMevxB40Cwcl75iAaieIu8sJa2D3SimzEUEYqzBKbvgk9OPT6pudDxSe+ZgwwgRo0iQYHxNdXpqHrqM5mBWFlThRHGGJiqsO+p2LkqUhTVVNBQ1/Hnk8GCK08DWqq4q1AkX0s28cpQYxMaZbEGa3Nm9jW2LWWs7+Xc8uaWtNK6j6lV9B/7RlHuvZiPuEpFTBJacyxwtLZ3LXs8cSdva+jeUYeSYDwwXkJOcJHYUYemnl4QinaEkbSjAc/C6GloRKIeKCoyaejsoBz1XD3kDTmF+aUY+Dw5iZujedbQExigW4IGo5zmtMKOCU3ZRt4w+TO3lqOO377BE7BY/12fLhBVC6ocZqiw51UY9sefzzD8gVQZLtXGS0fZvM3TWtNb/d2WW+AO3u8MP6G9y2+a2POS/yQ8qbMc6Gt96+/q///n6gL324Zd+nDLet+4t1N81rutQZfby+lTgt1vNqv71GYtzZsqVH8nZ33eLbwzjv/KV46ib8weFcxAnTvLjg7AlrUp0oK7OtdqHYVKPW1ERyMoPYnhCMIwaUP1bN8iN/CmWN1SXEOltcqxEiU58fqvJyQE4ry6wafkOL4Lq4eWs56X1OhAdW+Cc2BWd6KPCll1/Ghppbx9lUoqLckYm69JMSVCM565ed3uiRbRIvTG6i6rg6HqtofaF1+bzHYj7dsiBRjuMFqgAP6sn7ZhKYXe+v89YrsSBgnJO0FvMiOYoIwjvOIFV8doHPs/AemgdnZPgkAAA==" + } + }, + { + "ID": "2118acae4c55d9ad", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/aR0PDLPDe7SUrRcIoCg36yjpkDk?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7ab8a420bb5edb10a8bfd273c3e4f321/10612984603463454247;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/aR0PDLPDe7SUrRcIoCg36yjpkDk?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:41 GMT" + ], + "Etag": [ + "pX5hEvbd85aplwXcshiyEw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4291,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/54.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/54.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/54" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41Rz2vCMBS+968o2VXBbahj4MFp2QpVulZBGDvE9FmjaZMlr5Mi/u9rYp07bLDTy/cj73t5OXq+T/a8zMijT9Y8/6hA1zc54Ks9JGAqgaYpSpYGSMe6AWlu3WrV3waf6+yhT5U4rJjZ8jo4jEZnl2FbKGjjOzaowRsOIjMNfnPYb3mnlbQA23HBCzBIC+VatCLW6iyGsyBdjGfxT7GQmRPnyygaP0UBaaVT5++UFDUv898i0kUSzp//1d/Vd69NIju5TmADGkoG1zcrLXfAMHTLzSrBALtKauwOB3dtir151mnSi6dRPIVhutQJC+Ukvx/UO7Wf7i9eIRlFLktrX6bkOx0lUpHIg10vuSVX7qlGMLGWDIwBl9Ijl3knslAC0I6LugJHM9r82gvHlvNO3heBlbjwIAIAAA==" + } + }, + { + "ID": "dc9764987fb4bbb8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc0ce8391b3cab3a53382a9f6cbfe4aadb50532bd/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7ab8a420bb5edb10a8bfd273c3e4f321/621610647779766331;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonc0ce8391b3cab3a53382a9f6cbfe4aadb50532bd/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:41 GMT" + ], + "Etag": [ + "G5nt7AgS538pP8cn0rD2SA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4132,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44.esf,ybpo1-v6:9853,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/96,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jd\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:d7d4::]:4067 had response of OK with response time of 0.012370824813842773 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpo1-v6:9853,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44.esf,ybpo1-v6:9853,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/44" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXE3zSsxd0wPNjW2KAiwSM4zKHIxCna0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNJqYWJhZmRiamegZKcNlaHTxaEpHUQVmxXDBeLFctFwDruBwp7QAAAA==" + } + }, + { + "ID": "536b64668fae1d8b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "229" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e4e87194de72a6257cffc6a7c4ec9ea0/17460011810521822564;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7ImRyeVJ1biI6dHJ1ZSwicXVlcnkiOnsicXVlcnkiOiJTRUxFQ1Qgd29yZCBmcm9tIGBiaWdxdWVyeS1wdWJsaWMtZGF0YS5zYW1wbGVzLnNoYWtlc3BlYXJlYCBMSU1JVCAxMCIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoieU1LcWFLdWMydUE5YVN5dFVDajBnazRWZ2JnIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:41 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/czE51iyf1_4MksT_SRooI371Qq4\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnt135:4087,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/57,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ta08W-H9H4jzqQXN84voAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/57,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/57" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TTXPaMBC98ys86rVQGX9BT03ATT1DaGtM22mTIbK8BgVjGUlOhmT477VlO9C0h140o923u2933z73DLRleYLeGyhm630J4vDmnsfobeUARda14wZ5w603fYID+JktN9nmKs1/TlfLxx/YfkeffMdkh9Rc2ddbGa0WIeeB5Zlf9/YN0nmqfCGkICCnUOV77hkGKgS/B6oCXTkpMwqqX3Ch+p47rIMMlHFKFON5DVguUM841rkoz1O2LkXn0sk07fZz+qGFP/MnkfHIRWKkgu+Mu67FflHGGaP9hCgykGRXZCAHckO2IAsgAu6MWXAdRIaJNZeKIUjFcl00InHWtfE/jdTRVRkJLWZFR+kIx3gcm6M4xcMUUsezKWAHj8DBNqRuMvJcl3TRqi7YxJKc504yIi4mFOPUdanjpJ6TjB3HdTBxbQyuk8R2OsQxqqOPDX0qgCiYMllwybqhTkL/IvJXwcfV3Pen/rRt9VGwv6Dfw6BCRuFyPqlCWmAhGK+wetTBPPLDi0kUfOu8pYQZrAk9LPZZhUhJJqHXEkKJOIRlnVmJEtrNSkVUKbuV1r96ymj6ee6jMwirFkFfYLqxeilsp9GmY2EPW+bIdDsdKa5IdnlQIL8ITkFK0KM0LWs4tq0G86eA/iPiHHTJsqxBdGqhhG7gE1Nd341VdDeQaAnVPfzSG26U9EpK/5Jqq4hXgmr1++I808uZpLUcjGP93jZ8ZEVyR05KThlkyYlVR8tAOWmGWx9SV6Uqcyi0dRGFwfzqZN/xRNvny9ns4nLmN4WbysbtmSj1ineQq6jLpO+1xh/bhVciEquKJKslhCzXtcZjyzJtp18dj8Im36kxGXrjLbXWdM32m/02G8U83pQfEniAjBcgBusqyQOjQCjlZa4GlO9Q79j7DdjAhP37BAAA" + } + }, + { + "ID": "feae4d8f9cda54e6", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "268" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "43d717f46ad660f508852be91db20bdc/15924007972847155852;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJudW0iLCJ0eXBlIjoiSU5URUdFUiJ9XX0sInRhYmxlUmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDEzIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:41 GMT" + ], + "Etag": [ + "8EBu8XTL8ZnZBAbhhltDrw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnat124:4348,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ta08W8nMMsTtqwWv4qKADg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SX2+CMBTF3/kUpHtVC/gPTXzQjBgT5wNjyeKymApX6ETKaB0zxu++0mKyGLbIA6Tn3tPzuzecDRPtaRahsYm2NP48QnF6EGSbAmrJEggSVyXXmx3d12DprrP1bLpNklQ8FuVkopqockfHNATRzlkh2sOBM46IIBzExrFs1xpavU3PGoyckWv3e0NnaG8s+XRU0j8tdlclcEh3S5rtq5xEiJyPMS7LshMzFqdAcso7ITvg6wD4y8F5wT4gFBzfcOGai+N7ALEC5PhOTtXmww4KyEKQtGfDNFGNsmhaU2WTokapO+7g0jYVp013ABrmRe0yTOBArmw7CmnE5elNnpQkxYwcKnj9bWlNnHKlPQf+YjVHlXhpNXmOh1vLYhV4c8/XHvl6r0lk6+wkoEpHaqJKWbIsDqBoqPis/CWEBRBBWRZQnWv3u3Lyru3aI9vR/+53ToumnsGoW68QpYSLJxZRuYbo75uugwTT2dLTPhaqmyv15RkZF+MH94rqJUkDAAA=" + } + }, + { + "ID": "05346fad94e7011b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "294" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "18ae479f1fd77e666f6bb131e96706b5/14315946536873148085;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiSU5TRVJUIGRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMC50YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDEzIChuYW1lLCBudW0pXG5cdFx0ICAgICAgICAgICAgICAgIFZBTFVFUyAoJ2EnLCAxKSwgKCdiJywgMiksICgnYycsIDMpIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJDTWJ1emtIRzRMbndZZm5BU20yU3pjcnJHWHoiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:42 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/fd2tfLAJkJL6ZyNrxBTMVPJMNyY\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnt135:4087,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ta08W6HlPMr0qAWI7IHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2Tb2/aMBDG3/MpIu9FW4mSvxBSadpYhxgVRROBqq2QkGMuwU1iB8cpg4rvPieBteumtnl11t1zz/3OzlNDQzFlS3ShoYBG6wLE9tMDD1BTJUDiqEzMkWvF7vcdbKGfOPkqWQ1Cdv99MdvcGo4eLi0ZjnpX8dWoc78di1/fptc3P6+ux9u7Oar60Kr9skgIyPOMC3nudqyLmd+6vA6KXfxj4IzY5i5kPT+1/B0RYnC7q4Q5JOGIsriUr6TM8gtd32w2rYjzKAGc0bxFeKofB9cfLT0T/AGIzPVXdrqCyvU3DL8knGBJOfs88ytzJZhACAIYATXAU0PT0KH78H9ApahS1dl32DR09CuLlWVD25euhLOQRoU4pirbiu5weD6h4djvT6baEkucg1xYhtk1XMNZOEbHs7yu2XZcyzUXhvpaEgcJvFFi2topwyk0NVakZ3M2l3OpvfpueqNZ39dOT/BJUzPPmioKVGRVEVGRfVahqdVALimrGKal8XH2D2ywVNdAh5oP0B2FFWQte58XlZp9PW8mKBdUHrY67U96l9PhTf9AU+QwggiTrb9OVEWIkxxUYn+4slxiWeTHuypPJS+azMbj4XiAXlRRtRXyp5IIqDdE00pgtm01q212LcNt129EiYT8N2963Tr/98OorFNgcrrN4PmBoBezKhSxgBTTEgTZnY7tebZtOu3zwOxKw+Sp9LDlejGxIxLR9WodJ92AB6vi6xIeIeEZiFakmjxSApgQXjBZ/oOosW/8BrI6SSBMBAAA" + } + }, + { + "ID": "2da0c527d8fdf88b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/CMbuzkHG4LnwYfnASm2SzcrrGXz?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "17a4bab84e22e3df6ecf0d511fbc1fe4/12707885100882363358;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/CMbuzkHG4LnwYfnASm2SzcrrGXz?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:43 GMT" + ], + "Etag": [ + "DLLTXpGHspcWAELegSKJxw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4008,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/108.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/108.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/108" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/31Q0U7CMBR931cs9RUSg0YTEx4QyEAn0Q0jxvhQurs56Nra3mUC4d9ty4AX49PtOff03HPvLghDsi5FRu5CsiyL7xr05qIAfHGPBEzN0diipDBAOk4NSAunHsXxfKGiiVHsbTCOoUgfH36afv+gMuwLKmp1O4sszkvgmbH4w+Ow5X1P0Aqco6+dM48b5fl0nkxnEWkb+84/DnX1l8F0Nh9H4+Tk4Otn0HqRlVwmkIMGweCcWGm5AoZTf5qs5gywq6TG7u1Nrx3ifh76w6dlvV1PoutYNO+5GKRVL90yraPF9qjlklEspXDy15ScpqNEyu83COZZSwbGgHe8JMdsQ1kpDuiioa7B04za+05KtFxOuTmQdvtRxQd5bmNDlsjG3ZtckWAf/AJNzI2o6AEAAA==" + } + }, + { + "ID": "c0068886b9418daf", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/CMbuzkHG4LnwYfnASm2SzcrrGXz?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "17a4bab84e22e3df6ecf0d511fbc1fe4/11099824764386428935;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/CMbuzkHG4LnwYfnASm2SzcrrGXz?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:43 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/tbbl8MZFGSAjAYzd0Ok39EOnf7w\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:43 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnm125:4095,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/177,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=T608W6HIHJGKqwWO7Yq4DQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/177,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/177" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAMVYW2+bSBR+969AqFJf0tVcgbG0D2nj1UZa15Htqg+rlUVgnNLFwMJ406jKf98Dw83cnG2bxg+WObfvcJjvY8ZfZ4aZKVcdM3NufJ0Z+krChXm1er8wZ8bjRRkSZCrw6jAvlWCKo21wKKIxp8hGFDsE2dy8KCulqu/HwtF+Gfk9L8W8zFaxcsO3D0pmN2nsySyTfh6JtPefo0wfyl7Kq5vQjcDyZ27RdvBErgbYIDQ3rqPkqIr83BW06hlVu8vstFlKRR0A/fbcrHHfu4Fa5zO5/PcOotAvqO1ZZtrcAqwTlu6XoQRtbiXAyP1hhNwzgFAn9BB0Qg/Biw8wInkKgh2GmEC2sDmzECasE10D08E6Nfb5OmU/TZ37NOh2QxinwrFtipCFYf7OSWzdCxuoUXdyrkbZR1Mj+3Tc70O5OipotFiUhZ9PBGySIAyl330gXpz62RqmP+j4CPhKRqdDSNzUhVJhsXz1AjwZdCiV9G8mgmqCm+9Wy5s/FttFyyWTrGZNzRvw/B1EfpXyocnIc4633TSwvsLImP9qvHZfN6G5FedWbFamv8ofjxffj0g04u0pIikQybMgUo3onSLSApH+H8SP6+snzfTCeEXaWNuVsdvB87yTCO3iYrX1YWety7KHvh7iufEudkOZebIjiXhaEpk1KYmcNKwI8sW4ybtt3xgs+7LDUem0BCUcalKbgQRjOqikGNNxLbWEoI7FEUWWzRxmDUorxuylxXVUirriyqbFlVoOEg7mgiNBEOFj2mpNaCu2sW0LzDBGjAjGxLC0EmdCW0nnjninRiXx9Lm1lX6jtnL0FHHl6Meo63pxeXVWBn5br5Y/ivZkbuhBdkhPpknPCZokvRDnSI/PkB6P7JewNcryXkpFa4u+NK0LLrdUbIzVeLhOjX22TnXLaILXhYYSx6GMcu6QkR1TS9b7tD5TohIXcYbV6IVI/TM3TE+idP/NfkJyPEryZ91SkKdpS/6taWxKOBQe4MDoj53U4CAHZ7wwiGTvbCZDN4HYUksQa14rxdlvE8alDBEqmrdfAvITRHcfokBlfSIWj7b28XIX2O64qA0PXwX58XWk5amzZ9v9trtkzVuwQHvbQKa5QrUSWvcDu5Iy3HO9T/L3QIF574aZ1NZU7mUqI0/6W/c2bOloNbokjT9LT10X0P4x9KR6k8SpemNbjZD7rnIzWQXpix2BYxiINtsxZAmS7xiYTWy8Q/Bp5p+j6rzi50QWpv0RR8fD1SG83O+hRemv4/usxU/9B8NBRmr7kBQvpev3m8V6m5eBIo+zx9l/x3kZeZUQAAA=" + } + }, + { + "ID": "e2ad01a5514bc12a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "377" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "22bfe37e4589f30fc7da89c6f248bce7/9563819822921944368;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7ImV4dHJhY3QiOnsiZGVzdGluYXRpb25Gb3JtYXQiOiJDU1YiLCJkZXN0aW5hdGlvblVyaXMiOlsiZ3M6Ly9kdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDEzLmNzdiJdLCJzb3VyY2VUYWJsZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxMyJ9fX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IjZvNDlydGtwc3pZcEdWNkRURkd3aExEMVBleSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:43 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/uaJwtezhJMipokUqU6mUzaPaD5I\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703424000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vngg128:4127,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/59,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=T608W5mGJY3_qgWMk5KQBA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/59,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/59" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAK2TXW/aMBSG7/kVkXc7yCcJQZo2aayIqkNVIdU+OiHHHBI3Hw62QwoV/312INPUVu0ulitb533e9xwf5bFnoIyWazQ2UEyTbQ18/+6exei9KoDEiS7cocDJgskB9vAl90Sap9NN+WOyippvlmfW+LKRcEgvv9KKZdE28ovogK/xZDi7Q60Pbe3XdU5A9ivGZT/wnXG0GPjMC7nMKnH4Xk1v/cnyYtqkVxP7GvYtKCDfXNEy03gqZSXGptk0zSBhLMkBV1QMCCvMrnFz55gVZ/dApDCfxJlqKGG+EvgxZwRLysoP0aINV8ANbIBDSUA18NgzDHR2n700kIZa6lR9YzYDdXlarCJ7xlGnElZuaFLzrtTGwoPkmMjzVb0LqzmBJY7zrrN/6U1p1lhiAZ3mdFk5lj2yAstbeZYfOuHIHnqBE9grS30dKHXWCWuPr0C2izRzbEm0BiFp2U4Tcar5RG/x6XbibV8qZf9t8wERO/SSt1DmP0/d/o8I7fTrWc4F4wXWi0CfF7daczzvTUgsa9EtTN/0atBNNJ/P5lP0l4oqM/JHSTi0xktatIA9dFVbrj1y/fD0+hri8nk9UPWzbS2Ar6DANNcS1/fdMHRd2xv2Y3skLZsVMsROEGbETUhCt+k2y0cxi9P60xp2kLMK+CBRJjtKABPC6lLqPwv1jr3fuT1gWCIEAAA=" + } + }, + { + "ID": "fe18609620176371", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/6o49rtkpszYpGV6DTFGwhLD1Pey?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ae932442463692f97d3f48ecea8a2afd/16410847034275214445;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/6o49rtkpszYpGV6DTFGwhLD1Pey?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:44 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/uaJwtezhJMipokUqU6mUzaPaD5I\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:44 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnau23:4246,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/55,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=T608W_CMNYvMqAXxlrL4Ag" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/55,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/55" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqLkksKS1WslKo5lKA8FKBHKWgUD8/Tz93JS6FWh2oqsziksxkuMrkolSgUH5eSGYuWIOhqbGBuYGxoYWxmaWBkg7UsKISTHlzoDzQWK5aLgDYtUQ5gAAAAA==" + } + }, + { + "ID": "aa9682550f7917a6", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/6o49rtkpszYpGV6DTFGwhLD1Pey?alt=json\u0026fields=status%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ae932442463692f97d3f48ecea8a2afd/14802785598284495253;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/6o49rtkpszYpGV6DTFGwhLD1Pey?alt=json\u0026fields=status%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:45 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/kqVWiiaQi2dMjEo1ULYBW9GJHm4\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:45 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnme13:4459,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=UK08W4n8N4KHqwWJpq2YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqLkksKS1WslKo5lKA8FKBHCUXfz9XJS6FWh2oksziksxkuLLkolSgUH5eSGYuWLWhqbGBuYGxoYWxmaWBkg7UpKISTHlzmHxqXgqGrIm5qSlENjk/tyAnFWRFEMgioCoDPVOIvoqSosTkEqhLFJRSUoEuywO7JrQo0y0zJ9U5vzSvBOTUaJACoPFKIDoWSNQCPcRVywUATAsMT/cAAAA=" + } + }, + { + "ID": "f382fee6a76b15cf", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "422" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2645e381b3b99f0d66c34c4c82b096ec/13194725261805338046;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICogRlJPTSBjc3YiLCJ0YWJsZURlZmluaXRpb25zIjp7ImNzdiI6eyJjc3ZPcHRpb25zIjp7InNraXBMZWFkaW5nUm93cyI6IjEifSwic2NoZW1hIjp7ImZpZWxkcyI6W3sibmFtZSI6Im5hbWUiLCJ0eXBlIjoiU1RSSU5HIn0seyJuYW1lIjoibnVtIiwidHlwZSI6IklOVEVHRVIifV19LCJzb3VyY2VGb3JtYXQiOiJDU1YiLCJzb3VyY2VVcmlzIjpbImdzOi8vZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxMy5jc3YiXX19LCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6InA3bHptSDEzb3ZKWGdFeWtVYmdOOEVtSDAzOCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:45 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/K7v-mr89Li6nUYd6WjyM8-hRKns\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vna15:4288,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ua08W5nsA5eAqgXe7Ipo" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U247aSBB95yss79tqwPfbSKvdCDwTNoRIBpJsLrLa7bLpwXYbuw0iEf++3W08zCRRkifoPnXOqaqu8teRou5Ilaq3ipqQfN9Bc/rjgSbqDQeAoVwAn1TP3HmzL3CCsLDbbbG9z6oPs3hzfK/b2ivvMC4bP1gQt9r8l7rvHk6v/fE2elW1n1SpQ6R82hUY2LimDRt7rnm7WU1qr/hSvjQsevj3fR6edpskX/ph+VK3fElsocgWpNoJ+paxur3VtOPxOMkpzQtANWknmJbakLh2MLW6oQ+AWat9Y6fxolrtJ4Z/FxQjRmj112YlzTkhggwaqDDwBL6OFEW9qM9/VJAgSVaP/qI2RR38RDC3HCln4YpplZG8awZI2srqLofrSV2Fi3C6Vv5U7qI3rxXcHqQwTwxaRiqpsEZJMWT/O/kLNmKohUtMjP3M1xM9SAw/yXQzg8zxbAy6o/vg6DZkbup7rosGNhOGPRdVtNJN07FNPYstK3Vj2zSy2PcTFLtOangYAg/AUQXz3Kcu6TPISEVE+u01dVHecOCTQbsGw6YhIuJjf6mouRiQbx8+2Y8Zb8hYSsembvi6p9uxrbuBGfiGY3umZ8S6blgT4dFrfb4ZjPAWSnR1VtSMQJE+tX2EOFihUrS7/7253rNTLe9X62i+vFcH4HzzM42u/JHEfLkO78PoqnH583n0TPLSojvalIgJ3nT1dpATzXxTP2+wYOxIvQCUkiqP6FFAqnGx6U3OT14KN4AYzEhb05YMczyNwhfrMJ7fxcswnIWzy0AeG/Jd6LtoziPX0WY55ZRLYN0QymNPQ53Ri+l6/nZAuxYWkCN8Wu0LHpGhooWRzEquTssQ64aC5En2izss+54/RhG+H/gxUlYidoX0fTcci0+IZfiOYTn9rnJSw77HzcDu8ecLKq1LqNh6eHW5qOqTXHkpTcwHi4hCVMt1rSCwLMN2xnzRmG7QkgXI9IIdtnKck/12vyv8hCbb7p8UDlDQGppJzkUOBAPCmHYVE99CdXQe/Q/eKZe61AUAAA==" + } + }, + { + "ID": "7b8a12cb612fcae4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/p7lzmH13ovJXgEykUbgN8EmH038?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a745aa8ffd086e70f5633deb083c131c/11658720320340853479;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/p7lzmH13ovJXgEykUbgN8EmH038?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:46 GMT" + ], + "Etag": [ + "jNKGnL+zOFBk+wbSSzjrNQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4001,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/41.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/41.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/41" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41RXU/CMBR9369Y6iOQiCRCTHgQMwFdpmyQmBgftu4yN9p1tneSQfjvtGWKD8b4dHs+ek4/9o7rkk1epuTGJUmefdQgm4sMcGEWIaiaodKjEqUC0jVuwDgz7iJ4nJZ+Z/d0P9l0tkkU7QoZLMbjk0vRd+Cx9u010nidA0uVxq8Wuy1vtTLmYBLt7J55bCrLR8twHkx/KlykVglWvn878T3SSofuH+k1/y18Hiy9qRf+K93ON6ftIYVIQliDhJLC+aaVFAVQnNsnTWtGAXuVkNgbXl+1LWbnSa+GbMdn/YH4fHjJvGazSrJg5PHZ5WD05WWCxpiL0thXEfluR4ExC8XWPCoZkDM3aRDUsxQUlALb0j8Vm9o7wSsGaM6LsgZL01h/1ixHza1jpsA5OEclTwaxGAIAAA==" + } + }, + { + "ID": "c55078627fea87f7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0225420f_33d6_421f_88ba_65d17ce97ee5/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a745aa8ffd086e70f5633deb083c131c/1595571340812608763;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0225420f_33d6_421f_88ba_65d17ce97ee5/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:46 GMT" + ], + "Etag": [ + "nxzoB6ed23J8bGchRHC5/Q==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4194,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/41.esf,ybph8-v6:9829,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/66,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ej\" allowed_cluster: \"ws\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"el\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:c7c9::]:4109 had response of OK with response time of 0.018150806427001953 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybph8-v6:9829,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/41.esf,ybph8-v6:9829,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/41" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqcmrqMp3MktNMTL2skhyT84I8nA21Q+0tYWoKskvScwJyi8vBik1hogVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVodPOoMkdRBWbFcSLqItjCZSAuNqWVhEpEWGuG0kAvEquUCAD2rhdHEAQAA" + } + }, + { + "ID": "ae63853decc00fa8", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "527" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8f98eee7556934fbbf1dbe47e36c854d/59566399348123940;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJleHRlcm5hbERhdGFDb25maWd1cmF0aW9uIjp7ImNzdk9wdGlvbnMiOnsic2tpcExlYWRpbmdSb3dzIjoiMSJ9LCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJudW0iLCJ0eXBlIjoiSU5URUdFUiJ9XX0sInNvdXJjZUZvcm1hdCI6IkNTViIsInNvdXJjZVVyaXMiOlsiZ3M6Ly9kdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDEzLmNzdiJdfSwic2NoZW1hIjp7ImZpZWxkcyI6W3sibmFtZSI6Im5hbWUiLCJ0eXBlIjoiU1RSSU5HIn0seyJuYW1lIjoibnVtIiwidHlwZSI6IklOVEVHRVIifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:46 GMT" + ], + "Etag": [ + "xqY4KSyfKZypWmwZtZAoNg==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnk4:4129,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/110,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Uq08W5iDJsO_qgWW7LrADA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/110,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/110" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2S226bQBCG73kKtL21zTHGtpSLNHUjK64jYdKDq8haw0K2BhbvLiEo8rt3D9BWltuaCxD//jPzzey8GSbY4zIBMxPscHaoEW3fcbjLERiII8RhJo9eD9/8+3Wb3m/a6kvRbPjmhqyy62tlwio6qfMY8WFFKB8GY3eWQA4Z4lvXdiZ2YPtb3x5P3enEufIDN3C2tnhGqtI/LI6vKjCUp0tc7mWdZ84rNrOspmlGGSFZjmCF2SgmhdU3YL24VkXJDxRzZp1wWR0Xsy4BtBQgsy7kVLYQpYiiMkaC9s0wTdChLM6NSYYJUaN0jgu4dJgqp4MuADTMo5pl/IwK2LOlGOUJE3/fxZ+ShFjCQsLr70BrvK2Uto7CxeoOSPE4OBdTF6chi1U0v5uHOka8njoSYX3fciSrA9WRVJakzCJEz5yEpPlDiCmCHJMywrquc+WJzj1nMg4mnt7d1wrTc57x1OtGCHLI+CeSYDGG5O+Z+kbmX6N5uLpZduk5oiXMP4jruiVlirNaV+tHy0hNY/RI8a/xgkwu7ulC7g5D0Sof/vcKvVHMXuQUnwa/838ktIBc0t2uP+u1EK6HSpKwDkV497haIpjgMuvH6MhMx+4qchL37OBxDYyj8ROKpx2hGQQAAA==" + } + }, + { + "ID": "56efeaed85fad284", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "232" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "90677294d11c51aacc98290b6e7d7dc5/16897968665879932493;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICogRlJPTSBkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAudGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNCIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiVG1VTHZkZ2FNdjBYbDd0bEpEWFlCdUN0ZXNXIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:47 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/-n61TyheykPve8Jj4HzjlrZ7bss\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbt65:4165,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/144,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Uq08W7j1NJLbqwWA56-YDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/144,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/144" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aMBR951dE2ds0iPNBPipNWwdpR0XpBGHtqkqR49wEQ4hD7IBoxX+fE8jarVO3vFnnHJ9z7nWeOoq6onmsnilqRNNNBeX+3ZJF6gcJgMBpDTyojrFyho+wBz+z+CJbXCb5/TCc7+6QpXVzWw/2C9ivvm3BvVpaXx+XWXnvRJw/qM09tLk+rjIColuwUnQd2zibz3rBej7exim+3qK7zBHZ1fDux5dqIIDfNkIOWTKm+aqWL4Qo+Jmm7Xa7XspYmgEuKO8Rttba4NrW0IqSLYEIrv1hp8lSXHvD8FPGCBaU5R/ns8ZcCqaQQAk5ARngqaMo6un20d8K1aJGdUT/0U1RW7+aLC07yqF2JSxPaFqVLdTYNu1Oh+eTOvPH/iBQ3isX05trJcYCcxChgXQXOcgKLWR7hufqfcsxHD1E8usJHGXwBkW3mnCyHHBB8yZFUGta9/+YQa0+ZjlyQuImLoqQF+lulCAjgaTvWARQH7nQRxYkduw6to1bdRPyqMU5y+2kj12EjNCKdQgtF3thBGYcxhbYrof1xPZArZWHY3RSAhYwpLxgnLYTHkz988APRxfhxPeH/vBUc1fSV9Tb6Ugyg+l8MpCSE7EoKZPcZu6jSeBPzwfB6HuLVhzGkGKyn20yyUhwxkECh9NSucCi4u0261M9T1U6TEaTS/UFi8qpk1/Mpkm9AbpuBHrflGszddfR9dN7k6JSvMZN3Tzivz+dxnoNuQj2BTw/IfVFVlmlDGGNaV1ENW3b9DzT1K1+V65PIJ2thYcNx1sRMyUp3Sw2q8yNWLSoPsewhYwVUPZSecmWEsCEsCoX9V+qdg6dn9d1phJuBAAA" + } + }, + { + "ID": "c6e2c82d9dbb157b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TmULvdgaMv0Xl7tlJDXYBuCtesW?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "01403280b408a38ed3dd2504145497d5/15289907229889147766;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TmULvdgaMv0Xl7tlJDXYBuCtesW?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:48 GMT" + ], + "Etag": [ + "OkSkfhdoQ0bEne+dYg8Qfg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4321,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/89.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/89.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/89" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41R0U7CMBR931cs9dGRoCZiTHgQXBAzUcYWIcaH0t6NsW6d6x2GEP6dtkzxwRifbu85557T3u4c1yV5VnJy65Jlln40UG/PUsCpOYSgGoFKl0qWCohn1IA0NernfJYnKy6n3aVfwjlfpDfTJO33jyrFVlBQrdvpTvdJBoIr3b/Z3m1xy5W0AONoq3fCcVtZfBaF48noJ1NIbplJHAR3g8AnLbX3/nBvit/Mx5PIH/nhv9xtfXfaHLKWyxASqKFkcHppVcs1MBzblfJGMMBOJWvs9K4v2xQzeeSjIg42PKVPm+5c9FA83s8Xg2aIoF6/tEIyipksjTyeke90lEhFKD/NUskVOWGDrR5/qSUDpcCmXByDTexQFpUANPfFugELM6o/6yFDjSVUKHD2zgET7yXNGAIAAA==" + } + }, + { + "ID": "57d953d65a16578c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon6f5a8002_4d1e_48a9_be3d_d4e689a1f69e/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "01403280b408a38ed3dd2504145497d5/5298533274205459850;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon6f5a8002_4d1e_48a9_be3d_d4e689a1f69e/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:48 GMT" + ], + "Etag": [ + "3MQvwEng+KfYGQw9hFVnQg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4217,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/8.esf,ybcx185-v6:9811,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/84,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"oj\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:124f::]:4003 had response of OK with response time of 0.014862060546875 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybcx185-v6:9811,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/8.esf,ybcx185-v6:9811,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/8" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTH2DSwrd81L1/ZOi3QPLLfMcAvLC0y3tYWoKskvScwJyi8vBiuFiBVBuNFAtoJCNZgEiqbBhZCFwVJlIM2JSnChWh086gyR1EFZsVxIuoi2MJlIC42pZWESkRYa4bSQC8Sq5QIA4epPdsQBAAA=" + } + }, + { + "ID": "69af53d1eb11bd11", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0014?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e1c8a0dab36cccc40e63d5fa3bf99aca/3690471838214675123;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0014?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:49 GMT" + ], + "Etag": [ + "xqY4KSyfKZypWmwZtZAoNg==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:49 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnp127:4499,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/161,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=VK08W_GAOdCIqwWyt4GQDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/161,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/161" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2S226bQBCG73kKtL21zTHGtpSLNHUjK64jYdKDq8haw0K2BhbvLiEo8rt3D9BWltuaCxD//jPzzey8GSbY4zIBMxPscHaoEW3fcbjLERiII8RhJo9eD9/8+3Wb3m/a6kvRbPjmhqyy62tlwio6qfMY8WFFKB8GY3eWQA4Z4lvXdiZ2YPtb3x5P3enEufIDN3C2tnhGqtI/LI6vKjCUp0tc7mWdZ84rNrOspmlGGSFZjmCF2SgmhdU3YL24VkXJDxRzZp1wWR0Xsy4BtBQgsy7kVLYQpYiiMkaC9s0wTdChLM6NSYYJUaN0jgu4dJgqp4MuADTMo5pl/IwK2LOlGOUJE3/fxZ+ShFjCQsLr70BrvK2Uto7CxeoOSPE4OBdTF6chi1U0v5uHOka8njoSYX3fciSrA9WRVJakzCJEz5yEpPlDiCmCHJMywrquc+WJzj1nMg4mnt7d1wrTc57x1OtGCHLI+CeSYDGG5O+Z+kbmX6N5uLpZduk5oiXMP4jruiVlirNaV+tHy0hNY/RI8a/xgkwu7ulC7g5D0Sof/vcKvVHMXuQUnwa/838ktIBc0t2uP+u1EK6HSpKwDkV497haIpjgMuvH6MhMx+4qchL37OBxDYyj8ROKpx2hGQQAAA==" + } + }, + { + "ID": "8f25bf38c955133d", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/dulcet-port-762/o/bq-test-table_20180704_40692981547271_0013.csv?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5f2bb86b7bf49e46f59e1a207abb1f96/12145841951945571527;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/dulcet-port-762/o/bq-test-table_20180704_40692981547271_0013.csv?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:49 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703489000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrm187:4158,/bns/yk/borg/yk/bns/blobstore2/bitpusher/842.scotty,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Va08W5CFDNOZqwWA9pyIBA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/blobstore2/bitpusher/842.scotty,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/blobstore2/bitpusher/842:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjdnVUJQazNwQmRIS185bmdmYnJxc18zZ2tXS0U0ZHRUVTdsSXhHVG9KUTFNaW1KNGY5bkFIb3Q4UGVBX2w5YTdpM3NkR1RJRG1HelFLWW5NVks3cE1JSy1hR21CVG1GaDZfT0d1dGlwMkJnYmhDaGpfZEFPaE1LQUNpZ3pWNC1MWExtRklYMlFMZjRvNVhTUjlfQlhhRDZJTThCNG9Kb1BDanpCWUxnU1A4aEVoend2aXNLRURlWTRFSm8wBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq8iia37Unv0ASA-hfz27BTWGug27gV602OaQyN6laa_LGX6PvvMPVSWu1UlHi6l8FwZYSoBJ5N_2RATMzTR2l8I2yXV94ZEkYMX0HEYUERm667Pcc" + ] + }, + "Body": "" + } + }, + { + "ID": "8a5f3ab81a1f5964", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0013?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "af4f401f77a410c8b719f16e8fa66071/10537780515954786800;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0013?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:49 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmp20:4344,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/142,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Va08W_mKGYHMqwWCx6ToDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/142,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/142" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "ea1eeb1c72cbce73", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "00752514d152e38710cdd29029f118de/9001775574490301977;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNSJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:49 GMT" + ], + "Etag": [ + "kTKzU4fS8xZH3jRLCNoyjA==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnda124:4445,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/4,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Va08W5qSIc2ZqwWqw42ABQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/4,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/4" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TUW/aMBDH3/MpIu+V4iQEAkh9gDbq0DKYQvqyqUImOVJDEmexKWUV332OnUgM0Ym8OP77/ne/O8sfhol2tEjQ2ERrmv7eQ3X8Isg6A9SRRyBIWh/tom9/nt3Ncvj+82tvGwYPc3bcTu7vVRBV7mSfxSDuSlaJO2/gjBMiCAexcix7aHmWu3KtwcgZDe2+6zmevbLk11WV/hNi91UFDtkmoMWurvMqRMnHGB8Oh27KWJoBKSnvxizHbQP4zcFlxbYQC44vuHDDxfEtgFgBcnwjpwoLYQMVFDFI2g/DNFGDMrs2ptomRY3SRNzApW2qnDbdAGiYJzXL+BVy0rJtKGQJl7tfcqckKRYkr+H12tGaOJZKW0bhbP6EavHUuebZ5/zSM5tH/pMftnLOEiWH/g9/EvmPnyerIL7MFfoPi/CxVf/Fb/1nGdaMZU3wWZLpYhH4kznS+kktL0bz+9IMSnYyPQqosyM18FoJWJFGUF05CdnhTIgrIIKyIqKaw+735MX07OHIc1z9tN5LWl2LGYx6zQ2jjHDxnSVUtpl8nqntKppMA1/7WKwy1+rzEhkn4y9t2m3X6AMAAA==" + } + }, + { + "ID": "564a93e9079fc5b5", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015/insertAll?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "105" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5052583a1962b5340fd2f5d50a880e41/7393715242289269570;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015/insertAll?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyb3dzIjpbeyJpbnNlcnRJZCI6Im1EaGRXS2VEb1ZVSWMwcFkxQTdCUjkwb2NHWCIsImpzb24iOnsibmFtZSI6bnVsbCwibnVtcyI6W10sInJlYyI6eyJib29sIjpudWxsfX19XX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:49 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/17.esf,ybke11-v6:9866,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/48,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-streaming-frontend-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-streaming-frontend-thermostat\" pick_cluster_params { allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:de4d::]:4102 had response of OK with response time of 0.014403343200683594 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybke11-v6:9866,/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/17.esf,ybke11-v6:9866,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-streaming-frontend-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/17" + ], + "X-Google-Service": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0zCtOLSpxzMkJSi0uyAdylLhquQAsgTGpNAAAAA==" + } + }, + { + "ID": "7e0db3dfec0a63c4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2081b1875cf137f8b572b725f3f72acc/15848802785843572054;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:50 GMT" + ], + "Etag": [ + "OeoeJjfMXkYOGSi9brJLWg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4139,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26.esf,ybba187-v6:9866,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/93,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"wo\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:3709::]:4135 had response of OK with response time of 0.012969255447387695 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybba187-v6:9866,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26.esf,ybba187-v6:9866,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/26" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfFPzU/1ykrzjciO9HcPzrRMKvLyCU+3tYWoKskvScwJyi8vBik1gIgVQbjRQLaCQjWYBIqmwYWQhcFSZUCpvNKcHLhYrQ5uhdGxRClDFsCwHVMPLpdALULhx3Jhk4GxILIgXixXLRcAbDsy6W8BAAA=" + } + }, + { + "ID": "cd25412b329c211d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015?alt=json\u0026fields=schema", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b48e262b8c79177ad627b021d44d382a/5785653806315327338;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015?alt=json\u0026fields=schema" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:50 GMT" + ], + "Etag": [ + "kTKzU4fS8xZH3jRLCNoyjA==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:50 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlm18:4038,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/7,ykkk191-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Va08W7G2PIenqgWf_quwBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykkk191-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/7,ykkk191-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/7" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKvmUlAqTs5IzU1UslKo5lJQUErLTM1JKQbyooE8sBBQMC8xNxUoBKF1IGIllQVgseCQIE8/dyWQYK0ONj2lucXoejz9QlzdXYNgwrn5KWDhINcAV8cQVxfchhWlJqObFeTq7B/kAhNFdT5MP5IJSfn5OVDFSIY4+fv7uDr6KUHEa8FULBeUCWTUctVyAQDcRh1+LAEAAA==" + } + }, + { + "ID": "2c5aad16ce227404", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "298" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "034ed53322525dc48172e69119449dad/4249648864850842771;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7ImRlZmF1bHREYXRhc2V0Ijp7ImRhdGFzZXRJZCI6ImRhdGFzZXRfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAwMCIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9LCJxdWVyeSI6InNlbGVjdCBuYW1lIGZyb20gdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiSVA4VlJRQXVrb0JGNWJNVWdmTEwyNzd2cjFWIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:50 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/YFIcbMJzISzLYJdvwRggTss5-jw\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnpp127:4466,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/77,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Vq08W5GeEILqqQXwq7agCg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/77,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/77" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T227aQBB95yss97Xg9d2OVLUpOJEjglowaVNFstbL2GwwXuNdg0jEv3dtcC5NlfZxNXPmXGb2saeoK1os1DNFTWi2qaHaf7hnifpRFkDgrCncqa6xckcPsIcgt/gyX16mxa9RPN/9RJZ2exGS5PrqIZw9jG+vFtvdNMsizu3+/e5ObefQdvyizgmIfskq0Xcd42w+G4TfvJvp9/N6xb5e2Mn1PEvHY8N1t5V+0wI55OmYFqsGvhSi5GeattvtBhljWQ64pHxA2FrrhGtbQysrdg9EcO0POk2a4to7hJ9zRrCgrPg0n7XkEjCFFCooCEgBjz1FUU/Tw78ZakAt6lj9hzdF7fiaZknZUw4NK2FFSrO66kotbevu9Hh+NfFIMUqB16CkFVsrAic5xAbSPeQiK7aQ4xu+p9uWa7h6jJBut9RSOnBBi5YjajDd7P9w2KCxwBxOPTHxUg8lyE90L0mRkUJquxYBZCMPbGRB6iw813Fwh25FHrG4YIWt65atm3Zs4wRiy0zNOLHcJLYMtLAdWBi6Y6oN8nCUTirAAkaUl4zTLr/hNDiPgji8iCdBMApGJ5u7ir5p/TENZWc0nU+GEvKUR4rrXIyOxp7TeOX09HgnX4Q6k+/G+MJNWVEmRbbrDCdRMD0fRuFNJ6vmMIYMk/1sk8uOFOccZOFwuhUusKh5dyTNq1mkKq1Nwsml+qKLynWTp842wmb1dN0CdNuUfkzdR6Z/OmMJqsTbuq37x/rri2yp11CIaF+2iFkwDoaR+kKrtFLFsMa0MaKajmP6vmnK1ffl3Qiks7XwseH6K2JmJKOb5WaVewlLlvWXBWwhZyVUg0wO2VICmBBWF6L5/Grv0PsN1q9brcUEAAA=" + } + }, + { + "ID": "0112947c2e76a98d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/IP8VRQAukoBF5bMUgfLL277vr1V?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "123b80ced9fbb8c594a1f324b4e77475/2641588528354908604;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/IP8VRQAukoBF5bMUgfLL277vr1V?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:51 GMT" + ], + "Etag": [ + "z5np2PpknOlxKB5c3dodnQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4405,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/113.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/113.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/113" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QS1PCMBC+91d04hVmBAdxnOFAHR+MFaEIF8dDSLe1kGZjslWR4b+bBBAv2XyPzX6bbRTHbF2pnF3HbFmVHw2YzVkJNPWXDGwjybqiUVlgLe8G4qV3//SU7k70Wj3L78ekJy5yzNV0MNi7rHiHmjvf1iGHiwpkbh1+DTg+8EFTvAb/YqitE08bHfjZSzYa3/9XasyDMp6n6TBJb9lB2oX65s5dSLHCZQYFGFACTlm0wRUIGoWl80YKoLZGQ+3+ZfcwxXfu9dHkapFNh80ak7ve8mleFmna7fc/TWdx9EoUnCpU3j6fsb/phMRlhl9+bdZhJy7ZENiJQQHWQphyzo55b7DWEsjHJdNAoAV3v/lQkeMKLi1Eu+gXU8NilbkBAAA=" + } + }, + { + "ID": "739499cb20e10dae", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon51145135_5abe_43f3_b47b_420d56ed2163/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "123b80ced9fbb8c594a1f324b4e77475/11096676071909211088;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon51145135_5abe_43f3_b47b_420d56ed2163/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:51 GMT" + ], + "Etag": [ + "R3bOtHcCAS20XmZd/decHA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4184,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29.esf,ybar66-v6:9843,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/110,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jx\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e1c2::]:4062 had response of OK with response time of 0.012644529342651367 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybar66-v6:9843,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29.esf,ybar66-v6:9843,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/29" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQkyTvIv8Uh2dgw2MojIjUrRT0lN9nC0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZUCpvNKcHLhYLZQVywXjxXLVcgEANPOSwrsAAAA=" + } + }, + { + "ID": "3cda810db7dfd0b8", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "94f90983f7c56f74048e8d1c2f6edbba/9488615735413276921;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0015?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:51 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnap199:4017,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=V608W4H_KYT0qgXmtqDQBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/140" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "1ca72e183f11c143", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "213" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e2cd228cb8642e47197712944661ac08/7880554303717393698;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0IHdvcmQgZnJvbSBbYmlncXVlcnktcHVibGljLWRhdGE6c2FtcGxlcy5zaGFrZXNwZWFyZV0gbGltaXQgMSIsInVzZUxlZ2FjeVNxbCI6dHJ1ZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJhY3V0SHhMNlU3WDBaZkp6MVRKZGM2bHo0bDMiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:52 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/nG49_QLfkOTJHKEkApQnCjqhEAs\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneg123:4118,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/11,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=V608W9mdMozzqgXBl5rgBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/11,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/11" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aMBR951dE2esgn+QDadoQZC0dYiqEreo6Ice5CW6cOMROGa3473MC2bp12vYSyTnn3HPPvfZTT1EzUsTqSFEjku5qqA6v7lmkvpYACJQ2wJ3qmpk7fYQDBNTmW7q9SIrb6Wa9v9Ftrbiw/c31PMk+hleXH4JsXF4Xk/vdNhjzO7WtQ9rycU0xiH7JKtF3HXO0Xg0QrsXlt7mzdm/02+Tq0QivYuzQR5tarZADTeakyBr5VoiSjzRtv98PUsZSCqgkfIBZrnWNaw+mVlbsHrDg2m92mgzFtb8YvqUMI0FY8Wa9as2lYAkJVFBgkA089RRFPVef/SlQI2pVJ/Qf2RS182vI0rKnHBtXzIqEpHXVQa1tm+58+HlqxiObUfasipWkYrnypZtEv6wjSnA/RgKNOMpLCnzAtygDXgKq4KtCSU6EYrStyCjABSlazxBFtMv7P4kbtXThcOZssJd4eqT7keFFiW4mkAxdG4M+1D0Y6jYkTuy5joM6tWgMzzMrWGHYURTptgMmAjux0NCPTDPCvvw4thUlpmfGMWBLbdTHU/u4AiRgSnjJOOlmOlkG4zDYzN5vFkEwDabnqPuKvKB+Xs4kM1yuFxMpORPLijDJbSc9W4TBcjwJZ586tOYwhxThw2pHJUNUNcj/x/MWuUCi5t36mlMzUlUaLGaLC/UZi8jB4x/MNkizBJK3AmNo6a5uGb7hm/rp2khRJV7gpq47J/zXu9Ja51CI8FC2ilUwDyah+qxXmaTaQI5Ik0O1HMfyfcsy7GFfblDoBsuFj0zXz7CV4pTstruMehGLtvW7GB6AshKqQSqLPBAMCGNWF6J5lmrv2PsOdeyw9F8EAAA=" + } + }, + { + "ID": "df8c3cfa2a151813", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/acutHxL6U7X0ZfJz1TJdc6lz4l3?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e4d207954503614dd490795da46021b3/6272492867726608971;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/acutHxL6U7X0ZfJz1TJdc6lz4l3?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:52 GMT" + ], + "Etag": [ + "gdninLjFrHkPSHRJ2vbBtw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4307,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/5.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/5.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/5" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qskgGYkJjyIUQdZCA6WGI0PpT1moWtnexMH4X+3LUN86fX7cb3veuhEEdkKxcldRFai+KrBNFcF4Iu/ZGBridaVSisL5Nq7AWnh3QVXQqWbJ5Ns54skmw6+V2PcjUYnl2WfUFLnOzjk8FqA5Nbh94Cjlg+aoiX4F3fa8NDd8thUgV8ss8ns+b9Sah6UWZ6m9+P0kbTSMdQPdx5Dio1eZbAGA4rBJUtl9AYYTsLSvJYMsFtpg91hPGin+M6TTlmNyU8a58PX3tt6uu8vp5zFcn8rb85eqRlFoZW35wvyNx01UpnpnV+b9MmFGzcIdm40A2shTOmRc94HXVYS0MdFU0OgGXW/mQhsuc6x8wvbKEI3uAEAAA==" + } + }, + { + "ID": "b83328492a049897", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "214" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "eb1137d1ab408c6cf3fdfdf57b87d622/4736489025773817459;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0IHdvcmQgZnJvbSBbYmlncXVlcnktcHVibGljLWRhdGE6c2FtcGxlcy5zaGFrZXNwZWFyZV0gbGltaXQgMSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiaTNWTU1hdGRKNlQ0MUtCbk1GVFBTS21FVHd3IiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:52 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/WxejWJQskydMM6XowMhbqnrY4dQ\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnt135:4087,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/52,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=WK08W8mvEsKbqgWKtKHoAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/52,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/52" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAANVZ3XIauRK+91OoWC44VfGY4R+qXHt8bJyQNY6Dwdk9tssRMw0ozEhjjcaYbOXdt6UZMJAB7Nj52UoKI3Xr+7qlVquF/t4hmTHjbqZBMn02vI1ATn/7JPqZVygARYdacJWpFsbVo88whaZXCkfe6PWA///opjf5M1/a+3APnz68fR+Op267XflTTNqj/i2Xf5Xc91cZg8MMvBt5DqjdQEi1W60UGr1zixUv2m2q3LeVbsn+43+8fdw9O//Db3YnEzMwBG9wwvhYDx8pFYSNvb3JZGINhRh6QAMWWo7w92aG790V9gIpPoGjwr0Vuj10KtzbQPi7JxyqmOD7vXNDjgM6MAAJ3AE04O8dQjIJeivNIT3IjIqlW3wjmRmfVkbKHfJFszqCD9gwkjORoTXeJY2Hlp4eNIZMhHTJQAqfXM5mYjeI+h5zdl2qaCOkfuBBaIUjOoYwACrhmnjMZ4rYxhTtFxOSKYPaOu02OweH3dZFM5FGIZzAkDrT81sPNQbUCwEFXxKTQ0VVFM5s1S09X5mjd6cxQAakFLIDYeSpuRMSaBj7zvgd9Zj73ngVEy5Oze1Cvwv9aNjiA6EFl63Ti4OT1tFN6/Ss170mVA4jH7i6MXTkKsrniw7l1Jt+BrnU6TEODWK/coQX+Ry/1Uw/xJ+vSMfY1iCOcGF/iYX4EIZ0CPs88jxizNl/KV5iVM+kUGLfFT5lqHGVcTwRudYIPHZvNbXCkRFdZa64Nk+rvO81O3/dNDuddx3dPbNHi86nXNH7GLlBehzuA4wYcMnV1VXmUn8Qqsil3bBr1xZpDYgasZDgf0oU7XtAmItIbMBAviIQOjQAVAHCqQ9kwtSIfMR+a2iRj0bf0oKPBKN3BBI1KSeXD4JrS9s3W9wbNQ3Q/nfdN83OQ6+22iy5VjUTfMNwwXX3S020RsZ/GKe4wn6STKzFicZuX3Cree9AoI0Ks2dmP1nGMrMMuQeh9Yne0Ua5WvvPZtQQ5B1ICzOEdSTBB88A9RTzLJ9O+3CIe0LBG61rJMe4pc/N3jrg7hEmY+aFudWRhrtUKv407mK98u3czrfTVqqPp31tpJi9TJLpSsrDgZA+SuFeSeqo5r0CHuJitgVmdQhz60fE7OUnTPh6rCGoh2acAT/gpjqOuGMiCyehq3fPdnsK9V/LnvyL2GMOBgwQI9jGaReeEBEnmG5inLeifyi4ksLzUOaMwBnn1kkNT6FYLzyRaC1HCnyt8oQ8sjQ+jt2snRCkyGaLU34Sg96Q0KbOCLPqIvpifzI1tp3/ZugYuMUVSMzp6QR2vfy02VmC8Kjfd2lWRhwl2cIaimrtGRSIbdprsGv1J8RoCjZ2fQ+r4R6caK3R1bL9OGgpwqllkjkCdXSDcRSpi2Qbu9r8JXlMYOdLzyPA1jxuUvAL5UceUevwN4LXH5nqvgm89kzL00Dr1fkmDSNuSRjoa4T1GjhIvUptUCPhHjgO1rpCFkr5GoLdiTHkenzMxYSTcxFJB9JAjvBKMsRCjg+XUVp4BZmhbNSJ65n56aqbuG/5cM4Qj5lhJa14VH3bFkjqOjNZekI6gAVdqLDmvp/qzEY9L7fan2BvTWxrsI9oEIB8I8T4GOsMIadZPLgc9D2Ra8lXxF8NSo6GbefORv8KG/0rbito1mBfUPl50btUf1aUZjXUtl3/Pd1Z3DcLGAeRy9SiP19HxapK4s2zYu/7rA0W9W642ZlVlZixui3h/4SV0bUMVutb3PlKKWbdeln4CatzIoabnVlWSG6Zv2CYxRhJQk7HLleXzjF9YgRGmjVKxeLsuEo/YdbYE+jfSuLPAg3Y/PzLzr6cYygxB7LJ3zMqqQ942oY42yPKXQ8SU+fHZFLHVsqFtFzLQVkycNBafVQlgdoJnHPzbXaQJ0Go6zWUtfhBEHgs/nUjt1Y5maa0ON3MeoiOcPDQIzw0vGluVZAcHdW0KuVFkNP3VhwqkbnkC+5EUgJX1kE/NPft+eXtOFKRhOxK23LFvCe3ZkyyUqXUc/lHsRefz67DZDOLbafNsIrLCEuXE5BEUXax0Yk4Nz+9IUGLJ525RY1k35fTroOPhsfw0B6k4FbStlAq7oL7885DzFR96oxj81t8BJJhcZpIT0WPY0WYQlsspKWxF6JN47MfzbdudVIXJfVWkR5ZbSGhaa5wAlNbOVmRpd4kkvKpN6F/eSgdxtOwkHYfbFyRJRGStmt/QGCmVVX/4sA0d7PVWOyOJFD3TAhvFnsa6IOQY5C5FGGS4UpPRc3GkLGVa2ArhdISqrlHxsoL45KSt6Qrq+SdKXnl0a9M619QMpc/+vUkox/dHl7U9JPbpbbXvKhtflJb+6b2SzurnxjJ9cIzIwsVc+ZPjeb1AF3qMt+Yb5eL+Wq+aNexKohfNvUgqVLkhXLyNMnddGlCGoX6acmnTD98ZoqVSrFeLxbtUnm3b9dU3ha+qtNCtT52ikNnyG5Ht2Ov1hf9UfRfF+7AE4H+WT2M60/qOCLCaMbtmNn5svMPj2F2w3wfAAA=" + } + }, + { + "ID": "8493031e2ab09640", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/i3VMMatdJ6T41KBnMFTPSKmETww?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b8025100a8aa86d101a4cb6056e0375a/3128427589783032732;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/i3VMMatdJ6T41KBnMFTPSKmETww?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:52 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4051,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/84.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/84.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/84" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/9WQzWrDMBCE736KRecgEuih+BZoKYY00J/04ph6Y20cgS250qZNCH73SoqheYWCQMzMp2W0lwxAkHPWiRwuQQTZWEVB3c3ns6vRk/fYRk+8nQ3jCdKLHDaGTgM1TAq2otwKQIZykS/uKwnFHvigPYSDwLjrCLQiw3qvyc2AfIMDBYTAYE/wo/kAdfBlK6FOvIxBDQ4D5AKJBsq/oJJiqpfK+NCuTBqmf/yD6qmisj1qExu2nd1hd5s5Qm9Tps03dlq9HMmdb4nONsj6ygzownQm5+WXmJAx3dW0Ks/Ix7gqUaw/lqvi4XP5+rR5fly/R37MxuwX442FphECAAA=" + } + }, + { + "ID": "2dfca80a580d90f8", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "214" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6aa8f72328c883f66ed8598a653445a5/1520367253287098565;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0IHdvcmQgZnJvbSBbYmlncXVlcnktcHVibGljLWRhdGE6c2FtcGxlcy5zaGFrZXNwZWFyZV0gbGltaXQgMSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiOTZOY2d3T3JRVXJtQnkwNE00SWFqOXNxR3kzIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:52 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/V1IzJBD7qvMdjvPkgjH_8dX14lk\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vngg128:4127,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=WK08W7H1J4L8qwXf556wDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/30" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAANVZbXMaORL+7l+hYvnAVcVjhneoct15DUnYsh0HG+/eGZcjZpphjEYaazS2yVb++7U0AwYygB07L1tJYaRuPU+31Gq10N87JDfxuZtrkdzQ925jkNPfbsQw9wYFoKinBYNcvTSptz/DFDqsEo3Z+N2I/6993b//q1jZu7C7n//4vV2/vTt2b+5OJ97N++uG+5ddYZNBzuD4Bt6NmQNqNxRS7dZrpVb/zGrWThzv/oP82JfB79Ni5bjSpTfN6PbdtGwGRsBGRz6f6OFjpcKotbd3f39veUJ4DGjoR5Yjgr2Z4Xt3pb1QihtwVLS3QreHTkV7Gwj/zYRDlS/4fv/MkOOAHoxAAncADfh7h5Bcit7NckgPMqMS6RbfSG7Gp5WRcod80ayO4CPfi+VMZGiNd2njsaWnB40h90K6ZCRFQC5nM7EbxkPmO7suVbQV0SBkEFnRmE4gCoFKuCLMD3xFbGOK9ssX0lcGtXty3ukdHJ53LzqpNI7gCDzqTM9uGWqMKIsABV9SkyNFVRzNbNUtPV+59oeTBCAHUgrZgyhmau6EBBolvvv8jjLf/Wi8SggXp+Z2od+FYex1+UhowWX35OLgqNu+7p6c9s+vCJVeHABX14aODOJisexQTtn0M8ilTuZzaBH7jSNYHHD81jD9kHy+IT1jW4s4woX9JRYSQBRRD/Z5zBgx5uy/Fi8xqqdSKLHvioD6qDHIOUzErjUG5j9YHa3QNqJBbsC1eVrlY7/T++91p9f70NPdM3u06GzKFX1IkFukz+EhxIgBlwwGg9yl/iBUkUu7ZTeuLNIdETX2I4L/KVF0yID4LiL5Ix/kGwKRQ0NAFSCcBkDufTUmn7Df8izyyehbWvCJYPSOQaIm5eTyUXBlaftmi3utpiHa/+H8faf32KutNkuuVc0EX/u44Lr7tSZaI+M/jFNc4SBNJtbiRGN3ILjVeXAg1EZF+VOznyxjmVmGwqPQuqF3tFWtN/61GTUCeQfSwgxhtSUEwAxQX/nMCuh0CIe4JxS817pG8ha39JnZWwfcbWMy9llUWB1puCuV8k/jLjdr387tfDttrf502ndGitnLJJlzSXk0EjJAKTwoSR3VeVDAI1zMY4FZHaLC+hEJe/UZE74eywP12Ewy4J+4qd7G3DGRhZNwrnfPdntKzV/LnuKr2GMOBgwQI9jGaZeeERFHmG4SnD/E8FBwJQVjKHPG4EwK66SGp1Rulp5JtJYjA75Re0YeWRqfxG7eTgkyZLPFqT6LQW9IOKbOGLPqIvpifzo1tl38ZugEuMsVSMzp2QR2s/q82VmCYDQYujQvY46SfGkNRb3xAgrENu012I3mM2I0Axu7vofV8ABOvNboetV+GrQU0dQyyRyBerrhcxSpi3Qbu9r8JXlCYBcrLyPA1jxuMvBL1SceUevwN4I3n5jqvgm88ULLs0Cb9fkmjWJuSRjpa4T1DjhIvUrHoMbCPXAcrHWFLJXqTQS7ExMo9PmEi3tOzkQsHcgCaeOVxMNCjnvLKF28gsxQNuok9cz8dNVN3LfcmzMkY2ZYaSsZ1dy2BdK6zkyWnpAeYEEXKay5H6Y6s1HGCqv9KfbWxLYGu03DEOR7ISZvsc4QcprHg8tB31O5lnxF/NWg9GjYdu5s9K+00b/ytoJmDfYFlZ8Xvcv0Z0VpVkNt2/Xf053FfbOAcRC7vlr05+uoWFVJvXlR7H2ftcGi3o02O7OqkjDWtyX8n7AyupbBan2LO18pJaxbLws/YXWOhLfZmWWF9Jb5C4ZZgpEm5Gzsan3pHNMnRmikeaNULs+Oq+wTZo09of6tJPks0dCfn3/52ZczDCXfgXz695RKGgCethHO9phyl0Fq6vyYTOvYWrWUlWs5KEuGDlqrj6o0UHuhc2a+zQ7yNAh1vYayLj8IQ+Ynv24U1iqn05QVp5tZD9ERDgw9wkODTQurgvToqGdVKa+CnL23klCJzSVfcCeWEriyDoaRuW/PL29vYxVLyK+0LVfMewprxqQrVck8l38Ue/nl7DpMNrPYdtYMq6SMsHQ5AWkU5RcbvZhz89MbEnR52llY1Ej3fTXrOvhkeAwP7UEGbi1rC2XiLrg/7zzETDWkziQxv8vHIH0sTlPpiehzrAgzaMulrDT2SrRZfPaT+datTuaiZN4qsiPrWEjomCucwNRWTVdkqTeNpGLmTegfHkqHyTQspN1HG1dkaYRk7dofEJhZVdU/ODDN3Ww1Fs/HEqh7KgSbxZ4G+lPICchChjDNcJXnouYTyMTKNbC1UmUJ1dwjE+WFcWnJW9GVVfrOlL7y6Fem9S8oucsf/XqS049ujy9q+sntUttrXtQ2P6mtfVP7pZ3VT4zkauGZ0Y+U78yfGs3rAbp07gfGfLtaLtaLZbtZatjF5OkRB0mVIS/X06dJ7mZLU9I40k9LAfX1w2euXKuVm81y2a5Ud4d2QxVtEagmLdWbE6fsOZ5/O76dsMZQDMfxf1y4AyZC/bN6lNSf1HFEjNGM2zG382Xn/5C2miR8HwAA" + } + }, + { + "ID": "fcb9c77bdcfa8041", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/96NcgwOrQUrmBy04M4Iaj9sqGy3?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b4a26d80f18ff8b072b4d36b228ecb82/18430824914850356718;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/96NcgwOrQUrmBy04M4Iaj9sqGy3?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:53 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4132,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/49.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/49.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/49" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/9WQzWrDMBCE736KRecgEuih+BZoKYY00J/04ph6Y20cgS250qZNCH73SoqheYWCQMzMp2W0lwxAkHPWiRwuQQTZWEVB3c3ns6vRk/fYRk+8nQ3jCdKLHDaGTgM1TAq2otwKQIZykS/uKwnFHvigPYSDwLjrCLQiw3qvyc2AfIMDBYTAYE/wo/kAdfBlK6FOvIxBDQ4D5AKJBsq/oJJiqpfK+NCuTBqmf/yD6qmisj1qExu2nd1hd5s5Qm9Tps03dlq9HMmdb4nONsj6ygzownQm5+WXmJAx3dW0Ks/Ix7gqUaw/lqvi4XP5+rR5fly/R37MxuwX442FphECAAA=" + } + }, + { + "ID": "fcfeee1ffe02a6c5", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "213" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "63fd26c4e6c54654e0b24ddbd97ceea1/6831670993857627179;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0IHdvcmQgZnJvbSBgYmlncXVlcnktcHVibGljLWRhdGEuc2FtcGxlcy5zaGFrZXNwZWFyZWAgbGltaXQgMSIsInVzZUxlZ2FjeVNxbCI6dHJ1ZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJGZVVZb2xSbU5DQkFtWGFWcWk3ZlhCYmlWTFUiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:53 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/mq8mLg9_TMPBvRAKT41nzAjKoIo\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnww67:4387,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Wa08W-n7A4nsqwXJ4IaoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAANVZbXPiOBL+nl/h4vjAVm0MxrwEqqbuGMLscJtkswRyczeZygpbGA+25MhyErI1//1asuwAEW/DvFQqVUmklp6nu9VSt6y/j4zCzCduoW0Uxr53l2A2/8dnOi78CgLMkScEN4VmddY8fcJz3Atq8TSY/jYh/zu9HT18qNTK4d1JeOa1bofnl2/vB53fhzWLPHU+/0779KYgcXwJ7yaBg/lxRBk/bjaq7dGV+Q6P/kuDQXjRfdsJP6DrO785+fB27F+fjeTEGAeTM5/MxPQp51HcLpcfHh5Mj1IvwCjyY9OhYTlTvHxfLUeMfsYOj8srdGUwKi5vIPxnQB3EfUrejK4kOUwY4AlmmDgYFPj7yDAKCr2vM0hMkrNS6RbbjELGJwYD5ZHxRbA6lEx8L2GZSNJK61TjuSXcA8oYD5S5xoTR0Pgr88RxlIwD3zl2EUdmjMIowLEZT9EMxxFGDP9lBH7oc8OSqgi7fMp8LlH7F8PeoNMd9q97SprE+Ax7yJlf3QUwgrMEQ/8XpXHMEU/iTFXREu4qnP5xkc4vYMYoG+A4CXhuA8MoTk33yT0KIEJSqkWnaK1p66xRk108Trw+mVAx+2P/4rpz1j+97V9cjoaffjUGkrJtONTFb5aERojjGHnQm+picDQOsEFQiNt6n2q1uCEfh2xuJLFPPAP8QFwEC3P155lRyoLXCWjiqvBdDl2XOnGZZfFWzqYfx3dBGRPQB0CPF3t/+WQa0uA3JAkCQzr5klFO37g0RD4YelNI6aY48B/NnhhwKkU3hRsivCCGZI6A36OeECDmJSEmXAoP88cPd4jQP4ugWz6PQN0/hu97g+deYdUeFgAe/EBIQ9SEuZoLXoXukBKz9+jgSODHxUuJaaqw7pMo4aVnsfkZ3aO2Vav/shk3xuweswz+WkAhTllxKFbiEjGQm5H4U8plKbRdrVV2w4aTypRwsDVpAD1xcanZB3+kHH3CMSMoKC0PV7ZUq9+Qj6UNPVOlsSdTj3A279JAHJPgIAU+pCNCKAvBcU/Yzc/4km5K6lP75DDiEEJqyDC+gE33NvEDF2xaT1dtHkjnYS674w5xr338sImrvmMkCq4/xabpQCDMn6AHuW7KUlrul7iNxlfjypDLV2UDh9Vo7BHqZ3ACBBLl33TcpeAL4QlmBhS5slskjjzS141OnWY197BuJ+IthFV7j9BfB+VMsTPbQlSrW3sSreXQwJ809ojspfnnFKotXLQUgUamtmplj5UBlCtRrpwjZ+qTJfTF/mzRrT2ibRVCAufRpSWwWvX9vLMEEaBw7KIiSwhIitU1FM2TAygAW7bXYJ+0mgdhQ9f30Bo/YidZq3Rz13hnNJ6b70UPAA1Ewycg4ir9YleovyRPCaxK7TACaOVxo8Gv1nc8GdbhbwRvtb4j+MmBmutAW818k8YJgYw/EdnO/A0TzMQqnWM+pW7HcaDip8yqV1qiUqMzXBqRGaEPxLiiCXOwDuQUrlseVJHEW0aRhYtC2ThGqlizM2zRhH1LvJwhnZNhqVY6q7VtC6hSUTpLOGSAocaNOVwJHufiZENBUFrtV9hbD7Y12KcoijB7T+nsHRIlxbw4ZMgB25VcSF4Qv5ikUsO2KnKjfdWN9tn212FfI/a0aJ3WnpVBKg3Vt+3672nO4r5ZwOgkrs8X7XkZFatDlDUHxd73WZsuw2682ZjVISljc9uB/xNWRtQy+HHL2rwcpErtbWn3J6zOGfU2G7M8IGXbWv78BENSDHUg67HrzaU8JjJGJKVFOci2s3SlzzBr9InEp5z0dxVFfp7/itk/VxBKvoOL6u8lYijEXNyqLXOKiCuuz1LVPE2qOrZR197YCeYmixzQVqQqFaiDyLmS/2WJXAWhqNdA1iedKAr89NNKae1g5SZdnG5m7YIhBAdgESSNYF5aFajU0dRVKd8EWb+30lBJuB/A/8RJGNxUudkZxxzSH4ccSOIJZeG7hCcMF1fapkvzntKaOdllTJuXfxS7fTi7CJPNLJal8zBPywhTlBNYRVFxsTFIiPgEKAn6RHWWFkeofa/9vLEzPISHsECD29BtIS3ugvl5ZxdOqjFyZqn6fTLFzIfiVEkv6IhARaihtau6Y+wb0er4rJ351q2OdlG0twp9ZJ1ThnvyCkfhaKurFVnqzb4Oam9CrzyUuqkbFo7dZx1XZCpCdLv2BwSmrqp6xYEp72arsTicMozcS0qDLPYE0H8om2FW0gjVCVfbF7WYQqZaroFtVGtLqPIemQ5emKdK3pqorLooibFrjOftLTVQ/u31AiqKuCifMPIXjLbxyh7Jdir7Vkx++cqyPCD74rXjB8GvecVRdy7TNI2GbYRw3t0Q9cypXivlW+3rWouCeDZ+fhMWj8YfhUnyTXjto/Bhr8Kv3V/ind34tPDW7sfcd/L3dgecJlwz9ENpoVW3K82KbbVsq9lK399hEuMv5dWKpd7niauXKlI4NtgtDpEvHv8LdqNht1q2bdXqx2PrhFcsGvIWqjZbM8f2HM+/m97NgpMxHU+Tf7n4Hgc0gjD34vSeghyHJnDqgW8KR1+O/g+XD3TLgCIAAA==" + } + }, + { + "ID": "81c50ce31cf47c5b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/FeUYolRmNCBAmXaVqi7fXBbiVLU?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "24d1d98a53e969dbdedbf7396aeb7ae9/5223609557883619412;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/FeUYolRmNCBAmXaVqi7fXBbiVLU?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:53 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4243,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/62.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/62.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/62" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/92QzWrDMBCE734K4VMLtZVDT74FWkogDbRNe0lDs5a2iqh+HK1cCMHvXllxIM/Q0zIz2kHfngrGSgzBh7JhpySSFF5iUvez2d3ZsEgEavTKhfsFoyWL0BpkDiw2bNdqdegxHKuub40WlYQIDYHtDFJNe/hB6hAC7j7dZh2OrCftFKMITkKQ7O1lyW72MXbUcC6M72WtvFcGa+Etv5Rz6QXxgN8Y0Ankl/WKDoajS/9JpdW1e7uty4kgE1IC2GTNJtT/QZcppLeg3QihjG/BXGcBgXzO9BmwnLIhz+10pNQee8p3WH3Ml4uHr/nr0/vz42o9vh+KofgDBW7y0y4CAAA=" + } + }, + { + "ID": "656a75652178eb39", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "214" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1f3f919e356607f4bac70c0338218c88/3615267750706007420;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0IHdvcmQgZnJvbSBgYmlncXVlcnktcHVibGljLWRhdGEuc2FtcGxlcy5zaGFrZXNwZWFyZWAgbGltaXQgMSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiaWx0TWNBYll3c2FITG1IaGc2VTZhYmEzdU51IiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:53 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Kc9uNLlCm34zXVgKURjrTxBvx4E\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnac205:4139,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/74,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Wa08W6DUGJGJqQXrmaWgBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/74,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/74" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXXPaMBB851d43NeCP7ExM502BTdhQnkAk6adzBBZPhsF2TKSDCEZ/ntlg9u06bR9vNnd29s76bmj6RtSJPpQ02OSbSvghzcPLNbfKgAkymrgTvftjT9+ggOE1BVrur5Mi2/j1XJ/a7rGNQ6q2ZSOcsd9ur3JrpfzBx49ftw9uuGd3vQhTfukohhkt2Rcdn3PHi4XPULlZ3wRf90LdDXNr9aZt/RQjJxqVjVCATSdkmJTy9dSlmJoGPv9vpcxllFAJRE9zHKjHdzY2UbJ2QNgKYzf7AwVShh/MXxPGUaSsOLdctGYK8EcUuBQYFADPHc0TT93n/wpUC1qVCf0H9k0vfWrycqyox1rV8yKlGQVb6HGtkl3Ln5W9XrUMNqe8URLOcu1+3YT3bKKKcHdBEnUEygvKYieWKMNiBIQh3uNkpxIzWpGUVFASFI0nhGKaZv3fxLXauUi4MxZ4UE6MGMziK1BnJp2CmnfdzGYfXMAfdOF1EsGvuehVi1rw5MWFawwwYtt0/USP3WdfmrHATg+JBY4sWmZtu07A991/VSv1cfT+JgDkjAmomSCtDsdzcOLKFxNPq1mYTgOx+eoe05eUb/MJ4oZzZezkZKciSUnTHGbTU9mUTi/GEWTmxatBEwhQ/iw2FLFSBEVoIDj+YxCIlmJ9n51Ve9UVw6zyexSf8EiavP4B7NJUl+B5I3A6jumbzpW4PTN4PRulIjL17hnn/FfH0tjnUMho0PZKBbhNBxF+otZVRS+ghyROojueJ4TBI5juf2uOqE0LZbLANl+sMFOhjOyXW83dBCzeF19SGAHlJXAe5lqsiMYEMasKmT9L/XOsfMd5aI9I2AEAAA=" + } + }, + { + "ID": "e8f980a57b5f6851", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/iltMcAbYwsaHLmHhg6U6aba3uNu?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b7e15ca75127c89ac6d0ccd21874e160/2079262809241522853;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/iltMcAbYwsaHLmHhg6U6aba3uNu?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:53 GMT" + ], + "Etag": [ + "+bnQxJnUhcbmyvvLBGgfzQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4207,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/122.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/122.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/122" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QyU7DMBC99ysic4RKLFKQkHpoEWqKQkVTckCIg+NMluLYwR63hCr/XttNKReP3zKeN96PgoB81SInDwHJ6vLbgOouSsCVuySgDUdtSyuFBnLl3IC0dO7LTKx+nkVasazpttt4Ni+L39VkcnRpVkFDrW9vkcVFDTzXFn94HAy81wRtwL24kyr33QOPXev59VuyWM7/K43MvbJM43g6i5/IIPW+ftqz9yk2MkugAAWCwTlLq+QGGC780rnhDHDcSoXj+/B2mOI6j3rN8YVNs/edplHcRFUZpiHN6J1ZmpOXS0axlsLZ0zX5m44SKU/kzq1NbsiZm3UI+lVJBlqDn3JNTnkfZdNyQBcXlQFPM2p/M6px4Eb96ADMz0huuAEAAA==" + } + }, + { + "ID": "68778b2616c14bb2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "214" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b98474ff61f191158756331bdf68a3e8/471201373250738126;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0IHdvcmQgZnJvbSBgYmlncXVlcnktcHVibGljLWRhdGEuc2FtcGxlcy5zaGFrZXNwZWFyZWAgbGltaXQgMSIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoicnpxNGV4N0JyUzF1M0laeWU0Vkx2U1BHZWt0IiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:54 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/mslDP1uViPLCPDUKYgFNpIlV_s8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnjy18:4075,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Wa08W6mSO4LLqQWIlamoAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/195" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851dY7mvBn9gmUtWm4ERWEUJ8tY0ikfN5bS6cfebuDCUR/71ng9u0qdo+rmZmZ2f37rmj6VtSJPqVpsck21XAj28eWay/VQBIlNXAve7bW3/0BEcIqSs2dHObFnej9fLwxXSNXNDR1KpWZDoeTkfLT1+zm0kZ0dVaBPd604c07ZOKYpDdknHZ9T37ajnv8aedC9/8j3xuVU50dwR3Nd7Pp7ewlY1QAE3HpNjW8o2UpbgyjMPh0MsYyyigkogeZrnRDm7sbaPk7BGwFMZvdoYKJYy/GL6nDCNJWPFuOW/MlWAGKXAoMKgBnjuapl+6R38KVIsa1Rn9RzZNb/1qsrLsaKfaFbMiJVnFW6ixbdJdip9VvR41jHZgPNFSznLtod1Et6xiSnA3QRL1BMpLCqInNmgLogTE4UGjJCdSs5pRVBQQkhSN5wLFtM37P4lrtXIRcOGscZAGZmwOYiuIU9NOIe37LgazbwbQN11IvSTwPQ+1alkbnrWoYIUJXmybrpf4qev0UzsegONDYoETm5Zp274T+K7rp3qtPp3HxxyQhBERJROk3elwFl4vwnV0s56E4SgcXaIeOHlF/TyLFHMxW06GSnIhlpwwxW02HU0W4ex6uIhWLVoJGEOG8HG+o4qRIipAAafLGYVEshLt/eqq3qmuHCbR5FZ/wSJq8/gHs0lSX4HkjcDqO6ZvOtbANb3zsWoRl69xy7+8wF8fS2OdQyEXx7JRzMNxOFzoL2ZVUfgackTqILrjec5g4DiW2++qE0rTYrkcINsfbLGT4YzsNrstDWIWb6oPCeyBshJ4L1NN9gQDwphVhaz/pd45db4D+IMpAGAEAAA=" + } + }, + { + "ID": "304a4b9288c46d14", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/rzq4ex7BrS1u3IZye4VLvSPGekt?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7d3681c255d2ca4b0e30a49c89a647a3/17309603639782546679;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/rzq4ex7BrS1u3IZye4VLvSPGekt?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:54 GMT" + ], + "Etag": [ + "najSJCm4YDn/updN59Ktzw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4006,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/57.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/57.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/57" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qtE0SnRhAeHBtGF4CYmanwo3TE3una0N2GQ/e+2ZYgvvX4/rvdddx3PI8tMJOTWI/MsXVWg6pMU8MVeItAVR21KKYUGcmrdgDS1bkHz+GlY+O/34qwqk8nVzTNu14PB3qXZNxTU+HYGGbzIgCfa4E+HvZZ3mqAF2BfXUiWuu+WxLh0fv0bjyei/UsjEKZNZGN4F4QNppcbVL3M2LkUu5xEsQIFgcMxSKpkDw7FbOqk4A+yWUmG3f33RTrGde11tVz5s+oGKe9Xl+KMG/y38iacjWOLByyWjmElh7bOY/E1HiZRHcm3XJj1y5IIaQU+VZKA1uCnn5JB3KIuSA9q4qCpwNKPmNx8zbLlO0/kFEmmPt7gBAAA=" + } + }, + { + "ID": "de2fc7f60610bae4", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "362" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "de313ea88bbfd1aff9991736c9ef3ae9/5710449718789817140;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im1vZGUiOiJSRVBFQVRFRCIsIm5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifSx7ImZpZWxkcyI6W3sibmFtZSI6ImJvb2wiLCJ0eXBlIjoiQk9PTEVBTiJ9XSwibmFtZSI6InJlYyIsInR5cGUiOiJSRUNPUkQifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:54 GMT" + ], + "Etag": [ + "NPJjwV83x+6W/UFh4AEb6g==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnez28:4339,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/81,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Wq08W5inGoekqQWBpIe4DA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/81,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/81" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2TTW+jMBCG7/wK5D02jYEQ8iH1kLS0yiqbVJTuHqoqcmBC3AJmsbM0qvLf19ggZaN0FS7Gr+edeWYsfxomeqd5jMYmWtPk9w7K/TdB1imgjjwCQZL6aPH4/a36Oex9XHm/8PP91p34ay+5uVFBVLnjXRqBuC5YKa4HnjOOiSAcxMqx7KE1sNyVa3kjZzS0++7AGdgrS35dVek/IbanKnBIN3Oav9d1tkIUfIxxVVXdhLEkBVJQ3o1YhtsG8B8HFyV7g0hwfMKFGy6OLwHECpDjCzlVWAAbKCGPQNJ+GqaJGpTZuTHVNilqlCbiAi5tU+W06QJAwzyoWUZbyEjLtqGQxlzuXuROSVLMSVbD67WjNbEvlPYUBrPFA6rFQ+ecZ5fxU89sEfoPftDKGYuVHPiP/iT0775OVkJ0mivwb5fBXav+i9/6jzKsGUub4KMk0+Vy7k8WSOsHtbwaze9rMyjZyXQvoM6O1MBrZc7yJITyzEnAqiMhKoEIyvKQag6735MX07NHbt929NP6KGh5LsYb9ZobRinh4geLqWwz/jpT21U4mc597WORylyrz0/IOBh/AQILZznoAwAA" + } + }, + { + "ID": "78e2ac497d9c9e06", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "222" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "63b234e03bbb498ad9a842761efba1e2/4174444777342109533;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF92aWV3XzAifSwidmlldyI6eyJxdWVyeSI6IlNFTEVDVCB3b3JkIGZyb20gW2JpZ3F1ZXJ5LXB1YmxpYy1kYXRhOnNhbXBsZXMuc2hha2VzcGVhcmVdIiwidXNlTGVnYWN5U3FsIjp0cnVlfX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:54 GMT" + ], + "Etag": [ + "t41P6gGnbaG68saUmrBOdQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmh22:4372,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/22,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Wq08W8DPJILgqgXl05b4Cg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/22,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/22" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1S226bMBi+5ykQu11ioDSESL3phqJI2aEk2S6qCjnmD/FiMLVNUVTl3WsbmNqpk8qFJX//4TuYZ8f1TrQuvIXr7Wn52II4f1J4z8D7rEugcGlKKgp+zsplvcfL2VziXSVufxR3Nze2idrpomUE1KThQk3iWbgosMISVB76wdyP/SiP/FkSJvPgOorDOMh9/U1V/kShy327RwI7rGl9MtuOSjVygVDXddOS85IBbqicEl6hUSZ6ClEj+B8gSqJ/2NHALtFHZCBrWKI3aiyWwQEE1AS0pmfHdb2BcPWeZTOmwZ5w6PgAez9m6fqhvzIc92JzIUeo8KjgQIEVUt/u9c1CGqxxZSR6HReF3Wc2nhuLbbbZ6vtyRCteWDRL73arLP3qGfiij4eBrW6r27MCw+D1SWhkzetyC+KdSsa7VwARgBXl9Zb2eoLrK237KkiiOElsB8NSfeMF1TaK/3eN2n+t0t8WMImMCdjXt87Sdfpl6xrT7kHwyr0f/41J0+4ZJROT/0LiqtHPO5VHfALZABbw0IfeSlhDicl588j0QiVaGFJgnFgjhma38ZyL8wKG/v5HKgMAAA==" + } + }, + { + "ID": "14a9586316519ef1", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_0?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e0b6078d93ed60385d00c88164e809e2/2566383341351390341;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_0?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vni65:4255,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/114,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Wq08W7jyNZT7qAXMo7g4" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/114,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/114" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "d2f92528a673d509", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "223" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "de10addd20bf88625fa0c091edf14366/958323004855456174;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF92aWV3XzEifSwidmlldyI6eyJxdWVyeSI6IlNFTEVDVCB3b3JkIGZyb20gW2JpZ3F1ZXJ5LXB1YmxpYy1kYXRhOnNhbXBsZXMuc2hha2VzcGVhcmVdIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19Cg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnne9:4102,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/132,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=W608W-j7A8S3qwXk3o6oAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/132,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/132" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dbU8bORD+3l9hRfmQk6JVQhJeIuVDCuGIVFoaoKcTIOrsDmGPjb31eik5xH+/8csmm7AhFLhCK6sVsB7vzNgznhk/04bbd6QEQnBRapPbd8Q+JPh0gk96CAcDPqYhw8HSKOJDGpWqZlwATbgeD9k1jcLgcwpiklHHkCR0BIp8OGGS3hDNvE2OGdzE4EsIyGnp5LREqCQn9XZ988wj/QsiL8OE4F9KJB1GQMIAmAwvQhBVAolPY8ApQBgdA/keykvyFce9kUe+6vmeInwlguIkgTMpIyczwpmXqRdxn8qQs6NJrHXkav4iURG+5RcVwDAd9dkFV5ST/scv3Q/9nfP+x4PjozNCxSgdo7LneqHkNK3VGj5lNJr8C2JuMAoZtEm96vMoHTP8aVOPg/laJQO9s23i8wA6c1KI3dYOS6OIaHU6LyXXGOhAcMk7xuZttJAf8TTwLiEKb7yemrCjSaelU6bUU1M+H/cGf5/3BoNPAzWc6aNIy01/qox/+rPNr/TLrHsu0fZt8ulorzeYjSqttc3VVL3B5yEaXA2/1EYrzvhH4sp9PvZGnI9QwfxG4/CYM69340OslErKB+kwCn1Pa6bNUJkRvX/oNW23Njb/eJhrAuIahPcPH3o7AsYQaUbHMoy8MZ0MYRtPtIQ9NVdTdgUfHyK/NOmyYAckDaOksvimlt1sNl5NdmNr/emy/aeLXd94vNg/NfXwW6RD5JGgLLngYoxUuJGC+rJ3I4ElaMx9HqQRJJXlbxjprR/Y8OW8RiBnjyZ+/4WHajdlvvYs3IQjdXpW67O29bb0qb2IPjqtoYN8CeH7KpH1xiNFCp5MPO1yei0D9YgLVzJUaqkskAzz9Xr96dxDhkRZyHhzfepISco8ARcRBmjvT2CAQRSCfZCXPOj6PuYdLuqtrRZyu+ZXUDlmV4x/Z+SQp8KHIiY7EMEIgyobzXPpj+Mo4/LgHBNbpgqqRy+ibDSVYN7JeNkn89bWqmhoY6zeL7UfA8DgmkjMfzeTct3zaRRVFsct73rtabx3aByD2OP8ahfPPBeTMnqRj2u3dEW5J/jeS9Zwa89Z39qD62usCi5LeH+h4t/86grXszApi2fNV1xO/ujkeHTTIJT59dz3isUpdjXP8r3/xzaYYIPk4cUsTjESN1YFnlewzDZnEjPniuXcm2Skrkzcr2CdD3z08GLmJ9iK7w26meFhA3Ix79bGXMGmMkasqWU9qdHM8lVxhlmiT6zuLebrGo3DXAosz348RHcKfSjb7wdU4K1AgkjKWx7eFYII3uM94GoWkXM5M0vEta3W21GlyEoMpCdiv+GFKofaEzSI/YxfN47xKqEvO3takLACM0EPz9Wy14pcb5ngQ/1TH0+jwMuSPZaeSBnS+iwnorJ0snWcopP7sNRtXBqDCE8UptFoUlkk2GS6UVS7vQjn4mhjDk+qryCc+akQeK/0usNE3wamteVuKlMB5YVnL+DTkcqSd6yZmoWVys+S3ni+dOUmD0up14t2WJrCylMFFlgvKucfBilj+iyhgD6zg5X8DBsJW0XH/dHs0T3UCgr4rhcVcIV8c8ufDm5j7B5S/8qo32eXIEIs1y31Iz9mWCMXiG2sFYWMFxJbJK/+aHnLrFNolMK7VrFn7XMBvRvwU8ycSbllLTI3aj2p1iqCEn5xV9o225ALuzMdF2jWQ4pO7U9wzKI68xd2TH1bXfTFo0sBNDjgPMp8TzH6i4srEJUCoo1wzR/lWjYsjZZL2K6vNee46pu1mZx7z14Cmjrhs9xWqcrG5kQfz5K3S9NItkl/PE414mrAYrgIWahy++2QJjMw+6A76O73jnqDKsHUDyOsaTvHh72BQZHVYJoYkLtKpqB7xyHuDnF3iLtD3B3i7hB3h7g7xN0h7g5xd4i7Q9wd4u4Qd4e4O8TdIe4OcXeIu0PcHeLuEPffA3GvEoMLd8w/va8SULBVAAGWJrHFxy+ljPfwPaxkOrd35tkgap0hDWyJUSWhTfAG6u6Yb7cZwmpezcPuGgpfgkV7OQAaZ2pIL3g/eY/ugNp1pEjB4vb7Fj130L2D7h1076B7B9076N5B9w66d9C9g+4ddO+gewfdO+jeQfcOunfQvYPuHXTvoHsH3Tvo/neB7mcQ8bbCpucw8xnNQMTLkPYquQgjTO8KXJiB2VWSIbAd/cE1BuqszgHeVaJ2fwxBdwrxn7ylT+I5u1u1irekbZWYjzvq5D/rCAdjX9u2Wavdkbekb5u4jovruLiOi+u4uI6L67i4jovruLiOi+u4uI6L67i4jovruLiOi+u4uI6L67i4jovruLiOi+u4/B4dl/v7tfgZRRok2+aRup2jHMn1hxZVFoZNNi+68eX4CSxFPBrQWOYvAJYTwxlqPBNQQFpe668UYkudfIk8X/0UVt7PZ1t4b1zcYm185e7KDXNJcmHImvipYXInFKBgSeswoH+AoqjZrP2AkOzoWiUt2w9hIhXgVFkg26hcjC0+SgDOiiMsqws5bxZfbR/FOAFZyHN9a4VbOxs6G95jKE2oNRFXZZUrdRHMPapr4FzmmZtpN3ntFaqX3CLst22996aPUZ5mZyEmqEXmdtnlt75QjuReteZtPmtNrmT6pUqmHNdRAl72Hyq9nTCJqfQvP6eQguoM2PImV6zcm5IFNqVySf1yoTv8cqZ+zVBJ9a1LbdKs1fTjW/1FSu9Q5bt3/wHR46qpQ2oAAA==" + } + }, + { + "ID": "d5678beadd7a1f91", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f368d7c94b2ecdfa793dc8531c784084/17868780666418714327;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfs12:4294,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/53,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=W608W6qZD4mhqQX6vr_wAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/53,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/53" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2d62/bNhDAv/evEAx/8ABPsPxKYsAf0jzWDE2aOe32oS0CWmIcLTKpUVSbLMj/vuNDjizTrzZts+IaILZI6u5IHnlH/Wrn/oVXo0JwURt49y88e5HB1Xu40kVQGPEpiRkU1iYJH5Ok1jTlgpKM63LG5THPWVTUTGmWkQlVVWdceleqbuC9JeOEelGehFT+mnIhf93ptwcRkSSj8rLdCnZbO63uZbfV32vv7Qa97k57J7hswT9fXn6K6efLoFAQ0XE+OWFXXKl4f/bm7eXxm3dnhx+b3kgbNfBCHtHhrMKzFg2fwAaPFV3ytBlDlieJp0fuXHDJh2a8Bt6HWpjwPPKvaRLf+keqwaGu+lD7wJR9qsnMRFVIxCSfUiZVhba0WvhVhith8COJhNGZ+hPOJwn1yzZC8ZQz/+g2pKmMOcvq5/k4iUNf0IznIqRndqIbj038v8knMmi3+798M9md1hrZGRWfqPCnVBI1Iv6+5NM4vJBcwJy/FSS8oeIiJYxBo4TzmzzVo3vEZCzvGqtaawOCXnvv21iwge5Ot72lbi25qpNMxxGpl1TX2w1XQ6N07wn7Wz8mIZTf+SkVV1xMf+fj9d1ud9o7T9Ht0lCv6G13S1Wn8KYsbk7PQqXpUDfYdkzPk1vYUG7vrJysXpZaVM6prt5hVHd2e1tqvoC22Yj+k9NMvuaTScwmMyOK67nuL3pXp7FCiLWrt61nr7LL1oVcRHCL0G/W2wDb5o+3obetq29uAwlDiHz1Xqu1gR3tp5wPp5+UfHUD9wi2NOeYi89ERGUrSkXLDFm8y+j/glUDGdEU5LzMr66oOLXlhdS/BElhC6wva7Z8oDYRa2zurovC1mbBszv/lSrROkbqMqIJlbRRKTWeEfRnW2SWM/Dsq4SG0v+NwiZKJI3AqGse7Wtn4yLY3dv1Y/aJ39DGO3bD+GfmXegY7xJyCHonREIH56WcTNOkkLKyjel5p5CtLmFHYpOZBnNPIctembv2djdLW/SAqQGxHqu32XrghyRJGtVyKztYt7UskX2oJ/QVuICNnHU119B3W69qFhQv3GSdeN2SXtm/9sr+dTpfJvtPIv4t987Zn0qjYktYF6m/ZXfKa6ckYz+PYlnuz6JXVJsUcfn5zc2BoFG2ujPVJkbjzrrN+gfMzAFnkt6umZvFRkZrf11Q/gGzA3FydWfmGxhtvWfoZkaG3ZDdsns7c7FMRYxU19Z1o063CFjuCLPEnlSd0M3vNknjUgysP769AHeKQ1q3r+dEEAjyKp0IWv41YVFCXyY8vHnckktR00bMftBaNzbf0xbX6mRU+iINO36soqhdQ6M0LOTtpymcz4k6ib/SioRVWCha3dYce1zOt0zxhX53AutRMJLYhemLnEHdCSupaCxtbF3HtXZXaz24VifCBNYUBNLkrlGtsOF0x3WIexLJ7v3GLJ9cxgm8Z2EuBGXS3x9nElICCXkBy9SZ+jiXuaD1yrUf8VlJY8k9xenUmat8L+3uBzxbaVduslpLELhGWJrUylcpFrVeVC9fjHLG9FoCBSfMFjbKLexe2HOdFzYWD+6heuCQ23elcE65pe7PCg9g9x7DScGYf8KuqYghYbe1Z/wdgyzZobbTdm1fT6TWpS/YWN+y2XFOivOhi9uzTrmgR7c0zCF2wpHZzshcqfWkVs91zvqfu9KBGYbStvtoY6XOeohr1X4Hx3TFsv+xY+rzatUX317DoT865zwpfE8J+osLOPI3HJV2h+tuK7VuRBorl4jtt7tzUvXZ2jQu3WePAV0d8FlpqFRqY2NiCGvJPyZ5IgfeyXSaS/0UXoMRehWzWMX2+zHJSuSm6UHIpxPIZofvLo5Gl0ej0ZuRKsyhlSIvTW8Gg4ZIgpAEIQlCEoQkCEkQkiAkQUiCkAQhCUIShCQISRCSICRBSIKQBCEJQhKEJAhJEJIgJEHPzDF/QhLU9AwDGZqPDjU96DdlEY0gNUktv7mWMn0F90EqM7x/MNfqmVCeDYsPFjW92IZ3Q3WG5uW+ICLmxjIU0tRnCXXxy3QJLIZZf3n3Uj1EYdFQipxapnRqURFiJcRKiJUQKyFWQqyEWAmxEmIlxEqIlRArIVZCrIRYCbESYiXESoiVECshVkKshFgJsdIzc8yfEis94pCDORBTrjEwZBkFanpXcQLBXT1cMOzGkCNIOHQOYC+nc5Vq0Kc02p9xp/c6D2p+HfH5+LBU7dN+f17TM1/aVwJrkFfoEey2ug/e02obeEjOkJwhOUNyhuQMyRmSMyRnSM6QnCE5Q3KG5AzJGZIzJGdIzpCcITlDcobkDMkZkjMkZ8/MMX9CcrY4XtXv59M05IAn6nwOeiTXX9jXqBSbaO4685XkCUhFfBKRVJaPAFYSgxaqvFDgqFqe7a9VYlOdcpI8n/04c++vF+s8OVaHWE++cnflhqUgWSmyU/yl2+RhLGCuZg5D9Rvq2jW7rS2UFEvXGmnFvo4zqR45NSrVdld2U5qNFECrVJ8mXJJ33YfbjQRnVDpl9p3QAecQ53CVQGm2WrPjPnKhx0t1DJyLPHMti+f8PyB7KXXCvhzosT/lUQ4pyyw6C3EHVhRuV5x+g0o6UrrVTq/zyTemTD9nylSSOsmoX3xs2z+Ms5TI8PqPnOZUsQGb3pSSlYUmxcamTK6pP/z4AL8+qj8BWVP/R6M28Lqtrr78dn9w8gUofXjxH19Hi4QpcwAA" + } + }, + { + "ID": "974557edcf882e85", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "223" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e7831e005de59c7275357c0bfae024f8/16260720329922779904;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF92aWV3XzIifSwidmlldyI6eyJxdWVyeSI6IlNFTEVDVCB3b3JkIGZyb20gW2JpZ3F1ZXJ5LXB1YmxpYy1kYXRhOnNhbXBsZXMuc2hha2VzcGVhcmVdIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19Cg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnaf190:4081,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=W608W-GPGs_sqgXhhojQAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/170" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dbU8bORD+3l9hRfmQk6JVQsJbpHxIIRyRSo8G6OkEiDq7Q9hjY+95vS05xH+/8csmm2VDKHCFVlYrYD3embFnPDN+pg2370gFhOCi0iG374h9SPDpFJ/0EA4GfEJDhoOVccRHNKrUzbgAmnA9HrKvNAqDTymIaUadQJLQMSjy0ZRJekM08w45YXATgy8hIGeV07MKoZKcNjvNrXOPDC6JvAoTgn8pkXQUAQkDYDK8DEHUCSQ+jQGnAGF0AuRbKK/IFxz3xh75oud7ivCFCIqTBM6kjJzOCedepl7EfSpDzo6nsdaRq/lFoiL8k19UAKN0PGCXXFFOBx8/9z4Mdi8GHw9Pjs8JFeN0gspe6IWSs7TRaPmU0Wj6L4iFwShk0CHNus+jdMLwpy09DuZrnQz1znaIzwPoLkghdlu7LI0iotXpvpRcY6BDwSXvGpt30EJ+xNPAu4IovPH6asKuJp1VzphST035dNIf/nXRHw7/GKrhTB9FWm76M2X8sx9tfqVfZt0LibbvkD+O9/vD+ajSWttcTdUbfBGiwdXwS2204ox/JK7c5xNvzPkYFcxvNA5POPP6Nz7ESqmkepiOotD3tGbaDLU50fubfqWd9c2t3x7mmoD4CsL7m4+8XQETiDSjExlG3oROR7CDJ1rCvpqrKXuCT46QX5r0WLALkoZRUiu+qWW3261Xk93a3ni6bP/pYjc2Hy/2d009+ifSIfJYUJZccjFBKtxIQX3Zv5HAEjTmAQ/SCJLa8jeM9PXv2PDlvMYg548mfv+Jh2ovZb72LNyEY3V6Vuuztv229Gm8iD46raGDfA7h2yqRzdYjRQqeTD3tcnotQ/WIC1cyVGqpFUiG+Uaz+XTuIUOiLGW8tTFzpCRlnoDLCAO09zswwCAKwQHIKx70fB/zDhfNjfY6cvvKr6F2wq4Z/8bIEU+FD2VMdiGCMQZVNl7kMpjEUcblwTkmtswUVI9eRNl4JsG8k/GyT+at7VXR0MZYvV9qP4aAwTWRmP9uptWm59MoqhXHLe9m42m8d2kcg9jn/HoPzzwX0yp6kY9rt3RFuSf43kvWcGvPWd/ag+trrQouS3h/puLf/OpK11OYlMWz9isuJ390cjx6aRDK/Hrue0Vxil3Ns3zv/7ENJtggeXgxxSlG4uaqwPMKltnhTGLmXLGce5OM1JWJ+xWs84GPH17M4gRb8b1BNzM8bEAu572+uVCwqYwRa2pVT2q1s3xVnmGW6BOre4v5ukbjMJcCq/Mfj9CdQh+q9vshFXgrkCCS6raHd4Uggvd4D7ieR+RczswScWN7/e2oUmYlBtITsd/yQpVD7Qkaxn7GrxfHeJXQl519LUhYgZmgh+dq2WtlrrdM8JH+aYCnUeBlyR5LT6QMaQOWE1FbOtk6TtnJfVjqDi6NQYQnCtNoNK0VCTaZbpbVbi/CuTzamMOT6isIZ34qBN4rvd4o0beBWW25l8pUQLXw7AV8NlJb8o41U7u0UvlR0lvPl67c5GEpzWbZDktTWHmqwALrRdX8wzBlTJ8lFDBgdrCWn2Ej4XrZcX80e3QPtYISvhtlBVwp39zyZ4M7GLtH1L826g/YFYgQy3VL/chPGNbIJWJba2Uh44XElslrPlreMuuUGqX0rlXuWQdcQP8G/BQzZ1JdtxZZGLWe1FgvgxJ+clfaMduQC7tzHQs06yFlp/YHOGZZnfkTO6a+rRZ98fhKAA0OOY8y31OM/uTiGkSthGgjXPt7uVYNS6PlErYba+0FrvpmbSbn3rOXgLZO+Cy3VaqysTnRx7Pk7dE0kh0ymExSjbgasBguQxaq3H47oskczD7sDXsH/eP+sE4w9cMYa9ruyVF/aFBkNZgmBuSukxno3nWIu0PcHeLuEHeHuDvE3SHuDnF3iLtD3B3i7hB3h7g7xN0h7g5xd4i7Q9wd4u4Qd4e4O8T910Dc68Tgwl3zT+/rBBRsFUCApUls8fErKeN9fA8rme7tnXk2iFp3RANbYtRJaBO8gbq75ttthrCaV/Owu4bCl2DRXg6Axpka0gveT9+jO6B2XSlSsLj9gUXPHXTvoHsH3Tvo3kH3Drp30L2D7h1076B7B9076N5B9w66d9C9g+4ddO+gewfdO+jeQfcOuv9VoPs5RLyjsOkFzHxOMxDxMqS9Ti7DCNO7AhfmYHadZAhsV39wjYE66wuAd52o3Z9A0JtB/Kdv6ZN4zu9WreItaVsn5uOOuvnPOsLB2Ne2bTcad+Qt6dshruPiOi6u4+I6Lq7j4jouruPiOi6u4+I6Lq7j4jouruPiOi6u4+I6Lq7j4jouruPiOi6u4+I6Lr9Gx+X+fhU/o0iDZDs8UrdzlCO5/tCiWmHYZPOyG1+On8BSxKMBjWX+AmA5MZyhxjMBJaTltf5KIbbUyZfIi9VPaeX9fLal98biFmvjK3dXbphLkoUha+KnhsndUICCJa3DgP4ByqJmu/EdQrKja5W0bD+EiVSAU61AtlG5HFt8lACcFUdYVpdy3iq/2j6KcQKylOfG9gq3djZ0NrzHUJpQayKuyirX6iKYe1TXwIXMszDTbvLaK1QvuUXYbzt6700fozrLzkJMUYvM7bLLb7NQjuReteZtP2tNrmT6qUqmHNdxAl72Hyq93TCJqfSvPqWQguoM2PImV6zcm5IFNqVyRf1yoTv8cq5+zVBF9a0rHdJuNPTjW/1FSu9Q5bt3/wG6JvXpQ2oAAA==" + } + }, + { + "ID": "782c363258e916cf", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f20aa43c1bdc1c3fe178a7f609d51037/14652658893948837672;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneq9:4200,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=W608W6jdIsaqqQWRtJWYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/134" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2d62/bNhDAv/evEAx/8ABPsOVHEgP+kOaxZmjSzGm3D20R0BLjaJFJjaLaZEH+9x0fcmSZfrVpmxXXALFFUndH8sg76lc79y+8GhWCi9rAu3/h2YsMrt7DlS6CwohPScygsDZJ+JgktaYpF5RkXJczLo95zqKiZkqzjEyoqjrj0rtSdQPvLRkn1IvyJKTy15QL+etOPxhERJKMysug1d5t7bS6l91Wfy/Y2233ujvBTvuyBf98efkppp8vg0JBRMf55IRdcaXi/dmbt5fHb96dHX5seiNt1MALeUSHswrPWjR8Ahs8VnTJ02YMWZ4knh65c8ElH5rxGngfamHC88i/pkl86x+pBoe66kPtA1P2qSYzE1UhEZN8SplUFdrSauFXGa6EwY8kEkZn6k84nyTUL9sIxVPO/KPbkKYy5iyrn+fjJA59QTOei5Ce2YluPDbx/yafyCAI+r98M9md1hrZGRWfqPCnVBI1Iv6+5NM4vJBcwJy/FSS8oeIiJYxBo4TzmzzVo3vEZCzvGqtaawPavWDv21iwge5ON9hSt5Zc1Umm44jUS6rrQcPV0Cjde8L+1o9JCOV3fkrFFRfT3/l4fbeDTrDzFN0uDfWK3na3VHUKb8ri5vQsVJoOddvbjul5cgsbyu2dlZPVy1KLyjnV1TuM6s5ub0vNF9A2G9F/cprJ13wyidlkZkRxPdf9Re/qNFYIsXb1tvXsVXbZupCLCG4R+s16G2Db/PE29LZ19c1tIGEIka/ea7U2sCN4yvlw+knJVzdwj/aW5hxz8ZmIqGxFqWiZIYt3Gf1fsGogI5qCnJf51RUVp7a8kPqXIClsgfVlzZYP1CZijc3ddVHY2ix4due/UiVax0hdRjShkjYqpcYz2v3ZFpnlDDz7KqGh9H+jsIkSSSMw6ppH+9rZuGjvdrp+zD7xG9p4x24Y/8y8Cx3jXUIOQe+ESOjgvJSTaZoUUla2MT3vFLLVJexIbDLTYO4pZNkrc9fe7mZpix4wNSDWY/U2W2/7IUmSRrXcym6v21qWyD7UE/oKXMBGzrqaa+i7rVc1C4oXbrJOvG5Jr+xfsLJ/nc6Xyf6TiH/LvXP2p9Ko2BLWRepv2Z3y2inJ2M+jWJb7s+gV1SZFXH5+c3MgaJSt7ky1idG4s26z/gEzc8CZpLdr5maxkdHaXxeUf8DsQJxc3Zn5BkZb7xm6mZFhN2S37N7OXCxTESPVtXXdCCKMDVjuCLPEnlSd0M3vgKRxKQbWH99egDvFIa3b13MiCAR5lU60W/41YVFCXyY8vHnckktR00bMfru1bmy+py2u1cmo9EUadvxYRVG7hkZpWMjbT1M4nxN1En+lFQmrsFC0uq059ricb5niC/3uBNajYCSxC9MXOYO6E1ZS0Vja2LqOa+2u1npwrU6ECawpCKTJXaNaYcPpjusQ9ySS3fuNWT65jBN4z8JcCMqkvz/OJKQEEvIClqkz9XEuc0HrlWs/4rOSxpJ7itOpM1f5XtrdD3i20q7cZLWWdts1wtKkVr5Ksaj1onr5YpQzptcSKDhhtrBRbmH3wp7rvLCxeHAP1QOH3L4rhXPKLXV/VngAu/cYTgrG/BN2TUUMCbutPePvGGTJDrWdwLV9PZFal772xvqWzY5zUpwPXdyedcoFPbqlYQ6xE47MdkbmSq0ntXquc9b/3JUOzDCUtt1HGyt11kNcq/Y7OKYrlv2PHVOfV6u++PYaDv3ROedJ4XtK0F9cwJG/4ai0O1x3W6l1I9JYuURsP+jOSdVna9O4dJ89BnR1wGeloVKpjY2JIawl/5jkiRx4J9NpLvVTeA1G6FXMYhXb78ckK5Gbpgchn04gmx2+uzgaXR6NRm9GqjCHVoq8NL0ZDBoiCUIShCQISRCSICRBSIKQBCEJQhKEJAhJEJIgJEFIgpAEIQlCEoQkCEkQkiAkQUiCkAQ9M8f8CUlQ0zMMZGg+OtT0oN+URTSC1CS1/OZayvQV3AepzPD+wVyrZ0J5Niw+WNT0YhveDdUZmpf7goiYG8tQSFOfJdTFL9MlsBhm/eXdS/UQhUVDKXJqmdKpRUWIlRArIVZCrIRYCbESYiXESoiVECshVkKshFgJsRJiJcRKiJUQKyFWQqyEWAmxEmIlxErPzDF/Sqz0iEMO5kBMucbAkGUUqOldxQkEd/VwwbAbQ44g4dA5gL2czlWqQZ/SaH/Gnd7rPKj5dcTn48NStU/7/XlNz3xpXwmsQV6hR7Db6j54T6tt4CE5Q3KG5AzJGZIzJGdIzpCcITlDcobkDMkZkjMkZ0jOkJwhOUNyhuQMyRmSMyRnSM6QnD0zx/wJydnieFW/n0/TkAOeqPM56JFcf2Ffo1JsornrzFeSJyAV8UlEUlk+AlhJDFqo8kKBo2p5tr9WiU11yknyfPbjzL2/Xqzz5FgdYj35yt2VG5aCZKXITvGXbpOHsYC5mjkM1W+oa9fstrZQUixda6QV+zrOpHrk1KhU213ZTWk2UgCtUn2acEnedR9uNxKcUemU2XdCB5xDnMNVAqXZas2O+8iFHi/VMXAu8sy1LJ7z/4DspdQJ+3Kgx/6URzmkLLPoLMQdWFG4XXH6bVfSkdKtdnqdT74xZfo5U6aS1ElG/eJj2/5hnKVEhtd/5DSnig3Y9KaUrCw0KTY2ZXJN/eHHB/j1Uf0JyJr6Pxq1gddtdfXlt/uDky9A6cOL/wBHgnQ6KXMAAA==" + } + }, + { + "ID": "cc48e15c43d1e498", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_3?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "bdafc83a4454fb0841444df1845c8109/3053223502274299494;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_3?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbu128:4381,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/111,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=W608W7mTK9e3qQXK4LHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/111,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/111" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2dW2/buBKA3/srhMAPPoBX8D2JAT+kuZzmYJPmOO3uQ1sEtMQ4OpFJLUW1yQny33d4kUPL9K1N22wxDRBbJDUzJIecob7aeXgV7FAhuNgZBA+vAnuRw9UHuNJFUBjzKUkYFO5MUj4m6U7DlAtKcq7LGZcnvGBxWTOleU4mVFWdcxlcq7pB8I6MUxrERRpR+VvGhfxtt98exESSnMqrdrO119xtdq+6zf5+e3+v1evutndbV034F8qrzwn9ctUpFcR0XExO2TVXKj6cv313dfL2/fnRp0Yw0kYNgojHdDirCKxFw2ewIWBllwJtxpAVaRrokbsQXPKhGa9B8HEnSnkRhzc0Te7CY9XgSFd93PnIlH2qycxEVUjEpJhSJlWFtrRa+E2GK2HwI4mE0ZmGE84nKQ1dG6F4yll4fBfRTCac5bWLYpwmUShozgsR0XM70fWnJuH/yGcyaLf7//pusjvNNbJzKj5TEU6pJGpEwgPJp0l0KbmAOX8nSHRLxWVGGINGKee3RaZH95jJRN7XV7XWBrR67f3vY8EGujvd9pa6teSqTjIdx6TmqK61676GRun+M/a3dkIiKL8PMyquuZj+h4/Xd7vdae8+R7edoV7R2+6Wqs7gjStuTs9CpelQt7XtmF6kd7Ch3N1bOXnNlVpWzqmu3mFUd/Z6W2q+hLb5iP5V0Fz+zieThE1mRpTXc91f9K5OfYUQa1dvW89eZZeti7iI4Rah36y3AbbNn29Db1tX39wGEkUQ+Wq9ZnMDO9rPOR9eP3F8dQP3aG1pzgkXX4iIXSucomWGLN5l9H/FqoGMaApyXhfX11Sc2fJS6p+CZLAF1pY1Wz5Qm4g1NnfXRWFrs+D5ffhGlWgdI3UZ05RKWq+UGs9o9WdbZF4w8OzrlEYy/DeFTZRIGoNRNzw+0M7GRWu/2QsT9pnf0vp7dsv4FxZc6hjvE3IEeidEQgfnpZxOs7SUsrKN6XmnlK0uYUdik5kGc08py16Zu/b3Nktb9ICpAbEeq7fZWiuMSJrWq+VWdmvd1rJE9pGe0DfgAjZy1tRcQ99tvapZULxwk3XidUt6Zf/aK/vX6Xyd7D+I+L/bO29/Ko3KLWFdpP6e3XHXjiPjoIgT6fZn0SuqTcq4/PLm5lDQOF/dmWoTo3F33Wb9E2bmkDNJ79bMzWIjo7W/Lij/hNmBOLm6M/MNjLbeC3QzI8NuyH7Zvd25WKYiRqZra7pRp1sGLH+EWWJPpk7o5nebZIkTA2tPby/BnZKI1uzrBREEgrxKJ1rN8IawOKWvUx7dPm3JTtS0EbPfaq4bmx9pi291MipDkUWdMFFR1K6hURaV8g6yDM7nRJ3E32hFwiosFa1ua449PudbpvhSvzuF9SgYSe3CDEXBoO6UOSrqSxtb1/Gt3dVaD2/UiTCFNQWBNL2vVytsON31HeKeRbJ/vzHLp5BJCu9ZVAhBmQwPxrmElEBCXsBydaY+KWQhaK1yHcZ8VlJfck95OvXmKj9Ku/8Bz1balZus1tJq+UZYmtQqVCkWtV5Ucy9GBWN6LYGCU2YL624Luxf2fOeFjcWDe6geeOT2fSmcV67T/VnhIezeYzgpGPNP2Q0VCSTstvacv2eQJXvUdtq+7euZ1Pr0tTbWt2x2vJPifeji96wzLujxHY0KiJ1wZLYzMldqPanZ852z/uGudGiGwdl2n2ys1FkP8a3aH+CYvlj2D3ZMfV6t+uK7Gzj0xxecp6XvKUF/cgFH/rqn0u5w3W2l1oxIY+USsf12d06qPlubxs599hjQ1QGfOUOlUhsbEyNYS+EJKVI5CE6n00Lqp/AajNDrhCUqtj+MSe6Qm0YAIZ9OIJsdvr88Hl0dj0ZvR6qwgFaKvDSCGQwaIglCEoQkCEkQkiAkQUiCkAQhCUIShCQISRCSICRBSIKQBCEJQhKEJAhJEJIgJEFIgpAEvTDH/AVJUCMwDGRoPjrUCKDflMU0htQks/zmRsrsDdwHqczw4dFcq2dCRT4sP1jUCBIb3g3VGZqXh5KImBtdKKSpzxLqErp0CSyGWX99/1o9RGHxUIqCWqZ0ZlERYiXESoiVECshVkKshFgJsRJiJcRKiJUQKyFWQqyEWAmxEmIlxEqIlRArIVZCrIRYCbHSC3PMXxIrPeGQwzkQ49YYGLKMAjWC6ySF4K4eLhh2Y8gRJBw6B7CX07lKNehTGh/MuNMHnQc1vo34fHpcqvZ5vz+vEZgv7XPAGuQVegS7ze5j8LzaBgGSMyRnSM6QnCE5Q3KG5AzJGZIzJGdIzpCcITlDcobkDMkZkjMkZ0jOkJwhOUNyhuQMydkLc8xfkJwtjlf1+/k0DTnkqTqfgx7J9Rf21SvFJpr7znyOPAGpSEhikkn3CGAlMWihyksFnqrl2f5aJTbVcZPk+ezHm3t/u1jvybE6xHrylbsrN3SCZKXITvHXbpNHiYC5mjkM1W+ob9fsNrdQUi5da6QV+3uSS/XIqV6ptruyn9JspABaZfo04ZO85z/cbiQ4p9Irs++FDjiHOIerBEqz1Zod94kLPV2qY+Bc5JlrWT7n/wnZi9MJ+3Kox/6MxwWkLLPoLMQ9WFG6XXn6bVXSEedWO73eJ9+YMv2aKZMjdZLTsPzYdniU5BmR0c1/C1pQxQZseuMkKwtNyo1Nmbyj/vDjI/z6pP4E5I76Pxo7g6Db7OrL7/cHJ1+B0sdXfwNVvy3SKXMAAA==" + } + }, + { + "ID": "c4945a32a5238a3e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "222" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "78f524acb937df2e5b9ebb2225202bbc/1445162066283580302;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF92aWV3XzQifSwidmlldyI6eyJxdWVyeSI6IlNFTEVDVCB3b3JkIGZyb20gYGJpZ3F1ZXJ5LXB1YmxpYy1kYXRhLnNhbXBsZXMuc2hha2VzcGVhcmVgIiwidXNlTGVnYWN5U3FsIjp0cnVlfX0K" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:55 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndx193:4244,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=W608W4DbNZTrqgWAr42YDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/85" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dbW8aORD+3l+xQnzgJLoJr2mQ+EAJvSA1aUpI70NbtWbXgb0s9p7X2wuN8t9v/LKwkOUlTa8N0qgSYI/3GY9nbI/9NHD3wilQIbgotJy7F44txFD6CCVdBZU+n5KAQWVhHPIRCQtlUy8oibmuD9g3EgZ+KpjSOCZjqiR9I3EkGYXUYWRKW87XUTD+J6Fi9jJKRmHgvfSJJK2YTKOQxm48ITc0jigR9Osn9nEoZk4SB2zsxJIwnwjfuXz/1ilNpIzi1sGBF/LEd8ecj0Pqenx6kIIf+NyLDwS9poIyjx6kj7+M/wkPKIP+AOjLbO0fn93UgpB7RAacDWeRNoPLCRWrQiXY3Zb0aZ+OknGfXXP1+Mf++YfO2/7Jl/75xdXwc9kZ6DFtOR73aXtJ6NhRbe/ZkDra4DZLwtDR4XUhuORtE1Qt51PBqJvQMLh1e6rBiRZ9KnxiahRUk3Qg4PWqpwREjJMpZVILnzYev3xAVP/TEPoiIcBazrvhaW+wqFVWPcICwIN/kkiImum8m5lRheopZ27v1qORwo+LFxrTtfO2z6JElhZi92/yjbQq9cYfm3FjKr5RkcJ/UFBEclEcKk9cEAFyN1JvpbnMQNeq9cPdsP/mI1fDDWjMQ6iJi0vFPoyH0dFnkgpGwtJyc2tLtfoT9QlTyNd02Hykph6TYtblYUg9NUAWfMivGONiCgP3nfqDNMRKeY+YMa29epriKYTUUFB6DpPudRKEPti0Xl316InqxlTq6rjD/A8B/XeTrsaOkah0vVeTpgOBMPsONcT3jZbScr3GbTZ/GFeH3NwrG3RUms1HhPoywEh5QVep/SIXvVE72g1d8HjmnqoaE7WqqPHV2Gv4FaHGrzeeAA8e3gheaVZ2nCt56AEDocwFftWspbhxwmBGXatocv+kjAoiqX9G5YT7Hc+DHZWLSv2oqVZCfkNLV+yG8X+Zc8kT4dE8kBMa0jGs0my8jKIXBouysY0Z13kHVdENCRvPNZhnUixbMk8db5tzdinW46XGY0BhD4klbLm3s2LF9UgYllbrLXZlW5iuwT4hUUTFKec3b4iasrPiUBAPbLdyJXmg+MFD1nHbVumN9lU32ler/Rj2ByK+Z63LtWelkV2RG/XfaE526mQwOokfyKw9D6NitYm15kmx9//4piuoH282ZrWJ0XhUeX6e6XLIYG63+OZhI7uVbVunf4N33vLxZmOWGxhtjWcYZgbDLsj52I2jpa1M7RiRlhZ1o1o93a/yd5g1/YnUUcm8VkkUZLbA4uLjJYRT4NGifYe8G45AUmWux+4ETh4hfQ0Hi5vFipzZM9ON+PB4Wyb0C7uS5yVGpSsir+YGag+1M2gQeSleJ4rgQKNPT6dakbAKU0Wb25o0My/01im+1J/SQ4edlq5IGMj6LKOitLaxDZy8mbtZaxdMYzSEGQXbaDgrrQrsZnp0/H8h5682ZvIkMgjhM/MSAbmxdDujWEJCICErYPE1nGjeJDIRtLhSdn0+rymteca6Kf/s+Ku0156uXYXJZi2VSt4IS5NYuSrBojaKitnCIGFMzyVQ0Ge2spRtkab1edN9Z3gID2VBDm4zL4HLxc2YP6/swto9It6N6X6fTagIIF230nM4EUOOnKO2Vs1bMn6S2jx9lZ31rfNOrlPy5uuayDrjgvZuqZfAzhkXG9YjS7XpfUQj75C156HUNcOQWXYXfVyR2QjJm7W/IDDz8sw9Dkx9Wl2NxeFEUOJfcB6msaeA/uLihopSjtCucPXHohYNpOnlGthmtb6Eqk/WpnHmOXsIqKsNv0uSmPrOaNbakhXOb3vOIamJi/rSdH5n2nL27Fp+p0R4xeSH97rLDaxnX+144fUj98b2FOq6rtOoOVNY79Tld0aPyk3n+LCdviFJKME702miPWMYBnodsED57W5E4gXRctEZdM56w96g7EDyRsdwKmlfXfYGX3qDwTtdCbGiqYyyM+dx2kjiIImDJA6SOEjiIImDJA6SOEjiPLsbXCRxkMRBEgdJHCRxkMRBEgdJHCRxkMRBEgdJHCRxkMRBEuc5kDhlx5AJbfM3RmUHIpcyn/qQXEaWclGDeAqeh1y0fXdvypfQxSRuj4hvk8SyE9gUzdAwbfN2l7IN5tEsk6NpmjUEhrvEWkBbiDyYva9nr2FKQ//aUiTUkkFnlttBPgj5IOSDkA9CPgj5IOSDkA9CPujZXQYjH4R8EPJByAchH4R8EPJByAchH4R8EPJByAchH4R8EPJBz4MPWvAK3SxvYomYhdQwC+sInLJzHYSQoqkLIkO1GNInvZZv6692c3d3annO2RggNc2m1O/M6aWPexY0n+9/ymjsmdVlx3x9YdvSJVCOPB1o9cPD+32b+C0HSUckHZF0RNIRSUckHZF0RNIRScdnxzgg6YikI5KOSDoi6YikI5KOSDoi6YikI5KOSDoi6YikI5KOz4F0fKhs9esENa2wuFSUXH+/YGml2uRjeWf2DJ6AZNIlPolk9ghnkRi0UPWpghzR+tPaViU2Wc0ecpbz19yz09Nhc0/+q0Osp69asJQPM2nOSpWdpD+60Z0EAnw1n/JUf6B5+1798BFK0sXXdtLCvg1iqa4MSytiu69WH7FdryBAK1gBoON5yK/yLyd2Ao6pzMVsHm8Ja/Qh+vABoDSbpdkzVV5wo/mwRVEd5Jdyh6WWKfX2G/LPjBH2ravH/oz7CSSd8/xKiBn0Ig279PqispJQZh617q0/ySZMevcq6c2gjmPqpn+o7p4EcUSkN3mf0IQqbscmqJl080GTdGFTXS6on5a8h5fP6kcmC+o/OxRaTv3wUBf3+Oc4X4BV9y/+AyxSrQOndAAA" + } + }, + { + "ID": "d8d460f2fd06745a", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_4?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "408611a814ebd8af9c0e1104919b6ca8/18355620827341689015;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_4?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:56 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:56 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnat124:4348,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=XK08W8E-h9WrBcH-ocgN" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/211" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2dW2/buBKA3/srBMMPPoBXsHxNDPghzeU0B5s0x2l3H9oioCXG0YlMaimqTU6Q/77DixxZpm9t2maLaYDYIqmZITnkDPXVzsMrr0aF4KI29B5eefYig6sPcKWLoDDiMxIzKKxNEz4hSa1pygUlGdfljMsTnrOoqJnRLCNTqqrOufSuVd3Qe0cmCfWiPAmp/C3lQv426LeHEZEko/Kq3Qr2WoNW96rb6u+39/eCXnfQHgRXLfjny6vPMf1y1S0URHSST0/ZNVcqPpy/fXd18vb9+dGnpjfWRg29kEd0NK/wrEWjZ7DBY0WXPG3GiOVJ4umRuxBc8pEZr6H3sRYmPI/8G5rEd/6xanCkqz7WPjJln2oyN1EVEjHNZ5RJVaEtrRZ+k+FKGPxIImF0Zv6U82lC/bKNUDzjzD++C2kqY86y+kU+SeLQFzTjuQjpuZ3oxlMT/3/kMxm22/1/fTfZndYG2RkVn6nwZ1QSNSL+geSzOLyUXMCcvxMkvKXiMiWMQaOE89s81aN7zGQs7xvrWmsDgl57//tYsIXuTre9o24tuaqTzCYRqZdU19sNV0OjdP8Z+1s/ISGU3/spFddczP7DJ5u73e60B8/R7dJQr+ltd0dVZ/CmLG5Bz1Kl6VA32HVML5I72FDu7q2crF6WWlQuqK7eYVR39no7ar6EttmY/pXTTP7Op9OYTedGFNcL3V/2rk5jjRBrV29Xz15nl60LuYjgFqHfbLYBts2fb0NvV1ff3gYShhD56r1Waws72s85H04/KfnqFu4R7GjOCRdfiIjKVpSKVhmyfJfR/xWrBjKiGch5nV9fU3FmywupfwqSwhZYX9Vs9UBtI9bY3N0Uha3Ngmf3/htVonWM1WVEEyppo1JqPCPoz7fILGfg2dcJDaX/bwqbKJE0AqNueHSgnY2LYNAK/Jh95re08Z7dMv6FeZc6xruEHIHeKZHQwUUpp7M0KaSsbWN63ilkq0vYkdh0rsHcU8iyV+au/b3t0hY9YGpArMfqbbYe+CFJkka13MoONm0tK2Qf6Ql9Ay5gI2ddzTX03darmiXFSzdZJ960pNf2r722f53O18n+g4j/l3vn7E+lUbElbIrU37M75bVTknGQR7Es92fZK6pNirj88ubmUNAoW9+ZahOjcbBps/4JM3PImaR3G+ZmuZHR2t8UlH/C7ECcXN+ZxQZGW+8FupmRYTdkt+zeYCGWqYiR6tq6btTpFgHLHWFW2JOqE7r53SZpXIqB9ae3l+BOcUjr9vWCCAJBXqUTQcu/ISxK6OuEh7dPW3IpatqI2Q9am8bmR9riWp2MSl+kYcePVRS1a2ichoW8gzSF8zlRJ/E3WpGwCgtF69uaY4/L+VYpvtTvTmE9CkYSuzB9kTOoO2UlFY2Vja3ruNbueq2HN+pEmMCagkCa3DeqFTacDlyHuGeR7N5vzPLJZZzAexbmQlAm/YNJJiElkJAXsEydqU9ymQtar1z7EZ+XNFbcU5xOnbnKj9LufsCzk3blJuu1BIFrhKVJrXyVYlHrRfXyxThnTK8lUHDKbGGj3MLuhT3XeWFr8eAeqgcOuX1XCueUW+r+vPAQdu8JnBSM+afshooYEnZbe87fM8iSHWo7bdf29UxqXfqCrfWtmh3npDgfurg964wLenxHwxxiJxyZ7YwslFpPavVc56x/uCsdmmEobbtPNlbqrIe4Vu0PcExXLPsHO6Y+r1Z98d0NHPqjC86TwveUoD+5gCN/w1Fpd7jurlLrRqSxcoXYfru7IFWfrU3j0n32GNDVAZ+VhkqlNjYmhrCW/BOSJ3Lonc5mudRP4TUYodcxi1Vsf5iQrERumh6EfDqFbHb0/vJ4fHU8Hr8dq8IcWiny0vTmMGiEJAhJEJIgJEFIgpAEIQlCEoQkCEkQkiAkQUiCkAQhCUIShCQISRCSICRBSIKQBCEJQhL0whzzFyRBTc8wkJH56FDTg35TFtEIUpPU8psbKdM3cB+kMqOHR3Otngnl2aj4YFHTi214N1RnZF4eCiJibixDIU19VlAXv0yXwGKY9df3r9VDFBaNpMipZUpnFhUhVkKshFgJsRJiJcRKiJUQKyFWQqyEWAmxEmIlxEqIlRArIVZCrIRYCbESYiXESoiVECu9MMf8JbHSEw45XAAx5RoDQ1ZRoKZ3HScQ3NXDBcNuDDmChEPnAPZytlCpBn1Go4M5d/qg86DmtxGfT48r1T7v9+c1PfOlfSWwBnmFHsFuq/voPa+2oYfkDMkZkjMkZ0jOkJwhOUNyhuQMyRmSMyRnSM6QnCE5Q3KG5AzJGZIzJGdIzpCcITlDcvbCHPMXJGfL41X9fj5NQw55os7noEdy/YV9jUqxieauM19JnoBUxCcRSWX5CGAlMWihygsFjqrV2f5GJTbVKSfJi9mPM/f+drHOk2N1iPXkK3dXblgKkpUiO8Vfu00exQLmau4wVL+hrl2z29pBSbF0rZFW7O9xJtUjp0al2u7KbkqzlQJolerThEvynvtwu5XgjEqnzL4TOuAc4hyuEyjNVmt23Ccu9HSpjoELkWehZfGc/ydkL6VO2JdDPfZnPMohZZlHZyHuwYrC7YrTb1BJR0q32ul1PvnGlOnXTJlKUqcZ9YuPbftHcZYSGd78N6c5VWzApjelZGWpSbGxKZNr6g8/PsKvT+pPQNbU/9GoDb1uq6svv98fnHwFSh9f/Q1NTp0TKXMAAA==" + } + }, + { + "ID": "b7160e9ac0ed216d", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "223" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c23984eeab39098eb0fcc92afac4376d/16747559391350904288;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF92aWV3XzUifSwidmlldyI6eyJxdWVyeSI6IlNFTEVDVCB3b3JkIGZyb20gYGJpZ3F1ZXJ5LXB1YmxpYy1kYXRhLnNhbXBsZXMuc2hha2VzcGVhcmVgIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:56 GMT" + ], + "Etag": [ + "Qx8qqQ+CR2tgZoOX5GpvJw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncp68:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/174,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=XK08W4mGC8PvqwXStL2oCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/174,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/174" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1S226bMBi+5ymQd7kFAzlH6sVaRVUm1qpJuk2bJuqYH+LFYIJNWFTl3WcbmLapk8qFJX//4TuYZ8dFB1YkaOGiHcuONVTnN4rsOKB3ugSKZKb08HN2PD68vVmHKvsq7r+Mb8vTh+bqyjYxO53UnIIalKJSg+kkXCREEQkqDv1g5k/9UTzyJ/NwPgvGo2k4DWJff56KTwyaeGz3SOBpxIqD2bZXqpQLjJum8TIhMg6kZNKjIse9THwKcVmJH0CVxP+w445d4tfIwNawxH+psdgaUqigoKA1PTuuizrC1UuWzZgGW8Ku4xXs7Zila4d+y3Dci82F7iEnvYKUAU+kvn3TNwtpsCC5kYgaUSV2n9l4Li222a5Xd7c9movEonePUfT+OloiA1/08b1jK+r8+qzAMCCrzSCRKLItVC9U1qL5A6AVEMVEsWWtnmA81LaHwXwy9Ie2gxOpPoqEaRvJ/7t67Z9Wy88WMIn0CdjXt86W0fJm6xrTblqJ3H3q/41BWe84owOTvydJXurn9eSeHECWQCp4akOvJUSQEXreHLlemBIuoYuBC2qdGJ7HDXIuzi8nstddKwMAAA==" + } + }, + { + "ID": "df6adbb9f5a3b085", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_5?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ce316024411f94ba7530fa3ded54d57d/15139497955376896265;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_5?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:56 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnno25:4168,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/166,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=XK08W6LXF4jWqQX0rLrAAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/166,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/166" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "fb856db780ed351e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "223" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "55224108e3cf24df3ff57562d1e14d8e/13603494113407327793;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJkYXRhc2V0XzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMDAiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidF92aWV3XzYifSwidmlldyI6eyJxdWVyeSI6IlNFTEVDVCB3b3JkIGZyb20gYGJpZ3F1ZXJ5LXB1YmxpYy1kYXRhLnNhbXBsZXMuc2hha2VzcGVhcmVgIiwidXNlTGVnYWN5U3FsIjpmYWxzZX19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:56 GMT" + ], + "Etag": [ + "od2m/sR4YmJHoQMRuwQI9Q==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnde82:4460,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/64,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=XK08W5DiI8jpqAWo5bOoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/64,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/64" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1SXW+bMBR951cg93XBQNPQROrDOkVbJlopJN00VRN1zIV4MZhiUxRV+e+1DUxb1UrlwZLP/Tgf5tlx0YFVGVq4aMeKxxaa45kiOw7oky6BIoUpiSwssUymv8rv38T6Jmm79Wq+vrqyTcxOZy2noCa1aNQkmoWLjCgiQaWhH1z6kT9Np/5sHs4vg4tpFEZB6uvPU+kTgy6d2T0SeB6z6mC27ZWq5QLjruu8QoiCA6mZ9Kgo8SgTP4W4bsQfoEriV+x4YJf4IzKwNSzxf2oslkAODVQUtKZnx3XRQLh6y7IZ02BPOHR8gL0fs3T90F8ZjnuyudA9lGRUkDPgmdS3e32zkAYrUhqJqBNNZveZjcfaYpttsrr9OqKlyCx6exfHn6/jJTLwSR+/B7aqLa+PCgwDstoMEouq2ELzRiUR3T8AbYAoJqot6/UEF+fa9nkwn0VRZDs4kepGZEzbyN7vGrX/WC1/WsAkMiZgX986W8bLL1vXmHbzRpTuw/hvTOp2xxmdmPw9ScpaP68n9+QAsgbSwEMfeishhoLQ4+aR64U54RKGGLig1onhudsg5+S8AC3SPTUrAwAA" + } + }, + { + "ID": "a4228e607ccca0bd", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_6?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "48f18ab85e25b18826b1630c8b8c2f21/11995432677416543066;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_6?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:57 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnam66:4165,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/158,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=XK08W6D9NMbQqAXq0ZSYCg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/158,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/158" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "e1fdfcdf46e2b1de", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_7?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "09855bf939140c436c9b08a9d8bff8b2/396278760718715543;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/t_view_7?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:57 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:57 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnds123:4337,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Xa08W8G1A8SNqgXr0IDoBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/71" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2dWW/bOBCA3/srBMMPXsAVfDsx4Ic0xzaLJs067fahLQJaYhxtZFJLUWmyQf77Dg85skxfbdpmi2mA2CKpmSE55Az11c79C69CheCiMvDuX3j2IoWrj3Cli6Aw5FMSMSisTGI+JnGlbsoFJSnX5YzLI56xMK+Z0jQlE6qqTrn0LlXdwHtHxjH1wiwOqHyZcCFf9nutQUgkSam8aDWaO41+o3PRafR2W7s7zW6n3+o3Lxrwz5cXNxH9ctHPFYR0nE2O2SVXKj6evn13cfT2/enB57o30kYNvICHdDir8KxFwyewwWN5lzxtxpBlcezpkTsTXPKhGa+B96kSxDwL/SsaR7f+oWpwoKs+VT4xZZ9qMjNRFRIxyaaUSVWhLS0XfpPhShj8SCJhdKb+hPNJTP2ijVA85cw/vA1oIiPO0upZNo6jwBc05ZkI6Kmd6NpjE/9vckMGrVbvt+8mu91YIzul4oYKf0olUSPi70k+jYJzyQXM+TtBgmsqzhPCGDSKOb/OEj26h0xG8q62qrU2oNlt7X4fCzbQ3e60ttStJZd1kuk4JNWC6mqr5mpolO4+YX+rRySA8js/oeKSi+kffLy+2612q/8U3S4M9YredrZUdQJviuLm9CxUmg51mtuO6Vl8CxvK7Z2Vk1aLUvPKOdXlO4zq9k53S83n0DYd0X8ymso3fDKJ2GRmRH491/1F72rXVgixdnW39exVdtm6gIsQbhH6zXobYNv8+TZ0t3X1zW0gQQCRr9ptNDawo/WU8+H0k4KvbuAezS3NOeLiCxFh0YpC0TJDFu8y+r9i1UBGNAU5r7LLSypObHku9YMgCWyB1WXNlg/UJmKNzZ11UdjaLHh6579WJVrHSF2GNKaS1kqlxjOavdkWmWYMPPsypoH0f6ewiRJJQzDqiod72tm4aPZ3dvyI3fBrWnvPrhn/wrxzHeNdQg5A74RI6OC8lONpEudSVrYxPW/nstUl7EhsMtNg7sll2Stz1+7OZmmLHjA1INZj9TZbbfoBieNaudzKbq7bWpbIPtAT+hpcwEbOqppr6LutVzULihdusk68bkmv7F9rZf/a7a+T/RcR/xZ75+xPqVG+JayL1N+zO8W1U5Cxl4WRLPZn0SvKTfK4/PzmZl/QMF3dmXITo7G/brP+CTOzz5mkt2vmZrGR0dpbF5R/wuxAnFzdmfkGRlv3GbqZkWE3ZLfsbn8ulqmIkejaqm7U7uQByx1hltiTqBO6+d0iSVSIgdXHt+fgTlFAq/b1jAgCQV6lE82Gf0VYGNNXMQ+uH7fkQtS0EbPXbKwbmx9pi2t1Mip9kQRtP1JR1K6hURLk8vaSBM7nRJ3EX2tFwirMFa1ua449LudbpvhcvzuG9SgYie3C9EXGoO6YFVTUlja2ruNau6u17l+pE2EMawoCaXxXK1fYcNp3HeKeRLJ7vzHLJ5NRDO9ZkAlBmfT3xqmElEBCXsBSdaY+ymQmaLV07Yd8VlJbck9+OnXmKj9Ku/sBz1balZus1tJsukZYmtTKVykWtV5ULV6MMsb0WgIFx8wW1oot7F7YdZ0XNhYP7qF64JDbc6VwTrmF7s8K92H3HsNJwZh/zK6oiCBht7Wn/D2DLNmhtt1ybV9PpNalr7mxvmWz45wU50MXt2edcEEPb2mQQeyEI7OdkblS60mNruuc9T93pX0zDIVt99HGUp31ENeq/QGO6Ypl/2PH1OfVsi++u4JDf3jGeZz7nhL0gQs48tcclXaH62wrtWpEGiuXiO21OnNS9dnaNC7cZ48BHR3wWWGoVGpjY2IAa8k/IlksB97xdJpJ/RRegxF6GbFIxfb7MUkL5KbuQcinE8hmh+/PD0cXh6PR25EqzKCVIi91bwaDhkiCkAQhCUIShCQISRCSICRBSIKQBCEJQhKEJAhJEJIgJEFIgpAEIQlCEoQkCEkQkiAkQc/MMX9BElT3DAMZmo8O1T3oN2UhDSE1SSy/uZIyeQ33QSozvH8w1+qZUJYO8w8W1b3IhndDdYbm5T4nIubGIhTS1GcJdfGLdAkshll/dfdKPURh4VCKjFqmdGJREWIlxEqIlRArIVZCrIRYCbESYiXESoiVECshVkKshFgJsRJiJcRKiJUQKyFWQqyEWAmx0jNzzF8SKz3ikP05EFOsMTBkGQWqe5dRDMFdPVww7MaQI0g4dA5gL6dzlWrQpzTcm3GnjzoPqn8b8fn8sFTt035/Xt0zX9pXAGuQV+gR7DQ6D97Taht4SM6QnCE5Q3KG5AzJGZIzJGdIzpCcITlDcobkDMkZkjMkZ0jOkJwhOUNyhuQMyRmSMyRnz8wxf0Fytjhe5e/n0zRkn8fqfA56JNdf2FcrFZto7jrzFeQJSEV8EpJEFo8AVhKDFqo8V+CoWp7tr1ViU51ikjyf/Thz728X6zw5lodYT75yd+WGhSBZKrJT/LXb5EEkYK5mDkP1G+raNTuNLZTkS9caacW+iVKpHjnVStV2V3ZTmo0UQKtEnyZcknfch9uNBKdUOmX2nNAB5xDncJVAabZas+M+cqHHS3UMnIs8cy3z5/w/IXspdMK+7OuxP+FhBinLLDoLcQdW5G6Xn36bpXSkcKudXueTb0yZfs2UqSB1klI//9i2fxClCZHB1Z8ZzahiAza9KSQrC03yjU2ZXFF/+PEBfn1WfwKyov6PRmXgdRodffn9/uDkC1D68OI/NEYZXylzAAA=" + } + }, + { + "ID": "769ea978cf46372f", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0016?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "66f011780f3e3c9410a81e121bbf02f1/17234679923460771776;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0016?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:57 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnls3:4071,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/153,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Xa08W-jjC8TYqgWC8ZqABg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/153,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/153" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "4c18b6f1821943bd", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?allUsers=false\u0026alt=json\u0026pageToken=\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f2ee33362e1f627c640ca4f14e121388/7243305967777083860;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?allUsers=false\u0026alt=json\u0026pageToken=\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:58 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/t0Z3pSUgORWqlogWHb60eURvW08\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:58 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnfj19:4381,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Xa08W4niFtbRqwWDg5e4Cg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/160" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO2deVMbOdfo/59P4eI+Vcm8lzDal1TdqhcbswWzeGFJJsVIaskYr3hhe2q++1UbvGJ3Q4IdEzw1M9iWdI5ay09HR2rpv38kVsqlWrDyObGiS8Wrjm3e/Z/Lut4rtdorqz7QtlUxDPx7haMy37i3dzZdIa2LysWWq33dOC/cnALyVxt8xY1coXiQPbmq1Isn25oBW8henwDx90pXTs3etg9V0ebrZVsLBUKKAQfY/yGYfDo86xyWz+4a+xtsvX5UR7hTb6XOTpNNUegm91lq+VTf/kgk/uv/S6yUulkOOhVj258a9Wb7E2focyG31ry/IvaWJ5s52ME7X+8sOd67zh1u2XL3gRKTn/cxyH/KWmebtmasj9JVlVhpNOuX1rR3Jql8SNhN+RAeqz+xUqkb1S7Vu8VQyK2EP/77oL/VVu1Q8crGwX56ZfCbr42SaQ1yZJq2KyFfqtrhwpQEMNhT4xM2209jQN7PtK0FE8JlP7xdb6tK8q5tW4fNurGtlu0+IuiFd4uvn6vY+MMRkqVKZTzUKHNht0tt/2u72bG9n7uFUrW1dv6u0c1rLr2XTuW75Zb4d6j0TL3mSsVOs1e4/52cy973lZat+GpN3NSbQcI169XEP70m8anR0ZWS+RSotlprqWqjYltrrQtVtq2GVU37T6JSqpbaCdjPe2B9FdUeKkXpyqD1PK/9hBK8rpZ9jHVuhBNAA6mh0A4gZx3lxFhAgbAUEOtYIDhjapC+Hap9SK1q9RqwTCNAWMAdwdQhLS3mNoAWawABQhwLTgh3D8X4WIS9pmU3Sq1GvVXqtdJUNr2eT5/vbJ7vp9Mb6Y3+Y980S08in2R3fNx8trCf8on6URvNUt3H7pb8zn4+nV1P5XeOB+Gdlt2zRWXuclcVH8epSss+qeKwLXSGOsJohxmK6KU1z21VlUJZK5gxLCXGkNBPvkTbANarbakQl2WDi6ZYurq4KleEruuLzv8G9tpW6g3bXCt6IdclY5Ux9U6tvWbq1VBHV0UMhkqVdsas67Obltreq25fFFmBKa1wZ78zFwzF6p8phjAFMhpDmCEZhSHMCFliaImhJYZ+CkObtnBWr2Sr+6nkevVUHV+VuDtN6tLxXmEuGIrV/1IM2Waz3szaVqfSHmTJt5XWg4BS7VpVfGlMkD2xV32e1Kt6qaseHd5k7baUB7mJbutO1FTVfp7cTScK/Lv2Ld+8S3RapVox4Z+qFijf13NHe4mPF+12o/X5r79Mpd4J1or1erFiwwr+qyf8r6BuWn81e/XwVy/5p9ZV5S9b8/nxQj8N//rn97XxUnw+uCGPAzcCMBLcYfiC0PCFfT2k/Qu7eq8gJrTK6c3yJ9vlm2+YPco/FFtvXpdIDIaqqSX3s0X39svuoXEmvi/QICPZvineHDSPCs1q8g6QDNlRl7J1tXWH5zLIxOqfzSBz1EXWBAVXwwFD7S13V2ur20RX+udEoWZvG/4ZbZD4e+Xb3ysJ1U58g5+h+L6W2HGJ9kWplfD/qsfmWQq86VlyJdtcTdiWUQ3rozy02sRNqX2R+Mf/vlZcS/zTjb8WBvyT8Ni9sE0fU9US3wYBPzFCIAFB9AiBBOZRI0Q3/DVHiG/P7bPff3aE+BFr8GVDxHCjmt6qFrtZ/QDfRx474rkX/MEXEM4lfJzJqHawy/IEfknWMpv5w9yXajp/czMfR0Sc/iWcXxHOBMS4fxFBNBLOYfgSzks4L+E8Bzgr02lv3+6xAj8FX93uPczvBoZV7kllPpZzrP6Zeok9beJMSQBYJK0A6vPuvXmJfwKsC+clhkRrDQizSFnisKJSI6SN9P9jBGuHBAoCa/Cieol/wHO0QBTaORTH2aP1Trme3KQ6Uyi6vT3E+XUTHs+FQrH6Z0ohgAcL3pMpBCiMXKuCBLEfpVD3+2FF1QYDcb8vJlbC4asLEQA+J3ZqjU6734V69QmGfulmP9MazTzjZCiKz/94BI7ZUIQbVWpnw5Javw43foA1MBqWaT0EjCjuJ8qo28mJHgJGEvkKCaZpCsMmauonmqDpIdEETb4v+KKzo8qwgIxLLCSGSHIOn0Tv66dTRPWz8AxRj7kaFtXF0UieIAMYhBtiKPS9H2A+Frmfo/6oNyqnl6F4OY/ZGZHTuug4V7EHnbbPcrcJd9tJZIxc4+nQ2K0J48epVtZXyLiMx6ATn4/2406godCGaiovsNJt60/0h6VZsd6IPIyM1gfvSuogc7iXHnC9G2gbQ1ZvYqi7DYHODyEbg0Rhso5+mtL//h/4udtJV4d/3MweZB4s2XMEoPD9jJwTwCSSAlLCEYfnAEC6Mkjzvf/x39XIjHXHq2fmbDRT+YPE+bkvmqIF4LzercLJGfhj7Kd+jiZxCX5OPDSHJ2CCcWASTESDSQo+LDWs61yY/9FH9Q2vn+OpGINTMQYxnA6yCckee85osl+MMiY4QEgCwBnGVIjpKJNxKHuGqMdcyUiUTSXiKMgg4JEkixPTq40RMZNBBpYgS7wAF12GPQ8YcyAWfBmxHv72cLASTneq3kwMIqd73qyrlGp2oiFmK6rhUzwQitNhg6lr8eUq9R7f+HBTbHislWrFQq3UftII+42gH4qGQpVpl67tUMLpNB7JnBT4F2ZurOC76n0bb3fnW/ET7R+aiWv/m3+MfMk2Q2SPJBp6dNE3g4fn7l2nYe/3/oJs0J0WtyY2hWdNkMdmyI9fIqwBMFz4Q7Pj51sR42U/a09E1xnX9UQ8I48L63noWsgQ03OqtD0n2OFzTbg+JwgElNkAQTY/r0NgnepU2hsPjzhcNj/emqKLdezJ3tfmuHy1sHcdFFXmGpxWeLuyu3F6luykPGZO5uL3iNU/S7+H4BBG+z0Ex7DvBp7g9xDC24kxfo+Bink7PgQfrHQlJs0vfAQxYsM+cXx4Y1gySiFHBGJvhsLRyAMjNtITEiulZ8NGzSfQsACACZsyv8B8kpReXmKlPGZlRMrEGQfiVDA09GjTpxxkiqx+AT1H1mPGhmU9nXOAwZMR0ZU6bdrBJsrp5yhezmN+huVMnnUQGhnledMO/DRoaNoxHPoGpx1d/8lq4j/oc61TnTAHec5otxZrgpBZT13CJ1hoh4u3gwiMBiIfaa2zcriMKHm+w2Uk2a92uKARqv+UwyVe1LMcLuwRVT1x09AnJzO0l59YMb3MkFj0YbxEX+KFzPglXpdIdC2W50VGOTfYcJucv+cFDgzqX5G7sZL/KdfL4EEm+l4gIIJy9mIXDPw9XTBk7i6YBzmJ/0m8qoW0sE4a5qgSAKBzEkB7ToSS59ri4Dwg1o9SCjom7aJuDXnjPpIGr9xXtyGuX++eFtN35YIu7ot0dRtgMRcfSaz+mfpIQs9gtI+Eor4lNNFHwoaOW1g8HwnFMtpHQsnIIu0v3ByCqfSw8syifprirdQp9j6d6EbpaY4T0tulMW45Pt08QpEf7ZkfAynDWIAI/0f87pFnyHrW9hGABGTSkxwIQkPK42mTgOg5wDPkTPDHLP0fs/F/mNb10nfhe0a074JyNNwOZua7QNPBFuW7QFFom6vvAj5SptfHp4MLxpHrGaJ6BRANLkoQogMMTnXb8ujNIrFiHnPD4zeLLH0Xj8mWvotX812QqF0jbGTz0Px3jUjwCzM3VvAL6LoQfSt4Lp6Lofnv0PD/i9wLYQ4W1jUAPPIJAu4c44CdEwTduRBanTMaQG6s5NbScddAV8SGdaXaQwMbfojwYT8PVVir3mkaW2iWxuyIYni+w9hD/qWvPvnG1v4U62vBa8NW3ZAl0vJtq6qGc5BYcSVbCcbQN4TNgYH1dCNyu9cw8tmd/a0hDA4ReIqscVu0Jyp0eWyls8Oy+h/7cP13dawAN+tNj9Lu2JQ7HgZG6/qgMV4JYapyqbHnh1HfM7P1m8eh7o8xhf+O1evS5fMqLh9WJ7LZLjda92eNrWO2kd/curnY24CH9m4uLp9Y/TN1+WA22NQy2eWD+SDGJJcP4bTvNHocEL2irqX6YKb20t62m35IHALwEF09cTZLFZsKK2+47/e7wffnUv6pmocu+Rr8ftlOtJf41J9suhstm1DGayB4svSRAn890n+foG0UjG8XGqmM7tyXt7fIXu3mzNXWc1WUuzfN5tbp/VygEat/ptBAgMf4iRGUIgoaGA6gsWjvEAqE8fDmg6feF4RJ9Fa6Kf6Ut/4OoYc9IBJwySlhAEZtg8NTZPXz8BxZvf15MW5gQrEUnGMAGPQ1I8ZiP9sNHCtnvm7gCUEL7k8JExae7csAic//L/FBfRg1vP8DYfg7/BFPyov0owf9elw/6upHM9ePH/Sbcf24qx//Rk7wVF1VbMvYJyCOc4MjwmJATNGrbeEDa0xiRL1szIkfAiAejTpwFEMcxXHmLRHBKMDhq+hieNPxqJMcksUBewT6Xry/GTMBQptQUhAeWEIniJqwnXjC2+Ecci4hgRAQJAmR07CORCTX0djTTd3jF+MEfzWu/4yfnE50Pz4FOwWvS/Zne8pf4BV/EUbQ1LU0FAcRikAMRKR8DkTgz62lsQhqRKylMbw4mOiyYYiS0ykBp8kabASOl9V/2zmSE11WIyHC8x+oQFOtPzZ5Q3EvQ7FietCSsZR4jVev3/NiWvQ61pxMILRIi2nhMs70BSuE5Y9vtqVoJutSr/s2M/LW1FxXpOa1lxZPK/xap7pRraw71z1FsLco0O/3TxfDdvZz6ewrLIY9yHmlTbY48fFhu49/nD//rv3d/rudGPvneH2vkM4lPvqJ4GoC/rnqP2n/CXU/Gf8J/zmr5bg5unPf15KKyoLDjb3DDctzhWbW7NRTRczuLhvljfJcvKOx+mfqHQWiD+sp3lEgaeSbxn7+9F5uA3pce//fa1VZC8vBK6o2Vh++5zo65zNh2mv+jx8kFndZ3gBjBZZQY6M0Vt6gFUhJx4x2ligVaAooRjpY6F37q8N1E5qLVW8oNCcPo72Jma+mMVO0m+ixlQytcA8tzRd8uxlYXb5phVUcpnjOon+/jYwabu1xhcM68zuZdC6/njkcsuWGlvHjdwc8NL/1ZlPdvUDteja7fjYS3zeWUMaT0hkvodHNCyN5fVm+ex3oBbl+UkGJqDoa0/y00EYFTVEe8/ijBTD27fvPls38qvVJuUYX7NMyii7aqWUbU7hjpTv29ftzGuIfT2L/O4EKx6rSGcPCw/N3A0a3xAw33tEnikiT6BfNk2q4ftS9ol7as4aQM5aRvkxvCbJPAH9CIAHpZ0A+A/p/AfgMwMqkMup/GtvY83SLw5sx+OTxLqlfptiXzm3dIpPKHaTwrTxVO1vVuRh8sfpna/ABGH2LmAAQRi6H+xkDfn8G3+Lac5pT55DjSCBDiQkg44oFVBDKpbNYBoqEJ2nxpT23tOeW9tzSnuuHLu256fbcUKcbM6S6xd1PNFJqzzTjVp+TRk/thN8nN7RZ2aDmxTboWOv+4fKLyvf0nA/nPXh+V46slZ/PiX1BTp5R10szP9bMP8m09wNucgdwjyezm4UavDkBX9bTRdyei5kfq3+WZj6XpH/s9mQzn0saeVmwD4d8aeYvjpnPkSVEaasD5ohCQFOokfS9iiqmDPKClNBELfZt7XMx80OTe/BC0vNsgT43e7BcCxfUwGBn4+8DxvL9HQxOU8dneXawlSrzm0wzd+n2OdrJzAWMsfpnCkbBcfTRulwIiqLAKAT74SuFlmB8fTAqyIShwuGA2ABLxQMlpdSWwsBzkWlnqEPMwSUYN8Jc/igYBwblb8jEm62Sgzl0UXRFXbxINk7NRSe3fxLcXhTmwsRY/bNlIgRxTESD6yAnMhHhJRMXiIkcBBwzTpAnIA2kI4Rqy3DoGebaWsw0xs6IJ6/fv08m/pTBOGGi/fvajgdn5eZ19egrbGcJL29c2ZN7XzsOlM7Sc+FkrP6ZcpLj/hm3UzjJSeT755xT8MOvki45+fqcNBwqCbQ3IAOILEeSYIDCwUwFhGkrICEQB26xTzCdCyfHVx6mLTtELXwNvIzPY+xUV/FPuDynLc38oON0sObw2zAe1vZvIbo8qO3ubNyRy4vaPqYyna7elXbnwvhY/TNlPOM8xhZmgvIoxjPB38vF52+C8Sw894UhRC2H2AACiYJISgF8KkqNgVBhaOTScToB2a9gDI/B8/fh5OkRT21aLrNfm19S5fTlXhCUNd5qX7Uqc+FkrP7ZchIOPAJTOIkGV69P5CQicVeULTk5T58BCzjRVFs/zUFcSBgE2mIGA+GQcd4INtJIZeWSk8mzfDr3w4z8WpXXvyEOC8k9cH+ylbO7B7f3hZvdy0aDMXmoSyUyFxzG6p8pDimi0e9RcYp5JA7pkHNhicNfj0MWKJ9CUayYsUQyLYjzXYoG4bUSRAlrgLaakyUOxzb2vZSH68nUb4jDTru5J3KweeBKhSLekvcn+3fm4I7unZzOBYex+meKQ0IljMYhYSLSU0o4WM6iFwiH0hkMnJXOWYKoJn4aLQxjBhqnHDRIEBForZY4XEkeHOz9MAzDBvcb0lAc7yaPj2v3lS/pQilV1VAiKguildnYmAsNY/XPlIZYiBjjEEse9c4VD6trScPFoaGhFhMsOMaEEKmUH6wIg0xg6pR2BFJunaGSLWm4X8ikszupHwYiXMPg8Z/fkItfFT9t7385CarJTjaZyhzsZcut0kGlcCjnwsVY/bPlImQxm9QxirYSMZLLTeoLxEUXMMW9DAccUTA8UIRwxxEOF10C45wTUpCALPcdrWzuHaznGfkZLv6GPLwoHvAgdVd0d53C0aFVnUqgaplUvrI9HydirP6Z8hBhEbP2jCiIdCIOnR+65OEC8JApaaBFijMXYOQgkNhhCwItGCOE+UmI9rYjW66phJn5GRr+hiw8O/lyXM2JzZ3rfOHrKb6vbRyx5FV6u747n72WsfpnykLIBqSbzEI/3Yp8Twey/lHJ74SF4f6Nj/1NHB8/TNiN8eHPmZ1Q+Qqzam2B4B6NgXMMBJZBIwijgW/IghmvWXuJCi8sLcO6fru8yR/cV/bvYHkvu35TBqlkPYPy9/eX+OxmPvv+YvXPlDdAwhjeACki9/1BMHiL5n3wJnx34i3zhmhjuXAgEEjSgBDpJ6jOSAWxxMz/5P8EboFfqX7bvHH8yJIj1Ty8yuzXzsqF8ilPr5sTl67N5+DdWP2z5Q2UMXM9gKI3jAAk39k7d3GoWVzSKMAE0UpoHR7NoRTylWs0Fx4xKODaYoC1wXZhz9x926Q5PsJ5mWvcBdvHhzf4pHV1EwQ72cPqVXs+O3Vj9c+SNEwSGu1lZ36ki5pJ+XD6zrzsqfVc/uMHV69/SKznEt39ngtMF4ulVoYJi6QxNnxPkWumpJYMEAQMUMxbt+F4s6TLDOjSuKtVZJs26HEyeVDaaNhWtr6+fxrc18Rc6BKrf6Z0EXF3MjPfdqMuEPDh4p3Nmz6sJ1MfFhcnWgMuYCCFQYjqQAeCGSkAEUIRX2ciAEJpwhf2pdi3jRN+c21I8vL2S3v7+v7YbmXYKbm/KtugiOaCk1j9s8UJhNG3NfsYPNJYEVC8M2PF95f04tIEISuYpgFDDEBoAA2MRcxQSBA2SBtvs7BAA7WkySxoUrtNdZLFcrpl1dnZtjhYF0mWTl/v8fbXudAkVv9MacIJiXbqMk5hJE04Re/MyRJuLFlYmGhrNXAWBMIDhJMAcCkhd84pBwwHRBuGgAzMEiYzWSG63xf3rdbebaezs3t2upmsm9uLygHWaj5H6sbqnylMvGURAxM/FYqc6TAJ39mKNFxclBijPUAUQcohg6WDxjngEEIcSQgt4poji+yT2xGXKHkNlLTdgRGV5O7ZNs1VyxBuGfD1KH+5eZo9mgtKYvXPFiUAR1/CwxgkMhIl8L0dQts9gOrj45eHCy0+foOrCfR9kZeYfY9iTlNmw2krtRIpKD1kCCaIysB6xhjJFVvYJeY3frlrZrdc0ZspweqNo01Y3d3hLVu2l6hwS+eCmVj9M8UMxTLGYqG0f2vwRMzQd3cJwLcHqKwm8PfVBAkvdv5GVxPMf+F/fl9czlATIAdIOF/FBArrkWKcIZBhASCCTgGmLIRgyZmZcAY0r/MZeFVt37UucXIrBY+ayf3mbt50LubCmVj9M+UMYTLGaUtE9MyIiPc2M/r28REzIWLIaiKEzCIDJjRIkREYQg68QQOExIRbiDgWFihBlPVirVgaMjN6Mep68/TspHLYqpoTqL4eHLr9ej3DLk1mPqtCsfpnCxjA4wADI1+g9+HynR28/Dg58oRZTXwLAfN9gadJgQBUIyIlVdraQEoAMJLWeNPFSAkl9CIBDhb7dJG3S5dO7vLsGDbcQZ4fbKSb6UaDXxfBQbqp53MlUKz+mdIFYxDjjcEERK4ShSb3+6JLf5K0uEyRhGkGqRYIahhgK5nD/i8nijDrp0oAUqGxWex7kd8uUy6z6ZK52No+2bwSUu2nG3Yr02ptnqKt+RyAFqt/pkxBvplGMwWxyFe5ffh7e5W7OyNaYCMFMoO9jUI5YBxiygAiGBDnOUMVEtYgyjXQbmEXn984UE5heuvW7ZxcaLSVumxamam7o3yzxrLZuQAlVv9MgQIFjNnFDyWOXDLyM6B3tmQ0+j504q29NKT9xAc4KRiERAcCWk5swILAz42MdBBzgQJJlFjyZia8qeQ71+BL9qBWDUq5i4srsX93emdrhWpjPkvUsfpnyxvA43iDon26EKF35nIZfR/6zfGGYx0EAlhmESYUOsepMg5AawKHHTFQcEOpXOzDu94wb9KnDX2/06np6+B0O506sJVk9uCkWKjMZ3ddrP6Z8gZgEH23HgOERDphABHk/fHmjds4gEsvXriAc2E45Cac9iLJHISABYo4CqQ0cmnjzOg+z/WTo/3ydapSqGHxFd9snh2Bk+SlSx/Ohzmx+mfJHBrO3COZ4wc7EuWk8eGDh3ofzPn44cJWKvUPq4m3Cx2tpQ6ckYxqophzLiAGM6eo0IBJAIUBlHKw3CwzG+j4nzpn8tYeYrxT3jvN39GNckO5SlDfnAt0YvXPFDqCsWjPMPWNN8qR48Pfm6HzLYY1sTBa4GUqI6lUUjGJqSKM48AEWEItVMgi5AKorcHO6CWMZnNtmcub6g0+xZn9GswcH4tSJrdz1SSuOp+bKWL1zxRGXJJoLw8VgEXNuny4eGfLVDGoWVzSBIG3cqS1iGLlrDCGEggAYYw7FXAMtDFYQ7LYl1a8XdJsdS7rx8oddu5PNpq4skE6breZRIYU7+dCmlj9syXN0E0Ok0nDsYBRpAkvyXlfpHlbp1A5IwLkZVCpGUUWcEIEdEgo7gKtENLcICctXOJlNvtt9k+RLOSbxXJn+zK51Ug3jpLNM1glZ3tzwUus/pnihbHBDr3JeGE8cg+fD39ve/gW/BgqoDnDgXJG+n8t11AzwrGyziFoLPWskcJRvbAvaL9xntTzoi4P0h2Rwmz9Zvv26uZY76fVze1pfS48idU/U55QCWO8NAxEHppJw/Wo98WTxT6HCkPkuxDFFgcysAFW3hTxRIGWckOkE+GL2oFDSz/LbHCiWl/2b+obJxV2tAcuRaZwwS+bOLfZrNXmgpNY/bPFCYh5Q5JSKGgUTigC72z33uPNkYkPYA0tslvFRzCBsYIKqKGDhkukIQ+Mn/NgggBBUEptg4U9LvONg8Wc5VyTqEKxun50VqzfVrfMtd27b3Q25nOFZqz+mYKFIBG9bYaG14xFgYWQ93aNQNetAtdwIvSqPFJmgf0qSvv+oyDGFHKhNRXMUQcIZ0Sa8DJ2G0jQPfR0yZdZ8OXwrHNYPrtr7G+w9fpRHeFOvZU6O002RWEufInVP1O+YIJJNF8w5VFHyPhw8c6OdljoEzShoQYYJ7SCLtBGh4fFYEYB5VoirIAOvHZGluc4/ABN/kh8/+PfP/4/N9aBbLgtAQA=" + } + }, + { + "ID": "b5dc429c895323a8", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "72" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a8a74d6b7c0217b029686000bb871d85/5635244531786299133;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkYXRhc2V0UmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6InRva3lvIn0sImxvY2F0aW9uIjoiYXNpYS1ub3J0aGVhc3QxIn0K" + }, + "Response": { + "StatusCode": 409, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:19:59 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:19:59 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnff78:4058,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/189,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Xq08W_mTFoqbqQWg65_YAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/189,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/189" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1cW2/qOBB+76+IEA+sxIm4tyDxwAHOFqnd7UK7F51THZnEhSzGzjpOt2zV/77j2KEhmF7PjcqqBHg8+cZjTzzjj4bbA6eAOWe80HFuDxzdiKD1EVqJCIQ+W6KAgrAwI2yKSKGs5ByjiCVyPw5J4CGB064ljiI0w7KvR0DPXznDmyASUccZIIEiLBw/Jh4W70LGxbvDVq0j2GLF0ut9PI1nI3rFJMLH3sl42Bv89Xn452hyPrksO+PEcsfxmI+7m72ONt2lMSFOgqM+Jq6dcSZYVznUcT4VPMJi351jEty4Q6kwSLo+FT5RiS1VNuFlD+KzeImpkL3am7zY6JxUgj+BBAx86c4YmxHsZocA4iWj7vDGw6EIGI2KZ/EUZtZFahLVHJbu+92/0TXq1CtHP30d4OrXAq5VHwGOML/G3OUsWrnHUqJneiwFAYVuUdqSK+xGJcWOYupyfEWwJ9yfMcUcQtQ/xWLO/J7nQaAwXq3XjgDvmi1w6YIuKPuXOhMWcw+bQAaY4BkSAZ1tooyWIUlRHtRJRtiop9iy6RJEZ2sL6poUS7fUVe0nLkYyZ3I+xvifGEcCgv5mVay6HiKklJdr7GrlZdgDFIaYHzO2+IA8wfiqeM6RB77rftmzZXjrIjWIo9pr/Ks96F+9/jLs3xH/L+ud0Z+ckg7DZuM7upO9fTIYvdgPRNaf7ajIq2hvXhV7X2dt+hz70cPO5FWUxcPHNp/vsDJ9RgW+eWRttpWU1dbhj7c6J2z2sDObCspa8wcMM4WhN2QzdvOwlcWWGSNMeouJUr2eZixzhtkxnlAWK+q1hsJgIw0Ws40JhFTg4aJ+P0McLbHAPCq23TmiPsHvCfMW97vyRuZULlRblXb7RxuQKawpKPDQgymV+VTfTePQSxF7oapFodg4TkxxbTI19bBuYrtmCsNdhifJpxHcmZwiom9Rl8cU+kY0Y6K0U1kH0XPcVUB9cI1iAncXpFSyKuU7dGI9NK3sF0E27zzqRopFQOAz9WLOoS52e9NIQHEgoEKg0RXjyw+xiDku5tquz9aS0o5r9DI1jFXLt7Jef711GSYPW6lWTTMsVJHlymIL6ygqZhvjmFI0JYmBEdXCUlZD74rN5mvgITykBwbclqmYM+Jm3F8L+7CPT5G3UMMf0TnmAZTuuvcXdkGhXjaYrddMm/wXMmuyV32yvV2rY1wU405sjqxTxvHwBnsxZNGo2NQrsiHVkVRptt5eKPXVNGS23fsx5vp0hJju2m8QmKaac48DMzm55mPxfC4P/WeMkTT2JNAfjC8wLxk69Q7XeC5qUUGqUe6AbdUaG6jJKVspZ67TB4JGkvBpZqpkfaNzogf3kvsBxUR0nNFyGQs5Q4otwlcBDWRuv51C5dIdXJydjPq982HZkWzYDOra7sVkOP48HI9/HUthHClmquysKa6u5bcsv2X5LctvWX7L8luW37L8luW3LL9l+S3Lb1l+a19JCctvWX7L8lt7HZhvkN8qO4oY6qp/4yo74DemPvahOAk1KzUXIjyG66CW6d7eqfYEbMRRF0Z8BalelJ1Ap3dFU3XV223KD6kLs1RXQmPtoKLcTf4JlGHYsPTvV+8hHmB4XcFjrOmyU819WcbMMmaWMbOMmWXMLGNmGTPLmFnGzDJmljGzjJllzPaV5rCMmWXMLGO214H5Jhmze46ov80sZbsVTbSL5So7VwGBDC+5hnsyquxA1ZEUArq53OiUM7/Efm/Nq33U5VDZ/PDk5d1OwGc9hFl21JOd3fVjnSAJvWQCGpX2nfMstI5j2TrL1lm2zrJ1lq2zbJ1l6yxbZ9k6y9ZZts6ydZat21eKxbJ1lq2zbN1eB+YbZOu25yv/EGdCEfUZkSd1sCNY8lRnKSdW2dx0+svgcShFXOSjUGQPAxqJgoaUpwYMXbvr/keN6FInWy5vVj/GKvz1sMYzZH6Kk8WX4S7DMJMkcyK9xC/dJgcBh7VaBwxOPmDTrtmoPMNIeuvqQWrYkyASknwq5br1rlx7xmafQwCtkEBhbUQ+Mh9znwQMVbgRs9V+JKztGto13AIUaqtVO67MKgt5FMw05UFwI/NsaOpJrn2H6iXjhH7rJ3N/yvwYSpZ1duZ8BaNIwy49/lZz5UjmUr28jVf5ZEumvSqZMqizCLvpf8G7gyAKkfDmv8U4xvJbAl3eZIqVLZV0Y5NDLsjfPL2Dl0v566cF+cVVoeM0Ku2k+eKfUj0AzLuD/wHmwamJ41UAAA==" + } + }, + { + "ID": "f9bbfa3a6ad62d56", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ed3902c0cb729fa74642242dc18d31ce/4099240689816664870;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:00 GMT" + ], + "Etag": [ + "U3SmvBJDc9rDcwXnyO1vlg==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:00 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnnh23:4086,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=X608W7m9HcOUqwXjz4WoBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/61" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2R3UvDMBTF3/tXlPjq1rXdhx0IKhsyUQd1MkF8SNO7NjZtuiRtGeL/bhLmhOHX6z3n/HJu7pvjooJWKZq6KKHZtgGxO0mxwhIUOtUiKJwZ8TF8KNurmxmJxIx0T9Vu6bcsOz+3JmrzacMIqF7NhepNxsFU8WLHrS6BbW5pVRhXrlQtp57XdV0/4zxjgGsq+4SX3mcBrw28WvBXIEp6R1RvX056X/j9KIYNCKgI6GfeHPcwX9h2B7uL9uzFd62R474bJiYEpNSGZ50wNBcJzgwarePFah5blF6tBkIxuxa8qY24Z68FVSCkplncEWG5vv8LsOyq/+UbCeJqNy8xZUYKx+MwisLQH456iX+mBj4vVYSDSVSQMCMZ3ebbgp0lPMmbixRaYLwG0c80paUE9Nq8qZQ5xw9vx/PL2V/lY8DpZ3vHfTH/SQRgRXm1oqXF+KPA94fBYDiajCN7RYaluuMp3VBIf3FxYjlGxZLiXqUPl4PO+sh5dz4AU9vuetACAAA=" + } + }, + { + "ID": "1615a9b77439cdde", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "238" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8b8862a6ddb5515a91007135b35c2923/2491179253825880143;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJ0b2t5byIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE3In19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:02 GMT" + ], + "Etag": [ + "yonWfHhhXAa0btjRjsmRtQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmp20:4344,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/40,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=YK08W4HPOcKVqwXP5SY" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/40,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/40" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1SW2vCMBR+768o2auatF6qgg8K4gQ3WFfYYAyJ7bGNtk3XxLki/velicKQju2lJd/5biftybLRnuURGttow+KPA5TVnaSbFFBLjUDSuB5VPH/Z3ifJ65SSjdz5O5H58mky0SSm1dEhDUG2C17Ktjdwx5LvK97RVmuXOEPikd66RwYjdzR0+j3P9Zw1IY6nLQSk2xXL97VRImUhxhgfj8dOzHmcAi2Y6IQ8w9eG+NPFRcl3EEqBb4JxRCUVoAa6AdYNBP5nEU3zYQsl5CGoOifLttEla9m0aC1ToAk1DB1scO13Qf9uYNlnfRthAhm9hm8ZpJFQpzd10pACc5rV7cy7ZTBZFRp7Dvzl4wLV4LnVpDlk4lazfAzmi7lvROrxfqmiuLNKQh2PCLogK57HAZQNE58ffwBhCVQyngfMBDv9rlq96xLXcQfm//oqWNnEGYy6hBiXlAr5wCOm7iH63em6SDCdreZGx0PtXKNUMNrO1RdLQLk5yDpb32orU776AgAA" + } + }, + { + "ID": "7cb687146c35d2d2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/bigquery/v2/projects/dulcet-port-762/jobs?alt=json\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=994fdf2cdb7f9758ddc39647271b8fb2bccbd34f4a8948c2cf19afe741f9" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "166922398b2b2af05f99e7d2e9a8913f/883118921641624696;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/bigquery/v2/projects/dulcet-port-762/jobs?alt=json\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS05OTRmZGYyY2RiN2Y5NzU4ZGRjMzk2NDcyNzFiOGZiMmJjY2JkMzRmNGE4OTQ4YzJjZjE5YWZlNzQxZjkNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImNvbmZpZ3VyYXRpb24iOnsibG9hZCI6eyJkZXN0aW5hdGlvblRhYmxlIjp7ImRhdGFzZXRJZCI6InRva3lvIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIiwidGFibGVJZCI6InRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTcifX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJWY29DSllQMTZ4QzhHY1pzNG41YXVCVU4yYWIiLCJsb2NhdGlvbiI6ImFzaWEtbm9ydGhlYXN0MSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQoNCi0tOTk0ZmRmMmNkYjdmOTc1OGRkYzM5NjQ3MjcxYjhmYjJiY2NiZDM0ZjRhODk0OGMyY2YxOWFmZTc0MWY5DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KYSwwCmIsMQpjLDIKDQotLTk5NGZkZjJjZGI3Zjk3NThkZGMzOTY0NzI3MWI4ZmIyYmNjYmQzNGY0YTg5NDhjMmNmMTlhZmU3NDFmOS0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "1004" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:06 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/1LBNFVWLbsa7KKHcbvfSNB6dif8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703502000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vxls10:4218,/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/7,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Yq08W5jBEouNlAHfoYyQBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/7,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apps-upload" + ], + "X-Google-Netmon-Label": [ + "/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/7" + ], + "X-Google-Service": [ + "apps-upload" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrShg6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur2pkxanyf_M-rYJS9c4cpuAvuglnqROXeSDg8VM28HLcs5FMPG8mc3kA0_vBW1dGZjyZi8Xukh2Ws51qkQev44qHYAPw" + ] + }, + "Body": "ewogImtpbmQiOiAiYmlncXVlcnkjam9iIiwKICJldGFnIjogIlwiNzJrN0R6ZXllRWw0c2hsaEdmblpEX1V3WDA0LzFMQk5GVldMYnNhN0tLSGNidmZTTkI2ZGlmOFwiIiwKICJpZCI6ICJkdWxjZXQtcG9ydC03NjI6YXNpYS1ub3J0aGVhc3QxLlZjb0NKWVAxNnhDOEdjWnM0bjVhdUJVTjJhYiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYmlncXVlcnkvdjIvcHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2pvYnMvVmNvQ0pZUDE2eEM4R2NaczRuNWF1QlVOMmFiP2xvY2F0aW9uPWFzaWEtbm9ydGhlYXN0MSIsCiAiam9iUmVmZXJlbmNlIjogewogICJwcm9qZWN0SWQiOiAiZHVsY2V0LXBvcnQtNzYyIiwKICAiam9iSWQiOiAiVmNvQ0pZUDE2eEM4R2NaczRuNWF1QlVOMmFiIiwKICAibG9jYXRpb24iOiAiYXNpYS1ub3J0aGVhc3QxIgogfSwKICJjb25maWd1cmF0aW9uIjogewogICJsb2FkIjogewogICAic2NoZW1hIjogewogICAgImZpZWxkcyI6IFsKICAgICB7CiAgICAgICJuYW1lIjogIm5hbWUiLAogICAgICAidHlwZSI6ICJTVFJJTkciCiAgICAgfSwKICAgICB7CiAgICAgICJuYW1lIjogIm51bXMiLAogICAgICAidHlwZSI6ICJJTlRFR0VSIgogICAgIH0KICAgIF0KICAgfSwKICAgImRlc3RpbmF0aW9uVGFibGUiOiB7CiAgICAicHJvamVjdElkIjogImR1bGNldC1wb3J0LTc2MiIsCiAgICAiZGF0YXNldElkIjogInRva3lvIiwKICAgICJ0YWJsZUlkIjogInRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTciCiAgIH0KICB9CiB9LAogInN0YXR1cyI6IHsKICAic3RhdGUiOiAiUlVOTklORyIKIH0sCiAic3RhdGlzdGljcyI6IHsKICAiY3JlYXRpb25UaW1lIjogIjE1MzA3MDMyMDUxMDAiLAogICJzdGFydFRpbWUiOiAiMTUzMDcwMzIwNjAwNiIKIH0sCiAidXNlcl9lbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKfQo=" + } + }, + { + "ID": "54fb09f21116e865", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "45b164a3b40ee64b232ff661ddeea669/17793576578910046624;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:06 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:06 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlq25:4400,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Zq08W7DJDJXaqgWP8K_QAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/169" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dbU8bORD+zq9YRfmQk9JV3oFI+UADFCqgXCit7tqqcnZNssfG3vN6KRziv9947Q2bxHnjrbQaIZHYY8+M7dmZ8TOQ3G44BSoEF4W2c7vhmEYMrS/QSrug0+cjEjDoLAxC3idhoaz7BSUxT/sZl/s8YX5GGdE4JgOqSCdcOheK1nbe877jJ6FH5ZuIC/lms1Vrf/J49/1fp9XWdXfrnfd33GBNkrw9P6mRfsbMp/1kcMguuGL35eTDx+/7H85Pdr+VnV6qQNvxuE87Y4JjpHfWlOewTFUnFdlhSRg66Y6cCi55R+9D2/la8EKe+O6QhsG1u6cG7Kakr4WvTOmihozVUZ1EDJIRZVIRQKvprjWUVFPhRxIJqx65A84HIXXz+kD3iDN379qjkQw4i4unST8MPFfQmCfCoyfmsEr3Q9x/yBVp1xqbfzwb72ZlCe+Yiisq3BGVxCeSuLBPHwXxLqk4iwhjQLoiYQAkCpQjzi+TqDQ7xiyk8WhhIRn1fVIMU0FALtbmSqttP1oavaZeIunnQA7PgFE8T1Zzq/pyslqtx+9itn3zN6++ppBU5x79N6GxPOKDQcAGhnFcNO17WbPHWC0tYGAWXln3QBfpZGgeFz5MEemb5TpUt9Z9Xp5Bh8ZTns2kDsTzwEsXtyqVFfSo1Z7XRsZG+gymsc/FDyL8vAb3XVYlZmc81IUecY+EVhnTlFRAa0WvKXh84x6oHuDQU40BlYdMUsFIWJog6POrbzYfwdnGcbs15hgnDCz6IqSedN9RcCwQIfxjKofc30mNjItao1J1A3bFL2npnF0y/oM5Z2m8sjHZpSEdEAn7P8nlcBSFGZeFY1IV758d1QQ/xAZjCXpOxsu09KztrdVCcLpTakOMtUKGcn1TrLpwrGFput/wri6znzm8d0kUUXEAxrNPPMnFTVGZDazd0BVlRvDMJK3E1rJHeeH6agvXV1/mrubw/kTEf/nVWdczNUiLrDeXPTLPuZz8Q5PjsZP4gcyvZ9YqpoeY1TzK9p7nbLqC+vHixUwP0RI3lyVKP+Fkuhyc5PWSs5kdZLzzisn5S54OxMjFi5kckOWwr28hmodxyHbezc2JdFhFjCilFtNB9Xoaq+zhZY4ykbpW6t81EgXjyFfM3pyBHQUeLZrXUyIIBHeVQ9TcIWF+SI2e4xhpUoVWs25z9oxKV0Re3Q1UnDJW2ou8s/RdFsKNBboiYUA7ZDtRBHc8om5zpbmDzR7ZjHSx1O5QXQdCMB6IGOFNaZpg4samLfF6Es72B0vbSSKDEN4zLxECLuvuTj+WEPskBEAWX3Ax2k9kImhxqu36fNxTmjMnu6pag/JLSbdayXrSlZksllKt2nZY6hzCVbkENVZUzDd6CWOkH6YCDpnpLOVHmIe+acsuV2YP5qFWYOHbsuUqVr655Y87u+Cm+pBda/UP2ZCKADJTQz3h5wzSQYvYuvVG/ERibfKqK8ubdzrWQ7FelOyWdcwF3UtRCQ6urWlOZKLXWFKlacMkfnFT6uptyLndex2naMZCbE/tCximLaX6hQ0zvZhN2+LHoaDEP+U8zGxPMfrMBVyTSxai8XCNdbkWNUut5Ry2rVpjgmt6idSDc/NMvttI0yqW2yqVUpiY6MGz5O6TJJRt53A0SqTaIY1c04uABSq23/ZJnIPRyw6EfDqAtK1zfrbX+77X633oqc4ERilovOyMkfkOwvIIyyMsj7A8wvIIyyMsj7A8wvIIyyMsj7A8wvIIyyMsj7A8wvIIyyMs/6rQT4TlEZZ/lYb5G8LyZUeD1B39B/RlB9ZNmU99yA8iA6YPpYwOYB6kEZ3bO91WwEcSd7I/ry87gQnvGmLv6JfbDLTWE/MIfQrBz4HF3TzUDxrDqb+9eauAB+Z3pEioAfiPDW6PGD9i/IjxI8aPGD9i/IjxI8aPGD9i/IjxI8aPGD9i/IjxI8aPGD9i/IjxvyooFTF+xPhfpWH+lhj/PV7dnUDF8xSNVs+D5MvORRBCcFe3aA2kaxg/5DoHMM3RBFFt+oj6O+MiwBdIRsrrwO/f7uaKePjH95Qd/flAueoF5AvpzjQqjTvn4ZzbDpYisBSBpQgsRWApAksRWIrAUgSWIrAUgaUILEVgKQJLEViKwFIEliKwFIGliFeF+GIpAksRr9Iwf8NSxOx+TX8UUApXd3moLqIgR/L0s4FKU906mtsuNzl+AhIPl/gkkvlc13BiMEL1ZwIspPlp7VIhB2nyI/LZoOnKsBJbkvl4ttYr0vQWp4evzF2ZYS5ITnWZI36om9wNBJzV2GAMqmvzmo3KGkKyR9coadgeBbFU2Eppimy8sh0BX0kAjIpCyGmtnLfst7iVGMeQjdt4traXmDWeIZ7hDEOpXa32uGMkN9dUcNhE5JkYmQHZPyF7yS3CvHTTvT/mfgIpyzg6C3EDWmRml908q1PpSG6qOd7Go9aEKdMvlTLluA5i6mb/lOjuBnFEpDf8M6EJVSC4SW9yycrMkMyxKZUL6gt/7uDXN/XVPwVVRC+0nUalkTaf5kuFNkDA3cb/LPE6TPloAAA=" + } + }, + { + "ID": "080c1c5761801d4b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4baa3c3f5bc4933658d5bed1f392cb1d/16185515142919261897;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:06 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:06 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnyy203:4339,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/91,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Zq08W-GbFovSqgWbubnQCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/91,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/91" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dX08bORB/51OsojzkpHSV/4RIeaABChVQLpRWd21VObsm2evG3vN6KRziu9947Q2bxElIA5RWIyQSe+yZsT07M/4NJLdbToEKwUWh49xuOaYRQ+sTtNIu6PT5mAQMOgvDkA9IWCjrfkFJzNN+xuUBT5ifUcY0jsmQKtIpl86lonWct3zg+EnoUfkq4kK+2m7VOhfn7geP997+dVZtXffab7y/4wZrkuT1xWmNDDJ+Ph0kwyN2yRXHT6fv3n89eHdxuvel7PRTHTqOx33anRAco0B3fZEOyxR2UqldloShk+7LmeCSd/VudJzPBS/kie+OaBhcu/tqwF5K+lz4zJQ6ashEI9VJxDAZUyYVARSb7VpPTzUbfiSRsPaxO+R8GFI3rxJ0jzlz9689GsmAs7h4lgzCwHMFjXkiPHpqTq10P8T9h1yRTq2x/ceT8W5WVvCOqbiiwh1TSXwiiQtb9V4Q7xsV5xFhDEhXJAyARIFyzPm3JCrNjzELaWwsLCTjgU+KYSoIyMXaQmm1nY2l0WvqJZJ+DOToHBjFi2Q129Xnk9Vqbb6L2fYt3rz6mkJSnfv034TG8pgPhwEbGsZx0bTvZc0fY7W0hIFZeGXdA12mk6F5XPgwRaRvVutQba/7vDyBDo3HPJtpHYjnga8utiuVB+hRqz2tjUyM9AlM44CL70T4eQ3uu6xKzM/4URd6zD0SWmXMUlIBrQd6TcHjG/dQ9QCHvmoMqTxikgpGwtIUQZ9ffbu5AWcbx53WhGOcMLDoy5B60n1DwbFAhPBPqBxxfzc1Mi6q1faOG7Ar/o2WLtg3xr8z5zyNVzYmezSkQyJh/6e5HI2jMOOydEyq4v2zo5rgh9hwIkHPyXiZlp61035YCE53Sm2IsVZIUq5vilUXjjUszfYb3tVV9rOA9x6JIioOwXgOiCe5uCkqs4G1G7qizAmem6SVaK96lJeur7Z0ffVV7moB7w9E/JdfnXU9M4O0yHpz1SPzlMvJPzQ5HruJH8j8euatYnaIWc1Gtvc0Z9MT1I+XL2Z2iJa4vSpR+gkn0+PgJK9XnM38IOOdH5icP+fpQIxcvpjpAVkO+/IWonkYh2zn3dyeSodVxIhSajEdVK+nscoeXhYoE6mbpf5dI1EwiXzF7M052FHg0aJ5PSOCQHBXOUTNHRHmh9ToOYmRJlVoNes2Z8+odEXk1d1AxSljpf3IO0/fZSHcWKArEga0I7YbRXDHI+o2V1o42OyRzUiXS+2N1HUgBOOBiBHelGYJJm5s2xKvR+Fsf7C0nSQyCOE98xIh4L7u7g5iCbFPQgBk8SUX44NEJoIWZ9quzyc9pQVzsquqNSg/l3SrlawnXZnJcinVqm2Hpc4hXJVLUGNFxXyjnzBGBmEq4IiZzlJ+hHnom7bs8sHswTzUCix8W7Zcxco3t/xJZw/c1ACya63+ERtREUBmaqin/IJBOmgRW7feiB9JrE1e9cHyFp2O9VCsFyW7ZZ1wQfdTVIKDa2uaE5nqNZZUadowiV/clHp6G3Ju917HGZqxENtT+wyGaUupfmHDTC9ms7b4fiQo8c84DzPbU4w+cgHX5JKFaDxcY12uRc1Sa7mAbavWmOKaXiL14Nw8k+820rSK5bZKpRQmJnrwLLkHJAllxzkajxOpdkiD1/QyYIGK7bcDEufA9LIDIZ8OIW3rXpzv97/u9/vv+qozgVEKHS87E3y+i+A8gvMIziM4j+A8gvMIziM4j+A8gvMIziM4j+A8gvMIziM4j+A8gvMIzr9ADBTBeQTnX6Rh/obgfNnROHVX/z192YF1U+ZTH/KDyEDqIymjQ5gHaUT39k63FfCRxN3sr+3LTmDCuwbau/rlNsOt9cQ8Tp8C8QuQcTcP+IPGcOqvb14r4IH5XSkSamD+E4PeI9KPSD8i/Yj0I9KPSD8i/Yj0I9KPSD8i/Yj0I9KPSD8i/Yj0I9KPSD8i/S8QUEWkH5H+F2mYvyXSfw9Z96aw8TxFA9aLgPmycxmEENzVLVrD6RrMD7nOAUxzPEVUmz6m/u6kFPAJkpHymiD8l7uFUjb6WJ+yoz86KFfJgKwh3Z9GpXHnbMS842BlAisTWJnAygRWJrAygZUJrExgZQIrE1iZwMoEViawMoGVCaxMYGUCKxNYmXiBADBWJrAy8SIN8zesTMzv1+ynBKWIdY+H6iIKciRPPzaoNNOto7ntcpPjJyDxcIlPIpnPdQ0nBiNUfybAQlqc1q4UcpgmPyKfDZquDCuxJZmbs7VekWa3OD18Ze7KDHNBcqbLHPGPusm9QMBZTQzGoLo2r9morCEke3SNkobtcRBLha2UZsjGK9sR8AcJgFFRCDmtlXPbfot7EOMYsnEbz9bOCrPGM8QznGMotavVHneC5OaaCg6bijxTIzMg+ydkL7lFmJdeuvcn3E8gZZlEZyFuQIvM7LKbZ3UmHclNNcfb2GhNmDL9UilTjuswpm72n4ruXhBHRHqjPxOaUAWCm/Qml6zMDckcm1K5oL4R6A5+fVHfDVRQdfRCx2lUGmnz0b54aAtk3G39D0RRFD0gaQAA" + } + }, + { + "ID": "0802eb5da40de522", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=asia-northeast1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "b758b41769448c84c9fd298eed5a36de/14577454806423327730;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=asia-northeast1" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:07 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/SPFA4n4RfuvoZQ5BSaKrmJGDd8Q\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:07 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlk1:4163,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/147,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Zq08W4nEIIefqgW1kLW4Dw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/147,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/147" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAIVSW0/CMBR+369Y+gym3WADH1Uk+IBmookaQ0p30Opol7WLEsJ/t7ehhhif1vNdTs/5ul0Uoze5KmANDQgG6DTeRXGM6ka+AdOz0gCobCsGul/LRvfzLEE9qzAuz94zeX71cEOyz/PRlD2qgRjS9uxuntCVV1aSUc2lsGKqOO0L0+gVqNIERfHeaBCTYs1f2qbT7byPluEcI8VeYUO7MkZrDlWpTP3k6gAbQtCN3cJ/ex2qt7VDbxfFbD5FHt73/vK2G3Xsnc0Xk+mk6Mzu8xwd+qASlObCrbCgqwq+h/03TeummioIGi3ft7JjtG0WcHtcJpiMcI4HywHOxsl4RIaDPMnJEmOSu+nsbPsQrdJUt6rL1FZumYvr+QT9kHAzOzvIWAN+D+4DIcPUXJgmeEgw9q9qTI0+4jOMM8+DKI/YnKSBZXJTV2CvKOxFRkVOcO/o1bmoW33JK1Cuj0/kGz3b6sCEGJFsteEK+eHg9Bd6UKcdvqJlAUw27kdCGIXYon30BUoZZ/cYAwAA" + } + }, + { + "ID": "32268dd8fdccca79", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=status%2Cstatistics\u0026location=asia-northeast1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c934d50de911fb1159fa7ce9fe343d2a/2978019414748854831;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab?alt=json\u0026fields=status%2Cstatistics\u0026location=asia-northeast1" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:08 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/SPFA4n4RfuvoZQ5BSaKrmJGDd8Q\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:08 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnma19:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/65,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Z608W9CRKZHHqAX2ypcY" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/65,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/65" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAGWQTQqDMBBG9zlFyFrKxKBCl6XtsgXpBWySRUCNJCOliHdvjKm0uJz3vvlhJkKZxwZHz450InStdCjY+X67MELnLEWMRyO3mHQ6INs/TBfTvBBQgcih4AAsS5Mc7nwJUK5e92pnKy6SlbYbWr2sqJdFIcUPEE1rG5XOoMz0w4hX02of58TeH3p6YzJ5UnbE4Gr7ilj80S0tvvzZqFpL61TEEP5B5/ATMpMPRg2f2joBAAA=" + } + }, + { + "ID": "fa733e4cc7bda7cf", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab/cancel?alt=json\u0026fields=\u0026location=asia-northeast1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "0" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "56d00e0bd9021587e9c77c43a0b4a9ea/11433388428984900675;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/VcoCJYP16xC8GcZs4n5auBUN2ab/cancel?alt=json\u0026fields=\u0026location=asia-northeast1" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:09 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Stk4rDT7za94uOE0ynSm0E8fPMs\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703508000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbh141:4349,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=aK08W6jUJc_mqgWKjIewAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/185" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKuu5QIABrCh3QMAAAA=" + } + }, + { + "ID": "c570154ad85df726", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "230" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d0d91303e67a7dc5873ec2b6e58eed8c/9825328096783868268;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICogRlJPTSB0b2t5by50YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE3IiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJsRGk3UlJJc3I4OHRvTG1oQXhERkFvdnIzRFMiLCJsb2NhdGlvbiI6ImFzaWEtbm9ydGhlYXN0MSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:11 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/9F_prBgzcIw2_fHQxQMC6cw7Fmk\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnkj10:4176,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=aa08W5jxK9D5qQWHgJDABA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/70" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bMBR9z69A7G1aApjvStOWBdIhpZlK022aKiFjDHFDMMEmaVrlv884pK3Sadvj1T3nnnPutZ8GiroiVaZeKGpKik2Lm/27e5qqH0QDc1h0jTvVBSs3eMR7HJYWW5bLy7z6FSS3u5+6pfnTpG6+FI8o2oEk/3r9cH01cdDOna5Xd6qcQ+T4rC0R5sOaNnzoOuACMgKHlaiWGDJujMqAuHEcscbzOJ2tl+OHYDqm28YMbuQUhst8RqpVN2vJec0uNG23240KSosSw5qwEaJr7ZRC2wKtbug9RpxpZ9qaSMi0vwh+KimCnNDq45lL6USwY5zjBlcICzdPA0VRe6noT1E7kmQdu/8IKgC9eAc+1x8oh84ColVOirY54aQHmbsvXir1JpyFk4XyXpnG364UTld7OuIwLXECdMPTXd1KLN3xge8ZtuUC10h03XClFREFM04qKbPoOKfx/5G4Y0MOGe4xSQqBk+WZb4v5ngGBh9IstV2UG7aRZjB1LZDZyHpmS5NHLqxolTmpmaU+shzkAWABaDummwOYZY6DgJ7bCOpp5nU7UuSWxAjUYMhxQFhNGTntdBKH40WYRNNkHoZBGPRRdw15A/0RRwK5iG/nE0HpgXVDqMDK5UbzRRiPJ4vo+6nbMjzDBUT7m00pEDksGRaNQ385xiFv2elkXdXtVBUK82h+qb5CEbF59IyUSborkLUkGLYpTmcCQ3cs7/huBKnhb/qGrh+PefY+pPQaV3yxr/HLO1FfeRVRmgSvIemCqKbjmL5vmoZlD1PD47pB19yHwPVXyCxQQTbLzar0Upou288Z3uKS1rgZFWLIliAMEaJtxbtPqg4Og99fFSdAegQAAA==" + } + }, + { + "ID": "ccb913f10b4ad13b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/lDi7RRIsr88toLmhAxDFAovr3DS?alt=json\u0026location=asia-northeast1\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "89a690e536a6ee083f7c6691172a6130/8217266660793083541;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/lDi7RRIsr88toLmhAxDFAovr3DS?alt=json\u0026location=asia-northeast1\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:12 GMT" + ], + "Etag": [ + "fy5Ygia/egMfQq4xAoMyrQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4013,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/117.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/117.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/117" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41Rz0/CMBS+769Y6hVidCrEhMMQxCWDyMCDMR5K9zaK3TraN2Uh/O+2ZYoHYzy9fj/6vdfXvef75I2XKbn1yYrn2xpUc5YDzu0hAV0L1KZUstRAOtYNSHPrzprr55zTc8in2Xx7tQvltFHzweDo0mwNBTW+vUEGZxxEqg1+cdhveaeVtACb6GrnxGNTOX6xTKLZ5KdSyNQps6c4DofxmLTSofNHel3o39Kj2XI8GSf/inf11WsbkY1cJZCBgpLB6amVkhtgGLmdprVggN1KKuz2bi7bLvbmURcj3kuSSKt+H2VcrMPd6D6U7yoYLb68QjKKXJbWTjWn3dKErYFqvCDfo6BEKhL5YVdMAnLihg2CflSSgdbgWgatbGa4k0UlAO3wqGpwNKPm6x44Gi6jQoN38D4B4tb5XyYCAAA=" + } + }, + { + "ID": "7e7e1804e7af80be", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_ba26dfd9500181a28cbdb57cf151bdab742d5c42/tables/anond6b3db9c46c82242a5637f2add66c20f5ca0bd81/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "89a690e536a6ee083f7c6691172a6130/16672355303842236585;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_ba26dfd9500181a28cbdb57cf151bdab742d5c42/tables/anond6b3db9c46c82242a5637f2add66c20f5ca0bd81/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:14 GMT" + ], + "Etag": [ + "GbzI05Nef1oK1Jql0LiovA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "127.0.0.1:4296,/bns/xa/borg/xa/bns/cloud-dataengine-minicluster/prod.helix-tabledata.server/2.esf,ybdk16-v6:9870,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/9,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"xb\" allowed_cluster: \"xa\" allowed_cluster: \"xc\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:3709::]:4135 had response of OK with response time of 0.028278350830078125 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybdk16-v6:9870,/bns/xa/borg/xa/bns/cloud-dataengine-minicluster/prod.helix-tabledata.server/2.esf,ybdk16-v6:9870,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/xa/borg/xa/bns/cloud-dataengine-minicluster/prod.helix-tabledata.server/2" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXFPqvI0MPVLTTPM9zb0Kswx8MnML3O0tYWoKskvScwJyi8vBik1hogVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVodPOoMkNRBWbFcSLqItjCJSAsNqWVhMpEWGuG0kAvEquUCAIgikjDEAQAA" + } + }, + { + "ID": "fde8442c6b56abe7", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "377" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c358978e33640bf208040ede70f35076/6681261719345375934;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7ImNvcHkiOnsiZGVzdGluYXRpb25UYWJsZSI6eyJkYXRhc2V0SWQiOiJ0b2t5byIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE4In0sInNvdXJjZVRhYmxlcyI6W3siZGF0YXNldElkIjoidG9reW8iLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNyJ9XX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJqUmdGZ0k0UFFNdVg0THVubmhRWk5qTFhEcGgiLCJsb2NhdGlvbiI6ImFzaWEtbm9ydGhlYXN0MSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:14 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/uwsofIuVFG54XgEOzpMJsszFRb4\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlf1:4425,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/151,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=bq08W-jyBsPZqgWa6peIAg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/151,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/151" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAJWTXW/aMBSG7/kVkXs7yJchCdK0XVAQE2UrWivUdUKOOSQmIQ6xDYKK/77YkGpiX91NZPt9z3ueYysvLQtlrFiivoVilmwVVIebNY/Ru1oASRItPKPAy4LBEQ5wm2OR5uloVTwNFg/7uYNttRd8NVaPw1EXz5Pbz8fy7pMQx+Esxs/I5DATv1Q5BdkueSXbQc/rE8FIu6h3KRAh3c56lgyTMf5yf6fmeKKKIr1/mq4n80GZmhQB+WrCikxnpVKWom/b+/2+k3Ce5EBKJjqUb+xmCnvn2WXF10ClsK962/WEwv5Lww85p0QyXry/ojQkdfUMVlBBQaGmeWlZFrq0Gv9uVF1kqs7qPwa1UNNcm6/7t6yTRqC8WLFEVY3PMFBeHi7r+rq4qih8JXEOoj78pg/P0ltotWlJJBFwMUmeHfirJHXsRdDLhee4oRM4eIGdXuRFodvFgRe4C8dxA2SKTvr73QSgJQjJCsNuABvoN4H9meu/sEKDpalOlzsVkkglmsvUOw2GZg/T6Xg6Qj+5WI1PX520gvMobGMK3K5f9/Q9F/t+eH7RuqiSv+q9MGhilYBqARvCcm3xez0/inzfxd127IbScflGRsQLooz6CU3YNt1meRjzOFUfl7CDnJdQdZI6ZMcoEEq5KqT+H1Dr1PoBwIpXqOUDAAA=" + } + }, + { + "ID": "142121d7bd22501d", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "375" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c9208250dd920737ddf13551d497f8c5/5073201382849507302;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7ImV4dHJhY3QiOnsiZGVzdGluYXRpb25Gb3JtYXQiOiJDU1YiLCJkZXN0aW5hdGlvblVyaXMiOlsiZ3M6Ly9kdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE3LmNzdiJdLCJzb3VyY2VUYWJsZSI6eyJkYXRhc2V0SWQiOiJ0b2t5byIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE3In19fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiQ29BV0gyVVVVbmhIeVFiZkk1N3YyQzJLMDJVIiwibG9jYXRpb24iOiJhc2lhLW5vcnRoZWFzdDEiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:15 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/4L_aBo67QeTRBrKq-MaI8CUqEzI\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlu9:4362,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/221,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=bq08W-niNYH0qQXu1ZKYBw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/221,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/221" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAK2TX2+bMBTF3/MpkPc6wt8EiDRta9q1UbtIzcI2bZ2QcW7AheBgm2Rple8+m4RpSqt1D3vD3HN+91xfeOwZqKDVAo0MlNKsboDvXt2zFL1WBZA404U7FLhFcP4AO7gofZGX+eWy+naexNuvtm/5Nwk+Y8PgFuazM35dmx/xJBzH9cXD5A61HNriF01JQJprxqUZDN0RFhSblTrlgIV0+mP2/suVG8dxlV/tbtPlZBBs3LF7bbtxSxFQLm9oVWhWLuVajCxru932M8ayEvCaij5hK6ubwtq41pqzeyBSWCe9LTWhsP7S8G3JCJaUVW9OUrZJlHsGS+BQEVBpHnuGgY6tJs+Nqk2t61B9YVADdc21+LR/z9jrCIRVS5o1vNO1GeCn5JjI41HdGGs4gTlOyy7mvwRVmgWWWMBRI1mxY11FatjxvX5MXNsJ7cD2E98eRm4UOgM/cAMnsW0nQNqzb51oAULSqo0bc6r9mV7g6WLS2pRKab4M7xOxQc+xhYJ/P6T9Hy006ceTPh8YX2HZbvPTZ63ZHxcjJJaN6DaiT/ru0SyeTifTS/SHiioY+a0kHFrwnK5agzPwVCzPdQZ2GBw+C2Xi8ml9YAcdthHAE1hhWmqJNxx6UeR5jj8wUyeUtsNWMsJuEBXEy0hG67wuyjBlad68W8AGSrYG3s8UZEMJYEJYU0n9U6HevvcLJBZZCioEAAA=" + } + }, + { + "ID": "9016c415ccff5c6c", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/dulcet-port-762/o/bq-test-table_20180704_40692981547271_0017.csv?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "197d70b9a42128e8603ff7807dab9b79/13528288926386967291;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/dulcet-port-762/o/bq-test-table_20180704_40692981547271_0017.csv?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12549" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:15 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:15 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703515000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnz20:4077,/bns/yk/borg/yk/bns/blobstore2/bitpusher/653.scotty,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=b608W_eRKdeSqgW2x7_AAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/blobstore2/bitpusher/653.scotty,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/blobstore2/bitpusher/653:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjdnVUJQazNwQmRIS185bmdmYnJxc18zZ2tXS0U0ZHRUVTdsSXhHVG9KUTFNaW1KNGY5bkFIb3Q4UGVBX2w5YTdpM3NkR1RJRG1HelFLWW5NVks3cE1JSy1hR21CVG1GaDZfT0d1dGlwMkJnYmhDaGpfZEFPaE1LQUNpZ3pWNC1MWExtRklYMlFMZjRvNVhTUjlfQlhhRDZJTThCNG9Kb1BDanpCWUxnU1A4aEVoend2aXNLRURlWTRFSm8wBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpB3dh5hGAGc08J-Fr3yGP_p7uZRaTCDXx3vPWO-0z6ZD7GIZW6FOOMEiN_YRQHADtCdhY6jy7slFsXyOpHpqNDH6G7J4DH_5NGmcsjrd_zVvNyeuo" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTcuY3N2IiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6Ok9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBkdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE3LmNzdlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YTo4NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6MjcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmRlbGV0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTEzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTcuY3N2XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1OT1RfRk9VTkQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTcuY3N2XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNy5jc3Zcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPW5vdEZvdW5kLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5OT1RfRk9VTkQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNy5jc3Zcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBkdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE3LmNzdlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLm5hbWUsIG1lc3NhZ2U9Tm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTcuY3N2LCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLm5hbWUsIG1lc3NhZ2U9Tm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTcuY3N2LCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNy5jc3Y6IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNy5jc3Zcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBkdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE3LmNzdlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwNCwKICAibWVzc2FnZSI6ICJObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxNy5jc3YiCiB9Cn0K" + } + }, + { + "ID": "6acabe5be036c7bb", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables/table_20180704_40692981547271_0017?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "70ac4b61e4e158204a1c1d375ee92799/11920228589907809828;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables/table_20180704_40692981547271_0017?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:16 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnzz206:4433,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=b608W8jbLYreqgXKwp3ICQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/63" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "11d8e17a4b69274d", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "72" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "25c4d9c787617c993efa7964c7be9952/10312167158211992396;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkYXRhc2V0UmVmZXJlbmNlIjp7ImRhdGFzZXRJZCI6InRva3lvIn0sImxvY2F0aW9uIjoiYXNpYS1ub3J0aGVhc3QxIn0K" + }, + "Response": { + "StatusCode": 409, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:17 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:17 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnm125:4095,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/24,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=cK08W9KILIaGrAW35L-ICg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/24,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/24" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1cbW/iOBD+3l8RIT5wEhvx3gWJDyywV6T2rgftvWh3tTKJCzmMnXOcvXJV//uNY4cmwbTbdt+orEqAx5NnPPbEM35ouDlySphzxks95+bI0Y0IWu+glYhA6LM1CigISwvC5oiUqkrOMYpYIvfjkAQeEjjtWuMoQgss+wYE9PyNM74OIhH1nBESKMLC8WPiYfEqZFy8Ou40eoKtNiy93sfzeDGhV0wivBucTseD0V8fx39OZhezD1VnmljuOR7zcT/f62jTfRoT4iQ46mPi2jlngvWVQz3nfckjLPbdJSbBtTuWCqOk633pPZXYUiUPL3sQX8RrTIXs1d4UxUbnpBL8CSRg4Gt3wdiCYDc7BBCvGXXH1x4ORcBoVD6P5zCzLlKTqOawctfv/o0+oV6z9vqnrwNc/1rAjfoDwBHmnzB3OYs27omU6JmeSkFAoVtUduQKu1VLsaOYuhxfEewJ92dMMYcQ9c+wWDJ/4HkQKIzXW/Ua4H1iK1y5pCvK/qXOjMXcwyaQESZ4gURAF3mUyTokKcq9OskIW80UWzZdguhia0Fdk2Lplrqq+5mLkcyZnI8p/ifGkYCgv96U666HCKkU5Rq7Xnsa9giFIeYnjK3eIk8wvilfcOSB77pf9uwY3rlIDeJ14zn+Ne71r9l8GvbviP+X9c7oT0FJh2G79R3dyd4+GYxB7Aci689uVBRVtDfPir2vszZDjv3ofmeKKsri8UObz3dYmSGjAl8/sDa7Sspq5/jHW51TtrjfmbyCstb+AcNMYegN2YzdPu5ksWXGCJPecqLUbKYZy5xh9ownlMWKem2gMMilwXK2MYOQCjxc1u/niKM1FphH5a67RNQn+A1h3upuV85lTuVCvVPrdn+0AZnCmoICDz2YUplP9d00Db0UcRCqWhSKjZPEFNcmU1P36ya2G6Yw3Gd4lnyawJ3JKSL6FnV5TKFvQjMmKnuVdRA9xl0FNATXKCZwd0FKJZtKsUMn1mPTyn4RZPPOo26kWAQEPlMv5hzqYncwjwQUBwIqBBpdMb5+G4uY43Kh7fpsK6nsuUYvU8tYtXwr683nW5dhcr+Vet00w0IVWa4strCOonK2MY0pRXOSGJhQLaxkNfSu2G4/Bx7CQ3pgwO2Yijkjbsb9rXAI+/gceSs1/AldYh5A6a57f2GXFOplg9lmw7TJfyGzJnv1z7a3b3WMi2Lcic2RdcY4Hl9jL4YsGpXbekVyUh1JtXbn5YXSUE1DZtu9G2OhT0eI6a79BoFpqjkPODCTk2sxFi+W8tB/zhhJY08C/cH4CvOKoVPvcK3HopYVpBrlHthOo5VDTU7ZSjlznT4QtJKETzNTJesbnRM9uJfctygmoudM1utYyBlSbBG+Cmggc/vNHCqX/ujy/HQyHFyMq45kwxZQ1/YvZ+Ppx/F0+utUCuNIMVNVZ0tx9S2/Zfkty29ZfsvyW5bfsvyW5bcsv2X5LctvWX7L8luHSkpYfsvyW5bfOujAfIH8VtVRxFBf/RtX1QG/MfWxD8VJqFmppRDhCVwHtUz/5la1Z2Ajjvow4itI9aLqBDq9K5qqr95uUn5IXZiluhIaaw8V5eb5J1CGYcPSv9m8gXiA4fUFj7Gmy84092UZM8uYWcbMMmaWMbOMmWXMLGNmGTPLmFnGzDJmljE7VJrDMmaWMbOM2UEH5otkzO44ouEus5TtVjTRPpar6lwFBDK85BruyKiqA1VHUgjo5jrXKWd+jf3Blld7p8uhqvnhyQ+3ewEf9RBm1VFPdva3j3WCJPSSCWjVurfOo9B6jmXrLFtn2TrL1lm2zrJ1lq2zbJ1l6yxbZ9k6y9ZZtu5QKRbL1lm2zrJ1Bx2YL5Ct252v4kOcCUU0ZESe1MGOYMlTnZWCWGVz0+kvg8ehFHGRj0KRPQxoJAoaUp4aMHTtr/sfNKJLnWy5nK9+jFX482GNZ8jiFCeLL8NdhmEmSRZEeomfuk2OAg5rtQ0YnHzApl2zVXuEkfTW1YPUsKdBJCT5VCl061258YjNvoAAWiGBwtqI/Np8zP0sYKjCjZid7gNhbdfQruEOoFBbrdpxZVZZyaNgpikPgrnMk9PUk9z4DtVLxgn9Nkzm/oz5MZQs2+zM+QZGkYZdevytF8qRzKV6eVvP8smWTAdVMmVQFxF20/+Cd0dBFCLhLX+LcYzltwS6vMkUKzsq6cYmh1ySv3l6Cy8f5K+fluQXV6We06p1k+aTf0r1CDBvj/4HT1mZ0ONVAAA=" + } + }, + { + "ID": "e5cb25ded5dd4dea", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6afe28b9b73a74be2026ef88f31b2065/8776162216747507829;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:19 GMT" + ], + "Etag": [ + "U3SmvBJDc9rDcwXnyO1vlg==" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:19 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnls3:4071,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ca08W5ncMoW5qgWBiYYg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/149" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2R3UvDMBTF3/tXlPjq1rXdhx0IKhsyUQd1MkF8SNO7NjZtuiRtGeL/bhLmhOHX6z3n/HJu7pvjooJWKZq6KKHZtgGxO0mxwhIUOtUiKJwZ8TF8KNurmxmJxIx0T9Vu6bcsOz+3JmrzacMIqF7NhepNxsFU8WLHrS6BbW5pVRhXrlQtp57XdV0/4zxjgGsq+4SX3mcBrw28WvBXIEp6R1RvX056X/j9KIYNCKgI6GfeHPcwX9h2B7uL9uzFd62R474bJiYEpNSGZ50wNBcJzgwarePFah5blF6tBkIxuxa8qY24Z68FVSCkplncEWG5vv8LsOyq/+UbCeJqNy8xZUYKx+MwisLQH456iX+mBj4vVYSDSVSQMCMZ3ebbgp0lPMmbixRaYLwG0c80paUE9Nq8qZQ5xw9vx/PL2V/lY8DpZ3vHfTH/SQRgRXm1oqXF+KPA94fBYDiajCN7RYaluuMp3VBIf3FxYjlGxZLiXqUPl4PO+sh5dz4AU9vuetACAAA=" + } + }, + { + "ID": "24a7c82430e7c46d", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "238" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3ac64755bb65ed7babf767f25bffbf86/7168101880251573662;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibmFtZSIsInR5cGUiOiJTVFJJTkcifSx7Im5hbWUiOiJudW1zIiwidHlwZSI6IklOVEVHRVIifV19LCJ0YWJsZVJlZmVyZW5jZSI6eyJkYXRhc2V0SWQiOiJ0b2t5byIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE5In19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:20 GMT" + ], + "Etag": [ + "BNlCVl9DM2lTlXVgMZyleQ==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlw9:4453,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=c608W5rLDouYqgWVibugAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/197" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1SXWvCMBR9768o2auatDq1gg9zExFUWFdkbAyJ7bVmxqZr4lwR//vSRGGIY3tpybnn66Y9OC7asCxBPRctWfqxg6K8UXTJAdX0CBRNq9Fgxu/nPHiY+jziz/N0+lJyeOz3DYkZdbLjMah6LgpV77T9nhKbUjSM1cInXpd0SGvRIu3AD7rebavjd7wFIV5gLCTw1YRlm8porVQuexjv9/tGKkTKgeZMNmKxxeeG+NPHeSHeIVYSXwTjhCoqQQ9MA2waSPzPIoYWwgoKyGLQdQ6O66JT1vjaopVMgzbUMkywxY3fCf27geMezW3Ea9jSc/iKAU+kPr3qk4E0mNFt1c6+axZTZW6wpygcz0aoAo+1a5rdVl5qxrNoOBqGVqQfb6cqmjsoFVTxiKATMhFZGkFxZRKK/Q8gLoAqJrKI2WDvtqlXb/o+aZKu/b++clZc47SDJiHWhVOppiJh+h6S353Oi0R3g8nQ6kRsnCuUSkbrmf5ia9BuHnKOzjef/D0H+gIAAA==" + } + }, + { + "ID": "339025f60c8b56fa", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/bigquery/v2/projects/dulcet-port-762/jobs?alt=json\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=2fe606859d638629e6ed6bc71614f576cfa04a6ab83e1a0f1a17310291aa" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "667bf49d769473a22b04e88ddeb01205/5560040444277565895;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/bigquery/v2/projects/dulcet-port-762/jobs?alt=json\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0yZmU2MDY4NTlkNjM4NjI5ZTZlZDZiYzcxNjE0ZjU3NmNmYTA0YTZhYjgzZTFhMGYxYTE3MzEwMjkxYWENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImNvbmZpZ3VyYXRpb24iOnsibG9hZCI6eyJkZXN0aW5hdGlvblRhYmxlIjp7ImRhdGFzZXRJZCI6InRva3lvIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIiwidGFibGVJZCI6InRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTkifX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJ2Znl3UGJibVQ5ZnNDSzY1OTlIdTBvNjdWR1giLCJsb2NhdGlvbiI6ImFzaWEtbm9ydGhlYXN0MSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQoNCi0tMmZlNjA2ODU5ZDYzODYyOWU2ZWQ2YmM3MTYxNGY1NzZjZmEwNGE2YWI4M2UxYTBmMWExNzMxMDI5MWFhDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KYSwwCmIsMQpjLDIKDQotLTJmZTYwNjg1OWQ2Mzg2MjllNmVkNmJjNzE2MTRmNTc2Y2ZhMDRhNmFiODNlMWEwZjFhMTczMTAyOTFhYS0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "1004" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:24 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/jYhEWaqNNdUneBZkvM0VgLlG0kU\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703502000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vxnx14:4098,/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/15,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=dK08W-bBHdDMlwG3nhQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/15,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apps-upload" + ], + "X-Google-Netmon-Label": [ + "/bns/yb/borg/yb/bns/apps-upload/apps-upload.uploader/15" + ], + "X-Google-Service": [ + "apps-upload" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrShg6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoYN9HjzuFDwEXLsUAllsH76PDfdMImWOfbhQSVpUQe8cBe8rNyDV5zk0Ah6gBZLax2WletmJNlIAexZbDelgFNcCZB_Q" + ] + }, + "Body": "ewogImtpbmQiOiAiYmlncXVlcnkjam9iIiwKICJldGFnIjogIlwiNzJrN0R6ZXllRWw0c2hsaEdmblpEX1V3WDA0L2pZaEVXYXFOTmRVbmVCWmt2TTBWZ0xsRzBrVVwiIiwKICJpZCI6ICJkdWxjZXQtcG9ydC03NjI6YXNpYS1ub3J0aGVhc3QxLnZmeXdQYmJtVDlmc0NLNjU5OUh1MG82N1ZHWCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYmlncXVlcnkvdjIvcHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2pvYnMvdmZ5d1BiYm1UOWZzQ0s2NTk5SHUwbzY3VkdYP2xvY2F0aW9uPWFzaWEtbm9ydGhlYXN0MSIsCiAiam9iUmVmZXJlbmNlIjogewogICJwcm9qZWN0SWQiOiAiZHVsY2V0LXBvcnQtNzYyIiwKICAiam9iSWQiOiAidmZ5d1BiYm1UOWZzQ0s2NTk5SHUwbzY3VkdYIiwKICAibG9jYXRpb24iOiAiYXNpYS1ub3J0aGVhc3QxIgogfSwKICJjb25maWd1cmF0aW9uIjogewogICJsb2FkIjogewogICAic2NoZW1hIjogewogICAgImZpZWxkcyI6IFsKICAgICB7CiAgICAgICJuYW1lIjogIm5hbWUiLAogICAgICAidHlwZSI6ICJTVFJJTkciCiAgICAgfSwKICAgICB7CiAgICAgICJuYW1lIjogIm51bXMiLAogICAgICAidHlwZSI6ICJJTlRFR0VSIgogICAgIH0KICAgIF0KICAgfSwKICAgImRlc3RpbmF0aW9uVGFibGUiOiB7CiAgICAicHJvamVjdElkIjogImR1bGNldC1wb3J0LTc2MiIsCiAgICAiZGF0YXNldElkIjogInRva3lvIiwKICAgICJ0YWJsZUlkIjogInRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTkiCiAgIH0KICB9CiB9LAogInN0YXR1cyI6IHsKICAic3RhdGUiOiAiUlVOTklORyIKIH0sCiAic3RhdGlzdGljcyI6IHsKICAiY3JlYXRpb25UaW1lIjogIjE1MzA3MDMyMjMxMjIiLAogICJzdGFydFRpbWUiOiAiMTUzMDcwMzIyMzg4OCIKIH0sCiAidXNlcl9lbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKfQo=" + } + }, + { + "ID": "0017906f19152e36", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=asia-northeast1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "57e0ed5cddad6410460efdab5a37edc0/4024036602307997423;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=asia-northeast1" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:25 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/D2pnfKzvBqB-K5yNHIXYcQIfkhQ\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:25 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnv124:4415,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=eK08W8C1A4HdqAWszJ3oDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/95" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAIVSXUvDMBR9768Ied4kybZ++CpjDqFIrSKIjDRNXWbblCZVxuh/N2na+jDEp+See87JvYdcPABPMkt4wVteMw5vwcUDADatPHGm97kBYN6VjOtlI1u9DHwCF5ZhVK77VZy/H7OsSqNC3T34myi675D0g5fdq2OWklEtZG3JVAm6rI3RkVOlMfRAbziQyboQH1078S5OR/PxDqBiR17RqQSwELzMlanfhnqETaOmld3CnYsJ1edmQJ/SZB/voIP7xV/arlLX2n2cbnfbZBIPx7s3+8CcKy3qYYWUZiX/HfbfNK2aaqr4yNHy8yynjrZmI26vB4JwiAK0PqyRH5EoxJt1QAJ8QAhHw3R2tn6MVmmqOzVlaqthmeQ5jl0SM0uY8dnMZC13qwiXCd6szJsrQlaYjF/AiFp93Q/D0PWZrJqSW5PEWhkWvkHmQa/3fgD3A3rDeAIAAA==" + } + }, + { + "ID": "f8eb1aa8b1201cf4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=US", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "85d0dc8b849c099c596ade5763a01b11/2415975166317212440;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics\u0026location=US" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:25 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:25 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnda124:4445,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/192,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ea08W9HuCof2qQXu4J7QCA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/192,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/192" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dbU8bORD+zq9YRfmQk9JV3oFI+UADHNwB5QK0J7VV5eyaZK8be8/rpXCI/37jtTdsEuetAUqrERKJPfbM2J6dGT8Dyf2WU6BCcFFoO/dbjmnE0PoIrbQLOn0+IgGDzsIg5H0SFsq6X1AS87SfcXnIE+ZnlBGNYzKginTGpXOtaG3nD953/CT0qHwTcSHfbLdq7asL9+b67tt5vz+63L2Ou3+2mru7R0mFt7bf//53xs+n/WRwzK654vjx7N3ll8N3V2f7n8tOL9Wh7Xjcp50xwTEKdNYX6bBMYSeV2mFJGDrpvpwLLnlH70bb+VTwQp747pCGwa17oAbsp6RPhU9MqaOGjDVSnUQMkhFlUhFAsemu9fRUs+FHEglrH7kDzgchdfMqQfeIM/fg1qORDDiLi+dJPww8V9CYJ8KjZ+bUSo9D3H/IDWnXGtu/PRvvZmUJ75iKGyrcEZXEJ5K4sFWXgnhfqbiICGNAuiFhACQKlBPOvyZRaXaMWUhjY2EhGfV9UgxTQUAu1uZKq+1uLI3eUi+R9EMghxfAKJ4nq7lTfTlZrdbmu5ht3/zNq68pJNW5R/9NaCxP+GAQsIFhHBdN+1HW7DFWSwsYmIVX1j3QRToZmseFD1NE+ma5DtWddZ+XZ9Ch8ZRnM6kD8Tzw1cWdSmUFPWq157WRsZE+g2kccvGNCD+vwWOXVYnZGd/rQk+4R0KrjGlKKqC1otcUPL5zj1QPcOipxoDKYyapYCQsTRD0+dW3mxtwtnHcbY05xgkDi74OqSfd3yk4FogQ/imVQ+7vpUbGRbXaarkBu+FfaemKfWX8G3Mu0nhlY7JPQzogEvZ/ksvxKAozLgvHpCo+PjuqCX6IDcYS9JyMl2npWbs7q4XgdKfUhhhrhSTl9q5YdeFYw9J0v+FdXWY/c3jvkyii4giM55B4kou7ojIbWLuhK8qM4JlJWomdZY/ywvXVFq6vvsxdzeH9noj/8quzrmdqkBZZby57ZJ5zOfmHJsdjL/EDmV/PrFVMDzGr2cj2nudsuoL68eLFTA/REreXJUo/4GS6HJzk7ZKzmR1kvPOKyflLng7EyMWLmRyQ5bCvbyGah3HIdt7N7Yl0WEWMKKUW00H1ehqr7OFljjKRulnq3zUSBePIV8zeXIAdBR4tmtdzIggEd5VD1NwhYX5IjZ7jGGlShVazbnP2jEpXRF7dDVScMlbai7yL9F0Wwo0FuiJhQDtme1EEdzyibnOluYPNHtmMdLHU7lBdB0IwHogY4V1pmmDixrYt8XoSzvYHS9tJIoMQ3jMvEQLu6+5eP5YQ+yQEQBZfczE6TGQiaHGq7fp83FOaMye7qlqD8ktJt1rJetKVmSyWUq3adljqHMJVuQQ1VlTMN3oJY6QfpgKOmeks5UeYh75pyy5XZg/moVZg4duy5SpWvrnljzu74Kb6kF1r9Y/ZkIoAMlNDPeNXDNJBi9i69Ub8RGJt8qory5t3OtZDsV6U7JZ1ygU9SFEJDq6taU5kotdYUqVpwyR+clPq6m3Iud1HHadoxkJsT+0LGKYtpfqJDTO9mE3b4uVQUOKfcx5mtqcYfeACrsklC9F4uMa6XIuapdZyDttWrTHBNb1E6sG5eSbfbaRpFcttlUopTEz04FlyD0kSyrZzPBolUu2QBq/pdcACFdvv+yTOgellB0I+HUDa1rm6OOh9Oej13vVUZwKjFDpedsb4fAfBeQTnEZxHcB7BeQTnEZxHcB7BeQTnEZxHcB7BeQTnEZxHcB7BeQTnEZx/hRgogvMIzr9Kw/wFwfmyo3Hqjv57+rID66bMpz7kB5GB1IdSRkcwD9KIzv2DbivgI4k72V/bl53AhHcNtHf0y32GW+uJeZw+BeLnIONuHvAHjeHU3969VcAD8ztSJNTA/KcGvUekH5F+RPoR6UekH5F+RPoR6UekH5F+RPoR6UekH5F+RPoR6UekH5F+RPpfIaCKSD8i/a/SMH9JpP8Rsu5OYON5igas5wHzZec6CCG4q1u0htM1mB9ynQOY5miCqDZ9RP29cSngIyQj5TVB+M8Pc6Vs9LE+ZUd/dFCukgFZQ7o/jUrjwdmIedvBygRWJrAygZUJrExgZQIrE1iZwMoEViawMoGVCaxMYGUCKxNYmcDKBFYmsDLxCgFgrExgZeJVGuYvWJmY3a/pTwlKEesuD9VFFORInn5sUGmqW0dz2+Umx09A4uESn0Qyn+saTgxGqP5MgIU0P61dKuQoTX5EPhs0XRlWYksyN2drvSJNb3F6+MrclRnmguRUlzni73WT+4GAsxobjEF1bV6zUVlDSPboGiUN25MglgpbKU2RjVe2I+ArCYBRUQg5rZXzjv0WtxLjGLJxG8/W7hKzxjPEM5xhKLWr1R53jOTmmgoOm4g8EyMzIPsHZC+5RZiXbrr3p9xPIGUZR2ch7kCLzOyym2d1Kh3JTTXH29hoTZgy/VQpU47rIKZu9p+K7n4QR0R6w78SmlAFgpv0JpeszAzJHJtSuaC+EegBfn1W3w1UUHX0QttpVBpp88m+eGgLZDxs/Q9G6Tq4IGkAAA==" + } + }, + { + "ID": "0c8486099982a544", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "afea927957c68f5c673fc5a0638a982b/807913734621329473;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=configuration%2CjobReference%2Cstatus%2Cstatistics" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:25 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:25 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vngh2:4427,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ea08W4juEo-NqwXt2aDwDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/212" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1dbU8bORD+zq9YRfmQk9JV3oFI+UADHNwB5QK0J7VV5eyaZI+Nvef1UjjEf7/x2hs2ifPGW2k1QiKxx54Z27Mz42cgudtwClQILgpt527DMY0YWp+hlXZBp89HJGDQWRiEvE/CQln3C0pinvYzLvd5wvyMMqJxTAZUkU64dC4Vre38wfuOn4Qele8iLuS7zVatfX15+/203x+db1/G3T9bze3tg6TCW5sff/87Y+bTfjI4ZJdcsft88uH82/6Hi5Pdr2WnlyrQdjzu086Y4BjpnTXlOSxT1UlFdlgShk66I6eCS97R+9B2vhS8kCe+O6RhcOPuqQG7KelL4QtTuqghY3VUJxGDZESZVATQarprDSXVVPiRRMKqR+6A80FI3bw+0D3izN278WgkA87i4mnSDwPPFTTmifDoiTms0sMQ9x9yTdq1xuZvL8a7WVnCO6bimgp3RCXxiSQu7NO5IN4VFWcRYQxI1yQMgESBcsT5VRKVZseYhTSeLCwko75PimEqCMjF2lxpte0nS6M31Esk/RTI4RkwiufJam5VX09Wq/X0Xcy2b/7m1dcUkurco/8mNJZHfDAI2MAwjoum/SBr9hirpQUMzMIr6x7oIp0MzePChykifbNch+rWus/LC+jQeM6zmdSBeB546eJWpbKCHrXay9rI2EhfwDT2ufhOhJ/X4KHLqsTsjMe60CPukdAqY5qSCmit6DUFj2/dA9UDHHqqMaDykEkqGAlLEwR9fvXN5hM42zhut8Yc44SBRV+G1JPu7xQcC0QI/5jKIfd3UiPjogoauAG75le0dMGuGP/OnLM0XtmY7NKQDoiE/Z/kcjiKwozLwjGpig/PjmqCH2KDsQQ9J+NlWnrW9tZqITjdKbUhxlohQ7m5LVZdONawNN1veFeX2c8c3rskiqg4AOPZJ57k4raozAbWbuiKMiN4ZpJWYmvZo7xwfbWF66svc1dzeH8k4r/86qzrmRqkRdabyx6Zl1xO/qHJ8dhJ/EDm1zNrFdNDzGqeZHsvczZdQf148WKmh2iJm8sSpR9wMl0OTvJmydnMDjLeecXk/DVPB2Lk4sVMDshy2Le3EM3DOGQ77+bmRDqsIkaUUovpoHo9jVX28DJHmUhdK/XvGomCceQrZm/OwI4CjxbN6ykRBIK7yiFq7pAwP6RGz3GMNKlCq1m3OXtGpSsir+4GKk4ZK+1F3ln6LgvhxgJdkTCgHbKdKII7HlG3udLcwWaPbEa6WGp3qK4DIRgPRIzwtjRNMHFj05Z4PQtn+4Ol7SSRQQjvmZcIAZd1d6cfS4h9EgIgiy+5GO0nMhG0ONV2fT7uKc2Zk11VrUH5taRbrWQ96cpMFkupVm07LHUO4apcghorKuYbvYQx0g9TAYfMdJbyI8xD37RllyuzB/NQK7DwbdlyFSvf3PLHnV1wU33IrrX6h2xIRQCZqaGe8AsG6aBFbN16I34msTZ51ZXlzTsd66FYL0p2yzrmgu6lqAQH19Y0JzLRayyp0rRhEj+5KXX1NuTc7oOOUzRjIban9hUM05ZS/cSGmV7Mpm3xfCgo8U85DzPbU4w+cQHX5JKFaDxcY12uRc1SazmHbavWmOCaXiL14Nw8k+820rSK5bZKpRQmJnrwLLn7JAll2zkcjRKpdkgj1/QyYIGK7Xd9Eudg9LIDIZ8OIG3rXJzt9b7t9XofeqozgVEKGi87Y2S+g7A8wvIIyyMsj7A8wvIIyyMsj7A8wvIIyyMsj7A8wvIIyyMsj7A8wvIIy78p9BNheYTl36Rh/oKwfNnRIHVH/wF92YF1U+ZTH/KDyIDpQymjA5gHaUTn7l63FfCRxJ3sz+vLTmDCu4bYO/rlLgOt9cQ8Qp9C8HNgcTcP9YPGcOrvb98r4IH5HSkSagD+Y4PbI8aPGD9i/IjxI8aPGD9i/IjxI8aPGD9i/IjxI8aPGD9i/IjxI8aPGD9i/G8KSkWMHzH+N2mYvyTG/4BXdydQ8TxFo9XzIPmycxmEENzVLVoD6RrGD7nOAUxzNEFUmz6i/s64CPAZkpHyOvD71/u5Ih7/8T1lR38+UK56AflCujONSuPeeTzntoOlCCxFYCkCSxFYisBSBJYisBSBpQgsRWApAksRWIrAUgSWIrAUgaUILEVgKeJNIb5YisBSxJs0zF+wFDG7X9MfBZTC1V0eqosoyJE8/Wyg0lS3jua2y02On4DEwyU+iWQ+1zWcGIxQ/ZkAC2l+WrtUyEGa/Ih8Nmi6MqzElmQ+na31ijS9xenhK3NXZpgLklNd5ogf6yZ3AwFnNTYYg+ravGajsoaQ7NE1Shq2R0EsFbZSmiIbr2xHwFcSAKOiEHJaK+ct+y1uJcYxZOM2nq3tJWaNZ4hnOMNQalerPe4Yyc01FRw2EXkmRmZA9g/IXnKLMC/ddO+PuZ9AyjKOzkLcghaZ2WU3z+pUOpKbao638aQ1Ycr0U6VMOa6DmLrZPyW6u0EcEekN/0poQhUIbtKbXLIyMyRzbErlgvrCn3v49VV99U9BFdELbadRaaTN5/lSoQ0QcL/xP1g6hlf5aAAA" + } + }, + { + "ID": "55cf8ee00ba14a17", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=status%2Cstatistics\u0026location=asia-northeast1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "01802533b4f941bce02c9ca09c9b1be4/7655222412361441150;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX?alt=json\u0026fields=status%2Cstatistics\u0026location=asia-northeast1" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:26 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/tG6iVHRHgZlQNN12UC8DJwGoL3Q\"" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:26 GMT" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.readonly" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlv21:4338,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/167,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ea08W7nDG4LUqAXF5oC4Ag" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/167,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/167" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp7ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMNkoSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAGWQ3wrCIBSH730K8XqEU6zRZVSXBaMXWOqFsM2hZ0SMvXtObRS7PN/3O384E8LEQwOjJ0c8IZwqHQpyvt8uBOG5yBHjwcg1Jp0OyPYP08V0KTg9UM4YLxkjRZ7kYOurqkpe92pjBd2LZKXthlYvK+plUUiVOxpNaxuVz8DE9MMIV9NqH+fE3h96ekM2LCs7QnC1fUXM/+ia5l/+bFStpXUqYhr+gefwEzSjD2H2CUE6AQAA" + } + }, + { + "ID": "3a2038f9e3ada242", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX/cancel?alt=json\u0026fields=\u0026location=asia-northeast1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "0" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ed9a1aedd66d14fa7bffcaf26fd38333/16110311055410528659;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs/vfywPbbmT9fsCK6599Hu0o67VGX/cancel?alt=json\u0026fields=\u0026location=asia-northeast1" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:26 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/Jin6iW14xmm5gmEfTLqJZSPqnHk\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703508000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncg5:4019,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=eq08W8n_IZL-qQWtmJlo" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/217" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAKuu5QIABrCh3QMAAAA=" + } + }, + { + "ID": "b31c2e6900d492eb", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "230" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d792d1853b316d4c2db27d93d9574411/14502249619419809467;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiU0VMRUNUICogRlJPTSB0b2t5by50YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE5IiwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiI1UU85YkVkWnVOUG9PMXVvQ2VBT1lyaU9adEgiLCJsb2NhdGlvbiI6ImFzaWEtbm9ydGhlYXN0MSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:28 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/pFm8ziDetJqbXt14376l9Xz_dm0\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnma19:4101,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=eq08W_GAOYqcqwXBmbTYBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/154" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/TMBR976+IzBtiTZxvT0IwtdkoGi10HYxpUuQ4N6nXNE5jZ1U39b/jpO02NgQ8Xt1zzj3nXvuhZ6AFL1N0bKCE56sG6s2bW5Ggd7oBiuZt4wYF9iIY3sMGosKV82J+lpXXw/hyfWW5ZnW6DO/5ENTnVXKlsOsEfkGu7uN0ad2gTod38mlTMFBHlajVUeDbx1RyelTqag5UKtz3vk1IEqXXzfirmOBGDOBk8rPmk2v1qVORUGTnvFy0WnOlKnlsmuv1up8LkRdAKy77TCzNQwrzzjarWtwCU9J8MdvUCaX5l4EfCsGo4qJ8/8Jl50Szp5BBDSUD7eahZxhoP2r0p6gtqWPtuv8IaqDD8Bb8cn7P2LYWmCgznjf1Add56HLvi6cKXUTn0WBmvDVOp5MvhhKLjegrmhQQ2xYOrcByY9fyiU1C7LmBHeDYsjDprOgoIBUvuzGzlnOQ/4/ELZsqKmGPiRNq+2mWEk/rh5jaIUvSxAtYhj2cpDQJXDv1mPvI7kzuuLQUJcZZ6iZAQkICJwUSaCk/yCxKvBAygokNfua6HmrZ2519VgNVMOSyEpIfdjqYRiezKB6dxuMoGkbDfdR1zV9Bf0xHGjmbXo4HmrIHVjUXGtstdzSeRdOTwWz0/dBtJJxDTtnmYlVoREYLCbqx3V9OKqoaeThZW7U7RXrCeDQ+Q89QXG+ePSK7JO0V+LIjYM/Rp3NsO9An3L0bTarVq76+qrXr//4+utFLKNVsU8HTO0HPvOoodQxLytsgyPF9hxDHwa53lOBQWVgsFaF2QBbMyVnOV/PVoggTkcybjyncQSEqqPu5FrnjDChjoilV+0lRb9v7BT+L3Jx6BAAA" + } + }, + { + "ID": "cfcb2333eaf78b24", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/5QO9bEdZuNPoO1uoCeAOYriOZtH?alt=json\u0026location=asia-northeast1\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "aba9d353fe521853b8925d82b2eda86d/12966245777450175460;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/5QO9bEdZuNPoO1uoCeAOYriOZtH?alt=json\u0026location=asia-northeast1\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:29 GMT" + ], + "Etag": [ + "g1lth1EwAGs/fxTYqurorQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4207,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/95.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/95.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/95" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/41Rz0/CMBS+769Y6hViJlGjCQcgC5CQDSYewHgo3dsodutoX4OE+L/blikejPH0+v3o915fT0EYkjde5+QxJBte7g2o41UJuHCHDLQRqG1pZK2BdJwbkJbOXUYCt1F8GIz1dfG+XO2NkmrR759dmm2hotZ3ssjigoPItcUvHoct77WaVuASfe1ceDw2nn9aZtNk/FOpZO6V5Hk2GwxnMWmlj84f6abSv6VPk2U8jrN/xfv6GrSNyE5uMihAQc3g8tRGyR0wnPqd5kYwwG4jFXbv727aLu7mWb9dpA+bOF+bZC7TyMgRDNKV4ukaJ19eIRlFLmtnp5rTbm3DtkA1RuR7FJRIRSYPbsWkRy7c8Iig50oy0Bp8y14r2xlGsmoEoBselQFPM2q/bsLRcgUVGoKP4BP+9iWHJgIAAA==" + } + }, + { + "ID": "aec8189ddd633a74", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_ba26dfd9500181a28cbdb57cf151bdab742d5c42/tables/anon11fd4be989973de97fd967f0a958ef9192e6f445/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "aba9d353fe521853b8925d82b2eda86d/2903095698427080184;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_ba26dfd9500181a28cbdb57cf151bdab742d5c42/tables/anon11fd4be989973de97fd967f0a958ef9192e6f445/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:31 GMT" + ], + "Etag": [ + "1BajMpwGbAf5HE6o1x/oqw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "127.0.0.1:4343,/bns/xa/borg/xa/bns/cloud-dataengine-minicluster/prod.helix-tabledata.server/0.esf,yblv10-v6:9880,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/123,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"xa\" allowed_cluster: \"xb\" allowed_cluster: \"xc\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:c7c9::]:4109 had response of OK with response time of 0.013940095901489258 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,yblv10-v6:9880,/bns/xa/borg/xa/bns/cloud-dataengine-minicluster/prod.helix-tabledata.server/0.esf,yblv10-v6:9880,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/xa/borg/xa/bns/cloud-dataengine-minicluster/prod.helix-tabledata.server/0" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTF0SszyLSh3T3JMM/VwNcs3rNDPLyy3tYWoKskvScwJyi8vBik1hogVQbjRQLaCQjWYBIqmwYWQhcFSZSDNiUpwoVodPOoMkNRBWbFcSLqItjCJSAsNqWVhMpEWGuG0kAvEquUCACL9/vvEAQAA" + } + }, + { + "ID": "14430fd2131326c6", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "377" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c94fd507d0ba213ae0e288c16559a82f/11358184345754292237;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7ImNvcHkiOnsiZGVzdGluYXRpb25UYWJsZSI6eyJkYXRhc2V0SWQiOiJ0b2t5byIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDIwIn0sInNvdXJjZVRhYmxlcyI6W3siZGF0YXNldElkIjoidG9reW8iLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIiLCJ0YWJsZUlkIjoidGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxOSJ9XX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiIxdWdKbmVCQUFKaFdmQnd2QlUxcE1WUXU3czYiLCJsb2NhdGlvbiI6ImFzaWEtbm9ydGhlYXN0MSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:32 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/yMGC_YkVUksKc43iIIL13GxCbwQ\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vndc65:4052,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/42,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=f608W6CBCpLjqgWYr5KYAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/42,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/42" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAJWTXW+bMBSG7/MrkHe7BIxJgEjT1rRTlK6N1Grpvjoh45yAC8EEm7Csyn8fdkI1ZV/dDbL9vuc9z7HFY89CGS+WaGyhmCebGqrdiwcRo5etAIomWrhHvpv5F99hB29zT6Z5Ol0Vny+iRfPR8ezd9fQ8+pTdLTL5jnmEz2ZXmEy/ncfNzT0yOdzEL+ucgeqXolJ9f+SOqeS0X7S7FKhUeIDr5LKAydnZZfphNWm2kwUur+9ual+OTIqEfHXFi0xnpUqVcmzbTdMMEiGSHGjJ5YCJtd1NYW9du6zEAzAl7ZPedjuhtP/S8HUuGFVcFK9OKA1JW30LK6igYNDSPPYsCx1bzX43qi4yVQf1H4NaqGuuzaf9e9ZeIzBRrHhSV53PMDBR7o7r9rpEXTF4T+McZHv4RR8epOfQatOSKirhaFIi24knSenYo6CXkevgwPEdL/KcUeiGAR56vuvjyHFwiEzRXn+/mgC0BKl4YdgNYAf9LLA/c/0PlusYLE21P96pVFTVsrtMvdNg6HYxn8/mU/STi7f47MnJKjiMwtemAA9J25O4BIdBcHjRtqhSv+gucYZdbC2himBNea4tZDQiYUgI9ob9GAfKwWKtQur6YcZIwhK+STdZHsQiTus3S9hCLkqoBkkbsuUMKGOiLpT+H1Bv3/sBjYs8NeUDAAA=" + } + }, + { + "ID": "96dd956952ac09ec", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "375" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8e7403b6ea7acfe0ed777adb7c56d544/9750122909780284470;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7ImV4dHJhY3QiOnsiZGVzdGluYXRpb25Gb3JtYXQiOiJDU1YiLCJkZXN0aW5hdGlvblVyaXMiOlsiZ3M6Ly9kdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE5LmNzdiJdLCJzb3VyY2VUYWJsZSI6eyJkYXRhc2V0SWQiOiJ0b2t5byIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiIsInRhYmxlSWQiOiJ0YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE5In19fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiTUplNE9vUHdsVElFdEJUd1JFNXFjRk5LcDNZIiwibG9jYXRpb24iOiJhc2lhLW5vcnRoZWFzdDEiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:33 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/M2wlowSH4_RI0LafJjeS-PW5bfw\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncd187:4490,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=gK08W-mwHMTtqwWv4qKADg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/143" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAK1TW2/aMBR+51cg73UhVwhBmjZtpR1dyyouu3ZCjjkkJiYG2yFjFf99dppUE63WPewtzvlu5xz7rtVGGc2XaNBGMU12BYjDizWP0UtdAIUTU7hFoZeFZ7/gAEMWyJSlF6v829liXn5xAvvaKxkvp++DxWTkXOHV5Rqm1s3nbrwqb1GlQyv5ZcEIKGvLhbLCnjfAkmIr16cUsFRu5/oSgo/8pmSz0VC9nZWTYXdHzscftv7XSkUCW13RPDNaqVJbObDtsiw7CecJA7ylskP4xm66sPeevRV8DURJ+8Tb1h1K+y+GrxknWFGevzpJWSXR7AmsQEBOQKe5a7XbqLYaPdWqIVWs++ozjbZRY27Ap/6t9tFEIDxf0aQQDa7KAD+VwETVRz0xXggCMxyzJua/BNWYJVZYQo1RPDvwpqKMWP3ffC48x+07oRMsAqcXeVHf7QahF7oLx3EjZDjHiomWIBXNq7hzQQ0/MQs8XUy8s5RGWs+Ld4jco6e0pRb/fp/2f1gYpR+PfM652GAzafRu+slgjvVipMKqkM1GzMnMHk3m4/FofIH+QFEtRh6QREAlPKObiuB2fR3L93yv59YXSJOEelyPeg+3opAgFrDBlBmI3+v5UeT7btC1YrevHJdvVIS9MMqIn5CE7tJdxvoxj9PizRL2wPgWRCfRIntKABPCi1yZR4Vax9Zvf60JhCoEAAA=" + } + }, + { + "ID": "cc9ca372b15cfb95", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/dulcet-port-762/o/bq-test-table_20180704_40692981547271_0019.csv?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1f3c99ec54dcd104c5de3bede329e32c/18205493023494403914;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/dulcet-port-762/o/bq-test-table_20180704_40692981547271_0019.csv?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12549" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:33 GMT" + ], + "Expires": [ + "Wed, 04 Jul 2018 11:20:33 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703515000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcb63:4330,/bns/yk/borg/yk/bns/blobstore2/bitpusher/1125.scotty,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ga08W63QB4aKrAWB0YD4Aw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/blobstore2/bitpusher/1125.scotty,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/blobstore2/bitpusher/1125:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjdnVUJQazNwQmRIS185bmdmYnJxc18zZ2tXS0U0ZHRUVTdsSXhHVG9KUTFNaW1KNGY5bkFIb3Q4UGVBX2w5YTdpM3NkR1RJRG1HelFLWW5NVks3cE1JSy1hR21CVG1GaDZfT0d1dGlwMkJnYmhDaGpfZEFPaE1LQUNpZ3pWNC1MWExtRklYMlFMZjRvNVhTUjlfQlhhRDZJTThCNG9Kb1BDanpCWUxnU1A4aEVoend2aXNLRURlWTRFSm8wBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqMRmlMKP5dxhUqr446vVYqibvwq2BMe8SCo4uj1Q4fw8EiJ8myn1GXahPqRLHYS4nhGJbHSQ_RBJkSYV_Tf_pikzZNoF01QLZ_KAiwO69RTsElafw" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTkuY3N2IiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6Ok9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBkdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE5LmNzdlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YTo4NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6MjcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmRlbGV0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTEzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTkuY3N2XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1OT1RfRk9VTkQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTkuY3N2XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxOS5jc3Zcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPW5vdEZvdW5kLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5OT1RfRk9VTkQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxOS5jc3Zcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBkdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE5LmNzdlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLm5hbWUsIG1lc3NhZ2U9Tm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTkuY3N2LCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLm5hbWUsIG1lc3NhZ2U9Tm8gc3VjaCBvYmplY3Q6IGR1bGNldC1wb3J0LTc2Mi9icS10ZXN0LXRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMTkuY3N2LCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxOS5jc3Y6IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxOS5jc3Zcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBkdWxjZXQtcG9ydC03NjIvYnEtdGVzdC10YWJsZV8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDE5LmNzdlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwNCwKICAibWVzc2FnZSI6ICJObyBzdWNoIG9iamVjdDogZHVsY2V0LXBvcnQtNzYyL2JxLXRlc3QtdGFibGVfMjAxODA3MDRfNDA2OTI5ODE1NDcyNzFfMDAxOS5jc3YiCiB9Cn0K" + } + }, + { + "ID": "20e3ca772e3f1ded", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables/table_20180704_40692981547271_0019?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d51a07ce1cc1c01a1e9a1cfa779f265d/16597431587503619187;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/tokyo/tables/table_20180704_40692981547271_0019?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:34 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnac205:4139,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ga08W8GOENW7qQXv7o-ICw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/27" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "0b838c767823c9d6", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "234" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2342d0dd6e73ea3cfe6ab6d5c670fbaa/14989371251024461980;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJleHBpcmF0aW9uVGltZSI6IjE1MzA3MDM2OTMwMDAiLCJzY2hlbWEiOnsiZmllbGRzIjpbeyJuYW1lIjoibiIsInR5cGUiOiJOVU1FUklDIn1dfSwidGFibGVSZWZlcmVuY2UiOnsiZGF0YXNldElkIjoiZGF0YXNldF8yMDE4MDcwNF80MDY5Mjk4MTU0NzI3MV8wMDAwIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIiwidGFibGVJZCI6InRhYmxlXzIwMTgwNzA0XzQwNjkyOTgxNTQ3MjcxXzAwMjEifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:34 GMT" + ], + "Etag": [ + "DjVHbJ3G7PFmorowLkA2nw==" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnh126:4324,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/44,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=gq08W-CeFYvCqgXjnZbwDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/44,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/44" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI2SXU/CMBSG7/crlnordOsmAxIvwG+DxiB6Ywwp22EWtnauxUkM/922G4kxaNaLJn3Px/uck345LloznqChixYsfd9AuT1SdJEBOtYhUDQ1ofPV8/XiNriKHi5zUYpqsh4RXp2e2iRmq5NNFoPqFKJUnahHhglVVIKaE8/ve5EXzkOvNyCDvn8SRiTy554+Xev0TwrxrYOEbDlhfG183pQq5BDjqqq6qRBpBrRgshuLHO8HwB8EF6VYQawk/sWFGy6J2wBiCyhxS06bNoUllMBj0LRfjuuiBuXm0JpMmRZrlCajBVddZu3qohaAjruzu4zfIKd7tiWDLJH69aJfVtIip7mBR9zaGKNtYYX7p7uL6c0ZMupOX69NT77Jx1sFpg+ybEaZCJ7OoDwQmYrqhxCXQBUTfMZqV/8k0DMEJAjDoF//ws+ClYdyeoOgWQbKqFR3ImF6oOTvTvtBZqPx5KKuE7HtbNSnR+TsnG9JcZcCEwMAAA==" + } + }, + { + "ID": "33d20e1edff82481", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0021/insertAll?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "121" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "28c673b4b5b0348be02c4a1ddae3676a/13453366309560042948;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0021/insertAll?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyb3dzIjpbeyJpbnNlcnRJZCI6IndNbnZvbTliWHpmM1N6WEMySXJ0OHgxcndHSSIsImpzb24iOnsibiI6IjEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwLjAwMDAwMDAwMCJ9fV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:34 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/171.esf,ybpm14-v6:9841,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/121,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-streaming-frontend-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-streaming-frontend-thermostat\" pick_cluster_params { allowed_cluster: \"oj\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:185::]:4021 had response of OK with response time of 0.014589786529541016 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpm14-v6:9841,/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/171.esf,ybpm14-v6:9841,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-streaming-frontend-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/ow/borg/ow/bns/cloud-dataengine/prod.streaming.frontend/171" + ], + "X-Google-Service": [ + "bigquery-prod-streaming-frontend-thermostat,shared-layer2-gfe" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/1WOsQ6CMBCG9z5FU1cGZzcTHVhdDcOVHqSx9PRaUEN4d0sVA8vl7vv+P7lRSKlu1ht1kErb9tEjv3cRtMMTRCh9QI5H5y4Y7pQOVcwFm/GZmTik4jUxKcc8szX4SnhfLAS3yXU6e0YI5OcXrB/AWaOKtXZUQ7TfgN8qg7pvS9/Q7LaqwxCgxVzqO2RbSxqQG0dP9c9Nv60Sy1WJSXwAeAXOfhYBAAA=" + } + }, + { + "ID": "f906e187f3a99340", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0021?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cb53072359191b38b52bc1cee63d6311/11845304877864159981;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000/tables/table_20180704_40692981547271_0021?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:34 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703481000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneb206:4483,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/178,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=gq08W8HJJsrkqgWQ3ovQCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/178,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/178" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + }, + { + "ID": "be236ac47fabc8da", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "163" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "40f4d14f800253daeae5821b1eeb1a7f/10237244541368225558;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5IjoiYmxhaCBibGFoIGJyb2tlbiIsInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiS1ZXdFZreVFWbW12MnE2RXh6N05WTTV3UW9XIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:35 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/ngaCKhE2wRAltNG4sw2fHO9wEgo\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnqq6:4031,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=gq08W8jVL8r0qAWI7IHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/141" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAANVZ728aORP+nr9ixcsHTmoXdvkVIkV3UaAtd0makpC+7zVVZLzD4uK1N15vgJ76v7+21xAgCyRNer1TJII94+eZscfjMf5rzymMCQsKB05hQMLbFMTsP1/4oPBKCUCiUAuuC01/3Gx/hRl0aC0Z0dHbIfuzfdOf/LdSK7MQHf8x6viT3hGVZ29rycQfvnvfmnRCfl0wOMTABynFIF/HXMjXzYZ/0L9w/7j6KK/Gsw9XUXTn3zY606/Ns6vT+uQD/2gGJkCHJ4SN9fCRlHFyUC5PJhM35DykgGKSuJhH5bnh5Tu/HAv+BbBMymt0ZeVUUt5C+CvlGEnC2WH/wpCrAT0YggCGQRnw157jFCx6N88hPciMyqQ7fHMKcz6trCj3nG+aFXM2JGEq5iJDa7yzjftWYUDRyMk+BB8DM7jaSMIFkUale3bZ6R0dX3avOlaaJnACIcKzi1uqNIaIJqAE3yx/IpFMkzmxbmnnC+33ZxlAAYTgogdJSuXCIgEoyRwh7A5REnwwJmaEy37eLvUHMEjDLhtyLfjUPbs6Oum2b7pn5/3Lzw4SYRoBkzeGzrlOK5UqRgzR2VcQK52UMDhwvFeY0zRi6pvphuzzldMzph04mAdwuELiRJAkKIRDllLqGGsOX4jWMZrngkt+GPAIEaVwXcCUp4E7AkqmbkcrtI3ounDNtHVa5UO/0/vfTafXe9/T3XNztOhixiSaZsgHTp/BNFaRCIFDAqVBhgSUadfXJij0fwdJ55N34H3WQPM1uJGzWBG9v3zX6d33anizMlrVTMQNUeuiu19oQjSw+lPRpBYisvvXXZ4Q1R1x5namGGJtU1I8TweUYNcYZqardC90v6A7dFBv7v+yHTUBcQfCVZvSbQuIgBqgviTUjdBsAMcqciW807pG8kbw6MLsgCMWtFX+IzQprY803LVa9adxV1uN7+fG30/baD6e9q2RqhxjUsGlQCwZchEpKUylQFh2phJYohbzlKtECklp84iMvf6ECd+MFYK8b2Z56iORozcpwyay1CRcosFj7PFb/yx7Ki9ij0nfKkCMYBen5z8hIk5UtslwfueDY86k4JQqGR4BHpc2SQ2PX235TyTayJEDv994Qh5ZGZ/FbtGzBDmy+eLUn8SgNyScIjxSSXUZfbnfTo3nVb4bOgPuMglCpfR8Aq9Vf9rsrEBQFA0CVBQpU5Kiv4Giuf8MCoVt2huw91tPiNEcbNX1I6yGKeB0o9HNuvc4aMGTmWuSuQLq6QZhSiSv7DYOtPkr8ozAq9SeR6Bai7jJwffrjzyiNuFvBW89MtV9F/j+My3PA201F5s0SZkrYEhV8ea+BQZCr9IpyBEPjjBWJSkXXq1eU2B3qqYv9dmY8QlzLngqMOSBtIGqel4SFq6idKOYzlG26mT1zOJ01U21b1m4YMjGzLFsKxvV2rUFbF1nJktPSA9UQZdIVRtPZzqzIUpL6/0We2di24DdRnEM4h3n4zeqzuBiVlQHF1a+W7mWPCB+MMgeDbvOna3++Vv9q+4qaDZgXyHxddm7XH/WlOY11K5d/yPdWd43SxhHaUDksj8Po2JdxXrzrNj7MWujivog2e7MukrG2NyV8H/CyuhaRlXrO9x5oJSx7rws/ITVOeHhdmdWFewt8x8YZhmGTcj52PXmyjmmT4zYSItGqVqdH1f5J8wGe2L9m0b26aOYLM6/4vzLhQolgqFo/58jgSJQp22iZnuEWEDBmro4Jm0d26j7ebmWgXRFjJW1+qiygdqL8YX5Nj/IbRDqek3JuuwojinJftwobVS205QXp9tZj5UjDKjySB0adFZaF9ijo5lXpbwIcv7eykIlNZd8znAqBDDpHg0Sc99eXN7epDIVUFxruwFf9JQ2jLErVcs9l/8u9urz2XWYbGfxvLwZllkZ4epyAmwUFZcbvZQxfVnXBF1mO0vLGnbf1/Oug4+GV+GhPcjBbeRtoVzcJfcXnccqUw0QHmfmd9kIBFHFqZWe8T5TFWEObdXPS2MvRJvH5z2ab9Pq5C5K7q0iP7JOuYCOucJxldrqdkVWem0kVXJvQv/yUDrOpmEp7d7buCazEZK3a/+GwMyrqv7FgWnuZuuxeDkSgIJzzuk89jTQRy7GIEo5Qpvhak9FLWaQmZUbYBt+bQXV3CMz5aVxtuSt6crKvgbZxxj9FvSol47sneP+laOg37DuH6j0C9YnDWweqLa/UG18onoZq/TTmvN56XmNJJLgxROb+T1ecV+SyPB49WqlWVEptdaq2TdFNUjIB/J6pVqzT3IsyJda0jTRbzURIvrBr1BtNKqtVrXq1eqvB96+rHg8ki3kN1tjXA1xSG5Ht2O6P+CDUfpbAHdAeax/qE6yig5hzFMVHyrAC3vf9v4PM+IxiEEeAAA=" + } + }, + { + "ID": "76488b7e63c9e033", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/KVWtVkyQVmmv2q6Exz7NVM5wQoW?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2863a98e34cf7d4a621df31acebd3abf/8700958129238774591;o=0" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/KVWtVkyQVmmv2q6Exz7NVM5wQoW?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:35 GMT" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4409,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/121.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/121.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/121" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/61PSwrCMBDd9xRD1iIWXHVXUETQgp+6qSJjM9ZAmtQkFUvp3W1jFx7A1fDm/XhtAMDIGG1YBG0PephrTj2az2aT76Mka7EYfuzQKIdv8I4IUkXvinJHHAQn5cRdkIEzu0l8nBmggyyMwgsbc7zL9jGZxzAW/rPDZ3FdolBDVCH1DeUvZwit9pxQL5SC72oyza9C6hyd+GoqNFiSI2OnTzZKOn8v4ybr0NXDJrZOTvFmvbjG+1W6XSbHQd8FXfABQ4/2rWMBAAA=" + } + }, + { + "ID": "ba8faeedda7a8140", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "155" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "f05a7236014dbb1e9d52bded6cfee895/7092897792742905959;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ICoiLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlFURFdBZGd0UmtWVlNGS2lOTlE5c3hrWXhzcyIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:35 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/4apw52252JJYnpkBU2m2qxN_a1Q\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vneg123:4118,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=g608W8GIEsOHqQXd0p2oDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/112" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAO1bbXPaSBL+nl+h4vjAbW1kJMTrVmqPNSTxru0kYCeXi7dcgzQGBTGjjCTbZCv//XpeBAJGCGI7uey5XGVb8/I83dM90z0z0l9PjNLUJ16pY5RG/vhTgtn8Hx/pqPQzVOAYjXnFRalpT5u9z3iO+4ETTYLJiyvyn97l+c2/q86Bg8Kbum3X7d9/f0/C6W/n9sz+dHt6iaw3FyWB4wt4LwlcHD8NKYufNht253xovjnrvet643gwfft2+PwP//T0TTu6nb6/jSLRMcLB1bFPprz7JI7DqHNwcHNzY44pHQcYhX5kunR2kAp+cG0fhIx+xG4cHazRHYBS0cEWwl8D6qLYp+TZ+VCQQ4cBvsIMExeDAH89MYySQj/SKcQ7iV6ytkA3o5Ty8cZA+cT4wlldSq78ccLSKkErtFMPyyc+PCCM8ZPA48L5lPmxqDo6PesPuodnR2/7qjaJ8DEeI3c+/BRAiysURBgqvijeKEZxEqWE/IkrXeq9OpUAJcwYZQMcJUG8kIRhFEkFfHKNAt97I0SThFn9PmXKPTxKxkfkivKKD0enb7vHR73Lo9PX52d/GmNMMPPdTict7w5enJ/0T886xrB/3D88M34yZkkUGxN0jQ1kPB+8OjHcAIF2vxiIjZMZJvGlkNW4SKrVmosICuafMVspDHyCO4b1s0uDZEY6RksUY/n7F4P7Griazz4Gtt1x2lXrIEbR9NcJIl6AnwV0HJk1x4wwu/bJ2Jz5t5iZVdMNaOI99VCMMBkDg2lZju3UWnarXnV+NgZitDqGSz38bEVvY4ajCI3xs61VM+X3puAXekUwAzyGZzgAch9UN3vi6ZASAq7Rv3VxyG3QMQ4RITQ2fOLHPljqMzZWWkIb48aPJ4awlMFwGMw7Rp8PWfHIAzj8hvE0erBk+EHU0ZvighiG1hy8wjCUUeTD0jLwqKxD0r+G8KFnf19nMQT/a0Zj+syjM+QD7UVJdDEnOPBvTWGanqi6KF0Q7ji8yZvz/uD9ZX8weDXgxamSvKrQiig2Plid1p+8Yzp5L+N5CMCvzl72B8tSDicchTcVtrj0YULz4r+vTbiuj4736HjfxfHgB0KykQkCWZtwOMCB3EOt/8JI57EfmKPED7yXvNUiGLyDdf41mgcUeZX15h/RNeq0Gu1/bieE4hklaegRUb9/i90kpuxoFgblQ5XFYJkR8DJTDoGgqmz0kMR27SGIsWiAu8R7uRQhypPBajcfQIYAzUYeKitRuAUGOGbzspU7FE5LJ4akDtCIZ77ETRjjQV9ggT8dogCqeDv4p7JRKoBr1cbDjfFCsVy16s4DsIvcEzLWXJvWqwWsmzMIMBMEKCDJfJCQynqxslKROhngF6IW0m8h5RlDJLqibAa1IWb8v55kym+nJskeyuRjjXEsymCtB/rYx1EhcbW2O7GA6KoVFCriDBxkiZjNYJnzMuSrHaTZqi3raxnFuic9BAKEFr3uFM2EDPoxRCA5Lr/TEfhgzGgQQB1fRRc0RwQUA5JKXms5jrZ138RFhLV7IHQn2J0WEDn1PQwmoHI5NPCthnZN1KOv9D+hsFnHZUsRaOrSxbG+F8OQ75NPkDsBZ86iZ8vV0FjWHtN2HUIAL7xLS2C16/uNzgqEik8sIVBTtnMomq07UAC2eM7BbhXG3e3YUPQQUqvAlge9q78zGs1NkYYB0IA/+ASq4rcqcnlc/JV6SWBVdwwweQTwtPAbDb5d33FlyMPfCt4uSufuAt66o+Q60HZzMUmjhJgMX/ETNvMF31twK53geEK9ruviKKLMqterAHZNp7hyTqaE3hBjSBPmYh1IDwd4DLsYMl5FEWmKQtnaRojoLGIwf4R5C7uIlEH2SbHUk+zVLpoCKu0Sg8UHZIBhnxXFEKBv53xlEwnlWrnCLlzYcrB7KITg/5LS6XPkQko1L0OO4ILuqp7XbBBvdFKhwb6LfvZW/WpFaU8O9lvEPme10+qz1kiFoV2z5AdRJztvMhjdxPPjrD6bXrHeRGlzJ997GNscwv4h2q7MehPJ2Cxa8L+DZXgug28LbLPZSLI2dtzufkvrHNPxdmVWG0i2wvTnOygiMdSCrMeuN1fiGI8Yoagti0a1Whqu9BEmR56Qn+DJ3zYK/UX8K6f/DPkJlIvL6u9rxNCMb8siGG15UqNEXYRJlcc26rZurSU4NlnogrQ8VClHHYTuUPyXBnLlhDxfg7oj0g3DwJdHe5XcxmqYdH66nfUQFCE4AI0gaATzynqFCh1NXZZyL8j6uSVdJeHHbZkznO4oiiH8LffJz5M4Ybi89mx6dFFSyemTbsa0cflbsetPKfZi526yncWydCMcyzTC5OkEVl5Uzj4MEkLEiRgQHBFVWMm2UPO+rtsO7gwP7sE10OA2dFNIi5tRf1HID/RGyJ1K8Y/IBDMfklNVe0rPCWSEGtqarVvG7olWx2ftzJdnHa1RtLsKvWedUIbT88CoXFcWWSlNz5q0O6Ef3JUO5TBklt2ljGt1ykN0s/YbOKYuq/qBHVPszdZ98WzCMPJeUxqkvseB3lE2xayiqVQrnLMvallCSilzYBu2s4Iq9pGycaafSnnFVYS44/eM0bzgBYbHtxQe31J4vCz+H74sVpFyMeui8utkBFm4KcZK3s8uK9Pse48j1PU75RmajzDsplGM5UU0r3nO6Gwo3jnrEk9NSP1ttOPsce20juAK2ldkjTnTTE9q73Naryfdk9GqP8S98y6X7iorNE3TaDSMGeRGa6v945K++LthoOIhgb2h1WM+OAtP4kL3OQxFNMHpuxfL6m3vH6xuR6EihCVu5Ad+PDdhJKF3BCPJvSkqc8RDIYFKT94xcWiaJV+crK91ljK0tcnqvciAxIl22a5W9dQt7bH+fVDD6EeJYM9R2tqqtLIp3+nHyYizpGlnz49AHHciDZxNsVVjpVl75yT3cQfww+8AvtaFOHJauuo/bfsewDPLzyq49s2r/cDT2bUKbOkyh7X5vECX81ZzWpjSCXR9K7Vdaun8dz++KzFEUA2pySmN/at5NwyLiKvbDygVJ39rY4Rgt+Xy40QsTz+xx7HVkaJA59ET3Df16oVAFV2XVID2Vs3vJMCK3+SK0K5vPRm+kwQL58pnt3SXZv9/nrZGfIrjdJjQFcBJO8Iq14Xk6xoLp1pppd5s0F507UCS8VUNbL2xj5Ey3UNGuf2HMeTWMzA+jkLYnWAdSa2uPRXegSS9+NiC7bSKbwu02OqEZIBdDMPuacFt7T3TLpaVWVXNgrRKN+ztr8OVx//8A6flSqDkP5Hq6Ngcp7HXzJB9F1Q92NjuRqzrqY632sUJ7I3PMOf/DUV4AaGjWW2gcq5iN07x5VQ+c8MlRohYxDelWV02W6mssXgsi5gUTbSNp6m9q9qZRzmgI/0vj8TZ3SqbEGXb5Gce2QmUx1PLVQZFcwJUQ+pOcbzcgqqZD558Q9l0gJHXvwbkyka7NNTmJTV5+Gp8WjA+ekxnT4nLGVnla+1yS88T0xypLStv6cJc2RnsYscQktP8Wt6znaFoWh6CCKYPcGyQuZFbNkivhvNctQB/5Z5vHdXS7gw2UIW9TuRDOhAcpLJRIQ+02nkTeDuqryLyID24P6Y0zOGwqlZePlQoeiF4q70TtsSRp/n8ReE17I1qKXjjq4a8nEUTNs0Zl5ZIYNTnoCou8o9Bdz77LfEPV5dfpfLPVj9wMPFV6vbPUnO/S/16Sfg3tMafme9o/Sj23cW3tOI0EPjO/JnAtuq1arMKsb7uOOqjYejEYk19q6m+vSWevlaRgkTsEs+Qz7/sLdUajVq7XatZTv3pyGrFVYvO4jaym+2pWxu7Y//T5NM0aI3oaJL8ywOTBvwlfHMcyVdBYKmiCYRmMH3pyZcn/wUP3JiFIj4AAA==" + } + }, + { + "ID": "fab4423f61ceb69a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "241" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ede01280acfb3567e56a3edca6525f18/5484836356752121232;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IklOVDY0In0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoiMCJ9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiZGFzTzN5d3I2eHhGdjJWZ0lUUWljdTRaVTJBIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:35 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/R0x2Kj482lnaLzNAbW15-jTrpHQ\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vned124:4463,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=g608W9GjI4KHqwWJpq2YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/16" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aQBB851eg62uIjT9xpCpF4KSoiDZgkipNhc7ntTkwPmOfISTKf+/dYeejaau+IO/NzM7uaHlstdGaZhE6a6OQJtsKisOHFQvRiQCA40QCd8g11u7wAQ7gp1a5TJeXcXY7XMz333VLm+r3xpeV1TPSDI8fJv3wpmt3VkGRf766Q6oPVe2jKiXAOzkreMd1jLP57DTC5VfzsC+c+/uLnXGdjIIrSirrdm70lbCENB7TbC3lS87z8kzT9vv9acJYkgLOaXlK2EZrBtd2hpYXbAWEl9pvdppYSjz+3fA8ZQRzyrKP85kyF4IpxFBARkAM8Nhqt1HdffSnhaRIqWr037u1UeMnycKy1X6SroRlMU2qooGUrdquLl4qGY8Ypn2u+glHKDnNlDDAYdoM/T9jSzXmuISasyC9uKeHuhd2e2GsGzHEtmsR0G29B7ZuQexEPddxcKPm0vCoxRnLbBIRodVtACC2AcQhnufqlhlGoRmbjhfZoWEZIZLqp+P4pADMYUjLnJW0CWYw9fuBvxhdLCa+P/SH9ar7gr6j3kxHghlM55OBkNTEvKBMcFVco0ngT/uDYHTdoFUJY0gwOcy2qWDEOC3h5CXjb7jAG+BQlAL8oRY9Bir6NlBwyF+ClkEca2nmWOj4elzwteoap9Ub2a5+QHqjaT3//mypD3UeJce8Kpu7kJVSiaUno8klesWi4hjIM1OFKw+DbpSga5u6q5uGaTuuc7xHISr4e9xt8LdHqKw3kPE6ADTzx/4gQK9mFekWC9hgKrNFpuOYnmeaXcvuiKviepdtuIcN11sTMyEJ3S6367QXsnBZfYpgBynLoThNRJMdJYAJYVXG5f8dtZ5avwDU8LvXuAQAAA==" + } + }, + { + "ID": "e81bdc76e099456a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/dasO3ywr6xxFv2VgITQicu4ZU2A?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1fa4ed1610a5760e0b9d505d0ddfd281/3948831415287636665;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/dasO3ywr6xxFv2VgITQicu4ZU2A?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:36 GMT" + ], + "Etag": [ + "Zr1c2kjqZk8mCbogKiJGMg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "1fa4ed1610a5760e0b9d505d0ddfd281" + ], + "X-Google-Backends": [ + "[::1]:4158,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/30.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/30.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/30" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qskMA0aEx6AIE4nymQ+zBjTdbdZ2NbR3oSF8L/T1iG+9Pr9uN533Xcch6x5mZA7h8Q829Qgm4sMcGEuAag6R6VLJUoF5NK4AWlm3JHsM3e92kTr22ISi+yJP86es+Hw16XYNxRU+/YaaZxyyBOl8YfFTstbraQFmBfT3pdtbmlsKkt78+V0Ng3+S4VIrDQPfX809qeklQ62furzYFOsRBxAChJKBucslRQrYOjZpZM6Z4DdSkjs3gzcdorpbHWqXq6arRzsdvc/7nvmLRec1ddR6I5O3lwwilyUxh6+kb/pKJDmgdiatUmfnLlxg6BepWCgFNgpPXLKOxFFlQOauChrsDSj+jcfOLZc59A5AsSdfG64AQAA" + } + }, + { + "ID": "2d78fdad14d61c0c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon5cdc9b105eeec52ec6c997043bdb3f369d5b242b/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1fa4ed1610a5760e0b9d505d0ddfd281/12332145034492232909;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon5cdc9b105eeec52ec6c997043bdb3f369d5b242b/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:36 GMT" + ], + "Etag": [ + "mBJdqoHVkzGK0Izu9/IdgA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "1fa4ed1610a5760e0b9d505d0ddfd281" + ], + "X-Google-Backends": [ + "[::1]:4496,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/25.esf,yblr14-v6:9811,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/39,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:dad4::]:4134 had response of OK with response time of 0.013651371002197266 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,yblr14-v6:9811,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/25.esf,yblr14-v6:9811,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/25" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqcl18kopzPcIy65y9zbwrCq11PdMSXe0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNBkpwoVooK5YLxovlquUCAPn49C66AAAA" + } + }, + { + "ID": "f7c961cfd9ee1b7a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "246" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "919ee89e0b63c38f30a9b8c770275c59/10796140097322650102;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IkZMT0FUNjQifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiIzLjE0In19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJ6R21uV1FscU12ZXQ5WFVlOVh2ZFkzS1U2cloiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:36 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/bx8fmRj0v8w0lqjX1c9w28dh9u8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnoa21:4387,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=hK08W9mSDJeAqgXe7Ipo" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/164" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T226bQBB991dY29fEXM0lUpVGNomsum5r48RNU1nLesBrA4thsetE+fcuC+TSVG1fEMOcMzPn6PDQ6aItTVforIsCGu1KyI/vNixAJ6IBHEdV4w7Z+tYe3sMRvNgs1vH6Kkxvh8v5YaGaSvDTCZPpRt07BzXebRYacQ+6s1q7pXOH5Bwqx6/KmAA/zVjOT21LP5vPevdXSXrzNd592gN3F3NwF/vVN+Pj3MpvJbGAOBzTdFvR15xnxZmiHA6HXsRYFAPOaNEjLFHaw5W9rmQ52wDhhfLbOkWIKpS/LDyPGcGcsvT9fCaXC8IUQsghJSAOeOh0u6iZPvqToIokWXX3H9q6qN1XgcXKTvex2kpYGtKozNuWXCvVNcVzVdkjjumey3niHig4TSXRx0HcHv0/Z1dszHEBDWZJnNBRA9UNNCcIVT2EsG+bBNS+6kBfNSG0Vo5tWbhl82phzcUpS8HA2LFc2wLLMjCEATbBcFWVgBEQQzP1laaSwO2jiv1Yn09ywByGtMhYQVtjBlPvwveWo8vlxPOG3rCResjpG+jNdCSQ/nQ+GQhKA8xyygRW2jWa+N70YuCPrttuWcAYIkyOs10sECGOCzh59vgLznECHPJCNL9LobWhYm7b8o/Zs9GVEXWNLsefL3zLRPX3WuJL3jWOy1fEffMBGT3tidZ5ev7oyBeZkYJjXhZtOKpKEoXyyWhyhV6gqEgEeUJKh6t00EQStL6h2qqhG5ahNfEVpJy/7Ztav+6/TqJcnUDKGxfQzBt7Ax+9uFVYnC8hwTSW2kQcXNcQCeifimhxVWMJd7Fuu1tiRCSiu/VuGzsBC9blhxXsIWYZ5L1IDNlTApgQVqa8+ulR57HzC6DCLBW9BAAA" + } + }, + { + "ID": "1c300bf38e2733f2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/zGmnWQlqMvet9XUe9XvdY3KU6rZ?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a58986ac21f475f6a7290188277bd7a5/9188079760826715679;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/zGmnWQlqMvet9XUe9XvdY3KU6rZ?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:36 GMT" + ], + "Etag": [ + "PncQxuMWU/pJ+GVtoLlgPA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "a58986ac21f475f6a7290188277bd7a5" + ], + "X-Google-Backends": [ + "[::1]:4496,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/127.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/127.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/127" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+51cs9VGJqAkGEx6GUbwM3aYT1BgzurM57NrRnqGT7L/bliG+9PS7nJ7vdN1xHPKZ84ScOWSeZ8sKZL2XAQbmEoKqGCpdSsEVkAPjBowz4/Y5Db6ryTQ6LG/2x08oPJb57nC4cSn6AUWsfWuNNE5zYInS+NVip+WtxuMCzItp7902tzTWpaUvvXv38b9QiMQKd5HnuSPvgrRSY+ubPhubYSHmIaQggVPYJSmlWADFa7tyUjEK2C2FxO5p/7idYjo3+s+44NOALScrwMEsgsFslTyf3EZ9+bL1MkFjzAU39uiB/E1HgTELxZdZmhyRHTeqEZQvBQWlwE7pkW3ec1GUDNDERVmBpWms//Iqx5brNJ1f6dkdabYBAAA=" + } + }, + { + "ID": "e9e31a163e9712b3", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anone3aa86976e663aefba4e3900ce3bc3142d10cb95/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a58986ac21f475f6a7290188277bd7a5/17643167304381018163;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anone3aa86976e663aefba4e3900ce3bc3142d10cb95/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:36 GMT" + ], + "Etag": [ + "njqOAG4hzOa/p8mHIVrFyQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "a58986ac21f475f6a7290188277bd7a5" + ], + "X-Google-Backends": [ + "[::1]:4143,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48.esf,ybah5-v6:9805,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/75,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1101::]:4358 had response of OK with response time of 0.014767646789550781 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybah5-v6:9805,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48.esf,ybah5-v6:9805,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/48" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqcnLKvR3dDfJqPJP1C+wyPXwDCtyqwy0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNxnqGJkpw0VooK5YLxovlquUCAK+tpb+9AAAA" + } + }, + { + "ID": "9c16137f1413663c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "253" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6b252b920a2b38157b4d46ba6d0803ef/16035106967885083996;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IkZMT0FUNjQifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiIzLjE0MTU5ZS04NyJ9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiMU4xZUpNVEpmY0xNazB0SFpONm5WSGZyWWNGIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:37 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/lpop0nnVB0Pvq8vzHD15jFt6iA0\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnhr7:4465,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/60,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=hK08W4rtPIixqQW6x6ugCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/60,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/60" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1T227aQBB95yvQ9jXg+y1SlVJwEiJCIzC0TVOh9TI2G4zX2GsQifLvXa9xLk3V9gV59pwzZ+ZoeGy10ZqmS3TaRiGNtyXkhw/3LEQnAgCO4wq4Q46+dgYPcAA/MYtVsrqI0tvBYrb/pppKkrFMTdP5Z/Vmt3V3D5cDzbo/5zbtqXdI9qGy/bJMCPBOxnLecWz9dDbtamMNrq6Dq4iMrtcqv7wd2+n8Msq/k3MpLCCJRjRdV/IV51lxqij7/b4bMxYngDNadAnbKM3gyk5XspzdA+GF8pudIpYqlL8YniWMYE5Z+nE2leZCMIEIckgJiAEeW+02OnYf/mmhSiRVNfqP3dqo8avIwrLVfqpcCUsjGpd5A0lbud2xeKmqeMQw7TPZT8wDBaepFAY4TJqh/2fsSo05LuDIWRA3ctVQ9ULNDSNVjyCyHJOAaqkuWKoJkb10HdvGjZpXhrUWpyx1PWLYbqiZgA3TDnUdE5dYmni0IgjBJmBpNrE8VKmf6vFJDpjDgBYZK2gTTH/i9wJ/MTxfjH1/4A+Oq+5z+o76dTIUzGAyG/eF5EjMcsoEV8Y1HAf+pNcPhvMGLQsYQYzJYbpNBCPCSQEnLxnf4BxvgENeCPCHXLQOVPRtoOCQvQRdBVHX6Hz0pRfYJqrf6xVf6+Y4Kd8Id8cHZHQ1U7M86LhOo249//5syQ95KgXHvCyaG6kqqRcBjIfjC/SKRcVhkGemDLo6ErqRAs0yVEc1dMNRXbO+TSHK+Xtcs70af3uQ0noDKT+Ggab+yO8H6NWsIul8ARtME7mibRueZxiaaXXEhXFVYxvuYd3x1sSISUy3q+06cUMWrspPS9hBwjLIu7FosqMEMCGsTHn130etp9YvZX6aJMQEAAA=" + } + }, + { + "ID": "f920df4f148efdc4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/1N1eJMTJfcLMk0tHZN6nVHfrYcF?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a5cb5ed07521f498e619afa90b1390c4/14427045531911076229;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/1N1eJMTJfcLMk0tHZN6nVHfrYcF?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:37 GMT" + ], + "Etag": [ + "tKUAFGpU2DwVbVWReDXknA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "a5cb5ed07521f498e619afa90b1390c4" + ], + "X-Google-Backends": [ + "[::1]:4190,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/11.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/11.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/11" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+51cs9VUS4AETEx6GiIhj6mB4izGlO8Oxrq3tWQgh/HfbMsSXnn6Xc87X7ltBQMpCZOQ6IKti/VOD3l2sAZ/dJQFTczS2KCkMkEvnBqRr58aHNBzfqbQ32i5Xy5cERq+lCAeDo8uwb6io9e0tsjgvgGfG4g+Pg4b3mqAVuIl558s3NzTulKfH0WO4+C9UMvNCnEZROIxuSSMdfP2058Fn2MhVAjloEAzOSZSWG2B475+c1ZwBtpXU2L7q95otrvOod+MuTGeLac6iWdnByXvcF8tJrt/Y+OTlklEspHD2dE7+tqNEyhO5NX4OOXPDHYJ50pKBMeC3dMgp742sFAd0cVHX4GlG7V9OCmy41qH1C3MN7s62AQAA" + } + }, + { + "ID": "874afe309732d338", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon89c368b14ea346b22ac8c51c365febe6ce516c59/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a5cb5ed07521f498e619afa90b1390c4/4435953046892354713;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon89c368b14ea346b22ac8c51c365febe6ce516c59/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:37 GMT" + ], + "Etag": [ + "4ECZ+QCznL0WtspmF+ng8Q==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "a5cb5ed07521f498e619afa90b1390c4" + ], + "X-Google-Backends": [ + "[::1]:4080,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/1.esf,ybjj195-v6:9892,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/19,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:ed13::]:4001 had response of OK with response time of 0.014510631561279297 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybjj195-v6:9892,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/1.esf,ybjj195-v6:9892,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/1" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTFxdY7SDnSuyvMxCC8pLsh1085Ltwi0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNxnqGJoamlqm6FuZKcMlaKCuWC8aL5arlAgDIFZ7RxAAAAA==" + } + }, + { + "ID": "8751488298b3973d", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "243" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "258f386bcce3ba939f6f526116889f87/2827891615196471746;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IkJPT0wifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiJ0cnVlIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJoY2J3M0t2QWJMRkNVRnVpbFFHYkN3RUp3WHYiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:38 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/bZoOlMd2T3knWU7AcE77ip3O4dA\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vned124:4463,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/80,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ha08W9myK4WOqwWy47aYCw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/80,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/80" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB896+wrq+JwWC+IlWpa5PIrWu3Nk6iNJV1HAu+GHMYDiM3yn/vcUA+mqrtC3A3Mzu7o+Wh00VbmgTorIt8Gu0LyI7v7pmPTgQAHEcVcIcsbWuNf8IR3HiQb+LNZZjcjter8kYdKP4tm8dfAs3Tt8n1yhoS17Joqs8HwfAOyTpUlg+KmAA/TVnGTy1TO1stexvil/rnw9CfXoxWFwWNv136o9L9VN4cpDCHOJzSZFvJN5yn+ZmilGXZixiLYsApzXuE7ZS2ceWgKWnG7oHwXPnNThFD5cpfDM9jRjCnLHm/WkpzIVhACBkkBEQDD51uFzXVJ38aqBJJVY3+Y7Yuav0qsrDsdB8rV8KSkEZF1kLSVk7XHJ5PVTyime65rCf6gZzTRAo97Mdt0//TdqXGHOfQcNbEDm3VVx2/b/uhqoUQGtaAgGqoNhjqAEIzsC3TxK2aV4a1FicsAcckZqDp/dAOHGIYphGYuu8Ymh0EqtUnpu8QRxsYqFI/1u2TDDCHMc1TltM2mNHCHXruenKxnrnu2B03o5YZfUO9XkwE01usZiMhaYhpRpngyrgmM89dDEfe5KpFixymEGFyXO5jwQhxnMPJc8ZfcYZ3wCHLBfhdDloHKuq2kHdMn4OugqjP6ON8PkX1ZT3fS9EVjotXqkNzgXgm3o2s8/T80ZEfckFyjnmRt5tRnaRQjD2bzC7RCxYV60CemDLeajXoTgr6hq5aqq7plq3r9UYKUcbf4o7Rr/HXayitd5DwJgK0dKfuyEMvehX5ZmvYYVqli3TT1B1H1/sD41TsFVf7bMcdrFnOlugRieh+s9/Gts/8TfEhgAPELIWsF4kiB0oAE8KKhFd/POo8dn4BrEgVg7oEAAA=" + } + }, + { + "ID": "5b89cd29e4e5bbd1", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hcbw3KvAbLFCUFuilQGbCwEJwXv?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "29e13ae6821680e30b3351a7fa71658b/1291886673748764139;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hcbw3KvAbLFCUFuilQGbCwEJwXv?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:38 GMT" + ], + "Etag": [ + "4rPuvehwDHqWLJx2Gi/3mA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "29e13ae6821680e30b3351a7fa71658b" + ], + "X-Google-Backends": [ + "[::1]:4287,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/128.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/128.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/128" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+369Y6qtEBKOJCQ8DB4gLN0M0McZs3dkodutozzYJ2X+3LUN86el3OT3f6dFxXfLN8pg8uiRi6b4EebhKAVfmsgZVclS6FCJXQK6NGzBMjftOLssKtvXTdP8WzH56E3bTz7zB4ORSdAtZqH1HjTROGPBYafxhsdvyVsvDDMyLSffLNrc0HgpLDxeLwPfm/6VMxFaab4LAGwY+aaXG1k99NjbFTkRrSEBCTuGSpZBiBxSf7dJxySlgpxASOw/3vXaK6TzpWxrV/ZfKi4LxaDMuGV9NolHtz+r36uzlgobIRG7sm1fyNx0FhnwtarM2uSUXbnhAUEspKCgFdkqXnPOORFZwQBMXZQmWpqH+zSnDlnMa5xcRj3MLuAEAAA==" + } + }, + { + "ID": "82f60b6de4d520d8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anone96c6d231f8d9c5565d63b9528dd071c6b9c9245/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "29e13ae6821680e30b3351a7fa71658b/9674918822254840063;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anone96c6d231f8d9c5565d63b9528dd071c6b9c9245/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:38 GMT" + ], + "Etag": [ + "H4rCBIQmGR//+MI3e/zwHA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "29e13ae6821680e30b3351a7fa71658b" + ], + "X-Google-Backends": [ + "[::1]:4205,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybon9-v6:9872,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/26,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:cd57::]:4174 had response of OK with response time of 0.01285243034362793 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybon9-v6:9872,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybon9-v6:9872,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfEwKXJ28gzMdQ/S19f29TRO1a8q93C0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNJUWlqUpw0VooK5YLxovlquUCACnXEAW9AAAA" + } + }, + { + "ID": "a5853998eb710460", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "247" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6d12bc536ea7c8dcf0a820d3ef69d611/8138913880790355240;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IlNUUklORyJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJ2YWx1ZSI6InN0cmluZyJ9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiQmVNNE1RUWEzRjAxalkzS01BNENObDNhdmVuIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:38 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/zkb-LFVNYZ-wXhXMUC0KNr4mbRs\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnnk5:4167,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=hq08W-GAH4iOqQWKlL3gDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/181" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB896+w6Gscjm+IVKWpTSIrNmpsnCZpKuvAC74Yc5g7bDlR/nuPw+Sjqdq+IO5mZ2dnWJ46XWVF8oVy0lUikm4qKPefHmikHAkAOE5r4F5x9JUzeIQ9+JnJltnyIsnvBvPZ7gaZ6uMq6o3Or4Pbu97uZnkznvXRZVCa62jC7hXZh8j2iyqLgfcKWvKeY+sns+nxVxib46srbJwj7eHWuByfmf0gM/AWcklkkCUjkq9q+pLzgp2o6m63O04pTTPABWHHMV2r7eDqVleLkj5AzJn6m5wqTDH1L4KnGY0xJzT/PJtKcUGYQAIl5DGIAZ463a5y6D78k6GaJFkN+g9vXaXVq4uFZKf7XKvGNE9IWpUtJGWlu8Ph9VTHI4bpnsp+Yh5gnOSSGOIoa4f+n7FrNuaYwaFmHruJiyLkRZobJUhPILEcMwZkIRcsZEJiL1zHtnHL5rVgw8U5zS3HtvQFIEAR9qzEQwukO5YGmmaBZwNa2C42NFNXavZzM35cAuYwIKygjLTB9Cf+WejPh+fzwPcH/uBgdVeSD6XfJ0NRGU5mQV9QDoVFSaiolXENg9CfnPXD4XWLVgxGkOJ4P91koiLBGYOj14y/4RKvgUPJBPhDGm0CFX1bKNwXr0HXQTRnZRpOhsGF0lw3Dt/SrnFWveNtDxcK4yXJ05bYeXn+7MgXuSSMY16xdjvqk6QK60Ej+lJFxErEL5Uy4no9yFoSNMtADjJ0w3V0rdlKQSr5R9zVUYO/X0UpvYach61tf+T3Q+XNrCLjcg5rTOqEFcO2Dc8zxLe3emK3ONLomntYd7xVbKRxSjbLzSpzIxotqy8L2EJGCyiPU9FkS2LAcUyrnNd/vdJ57vwCFMJJzr4EAAA=" + } + }, + { + "ID": "bf7ce80c8c1dab52", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/BeM4MQQa3F01jY3KMA4CNl3aven?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "32089f74b603f99f1ccbf5e555d82bd9/6530853544311198033;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/BeM4MQQa3F01jY3KMA4CNl3aven?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:39 GMT" + ], + "Etag": [ + "A/0MmBH1bhHbmmlQBumhfg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "32089f74b603f99f1ccbf5e555d82bd9" + ], + "X-Google-Backends": [ + "[::1]:4006,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/114.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/114.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/114" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0+DMBR+368g9dVFkEUTkz2MRWURFmHuwRhjSjncbCnSg2ZZ9t9tO+Z86el3OT3f6X7iOOSzbnNy55CsLr8G6HcXJWBiLimogaPSpZOtAnJp3IC0NO7FlRuLIPSyKsyE4EkwiKoo5/OjS7EKBNW+vUYaFzXwXGn8ZrEz8lZrqQDzYuF+2OaRxl1n6c1Lulo//leEzK2y3kbRIojuySgdbH3X58GGaGSWQgE9tAzOUbpeNsBwZXfOB84Ap53scXp7cz1OMZ1HPYB4FicJ9R9cr3n1n+LFbLnmPv2G9uTlklGsZWvs2w35m44SKU/lj9maeOTMBTsE9dxLBkqBneKSU96lFB0HNHGxH8DSjOrPDGscuclh8gvkziMLtwEAAA==" + } + }, + { + "ID": "94560f03923a3815", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon57652de0e0ba95f90d02751e115e96e0d68a3142/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "32089f74b603f99f1ccbf5e555d82bd9/14986222558530466917;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon57652de0e0ba95f90d02751e115e96e0d68a3142/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:39 GMT" + ], + "Etag": [ + "UlFFX1REA974RY1d0zLcvQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4205,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybra5-v6:9838,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/4,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e690::]:4077 had response of OK with response time of 0.013889312744140625 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybra5-v6:9838,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybra5-v6:9838,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQnNcXOLMAxydbQ0NwmKNEwxqPJJLgu0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNxSVFmXnpSnDxWigrlgvGi+Wq5QIAmNhh+b8AAAA=" + } + }, + { + "ID": "1799ecd6ba4da29f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "252" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "535c7dd03285ea33f6bae702d8d44b2a/13378162222051309710;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IlNUUklORyJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJ2YWx1ZSI6IuaXpeacrOiqnlxuIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJqQ2xYNXFDaWJ2c3pkN3lRdjRqTE9MbmlNcE8iLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:39 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/ikt64B_Ye-8QAGU9ndAEX6OJvZg\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnd8:4477,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=h608W-GzDov1qQXaw6mACw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/176" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ty27TQBTd5yuiYdvUjt+uhEpITBUUUpo4pZSiaDy5diZxbMczTpRW/Q42iB0bxHdVfAbjcdwHIGAT+c495557jm5uGk20pMkMHTVRQKN1Afnu2SIN0IFoAMdR2bhCtra0e9ewAy822Dyen4TJZW862V6ohkKX3DJeTt9DyznrnEzcZNbxLqzT15vL6ArJOVSOnxUxAd7K0py3bEs7mowPF934wlx3abBh1zN7d7YxFoPTQULfZKeSyCAOBzRZlvQ55xk7UpTtdnsYpWkUA84oOyTpSqkXVzaakuXpAghnyi9yijDFlL8IHscpwZymyfPJWIoLwghCyCEhIBa4aTSbaD+9/ydDJUmyqu4/vDVRrVeChWSjeVuqkjQJaVTkdUvKSnf74qEq4xHLNI/lPLEPME4TSfRxENdL/8/aJRtzzGCPmRIndNRAdYO2E4SqFkJo2gYB1VQdMFUDQmvm2JaFazYvBSsuToQFk4SBK0aYqmoRxwkNsHGouY6NZ6Hjaq6tY/ECqGTfVuuTHDCHHmVZymgdTHfkdXxv2n81HXpez+vtrW5z+hv03agvkP5oMuwKyh6Y5TQVWBlXf+h7o07X75/X3YLBACJMduN1LBAhjhkcPGT8Fud4BRxyJpofpNEqUDG3bvm77CHoMoiqRmN/1B+eoOq5cviYdo7j4glvs39Ad5++3n3+/uPbl6ukZjfufz825Ie8FMYxL1h9ImUl+cL/sFK+R1FxF+QeKXMub4SuJKFt6qqt6pru6rpbnaYg5fz3vqHvj/zpPUrpFSTcr717A6/ro0e7iqDzKawwLWNGumXprlBrG2ZLHBhX2+mKu1iz3SXRIxLR9Xy9jJ0gDebFixlsIE4zyA8jMWRDCWBC0iLh5V8fNW4bPwFoW8OawwQAAA==" + } + }, + { + "ID": "d0dc660e27a2f207", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jClX5qCibvszd7yQv4jLOLniMpO?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d803f69f8e36559348944a072737299c/11770100790355492278;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/jClX5qCibvszd7yQv4jLOLniMpO?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:39 GMT" + ], + "Etag": [ + "R8AZG79yhMb4lcDHizTw9w==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "d803f69f8e36559348944a072737299c" + ], + "X-Google-Backends": [ + "[::1]:4257,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/17.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/17.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/17" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q32+CMBB+568g3etM3OLGXOKDskVNUCdqsmxZFigHlhWK9JCg8X9fW3Hupdfvx/W+69GybfLD8og82yRkya6CsrlJAJf64oOsOEpVCpFLILfaDRgk2u0/DT/GTr/ZzsIepy8TdljX/XowOLsk3UIWKN9RIYVjBjySCn8abLe80fIgA/1i3P02zS2NTWHo1dqfzsf/lUxERplvPG848l5JK51M/VLnyYRIRehDDCXkFK5RilKkQHFqdo4qTgE7hSix4zzet1N051lPXf7+sHNZuJeHyGmW+17qLbyczYrFxcsFDZCJXNs3K/I3HQUG3Be13prckSs3ahDkWykoSAlmSpdc8roiKzigjotlBYamgfrMCcOWs07WLwHf0qq3AQAA" + } + }, + { + "ID": "501b30683beaa82c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonf5cfb9f805006c88f4e7af2987adf892973ae7ae/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d803f69f8e36559348944a072737299c/1778726834654961867;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonf5cfb9f805006c88f4e7af2987adf892973ae7ae/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:39 GMT" + ], + "Etag": [ + "mQl8gDuys0qc1iYRrYsGng==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4205,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybfx10-v6:9879,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/10,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:488::]:4004 had response of OK with response time of 0.013629674911499023 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybfx10-v6:9879,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybfx10-v6:9879,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqckNzLFIdymtLDYoTDbMjAwqiix2z0u3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNz6YvfTZnzYtV82LylOCStVBWLBeMF8tVywUARXBePcQAAAA=" + } + }, + { + "ID": "1de68ccc4a522b3b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "245" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "927cb4b9b4f5c925d42183b1f4febaf8/170665398680954100;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IkZMT0FUNjQifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiJOYU4ifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IkVoeWZTN1dJUUVyTE1wOEZMZ0w5Z0NsdDhGVSIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:40 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/8YAlz_drqrhoWbohcvAKDCwsqjs\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbg127:4174,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iK08W8A-hoqqBdPKpPAF" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/187" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ta2/aMBT9zq9A3teWvB9UmjoEYUJjbIPQ7tEJOcZOXEIcbAOiVf/7HCfpY522fYlyc8+559yjm/tOF2xosQYXXZDQdLfH/PTmliXgTDWwhGnVuAGBvQlGd/iEo9wVWZ69J8X30Wp5/Gq6RvhtkN+t1nzHM3adsAwdBh9Gw6PY3YoboOdQPX69zxGW5yXj8jzw7YvlohdlJ7IIridfIj79WIbjaTrtp8NchuOlJgqckyktNhU9k7IUF4ZxPB57KWNpjmFJRQ+xrdEaNw62UXJ2i5EUxm9yhlpKGH8RvMwZgpKy4u1yocUVYY4J5rhAWBm473S7oJk++dNCFUmz6u4/duuCVq8CK8lO96FSRawgNN3ztqVl9XZN8VRV8Sgz3Us9T/nBQtJCE2OY5K3p/7FdsaGEAjeYFQpJaCZmP7HChJg2wcQLXIRNzwyxZ7qY+Osw8H3YsmUlWHNhwQpEvCQwfZJ4loXCJCBkTULfhMTs257vrk3sEM/zE1CxH2r7iGMo8YiKkgnaBjOcR4M4Wk3Gq1kUjaJRs+qR01fQ6/lEIeP5cjZUlAZYcsoUVsc1mcXRfDCMJ1dtdy/wFKcQnRa7XCEIzAU+e8r4M+RwiyXmQjV/6EXrQNXcthWfyqegqyDqGoynnwax74L6e73ic94VzPcviIfmA5jBWcvqPD5/dvSLPhEhodyL9jaqSvPU4rPJ7D14hqLqINAjUgdcHQfdaoLlOWZgOrZrWpZb36Qicfm6b1vNdb88RC29xYVsQgCLaBoNY/DMq0qYr/AW0ipf4Pi+0+87juV65+qypGmxrexDO+hvkJOilO6y3SYPE5Zk+3drfMA5KzHvpWrIgSIMEWL7Qlb/POg8dH4BvBpyA7wEAAA=" + } + }, + { + "ID": "fd730990cf97f92b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/EhyfS7WIQErLMp8FLgL9gClt8FU?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6630108a5b09b54bfc51bff357969302/17009067660917926172;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/EhyfS7WIQErLMp8FLgL9gClt8FU?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:40 GMT" + ], + "Etag": [ + "JpTEXBvnRnhXAtH2MWU2fg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "6630108a5b09b54bfc51bff357969302" + ], + "X-Google-Backends": [ + "[::1]:4121,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/56.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/56.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/56" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qskuAdBEx6AjIApKoMFEmPM6G5j2LW1vWkWwv9uW4b40uv343rf9dgJAvJZiow8BmRXFl816OamAFy6Swym5mhsUVIYILfODZgWzv2k1tF2/C1isd+OcBYuNkmYF8Ph2WXYHqrU+o4WWZyXwDNj8ZvHQct7TaQVuBfz3odvbmlslKen9GW0/i9UMvPCc0LpaEwj0konX9/tefIZDnIXQw4aBINrEqXlARjO/cpZzRlgV0mN3f592E5xnWc92jf5qr+ZLyNNF2owpQV9KCYcB9Pk4uWSpVhK4ezJivxNR4kpj+WPW5rckSs3bhDMq5YMjAE/pUcueSeyUhzQxUVdg6dZav9yVmLLdU6dX/7O6n22AQAA" + } + }, + { + "ID": "e766e601920d3c30", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anoncf5b706fb511c8b7ffdf860af092564d0e3f556b/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "6630108a5b09b54bfc51bff357969302/7017974076421065521;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anoncf5b706fb511c8b7ffdf860af092564d0e3f556b/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:40 GMT" + ], + "Etag": [ + "SVJYPuuqfSRD8gVN5t5V/A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "6630108a5b09b54bfc51bff357969302" + ], + "X-Google-Backends": [ + "[::1]:4331,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/39.esf,ybnc13-v6:9808,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/95,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:eb08::]:4064 had response of OK with response time of 0.013359785079956055 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybnc13-v6:9808,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/39.esf,ybnc13-v6:9808,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/39" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQkO84oMKC0tTAsOcrFID/MzLTEN03e0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNfol+SnDBWigrlgvGi+Wq5QIAQkPq57wAAAA=" + } + }, + { + "ID": "2f0ab33563bdef61", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "244" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2e32f30940be5a41d41d3e4d90ce5c61/5409913739925131354;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IkJZVEVTIn0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoiWm05diJ9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoicWhremYyRXlORXNJbWN6Z1IyeEZ4N0VwWDNmIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:41 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/ejB1L-5wDydhNKEErId89CSXGow\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnmh22:4372,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iK08W5jlL9b-qAX8y7zwDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/17" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB896+wrq+xOcAYE6lKE5tGqJZV2ThN01TWcSz4YuAwHHacKP+9x2Hy0VRtXxDLzuzMjpbHThdtWBai0y4KWLytoDh8uOMBOpENECSuG7fINjb25AEO4CaDcp2sL6PsZrJa7q/xQIO7C33as/aTQ7iefXHdwgtHznhxfcn3t0jNYWp8WCUURC/nhejZQ+N0uehv15uHyHAPM7f0UvoQz437z/e2m1+bkSKWkERTlm1q+lqIvDzVtP1+3485jxMgOSv7lKdaa1zbGVpe8DugotR+k9PkUqX2F8GzhFMiGM8+LhdKXBLmEEEBGQVp4LHT7aLjdO9PC9UkxWq6/9iti1q9GiwlO92nWpXyLGJxVbQtJau2OxYvVR2PNNM9U/OkHygFyxTRJ0HSmv4f2zWbCFLCEbOio2iEA+wE+iiIsBFBZNkDCtjCI7DwAKJhOLKHQ9KyRS3YcEnGM900A0xgSA2iDzB1DBuHjoMd2wgj27Jsg4a6HmIL1eynxj4tgAiYsDLnJWuDGc/dc99deZ9XM9eduJPjqvuCvYN+m3sS6c+Xs7GkHIF5wbjEqri8me/Oz8e+d9V2qxKmEBN6WGwTiYhIUsLJS8ZfSUFSEFCUsvlDLdoEKue2Lf+QvwRdB9HU6OK77y5Q87VZ8DXriiTVG9ru+AHdpM6upXWenz876kVdSCmIqMr2NOpKEeXeM292iV6hmLwH+oxU+da3wVJF0C0T29g0BtixzOYkJakQ7/o6bo/77R0q6RQyccwALdypO/bRK68y4GIFKWF1vMgcDk3HMU19YPXkYQms81Q4xLCdDTVjGrPtertJRgEP1tWnEHaQ8ByKfiyH7BgFQimvMlH/8qjz1PkFiinQNbsEAAA=" + } + }, + { + "ID": "47a52b3e7d8be7b4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/qhkzf2EyNEsImczgR2xFx7EpX3f?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2f512455d4674cf9f20e441a65ad6fee/3873908798460712322;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/qhkzf2EyNEsImczgR2xFx7EpX3f?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:41 GMT" + ], + "Etag": [ + "EpfIGlutYGjXLq5RxLVWBg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "2f512455d4674cf9f20e441a65ad6fee" + ], + "X-Google-Backends": [ + "[::1]:4102,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/126.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/126.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/126" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30+DMBB+568g9dUlE6NLTPYgBicJWZS5ucUYw8rBYIUWekTYwv9u2zHnS6/fj+t916Nl22SflTF5sMk2S6sG6u4qBXzTlxBkw1CqIngpgVxrN2CUarcnEn/GGtzM8nVQ3YVtsPpw0+n05JJ0B0WkfEeFFE4yYLFU+NNge+CNVkYF6BeT8bdpHmjshKHdzbu3+C8UPDbCfBkEj27gkUHqTf1SZ28y5HwbQgI1lBQuSUTNc6Dom5XjhlHAkeA1jib3zjBFd570arc/JI7XzT3pF/SQhk773E48sb5Nzl7GaYQZL7V9uSB/05FjxEL+o5cmN+TCuR2CfK05BSnBTBmTc94nXggGqONi3YChaaT+8iXDgbN66xcwCebttgEAAA==" + } + }, + { + "ID": "f71b1e284547eb4f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon133b0ae6c2a140c9270d990972df75572cd11d05/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "2f512455d4674cf9f20e441a65ad6fee/12256940951278401431;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon133b0ae6c2a140c9270d990972df75572cd11d05/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:41 GMT" + ], + "Etag": [ + "rRrYJfVSi0l95NdarS6jeA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "2f512455d4674cf9f20e441a65ad6fee" + ], + "X-Google-Backends": [ + "[::1]:4078,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35.esf,ybbt9-v6:9811,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/23,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"el\" allowed_cluster: \"na\" allowed_cluster: \"ef\" allowed_cluster: \"ws\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"os\" allowed_cluster: \"ej\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1101::]:4358 had response of OK with response time of 0.012731313705444336 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybbt9-v6:9811,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35.esf,ybbt9-v6:9811,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqSkKKor0SgsLzjTIsTT1S0ksCjbLSnW0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNUbmWZUpw0VooK5YLxovlquUCAGVpCMm9AAAA" + } + }, + { + "ID": "78874cb778baae9e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "276" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e4de6fbc609d228418f8f9762de8fd5e/10720936009813982399;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IlRJTUVTVEFNUCJ9LCJwYXJhbWV0ZXJWYWx1ZSI6eyJ2YWx1ZSI6IjIwMTYtMDMtMjAgMDQ6MjI6MDkuMDAwMDA1LTAxOjAyIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJrQVVFY3ZSVlFkWEtlcE5MRWpuRVpJTldLZ0oiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:41 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/56p1IYt5GZN59TwwUT3rXtWVcUc\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnqq6:4031,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/218,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ia08W9GsII6GqgW5-qagBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/218,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/218" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1UXXOaQBR991c429coy7c400kdpRka46SKJk3TcZblghsREFYdm8l/77JIPppOWx4YlnPOPfce7vDYaqM1S0PUb6OAxdsdFMcPD1mAzgQAnMQVcI9sbW2PfsIR3MQoV8nqIkrvRsv54RYbimnlqveNmxd3E9PxD4e5rxe3/GZB5/QeyTpMlg93CQXeybOCd2xL689n3fVg7tL9dPE1vL2EfDJ2H1L3zpvcXMZfpLCEJBqzdF3JV5znZV9RDodDN86yOAGSs7JLs43SNK7sNSUvsgegvFR+s1PEUKXyF8PzJKOEsyz9OJ9JcyGYQgQFpBREA4+tdhudqnt/GqgSSVWN/mO2Nmr8KrKwbLWfKleapRGLd0UDSVs53enwcqriEc20z2U90Q+UnKVS6JMgaZr+n7YrNeGkhBNnSXtRDwfYCdReEGEtgsi0DQrYxD0wsQGRFfZsyyKNmleGtZakWYptO9BszVAdTKDn0DDUNAuwZhhOhHUCtmaF2AwjVKmf6vZpAYTDiJV5VrImmOHUHfju0vu8nLjuyB2dRj0U7B31ZuoJpj+dT4ZCciLmBcsEV8blTXx3Ohj63qJBdyWMISb0ONsmghGRpISzl4yvSUE2wKEoBfhdDloHKuo2kH/MX4KugqjPyPeu3Jk/uLpGNVIP+Vq5IMnujXR/eoE0rFodrHc03MZGX9P62Oni6jI7WO1jrSnZer7/aMkHuUElJ3xXNqtTnWRRkcvEm1ygVywm9oU+M2X+1e6wjRSopo5trIuPaDpmvbJCVPD3uI1xjb/dU2m9gZSfMkIzd+wOffSqV/EBiiVsCKviR7pl6Y6j66phdsTicaxmG+4QzXbWVI9pzLar7TrpBVmw2n0KYQ9JlkPRjUWRPaNAKM12Ka9+Caj11PoFY3CImdsEAAA=" + } + }, + { + "ID": "610f150055d6f405", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/kAUEcvRVQdXKepNLEjnEZINWKgJ?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5eb2602ba36ec93e7789b6234bb8630a/9112874573823197672;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/kAUEcvRVQdXKepNLEjnEZINWKgJ?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:41 GMT" + ], + "Etag": [ + "jZMH3kPCtFQ44xqDZj2aig==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "5eb2602ba36ec93e7789b6234bb8630a" + ], + "X-Google-Backends": [ + "[::1]:4067,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/92.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/92.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/92" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+31+x1FdJEIkmJjwAzoAMwgaowRhTumN269ax3sSF8L/bliG+9Pr9uN53PTiuS1KeR+TBJRse7yoo66sYMDCXEFQlUOlSyFwBuTZuQBobd7Kejm7T+RCfgm73Z/e4TjqUx73eyaXYF2RU+w4aabzlICKl8bvFbsNbLacZmBe37U/b3NBYF5ZejqfeYtmfzv+LmYysOFv5fn/ge6SRjrZ+6PNocyRyE8IWSsgZXNIUpUyA4diuHVWCAbYKWWLr/q7TTDGdJz3trzz2Hb4E0dsEipnvJbm3Hs9eJ/Hz2Ssko8hlbuyrBfmbjhKpCOXeLE5uyIUb1AhqXkoGSoGd0ibnvEOZFQLQxMWyAkszqv9zxLHhnKPzC5thC/W6AQAA" + } + }, + { + "ID": "1e15998ee2b47a90", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon077b2724190ae89cdd226e02449f03ae726d05df/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "5eb2602ba36ec93e7789b6234bb8630a/17568244687554028541;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon077b2724190ae89cdd226e02449f03ae726d05df/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:42 GMT" + ], + "Etag": [ + "2y5FEPF26ue5VUbSEiHstQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "5eb2602ba36ec93e7789b6234bb8630a" + ], + "X-Google-Backends": [ + "[::1]:4246,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybes23-v6:9831,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/31,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"ej\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:f0d7::]:4151 had response of OK with response time of 0.014034748077392578 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybes23-v6:9831,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40.esf,ybes23-v6:9831,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/40" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqTGqNHVzDXAzMitNNQ0LTQp2zfQoLgm0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNJqYWJqaGJiaWegYgYKoEV1ILZcVywXixXLVcAChzZ+3KAAAA" + } + }, + { + "ID": "d343a70ecfb2a869", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "249" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "cf664b097b0b60f8b4f3e04a7d7c9233/15960183251563309093;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IkRBVEUifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiIyMDE2LTAzLTIwIn19XSwidXNlTGVnYWN5U3FsIjpmYWxzZX19LCJqb2JSZWZlcmVuY2UiOnsiam9iSWQiOiJwOUF5T2d0QjJERGE1NDdCU21nb2xEc0k1dzgiLCJwcm9qZWN0SWQiOiJkdWxjZXQtcG9ydC03NjIifX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:42 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/bJTxJCLFMxTcKUbWHo43IYRZcxk\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnda124:4445,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iq08W7LbBtbHqQWl3oHYDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/83" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bMBR9z6+IvNcmOHxTaeraQDu6LJsS0m5dp8gYQ7wQTLCTNKv632dM6Mc6bXtBXO4595x7dLnvdMGSFgk47oKYZusNqfZvfrAYHMkGESirG7fA0ZeO/5PsSZCbfJEvLtLixp/Pdl+gqcWX0d3lcHT+8S7CH2bx9XtmGuHXyQ2+W94CNYeq8ckmx0T0SlaJnmPrx7Npv/RO958ycab7PrJM52y6ylju89DauYrISZ6OaLGs6QshSn6sabvdrp8xluUElZT3MVtprXFtq2tlxX4QLLj2m5wml+LaXwRPcoaRoKx4O5sqcUmYkJRUpMBEGrjvdLvgMD3800I1SbGa7j9264JWrwZLyU73oVbFrEhptqnalpJV2x2Kp6qOR5rpnqh50g/hghaKGKE4b03/j+2ajQTi5ICZYzd1YQy9eODGKdRTklqOiQm0oEssaJLUTlzHtlHLFrVgw0UFK9DATbCd6k6CcEqsxLVMM0EIph40oW5bKLHRwIE2qNkPjX1cESSIT3nJOG2DGU6C0yiYh+fzcRD4gX9YdVfRV9DrSSiR0WQ2HkrKAVhWlEmsiiscR8HkdBiFV213w8mIZAjvp+tcIlKUc3L0lPFnVKEVEaTisvlNLdoEKue2rWhfPgVdB9HUwK89NB+b/Z6TrlC+ecHaHj4AHQ7sHjR6OmzJncfn9456UWfCBRIb3t5HXSm6XH4cji/AMxSVR4EfkSrk+kDoShEGlgEdaOimrrtOc5eSVInXfcMzmv7LY1TSK1KIQxBgGoyCYQSeeZUpV3OyQrTOGBi2bXieYQxMqyevS8ABWwkP6Y63xEaGM7perJe5G7N4sXmXkC3JWUmqfiaHbCkmCGO2KUT934POQ+cXF2WR2cAEAAA=" + } + }, + { + "ID": "a8dc6756160cffbb", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/p9AyOgtB2DDa547BSmgolDsI5w8?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d3fc5993e4b626c1b4a2f615d1981041/14352122915067374926;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/p9AyOgtB2DDa547BSmgolDsI5w8?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:42 GMT" + ], + "Etag": [ + "f5Mjgr7N/0+qHm0dfZWOxw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "d3fc5993e4b626c1b4a2f615d1981041" + ], + "X-Google-Backends": [ + "[::1]:4236,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/75.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/75.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/75" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/02QUU+DMBDH3/kUpD7qIi7i1GQPIEu2BDdlLiYaY7pyIFgoa49Msuy723bM+dLr/e5/vf9157gu+S7qlNy7ZF3kmxZkd5YDPptLAqrlqHRoRK2AXBg1IM2NOvMfy1yO5pfe+WZaeWn29rr42Y7HB5ViX1BRrdvpTOdZATxVOn+3udtzW6tpBfZF79M29xi7xuIoeJn855VILZ+v4jgI4wnpS3sbP/S5txZKsU4gAwk1g5ORRooSGM7sxmnLGeCgERIHo5thP8V0HurNXdAtcgyHUUT961G4rHLBIzXzt7dHLReMYiFqI18tyd90FEh5IrZmZ3JFTizsENSTFAyUAjvFI0e/D6JqOKCxi7IFixnVXzktsGfO3vkF7VWYgLUBAAA=" + } + }, + { + "ID": "f6f950acd9873c07", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anona18dc6f27dacfe5d8544daa0f9040265ad6a1706/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "d3fc5993e4b626c1b4a2f615d1981041/4360747859888770915;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anona18dc6f27dacfe5d8544daa0f9040265ad6a1706/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:42 GMT" + ], + "Etag": [ + "h7n6B7xuJDQXjL/sYXKKgQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "d3fc5993e4b626c1b4a2f615d1981041" + ], + "X-Google-Backends": [ + "[::1]:4109,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/42.esf,ybn130-v6:9887,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/63,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:1983::]:4159 had response of OK with response time of 0.012872219085693359 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybn130-v6:9887,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/42.esf,ybn130-v6:9887,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/42" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqckwzzNzMq8o9XIJjMjy0S+OjPD2Tg+0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBkpwuVooK5YLxovlquUCANLexJDDAAAA" + } + }, + { + "ID": "df153a899c99256b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "254" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a60fd0c7d61a21dca87f4ea3c51ddbb6/2752687527687804043;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IlRJTUUifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiIwNDowNTowNi43ODkwMDAifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IjNPTmF0bW1UTm5jSGljNGN2elRHVjgxVWV5ZyIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:43 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/JCla8Ay2PySJOvNKxbCFTDWsfGM\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vni65:4255,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iq08W_GSM4qgqQXumri4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/122" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aMBR951cg77UlJgkkQZq6ClJG16YVhHbaOiHH3ASXfBE7MFr1v89xSD/WadtLFPucc8+9R9ePrTZas3SJBm0UsGhTQrH/cJ8F6EgCIEhUAXfI0tfW6AH24MYmX8WrcZh+Gy3mu6/Y1M6HMbFP9/r1fnZ+tfW+/AyGZ/7olofjyzuk6jBVflnGFMRxnhXi2Orrg/msY1x5RCSJ76X0M6Mm3T744xu7O4d9pIQc4vCCpetKvhIi5wNN2+12nSjLohhIzniHZonWNK5tdS0vsnuggmu/2WlyKK79xfAkzigRLEs/zmfKXAqmEEIBKQXZwGOr3UaH6pM/DVSJlKpG/zFbGzV+FVlattpPlSvN0pBFZdFAylZNdzi8nKp4ZDPtE1VP9gNcsFQJfRLETdP/03alJoJwOHAW1A5tHGAn6NpBiPUQwp5lUsA9bEMPmxD2l7bV75NGLSrDWktSOYJNbMskYJo92+j3jWUYkiVZmkQPjTCwDUIcA/rUQJX6qW6fFkAEjBjPM86aYIZT99R3F5Ozhee6I3d0GHVXsHfU2+lEMv3p3BtKyYGYFyyTXBXXxPPd6enQn9w0aMnhAiJC97NNLBkhiTkcvWR8TQqSgICCS/C7GrQOVNZtIH+fvwRdBVGfkT+5dFF9Wc/3WnRD4vKNanu4QNgc4N4A9zuW7WCMmwqt5++PlvpRu8IFESVvlqQ6qRoyAW/ijdErFpObQZ+ZKulqS1iiBN2egS1s6Kbu6Ea9nFJUiHe4gU2nxt9upLJOIBWHNNDMvXCHPnrVq4y6WEBCWKweh9wJxzGMrtk7lismcDdLhEN0y1lTI6IR26w269gOsmBVflrCFuIsh6ITySJbRoFQmpWpqB4/aj21fgENtlU3xQQAAA==" + } + }, + { + "ID": "105f922e9c95db23", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/3ONatmmTNncHic4cvzTGV81Ueyg?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0d6eb6f449d878b5a3f72887999da98f/1216682586223319476;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/3ONatmmTNncHic4cvzTGV81Ueyg?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:43 GMT" + ], + "Etag": [ + "cwWgSPsUKE+A+KqyO5Bc0g==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "0d6eb6f449d878b5a3f72887999da98f" + ], + "X-Google-Backends": [ + "[::1]:4004,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/43.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/43.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/43" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/02Q30+DMBDH3/krSH2cS5i/Y7KHYYhbhmwy0AdjTFcOZAJl7eGCC/+7bcecL73e577X+173lm2Tr7xKyL1N1nm2bUC0Zxngs76EIJsCpQo1rySQc60GpJlWs91rtlrKeO4NJoP5tl1cu8zJxuODSrJPKKnS7VWm8jSHIpEqfzO53XNTq2gJ+sXU+TDNPca2NjiaPXn/eckTw4PY9yeu75G+1Jn4rs7OWNjwdQgpCKgYnIzUgm+A4cxsnDQFAxzWXODw9uain6I7D/XLRUCxLKOgYtOcXbHvn+jx5W4UQ5sdtQVnFHNeaXm8In/TkSMtQr7TO5MROTG3RZBLwRlICWaKQ45+H3hZF4DaLooGDGZUfeU0x55ZnfULYOl+3rUBAAA=" + } + }, + { + "ID": "017bacec2901fe15", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonf8a874ae44583663dffadad4a2f3fb83aa93e6c3/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0d6eb6f449d878b5a3f72887999da98f/9599714734746172360;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonf8a874ae44583663dffadad4a2f3fb83aa93e6c3/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:43 GMT" + ], + "Etag": [ + "BSq3ihcvSWSGl2rlH5GXGQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "0d6eb6f449d878b5a3f72887999da98f" + ], + "X-Google-Backends": [ + "[::1]:4263,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/3.esf,ybmm68-v6:9859,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/43,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ws\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"ef\" allowed_cluster: \"na\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:5483::]:4465 had response of OK with response time of 0.014039754867553711 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybmm68-v6:9859,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/3.esf,ybmm68-v6:9859,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/3" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXEKLjTOzEguCw4Pds8xKsrxMHWPcA+0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNBiZWBqZWBmZ65haWBgYGSnAFtVBWLBeMF8tVywUAuUIXKsgAAAA=" + } + }, + { + "ID": "c14cb02f7f83eacf", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "269" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "4e42db056f656e77e9299c3a94d95093/8063709793281687793;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6IkRBVEVUSU1FIn0sInBhcmFtZXRlclZhbHVlIjp7InZhbHVlIjoiMjAxNi0wMy0yMCAwNDowNTowNi43ODkwMDAifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6InJwZXNjeFltZ3BMdWV5S2xZYjNQTXBQYnpRdyIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:43 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/DJMX2x0jJiN0gDqMiB9GWkrFtmk\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncs130:4254,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/28,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=i608W8HPJMjUqwWA67fYCw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/28,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/28" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U227aQBR85yvQ9jXg9Q3bSFXaxk5ECijlkkubCq2XY7PBN+w1hET5967XOJemavuCWGbmzJzREY+tNlqzZIn6beSzcFNCvv9wl/roSADASVgBt8jS1pb7AHvwIqNYRauzIPnuLua7a2wo7vnoWrvHd+dsjEN3M2JfnLOrdX7K4/UtknOYHL8sIwq8k6U571g9rT+fdvMMCnp/E4fZsIT91+jG1y9G2YX/8G0nhQVEwZAl60q+4jwr+oqy2+26YZqGEZCMFV2axkoTXNlqSpand0B5ofxmp4ilCuUvhsdRSglnafJxPpXmQjCBAHJIKIgAj612Gx2mD/60UCWSqhr9x25t1PhVZGHZaj9VrjRNAhaWeQNJW7nd4fHyquoRYdrHcp7IAwVniRTOiB81of8ndqUmnBRw4CyoHdjYx46v2n6AtQAC0zIoYBPbYGIDgt7Stno90qh5ZVhrSZImy6WDVSsgNlUptXRbtS0ILMs0dMcwVM02g0AjjmqiSv1Ux6c5EA4uK7K0YE0xJxPv88xbDE4XY89zPfew6i5n76hXk4Fgzibz8YmQHIhZzlLBlXUNxjNv8vlkNrhs0LKAIYSE7qebSDACEhVw9NLxBclJDBzyQoA/5KJ1oWJuA8322UvRVRH1G7kiw2ww8lAN1Du+Fl6SqHyj3B5+QBpWex2sdzTcxkYfm33c61q2gzFuhrWeP3+25Bd5OgUnvCyam6lecpwoZDwYn6FXLCYOhT4zZfHV0bBYClRTxxbWNUO3TL2+VSHK+XvcNswaf3ug0jqGhB/KQVNv6J3M0Kusovl8ATFhVe9I7/V0x9F11TA74uI4VtOYO0SznDXVQxqyzWqzjmw/9VflpyVsIUozyLuhGLJlFAilaZnw6r8AtZ5avwBmPySj1AQAAA==" + } + }, + { + "ID": "621f25e115b387e2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/rpescxYmgpLueyKlYb3PMpPbzQw?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7ab819031dcd77bb5d7d6c92009273b6/6455649456785753370;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/rpescxYmgpLueyKlYb3PMpPbzQw?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:44 GMT" + ], + "Etag": [ + "XMYplbpS4+CcI4B0cy6lDw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "7ab819031dcd77bb5d7d6c92009273b6" + ], + "X-Google-Backends": [ + "[::1]:4134,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/79.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/79.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/79" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+31+x1EclmUowMeGBAYmLw8CAxMUYs3XHHHZrbW+Zk+x/py1DfOn1+3G973pwXJd8FVVGHl2SFvl3DbK9ygFX5hKBqhkqXQSvFJAb4wZMcuN+XcSCpWI9vJ7SYOh7tB2xWTMen1yKfkKZaN9BI413BbBMafxmsdvzVquSEsyLO+/DNvc0tsLSs8lmvgkW8/9ayTOrvWzDcOKHc9JLna3v+uxsjD1PI9iBhIrCJYyQfA8UA7t1VjMKOBBc4uBhdNdPMZ0nXQpQ9CcucxHW0D6zOL1fLsQy/V01Zy/jNMGCV8a+XZO/6cgxYRFvzN7kllw4v0VQS8kpKAV2ikfOeae8FAzQxEVZg6Vpor/zqcCeczrnCAu0tcy5AQAA" + } + }, + { + "ID": "0fefd934b97521f5", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anondd9017fa8c1cc738187ef775439441285ff2a915/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "7ab819031dcd77bb5d7d6c92009273b6/14911018471021799214;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anondd9017fa8c1cc738187ef775439441285ff2a915/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:44 GMT" + ], + "Etag": [ + "8Z2L7JaSClRpuCHleqBmpA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4205,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybpa7-v6:9889,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/15,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"ow\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:db86::]:4115 had response of OK with response time of 0.014551401138305664 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybpa7-v6:9889,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23.esf,ybpa7-v6:9889,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/23" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqbGIMvIx90oMds4JKih19shJLXTKLXC0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSDNRgaGZroGxrpGBiEGJlYGplYGZnrmFpYGBgZKcLW1UFYsF4wXy1XLBQAKtxhF0wAAAA==" + } + }, + { + "ID": "3422ed794c35e687", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "254" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "042f57b1ff6ac96a3ce150531874ac7c/13302958138820766807;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsidHlwZSI6Ik5VTUVSSUMifSwicGFyYW1ldGVyVmFsdWUiOnsidmFsdWUiOiIxMi4zNDUwMDAwMDAifX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6IlRZVWNKUU1JZ2dDMzF2Tkd2OVdmNG9qUzBKMiIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:44 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/7FUxMh_zCVMj21AS8MLUZgr3du8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vns132:4112,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jK08W5KDGZDiqAW31KGwBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/62" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW+bQBB896+wrq+J+QYTqUojm0REttXakLRpKus4L/hszGE47DpR/nuPw+SjjdrygLibmZ3d0fLY6aI1zRborIsimmwrKA4fVixCJwIAjpMauEeOvnaGD3AALzXLZbq8irO74Tzcf1VNxbkMf46X84fBzXilaxez/ngU3iWFsaj690jWobL8okoJ8NOcFfzUsfWzcNYLvoXk+svYT5KBoe0mVzv3NjbZaqZe61JYQhqPaLau5UvO8/JMUfb7fS9hLEkB57TsEbZR2saVna7kBVsB4aXym50ihiqVvxiep4xgTln2MZxJcyGYQgwFZAREA4+dbhcdq/vvDVSLpKpB/zFbF7V+NVlYdrpPtSthWUyTqmghaSunOx5eTnU8opnuuawn+oGS00wKAxylbdP/03atxhyXcOTMST/uq5HqRlo/ilU9hthyTAKqpfbBUk2I7UXfsW3cqnlt2GhxxjLVtLEZ69gyDN02sE7Adk3H0Q0txpYZE8dxHc0Fgmr1U9M+KQBzGNIyZyVtgxlMvYvAm/uX84nnDb3hcdR9Qf+g3k59wQym4WQgJEdiXlAmuDIufxJ404tB4N+0aFXCCBJMDrNtKhgxTks4ecn4My7wBjgUpQC/y0GbQEXdFgoO+UvQdRDNGU3CsTf1B6i5b0Z8rbvBafVGuDteIE3vGaalyqeVd57fPzryQ+5KyTGvynZJ6pMsIBKY+JMr9IpFxWaQZ6ZMut4SumkcLUN1VEM3ha3VLKcQFfwd3NUa/O1GSusNZPyYBpp5I28QoFe9iqiLOWwwrYNGhm0brmsYmmmdihXjqsY23MW6466JkZCEbpfbddqPWLSsPi1gBynLoeglosiOEsCEsCrj9c+POk+dX01SlzHFBAAA" + } + }, + { + "ID": "e78c86cb6f13cfb8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TYUcJQMIggC31vNGv9Wf4ojS0J2?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "33531890d844f775e6cf10ce5ba0b01b/11694896702829982080;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/TYUcJQMIggC31vNGv9Wf4ojS0J2?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:44 GMT" + ], + "Etag": [ + "MBokGBMhI1J1MgKbTMGwhQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "33531890d844f775e6cf10ce5ba0b01b" + ], + "X-Google-Backends": [ + "[::1]:4399,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/96.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/96.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/96" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q206DQBB971eQ9dUmoEajSR+kaZBaGqElxhhj6DLcCgyyQ5um4d/d3WLry86ey+yc2ePIMNg2r2P2ZLBNnv500B6uUiBfXQIQXUlClgZrAexauYGiVLk9G7eO7WWuNbe89HWz9px95k8mJ5fgGVSR9B0lkjjJoYyFxJ8aGwOvtTqqQL2YmN+6eaDp0Gh6GXqzwJ3+lyqMB2mxeLYXMzZIva5f8ux1igI3ASTQQs3hkqVpsQBOrl467koONG6wpfHD/c0wRXWe9PVHyOe+56bp9NbaLZ3d43tyh8XKnJ+9JfKIcqyVPVyx83RCisoA92ptZrELZx8IxFuLHIQAPcVkf3mnWDUlkIpLbQea5pH8zZecBm7Uj34BRlD1sbgBAAA=" + } + }, + { + "ID": "a31c1af903a3ccf3", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon046a4f2a533263a2ce69477231fa54fc779719ec/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "33531890d844f775e6cf10ce5ba0b01b/1703522747146294164;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon046a4f2a533263a2ce69477231fa54fc779719ec/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:44 GMT" + ], + "Etag": [ + "JE8J+J/9VDKBzEnXyKGG7A==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "33531890d844f775e6cf10ce5ba0b01b" + ], + "X-Google-Backends": [ + "[::1]:4387,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0.esf,ybny4-v6:9814,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/6,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"wo\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"ow\" allowed_cluster: \"wa\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"iw\" allowed_cluster: \"ej\" allowed_cluster: \"el\" allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"jx\" allowed_cluster: \"oj\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:e508::]:4162 had response of OK with response time of 0.01287388801574707 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybny4-v6:9814,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0.esf,ybny4-v6:9814,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/0" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqfFytfDS9tK3DHPxdqpyzYuo9HZ3N3e0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZWDNRnrGJqZKcPFaKCuWC8aL5arlAgApC+ycvwAAAA==" + } + }, + { + "ID": "4b819ddcff20817c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "259" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a9fab31edb3d9de6c3c3dd4a58d68cc8/95461311155509437;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsiYXJyYXlUeXBlIjp7InR5cGUiOiJJTlQ2NCJ9LCJ0eXBlIjoiQVJSQVkifSwicGFyYW1ldGVyVmFsdWUiOnt9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiQTlHVmZoeGc1ZDFmZW9UMUpScWZnVm51SjdTIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:45 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/zYZRrtGOfypywBeu_H7m2H811x8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703485000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnw69:4198,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/150,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ja08W8niAcTbqQXC2IDoDw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/150,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/150" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXXOaQBR991c49DWR76/MdFKr1JjJ2A5i2qTpOMt6Fzcii7DEmIz/vcsiNU06bV+A5Zxzz72Hy3Onq6xotlDOukpMk00Fxe7dPYuVEwEAR0kN3CmusXKHT7CDILXKZbockex2OJ9tv2mW+nRzGxZ89Jns8t32I1TzC3dtXHi6/ujdKbIOleUXVYqBn+as4KeuY5zNpr2+P7omy8fEXugEWKRfhhuSXGfVpTuVwhJSckWzVS1fcp6XZ6q63W57CWNJCiinZQ+ztdo2rj4Yal6we8C8VF/ZqWKoUv2L4XnKMOKUZe9njbkQhECggAyDaOC50+0qh+rjPw1Ui6SqQf8xW1dp/WqysOx097UrZhmhSVW0kLSV0x0Ox1Mdj2imey7riX6g5DSTwgjFadv0/7RdqxFHJRw4c+wRT4s1P9a9mGgGAWK7FgbN1jywNQuIs/Bcx0GtmteGjRZlLLOI7RMTdD92bQfppm0gRyOWFpuGZTmGvyDI0zTHUWr1vmkfF4A4DGmZs5K2wQzCoB8F8/Gn+SQIhsHwMOq2oG+oX8OxYEbhbDIQkgMxLygTXBnXeBIFYX8Qja9btCrhChKEd9NNKhgEpSWcHDP+ggq0Bg5FKcDvctAmUFG3haJdfgy6DqI5K/0w7N8c0hGvUVGg3SvukSw6cyzl8HrfeXGT1x8d+SD3o+SIV2W7GPVJVhBTT8aTkfKCRcU24F9MmW69GXQtBbptaq4mPoetu26zkEJU8Le44eoN/vsWSus1ZPwwlTINroJBpLzoVcRbzGGNaB2uYjqO6fumqVv2qVgrrulszX1kuP4KmwlO6Ga5WaVezOJl9WEBD5CyHIpeIoo8UAwIY1ZlvP7hlc6+8xMm5DSyuQQAAA==" + } + }, + { + "ID": "360bc34b594418d6", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/A9GVfhxg5d1feoT1JRqfgVnuJ7S?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a053b1a4e7ca0d7e6c4fbb653a69c75f/16933862473914342630;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/A9GVfhxg5d1feoT1JRqfgVnuJ7S?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:45 GMT" + ], + "Etag": [ + "zStbOcEn6s1dn8cMkPQaNA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "a053b1a4e7ca0d7e6c4fbb653a69c75f" + ], + "X-Google-Backends": [ + "[::1]:4426,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/32.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/32.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/32" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+31+x1FdJmImgJjxMXRAScXSTF2NM193GYGvHeosi2f9OW4b40uv343rf9eC4LtkWIiUPLkmKfNdCs7/KAZfmQkG1JSpdaikUkGvjBmS5cf9GmLzxQIyUl4o7/roNl2zhTyYnl+JrqJj2HTTSOCugTJXGHxa7PW81wSowL2bDL9vc07ivLT1bxME0oP+lSqZWokEY+HHwTHqps/VTn51NsZEJhQwaEBwuWepGboDjzC6dtiUHHNSywcF4dNNPMZ0n3b+frrL1T36behnI2JvTXZavRDsfR2dvKTnDQgpjf4/I33SUyEoqv83axCMX7nGPoMJGclAK7JQhOed9klVdApq42LRgac70b74U2HNO5xwB7MGtP7gBAAA=" + } + }, + { + "ID": "faf35876a98b1364", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon4f59f3e19b756a1352a60f40b3244629dfa80066/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a053b1a4e7ca0d7e6c4fbb653a69c75f/6942769988895621114;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon4f59f3e19b756a1352a60f40b3244629dfa80066/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:45 GMT" + ], + "Etag": [ + "BeeiZdkdAp0HxZhH+PCtEQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "a053b1a4e7ca0d7e6c4fbb653a69c75f" + ], + "X-Google-Backends": [ + "[::1]:4495,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/34.esf,ybgz6-v6:9814,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/115,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"os\" allowed_cluster: \"ef\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:1119::]:4002 had response of OK with response time of 0.013724803924560547 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybgz6-v6:9814,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/34.esf,ybgz6-v6:9814,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/34" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXFKTc2MSslOcSww8KiIyvDQDnAucQ20tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSCpWLhILZQFEQHxYrlquQD0piGGuQAAAA==" + } + }, + { + "ID": "f75237045261199b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "259" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "a6a39d57a9d231068ce538ff71647dfc/5334708552904836131;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsiYXJyYXlUeXBlIjp7InR5cGUiOiJJTlQ2NCJ9LCJ0eXBlIjoiQVJSQVkifSwicGFyYW1ldGVyVmFsdWUiOnt9fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiaEFZZ3RsRnZabWNpbTBlaXNBRGhVTm91c3FvIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:46 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/493rUdjebl2k3JZVHGIH33mluTw\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vncd187:4490,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ja08W7DaMMbRqAWE-JmIBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/206" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1TXW/aMBR951cg77VtQr5TaeoQpB1ThSYI3dp1Qo65CS5JDLYDYhX/fY5JRtdV216SOOece+49uXnudNGKlgt02UUJzTYV8P27J5agMwWAxFkNPCLfWvnDH7CHKHfEMl/epOXDcD7bfTUdwwltPls8QZJbK/vTw93Hm9FH2y7yKt49Il2H6vKLKicgz9eMy3Pfsy5n04tl/z6T+fX2oSC0MIGK/nA5G7NKbJgWCsjTW1quavlSyrW4NIzdbneRMZblgNdUXBBWGG3jxtYy1pw9AZHCeGVnqKGE8RfDq5wRLCkr38+m2lwJJpACh5KAauC50+2ipvrorYFqkVYd0X/M1kWtX01Wlp3uoXYlrExpVvEW0rZ6uuZwOtXxqGa6V7qe6geEpKUWxjjJ26b/p+1ajSUW0HDmJEgDMzHDpBckqWmlkLq+Q8B0zQBc04HUWwS+5+FWLWvDoxaXrHRSN0xt6IWJ73q4Z7sW9szUMRPbchzPChcpDkzT81CtPhzbJxywhCEVayZoG8xgEvXjaD66no+jaBgNm1F3nP5B/TIZKWY8mY0HStIQ15wyxdVxjcZxNOkP4tFdi1YCbiHDZD/d5IqR4lzA2Snjz5jjAiRwocBvetBjoKpuC8X79SnoOojjGfUnk/59k456jTnH+1fcE1l15jmoeX3ovLjp6/eOftD7ISSWlWgXoz7pCmrq8Wh8g16wqNoG8oup0603gxZa0HNt0zfV53DDnnlcSCXi8g08CI/471uorQsoZTMVmka30SBGL3pV8fI5FJjW4SLb8+wwtO2e456rtZJmjxUyxJYfroidkYxulptVHiQsWVYfFrCFnK2BX2SqyJYSwISwqpT1D486h85PQpKHg7kEAAA=" + } + }, + { + "ID": "14cd4e48d3f7cf4a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hAYgtlFvZmcim0eisADhUNousqo?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "88421997791fe09c63f3a42a87db92fa/3798704715246880844;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/hAYgtlFvZmcim0eisADhUNousqo?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:46 GMT" + ], + "Etag": [ + "Od+QGjyCZG9F8qGRMtUpHA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "88421997791fe09c63f3a42a87db92fa" + ], + "X-Google-Backends": [ + "[::1]:4005,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/63.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/63.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/63" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QS1PCMBC+8ys68ajMVA++ZjhUKIWDCJUexHGcki4lmHZLstXpMP3vJqGIl2y+x2a/zaHneexLlBl79Nha5PsaVHORAy3sJQZdS9KmVFhqYFfWDZTm1v2SXS6iXTNcRQ/j+30UP1NSTYLB4OjSfAtFanwHgwzeCJCZNvjdYa/jnVamBdgXN/6na+5oaipHT2fLMArj/1KBmZPicB4Gy3DEOql19cOcrUuxw3UMG1BQcjhnqRTugNPULZ3VkgP1K1TUv7u96abYzqO+Dd5ykuPvVcFF4YPQwWibzLDWezx5JfKUBJbWnryyv+mElMoYf+za7JqduaeGQM8VctAa3BSfnfIOsagkkI1LqgZH89T85kRQx/Xa3i9NKswguAEAAA==" + } + }, + { + "ID": "0b8a9ca1a65c1383", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon4f59f3e19b756a1352a60f40b3244629dfa80066/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "88421997791fe09c63f3a42a87db92fa/12181735764258106208;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon4f59f3e19b756a1352a60f40b3244629dfa80066/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:46 GMT" + ], + "Etag": [ + "BeeiZdkdAp0HxZhH+PCtEQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4263,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/3.esf,ybog5-v6:9801,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/52,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jd\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"iw\" allowed_cluster: \"ef\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:ce89::]:4074 had response of OK with response time of 0.01443171501159668 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybog5-v6:9801,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/3.esf,ybog5-v6:9801,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/3" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXFKTc2MSslOcSww8KiIyvDQDnAucQ20tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZSCpWLhILZQFEQHxYrlquQD0piGGuQAAAA==" + } + }, + { + "ID": "b97ab534c0d5057f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "302" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "e89c7bbb4e4c6d948b380dce0876b6c0/10645731922288472201;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsiYXJyYXlUeXBlIjp7InR5cGUiOiJJTlQ2NCJ9LCJ0eXBlIjoiQVJSQVkifSwicGFyYW1ldGVyVmFsdWUiOnsiYXJyYXlWYWx1ZXMiOlt7InZhbHVlIjoiMSJ9LHsidmFsdWUiOiIyIn1dfX1dLCJ1c2VMZWdhY3lTcWwiOmZhbHNlfX0sImpvYlJlZmVyZW5jZSI6eyJqb2JJZCI6Ik43d2U5Vk42aU1XVURiVFJ1RHBLZjFMcjJuciIsInByb2plY3RJZCI6ImR1bGNldC1wb3J0LTc2MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:46 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/U3GrynuLbHgpo_QO73FuLxBMtrQ\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnw69:4198,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jq08W4G1GInsqwXJ4IaoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/31" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1U23LaMBB95ysY9TXBdxtnppNScFKmhDZgkl7SYWQhGxVjGVmG0gz/Xkm2Q27T9sV4tefsObtac99qgxXJFuCsDSKSbErM9m9+0giciATmMJGJO+CZK2/wG+9xkNrFMl1extm3wXy2+6Lb2sy6ZPusHEUfkpzOrz951kU5+vX+irPrO6DqEFV+UaYI89OcMn7quebZbNoZezvs34xdcnU7G0ThpBzkH2NjxMyMKWKB03hEspWkLznPizNN2+12nYTSJMUwJ0UH0bXWGNe2ppYz+hMjXmjP5DTRVKH9RfA8pQhyQrO3s6kSF4QJjjHDGcLCwH2r3QZ19eFrDUmSYlXZf/TWBo2eBAvJVvsgVRHNYpKUrEkpWdVdHRwjOR5hpn2u6gk/uOAkU8QQRmlj+n9sSzbksMA1Zo66cVePdD8yulGsmzGOHc9GWHf0LnZ0G8fuouu5LmzYXApWXJjRDC5s24wXtuPbEmw5roPiaIEM0+mirhUZjmE5yHeAZB8q+4hhyPGAFDktSDOY/iTohcF8eDEfB8EgGNSt7hh5Ab2dDAUynMzGfUGpgTkjVGDVuIbjMJj0+uHwpsmWBR7hBKL9dJMKRAzTAp8cZ/wZMrjGHLNCJL+rRquBirpNKtznx0HLQVQx6E0mva/1dMQxZAzun2GPYOHMtUF9fKh+DyfPpW5gWj7RUkXV6dHg0aNEbGsKMJrqD3Vfx5lHXP3yo/UoVE95cqjXteCQl0WzpzJSZcQljIfjS/AIRcRyogekumy5qGRd+XMs3dMt03Zt16q+D0Fi/GXecY0q//SjUNJrnPF6yGAajIJ+CB55FbfN5ngNibxrYLmu5fuWZdjOqdhyrht0zX1oev4KWQlKyGa5WaXdiEbL8t0Cb3FKc8w6iSiyJQhDhGiZcfn/A1qH1h8YahqkSAUAAA==" + } + }, + { + "ID": "b413f20d29324082", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/N7we9VN6iMWUDbTRuDpKf1Lr2nr?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c3559d79b29b0ae134f1a776b6fb367f/9037670486314464434;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/N7we9VN6iMWUDbTRuDpKf1Lr2nr?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:46 GMT" + ], + "Etag": [ + "3lCDQumBXsYCtPg7xFCREg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "c3559d79b29b0ae134f1a776b6fb367f" + ], + "X-Google-Backends": [ + "[::1]:4453,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/23.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/23.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/23" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01QW0/CMBR+51cs9VUSwIRFEx5km0rUZVTwEmPM1p3NYbfW9ixACP/dtgzxpaff5fR8p7ue55HvqsnJlUeyqvxpQW3PSsC5vVDQLUdtihSNBnJu3YBpad0XPAjnbT191W8BJqW/uQloVE4mB5dmX1CnxrczyOCiAp5rg98d9jreaU1ag32xGHy65o7GrXT0LF5EtxH9L9UidxKNkuh6EYWkk/aufphz71KsREahAAUNg1MWqcQKGM7c0nnLGWBfCoV9fzzqptjOgx77a7h8jsfV48syzBa0DeV9MXxQo0YdvVywFCvRWPvyifxNR4Epp2Jt1yZDcuKmWwSdKMFAa3BTBuSYNxC15IA2LqoWHM1S85t3FXZcb9/7BTC6DrW4AQAA" + } + }, + { + "ID": "5cedc56d79aef84f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonad442fd459404ef3565cfbdc1258c83b15135c95/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "c3559d79b29b0ae134f1a776b6fb367f/17493040600028583878;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anonad442fd459404ef3565cfbdc1258c83b15135c95/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:46 GMT" + ], + "Etag": [ + "iUorJw75VEUEERa69rr9Zw==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "c3559d79b29b0ae134f1a776b6fb367f" + ], + "X-Google-Backends": [ + "[::1]:4440,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/12.esf,ybaf15-v6:9800,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/35,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jc\" allowed_cluster: \"wa\" allowed_cluster: \"jd\" allowed_cluster: \"ik\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"na\" allowed_cluster: \"wo\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"ir\" allowed_cluster: \"jo\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jl\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae1:dd4b::]:4112 had response of OK with response time of 0.014462471008300781 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybaf15-v6:9800,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/12.esf,ybaf15-v6:9800,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/12" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqckMzS/yKjc3DXMNdXUNSjSzLCqyjCq3tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZShS6NJwJUDzUYRrdYjQY4SmB4kXy4UuChEB8WK5arkA4heeQisBAAA=" + } + }, + { + "ID": "c14261d54e169488", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "316" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "643e1bd6798e67d3af5ed75b28b9ee65/15884979164054576111;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsiYXJyYXlUeXBlIjp7InR5cGUiOiJJTlQ2NCJ9LCJ0eXBlIjoiQVJSQVkifSwicGFyYW1ldGVyVmFsdWUiOnsiYXJyYXlWYWx1ZXMiOlt7InZhbHVlIjoiMSJ9LHsidmFsdWUiOiIyIn0seyJ2YWx1ZSI6IjMifV19fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiSGxOQm5nVTcyUmVXWmxiWm5YS28zNkx1cXhsIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:47 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/_CwgkMYvMgyzJNkPF-ccOdXDabw\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnad72:4447,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/46,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jq08W_H3O4XFqgWj67DYDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/46,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/46" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAI1Ua3OaQBT97q9w6NcoyJvMdFKrJLU1NoOaV9NxlvWCG5FFWLQm43/vskDMa9p8Qe7ec+4593LXx0ZTWpJ4Lh03JZ+E6xzS3ad76ktHPAEMhUXiTrLUpdV/gB24kZ4tosVZEN/2Z9PttaLLs942XJ7fbM7D3cP30fLitIXxz/l1H/nbO0nUIaL8PI8wsFZCU9ayTPV4Om5/i0Zf43BqqR5c3Ub+bXz9g2rmMF//iQQxgygYknhZ0BeMJdmxLG+323ZIaRgBSkjWxnQl18bljSonKb0HzDL5lZzMm8rkfwieRBQjRmj8eToW4pzgQQApxBi4gcdGsylV1QfvNVSQBKvM/qe3plTrFWAu2WjuC1VM44CEeVqnhKzorgoOUTEebqZ5IupxP5AxEgviBPlRbfojtgs2YiiDCjPDdmArvuL4HdsPFDWAwLB0DIqh2GAoOgTm3LZME9VsVgiWXBTT2HYCy7a1wLICCyHbCAxQtAB1EJgOdAzsK0pHwb4mFex9aR+ngBj0SZbQjNSD6Xlud+LOBqezkev23X7V6jYlb6BX3oAjJ9501OOUCpikhHKsGNdgNHG9bm8yuKyzeQZDCBHejdcRRwQoyuDoMOMLlKIVMEgznvwlGi0HyuvWqckuOQy6GEQZS13P695U0+HHKE3R7hX2AObOTF2qjvfl7/7otdQlivIXWqKoOD0YPHgsEJuKInXq6k9138epH8RpB1z18rvxLBTP4mRfrXXGEMuzep+LSJThH2s0GJ1Jz1CELzF+QoqlKBaarMo+DE2xFE3VLcUxynvESSl7m1cVvcy/vDxCegUxqz6GNHaHbm8iPfPKtyKdwQqRSHRqmprjaFpHN1r8NjClQ1fMQarlLLEW4pCsF+tlZPvUX+Rf5rCBiCaQtkNeZEMwIIxpHrPif0pq7Bt/AbPDJMxwBQAA" + } + }, + { + "ID": "8a9adde4d0a2531e", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/HlNBngU72ReWZlbZnXKo36Luqxl?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0dd072ff9e19e5aa96ef2f97bff48bc6/14276917728063791128;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/HlNBngU72ReWZlbZnXKo36Luqxl?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:47 GMT" + ], + "Etag": [ + "vqJOPCrKLEcdxvJILAJjGg==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "0dd072ff9e19e5aa96ef2f97bff48bc6" + ], + "X-Google-Backends": [ + "[::1]:4004,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/85.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/85.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/85" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/01Q30/CMBB+569Y6qskiAkkJjwALrCxIE6IBmPM1t3mZtdu7Q0hhP/dtgzxpdfvx/W+67HjOOQ75wl5cEicZ3UD8nCTAT6bSwiqYah0qQRXQG6NGzDKjHtX+0+rqVwELk32O98Lxn4xy0ajs0vRLygj7TtqpHGaA0uUxu8WOy1vNR6VYF5Me5+2uaXxUFnaW67dmRv+l0qRWCl0V+547T6SVjrZ+qHPk01RiDiEFCRwCtcslRQFUPTs0knDKGC3EhK7w0G/nWI6z/qcLSc82wz7IbxuWbzlbwtxPwiaes8uXiZohLngxr55IX/TUWDEQvFj1iZ35MpNDghqJQUFpcBO6ZFL3qkoKwZo4qJswNI00r85z7HlOqfOLw52ZIe4AQAA" + } + }, + { + "ID": "5623d7eee462c1de", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon89f7883f77f7aa85f5e03fa1ae69e15cb0010cb3/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "0dd072ff9e19e5aa96ef2f97bff48bc6/4285543776658228012;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon89f7883f77f7aa85f5e03fa1ae69e15cb0010cb3/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:47 GMT" + ], + "Etag": [ + "PbU4eb8rBrueMeUb4eaILQ==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4078,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35.esf,yblt4-v6:9850,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/74,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"wo\" allowed_cluster: \"jx\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:f11::]:4067 had response of OK with response time of 0.014156341552734375 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,yblt4-v6:9850,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35.esf,yblt4-v6:9850,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/35" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqQlICjVJTbIocioqTfVNDU0ySU309Am0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZShS6NJwJUDzUYRrdYjQY0SGHmM0PUi8WC50UYgIiBfLVcsFAOnNJcVfAQAA" + } + }, + { + "ID": "ddd73a4e85028ee2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "475" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "8969e6625ec5c2a47df5a757a14e0957/2677482340684220245;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsic3RydWN0VHlwZXMiOlt7Im5hbWUiOiJBIiwidHlwZSI6eyJ0eXBlIjoiSU5UNjQifX0seyJuYW1lIjoiQiIsInR5cGUiOnsic3RydWN0VHlwZXMiOlt7Im5hbWUiOiJEIiwidHlwZSI6eyJ0eXBlIjoiU1RSSU5HIn19XSwidHlwZSI6IlNUUlVDVCJ9fSx7Im5hbWUiOiJDIiwidHlwZSI6eyJ0eXBlIjoiQk9PTCJ9fV0sInR5cGUiOiJTVFJVQ1QifSwicGFyYW1ldGVyVmFsdWUiOnsic3RydWN0VmFsdWVzIjp7IkEiOnsidmFsdWUiOiIwIn0sIkIiOnt9LCJDIjp7InZhbHVlIjoiZmFsc2UifX19fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoic3VXSWZ4T0xaU0w2S2swSG1hQVMydlBVU2lwIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:47 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/0cGonrnjVnEc4nL4nJ8uO0BAB4w\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnac205:4139,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=j608W-HrJYmeqQW954roBg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/18" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAJVVW3ObOBR+96/waF+TIAMGnJmd1rFp6q7HyfiSdrrd8Qj5gBVjRJCwN+34v1cICE7iyWZfDOJ8t3NA8q9WG21YskKXbRSw6CGH7PGPex6gM1UASaKi8AO55sYd/oRH8GNbrOP1dZh8Hy4X+2/YNjC95kmW3N8lPrWTsZ188fIbfNW/svc/kNZhWn6VxxTkecozee465uVidiHyr6Pw35vx99nY+WuDP29Jf2bubhczlmqigDgcs2RT0NdSpuLSMPb7/UXEeRQDSZm4oHxr1MGNnWmkGb8HKoXxws5QTQnjDcMPMadEMp78uZhpc0WYQggZJBRUgF+tdhtV6qNTDRUkzSqr/9FbG9V+BVhZttqHwpXyJGRRntUlbau7qxbNqhiPCtP+oPVUHhCSJZo4J0Fch35P7IJNJBFQYZbUCz0c4F7Q8YIQmyGEXdemgLvYgy62IXRWnus4pGbLwrDkkoQnbgA4tD3o2HaAcccMCZidDqys0HGtrge4B+aqF7ioYB/K+DQDImHIRMoFqwczmPr9ub8cfVpOfH/oD6tW9xl7Bf06HSnkfLqYDBSlAqYZ4wqrxzWazP1pfzAf3dXVXMAYIkIfZw+xQoQkFnDWzPiWZGQLEjKhin/rRsuBKt26NH9Mm0EXgyjXaKaCDObVeNRzIbOcygLdiDV6BSJRggWz/0Rq5BpY46DacWz0VDjUd4ezN8Sv3in+Iv4bHTzzObYaHrNPur0wHE2u0XHx0Dp5/8//bHnwzpavbm7Gp8bZemZbWzVfwB2J82efQDko/Vgce6k3e+yMdhURYfS6ERX7NFh/pOhluupaXvRvkfdQHSpCEpnXWfRKS6mtMimH/oRi6gihT0i9JYvjhJWj7HQt7GLLtF3X8cpTTJEy+bruuXZZf350aestJHJev3Z/7Kvv7Cir2pPZEraEFTsSWY5j9XqW1bG75+oskrjDt7JHTLe3oVZEI/awftjEXsCDdf5xBTuIeQrZRaREdowCoZTniSz+JVDr0PoNOwguu+4GAAA=" + } + }, + { + "ID": "83097e8117a2b92a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/suWIfxOLZSL6Kk0HmaAS2vPUSip?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "535df361c9b0aa4d5e3f7113a604982d/1141478498714586238;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/suWIfxOLZSL6Kk0HmaAS2vPUSip?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:48 GMT" + ], + "Etag": [ + "RRxzKeieXrplPBLXx7U37w==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "535df361c9b0aa4d5e3f7113a604982d" + ], + "X-Google-Backends": [ + "[::1]:4136,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/53.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/53.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/53" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/5VTXW+CMBR991eQ7lUT5xJNlvgAjiiRgCuSmS3LgnhxKFBGy9QZ//toRQWZMz7R83F77m3ptiZJaOlHM/Qooak//0oh2dzNgT3zBQaaBoxmn5hEFFCdu4E5c+7GeP0zBB8mSRyMFH2y7tgPnVW3u3dR9xNCJ/NtM5Rhz4dgRjP8JrCU80KLnBD4jl7zQxTnNNvEgsZqz8RPRSUkM6EYtq7Liq4WtUpQOawUKBcKS5GaMVb7Kj6XK7kFeVe/nqdcyquMeG3My6NW40stnIeU2rDGWDP6fzn+m1xMX8LvN55L79K5KKapq7Jx0z0c14cu9gxHohe0IFMMHiQQuXD6QeOELMBlmngJszRwgTVikrBGp93K83nlXqfpi+atTf3V0tvDZXMQOrLV+h7Zlh8fvAFxHeaTiNttCx3TGWFOgMmK3xu6RydO2TCgo4S4QCmIlCY69NsjYRwA4+2yJAVBu072xAY+y7narvYLrpx+l80DAAA=" + } + }, + { + "ID": "dd0f9a4cd98674bd", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon7be0f48e144b0012fae211ed3f67358e09e2d9b7/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "535df361c9b0aa4d5e3f7113a604982d/9524792117902405522;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon7be0f48e144b0012fae211ed3f67358e09e2d9b7/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:48 GMT" + ], + "Etag": [ + "8q2CaNSMAybnbCCFaRZtnA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "535df361c9b0aa4d5e3f7113a604982d" + ], + "X-Google-Backends": [ + "[::1]:4274,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/5.esf,ybca13-v6:9877,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/113,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"oj\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"ef\" allowed_cluster: \"iw\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"el\" allowed_cluster: \"ej\" allowed_cluster: \"os\" allowed_cluster: \"ow\" allowed_cluster: \"jc\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ac3:c9::]:4025 had response of OK with response time of 0.013855934143066406 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybca13-v6:9877,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/5.esf,ybca13-v6:9877,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/5" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqbEoNHJO9Av2daxMyktydnZLDIoqyXO0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZUApZAEM1Zh6kHQqGSihSdTqEKczrzQnh0ytSmmJOcWpGBaj8GO5sMnAWBBZEC+Wq5YLAEu4fgSXAQAA" + } + }, + { + "ID": "a68cd48fad9975b7", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "508" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "ac7f968bee11213bbd504fdcbc39fb6d/7988787176454697915;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/jobs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb25maWd1cmF0aW9uIjp7InF1ZXJ5Ijp7InF1ZXJ5Ijoic2VsZWN0ID8iLCJxdWVyeVBhcmFtZXRlcnMiOlt7InBhcmFtZXRlclR5cGUiOnsic3RydWN0VHlwZXMiOlt7Im5hbWUiOiJBIiwidHlwZSI6eyJ0eXBlIjoiSU5UNjQifX0seyJuYW1lIjoiQiIsInR5cGUiOnsic3RydWN0VHlwZXMiOlt7Im5hbWUiOiJEIiwidHlwZSI6eyJ0eXBlIjoiU1RSSU5HIn19XSwidHlwZSI6IlNUUlVDVCJ9fSx7Im5hbWUiOiJDIiwidHlwZSI6eyJ0eXBlIjoiQk9PTCJ9fV0sInR5cGUiOiJTVFJVQ1QifSwicGFyYW1ldGVyVmFsdWUiOnsic3RydWN0VmFsdWVzIjp7IkEiOnsidmFsdWUiOiIxIn0sIkIiOnsic3RydWN0VmFsdWVzIjp7IkQiOnsidmFsdWUiOiJzIn19fSwiQyI6eyJ2YWx1ZSI6InRydWUifX19fV0sInVzZUxlZ2FjeVNxbCI6ZmFsc2V9fSwiam9iUmVmZXJlbmNlIjp7ImpvYklkIjoiS3BmNTBBUHlCOVE5YWhUOGthMFNVUFdnTjdZIiwicHJvamVjdElkIjoiZHVsY2V0LXBvcnQtNzYyIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:48 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/ub92s-Fpwf2nE5fpFu7VvvQBgxA\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnlx6:4349,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/145,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kK08W7rbE5XqqgX76JDIDg" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/145,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/145" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp-ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooMOErMOIrSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAAAJVVa3PaOhD9zq9gdL8msYzfmbnTEnA6TDMkBZPe28cwslgbF2M7lgylHf57JT9iSGiafsGWd885u0fS8rPTRasoWaDLLvKj8KGAfPfPt9RHZyIAnIQy8AVZvZU1/AE7cGOdLePluyD5NJzPtv9hXSl8p8fOr7Nt0EtcI8iuC+t+s/lwFX7vf0ElT1TSL4qYAj/P0pyfW2bvcja9eJ8FBu7f7a6cDw5ZevaK4Ons7mM4tv4vgQzi4CZKVhK+5Dxjl4qy3W4vwjQNYyBZxC5oulaawpVNT8ny9BtQzpQncopoiikvCL6JU0p4lCb/zqaluABMIIAcEgqigJ+dbhfV7KNTDUlQiaqif+itixo9mSwkO929VKVpEkRhkTehUrbsrl60K2mPKKb7puQT9QDjUVICPeLHTdGvKVuiCScM6pw5tQMb+9jxVdsPcC+AwLB0CtjANhhYh8Bc2JZpkgbNpWCFJUmaYA2bYAo41iyCDaKpFiaOrRoQUDXQBVS1wIEFkuh9VT7NgXAYRixLWdQYM5i4fc+dj67nY9cdusO61W0ePUv9OBmJTG8yGw8EpE7M8igVuaVdo7HnTvoDb3TfRAsGNxASups+xCIjIDGDs9bjO5KTNXDImQh+LhutDBW8TcjbZa3R0ohqjaaikIFX2yO+M54XlMvslqzlkxmJIJTI/iOopWvTWgXRjqmjx8C+edufvUB+9UryJ+W/0MGRzqHU8BB9Uu2J4Gj8Dh0G952T71//suXBK1u+ur29OWVn50i2kWpPwD2Ji6MjUBlVfmaHWmJnD5XRpgYiFT1vROzUUfLvSLvS6GNXW2J24Of+JduESadLE6qAnlpRP6tH+SvN2dcTjHHCi6bGclUyiXs5rnb4MSsS84o+Zpb3X86uqNo31dCwhbWebuuWVY1MAcr587hh4Sp+PCdL6TUk3GvOmHvjikN9UKsYAPkc1iSS1x9ppqk5jqapunEuBh/HarrmDulZzopqIQ2jh+XDKrb91F8WbxewgTjNIL8IBckmokAoTYuEy78k1Nl3fgFvNz6uWwcAAA==" + } + }, + { + "ID": "46385223f1f99821", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/Kpf50APyB9Q9ahT8ka0SUPWgN7Y?alt=json\u0026location=US\u0026maxResults=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1f6a89cbd10b58d23266eecb0f066212/6380725740463978723;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/queries/Kpf50APyB9Q9ahT8ka0SUPWgN7Y?alt=json\u0026location=US\u0026maxResults=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:48 GMT" + ], + "Etag": [ + "4W2vOf1YeldspJ4UslF49Q==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Goog-Trace-Id": [ + "1f6a89cbd10b58d23266eecb0f066212" + ], + "X-Google-Backends": [ + "[::1]:4223,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/66.esf,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-routing-router" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/66.esf,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-routing-router" + ], + "X-Google-Netmon-Label": [ + "/bns/vx/borg/vx/bns/cloud-dataengine/prod.router.server/66" + ], + "X-Google-Service": [ + "bigquery-prod-routing-router" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/5VTXW+CMBR991eQ7tUlaNycS3wAx5wbQUWJMcuyIFwQBcpo2UKM/320ooLMGZ/o+bg997Z0UxMEtPZCGz0KaOG5XwnE6Y0LdMwWOpDEpyT7RDgkgOrMDdR0mbs1a34PncYcfJtEry2D+M+tzrjb3bmItYTAzHybDGXY8Zgvw+8cCznPtdAMgO3oiJ+8OKdpGnFaV3pD/amoBNjmimaoqiSrSlGrBJXDSoFSobAUOdCmSl/RT+VKbkHe1i/nyefyKiNeGvP8qNX4UgunIaU2JlN9oPX/cvw3OZ++hD+uPJfeuXORh0NVkbSr7uGw3nexYxjivaAVXujgQAyhBccfNIrxCiw64C/BTnwL6G2EY3rbvm/m+axyp79Fzp0ojVK5M+6Yy+nD2hQnxmjmau353utjy6QeDpndmKBDOsXU9HX8w+4NNdCRk1MKZBRjCwgBniKifb89HEQ+UNYujRPgtGVmT+zFozlX29Z+AT5d6bvNAwAA" + } + }, + { + "ID": "17a14f32c2c9a01b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0306e609b037a05a3170a9815efc1f487617e9ed/data?alt=json\u0026startIndex=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "1f6a89cbd10b58d23266eecb0f066212/14835814383513066232;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/_c8f80b09b18bf02fef574ce0508e504ef6d8766a/tables/anon0306e609b037a05a3170a9815efc1f487617e9ed/data?alt=json\u0026startIndex=0" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:49 GMT" + ], + "Etag": [ + "GSvmflBrdh2De5ki1v9ATA==" + ], + "Server": [ + "ESF" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Google-Backends": [ + "[::1]:4109,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/42.esf,ybid10-v6:9812,/bns/yb/borg/yb/bns/traffic-prod/shared-layer2-gfe/128,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:bigquery-prod-tabledata-server-thermostat,main:shared-layer2-gfe" + ], + "X-Google-Gfe-Dispatcher-Did-Lookup": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Did-Nonsticky-Assignment": [ + "true" + ], + "X-Google-Gfe-Dispatcher-Lookup-Server-Response": [ + "fall_back_to_nonsticky_assignment: true non_sticky_backend { backend_spec { gslb_spec { target: \"blade:bigquery-prod-tabledata-server-thermostat\" pick_cluster_params { allowed_cluster: \"os\" allowed_cluster: \"na\" allowed_cluster: \"ws\" allowed_cluster: \"el\" allowed_cluster: \"jo\" allowed_cluster: \"ir\" allowed_cluster: \"ej\" allowed_cluster: \"iw\" allowed_cluster: \"jc\" allowed_cluster: \"jl\" allowed_cluster: \"wa\" allowed_cluster: \"ow\" allowed_cluster: \"jx\" allowed_cluster: \"wo\" allowed_cluster: \"oj\" allowed_cluster: \"ik\" allowed_cluster: \"jd\" allowed_cluster: \"ef\" } } } }" + ], + "X-Google-Gfe-Dispatcher-Rpc-Response": [ + "Request to [2002:ae9:3709::]:4135 had response of OK with response time of 0.013309717178344727 seconds." + ], + "X-Google-Gfe-Dispatcher-Tried-Ip-Ports": [ + "" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,ybid10-v6:9812,/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/42.esf,ybid10-v6:9812,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend,response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Gslb-Service": [ + "bigquery-prod-tabledata-server-thermostat" + ], + "X-Google-Netmon-Label": [ + "/bns/os/borg/os/bns/cloud-dataengine/prod.helix-tabledata.server/42" + ], + "X-Google-Service": [ + "bigquery-prod-tabledata-server-thermostat,restricted-shared-layer2-grpc-aggregate" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoEGBAoCygBOm4SHGJpZ3F1ZXJ5LWpzb24uZ29vZ2xlYXBpcy5jb20Y2YWkv4gBIkczNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbUoYOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Xss-Protection": [ + "1; mode=block" + ] + }, + "Body": "H4sIAAAAAAAC/6vmUlBQys7MS1GyUlBKykwvLE0tqlQuSUzKSXVJLEn0ySwuUdIBqUktSUwHqXEPLstNy3EqSskwckk1zc40LLN0DHG0tYWoKskvScwJyi8vBik1hIgVQbjRQLaCQjWYBIqmwYWQhcFSZUApZAEM1Zh6kHQCrUWTqNUhTiemMA6bcZmC7IpidFdA3YJFNBZDDF0VsT5QKikqTcXwPgof2TKEDIwFkQXxYrlquQAgtDnoHQIAAA==" + } + }, + { + "ID": "81cf0135f9629297", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json\u0026deleteContents=true", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-999dd26db3080c05c76b" + ], + "X-Cloud-Trace-Context": [ + "3ef9c0461fe4f0d891b8c1f13b47a6bc/13227752951817183009;o=1" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/bigquery/v2/projects/dulcet-port-762/datasets/dataset_20180704_40692981547271_0000?alt=json\u0026deleteContents=true" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Date": [ + "Wed, 04 Jul 2018 11:20:49 GMT" + ], + "Etag": [ + "\"72k7DzeyeEl4shlhGfnZD_UwX04/vyGp6PvFo4RvsFtPoIWeCReyIC8\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "GSE" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530703549000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/bigquery" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vnbv126:4011,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/9,ykb130-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apiserving-bigquery" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ka08W_nPApH3qQWluJzwAw" + ], + "X-Google-Gfe-Request-Trace": [ + "ykb130-v6:443,/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/9,ykb130-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "apiserving-bigquery" + ], + "X-Google-Netmon-Label": [ + "/bns/yk/borg/yk/bns/apiserving/prod_hightraffic_api_frontend.server/9" + ], + "X-Google-Security-Signals": [ + "FRAMEWORK=GSE" + ], + "X-Google-Servertype": [ + "apiserving" + ], + "X-Google-Service": [ + "apiserving-bigquery" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp4ChljbG91ZC1kYXRhZW5naW5lLXByb2QtYXBpEghiaWdxdWVyeRjZhaS_iAEiRzM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tMNooSuMBEsYBeWEyOS5jLkVvd0I3Z1dLS29nc1BYTUtycVhqN1RVQVFvTEhwNVVCLUZZY1VsSUs1N2s0NmZYUXpvMXdUUnlwbGZqOHFmaEVjUjhIa0lQdk1zV3hVdzlCbkZvam4tOGJLSFRxak1DeW5VVGNmZUdUck1OWE52SnlXRndxQlVpVzdtcVFqamNZY0dKNG9SWUZYV21xZmlTMklEX1ZYaUo3S2pnTDJQbzNRMjVjd3I5MWk0TXdwN1FMT3RQLTYzMW1hVXhfRTlNMAQ6Fk5PVF9BX1BFUlNJU1RFTlRfVE9LRU4" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ] + }, + "Body": "" + } + } + ] +} \ No newline at end of file diff --git a/vendor/cloud.google.com/go/bigquery/copy.go b/vendor/cloud.google.com/go/bigquery/copy.go new file mode 100644 index 0000000000..694ad14087 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/copy.go @@ -0,0 +1,106 @@ +// 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 bigquery + +import ( + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" +) + +// CopyConfig holds the configuration for a copy job. +type CopyConfig struct { + // Srcs are the tables from which data will be copied. + Srcs []*Table + + // Dst is the table into which the data will be copied. + Dst *Table + + // CreateDisposition specifies the circumstances under which the destination table will be created. + // The default is CreateIfNeeded. + CreateDisposition TableCreateDisposition + + // WriteDisposition specifies how existing data in the destination table is treated. + // The default is WriteEmpty. + WriteDisposition TableWriteDisposition + + // The labels associated with this job. + Labels map[string]string + + // Custom encryption configuration (e.g., Cloud KMS keys). + DestinationEncryptionConfig *EncryptionConfig +} + +func (c *CopyConfig) toBQ() *bq.JobConfiguration { + var ts []*bq.TableReference + for _, t := range c.Srcs { + ts = append(ts, t.toBQ()) + } + return &bq.JobConfiguration{ + Labels: c.Labels, + Copy: &bq.JobConfigurationTableCopy{ + CreateDisposition: string(c.CreateDisposition), + WriteDisposition: string(c.WriteDisposition), + DestinationTable: c.Dst.toBQ(), + DestinationEncryptionConfiguration: c.DestinationEncryptionConfig.toBQ(), + SourceTables: ts, + }, + } +} + +func bqToCopyConfig(q *bq.JobConfiguration, c *Client) *CopyConfig { + cc := &CopyConfig{ + Labels: q.Labels, + CreateDisposition: TableCreateDisposition(q.Copy.CreateDisposition), + WriteDisposition: TableWriteDisposition(q.Copy.WriteDisposition), + Dst: bqToTable(q.Copy.DestinationTable, c), + DestinationEncryptionConfig: bqToEncryptionConfig(q.Copy.DestinationEncryptionConfiguration), + } + for _, t := range q.Copy.SourceTables { + cc.Srcs = append(cc.Srcs, bqToTable(t, c)) + } + return cc +} + +// A Copier copies data into a BigQuery table from one or more BigQuery tables. +type Copier struct { + JobIDConfig + CopyConfig + c *Client +} + +// CopierFrom returns a Copier which can be used to copy data into a +// BigQuery table from one or more BigQuery tables. +// The returned Copier may optionally be further configured before its Run method is called. +func (t *Table) CopierFrom(srcs ...*Table) *Copier { + return &Copier{ + c: t.c, + CopyConfig: CopyConfig{ + Srcs: srcs, + Dst: t, + }, + } +} + +// Run initiates a copy job. +func (c *Copier) Run(ctx context.Context) (*Job, error) { + return c.c.insertJob(ctx, c.newJob(), nil) +} + +func (c *Copier) newJob() *bq.Job { + return &bq.Job{ + JobReference: c.JobIDConfig.createJobRef(c.c), + Configuration: c.CopyConfig.toBQ(), + } +} diff --git a/vendor/cloud.google.com/go/bigquery/copy_test.go b/vendor/cloud.google.com/go/bigquery/copy_test.go new file mode 100644 index 0000000000..79b1d33de3 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/copy_test.go @@ -0,0 +1,165 @@ +// Copyright 2015 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 bigquery + +import ( + "testing" + + "github.com/google/go-cmp/cmp/cmpopts" + + "cloud.google.com/go/internal/testutil" + + bq "google.golang.org/api/bigquery/v2" +) + +func defaultCopyJob() *bq.Job { + return &bq.Job{ + JobReference: &bq.JobReference{JobId: "RANDOM", ProjectId: "client-project-id"}, + Configuration: &bq.JobConfiguration{ + Copy: &bq.JobConfigurationTableCopy{ + DestinationTable: &bq.TableReference{ + ProjectId: "d-project-id", + DatasetId: "d-dataset-id", + TableId: "d-table-id", + }, + SourceTables: []*bq.TableReference{ + { + ProjectId: "s-project-id", + DatasetId: "s-dataset-id", + TableId: "s-table-id", + }, + }, + }, + }, + } +} + +func TestCopy(t *testing.T) { + defer fixRandomID("RANDOM")() + testCases := []struct { + dst *Table + srcs []*Table + jobID string + location string + config CopyConfig + want *bq.Job + }{ + { + dst: &Table{ + ProjectID: "d-project-id", + DatasetID: "d-dataset-id", + TableID: "d-table-id", + }, + srcs: []*Table{ + { + ProjectID: "s-project-id", + DatasetID: "s-dataset-id", + TableID: "s-table-id", + }, + }, + want: defaultCopyJob(), + }, + { + dst: &Table{ + ProjectID: "d-project-id", + DatasetID: "d-dataset-id", + TableID: "d-table-id", + }, + srcs: []*Table{ + { + ProjectID: "s-project-id", + DatasetID: "s-dataset-id", + TableID: "s-table-id", + }, + }, + config: CopyConfig{ + CreateDisposition: CreateNever, + WriteDisposition: WriteTruncate, + DestinationEncryptionConfig: &EncryptionConfig{KMSKeyName: "keyName"}, + Labels: map[string]string{"a": "b"}, + }, + want: func() *bq.Job { + j := defaultCopyJob() + j.Configuration.Labels = map[string]string{"a": "b"} + j.Configuration.Copy.CreateDisposition = "CREATE_NEVER" + j.Configuration.Copy.WriteDisposition = "WRITE_TRUNCATE" + j.Configuration.Copy.DestinationEncryptionConfiguration = &bq.EncryptionConfiguration{KmsKeyName: "keyName"} + return j + }(), + }, + { + dst: &Table{ + ProjectID: "d-project-id", + DatasetID: "d-dataset-id", + TableID: "d-table-id", + }, + srcs: []*Table{ + { + ProjectID: "s-project-id", + DatasetID: "s-dataset-id", + TableID: "s-table-id", + }, + }, + jobID: "job-id", + want: func() *bq.Job { + j := defaultCopyJob() + j.JobReference.JobId = "job-id" + return j + }(), + }, + { + dst: &Table{ + ProjectID: "d-project-id", + DatasetID: "d-dataset-id", + TableID: "d-table-id", + }, + srcs: []*Table{ + { + ProjectID: "s-project-id", + DatasetID: "s-dataset-id", + TableID: "s-table-id", + }, + }, + location: "asia-northeast1", + want: func() *bq.Job { + j := defaultCopyJob() + j.JobReference.Location = "asia-northeast1" + return j + }(), + }, + } + c := &Client{projectID: "client-project-id"} + for i, tc := range testCases { + tc.dst.c = c + copier := tc.dst.CopierFrom(tc.srcs...) + copier.JobID = tc.jobID + copier.Location = tc.location + tc.config.Srcs = tc.srcs + tc.config.Dst = tc.dst + copier.CopyConfig = tc.config + got := copier.newJob() + checkJob(t, i, got, tc.want) + + jc, err := bqToJobConfig(got.Configuration, c) + if err != nil { + t.Fatalf("#%d: %v", i, err) + } + diff := testutil.Diff(jc.(*CopyConfig), &copier.CopyConfig, + cmpopts.IgnoreUnexported(Table{})) + if diff != "" { + t.Errorf("#%d: (got=-, want=+:\n%s", i, diff) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/dataset.go b/vendor/cloud.google.com/go/bigquery/dataset.go new file mode 100644 index 0000000000..5548323246 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/dataset.go @@ -0,0 +1,530 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "fmt" + "time" + + "cloud.google.com/go/internal/optional" + "cloud.google.com/go/internal/trace" + + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" + "google.golang.org/api/iterator" +) + +// Dataset is a reference to a BigQuery dataset. +type Dataset struct { + ProjectID string + DatasetID string + c *Client +} + +// DatasetMetadata contains information about a BigQuery dataset. +type DatasetMetadata struct { + // These fields can be set when creating a dataset. + Name string // The user-friendly name for this dataset. + Description string // The user-friendly description of this dataset. + Location string // The geo location of the dataset. + DefaultTableExpiration time.Duration // The default expiration time for new tables. + Labels map[string]string // User-provided labels. + Access []*AccessEntry // Access permissions. + + // These fields are read-only. + CreationTime time.Time + LastModifiedTime time.Time // When the dataset or any of its tables were modified. + FullID string // The full dataset ID in the form projectID:datasetID. + + // ETag is the ETag obtained when reading metadata. Pass it to Dataset.Update to + // ensure that the metadata hasn't changed since it was read. + ETag string +} + +// DatasetMetadataToUpdate is used when updating a dataset's metadata. +// Only non-nil fields will be updated. +type DatasetMetadataToUpdate struct { + Description optional.String // The user-friendly description of this table. + Name optional.String // The user-friendly name for this dataset. + + // DefaultTableExpiration is the the default expiration time for new tables. + // If set to time.Duration(0), new tables never expire. + DefaultTableExpiration optional.Duration + + // The entire access list. It is not possible to replace individual entries. + Access []*AccessEntry + + labelUpdater +} + +// Dataset creates a handle to a BigQuery dataset in the client's project. +func (c *Client) Dataset(id string) *Dataset { + return c.DatasetInProject(c.projectID, id) +} + +// DatasetInProject creates a handle to a BigQuery dataset in the specified project. +func (c *Client) DatasetInProject(projectID, datasetID string) *Dataset { + return &Dataset{ + ProjectID: projectID, + DatasetID: datasetID, + c: c, + } +} + +// Create creates a dataset in the BigQuery service. An error will be returned if the +// dataset already exists. Pass in a DatasetMetadata value to configure the dataset. +func (d *Dataset) Create(ctx context.Context, md *DatasetMetadata) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Create") + defer func() { trace.EndSpan(ctx, err) }() + + ds, err := md.toBQ() + if err != nil { + return err + } + ds.DatasetReference = &bq.DatasetReference{DatasetId: d.DatasetID} + // Use Client.Location as a default. + if ds.Location == "" { + ds.Location = d.c.Location + } + call := d.c.bqs.Datasets.Insert(d.ProjectID, ds).Context(ctx) + setClientHeader(call.Header()) + _, err = call.Do() + return err +} + +func (dm *DatasetMetadata) toBQ() (*bq.Dataset, error) { + ds := &bq.Dataset{} + if dm == nil { + return ds, nil + } + ds.FriendlyName = dm.Name + ds.Description = dm.Description + ds.Location = dm.Location + ds.DefaultTableExpirationMs = int64(dm.DefaultTableExpiration / time.Millisecond) + ds.Labels = dm.Labels + var err error + ds.Access, err = accessListToBQ(dm.Access) + if err != nil { + return nil, err + } + if !dm.CreationTime.IsZero() { + return nil, errors.New("bigquery: Dataset.CreationTime is not writable") + } + if !dm.LastModifiedTime.IsZero() { + return nil, errors.New("bigquery: Dataset.LastModifiedTime is not writable") + } + if dm.FullID != "" { + return nil, errors.New("bigquery: Dataset.FullID is not writable") + } + if dm.ETag != "" { + return nil, errors.New("bigquery: Dataset.ETag is not writable") + } + return ds, nil +} + +func accessListToBQ(a []*AccessEntry) ([]*bq.DatasetAccess, error) { + var q []*bq.DatasetAccess + for _, e := range a { + a, err := e.toBQ() + if err != nil { + return nil, err + } + q = append(q, a) + } + return q, nil +} + +// Delete deletes the dataset. Delete will fail if the dataset is not empty. +func (d *Dataset) Delete(ctx context.Context) (err error) { + return d.deleteInternal(ctx, false) +} + +// DeleteWithContents deletes the dataset, as well as contained resources. +func (d *Dataset) DeleteWithContents(ctx context.Context) (err error) { + return d.deleteInternal(ctx, true) +} + +func (d *Dataset) deleteInternal(ctx context.Context, deleteContents bool) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Delete") + defer func() { trace.EndSpan(ctx, err) }() + + call := d.c.bqs.Datasets.Delete(d.ProjectID, d.DatasetID).Context(ctx).DeleteContents(deleteContents) + setClientHeader(call.Header()) + return call.Do() +} + +// Metadata fetches the metadata for the dataset. +func (d *Dataset) Metadata(ctx context.Context) (md *DatasetMetadata, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Metadata") + defer func() { trace.EndSpan(ctx, err) }() + + call := d.c.bqs.Datasets.Get(d.ProjectID, d.DatasetID).Context(ctx) + setClientHeader(call.Header()) + var ds *bq.Dataset + if err := runWithRetry(ctx, func() (err error) { + ds, err = call.Do() + return err + }); err != nil { + return nil, err + } + return bqToDatasetMetadata(ds) +} + +func bqToDatasetMetadata(d *bq.Dataset) (*DatasetMetadata, error) { + dm := &DatasetMetadata{ + CreationTime: unixMillisToTime(d.CreationTime), + LastModifiedTime: unixMillisToTime(d.LastModifiedTime), + DefaultTableExpiration: time.Duration(d.DefaultTableExpirationMs) * time.Millisecond, + Description: d.Description, + Name: d.FriendlyName, + FullID: d.Id, + Location: d.Location, + Labels: d.Labels, + ETag: d.Etag, + } + for _, a := range d.Access { + e, err := bqToAccessEntry(a, nil) + if err != nil { + return nil, err + } + dm.Access = append(dm.Access, e) + } + return dm, nil +} + +// Update modifies specific Dataset metadata fields. +// To perform a read-modify-write that protects against intervening reads, +// set the etag argument to the DatasetMetadata.ETag field from the read. +// Pass the empty string for etag for a "blind write" that will always succeed. +func (d *Dataset) Update(ctx context.Context, dm DatasetMetadataToUpdate, etag string) (md *DatasetMetadata, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Update") + defer func() { trace.EndSpan(ctx, err) }() + + ds, err := dm.toBQ() + if err != nil { + return nil, err + } + call := d.c.bqs.Datasets.Patch(d.ProjectID, d.DatasetID, ds).Context(ctx) + setClientHeader(call.Header()) + if etag != "" { + call.Header().Set("If-Match", etag) + } + var ds2 *bq.Dataset + if err := runWithRetry(ctx, func() (err error) { + ds2, err = call.Do() + return err + }); err != nil { + return nil, err + } + return bqToDatasetMetadata(ds2) +} + +func (dm *DatasetMetadataToUpdate) toBQ() (*bq.Dataset, error) { + ds := &bq.Dataset{} + forceSend := func(field string) { + ds.ForceSendFields = append(ds.ForceSendFields, field) + } + + if dm.Description != nil { + ds.Description = optional.ToString(dm.Description) + forceSend("Description") + } + if dm.Name != nil { + ds.FriendlyName = optional.ToString(dm.Name) + forceSend("FriendlyName") + } + if dm.DefaultTableExpiration != nil { + dur := optional.ToDuration(dm.DefaultTableExpiration) + if dur == 0 { + // Send a null to delete the field. + ds.NullFields = append(ds.NullFields, "DefaultTableExpirationMs") + } else { + ds.DefaultTableExpirationMs = int64(dur / time.Millisecond) + } + } + if dm.Access != nil { + var err error + ds.Access, err = accessListToBQ(dm.Access) + if err != nil { + return nil, err + } + if len(ds.Access) == 0 { + ds.NullFields = append(ds.NullFields, "Access") + } + } + labels, forces, nulls := dm.update() + ds.Labels = labels + ds.ForceSendFields = append(ds.ForceSendFields, forces...) + ds.NullFields = append(ds.NullFields, nulls...) + return ds, nil +} + +// Table creates a handle to a BigQuery table in the dataset. +// To determine if a table exists, call Table.Metadata. +// If the table does not already exist, use Table.Create to create it. +func (d *Dataset) Table(tableID string) *Table { + return &Table{ProjectID: d.ProjectID, DatasetID: d.DatasetID, TableID: tableID, c: d.c} +} + +// Tables returns an iterator over the tables in the Dataset. +func (d *Dataset) Tables(ctx context.Context) *TableIterator { + it := &TableIterator{ + ctx: ctx, + dataset: d, + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.tables) }, + func() interface{} { b := it.tables; it.tables = nil; return b }) + return it +} + +// A TableIterator is an iterator over Tables. +type TableIterator struct { + ctx context.Context + dataset *Dataset + tables []*Table + pageInfo *iterator.PageInfo + nextFunc func() error +} + +// Next returns the next result. Its second return value is Done if there are +// no more results. Once Next returns Done, all subsequent calls will return +// Done. +func (it *TableIterator) Next() (*Table, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + t := it.tables[0] + it.tables = it.tables[1:] + return t, nil +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *TableIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +// for testing +var listTables = func(it *TableIterator, pageSize int, pageToken string) (*bq.TableList, error) { + call := it.dataset.c.bqs.Tables.List(it.dataset.ProjectID, it.dataset.DatasetID). + PageToken(pageToken). + Context(it.ctx) + setClientHeader(call.Header()) + if pageSize > 0 { + call.MaxResults(int64(pageSize)) + } + var res *bq.TableList + err := runWithRetry(it.ctx, func() (err error) { + res, err = call.Do() + return err + }) + return res, err +} + +func (it *TableIterator) fetch(pageSize int, pageToken string) (string, error) { + res, err := listTables(it, pageSize, pageToken) + if err != nil { + return "", err + } + for _, t := range res.Tables { + it.tables = append(it.tables, bqToTable(t.TableReference, it.dataset.c)) + } + return res.NextPageToken, nil +} + +func bqToTable(tr *bq.TableReference, c *Client) *Table { + if tr == nil { + return nil + } + return &Table{ + ProjectID: tr.ProjectId, + DatasetID: tr.DatasetId, + TableID: tr.TableId, + c: c, + } +} + +// Datasets returns an iterator over the datasets in a project. +// The Client's project is used by default, but that can be +// changed by setting ProjectID on the returned iterator before calling Next. +func (c *Client) Datasets(ctx context.Context) *DatasetIterator { + return c.DatasetsInProject(ctx, c.projectID) +} + +// DatasetsInProject returns an iterator over the datasets in the provided project. +// +// Deprecated: call Client.Datasets, then set ProjectID on the returned iterator. +func (c *Client) DatasetsInProject(ctx context.Context, projectID string) *DatasetIterator { + it := &DatasetIterator{ + ctx: ctx, + c: c, + ProjectID: projectID, + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// DatasetIterator iterates over the datasets in a project. +type DatasetIterator struct { + // ListHidden causes hidden datasets to be listed when set to true. + // Set before the first call to Next. + ListHidden bool + + // Filter restricts the datasets returned by label. The filter syntax is described in + // https://cloud.google.com/bigquery/docs/labeling-datasets#filtering_datasets_using_labels + // Set before the first call to Next. + Filter string + + // The project ID of the listed datasets. + // Set before the first call to Next. + ProjectID string + + ctx context.Context + c *Client + pageInfo *iterator.PageInfo + nextFunc func() error + items []*Dataset +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *DatasetIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +func (it *DatasetIterator) Next() (*Dataset, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +// for testing +var listDatasets = func(it *DatasetIterator, pageSize int, pageToken string) (*bq.DatasetList, error) { + call := it.c.bqs.Datasets.List(it.ProjectID). + Context(it.ctx). + PageToken(pageToken). + All(it.ListHidden) + setClientHeader(call.Header()) + if pageSize > 0 { + call.MaxResults(int64(pageSize)) + } + if it.Filter != "" { + call.Filter(it.Filter) + } + var res *bq.DatasetList + err := runWithRetry(it.ctx, func() (err error) { + res, err = call.Do() + return err + }) + return res, err +} + +func (it *DatasetIterator) fetch(pageSize int, pageToken string) (string, error) { + res, err := listDatasets(it, pageSize, pageToken) + if err != nil { + return "", err + } + for _, d := range res.Datasets { + it.items = append(it.items, &Dataset{ + ProjectID: d.DatasetReference.ProjectId, + DatasetID: d.DatasetReference.DatasetId, + c: it.c, + }) + } + return res.NextPageToken, nil +} + +// An AccessEntry describes the permissions that an entity has on a dataset. +type AccessEntry struct { + Role AccessRole // The role of the entity + EntityType EntityType // The type of entity + Entity string // The entity (individual or group) granted access + View *Table // The view granted access (EntityType must be ViewEntity) +} + +// AccessRole is the level of access to grant to a dataset. +type AccessRole string + +const ( + OwnerRole AccessRole = "OWNER" + ReaderRole AccessRole = "READER" + WriterRole AccessRole = "WRITER" +) + +// EntityType is the type of entity in an AccessEntry. +type EntityType int + +const ( + // A domain (e.g. "example.com") + DomainEntity EntityType = iota + 1 + + // Email address of a Google Group + GroupEmailEntity + + // Email address of an individual user. + UserEmailEntity + + // A special group: one of projectOwners, projectReaders, projectWriters or allAuthenticatedUsers. + SpecialGroupEntity + + // A BigQuery view. + ViewEntity +) + +func (e *AccessEntry) toBQ() (*bq.DatasetAccess, error) { + q := &bq.DatasetAccess{Role: string(e.Role)} + switch e.EntityType { + case DomainEntity: + q.Domain = e.Entity + case GroupEmailEntity: + q.GroupByEmail = e.Entity + case UserEmailEntity: + q.UserByEmail = e.Entity + case SpecialGroupEntity: + q.SpecialGroup = e.Entity + case ViewEntity: + q.View = e.View.toBQ() + default: + return nil, fmt.Errorf("bigquery: unknown entity type %d", e.EntityType) + } + return q, nil +} + +func bqToAccessEntry(q *bq.DatasetAccess, c *Client) (*AccessEntry, error) { + e := &AccessEntry{Role: AccessRole(q.Role)} + switch { + case q.Domain != "": + e.Entity = q.Domain + e.EntityType = DomainEntity + case q.GroupByEmail != "": + e.Entity = q.GroupByEmail + e.EntityType = GroupEmailEntity + case q.UserByEmail != "": + e.Entity = q.UserByEmail + e.EntityType = UserEmailEntity + case q.SpecialGroup != "": + e.Entity = q.SpecialGroup + e.EntityType = SpecialGroupEntity + case q.View != nil: + e.View = c.DatasetInProject(q.View.ProjectId, q.View.DatasetId).Table(q.View.TableId) + e.EntityType = ViewEntity + default: + return nil, errors.New("bigquery: invalid access value") + } + return e, nil +} diff --git a/vendor/cloud.google.com/go/bigquery/dataset_test.go b/vendor/cloud.google.com/go/bigquery/dataset_test.go new file mode 100644 index 0000000000..99cbb1caa5 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/dataset_test.go @@ -0,0 +1,328 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "strconv" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/internal/testutil" + + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" + itest "google.golang.org/api/iterator/testing" +) + +// readServiceStub services read requests by returning data from an in-memory list of values. +type listTablesStub struct { + expectedProject, expectedDataset string + tables []*bq.TableListTables +} + +func (s *listTablesStub) listTables(it *TableIterator, pageSize int, pageToken string) (*bq.TableList, error) { + if it.dataset.ProjectID != s.expectedProject { + return nil, errors.New("wrong project id") + } + if it.dataset.DatasetID != s.expectedDataset { + return nil, errors.New("wrong dataset id") + } + const maxPageSize = 2 + if pageSize <= 0 || pageSize > maxPageSize { + pageSize = maxPageSize + } + start := 0 + if pageToken != "" { + var err error + start, err = strconv.Atoi(pageToken) + if err != nil { + return nil, err + } + } + end := start + pageSize + if end > len(s.tables) { + end = len(s.tables) + } + nextPageToken := "" + if end < len(s.tables) { + nextPageToken = strconv.Itoa(end) + } + return &bq.TableList{ + Tables: s.tables[start:end], + NextPageToken: nextPageToken, + }, nil +} + +func TestTables(t *testing.T) { + c := &Client{projectID: "p1"} + inTables := []*bq.TableListTables{ + {TableReference: &bq.TableReference{ProjectId: "p1", DatasetId: "d1", TableId: "t1"}}, + {TableReference: &bq.TableReference{ProjectId: "p1", DatasetId: "d1", TableId: "t2"}}, + {TableReference: &bq.TableReference{ProjectId: "p1", DatasetId: "d1", TableId: "t3"}}, + } + outTables := []*Table{ + {ProjectID: "p1", DatasetID: "d1", TableID: "t1", c: c}, + {ProjectID: "p1", DatasetID: "d1", TableID: "t2", c: c}, + {ProjectID: "p1", DatasetID: "d1", TableID: "t3", c: c}, + } + + lts := &listTablesStub{ + expectedProject: "p1", + expectedDataset: "d1", + tables: inTables, + } + old := listTables + listTables = lts.listTables // cannot use t.Parallel with this test + defer func() { listTables = old }() + + msg, ok := itest.TestIterator(outTables, + func() interface{} { return c.Dataset("d1").Tables(context.Background()) }, + func(it interface{}) (interface{}, error) { return it.(*TableIterator).Next() }) + if !ok { + t.Error(msg) + } +} + +type listDatasetsStub struct { + expectedProject string + datasets []*bq.DatasetListDatasets + hidden map[*bq.DatasetListDatasets]bool +} + +func (s *listDatasetsStub) listDatasets(it *DatasetIterator, pageSize int, pageToken string) (*bq.DatasetList, error) { + const maxPageSize = 2 + if pageSize <= 0 || pageSize > maxPageSize { + pageSize = maxPageSize + } + if it.Filter != "" { + return nil, errors.New("filter not supported") + } + if it.ProjectID != s.expectedProject { + return nil, errors.New("bad project ID") + } + start := 0 + if pageToken != "" { + var err error + start, err = strconv.Atoi(pageToken) + if err != nil { + return nil, err + } + } + var ( + i int + result []*bq.DatasetListDatasets + nextPageToken string + ) + for i = start; len(result) < pageSize && i < len(s.datasets); i++ { + if s.hidden[s.datasets[i]] && !it.ListHidden { + continue + } + result = append(result, s.datasets[i]) + } + if i < len(s.datasets) { + nextPageToken = strconv.Itoa(i) + } + return &bq.DatasetList{ + Datasets: result, + NextPageToken: nextPageToken, + }, nil +} + +func TestDatasets(t *testing.T) { + client := &Client{projectID: "p"} + inDatasets := []*bq.DatasetListDatasets{ + {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "a"}}, + {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "b"}}, + {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "hidden"}}, + {DatasetReference: &bq.DatasetReference{ProjectId: "p", DatasetId: "c"}}, + } + outDatasets := []*Dataset{ + {"p", "a", client}, + {"p", "b", client}, + {"p", "hidden", client}, + {"p", "c", client}, + } + lds := &listDatasetsStub{ + expectedProject: "p", + datasets: inDatasets, + hidden: map[*bq.DatasetListDatasets]bool{inDatasets[2]: true}, + } + old := listDatasets + listDatasets = lds.listDatasets // cannot use t.Parallel with this test + defer func() { listDatasets = old }() + + msg, ok := itest.TestIterator(outDatasets, + func() interface{} { it := client.Datasets(context.Background()); it.ListHidden = true; return it }, + func(it interface{}) (interface{}, error) { return it.(*DatasetIterator).Next() }) + if !ok { + t.Fatalf("ListHidden=true: %s", msg) + } + + msg, ok = itest.TestIterator([]*Dataset{outDatasets[0], outDatasets[1], outDatasets[3]}, + func() interface{} { it := client.Datasets(context.Background()); it.ListHidden = false; return it }, + func(it interface{}) (interface{}, error) { return it.(*DatasetIterator).Next() }) + if !ok { + t.Fatalf("ListHidden=false: %s", msg) + } +} + +func TestDatasetToBQ(t *testing.T) { + for _, test := range []struct { + in *DatasetMetadata + want *bq.Dataset + }{ + {nil, &bq.Dataset{}}, + {&DatasetMetadata{Name: "name"}, &bq.Dataset{FriendlyName: "name"}}, + {&DatasetMetadata{ + Name: "name", + Description: "desc", + DefaultTableExpiration: time.Hour, + Location: "EU", + Labels: map[string]string{"x": "y"}, + Access: []*AccessEntry{{Role: OwnerRole, Entity: "example.com", EntityType: DomainEntity}}, + }, &bq.Dataset{ + FriendlyName: "name", + Description: "desc", + DefaultTableExpirationMs: 60 * 60 * 1000, + Location: "EU", + Labels: map[string]string{"x": "y"}, + Access: []*bq.DatasetAccess{{Role: "OWNER", Domain: "example.com"}}, + }}, + } { + got, err := test.in.toBQ() + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, test.want) { + t.Errorf("%v:\ngot %+v\nwant %+v", test.in, got, test.want) + } + } + + // Check that non-writeable fields are unset. + aTime := time.Date(2017, 1, 26, 0, 0, 0, 0, time.Local) + for _, dm := range []*DatasetMetadata{ + {CreationTime: aTime}, + {LastModifiedTime: aTime}, + {FullID: "x"}, + {ETag: "e"}, + } { + if _, err := dm.toBQ(); err == nil { + t.Errorf("%+v: got nil, want error", dm) + } + } +} + +func TestBQToDatasetMetadata(t *testing.T) { + cTime := time.Date(2017, 1, 26, 0, 0, 0, 0, time.Local) + cMillis := cTime.UnixNano() / 1e6 + mTime := time.Date(2017, 10, 31, 0, 0, 0, 0, time.Local) + mMillis := mTime.UnixNano() / 1e6 + q := &bq.Dataset{ + CreationTime: cMillis, + LastModifiedTime: mMillis, + FriendlyName: "name", + Description: "desc", + DefaultTableExpirationMs: 60 * 60 * 1000, + Location: "EU", + Labels: map[string]string{"x": "y"}, + Access: []*bq.DatasetAccess{ + {Role: "READER", UserByEmail: "joe@example.com"}, + {Role: "WRITER", GroupByEmail: "users@example.com"}, + }, + Etag: "etag", + } + want := &DatasetMetadata{ + CreationTime: cTime, + LastModifiedTime: mTime, + Name: "name", + Description: "desc", + DefaultTableExpiration: time.Hour, + Location: "EU", + Labels: map[string]string{"x": "y"}, + Access: []*AccessEntry{ + {Role: ReaderRole, Entity: "joe@example.com", EntityType: UserEmailEntity}, + {Role: WriterRole, Entity: "users@example.com", EntityType: GroupEmailEntity}, + }, + ETag: "etag", + } + got, err := bqToDatasetMetadata(q) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("-got, +want:\n%s", diff) + } +} + +func TestDatasetMetadataToUpdateToBQ(t *testing.T) { + dm := DatasetMetadataToUpdate{ + Description: "desc", + Name: "name", + DefaultTableExpiration: time.Hour, + } + dm.SetLabel("label", "value") + dm.DeleteLabel("del") + + got, err := dm.toBQ() + if err != nil { + t.Fatal(err) + } + want := &bq.Dataset{ + Description: "desc", + FriendlyName: "name", + DefaultTableExpirationMs: 60 * 60 * 1000, + Labels: map[string]string{"label": "value"}, + ForceSendFields: []string{"Description", "FriendlyName"}, + NullFields: []string{"Labels.del"}, + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("-got, +want:\n%s", diff) + } +} + +func TestConvertAccessEntry(t *testing.T) { + c := &Client{projectID: "pid"} + for _, e := range []*AccessEntry{ + {Role: ReaderRole, Entity: "e", EntityType: DomainEntity}, + {Role: WriterRole, Entity: "e", EntityType: GroupEmailEntity}, + {Role: OwnerRole, Entity: "e", EntityType: UserEmailEntity}, + {Role: ReaderRole, Entity: "e", EntityType: SpecialGroupEntity}, + {Role: ReaderRole, EntityType: ViewEntity, + View: &Table{ProjectID: "p", DatasetID: "d", TableID: "t", c: c}}, + } { + q, err := e.toBQ() + if err != nil { + t.Fatal(err) + } + got, err := bqToAccessEntry(q, c) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(got, e, cmp.AllowUnexported(Table{}, Client{})); diff != "" { + t.Errorf("got=-, want=+:\n%s", diff) + } + } + + e := &AccessEntry{Role: ReaderRole, Entity: "e"} + if _, err := e.toBQ(); err == nil { + t.Error("got nil, want error") + } + if _, err := bqToAccessEntry(&bq.DatasetAccess{Role: "WRITER"}, nil); err == nil { + t.Error("got nil, want error") + } +} diff --git a/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/ListDataSources_smoke_test.go b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/ListDataSources_smoke_test.go new file mode 100644 index 0000000000..86fdde5e57 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/ListDataSources_smoke_test.go @@ -0,0 +1,67 @@ +// 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 datatransfer + +import ( + datatransferpb "google.golang.org/genproto/googleapis/cloud/bigquery/datatransfer/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestDataTransferServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var formattedParent string = fmt.Sprintf("projects/%s", projectId) + var request = &datatransferpb.ListDataSourcesRequest{ + Parent: formattedParent, + } + + iter := c.ListDataSources(ctx, request) + if _, err := iter.Next(); err != nil && err != iterator.Done { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/data_transfer_client.go b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/data_transfer_client.go new file mode 100644 index 0000000000..7373c7d6c6 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/data_transfer_client.go @@ -0,0 +1,612 @@ +// 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 datatransfer + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + datatransferpb "google.golang.org/genproto/googleapis/cloud/bigquery/datatransfer/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 { + GetDataSource []gax.CallOption + ListDataSources []gax.CallOption + CreateTransferConfig []gax.CallOption + UpdateTransferConfig []gax.CallOption + DeleteTransferConfig []gax.CallOption + GetTransferConfig []gax.CallOption + ListTransferConfigs []gax.CallOption + ScheduleTransferRuns []gax.CallOption + GetTransferRun []gax.CallOption + DeleteTransferRun []gax.CallOption + ListTransferRuns []gax.CallOption + ListTransferLogs []gax.CallOption + CheckValidCreds []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("bigquerydatatransfer.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{ + GetDataSource: retry[[2]string{"default", "idempotent"}], + ListDataSources: retry[[2]string{"default", "idempotent"}], + CreateTransferConfig: retry[[2]string{"default", "non_idempotent"}], + UpdateTransferConfig: retry[[2]string{"default", "non_idempotent"}], + DeleteTransferConfig: retry[[2]string{"default", "idempotent"}], + GetTransferConfig: retry[[2]string{"default", "idempotent"}], + ListTransferConfigs: retry[[2]string{"default", "idempotent"}], + ScheduleTransferRuns: retry[[2]string{"default", "non_idempotent"}], + GetTransferRun: retry[[2]string{"default", "idempotent"}], + DeleteTransferRun: retry[[2]string{"default", "idempotent"}], + ListTransferRuns: retry[[2]string{"default", "idempotent"}], + ListTransferLogs: retry[[2]string{"default", "idempotent"}], + CheckValidCreds: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with BigQuery Data Transfer 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 datatransferpb.DataTransferServiceClient + + // 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 data transfer service client. +// +// The Google BigQuery Data Transfer Service API enables BigQuery users to +// configure the transfer of their data from other Google Products into BigQuery. +// This service contains methods that are end user exposed. It backs up the +// frontend. +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: datatransferpb.NewDataTransferServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// GetDataSource retrieves a supported data source and returns its settings, +// which can be used for UI rendering. +func (c *Client) GetDataSource(ctx context.Context, req *datatransferpb.GetDataSourceRequest, opts ...gax.CallOption) (*datatransferpb.DataSource, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetDataSource[0:len(c.CallOptions.GetDataSource):len(c.CallOptions.GetDataSource)], opts...) + var resp *datatransferpb.DataSource + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetDataSource(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListDataSources lists supported data sources and returns their settings, +// which can be used for UI rendering. +func (c *Client) ListDataSources(ctx context.Context, req *datatransferpb.ListDataSourcesRequest, opts ...gax.CallOption) *DataSourceIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListDataSources[0:len(c.CallOptions.ListDataSources):len(c.CallOptions.ListDataSources)], opts...) + it := &DataSourceIterator{} + req = proto.Clone(req).(*datatransferpb.ListDataSourcesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*datatransferpb.DataSource, string, error) { + var resp *datatransferpb.ListDataSourcesResponse + 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.ListDataSources(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.DataSources, 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 +} + +// CreateTransferConfig creates a new data transfer configuration. +func (c *Client) CreateTransferConfig(ctx context.Context, req *datatransferpb.CreateTransferConfigRequest, opts ...gax.CallOption) (*datatransferpb.TransferConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateTransferConfig[0:len(c.CallOptions.CreateTransferConfig):len(c.CallOptions.CreateTransferConfig)], opts...) + var resp *datatransferpb.TransferConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateTransferConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateTransferConfig updates a data transfer configuration. +// All fields must be set, even if they are not updated. +func (c *Client) UpdateTransferConfig(ctx context.Context, req *datatransferpb.UpdateTransferConfigRequest, opts ...gax.CallOption) (*datatransferpb.TransferConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateTransferConfig[0:len(c.CallOptions.UpdateTransferConfig):len(c.CallOptions.UpdateTransferConfig)], opts...) + var resp *datatransferpb.TransferConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.UpdateTransferConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteTransferConfig deletes a data transfer configuration, +// including any associated transfer runs and logs. +func (c *Client) DeleteTransferConfig(ctx context.Context, req *datatransferpb.DeleteTransferConfigRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteTransferConfig[0:len(c.CallOptions.DeleteTransferConfig):len(c.CallOptions.DeleteTransferConfig)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteTransferConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// GetTransferConfig returns information about a data transfer config. +func (c *Client) GetTransferConfig(ctx context.Context, req *datatransferpb.GetTransferConfigRequest, opts ...gax.CallOption) (*datatransferpb.TransferConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetTransferConfig[0:len(c.CallOptions.GetTransferConfig):len(c.CallOptions.GetTransferConfig)], opts...) + var resp *datatransferpb.TransferConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetTransferConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListTransferConfigs returns information about all data transfers in the project. +func (c *Client) ListTransferConfigs(ctx context.Context, req *datatransferpb.ListTransferConfigsRequest, opts ...gax.CallOption) *TransferConfigIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListTransferConfigs[0:len(c.CallOptions.ListTransferConfigs):len(c.CallOptions.ListTransferConfigs)], opts...) + it := &TransferConfigIterator{} + req = proto.Clone(req).(*datatransferpb.ListTransferConfigsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*datatransferpb.TransferConfig, string, error) { + var resp *datatransferpb.ListTransferConfigsResponse + 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.ListTransferConfigs(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.TransferConfigs, 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 +} + +// ScheduleTransferRuns creates transfer runs for a time range [start_time, end_time]. +// For each date - or whatever granularity the data source supports - in the +// range, one transfer run is created. +// Note that runs are created per UTC time in the time range. +func (c *Client) ScheduleTransferRuns(ctx context.Context, req *datatransferpb.ScheduleTransferRunsRequest, opts ...gax.CallOption) (*datatransferpb.ScheduleTransferRunsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ScheduleTransferRuns[0:len(c.CallOptions.ScheduleTransferRuns):len(c.CallOptions.ScheduleTransferRuns)], opts...) + var resp *datatransferpb.ScheduleTransferRunsResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ScheduleTransferRuns(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetTransferRun returns information about the particular transfer run. +func (c *Client) GetTransferRun(ctx context.Context, req *datatransferpb.GetTransferRunRequest, opts ...gax.CallOption) (*datatransferpb.TransferRun, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetTransferRun[0:len(c.CallOptions.GetTransferRun):len(c.CallOptions.GetTransferRun)], opts...) + var resp *datatransferpb.TransferRun + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetTransferRun(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteTransferRun deletes the specified transfer run. +func (c *Client) DeleteTransferRun(ctx context.Context, req *datatransferpb.DeleteTransferRunRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteTransferRun[0:len(c.CallOptions.DeleteTransferRun):len(c.CallOptions.DeleteTransferRun)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteTransferRun(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListTransferRuns returns information about running and completed jobs. +func (c *Client) ListTransferRuns(ctx context.Context, req *datatransferpb.ListTransferRunsRequest, opts ...gax.CallOption) *TransferRunIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListTransferRuns[0:len(c.CallOptions.ListTransferRuns):len(c.CallOptions.ListTransferRuns)], opts...) + it := &TransferRunIterator{} + req = proto.Clone(req).(*datatransferpb.ListTransferRunsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*datatransferpb.TransferRun, string, error) { + var resp *datatransferpb.ListTransferRunsResponse + 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.ListTransferRuns(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.TransferRuns, 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 +} + +// ListTransferLogs returns user facing log messages for the data transfer run. +func (c *Client) ListTransferLogs(ctx context.Context, req *datatransferpb.ListTransferLogsRequest, opts ...gax.CallOption) *TransferMessageIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListTransferLogs[0:len(c.CallOptions.ListTransferLogs):len(c.CallOptions.ListTransferLogs)], opts...) + it := &TransferMessageIterator{} + req = proto.Clone(req).(*datatransferpb.ListTransferLogsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*datatransferpb.TransferMessage, string, error) { + var resp *datatransferpb.ListTransferLogsResponse + 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.ListTransferLogs(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.TransferMessages, 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 +} + +// CheckValidCreds returns true if valid credentials exist for the given data source and +// requesting user. +// Some data sources doesn't support service account, so we need to talk to +// them on behalf of the end user. This API just checks whether we have OAuth +// token for the particular user, which is a pre-requisite before user can +// create a transfer config. +func (c *Client) CheckValidCreds(ctx context.Context, req *datatransferpb.CheckValidCredsRequest, opts ...gax.CallOption) (*datatransferpb.CheckValidCredsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CheckValidCreds[0:len(c.CallOptions.CheckValidCreds):len(c.CallOptions.CheckValidCreds)], opts...) + var resp *datatransferpb.CheckValidCredsResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CheckValidCreds(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DataSourceIterator manages a stream of *datatransferpb.DataSource. +type DataSourceIterator struct { + items []*datatransferpb.DataSource + 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 []*datatransferpb.DataSource, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *DataSourceIterator) 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 *DataSourceIterator) Next() (*datatransferpb.DataSource, error) { + var item *datatransferpb.DataSource + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *DataSourceIterator) bufLen() int { + return len(it.items) +} + +func (it *DataSourceIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// TransferConfigIterator manages a stream of *datatransferpb.TransferConfig. +type TransferConfigIterator struct { + items []*datatransferpb.TransferConfig + 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 []*datatransferpb.TransferConfig, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *TransferConfigIterator) 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 *TransferConfigIterator) Next() (*datatransferpb.TransferConfig, error) { + var item *datatransferpb.TransferConfig + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *TransferConfigIterator) bufLen() int { + return len(it.items) +} + +func (it *TransferConfigIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// TransferMessageIterator manages a stream of *datatransferpb.TransferMessage. +type TransferMessageIterator struct { + items []*datatransferpb.TransferMessage + 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 []*datatransferpb.TransferMessage, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *TransferMessageIterator) 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 *TransferMessageIterator) Next() (*datatransferpb.TransferMessage, error) { + var item *datatransferpb.TransferMessage + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *TransferMessageIterator) bufLen() int { + return len(it.items) +} + +func (it *TransferMessageIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// TransferRunIterator manages a stream of *datatransferpb.TransferRun. +type TransferRunIterator struct { + items []*datatransferpb.TransferRun + 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 []*datatransferpb.TransferRun, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *TransferRunIterator) 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 *TransferRunIterator) Next() (*datatransferpb.TransferRun, error) { + var item *datatransferpb.TransferRun + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *TransferRunIterator) bufLen() int { + return len(it.items) +} + +func (it *TransferRunIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/data_transfer_client_example_test.go b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/data_transfer_client_example_test.go new file mode 100644 index 0000000000..e8ef24b491 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/data_transfer_client_example_test.go @@ -0,0 +1,288 @@ +// 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 datatransfer_test + +import ( + "cloud.google.com/go/bigquery/datatransfer/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + datatransferpb "google.golang.org/genproto/googleapis/cloud/bigquery/datatransfer/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_GetDataSource() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.GetDataSourceRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetDataSource(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListDataSources() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.ListDataSourcesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListDataSources(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_CreateTransferConfig() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.CreateTransferConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateTransferConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_UpdateTransferConfig() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.UpdateTransferConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateTransferConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_DeleteTransferConfig() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.DeleteTransferConfigRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteTransferConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_GetTransferConfig() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.GetTransferConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetTransferConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListTransferConfigs() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.ListTransferConfigsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListTransferConfigs(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_ScheduleTransferRuns() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.ScheduleTransferRunsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ScheduleTransferRuns(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_GetTransferRun() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.GetTransferRunRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetTransferRun(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_DeleteTransferRun() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.DeleteTransferRunRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteTransferRun(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_ListTransferRuns() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.ListTransferRunsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListTransferRuns(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_ListTransferLogs() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.ListTransferLogsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListTransferLogs(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_CheckValidCreds() { + ctx := context.Background() + c, err := datatransfer.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &datatransferpb.CheckValidCredsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CheckValidCreds(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/doc.go b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/doc.go new file mode 100644 index 0000000000..856e8f9362 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/doc.go @@ -0,0 +1,47 @@ +// 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 datatransfer is an auto-generated package for the +// BigQuery Data Transfer API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Transfers data from partner SaaS applications to Google BigQuery on a +// scheduled, managed basis. +package datatransfer // import "cloud.google.com/go/bigquery/datatransfer/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/mock_test.go b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/mock_test.go new file mode 100644 index 0000000000..5278318eb7 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/mock_test.go @@ -0,0 +1,1146 @@ +// 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 datatransfer + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + timestamppb "github.com/golang/protobuf/ptypes/timestamp" + datatransferpb "google.golang.org/genproto/googleapis/cloud/bigquery/datatransfer/v1" + field_maskpb "google.golang.org/genproto/protobuf/field_mask" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockDataTransferServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + datatransferpb.DataTransferServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockDataTransferServer) GetDataSource(ctx context.Context, req *datatransferpb.GetDataSourceRequest) (*datatransferpb.DataSource, 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].(*datatransferpb.DataSource), nil +} + +func (s *mockDataTransferServer) ListDataSources(ctx context.Context, req *datatransferpb.ListDataSourcesRequest) (*datatransferpb.ListDataSourcesResponse, 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].(*datatransferpb.ListDataSourcesResponse), nil +} + +func (s *mockDataTransferServer) CreateTransferConfig(ctx context.Context, req *datatransferpb.CreateTransferConfigRequest) (*datatransferpb.TransferConfig, 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].(*datatransferpb.TransferConfig), nil +} + +func (s *mockDataTransferServer) UpdateTransferConfig(ctx context.Context, req *datatransferpb.UpdateTransferConfigRequest) (*datatransferpb.TransferConfig, 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].(*datatransferpb.TransferConfig), nil +} + +func (s *mockDataTransferServer) DeleteTransferConfig(ctx context.Context, req *datatransferpb.DeleteTransferConfigRequest) (*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 *mockDataTransferServer) GetTransferConfig(ctx context.Context, req *datatransferpb.GetTransferConfigRequest) (*datatransferpb.TransferConfig, 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].(*datatransferpb.TransferConfig), nil +} + +func (s *mockDataTransferServer) ListTransferConfigs(ctx context.Context, req *datatransferpb.ListTransferConfigsRequest) (*datatransferpb.ListTransferConfigsResponse, 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].(*datatransferpb.ListTransferConfigsResponse), nil +} + +func (s *mockDataTransferServer) ScheduleTransferRuns(ctx context.Context, req *datatransferpb.ScheduleTransferRunsRequest) (*datatransferpb.ScheduleTransferRunsResponse, 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].(*datatransferpb.ScheduleTransferRunsResponse), nil +} + +func (s *mockDataTransferServer) GetTransferRun(ctx context.Context, req *datatransferpb.GetTransferRunRequest) (*datatransferpb.TransferRun, 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].(*datatransferpb.TransferRun), nil +} + +func (s *mockDataTransferServer) DeleteTransferRun(ctx context.Context, req *datatransferpb.DeleteTransferRunRequest) (*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 *mockDataTransferServer) ListTransferRuns(ctx context.Context, req *datatransferpb.ListTransferRunsRequest) (*datatransferpb.ListTransferRunsResponse, 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].(*datatransferpb.ListTransferRunsResponse), nil +} + +func (s *mockDataTransferServer) ListTransferLogs(ctx context.Context, req *datatransferpb.ListTransferLogsRequest) (*datatransferpb.ListTransferLogsResponse, 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].(*datatransferpb.ListTransferLogsResponse), nil +} + +func (s *mockDataTransferServer) CheckValidCreds(ctx context.Context, req *datatransferpb.CheckValidCredsRequest) (*datatransferpb.CheckValidCredsResponse, 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].(*datatransferpb.CheckValidCredsResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockDataTransfer mockDataTransferServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + datatransferpb.RegisterDataTransferServiceServer(serv, &mockDataTransfer) + + 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 TestDataTransferServiceGetDataSource(t *testing.T) { + var name2 string = "name2-1052831874" + var dataSourceId string = "dataSourceId-1015796374" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var clientId string = "clientId-1904089585" + var supportsMultipleTransfers bool = true + var updateDeadlineSeconds int32 = 991471694 + var defaultSchedule string = "defaultSchedule-800168235" + var supportsCustomSchedule bool = true + var helpUrl string = "helpUrl-789431439" + var defaultDataRefreshWindowDays int32 = 1804935157 + var manualRunsDisabled bool = true + var expectedResponse = &datatransferpb.DataSource{ + Name: name2, + DataSourceId: dataSourceId, + DisplayName: displayName, + Description: description, + ClientId: clientId, + SupportsMultipleTransfers: supportsMultipleTransfers, + UpdateDeadlineSeconds: updateDeadlineSeconds, + DefaultSchedule: defaultSchedule, + SupportsCustomSchedule: supportsCustomSchedule, + HelpUrl: helpUrl, + DefaultDataRefreshWindowDays: defaultDataRefreshWindowDays, + ManualRunsDisabled: manualRunsDisabled, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/dataSources/%s", "[PROJECT]", "[DATA_SOURCE]") + var request = &datatransferpb.GetDataSourceRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDataSource(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.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 TestDataTransferServiceGetDataSourceError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/dataSources/%s", "[PROJECT]", "[DATA_SOURCE]") + var request = &datatransferpb.GetDataSourceRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDataSource(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 TestDataTransferServiceListDataSources(t *testing.T) { + var nextPageToken string = "" + var dataSourcesElement *datatransferpb.DataSource = &datatransferpb.DataSource{} + var dataSources = []*datatransferpb.DataSource{dataSourcesElement} + var expectedResponse = &datatransferpb.ListDataSourcesResponse{ + NextPageToken: nextPageToken, + DataSources: dataSources, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &datatransferpb.ListDataSourcesRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDataSources(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.DataSources[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 TestDataTransferServiceListDataSourcesError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &datatransferpb.ListDataSourcesRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDataSources(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 TestDataTransferServiceCreateTransferConfig(t *testing.T) { + var name string = "name3373707" + var destinationDatasetId string = "destinationDatasetId1541564179" + var displayName string = "displayName1615086568" + var dataSourceId string = "dataSourceId-1015796374" + var schedule string = "schedule-697920873" + var dataRefreshWindowDays int32 = 327632845 + var disabled bool = true + var userId int64 = 147132913 + var datasetRegion string = "datasetRegion959248539" + var expectedResponse = &datatransferpb.TransferConfig{ + Name: name, + DestinationDatasetId: destinationDatasetId, + DisplayName: displayName, + DataSourceId: dataSourceId, + Schedule: schedule, + DataRefreshWindowDays: dataRefreshWindowDays, + Disabled: disabled, + UserId: userId, + DatasetRegion: datasetRegion, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var transferConfig *datatransferpb.TransferConfig = &datatransferpb.TransferConfig{} + var request = &datatransferpb.CreateTransferConfigRequest{ + Parent: formattedParent, + TransferConfig: transferConfig, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateTransferConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.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 TestDataTransferServiceCreateTransferConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var transferConfig *datatransferpb.TransferConfig = &datatransferpb.TransferConfig{} + var request = &datatransferpb.CreateTransferConfigRequest{ + Parent: formattedParent, + TransferConfig: transferConfig, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateTransferConfig(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 TestDataTransferServiceUpdateTransferConfig(t *testing.T) { + var name string = "name3373707" + var destinationDatasetId string = "destinationDatasetId1541564179" + var displayName string = "displayName1615086568" + var dataSourceId string = "dataSourceId-1015796374" + var schedule string = "schedule-697920873" + var dataRefreshWindowDays int32 = 327632845 + var disabled bool = true + var userId int64 = 147132913 + var datasetRegion string = "datasetRegion959248539" + var expectedResponse = &datatransferpb.TransferConfig{ + Name: name, + DestinationDatasetId: destinationDatasetId, + DisplayName: displayName, + DataSourceId: dataSourceId, + Schedule: schedule, + DataRefreshWindowDays: dataRefreshWindowDays, + Disabled: disabled, + UserId: userId, + DatasetRegion: datasetRegion, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var transferConfig *datatransferpb.TransferConfig = &datatransferpb.TransferConfig{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &datatransferpb.UpdateTransferConfigRequest{ + TransferConfig: transferConfig, + UpdateMask: updateMask, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateTransferConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.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 TestDataTransferServiceUpdateTransferConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var transferConfig *datatransferpb.TransferConfig = &datatransferpb.TransferConfig{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &datatransferpb.UpdateTransferConfigRequest{ + TransferConfig: transferConfig, + UpdateMask: updateMask, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateTransferConfig(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 TestDataTransferServiceDeleteTransferConfig(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var request = &datatransferpb.DeleteTransferConfigRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteTransferConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDataTransferServiceDeleteTransferConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var request = &datatransferpb.DeleteTransferConfigRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteTransferConfig(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 TestDataTransferServiceGetTransferConfig(t *testing.T) { + var name2 string = "name2-1052831874" + var destinationDatasetId string = "destinationDatasetId1541564179" + var displayName string = "displayName1615086568" + var dataSourceId string = "dataSourceId-1015796374" + var schedule string = "schedule-697920873" + var dataRefreshWindowDays int32 = 327632845 + var disabled bool = true + var userId int64 = 147132913 + var datasetRegion string = "datasetRegion959248539" + var expectedResponse = &datatransferpb.TransferConfig{ + Name: name2, + DestinationDatasetId: destinationDatasetId, + DisplayName: displayName, + DataSourceId: dataSourceId, + Schedule: schedule, + DataRefreshWindowDays: dataRefreshWindowDays, + Disabled: disabled, + UserId: userId, + DatasetRegion: datasetRegion, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var request = &datatransferpb.GetTransferConfigRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTransferConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.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 TestDataTransferServiceGetTransferConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var request = &datatransferpb.GetTransferConfigRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTransferConfig(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 TestDataTransferServiceListTransferConfigs(t *testing.T) { + var nextPageToken string = "" + var transferConfigsElement *datatransferpb.TransferConfig = &datatransferpb.TransferConfig{} + var transferConfigs = []*datatransferpb.TransferConfig{transferConfigsElement} + var expectedResponse = &datatransferpb.ListTransferConfigsResponse{ + NextPageToken: nextPageToken, + TransferConfigs: transferConfigs, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &datatransferpb.ListTransferConfigsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTransferConfigs(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.TransferConfigs[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 TestDataTransferServiceListTransferConfigsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &datatransferpb.ListTransferConfigsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTransferConfigs(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 TestDataTransferServiceScheduleTransferRuns(t *testing.T) { + var expectedResponse *datatransferpb.ScheduleTransferRunsResponse = &datatransferpb.ScheduleTransferRunsResponse{} + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var startTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var endTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var request = &datatransferpb.ScheduleTransferRunsRequest{ + Parent: formattedParent, + StartTime: startTime, + EndTime: endTime, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ScheduleTransferRuns(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.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 TestDataTransferServiceScheduleTransferRunsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var startTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var endTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var request = &datatransferpb.ScheduleTransferRunsRequest{ + Parent: formattedParent, + StartTime: startTime, + EndTime: endTime, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ScheduleTransferRuns(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 TestDataTransferServiceGetTransferRun(t *testing.T) { + var name2 string = "name2-1052831874" + var destinationDatasetId string = "destinationDatasetId1541564179" + var dataSourceId string = "dataSourceId-1015796374" + var userId int64 = 147132913 + var schedule string = "schedule-697920873" + var expectedResponse = &datatransferpb.TransferRun{ + Name: name2, + DestinationDatasetId: destinationDatasetId, + DataSourceId: dataSourceId, + UserId: userId, + Schedule: schedule, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s/runs/%s", "[PROJECT]", "[TRANSFER_CONFIG]", "[RUN]") + var request = &datatransferpb.GetTransferRunRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTransferRun(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.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 TestDataTransferServiceGetTransferRunError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s/runs/%s", "[PROJECT]", "[TRANSFER_CONFIG]", "[RUN]") + var request = &datatransferpb.GetTransferRunRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTransferRun(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 TestDataTransferServiceDeleteTransferRun(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s/runs/%s", "[PROJECT]", "[TRANSFER_CONFIG]", "[RUN]") + var request = &datatransferpb.DeleteTransferRunRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteTransferRun(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDataTransferServiceDeleteTransferRunError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/transferConfigs/%s/runs/%s", "[PROJECT]", "[TRANSFER_CONFIG]", "[RUN]") + var request = &datatransferpb.DeleteTransferRunRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteTransferRun(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 TestDataTransferServiceListTransferRuns(t *testing.T) { + var nextPageToken string = "" + var transferRunsElement *datatransferpb.TransferRun = &datatransferpb.TransferRun{} + var transferRuns = []*datatransferpb.TransferRun{transferRunsElement} + var expectedResponse = &datatransferpb.ListTransferRunsResponse{ + NextPageToken: nextPageToken, + TransferRuns: transferRuns, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var request = &datatransferpb.ListTransferRunsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTransferRuns(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.TransferRuns[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 TestDataTransferServiceListTransferRunsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/transferConfigs/%s", "[PROJECT]", "[TRANSFER_CONFIG]") + var request = &datatransferpb.ListTransferRunsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTransferRuns(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 TestDataTransferServiceListTransferLogs(t *testing.T) { + var nextPageToken string = "" + var transferMessagesElement *datatransferpb.TransferMessage = &datatransferpb.TransferMessage{} + var transferMessages = []*datatransferpb.TransferMessage{transferMessagesElement} + var expectedResponse = &datatransferpb.ListTransferLogsResponse{ + NextPageToken: nextPageToken, + TransferMessages: transferMessages, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/transferConfigs/%s/runs/%s", "[PROJECT]", "[TRANSFER_CONFIG]", "[RUN]") + var request = &datatransferpb.ListTransferLogsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTransferLogs(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.TransferMessages[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 TestDataTransferServiceListTransferLogsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/transferConfigs/%s/runs/%s", "[PROJECT]", "[TRANSFER_CONFIG]", "[RUN]") + var request = &datatransferpb.ListTransferLogsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTransferLogs(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 TestDataTransferServiceCheckValidCreds(t *testing.T) { + var hasValidCreds bool = false + var expectedResponse = &datatransferpb.CheckValidCredsResponse{ + HasValidCreds: hasValidCreds, + } + + mockDataTransfer.err = nil + mockDataTransfer.reqs = nil + + mockDataTransfer.resps = append(mockDataTransfer.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/dataSources/%s", "[PROJECT]", "[DATA_SOURCE]") + var request = &datatransferpb.CheckValidCredsRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CheckValidCreds(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDataTransfer.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 TestDataTransferServiceCheckValidCredsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDataTransfer.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/dataSources/%s", "[PROJECT]", "[DATA_SOURCE]") + var request = &datatransferpb.CheckValidCredsRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CheckValidCreds(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/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/path_funcs.go b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/path_funcs.go new file mode 100644 index 0000000000..89eb5bb17c --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/datatransfer/apiv1/path_funcs.go @@ -0,0 +1,135 @@ +// 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. + +package datatransfer + +// ProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func ProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// LocationPath returns the path for the location resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/locations/%s", project, location) +// instead. +func LocationPath(project, location string) string { + return "" + + "projects/" + + project + + "/locations/" + + location + + "" +} + +// LocationDataSourcePath returns the path for the location data source resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/locations/%s/dataSources/%s", project, location, dataSource) +// instead. +func LocationDataSourcePath(project, location, dataSource string) string { + return "" + + "projects/" + + project + + "/locations/" + + location + + "/dataSources/" + + dataSource + + "" +} + +// LocationTransferConfigPath returns the path for the location transfer config resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/locations/%s/transferConfigs/%s", project, location, transferConfig) +// instead. +func LocationTransferConfigPath(project, location, transferConfig string) string { + return "" + + "projects/" + + project + + "/locations/" + + location + + "/transferConfigs/" + + transferConfig + + "" +} + +// LocationRunPath returns the path for the location run resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/locations/%s/transferConfigs/%s/runs/%s", project, location, transferConfig, run) +// instead. +func LocationRunPath(project, location, transferConfig, run string) string { + return "" + + "projects/" + + project + + "/locations/" + + location + + "/transferConfigs/" + + transferConfig + + "/runs/" + + run + + "" +} + +// DataSourcePath returns the path for the data source resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/dataSources/%s", project, dataSource) +// instead. +func DataSourcePath(project, dataSource string) string { + return "" + + "projects/" + + project + + "/dataSources/" + + dataSource + + "" +} + +// TransferConfigPath returns the path for the transfer config resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/transferConfigs/%s", project, transferConfig) +// instead. +func TransferConfigPath(project, transferConfig string) string { + return "" + + "projects/" + + project + + "/transferConfigs/" + + transferConfig + + "" +} + +// RunPath returns the path for the run resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/transferConfigs/%s/runs/%s", project, transferConfig, run) +// instead. +func RunPath(project, transferConfig, run string) string { + return "" + + "projects/" + + project + + "/transferConfigs/" + + transferConfig + + "/runs/" + + run + + "" +} diff --git a/vendor/cloud.google.com/go/bigquery/doc.go b/vendor/cloud.google.com/go/bigquery/doc.go new file mode 100644 index 0000000000..c7048287be --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/doc.go @@ -0,0 +1,301 @@ +// Copyright 2015 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 bigquery provides a client for the BigQuery service. + +Note: This package is in beta. Some backwards-incompatible changes may occur. + +The following assumes a basic familiarity with BigQuery concepts. +See https://cloud.google.com/bigquery/docs. + +See https://godoc.org/cloud.google.com/go for authentication, timeouts, +connection pooling and similar aspects of this package. + + +Creating a Client + +To start working with this package, create a client: + + ctx := context.Background() + client, err := bigquery.NewClient(ctx, projectID) + if err != nil { + // TODO: Handle error. + } + +Querying + +To query existing tables, create a Query and call its Read method: + + q := client.Query(` + SELECT year, SUM(number) as num + FROM [bigquery-public-data:usa_names.usa_1910_2013] + WHERE name = "William" + GROUP BY year + ORDER BY year + `) + it, err := q.Read(ctx) + if err != nil { + // TODO: Handle error. + } + +Then iterate through the resulting rows. You can store a row using +anything that implements the ValueLoader interface, or with a slice or map of bigquery.Value. +A slice is simplest: + + for { + var values []bigquery.Value + err := it.Next(&values) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(values) + } + +You can also use a struct whose exported fields match the query: + + type Count struct { + Year int + Num int + } + for { + var c Count + err := it.Next(&c) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(c) + } + +You can also start the query running and get the results later. +Create the query as above, but call Run instead of Read. This returns a Job, +which represents an asynchronous operation. + + job, err := q.Run(ctx) + if err != nil { + // TODO: Handle error. + } + +Get the job's ID, a printable string. You can save this string to retrieve +the results at a later time, even in another process. + + jobID := job.ID() + fmt.Printf("The job ID is %s\n", jobID) + +To retrieve the job's results from the ID, first look up the Job: + + job, err = client.JobFromID(ctx, jobID) + if err != nil { + // TODO: Handle error. + } + +Use the Job.Read method to obtain an iterator, and loop over the rows. +Query.Read is just a convenience method that combines Query.Run and Job.Read. + + it, err = job.Read(ctx) + if err != nil { + // TODO: Handle error. + } + // Proceed with iteration as above. + +Datasets and Tables + +You can refer to datasets in the client's project with the Dataset method, and +in other projects with the DatasetInProject method: + + myDataset := client.Dataset("my_dataset") + yourDataset := client.DatasetInProject("your-project-id", "your_dataset") + +These methods create references to datasets, not the datasets themselves. You can have +a dataset reference even if the dataset doesn't exist yet. Use Dataset.Create to +create a dataset from a reference: + + if err := myDataset.Create(ctx, nil); err != nil { + // TODO: Handle error. + } + +You can refer to tables with Dataset.Table. Like bigquery.Dataset, bigquery.Table is a reference +to an object in BigQuery that may or may not exist. + + table := myDataset.Table("my_table") + +You can create, delete and update the metadata of tables with methods on Table. +For instance, you could create a temporary table with: + + err = myDataset.Table("temp").Create(ctx, &bigquery.TableMetadata{ + ExpirationTime: time.Now().Add(1*time.Hour)}) + if err != nil { + // TODO: Handle error. + } + +We'll see how to create a table with a schema in the next section. + +Schemas + +There are two ways to construct schemas with this package. +You can build a schema by hand, like so: + + schema1 := bigquery.Schema{ + {Name: "Name", Required: true, Type: bigquery.StringFieldType}, + {Name: "Grades", Repeated: true, Type: bigquery.IntegerFieldType}, + {Name: "Optional", Required: false, Type: bigquery.IntegerFieldType}, + } + +Or you can infer the schema from a struct: + + type student struct { + Name string + Grades []int + Optional bigquery.NullInt64 + } + schema2, err := bigquery.InferSchema(student{}) + if err != nil { + // TODO: Handle error. + } + // schema1 and schema2 are identical. + +Struct inference supports tags like those of the encoding/json package, so you can +change names, ignore fields, or mark a field as nullable (non-required). Fields +declared as one of the Null types (NullInt64, NullFloat64, NullString, NullBool, +NullTimestamp, NullDate, NullTime and NullDateTime) are automatically inferred as +nullable, so the "nullable" tag is only needed for []byte, *big.Rat and +pointer-to-struct fields. + + type student2 struct { + Name string `bigquery:"full_name"` + Grades []int + Secret string `bigquery:"-"` + Optional []byte `bigquery:",nullable" + } + schema3, err := bigquery.InferSchema(student2{}) + if err != nil { + // TODO: Handle error. + } + // schema3 has required fields "full_name" and "Grade", and nullable BYTES field "Optional". + +Having constructed a schema, you can create a table with it like so: + + if err := table.Create(ctx, &bigquery.TableMetadata{Schema: schema1}); err != nil { + // TODO: Handle error. + } + +Copying + +You can copy one or more tables to another table. Begin by constructing a Copier +describing the copy. Then set any desired copy options, and finally call Run to get a Job: + + copier := myDataset.Table("dest").CopierFrom(myDataset.Table("src")) + copier.WriteDisposition = bigquery.WriteTruncate + job, err = copier.Run(ctx) + if err != nil { + // TODO: Handle error. + } + +You can chain the call to Run if you don't want to set options: + + job, err = myDataset.Table("dest").CopierFrom(myDataset.Table("src")).Run(ctx) + if err != nil { + // TODO: Handle error. + } + +You can wait for your job to complete: + + status, err := job.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + +Job.Wait polls with exponential backoff. You can also poll yourself, if you +wish: + + for { + status, err := job.Status(ctx) + if err != nil { + // TODO: Handle error. + } + if status.Done() { + if status.Err() != nil { + log.Fatalf("Job failed with error %v", status.Err()) + } + break + } + time.Sleep(pollInterval) + } + +Loading and Uploading + +There are two ways to populate a table with this package: load the data from a Google Cloud Storage +object, or upload rows directly from your program. + +For loading, first create a GCSReference, configuring it if desired. Then make a Loader, optionally configure +it as well, and call its Run method. + + gcsRef := bigquery.NewGCSReference("gs://my-bucket/my-object") + gcsRef.AllowJaggedRows = true + loader := myDataset.Table("dest").LoaderFrom(gcsRef) + loader.CreateDisposition = bigquery.CreateNever + job, err = loader.Run(ctx) + // Poll the job for completion if desired, as above. + +To upload, first define a type that implements the ValueSaver interface, which has a single method named Save. +Then create an Uploader, and call its Put method with a slice of values. + + u := table.Uploader() + // Item implements the ValueSaver interface. + items := []*Item{ + {Name: "n1", Size: 32.6, Count: 7}, + {Name: "n2", Size: 4, Count: 2}, + {Name: "n3", Size: 101.5, Count: 1}, + } + if err := u.Put(ctx, items); err != nil { + // TODO: Handle error. + } + +You can also upload a struct that doesn't implement ValueSaver. Use the StructSaver type +to specify the schema and insert ID by hand, or just supply the struct or struct pointer +directly and the schema will be inferred: + + type Item2 struct { + Name string + Size float64 + Count int + } + // Item implements the ValueSaver interface. + items2 := []*Item2{ + {Name: "n1", Size: 32.6, Count: 7}, + {Name: "n2", Size: 4, Count: 2}, + {Name: "n3", Size: 101.5, Count: 1}, + } + if err := u.Put(ctx, items2); err != nil { + // TODO: Handle error. + } + +Extracting + +If you've been following so far, extracting data from a BigQuery table +into a Google Cloud Storage object will feel familiar. First create an +Extractor, then optionally configure it, and lastly call its Run method. + + extractor := table.ExtractorTo(gcsRef) + extractor.DisableHeader = true + job, err = extractor.Run(ctx) + // Poll the job for completion if desired, as above. +*/ +package bigquery // import "cloud.google.com/go/bigquery" diff --git a/vendor/cloud.google.com/go/bigquery/error.go b/vendor/cloud.google.com/go/bigquery/error.go new file mode 100644 index 0000000000..ab1c32236a --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/error.go @@ -0,0 +1,82 @@ +// Copyright 2015 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 bigquery + +import ( + "fmt" + + bq "google.golang.org/api/bigquery/v2" +) + +// An Error contains detailed information about a failed bigquery operation. +type Error struct { + // Mirrors bq.ErrorProto, but drops DebugInfo + Location, Message, Reason string +} + +func (e Error) Error() string { + return fmt.Sprintf("{Location: %q; Message: %q; Reason: %q}", e.Location, e.Message, e.Reason) +} + +func bqToError(ep *bq.ErrorProto) *Error { + if ep == nil { + return nil + } + return &Error{ + Location: ep.Location, + Message: ep.Message, + Reason: ep.Reason, + } +} + +// A MultiError contains multiple related errors. +type MultiError []error + +func (m MultiError) Error() string { + switch len(m) { + case 0: + return "(0 errors)" + case 1: + return m[0].Error() + case 2: + return m[0].Error() + " (and 1 other error)" + } + return fmt.Sprintf("%s (and %d other errors)", m[0].Error(), len(m)-1) +} + +// RowInsertionError contains all errors that occurred when attempting to insert a row. +type RowInsertionError struct { + InsertID string // The InsertID associated with the affected row. + RowIndex int // The 0-based index of the affected row in the batch of rows being inserted. + Errors MultiError +} + +func (e *RowInsertionError) Error() string { + errFmt := "insertion of row [insertID: %q; insertIndex: %v] failed with error: %s" + return fmt.Sprintf(errFmt, e.InsertID, e.RowIndex, e.Errors.Error()) +} + +// PutMultiError contains an error for each row which was not successfully inserted +// into a BigQuery table. +type PutMultiError []RowInsertionError + +func (pme PutMultiError) Error() string { + plural := "s" + if len(pme) == 1 { + plural = "" + } + + return fmt.Sprintf("%v row insertion%s failed", len(pme), plural) +} diff --git a/vendor/cloud.google.com/go/bigquery/error_test.go b/vendor/cloud.google.com/go/bigquery/error_test.go new file mode 100644 index 0000000000..b4ff2165bb --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/error_test.go @@ -0,0 +1,110 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "strings" + "testing" + + "cloud.google.com/go/internal/testutil" + + bq "google.golang.org/api/bigquery/v2" +) + +func rowInsertionError(msg string) RowInsertionError { + return RowInsertionError{Errors: []error{errors.New(msg)}} +} + +func TestPutMultiErrorString(t *testing.T) { + testCases := []struct { + errs PutMultiError + want string + }{ + { + errs: PutMultiError{}, + want: "0 row insertions failed", + }, + { + errs: PutMultiError{rowInsertionError("a")}, + want: "1 row insertion failed", + }, + { + errs: PutMultiError{rowInsertionError("a"), rowInsertionError("b")}, + want: "2 row insertions failed", + }, + } + + for _, tc := range testCases { + if tc.errs.Error() != tc.want { + t.Errorf("PutMultiError string: got:\n%v\nwant:\n%v", tc.errs.Error(), tc.want) + } + } +} + +func TestMultiErrorString(t *testing.T) { + testCases := []struct { + errs MultiError + want string + }{ + { + errs: MultiError{}, + want: "(0 errors)", + }, + { + errs: MultiError{errors.New("a")}, + want: "a", + }, + { + errs: MultiError{errors.New("a"), errors.New("b")}, + want: "a (and 1 other error)", + }, + { + errs: MultiError{errors.New("a"), errors.New("b"), errors.New("c")}, + want: "a (and 2 other errors)", + }, + } + + for _, tc := range testCases { + if tc.errs.Error() != tc.want { + t.Errorf("PutMultiError string: got:\n%v\nwant:\n%v", tc.errs.Error(), tc.want) + } + } +} + +func TestErrorFromErrorProto(t *testing.T) { + for _, test := range []struct { + in *bq.ErrorProto + want *Error + }{ + {nil, nil}, + { + in: &bq.ErrorProto{Location: "L", Message: "M", Reason: "R"}, + want: &Error{Location: "L", Message: "M", Reason: "R"}, + }, + } { + if got := bqToError(test.in); !testutil.Equal(got, test.want) { + t.Errorf("%v: got %v, want %v", test.in, got, test.want) + } + } +} + +func TestErrorString(t *testing.T) { + e := &Error{Location: "", Message: "", Reason: ""} + got := e.Error() + if !strings.Contains(got, "") || !strings.Contains(got, "") || !strings.Contains(got, "") { + t.Errorf(`got %q, expected to see "", "" and ""`, got) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/examples_test.go b/vendor/cloud.google.com/go/bigquery/examples_test.go new file mode 100644 index 0000000000..031b0cd47b --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/examples_test.go @@ -0,0 +1,829 @@ +// 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 bigquery_test + +import ( + "fmt" + "os" + "time" + + "cloud.google.com/go/bigquery" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleNewClient() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + _ = client // TODO: Use client. +} + +func ExampleClient_Dataset() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + ds := client.Dataset("my_dataset") + fmt.Println(ds) +} + +func ExampleClient_DatasetInProject() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + ds := client.DatasetInProject("their-project-id", "their-dataset") + fmt.Println(ds) +} + +func ExampleClient_Datasets() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Datasets(ctx) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleClient_DatasetsInProject() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.DatasetsInProject(ctx, "their-project-id") + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func getJobID() string { return "" } + +func ExampleClient_JobFromID() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + jobID := getJobID() // Get a job ID using Job.ID, the console or elsewhere. + job, err := client.JobFromID(ctx, jobID) + if err != nil { + // TODO: Handle error. + } + fmt.Println(job.LastStatus()) // Display the job's status. +} + +func ExampleClient_Jobs() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Jobs(ctx) + it.State = bigquery.Running // list only running jobs. + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleNewGCSReference() { + gcsRef := bigquery.NewGCSReference("gs://my-bucket/my-object") + fmt.Println(gcsRef) +} + +func ExampleClient_Query() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + q := client.Query("select name, num from t1") + q.DefaultProjectID = "project-id" + // TODO: set other options on the Query. + // TODO: Call Query.Run or Query.Read. +} + +func ExampleClient_Query_parameters() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + q := client.Query("select num from t1 where name = @user") + q.Parameters = []bigquery.QueryParameter{ + {Name: "user", Value: "Elizabeth"}, + } + // TODO: set other options on the Query. + // TODO: Call Query.Run or Query.Read. +} + +// This example demonstrates how to run a query job on a table +// with a customer-managed encryption key. The same +// applies to load and copy jobs as well. +func ExampleClient_Query_encryptionKey() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + q := client.Query("select name, num from t1") + // TODO: Replace this key with a key you have created in Cloud KMS. + keyName := "projects/P/locations/L/keyRings/R/cryptoKeys/K" + q.DestinationEncryptionConfig = &bigquery.EncryptionConfig{KMSKeyName: keyName} + // TODO: set other options on the Query. + // TODO: Call Query.Run or Query.Read. +} + +func ExampleQuery_Read() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + q := client.Query("select name, num from t1") + it, err := q.Read(ctx) + if err != nil { + // TODO: Handle error. + } + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleRowIterator_Next() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + q := client.Query("select name, num from t1") + it, err := q.Read(ctx) + if err != nil { + // TODO: Handle error. + } + for { + var row []bigquery.Value + err := it.Next(&row) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(row) + } +} + +func ExampleRowIterator_Next_struct() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + type score struct { + Name string + Num int + } + + q := client.Query("select name, num from t1") + it, err := q.Read(ctx) + if err != nil { + // TODO: Handle error. + } + for { + var s score + err := it.Next(&s) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(s) + } +} + +func ExampleJob_Read() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + q := client.Query("select name, num from t1") + // Call Query.Run to get a Job, then call Read on the job. + // Note: Query.Read is a shorthand for this. + job, err := q.Run(ctx) + if err != nil { + // TODO: Handle error. + } + it, err := job.Read(ctx) + if err != nil { + // TODO: Handle error. + } + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleJob_Wait() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + ds := client.Dataset("my_dataset") + job, err := ds.Table("t1").CopierFrom(ds.Table("t2")).Run(ctx) + if err != nil { + // TODO: Handle error. + } + status, err := job.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + if status.Err() != nil { + // TODO: Handle error. + } +} + +func ExampleJob_Config() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + ds := client.Dataset("my_dataset") + job, err := ds.Table("t1").CopierFrom(ds.Table("t2")).Run(ctx) + if err != nil { + // TODO: Handle error. + } + jc, err := job.Config() + if err != nil { + // TODO: Handle error. + } + copyConfig := jc.(*bigquery.CopyConfig) + fmt.Println(copyConfig.Dst, copyConfig.CreateDisposition) +} + +func ExampleDataset_Create() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + ds := client.Dataset("my_dataset") + if err := ds.Create(ctx, &bigquery.DatasetMetadata{Location: "EU"}); err != nil { + // TODO: Handle error. + } +} + +func ExampleDataset_Delete() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + if err := client.Dataset("my_dataset").Delete(ctx); err != nil { + // TODO: Handle error. + } +} + +func ExampleDataset_Metadata() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + md, err := client.Dataset("my_dataset").Metadata(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Println(md) +} + +// This example illustrates how to perform a read-modify-write sequence on dataset +// metadata. Passing the metadata's ETag to the Update call ensures that the call +// will fail if the metadata was changed since the read. +func ExampleDataset_Update_readModifyWrite() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + ds := client.Dataset("my_dataset") + md, err := ds.Metadata(ctx) + if err != nil { + // TODO: Handle error. + } + md2, err := ds.Update(ctx, + bigquery.DatasetMetadataToUpdate{Name: "new " + md.Name}, + md.ETag) + if err != nil { + // TODO: Handle error. + } + fmt.Println(md2) +} + +// To perform a blind write, ignoring the existing state (and possibly overwriting +// other updates), pass the empty string as the etag. +func ExampleDataset_Update_blindWrite() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + md, err := client.Dataset("my_dataset").Update(ctx, bigquery.DatasetMetadataToUpdate{Name: "blind"}, "") + if err != nil { + // TODO: Handle error. + } + fmt.Println(md) +} + +func ExampleDataset_Table() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // Table creates a reference to the table. It does not create the actual + // table in BigQuery; to do so, use Table.Create. + t := client.Dataset("my_dataset").Table("my_table") + fmt.Println(t) +} + +func ExampleDataset_Tables() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Dataset("my_dataset").Tables(ctx) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleDatasetIterator_Next() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Datasets(ctx) + for { + ds, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(ds) + } +} + +func ExampleInferSchema() { + type Item struct { + Name string + Size float64 + Count int + } + schema, err := bigquery.InferSchema(Item{}) + if err != nil { + fmt.Println(err) + // TODO: Handle error. + } + for _, fs := range schema { + fmt.Println(fs.Name, fs.Type) + } + // Output: + // Name STRING + // Size FLOAT + // Count INTEGER +} + +func ExampleInferSchema_tags() { + type Item struct { + Name string + Size float64 + Count int `bigquery:"number"` + Secret []byte `bigquery:"-"` + Optional bigquery.NullBool + OptBytes []byte `bigquery:",nullable"` + } + schema, err := bigquery.InferSchema(Item{}) + if err != nil { + fmt.Println(err) + // TODO: Handle error. + } + for _, fs := range schema { + fmt.Println(fs.Name, fs.Type, fs.Required) + } + // Output: + // Name STRING true + // Size FLOAT true + // number INTEGER true + // Optional BOOLEAN false + // OptBytes BYTES false +} + +func ExampleTable_Create() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + t := client.Dataset("my_dataset").Table("new-table") + if err := t.Create(ctx, nil); err != nil { + // TODO: Handle error. + } +} + +// Initialize a new table by passing TableMetadata to Table.Create. +func ExampleTable_Create_initialize() { + ctx := context.Background() + // Infer table schema from a Go type. + schema, err := bigquery.InferSchema(Item{}) + if err != nil { + // TODO: Handle error. + } + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + t := client.Dataset("my_dataset").Table("new-table") + if err := t.Create(ctx, + &bigquery.TableMetadata{ + Name: "My New Table", + Schema: schema, + ExpirationTime: time.Now().Add(24 * time.Hour), + }); err != nil { + // TODO: Handle error. + } +} + +// This example demonstrates how to create a table with +// a customer-managed encryption key. +func ExampleTable_Create_encryptionKey() { + ctx := context.Background() + // Infer table schema from a Go type. + schema, err := bigquery.InferSchema(Item{}) + if err != nil { + // TODO: Handle error. + } + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + t := client.Dataset("my_dataset").Table("new-table") + + // TODO: Replace this key with a key you have created in Cloud KMS. + keyName := "projects/P/locations/L/keyRings/R/cryptoKeys/K" + if err := t.Create(ctx, + &bigquery.TableMetadata{ + Name: "My New Table", + Schema: schema, + EncryptionConfig: &bigquery.EncryptionConfig{KMSKeyName: keyName}, + }); err != nil { + // TODO: Handle error. + } +} + +func ExampleTable_Delete() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + if err := client.Dataset("my_dataset").Table("my_table").Delete(ctx); err != nil { + // TODO: Handle error. + } +} + +func ExampleTable_Metadata() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + md, err := client.Dataset("my_dataset").Table("my_table").Metadata(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Println(md) +} + +func ExampleTable_Uploader() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + u := client.Dataset("my_dataset").Table("my_table").Uploader() + _ = u // TODO: Use u. +} + +func ExampleTable_Uploader_options() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + u := client.Dataset("my_dataset").Table("my_table").Uploader() + u.SkipInvalidRows = true + u.IgnoreUnknownValues = true + _ = u // TODO: Use u. +} + +func ExampleTable_CopierFrom() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + ds := client.Dataset("my_dataset") + c := ds.Table("combined").CopierFrom(ds.Table("t1"), ds.Table("t2")) + c.WriteDisposition = bigquery.WriteTruncate + // TODO: set other options on the Copier. + job, err := c.Run(ctx) + if err != nil { + // TODO: Handle error. + } + status, err := job.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + if status.Err() != nil { + // TODO: Handle error. + } +} + +func ExampleTable_ExtractorTo() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + gcsRef := bigquery.NewGCSReference("gs://my-bucket/my-object") + gcsRef.FieldDelimiter = ":" + // TODO: set other options on the GCSReference. + ds := client.Dataset("my_dataset") + extractor := ds.Table("my_table").ExtractorTo(gcsRef) + extractor.DisableHeader = true + // TODO: set other options on the Extractor. + job, err := extractor.Run(ctx) + if err != nil { + // TODO: Handle error. + } + status, err := job.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + if status.Err() != nil { + // TODO: Handle error. + } +} + +func ExampleTable_LoaderFrom() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + gcsRef := bigquery.NewGCSReference("gs://my-bucket/my-object") + gcsRef.AllowJaggedRows = true + gcsRef.MaxBadRecords = 5 + gcsRef.Schema = schema + // TODO: set other options on the GCSReference. + ds := client.Dataset("my_dataset") + loader := ds.Table("my_table").LoaderFrom(gcsRef) + loader.CreateDisposition = bigquery.CreateNever + // TODO: set other options on the Loader. + job, err := loader.Run(ctx) + if err != nil { + // TODO: Handle error. + } + status, err := job.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + if status.Err() != nil { + // TODO: Handle error. + } +} + +func ExampleTable_LoaderFrom_reader() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + f, err := os.Open("data.csv") + if err != nil { + // TODO: Handle error. + } + rs := bigquery.NewReaderSource(f) + rs.AllowJaggedRows = true + rs.MaxBadRecords = 5 + rs.Schema = schema + // TODO: set other options on the GCSReference. + ds := client.Dataset("my_dataset") + loader := ds.Table("my_table").LoaderFrom(rs) + loader.CreateDisposition = bigquery.CreateNever + // TODO: set other options on the Loader. + job, err := loader.Run(ctx) + if err != nil { + // TODO: Handle error. + } + status, err := job.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + if status.Err() != nil { + // TODO: Handle error. + } +} + +func ExampleTable_Read() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Dataset("my_dataset").Table("my_table").Read(ctx) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +// This example illustrates how to perform a read-modify-write sequence on table +// metadata. Passing the metadata's ETag to the Update call ensures that the call +// will fail if the metadata was changed since the read. +func ExampleTable_Update_readModifyWrite() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + t := client.Dataset("my_dataset").Table("my_table") + md, err := t.Metadata(ctx) + if err != nil { + // TODO: Handle error. + } + md2, err := t.Update(ctx, + bigquery.TableMetadataToUpdate{Name: "new " + md.Name}, + md.ETag) + if err != nil { + // TODO: Handle error. + } + fmt.Println(md2) +} + +// To perform a blind write, ignoring the existing state (and possibly overwriting +// other updates), pass the empty string as the etag. +func ExampleTable_Update_blindWrite() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + t := client.Dataset("my_dataset").Table("my_table") + tm, err := t.Update(ctx, bigquery.TableMetadataToUpdate{ + Description: "my favorite table", + }, "") + if err != nil { + // TODO: Handle error. + } + fmt.Println(tm) +} + +func ExampleTableIterator_Next() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Dataset("my_dataset").Tables(ctx) + for { + t, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(t) + } +} + +type Item struct { + Name string + Size float64 + Count int +} + +// Save implements the ValueSaver interface. +func (i *Item) Save() (map[string]bigquery.Value, string, error) { + return map[string]bigquery.Value{ + "Name": i.Name, + "Size": i.Size, + "Count": i.Count, + }, "", nil +} + +func ExampleUploader_Put() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + u := client.Dataset("my_dataset").Table("my_table").Uploader() + // Item implements the ValueSaver interface. + items := []*Item{ + {Name: "n1", Size: 32.6, Count: 7}, + {Name: "n2", Size: 4, Count: 2}, + {Name: "n3", Size: 101.5, Count: 1}, + } + if err := u.Put(ctx, items); err != nil { + // TODO: Handle error. + } +} + +var schema bigquery.Schema + +func ExampleUploader_Put_structSaver() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + u := client.Dataset("my_dataset").Table("my_table").Uploader() + + type score struct { + Name string + Num int + } + + // Assume schema holds the table's schema. + savers := []*bigquery.StructSaver{ + {Struct: score{Name: "n1", Num: 12}, Schema: schema, InsertID: "id1"}, + {Struct: score{Name: "n2", Num: 31}, Schema: schema, InsertID: "id2"}, + {Struct: score{Name: "n3", Num: 7}, Schema: schema, InsertID: "id3"}, + } + if err := u.Put(ctx, savers); err != nil { + // TODO: Handle error. + } +} + +func ExampleUploader_Put_struct() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + u := client.Dataset("my_dataset").Table("my_table").Uploader() + + type score struct { + Name string + Num int + } + scores := []score{ + {Name: "n1", Num: 12}, + {Name: "n2", Num: 31}, + {Name: "n3", Num: 7}, + } + // Schema is inferred from the score type. + if err := u.Put(ctx, scores); err != nil { + // TODO: Handle error. + } +} + +func ExampleUploader_Put_valuesSaver() { + ctx := context.Background() + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + u := client.Dataset("my_dataset").Table("my_table").Uploader() + + var vss []*bigquery.ValuesSaver + for i, name := range []string{"n1", "n2", "n3"} { + // Assume schema holds the table's schema. + vss = append(vss, &bigquery.ValuesSaver{ + Schema: schema, + InsertID: name, + Row: []bigquery.Value{name, int64(i)}, + }) + } + + if err := u.Put(ctx, vss); err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/bigquery/external.go b/vendor/cloud.google.com/go/bigquery/external.go new file mode 100644 index 0000000000..2ceb38d54c --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/external.go @@ -0,0 +1,400 @@ +// Copyright 2017 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 bigquery + +import ( + "encoding/base64" + "unicode/utf8" + + bq "google.golang.org/api/bigquery/v2" +) + +// DataFormat describes the format of BigQuery table data. +type DataFormat string + +// Constants describing the format of BigQuery table data. +const ( + CSV DataFormat = "CSV" + Avro DataFormat = "AVRO" + JSON DataFormat = "NEWLINE_DELIMITED_JSON" + DatastoreBackup DataFormat = "DATASTORE_BACKUP" + GoogleSheets DataFormat = "GOOGLE_SHEETS" + Bigtable DataFormat = "BIGTABLE" + Parquet DataFormat = "PARQUET" + ORC DataFormat = "ORC" +) + +// ExternalData is a table which is stored outside of BigQuery. It is implemented by +// *ExternalDataConfig. +// GCSReference also implements it, for backwards compatibility. +type ExternalData interface { + toBQ() bq.ExternalDataConfiguration +} + +// ExternalDataConfig describes data external to BigQuery that can be used +// in queries and to create external tables. +type ExternalDataConfig struct { + // The format of the data. Required. + SourceFormat DataFormat + + // The fully-qualified URIs that point to your + // data in Google Cloud. Required. + // + // For Google Cloud Storage URIs, each URI can contain one '*' wildcard character + // and it must come after the 'bucket' name. Size limits related to load jobs + // apply to external data sources. + // + // For Google Cloud Bigtable URIs, exactly one URI can be specified and it has be + // a fully specified and valid HTTPS URL for a Google Cloud Bigtable table. + // + // For Google Cloud Datastore backups, exactly one URI can be specified. Also, + // the '*' wildcard character is not allowed. + SourceURIs []string + + // The schema of the data. Required for CSV and JSON; disallowed for the + // other formats. + Schema Schema + + // Try to detect schema and format options automatically. + // Any option specified explicitly will be honored. + AutoDetect bool + + // The compression type of the data. + Compression Compression + + // IgnoreUnknownValues causes values not matching the schema to be + // tolerated. Unknown values are ignored. For CSV this ignores extra values + // at the end of a line. For JSON this ignores named values that do not + // match any column name. If this field is not set, records containing + // unknown values are treated as bad records. The MaxBadRecords field can + // be used to customize how bad records are handled. + IgnoreUnknownValues bool + + // MaxBadRecords is the maximum number of bad records that will be ignored + // when reading data. + MaxBadRecords int64 + + // Additional options for CSV, GoogleSheets and Bigtable formats. + Options ExternalDataConfigOptions +} + +func (e *ExternalDataConfig) toBQ() bq.ExternalDataConfiguration { + q := bq.ExternalDataConfiguration{ + SourceFormat: string(e.SourceFormat), + SourceUris: e.SourceURIs, + Autodetect: e.AutoDetect, + Compression: string(e.Compression), + IgnoreUnknownValues: e.IgnoreUnknownValues, + MaxBadRecords: e.MaxBadRecords, + } + if e.Schema != nil { + q.Schema = e.Schema.toBQ() + } + if e.Options != nil { + e.Options.populateExternalDataConfig(&q) + } + return q +} + +func bqToExternalDataConfig(q *bq.ExternalDataConfiguration) (*ExternalDataConfig, error) { + e := &ExternalDataConfig{ + SourceFormat: DataFormat(q.SourceFormat), + SourceURIs: q.SourceUris, + AutoDetect: q.Autodetect, + Compression: Compression(q.Compression), + IgnoreUnknownValues: q.IgnoreUnknownValues, + MaxBadRecords: q.MaxBadRecords, + Schema: bqToSchema(q.Schema), + } + switch { + case q.CsvOptions != nil: + e.Options = bqToCSVOptions(q.CsvOptions) + case q.GoogleSheetsOptions != nil: + e.Options = bqToGoogleSheetsOptions(q.GoogleSheetsOptions) + case q.BigtableOptions != nil: + var err error + e.Options, err = bqToBigtableOptions(q.BigtableOptions) + if err != nil { + return nil, err + } + } + return e, nil +} + +// ExternalDataConfigOptions are additional options for external data configurations. +// This interface is implemented by CSVOptions, GoogleSheetsOptions and BigtableOptions. +type ExternalDataConfigOptions interface { + populateExternalDataConfig(*bq.ExternalDataConfiguration) +} + +// CSVOptions are additional options for CSV external data sources. +type CSVOptions struct { + // AllowJaggedRows causes missing trailing optional columns to be tolerated + // when reading CSV data. Missing values are treated as nulls. + AllowJaggedRows bool + + // AllowQuotedNewlines sets whether quoted data sections containing + // newlines are allowed when reading CSV data. + AllowQuotedNewlines bool + + // Encoding is the character encoding of data to be read. + Encoding Encoding + + // FieldDelimiter is the separator for fields in a CSV file, used when + // reading or exporting data. The default is ",". + FieldDelimiter string + + // Quote is the value used to quote data sections in a CSV file. The + // default quotation character is the double quote ("), which is used if + // both Quote and ForceZeroQuote are unset. + // To specify that no character should be interpreted as a quotation + // character, set ForceZeroQuote to true. + // Only used when reading data. + Quote string + ForceZeroQuote bool + + // The number of rows at the top of a CSV file that BigQuery will skip when + // reading data. + SkipLeadingRows int64 +} + +func (o *CSVOptions) populateExternalDataConfig(c *bq.ExternalDataConfiguration) { + c.CsvOptions = &bq.CsvOptions{ + AllowJaggedRows: o.AllowJaggedRows, + AllowQuotedNewlines: o.AllowQuotedNewlines, + Encoding: string(o.Encoding), + FieldDelimiter: o.FieldDelimiter, + Quote: o.quote(), + SkipLeadingRows: o.SkipLeadingRows, + } +} + +// quote returns the CSV quote character, or nil if unset. +func (o *CSVOptions) quote() *string { + if o.ForceZeroQuote { + quote := "" + return "e + } + if o.Quote == "" { + return nil + } + return &o.Quote +} + +func (o *CSVOptions) setQuote(ps *string) { + if ps != nil { + o.Quote = *ps + if o.Quote == "" { + o.ForceZeroQuote = true + } + } +} + +func bqToCSVOptions(q *bq.CsvOptions) *CSVOptions { + o := &CSVOptions{ + AllowJaggedRows: q.AllowJaggedRows, + AllowQuotedNewlines: q.AllowQuotedNewlines, + Encoding: Encoding(q.Encoding), + FieldDelimiter: q.FieldDelimiter, + SkipLeadingRows: q.SkipLeadingRows, + } + o.setQuote(q.Quote) + return o +} + +// GoogleSheetsOptions are additional options for GoogleSheets external data sources. +type GoogleSheetsOptions struct { + // The number of rows at the top of a sheet that BigQuery will skip when + // reading data. + SkipLeadingRows int64 +} + +func (o *GoogleSheetsOptions) populateExternalDataConfig(c *bq.ExternalDataConfiguration) { + c.GoogleSheetsOptions = &bq.GoogleSheetsOptions{ + SkipLeadingRows: o.SkipLeadingRows, + } +} + +func bqToGoogleSheetsOptions(q *bq.GoogleSheetsOptions) *GoogleSheetsOptions { + return &GoogleSheetsOptions{ + SkipLeadingRows: q.SkipLeadingRows, + } +} + +// BigtableOptions are additional options for Bigtable external data sources. +type BigtableOptions struct { + // A list of column families to expose in the table schema along with their + // types. If omitted, all column families are present in the table schema and + // their values are read as BYTES. + ColumnFamilies []*BigtableColumnFamily + + // If true, then the column families that are not specified in columnFamilies + // list are not exposed in the table schema. Otherwise, they are read with BYTES + // type values. The default is false. + IgnoreUnspecifiedColumnFamilies bool + + // If true, then the rowkey column families will be read and converted to string. + // Otherwise they are read with BYTES type values and users need to manually cast + // them with CAST if necessary. The default is false. + ReadRowkeyAsString bool +} + +func (o *BigtableOptions) populateExternalDataConfig(c *bq.ExternalDataConfiguration) { + q := &bq.BigtableOptions{ + IgnoreUnspecifiedColumnFamilies: o.IgnoreUnspecifiedColumnFamilies, + ReadRowkeyAsString: o.ReadRowkeyAsString, + } + for _, f := range o.ColumnFamilies { + q.ColumnFamilies = append(q.ColumnFamilies, f.toBQ()) + } + c.BigtableOptions = q +} + +func bqToBigtableOptions(q *bq.BigtableOptions) (*BigtableOptions, error) { + b := &BigtableOptions{ + IgnoreUnspecifiedColumnFamilies: q.IgnoreUnspecifiedColumnFamilies, + ReadRowkeyAsString: q.ReadRowkeyAsString, + } + for _, f := range q.ColumnFamilies { + f2, err := bqToBigtableColumnFamily(f) + if err != nil { + return nil, err + } + b.ColumnFamilies = append(b.ColumnFamilies, f2) + } + return b, nil +} + +// BigtableColumnFamily describes how BigQuery should access a Bigtable column family. +type BigtableColumnFamily struct { + // Identifier of the column family. + FamilyID string + + // Lists of columns that should be exposed as individual fields as opposed to a + // list of (column name, value) pairs. All columns whose qualifier matches a + // qualifier in this list can be accessed as .. Other columns can be accessed as + // a list through .Column field. + Columns []*BigtableColumn + + // The encoding of the values when the type is not STRING. Acceptable encoding values are: + // - TEXT - indicates values are alphanumeric text strings. + // - BINARY - indicates values are encoded using HBase Bytes.toBytes family of functions. + // This can be overridden for a specific column by listing that column in 'columns' and + // specifying an encoding for it. + Encoding string + + // If true, only the latest version of values are exposed for all columns in this + // column family. This can be overridden for a specific column by listing that + // column in 'columns' and specifying a different setting for that column. + OnlyReadLatest bool + + // The type to convert the value in cells of this + // column family. The values are expected to be encoded using HBase + // Bytes.toBytes function when using the BINARY encoding value. + // Following BigQuery types are allowed (case-sensitive): + // BYTES STRING INTEGER FLOAT BOOLEAN. + // The default type is BYTES. This can be overridden for a specific column by + // listing that column in 'columns' and specifying a type for it. + Type string +} + +func (b *BigtableColumnFamily) toBQ() *bq.BigtableColumnFamily { + q := &bq.BigtableColumnFamily{ + FamilyId: b.FamilyID, + Encoding: b.Encoding, + OnlyReadLatest: b.OnlyReadLatest, + Type: b.Type, + } + for _, col := range b.Columns { + q.Columns = append(q.Columns, col.toBQ()) + } + return q +} + +func bqToBigtableColumnFamily(q *bq.BigtableColumnFamily) (*BigtableColumnFamily, error) { + b := &BigtableColumnFamily{ + FamilyID: q.FamilyId, + Encoding: q.Encoding, + OnlyReadLatest: q.OnlyReadLatest, + Type: q.Type, + } + for _, col := range q.Columns { + c, err := bqToBigtableColumn(col) + if err != nil { + return nil, err + } + b.Columns = append(b.Columns, c) + } + return b, nil +} + +// BigtableColumn describes how BigQuery should access a Bigtable column. +type BigtableColumn struct { + // Qualifier of the column. Columns in the parent column family that have this + // exact qualifier are exposed as . field. The column field name is the + // same as the column qualifier. + Qualifier string + + // If the qualifier is not a valid BigQuery field identifier i.e. does not match + // [a-zA-Z][a-zA-Z0-9_]*, a valid identifier must be provided as the column field + // name and is used as field name in queries. + FieldName string + + // If true, only the latest version of values are exposed for this column. + // See BigtableColumnFamily.OnlyReadLatest. + OnlyReadLatest bool + + // The encoding of the values when the type is not STRING. + // See BigtableColumnFamily.Encoding + Encoding string + + // The type to convert the value in cells of this column. + // See BigtableColumnFamily.Type + Type string +} + +func (b *BigtableColumn) toBQ() *bq.BigtableColumn { + q := &bq.BigtableColumn{ + FieldName: b.FieldName, + OnlyReadLatest: b.OnlyReadLatest, + Encoding: b.Encoding, + Type: b.Type, + } + if utf8.ValidString(b.Qualifier) { + q.QualifierString = b.Qualifier + } else { + q.QualifierEncoded = base64.RawStdEncoding.EncodeToString([]byte(b.Qualifier)) + } + return q +} + +func bqToBigtableColumn(q *bq.BigtableColumn) (*BigtableColumn, error) { + b := &BigtableColumn{ + FieldName: q.FieldName, + OnlyReadLatest: q.OnlyReadLatest, + Encoding: q.Encoding, + Type: q.Type, + } + if q.QualifierString != "" { + b.Qualifier = q.QualifierString + } else { + bytes, err := base64.RawStdEncoding.DecodeString(q.QualifierEncoded) + if err != nil { + return nil, err + } + b.Qualifier = string(bytes) + } + return b, nil +} diff --git a/vendor/cloud.google.com/go/bigquery/external_test.go b/vendor/cloud.google.com/go/bigquery/external_test.go new file mode 100644 index 0000000000..454bb08679 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/external_test.go @@ -0,0 +1,143 @@ +// Copyright 2017 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 bigquery + +import ( + "testing" + + "cloud.google.com/go/internal/pretty" + "cloud.google.com/go/internal/testutil" +) + +func TestExternalDataConfig(t *testing.T) { + // Round-trip of ExternalDataConfig to underlying representation. + for i, want := range []*ExternalDataConfig{ + { + SourceFormat: CSV, + SourceURIs: []string{"uri"}, + Schema: Schema{{Name: "n", Type: IntegerFieldType}}, + AutoDetect: true, + Compression: Gzip, + IgnoreUnknownValues: true, + MaxBadRecords: 17, + Options: &CSVOptions{ + AllowJaggedRows: true, + AllowQuotedNewlines: true, + Encoding: UTF_8, + FieldDelimiter: "f", + Quote: "q", + SkipLeadingRows: 3, + }, + }, + { + SourceFormat: GoogleSheets, + Options: &GoogleSheetsOptions{SkipLeadingRows: 4}, + }, + { + SourceFormat: Bigtable, + Options: &BigtableOptions{ + IgnoreUnspecifiedColumnFamilies: true, + ReadRowkeyAsString: true, + ColumnFamilies: []*BigtableColumnFamily{ + { + FamilyID: "f1", + Encoding: "TEXT", + OnlyReadLatest: true, + Type: "FLOAT", + Columns: []*BigtableColumn{ + { + Qualifier: "valid-utf-8", + FieldName: "fn", + OnlyReadLatest: true, + Encoding: "BINARY", + Type: "STRING", + }, + }, + }, + }, + }, + }, + } { + q := want.toBQ() + got, err := bqToExternalDataConfig(&q) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("#%d: got=-, want=+:\n%s", i, diff) + } + } +} + +func TestQuote(t *testing.T) { + ptr := func(s string) *string { return &s } + + for _, test := range []struct { + quote string + force bool + want *string + }{ + {"", false, nil}, + {"", true, ptr("")}, + {"-", false, ptr("-")}, + {"-", true, ptr("")}, + } { + o := CSVOptions{ + Quote: test.quote, + ForceZeroQuote: test.force, + } + got := o.quote() + if (got == nil) != (test.want == nil) { + t.Errorf("%+v\ngot %v\nwant %v", test, pretty.Value(got), pretty.Value(test.want)) + } + if got != nil && test.want != nil && *got != *test.want { + t.Errorf("%+v: got %q, want %q", test, *got, *test.want) + } + } +} + +func TestQualifier(t *testing.T) { + b := BigtableColumn{Qualifier: "a"} + q := b.toBQ() + if q.QualifierString != b.Qualifier || q.QualifierEncoded != "" { + t.Errorf("got (%q, %q), want (%q, %q)", + q.QualifierString, q.QualifierEncoded, b.Qualifier, "") + } + b2, err := bqToBigtableColumn(q) + if err != nil { + t.Fatal(err) + } + if got, want := b2.Qualifier, b.Qualifier; got != want { + t.Errorf("got %q, want %q", got, want) + } + + const ( + invalidUTF8 = "\xDF\xFF" + invalidEncoded = "3/8" + ) + b = BigtableColumn{Qualifier: invalidUTF8} + q = b.toBQ() + if q.QualifierString != "" || q.QualifierEncoded != invalidEncoded { + t.Errorf("got (%q, %q), want (%q, %q)", + q.QualifierString, "", b.Qualifier, invalidEncoded) + } + b2, err = bqToBigtableColumn(q) + if err != nil { + t.Fatal(err) + } + if got, want := b2.Qualifier, b.Qualifier; got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/extract.go b/vendor/cloud.google.com/go/bigquery/extract.go new file mode 100644 index 0000000000..d31ecaaf4f --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/extract.go @@ -0,0 +1,109 @@ +// 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 bigquery + +import ( + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" +) + +// ExtractConfig holds the configuration for an extract job. +type ExtractConfig struct { + // Src is the table from which data will be extracted. + Src *Table + + // Dst is the destination into which the data will be extracted. + Dst *GCSReference + + // DisableHeader disables the printing of a header row in exported data. + DisableHeader bool + + // The labels associated with this job. + Labels map[string]string +} + +func (e *ExtractConfig) toBQ() *bq.JobConfiguration { + var printHeader *bool + if e.DisableHeader { + f := false + printHeader = &f + } + return &bq.JobConfiguration{ + Labels: e.Labels, + Extract: &bq.JobConfigurationExtract{ + DestinationUris: append([]string{}, e.Dst.URIs...), + Compression: string(e.Dst.Compression), + DestinationFormat: string(e.Dst.DestinationFormat), + FieldDelimiter: e.Dst.FieldDelimiter, + SourceTable: e.Src.toBQ(), + PrintHeader: printHeader, + }, + } +} + +func bqToExtractConfig(q *bq.JobConfiguration, c *Client) *ExtractConfig { + qe := q.Extract + return &ExtractConfig{ + Labels: q.Labels, + Dst: &GCSReference{ + URIs: qe.DestinationUris, + Compression: Compression(qe.Compression), + DestinationFormat: DataFormat(qe.DestinationFormat), + FileConfig: FileConfig{ + CSVOptions: CSVOptions{ + FieldDelimiter: qe.FieldDelimiter, + }, + }, + }, + DisableHeader: qe.PrintHeader != nil && !*qe.PrintHeader, + Src: bqToTable(qe.SourceTable, c), + } +} + +// An Extractor extracts data from a BigQuery table into Google Cloud Storage. +type Extractor struct { + JobIDConfig + ExtractConfig + c *Client +} + +// ExtractorTo returns an Extractor which can be used to extract data from a +// BigQuery table into Google Cloud Storage. +// The returned Extractor may optionally be further configured before its Run method is called. +func (t *Table) ExtractorTo(dst *GCSReference) *Extractor { + return &Extractor{ + c: t.c, + ExtractConfig: ExtractConfig{ + Src: t, + Dst: dst, + }, + } +} + +// Run initiates an extract job. +func (e *Extractor) Run(ctx context.Context) (j *Job, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Extractor.Run") + defer func() { trace.EndSpan(ctx, err) }() + + return e.c.insertJob(ctx, e.newJob(), nil) +} + +func (e *Extractor) newJob() *bq.Job { + return &bq.Job{ + JobReference: e.JobIDConfig.createJobRef(e.c), + Configuration: e.ExtractConfig.toBQ(), + } +} diff --git a/vendor/cloud.google.com/go/bigquery/extract_test.go b/vendor/cloud.google.com/go/bigquery/extract_test.go new file mode 100644 index 0000000000..ccaae80ea1 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/extract_test.go @@ -0,0 +1,118 @@ +// Copyright 2015 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 bigquery + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/internal/testutil" + + bq "google.golang.org/api/bigquery/v2" +) + +func defaultExtractJob() *bq.Job { + return &bq.Job{ + JobReference: &bq.JobReference{JobId: "RANDOM", ProjectId: "client-project-id"}, + Configuration: &bq.JobConfiguration{ + Extract: &bq.JobConfigurationExtract{ + SourceTable: &bq.TableReference{ + ProjectId: "client-project-id", + DatasetId: "dataset-id", + TableId: "table-id", + }, + DestinationUris: []string{"uri"}, + }, + }, + } +} + +func defaultGCS() *GCSReference { + return &GCSReference{ + URIs: []string{"uri"}, + } +} + +func TestExtract(t *testing.T) { + defer fixRandomID("RANDOM")() + c := &Client{ + projectID: "client-project-id", + } + + testCases := []struct { + dst *GCSReference + src *Table + config ExtractConfig + want *bq.Job + }{ + { + dst: defaultGCS(), + src: c.Dataset("dataset-id").Table("table-id"), + want: defaultExtractJob(), + }, + { + dst: defaultGCS(), + src: c.Dataset("dataset-id").Table("table-id"), + config: ExtractConfig{ + DisableHeader: true, + Labels: map[string]string{"a": "b"}, + }, + want: func() *bq.Job { + j := defaultExtractJob() + j.Configuration.Labels = map[string]string{"a": "b"} + f := false + j.Configuration.Extract.PrintHeader = &f + return j + }(), + }, + { + dst: func() *GCSReference { + g := NewGCSReference("uri") + g.Compression = Gzip + g.DestinationFormat = JSON + g.FieldDelimiter = "\t" + return g + }(), + src: c.Dataset("dataset-id").Table("table-id"), + want: func() *bq.Job { + j := defaultExtractJob() + j.Configuration.Extract.Compression = "GZIP" + j.Configuration.Extract.DestinationFormat = "NEWLINE_DELIMITED_JSON" + j.Configuration.Extract.FieldDelimiter = "\t" + return j + }(), + }, + } + + for i, tc := range testCases { + ext := tc.src.ExtractorTo(tc.dst) + tc.config.Src = ext.Src + tc.config.Dst = ext.Dst + ext.ExtractConfig = tc.config + got := ext.newJob() + checkJob(t, i, got, tc.want) + + jc, err := bqToJobConfig(got.Configuration, c) + if err != nil { + t.Fatalf("#%d: %v", i, err) + } + diff := testutil.Diff(jc, &ext.ExtractConfig, + cmp.AllowUnexported(Table{}, Client{})) + if diff != "" { + t.Errorf("#%d: (got=-, want=+:\n%s", i, diff) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/file.go b/vendor/cloud.google.com/go/bigquery/file.go new file mode 100644 index 0000000000..a965b4594a --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/file.go @@ -0,0 +1,135 @@ +// 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 bigquery + +import ( + "io" + + bq "google.golang.org/api/bigquery/v2" +) + +// A ReaderSource is a source for a load operation that gets +// data from an io.Reader. +// +// When a ReaderSource is part of a LoadConfig obtained via Job.Config, +// its internal io.Reader will be nil, so it cannot be used for a +// subsequent load operation. +type ReaderSource struct { + r io.Reader + FileConfig +} + +// NewReaderSource creates a ReaderSource from an io.Reader. You may +// optionally configure properties on the ReaderSource that describe the +// data being read, before passing it to Table.LoaderFrom. +func NewReaderSource(r io.Reader) *ReaderSource { + return &ReaderSource{r: r} +} + +func (r *ReaderSource) populateLoadConfig(lc *bq.JobConfigurationLoad) io.Reader { + r.FileConfig.populateLoadConfig(lc) + return r.r +} + +// FileConfig contains configuration options that pertain to files, typically +// text files that require interpretation to be used as a BigQuery table. A +// file may live in Google Cloud Storage (see GCSReference), or it may be +// loaded into a table via the Table.LoaderFromReader. +type FileConfig struct { + // SourceFormat is the format of the data to be read. + // Allowed values are: Avro, CSV, DatastoreBackup, JSON, ORC, and Parquet. The default is CSV. + SourceFormat DataFormat + + // Indicates if we should automatically infer the options and + // schema for CSV and JSON sources. + AutoDetect bool + + // MaxBadRecords is the maximum number of bad records that will be ignored + // when reading data. + MaxBadRecords int64 + + // IgnoreUnknownValues causes values not matching the schema to be + // tolerated. Unknown values are ignored. For CSV this ignores extra values + // at the end of a line. For JSON this ignores named values that do not + // match any column name. If this field is not set, records containing + // unknown values are treated as bad records. The MaxBadRecords field can + // be used to customize how bad records are handled. + IgnoreUnknownValues bool + + // Schema describes the data. It is required when reading CSV or JSON data, + // unless the data is being loaded into a table that already exists. + Schema Schema + + // Additional options for CSV files. + CSVOptions +} + +func (fc *FileConfig) populateLoadConfig(conf *bq.JobConfigurationLoad) { + conf.SkipLeadingRows = fc.SkipLeadingRows + conf.SourceFormat = string(fc.SourceFormat) + conf.Autodetect = fc.AutoDetect + conf.AllowJaggedRows = fc.AllowJaggedRows + conf.AllowQuotedNewlines = fc.AllowQuotedNewlines + conf.Encoding = string(fc.Encoding) + conf.FieldDelimiter = fc.FieldDelimiter + conf.IgnoreUnknownValues = fc.IgnoreUnknownValues + conf.MaxBadRecords = fc.MaxBadRecords + if fc.Schema != nil { + conf.Schema = fc.Schema.toBQ() + } + conf.Quote = fc.quote() +} + +func bqPopulateFileConfig(conf *bq.JobConfigurationLoad, fc *FileConfig) { + fc.SourceFormat = DataFormat(conf.SourceFormat) + fc.AutoDetect = conf.Autodetect + fc.MaxBadRecords = conf.MaxBadRecords + fc.IgnoreUnknownValues = conf.IgnoreUnknownValues + fc.Schema = bqToSchema(conf.Schema) + fc.SkipLeadingRows = conf.SkipLeadingRows + fc.AllowJaggedRows = conf.AllowJaggedRows + fc.AllowQuotedNewlines = conf.AllowQuotedNewlines + fc.Encoding = Encoding(conf.Encoding) + fc.FieldDelimiter = conf.FieldDelimiter + fc.CSVOptions.setQuote(conf.Quote) +} + +func (fc *FileConfig) populateExternalDataConfig(conf *bq.ExternalDataConfiguration) { + format := fc.SourceFormat + if format == "" { + // Format must be explicitly set for external data sources. + format = CSV + } + conf.Autodetect = fc.AutoDetect + conf.IgnoreUnknownValues = fc.IgnoreUnknownValues + conf.MaxBadRecords = fc.MaxBadRecords + conf.SourceFormat = string(format) + if fc.Schema != nil { + conf.Schema = fc.Schema.toBQ() + } + if format == CSV { + fc.CSVOptions.populateExternalDataConfig(conf) + } +} + +// Encoding specifies the character encoding of data to be loaded into BigQuery. +// See https://cloud.google.com/bigquery/docs/reference/v2/jobs#configuration.load.encoding +// for more details about how this is used. +type Encoding string + +const ( + UTF_8 Encoding = "UTF-8" + ISO_8859_1 Encoding = "ISO-8859-1" +) diff --git a/vendor/cloud.google.com/go/bigquery/file_test.go b/vendor/cloud.google.com/go/bigquery/file_test.go new file mode 100644 index 0000000000..96e18bff3a --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/file_test.go @@ -0,0 +1,98 @@ +// 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 bigquery + +import ( + "testing" + + "cloud.google.com/go/internal/pretty" + "cloud.google.com/go/internal/testutil" + bq "google.golang.org/api/bigquery/v2" +) + +var ( + hyphen = "-" + fc = FileConfig{ + SourceFormat: CSV, + AutoDetect: true, + MaxBadRecords: 7, + IgnoreUnknownValues: true, + Schema: Schema{ + stringFieldSchema(), + nestedFieldSchema(), + }, + CSVOptions: CSVOptions{ + Quote: hyphen, + FieldDelimiter: "\t", + SkipLeadingRows: 8, + AllowJaggedRows: true, + AllowQuotedNewlines: true, + Encoding: UTF_8, + }, + } +) + +func TestFileConfigPopulateLoadConfig(t *testing.T) { + want := &bq.JobConfigurationLoad{ + SourceFormat: "CSV", + FieldDelimiter: "\t", + SkipLeadingRows: 8, + AllowJaggedRows: true, + AllowQuotedNewlines: true, + Autodetect: true, + Encoding: "UTF-8", + MaxBadRecords: 7, + IgnoreUnknownValues: true, + Schema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqStringFieldSchema(), + bqNestedFieldSchema(), + }}, + Quote: &hyphen, + } + got := &bq.JobConfigurationLoad{} + fc.populateLoadConfig(got) + if !testutil.Equal(got, want) { + t.Errorf("got:\n%v\nwant:\n%v", pretty.Value(got), pretty.Value(want)) + } +} + +func TestFileConfigPopulateExternalDataConfig(t *testing.T) { + got := &bq.ExternalDataConfiguration{} + fc.populateExternalDataConfig(got) + + want := &bq.ExternalDataConfiguration{ + SourceFormat: "CSV", + Autodetect: true, + MaxBadRecords: 7, + IgnoreUnknownValues: true, + Schema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqStringFieldSchema(), + bqNestedFieldSchema(), + }}, + CsvOptions: &bq.CsvOptions{ + AllowJaggedRows: true, + AllowQuotedNewlines: true, + Encoding: "UTF-8", + FieldDelimiter: "\t", + Quote: &hyphen, + SkipLeadingRows: 8, + }, + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("got=-, want=+:\n%s", diff) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/gcs.go b/vendor/cloud.google.com/go/bigquery/gcs.go new file mode 100644 index 0000000000..c6e1dec4ed --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/gcs.go @@ -0,0 +1,73 @@ +// Copyright 2015 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 bigquery + +import ( + "io" + + bq "google.golang.org/api/bigquery/v2" +) + +// GCSReference is a reference to one or more Google Cloud Storage objects, which together constitute +// an input or output to a BigQuery operation. +type GCSReference struct { + // URIs refer to Google Cloud Storage objects. + URIs []string + + FileConfig + + // DestinationFormat is the format to use when writing exported files. + // Allowed values are: CSV, Avro, JSON. The default is CSV. + // CSV is not supported for tables with nested or repeated fields. + DestinationFormat DataFormat + + // Compression specifies the type of compression to apply when writing data + // to Google Cloud Storage, or using this GCSReference as an ExternalData + // source with CSV or JSON SourceFormat. Default is None. + Compression Compression +} + +// NewGCSReference constructs a reference to one or more Google Cloud Storage objects, which together constitute a data source or destination. +// In the simple case, a single URI in the form gs://bucket/object may refer to a single GCS object. +// Data may also be split into mutiple files, if multiple URIs or URIs containing wildcards are provided. +// Each URI may contain one '*' wildcard character, which (if present) must come after the bucket name. +// For more information about the treatment of wildcards and multiple URIs, +// see https://cloud.google.com/bigquery/exporting-data-from-bigquery#exportingmultiple +func NewGCSReference(uri ...string) *GCSReference { + return &GCSReference{URIs: uri} +} + +// Compression is the type of compression to apply when writing data to Google Cloud Storage. +type Compression string + +const ( + None Compression = "NONE" + Gzip Compression = "GZIP" +) + +func (gcs *GCSReference) populateLoadConfig(lc *bq.JobConfigurationLoad) io.Reader { + lc.SourceUris = gcs.URIs + gcs.FileConfig.populateLoadConfig(lc) + return nil +} + +func (gcs *GCSReference) toBQ() bq.ExternalDataConfiguration { + conf := bq.ExternalDataConfiguration{ + Compression: string(gcs.Compression), + SourceUris: append([]string{}, gcs.URIs...), + } + gcs.FileConfig.populateExternalDataConfig(&conf) + return conf +} diff --git a/vendor/cloud.google.com/go/bigquery/integration_test.go b/vendor/cloud.google.com/go/bigquery/integration_test.go new file mode 100644 index 0000000000..82e362645e --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/integration_test.go @@ -0,0 +1,2080 @@ +// Copyright 2015 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 bigquery + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "log" + "math/big" + "net/http" + "os" + "sort" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + gax "github.com/googleapis/gax-go" + + "cloud.google.com/go/civil" + "cloud.google.com/go/httpreplay" + "cloud.google.com/go/internal" + "cloud.google.com/go/internal/pretty" + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/internal/uid" + "cloud.google.com/go/storage" + "golang.org/x/net/context" + "google.golang.org/api/googleapi" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +const replayFilename = "bigquery.replay" + +var record = flag.Bool("record", false, "record RPCs") + +var ( + client *Client + storageClient *storage.Client + dataset *Dataset + schema = Schema{ + {Name: "name", Type: StringFieldType}, + {Name: "nums", Type: IntegerFieldType, Repeated: true}, + {Name: "rec", Type: RecordFieldType, Schema: Schema{ + {Name: "bool", Type: BooleanFieldType}, + }}, + } + testTableExpiration time.Time + datasetIDs, tableIDs *uid.Space +) + +// Note: integration tests cannot be run in parallel, because TestIntegration_Location +// modifies the client. + +func TestMain(m *testing.M) { + cleanup := initIntegrationTest() + r := m.Run() + cleanup() + os.Exit(r) +} + +func getClient(t *testing.T) *Client { + if client == nil { + t.Skip("Integration tests skipped") + } + return client +} + +// If integration tests will be run, create a unique dataset for them. +// Return a cleanup function. +func initIntegrationTest() func() { + ctx := context.Background() + flag.Parse() // needed for testing.Short() + projID := testutil.ProjID() + switch { + case testing.Short() && *record: + log.Fatal("cannot combine -short and -record") + return func() {} + + case testing.Short() && httpreplay.Supported() && testutil.CanReplay(replayFilename) && projID != "": + // go test -short with a replay file will replay the integration tests if the + // environment variables are set. + log.Printf("replaying from %s", replayFilename) + httpreplay.DebugHeaders() + replayer, err := httpreplay.NewReplayer(replayFilename) + if err != nil { + log.Fatal(err) + } + var t time.Time + if err := json.Unmarshal(replayer.Initial(), &t); err != nil { + log.Fatal(err) + } + hc, err := replayer.Client(ctx) // no creds needed + if err != nil { + log.Fatal(err) + } + client, err = NewClient(ctx, projID, option.WithHTTPClient(hc)) + if err != nil { + log.Fatal(err) + } + storageClient, err = storage.NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + log.Fatal(err) + } + cleanup := initTestState(client, t) + return func() { + cleanup() + _ = replayer.Close() // No actionable error returned. + } + + case testing.Short(): + // go test -short without a replay file skips the integration tests. + if testutil.CanReplay(replayFilename) && projID != "" { + log.Print("replay not supported for Go versions before 1.8") + } + client = nil + storageClient = nil + return func() {} + + default: // Run integration tests against a real backend. + ts := testutil.TokenSource(ctx, Scope) + if ts == nil { + log.Println("Integration tests skipped. See CONTRIBUTING.md for details") + return func() {} + } + bqOpt := option.WithTokenSource(ts) + sOpt := option.WithTokenSource(testutil.TokenSource(ctx, storage.ScopeFullControl)) + cleanup := func() {} + now := time.Now().UTC() + if *record { + if !httpreplay.Supported() { + log.Print("record not supported for Go versions before 1.8") + } else { + nowBytes, err := json.Marshal(now) + if err != nil { + log.Fatal(err) + } + recorder, err := httpreplay.NewRecorder(replayFilename, nowBytes) + if err != nil { + log.Fatalf("could not record: %v", err) + } + log.Printf("recording to %s", replayFilename) + hc, err := recorder.Client(ctx, bqOpt) + if err != nil { + log.Fatal(err) + } + bqOpt = option.WithHTTPClient(hc) + hc, err = recorder.Client(ctx, sOpt) + if err != nil { + log.Fatal(err) + } + sOpt = option.WithHTTPClient(hc) + cleanup = func() { + if err := recorder.Close(); err != nil { + log.Printf("saving recording: %v", err) + } + } + } + } + var err error + client, err = NewClient(ctx, projID, bqOpt) + if err != nil { + log.Fatalf("NewClient: %v", err) + } + storageClient, err = storage.NewClient(ctx, sOpt) + if err != nil { + log.Fatalf("storage.NewClient: %v", err) + } + c := initTestState(client, now) + return func() { c(); cleanup() } + } +} + +func initTestState(client *Client, t time.Time) func() { + // BigQuery does not accept hyphens in dataset or table IDs, so we create IDs + // with underscores. + ctx := context.Background() + opts := &uid.Options{Sep: '_', Time: t} + datasetIDs = uid.NewSpace("dataset", opts) + tableIDs = uid.NewSpace("table", opts) + testTableExpiration = t.Add(10 * time.Minute).Round(time.Second) + // For replayability, seed the random source with t. + Seed(t.UnixNano()) + + dataset = client.Dataset(datasetIDs.New()) + if err := dataset.Create(ctx, nil); err != nil { + log.Fatalf("creating dataset %s: %v", dataset.DatasetID, err) + } + return func() { + if err := dataset.DeleteWithContents(ctx); err != nil { + log.Printf("could not delete %s", dataset.DatasetID) + } + } +} + +func TestIntegration_TableCreate(t *testing.T) { + // Check that creating a record field with an empty schema is an error. + if client == nil { + t.Skip("Integration tests skipped") + } + table := dataset.Table("t_bad") + schema := Schema{ + {Name: "rec", Type: RecordFieldType, Schema: Schema{}}, + } + err := table.Create(context.Background(), &TableMetadata{ + Schema: schema, + ExpirationTime: testTableExpiration.Add(5 * time.Minute), + }) + if err == nil { + t.Fatal("want error, got nil") + } + if !hasStatusCode(err, http.StatusBadRequest) { + t.Fatalf("want a 400 error, got %v", err) + } +} + +func TestIntegration_TableCreateView(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + + // Test that standard SQL views work. + view := dataset.Table("t_view_standardsql") + query := fmt.Sprintf("SELECT APPROX_COUNT_DISTINCT(name) FROM `%s.%s.%s`", + dataset.ProjectID, dataset.DatasetID, table.TableID) + err := view.Create(context.Background(), &TableMetadata{ + ViewQuery: query, + UseStandardSQL: true, + }) + if err != nil { + t.Fatalf("table.create: Did not expect an error, got: %v", err) + } + if err := view.Delete(ctx); err != nil { + t.Fatal(err) + } +} + +func TestIntegration_TableMetadata(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + // Check table metadata. + md, err := table.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + // TODO(jba): check md more thorougly. + if got, want := md.FullID, fmt.Sprintf("%s:%s.%s", dataset.ProjectID, dataset.DatasetID, table.TableID); got != want { + t.Errorf("metadata.FullID: got %q, want %q", got, want) + } + if got, want := md.Type, RegularTable; got != want { + t.Errorf("metadata.Type: got %v, want %v", got, want) + } + if got, want := md.ExpirationTime, testTableExpiration; !got.Equal(want) { + t.Errorf("metadata.Type: got %v, want %v", got, want) + } + + // Check that timePartitioning is nil by default + if md.TimePartitioning != nil { + t.Errorf("metadata.TimePartitioning: got %v, want %v", md.TimePartitioning, nil) + } + + // Create tables that have time partitioning + partitionCases := []struct { + timePartitioning TimePartitioning + wantExpiration time.Duration + wantField string + }{ + {TimePartitioning{}, time.Duration(0), ""}, + {TimePartitioning{Expiration: time.Second}, time.Second, ""}, + { + TimePartitioning{ + Expiration: time.Second, + Field: "date", + }, time.Second, "date"}, + } + + schema2 := Schema{ + {Name: "name", Type: StringFieldType}, + {Name: "date", Type: DateFieldType}, + } + + for i, c := range partitionCases { + table := dataset.Table(fmt.Sprintf("t_metadata_partition_%v", i)) + err = table.Create(context.Background(), &TableMetadata{ + Schema: schema2, + TimePartitioning: &c.timePartitioning, + ExpirationTime: testTableExpiration, + }) + if err != nil { + t.Fatal(err) + } + defer table.Delete(ctx) + md, err = table.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + + got := md.TimePartitioning + want := &TimePartitioning{ + Expiration: c.wantExpiration, + Field: c.wantField, + } + if !testutil.Equal(got, want) { + t.Errorf("metadata.TimePartitioning: got %v, want %v", got, want) + } + } +} + +func TestIntegration_DatasetCreate(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + ds := client.Dataset(datasetIDs.New()) + wmd := &DatasetMetadata{Name: "name", Location: "EU"} + err := ds.Create(ctx, wmd) + if err != nil { + t.Fatal(err) + } + gmd, err := ds.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + if got, want := gmd.Name, wmd.Name; got != want { + t.Errorf("name: got %q, want %q", got, want) + } + if got, want := gmd.Location, wmd.Location; got != want { + t.Errorf("location: got %q, want %q", got, want) + } + if err := ds.Delete(ctx); err != nil { + t.Fatalf("deleting dataset %v: %v", ds, err) + } +} + +func TestIntegration_DatasetMetadata(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + md, err := dataset.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + if got, want := md.FullID, fmt.Sprintf("%s:%s", dataset.ProjectID, dataset.DatasetID); got != want { + t.Errorf("FullID: got %q, want %q", got, want) + } + jan2016 := time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC) + if md.CreationTime.Before(jan2016) { + t.Errorf("CreationTime: got %s, want > 2016-1-1", md.CreationTime) + } + if md.LastModifiedTime.Before(jan2016) { + t.Errorf("LastModifiedTime: got %s, want > 2016-1-1", md.LastModifiedTime) + } + + // Verify that we get a NotFound for a nonexistent dataset. + _, err = client.Dataset("does_not_exist").Metadata(ctx) + if err == nil || !hasStatusCode(err, http.StatusNotFound) { + t.Errorf("got %v, want NotFound error", err) + } +} + +func TestIntegration_DatasetDelete(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + ds := client.Dataset(datasetIDs.New()) + if err := ds.Create(ctx, nil); err != nil { + t.Fatalf("creating dataset %s: %v", ds.DatasetID, err) + } + if err := ds.Delete(ctx); err != nil { + t.Fatalf("deleting dataset %s: %v", ds.DatasetID, err) + } +} + +func TestIntegration_DatasetDeleteWithContents(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + ds := client.Dataset(datasetIDs.New()) + if err := ds.Create(ctx, nil); err != nil { + t.Fatalf("creating dataset %s: %v", ds.DatasetID, err) + } + table := ds.Table(tableIDs.New()) + if err := table.Create(ctx, nil); err != nil { + t.Fatalf("creating table %s in dataset %s: %v", table.TableID, table.DatasetID, err) + } + // We expect failure here + if err := ds.Delete(ctx); err == nil { + t.Fatalf("non-recursive delete of dataset %s succeeded unexpectedly.", ds.DatasetID) + } + if err := ds.DeleteWithContents(ctx); err != nil { + t.Fatalf("deleting recursively dataset %s: %v", ds.DatasetID, err) + } +} + +func TestIntegration_DatasetUpdateETags(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + + check := func(md *DatasetMetadata, wantDesc, wantName string) { + if md.Description != wantDesc { + t.Errorf("description: got %q, want %q", md.Description, wantDesc) + } + if md.Name != wantName { + t.Errorf("name: got %q, want %q", md.Name, wantName) + } + } + + ctx := context.Background() + md, err := dataset.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + if md.ETag == "" { + t.Fatal("empty ETag") + } + // Write without ETag succeeds. + desc := md.Description + "d2" + name := md.Name + "n2" + md2, err := dataset.Update(ctx, DatasetMetadataToUpdate{Description: desc, Name: name}, "") + if err != nil { + t.Fatal(err) + } + check(md2, desc, name) + + // Write with original ETag fails because of intervening write. + _, err = dataset.Update(ctx, DatasetMetadataToUpdate{Description: "d", Name: "n"}, md.ETag) + if err == nil { + t.Fatal("got nil, want error") + } + + // Write with most recent ETag succeeds. + md3, err := dataset.Update(ctx, DatasetMetadataToUpdate{Description: "", Name: ""}, md2.ETag) + if err != nil { + t.Fatal(err) + } + check(md3, "", "") +} + +func TestIntegration_DatasetUpdateDefaultExpiration(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + md, err := dataset.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + // Set the default expiration time. + md, err = dataset.Update(ctx, DatasetMetadataToUpdate{DefaultTableExpiration: time.Hour}, "") + if err != nil { + t.Fatal(err) + } + if md.DefaultTableExpiration != time.Hour { + t.Fatalf("got %s, want 1h", md.DefaultTableExpiration) + } + // Omitting DefaultTableExpiration doesn't change it. + md, err = dataset.Update(ctx, DatasetMetadataToUpdate{Name: "xyz"}, "") + if err != nil { + t.Fatal(err) + } + if md.DefaultTableExpiration != time.Hour { + t.Fatalf("got %s, want 1h", md.DefaultTableExpiration) + } + // Setting it to 0 deletes it (which looks like a 0 duration). + md, err = dataset.Update(ctx, DatasetMetadataToUpdate{DefaultTableExpiration: time.Duration(0)}, "") + if err != nil { + t.Fatal(err) + } + if md.DefaultTableExpiration != 0 { + t.Fatalf("got %s, want 0", md.DefaultTableExpiration) + } +} + +func TestIntegration_DatasetUpdateAccess(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + md, err := dataset.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + origAccess := append([]*AccessEntry(nil), md.Access...) + newEntry := &AccessEntry{ + Role: ReaderRole, + Entity: "Joe@example.com", + EntityType: UserEmailEntity, + } + newAccess := append(md.Access, newEntry) + dm := DatasetMetadataToUpdate{Access: newAccess} + md, err = dataset.Update(ctx, dm, md.ETag) + if err != nil { + t.Fatal(err) + } + defer func() { + _, err := dataset.Update(ctx, DatasetMetadataToUpdate{Access: origAccess}, md.ETag) + if err != nil { + t.Log("could not restore dataset access list") + } + }() + if diff := testutil.Diff(md.Access, newAccess); diff != "" { + t.Fatalf("got=-, want=+:\n%s", diff) + } +} + +func TestIntegration_DatasetUpdateLabels(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + md, err := dataset.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + var dm DatasetMetadataToUpdate + dm.SetLabel("label", "value") + md, err = dataset.Update(ctx, dm, "") + if err != nil { + t.Fatal(err) + } + if got, want := md.Labels["label"], "value"; got != want { + t.Errorf("got %q, want %q", got, want) + } + dm = DatasetMetadataToUpdate{} + dm.DeleteLabel("label") + md, err = dataset.Update(ctx, dm, "") + if err != nil { + t.Fatal(err) + } + if _, ok := md.Labels["label"]; ok { + t.Error("label still present after deletion") + } +} + +func TestIntegration_TableUpdateLabels(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + + var tm TableMetadataToUpdate + tm.SetLabel("label", "value") + md, err := table.Update(ctx, tm, "") + if err != nil { + t.Fatal(err) + } + if got, want := md.Labels["label"], "value"; got != want { + t.Errorf("got %q, want %q", got, want) + } + tm = TableMetadataToUpdate{} + tm.DeleteLabel("label") + md, err = table.Update(ctx, tm, "") + if err != nil { + t.Fatal(err) + } + if _, ok := md.Labels["label"]; ok { + t.Error("label still present after deletion") + } +} + +func TestIntegration_Tables(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + wantName := table.FullyQualifiedName() + + // This test is flaky due to eventual consistency. + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + err := internal.Retry(ctx, gax.Backoff{}, func() (stop bool, err error) { + // Iterate over tables in the dataset. + it := dataset.Tables(ctx) + var tableNames []string + for { + tbl, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return false, err + } + tableNames = append(tableNames, tbl.FullyQualifiedName()) + } + // Other tests may be running with this dataset, so there might be more + // than just our table in the list. So don't try for an exact match; just + // make sure that our table is there somewhere. + for _, tn := range tableNames { + if tn == wantName { + return true, nil + } + } + return false, fmt.Errorf("got %v\nwant %s in the list", tableNames, wantName) + }) + if err != nil { + t.Fatal(err) + } +} + +func TestIntegration_UploadAndRead(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + + // Populate the table. + upl := table.Uploader() + var ( + wantRows [][]Value + saverRows []*ValuesSaver + ) + for i, name := range []string{"a", "b", "c"} { + row := []Value{name, []Value{int64(i)}, []Value{true}} + wantRows = append(wantRows, row) + saverRows = append(saverRows, &ValuesSaver{ + Schema: schema, + InsertID: name, + Row: row, + }) + } + if err := upl.Put(ctx, saverRows); err != nil { + t.Fatal(putError(err)) + } + + // Wait until the data has been uploaded. This can take a few seconds, according + // to https://cloud.google.com/bigquery/streaming-data-into-bigquery. + if err := waitForRow(ctx, table); err != nil { + t.Fatal(err) + } + // Read the table. + checkRead(t, "upload", table.Read(ctx), wantRows) + + // Query the table. + q := client.Query(fmt.Sprintf("select name, nums, rec from %s", table.TableID)) + q.DefaultProjectID = dataset.ProjectID + q.DefaultDatasetID = dataset.DatasetID + + rit, err := q.Read(ctx) + if err != nil { + t.Fatal(err) + } + checkRead(t, "query", rit, wantRows) + + // Query the long way. + job1, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } + if job1.LastStatus() == nil { + t.Error("no LastStatus") + } + job2, err := client.JobFromID(ctx, job1.ID()) + if err != nil { + t.Fatal(err) + } + if job2.LastStatus() == nil { + t.Error("no LastStatus") + } + rit, err = job2.Read(ctx) + if err != nil { + t.Fatal(err) + } + checkRead(t, "job.Read", rit, wantRows) + + // Get statistics. + jobStatus, err := job2.Status(ctx) + if err != nil { + t.Fatal(err) + } + if jobStatus.Statistics == nil { + t.Fatal("jobStatus missing statistics") + } + if _, ok := jobStatus.Statistics.Details.(*QueryStatistics); !ok { + t.Errorf("expected QueryStatistics, got %T", jobStatus.Statistics.Details) + } + + // Test reading directly into a []Value. + valueLists, schema, _, err := readAll(table.Read(ctx)) + if err != nil { + t.Fatal(err) + } + it := table.Read(ctx) + for i, vl := range valueLists { + var got []Value + if err := it.Next(&got); err != nil { + t.Fatal(err) + } + if !testutil.Equal(it.Schema, schema) { + t.Fatalf("got schema %v, want %v", it.Schema, schema) + } + want := []Value(vl) + if !testutil.Equal(got, want) { + t.Errorf("%d: got %v, want %v", i, got, want) + } + } + + // Test reading into a map. + it = table.Read(ctx) + for _, vl := range valueLists { + var vm map[string]Value + if err := it.Next(&vm); err != nil { + t.Fatal(err) + } + if got, want := len(vm), len(vl); got != want { + t.Fatalf("valueMap len: got %d, want %d", got, want) + } + // With maps, structs become nested maps. + vl[2] = map[string]Value{"bool": vl[2].([]Value)[0]} + for i, v := range vl { + if got, want := vm[schema[i].Name], v; !testutil.Equal(got, want) { + t.Errorf("%d, name=%s: got %#v, want %#v", + i, schema[i].Name, got, want) + } + } + } + +} + +type SubSubTestStruct struct { + Integer int64 +} + +type SubTestStruct struct { + String string + Record SubSubTestStruct + RecordArray []SubSubTestStruct +} + +type TestStruct struct { + Name string + Bytes []byte + Integer int64 + Float float64 + Boolean bool + Timestamp time.Time + Date civil.Date + Time civil.Time + DateTime civil.DateTime + Numeric *big.Rat + + StringArray []string + IntegerArray []int64 + FloatArray []float64 + BooleanArray []bool + TimestampArray []time.Time + DateArray []civil.Date + TimeArray []civil.Time + DateTimeArray []civil.DateTime + NumericArray []*big.Rat + + Record SubTestStruct + RecordArray []SubTestStruct +} + +// Round times to the microsecond for comparison purposes. +var roundToMicros = cmp.Transformer("RoundToMicros", + func(t time.Time) time.Time { return t.Round(time.Microsecond) }) + +func TestIntegration_UploadAndReadStructs(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + schema, err := InferSchema(TestStruct{}) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + + d := civil.Date{Year: 2016, Month: 3, Day: 20} + tm := civil.Time{Hour: 15, Minute: 4, Second: 5, Nanosecond: 6000} + ts := time.Date(2016, 3, 20, 15, 4, 5, 6000, time.UTC) + dtm := civil.DateTime{Date: d, Time: tm} + d2 := civil.Date{Year: 1994, Month: 5, Day: 15} + tm2 := civil.Time{Hour: 1, Minute: 2, Second: 4, Nanosecond: 0} + ts2 := time.Date(1994, 5, 15, 1, 2, 4, 0, time.UTC) + dtm2 := civil.DateTime{Date: d2, Time: tm2} + + // Populate the table. + upl := table.Uploader() + want := []*TestStruct{ + { + "a", + []byte("byte"), + 42, + 3.14, + true, + ts, + d, + tm, + dtm, + big.NewRat(57, 100), + []string{"a", "b"}, + []int64{1, 2}, + []float64{1, 1.41}, + []bool{true, false}, + []time.Time{ts, ts2}, + []civil.Date{d, d2}, + []civil.Time{tm, tm2}, + []civil.DateTime{dtm, dtm2}, + []*big.Rat{big.NewRat(1, 2), big.NewRat(3, 5)}, + SubTestStruct{ + "string", + SubSubTestStruct{24}, + []SubSubTestStruct{{1}, {2}}, + }, + []SubTestStruct{ + {String: "empty"}, + { + "full", + SubSubTestStruct{1}, + []SubSubTestStruct{{1}, {2}}, + }, + }, + }, + { + Name: "b", + Bytes: []byte("byte2"), + Integer: 24, + Float: 4.13, + Boolean: false, + Timestamp: ts, + Date: d, + Time: tm, + DateTime: dtm, + Numeric: big.NewRat(4499, 10000), + }, + } + var savers []*StructSaver + for _, s := range want { + savers = append(savers, &StructSaver{Schema: schema, Struct: s}) + } + if err := upl.Put(ctx, savers); err != nil { + t.Fatal(putError(err)) + } + + // Wait until the data has been uploaded. This can take a few seconds, according + // to https://cloud.google.com/bigquery/streaming-data-into-bigquery. + if err := waitForRow(ctx, table); err != nil { + t.Fatal(err) + } + + // Test iteration with structs. + it := table.Read(ctx) + var got []*TestStruct + for { + var g TestStruct + err := it.Next(&g) + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + got = append(got, &g) + } + sort.Sort(byName(got)) + + // BigQuery does not elide nils. It reports an error for nil fields. + for i, g := range got { + if i >= len(want) { + t.Errorf("%d: got %v, past end of want", i, pretty.Value(g)) + } else if diff := testutil.Diff(g, want[i], roundToMicros); diff != "" { + t.Errorf("%d: got=-, want=+:\n%s", i, diff) + } + } +} + +type byName []*TestStruct + +func (b byName) Len() int { return len(b) } +func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byName) Less(i, j int) bool { return b[i].Name < b[j].Name } + +func TestIntegration_UploadAndReadNullable(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctm := civil.Time{Hour: 15, Minute: 4, Second: 5, Nanosecond: 6000} + cdt := civil.DateTime{Date: testDate, Time: ctm} + rat := big.NewRat(33, 100) + testUploadAndReadNullable(t, testStructNullable{}, make([]Value, len(testStructNullableSchema))) + testUploadAndReadNullable(t, testStructNullable{ + String: NullString{"x", true}, + Bytes: []byte{1, 2, 3}, + Integer: NullInt64{1, true}, + Float: NullFloat64{2.3, true}, + Boolean: NullBool{true, true}, + Timestamp: NullTimestamp{testTimestamp, true}, + Date: NullDate{testDate, true}, + Time: NullTime{ctm, true}, + DateTime: NullDateTime{cdt, true}, + Numeric: rat, + Record: &subNullable{X: NullInt64{4, true}}, + }, + []Value{"x", []byte{1, 2, 3}, int64(1), 2.3, true, testTimestamp, testDate, ctm, cdt, rat, []Value{int64(4)}}) +} + +func testUploadAndReadNullable(t *testing.T, ts testStructNullable, wantRow []Value) { + ctx := context.Background() + table := newTable(t, testStructNullableSchema) + defer table.Delete(ctx) + + // Populate the table. + upl := table.Uploader() + if err := upl.Put(ctx, []*StructSaver{{Schema: testStructNullableSchema, Struct: ts}}); err != nil { + t.Fatal(putError(err)) + } + // Wait until the data has been uploaded. This can take a few seconds, according + // to https://cloud.google.com/bigquery/streaming-data-into-bigquery. + if err := waitForRow(ctx, table); err != nil { + t.Fatal(err) + } + + // Read into a []Value. + iter := table.Read(ctx) + gotRows, _, _, err := readAll(iter) + if err != nil { + t.Fatal(err) + } + if len(gotRows) != 1 { + t.Fatalf("got %d rows, want 1", len(gotRows)) + } + if diff := testutil.Diff(gotRows[0], wantRow, roundToMicros); diff != "" { + t.Error(diff) + } + + // Read into a struct. + want := ts + var sn testStructNullable + it := table.Read(ctx) + if err := it.Next(&sn); err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(sn, want, roundToMicros); diff != "" { + t.Error(diff) + } +} + +func TestIntegration_TableUpdate(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + + // Test Update of non-schema fields. + tm, err := table.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + wantDescription := tm.Description + "more" + wantName := tm.Name + "more" + wantExpiration := tm.ExpirationTime.Add(time.Hour * 24) + got, err := table.Update(ctx, TableMetadataToUpdate{ + Description: wantDescription, + Name: wantName, + ExpirationTime: wantExpiration, + }, tm.ETag) + if err != nil { + t.Fatal(err) + } + if got.Description != wantDescription { + t.Errorf("Description: got %q, want %q", got.Description, wantDescription) + } + if got.Name != wantName { + t.Errorf("Name: got %q, want %q", got.Name, wantName) + } + if got.ExpirationTime != wantExpiration { + t.Errorf("ExpirationTime: got %q, want %q", got.ExpirationTime, wantExpiration) + } + if !testutil.Equal(got.Schema, schema) { + t.Errorf("Schema: got %v, want %v", pretty.Value(got.Schema), pretty.Value(schema)) + } + + // Blind write succeeds. + _, err = table.Update(ctx, TableMetadataToUpdate{Name: "x"}, "") + if err != nil { + t.Fatal(err) + } + // Write with old etag fails. + _, err = table.Update(ctx, TableMetadataToUpdate{Name: "y"}, got.ETag) + if err == nil { + t.Fatal("Update with old ETag succeeded, wanted failure") + } + + // Test schema update. + // Columns can be added. schema2 is the same as schema, except for the + // added column in the middle. + nested := Schema{ + {Name: "nested", Type: BooleanFieldType}, + {Name: "other", Type: StringFieldType}, + } + schema2 := Schema{ + schema[0], + {Name: "rec2", Type: RecordFieldType, Schema: nested}, + schema[1], + schema[2], + } + + got, err = table.Update(ctx, TableMetadataToUpdate{Schema: schema2}, "") + if err != nil { + t.Fatal(err) + } + + // Wherever you add the column, it appears at the end. + schema3 := Schema{schema2[0], schema2[2], schema2[3], schema2[1]} + if !testutil.Equal(got.Schema, schema3) { + t.Errorf("add field:\ngot %v\nwant %v", + pretty.Value(got.Schema), pretty.Value(schema3)) + } + + // Updating with the empty schema succeeds, but is a no-op. + got, err = table.Update(ctx, TableMetadataToUpdate{Schema: Schema{}}, "") + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got.Schema, schema3) { + t.Errorf("empty schema:\ngot %v\nwant %v", + pretty.Value(got.Schema), pretty.Value(schema3)) + } + + // Error cases when updating schema. + for _, test := range []struct { + desc string + fields Schema + }{ + {"change from optional to required", Schema{ + {Name: "name", Type: StringFieldType, Required: true}, + schema3[1], + schema3[2], + schema3[3], + }}, + {"add a required field", Schema{ + schema3[0], schema3[1], schema3[2], schema3[3], + {Name: "req", Type: StringFieldType, Required: true}, + }}, + {"remove a field", Schema{schema3[0], schema3[1], schema3[2]}}, + {"remove a nested field", Schema{ + schema3[0], schema3[1], schema3[2], + {Name: "rec2", Type: RecordFieldType, Schema: Schema{nested[0]}}}}, + {"remove all nested fields", Schema{ + schema3[0], schema3[1], schema3[2], + {Name: "rec2", Type: RecordFieldType, Schema: Schema{}}}}, + } { + _, err = table.Update(ctx, TableMetadataToUpdate{Schema: Schema(test.fields)}, "") + if err == nil { + t.Errorf("%s: want error, got nil", test.desc) + } else if !hasStatusCode(err, 400) { + t.Errorf("%s: want 400, got %v", test.desc, err) + } + } +} + +func TestIntegration_Load(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + // CSV data can't be loaded into a repeated field, so we use a different schema. + table := newTable(t, Schema{ + {Name: "name", Type: StringFieldType}, + {Name: "nums", Type: IntegerFieldType}, + }) + defer table.Delete(ctx) + + // Load the table from a reader. + r := strings.NewReader("a,0\nb,1\nc,2\n") + wantRows := [][]Value{ + {"a", int64(0)}, + {"b", int64(1)}, + {"c", int64(2)}, + } + rs := NewReaderSource(r) + loader := table.LoaderFrom(rs) + loader.WriteDisposition = WriteTruncate + loader.Labels = map[string]string{"test": "go"} + job, err := loader.Run(ctx) + if err != nil { + t.Fatal(err) + } + if job.LastStatus() == nil { + t.Error("no LastStatus") + } + conf, err := job.Config() + if err != nil { + t.Fatal(err) + } + config, ok := conf.(*LoadConfig) + if !ok { + t.Fatalf("got %T, want LoadConfig", conf) + } + diff := testutil.Diff(config, &loader.LoadConfig, + cmp.AllowUnexported(Table{}), + cmpopts.IgnoreUnexported(Client{}, ReaderSource{}), + // returned schema is at top level, not in the config + cmpopts.IgnoreFields(FileConfig{}, "Schema")) + if diff != "" { + t.Errorf("got=-, want=+:\n%s", diff) + } + if err := wait(ctx, job); err != nil { + t.Fatal(err) + } + checkReadAndTotalRows(t, "reader load", table.Read(ctx), wantRows) + +} + +func TestIntegration_DML(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + + sql := fmt.Sprintf(`INSERT %s.%s (name, nums, rec) + VALUES ('a', [0], STRUCT(TRUE)), + ('b', [1], STRUCT(FALSE)), + ('c', [2], STRUCT(TRUE))`, + table.DatasetID, table.TableID) + if err := runDML(ctx, sql); err != nil { + t.Fatal(err) + } + wantRows := [][]Value{ + {"a", []Value{int64(0)}, []Value{true}}, + {"b", []Value{int64(1)}, []Value{false}}, + {"c", []Value{int64(2)}, []Value{true}}, + } + checkRead(t, "DML", table.Read(ctx), wantRows) +} + +func runDML(ctx context.Context, sql string) error { + // Retry insert; sometimes it fails with INTERNAL. + return internal.Retry(ctx, gax.Backoff{}, func() (bool, error) { + // Use DML to insert. + q := client.Query(sql) + job, err := q.Run(ctx) + if err != nil { + if e, ok := err.(*googleapi.Error); ok && e.Code < 500 { + return true, err // fail on 4xx + } + return false, err + } + if err := wait(ctx, job); err != nil { + if e, ok := err.(*googleapi.Error); ok && e.Code < 500 { + return true, err // fail on 4xx + } + return false, err + } + return true, nil + }) +} + +func TestIntegration_TimeTypes(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + dtSchema := Schema{ + {Name: "d", Type: DateFieldType}, + {Name: "t", Type: TimeFieldType}, + {Name: "dt", Type: DateTimeFieldType}, + {Name: "ts", Type: TimestampFieldType}, + } + table := newTable(t, dtSchema) + defer table.Delete(ctx) + + d := civil.Date{Year: 2016, Month: 3, Day: 20} + tm := civil.Time{Hour: 12, Minute: 30, Second: 0, Nanosecond: 6000} + dtm := civil.DateTime{Date: d, Time: tm} + ts := time.Date(2016, 3, 20, 15, 04, 05, 0, time.UTC) + wantRows := [][]Value{ + {d, tm, dtm, ts}, + } + upl := table.Uploader() + if err := upl.Put(ctx, []*ValuesSaver{ + {Schema: dtSchema, Row: wantRows[0]}, + }); err != nil { + t.Fatal(putError(err)) + } + if err := waitForRow(ctx, table); err != nil { + t.Fatal(err) + } + + // SQL wants DATETIMEs with a space between date and time, but the service + // returns them in RFC3339 form, with a "T" between. + query := fmt.Sprintf("INSERT %s.%s (d, t, dt, ts) "+ + "VALUES ('%s', '%s', '%s', '%s')", + table.DatasetID, table.TableID, + d, CivilTimeString(tm), CivilDateTimeString(dtm), ts.Format("2006-01-02 15:04:05")) + if err := runDML(ctx, query); err != nil { + t.Fatal(err) + } + wantRows = append(wantRows, wantRows[0]) + checkRead(t, "TimeTypes", table.Read(ctx), wantRows) +} + +func TestIntegration_StandardQuery(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + + d := civil.Date{Year: 2016, Month: 3, Day: 20} + tm := civil.Time{Hour: 15, Minute: 04, Second: 05, Nanosecond: 0} + ts := time.Date(2016, 3, 20, 15, 04, 05, 0, time.UTC) + dtm := ts.Format("2006-01-02 15:04:05") + + // Constructs Value slices made up of int64s. + ints := func(args ...int) []Value { + vals := make([]Value, len(args)) + for i, arg := range args { + vals[i] = int64(arg) + } + return vals + } + + testCases := []struct { + query string + wantRow []Value + }{ + {"SELECT 1", ints(1)}, + {"SELECT 1.3", []Value{1.3}}, + {"SELECT CAST(1.3 AS NUMERIC)", []Value{big.NewRat(13, 10)}}, + {"SELECT NUMERIC '0.25'", []Value{big.NewRat(1, 4)}}, + {"SELECT TRUE", []Value{true}}, + {"SELECT 'ABC'", []Value{"ABC"}}, + {"SELECT CAST('foo' AS BYTES)", []Value{[]byte("foo")}}, + {fmt.Sprintf("SELECT TIMESTAMP '%s'", dtm), []Value{ts}}, + {fmt.Sprintf("SELECT [TIMESTAMP '%s', TIMESTAMP '%s']", dtm, dtm), []Value{[]Value{ts, ts}}}, + {fmt.Sprintf("SELECT ('hello', TIMESTAMP '%s')", dtm), []Value{[]Value{"hello", ts}}}, + {fmt.Sprintf("SELECT DATETIME(TIMESTAMP '%s')", dtm), []Value{civil.DateTime{Date: d, Time: tm}}}, + {fmt.Sprintf("SELECT DATE(TIMESTAMP '%s')", dtm), []Value{d}}, + {fmt.Sprintf("SELECT TIME(TIMESTAMP '%s')", dtm), []Value{tm}}, + {"SELECT (1, 2)", []Value{ints(1, 2)}}, + {"SELECT [1, 2, 3]", []Value{ints(1, 2, 3)}}, + {"SELECT ([1, 2], 3, [4, 5])", []Value{[]Value{ints(1, 2), int64(3), ints(4, 5)}}}, + {"SELECT [(1, 2, 3), (4, 5, 6)]", []Value{[]Value{ints(1, 2, 3), ints(4, 5, 6)}}}, + {"SELECT [([1, 2, 3], 4), ([5, 6], 7)]", []Value{[]Value{[]Value{ints(1, 2, 3), int64(4)}, []Value{ints(5, 6), int64(7)}}}}, + {"SELECT ARRAY(SELECT STRUCT([1, 2]))", []Value{[]Value{[]Value{ints(1, 2)}}}}, + } + for _, c := range testCases { + q := client.Query(c.query) + it, err := q.Read(ctx) + if err != nil { + t.Fatal(err) + } + checkRead(t, "StandardQuery", it, [][]Value{c.wantRow}) + } +} + +func TestIntegration_LegacyQuery(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + + ts := time.Date(2016, 3, 20, 15, 04, 05, 0, time.UTC) + dtm := ts.Format("2006-01-02 15:04:05") + + testCases := []struct { + query string + wantRow []Value + }{ + {"SELECT 1", []Value{int64(1)}}, + {"SELECT 1.3", []Value{1.3}}, + {"SELECT TRUE", []Value{true}}, + {"SELECT 'ABC'", []Value{"ABC"}}, + {"SELECT CAST('foo' AS BYTES)", []Value{[]byte("foo")}}, + {fmt.Sprintf("SELECT TIMESTAMP('%s')", dtm), []Value{ts}}, + {fmt.Sprintf("SELECT DATE(TIMESTAMP('%s'))", dtm), []Value{"2016-03-20"}}, + {fmt.Sprintf("SELECT TIME(TIMESTAMP('%s'))", dtm), []Value{"15:04:05"}}, + } + for _, c := range testCases { + q := client.Query(c.query) + q.UseLegacySQL = true + it, err := q.Read(ctx) + if err != nil { + t.Fatal(err) + } + checkRead(t, "LegacyQuery", it, [][]Value{c.wantRow}) + } +} + +func TestIntegration_QueryParameters(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + + d := civil.Date{Year: 2016, Month: 3, Day: 20} + tm := civil.Time{Hour: 15, Minute: 04, Second: 05, Nanosecond: 3008} + rtm := tm + rtm.Nanosecond = 3000 // round to microseconds + dtm := civil.DateTime{Date: d, Time: tm} + ts := time.Date(2016, 3, 20, 15, 04, 05, 0, time.UTC) + rat := big.NewRat(13, 10) + + type ss struct { + String string + } + + type s struct { + Timestamp time.Time + StringArray []string + SubStruct ss + SubStructArray []ss + } + + testCases := []struct { + query string + parameters []QueryParameter + wantRow []Value + wantConfig interface{} + }{ + { + "SELECT @val", + []QueryParameter{{"val", 1}}, + []Value{int64(1)}, + int64(1), + }, + { + "SELECT @val", + []QueryParameter{{"val", 1.3}}, + []Value{1.3}, + 1.3, + }, + { + "SELECT @val", + []QueryParameter{{"val", rat}}, + []Value{rat}, + rat, + }, + { + "SELECT @val", + []QueryParameter{{"val", true}}, + []Value{true}, + true, + }, + { + "SELECT @val", + []QueryParameter{{"val", "ABC"}}, + []Value{"ABC"}, + "ABC", + }, + { + "SELECT @val", + []QueryParameter{{"val", []byte("foo")}}, + []Value{[]byte("foo")}, + []byte("foo"), + }, + { + "SELECT @val", + []QueryParameter{{"val", ts}}, + []Value{ts}, + ts, + }, + { + "SELECT @val", + []QueryParameter{{"val", []time.Time{ts, ts}}}, + []Value{[]Value{ts, ts}}, + []interface{}{ts, ts}, + }, + { + "SELECT @val", + []QueryParameter{{"val", dtm}}, + []Value{civil.DateTime{Date: d, Time: rtm}}, + civil.DateTime{Date: d, Time: rtm}, + }, + { + "SELECT @val", + []QueryParameter{{"val", d}}, + []Value{d}, + d, + }, + { + "SELECT @val", + []QueryParameter{{"val", tm}}, + []Value{rtm}, + rtm, + }, + { + "SELECT @val", + []QueryParameter{{"val", s{ts, []string{"a", "b"}, ss{"c"}, []ss{{"d"}, {"e"}}}}}, + []Value{[]Value{ts, []Value{"a", "b"}, []Value{"c"}, []Value{[]Value{"d"}, []Value{"e"}}}}, + map[string]interface{}{ + "Timestamp": ts, + "StringArray": []interface{}{"a", "b"}, + "SubStruct": map[string]interface{}{"String": "c"}, + "SubStructArray": []interface{}{ + map[string]interface{}{"String": "d"}, + map[string]interface{}{"String": "e"}, + }, + }, + }, + { + "SELECT @val.Timestamp, @val.SubStruct.String", + []QueryParameter{{"val", s{Timestamp: ts, SubStruct: ss{"a"}}}}, + []Value{ts, "a"}, + map[string]interface{}{ + "Timestamp": ts, + "SubStruct": map[string]interface{}{"String": "a"}, + "StringArray": nil, + "SubStructArray": nil, + }, + }, + } + for _, c := range testCases { + q := client.Query(c.query) + q.Parameters = c.parameters + job, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } + if job.LastStatus() == nil { + t.Error("no LastStatus") + } + it, err := job.Read(ctx) + if err != nil { + t.Fatal(err) + } + checkRead(t, "QueryParameters", it, [][]Value{c.wantRow}) + config, err := job.Config() + if err != nil { + t.Fatal(err) + } + got := config.(*QueryConfig).Parameters[0].Value + if !testutil.Equal(got, c.wantConfig) { + t.Errorf("param %[1]v (%[1]T): config:\ngot %[2]v (%[2]T)\nwant %[3]v (%[3]T)", + c.parameters[0].Value, got, c.wantConfig) + } + } +} + +func TestIntegration_QueryDryRun(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + q := client.Query("SELECT word from " + stdName + " LIMIT 10") + q.DryRun = true + job, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } + + s := job.LastStatus() + if s.State != Done { + t.Errorf("state is %v, expected Done", s.State) + } + if s.Statistics == nil { + t.Fatal("no statistics") + } + if s.Statistics.Details.(*QueryStatistics).Schema == nil { + t.Fatal("no schema") + } +} + +func TestIntegration_ExtractExternal(t *testing.T) { + // Create a table, extract it to GCS, then query it externally. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + schema := Schema{ + {Name: "name", Type: StringFieldType}, + {Name: "num", Type: IntegerFieldType}, + } + table := newTable(t, schema) + defer table.Delete(ctx) + + // Insert table data. + sql := fmt.Sprintf(`INSERT %s.%s (name, num) + VALUES ('a', 1), ('b', 2), ('c', 3)`, + table.DatasetID, table.TableID) + if err := runDML(ctx, sql); err != nil { + t.Fatal(err) + } + // Extract to a GCS object as CSV. + bucketName := testutil.ProjID() + objectName := fmt.Sprintf("bq-test-%s.csv", table.TableID) + uri := fmt.Sprintf("gs://%s/%s", bucketName, objectName) + defer storageClient.Bucket(bucketName).Object(objectName).Delete(ctx) + gr := NewGCSReference(uri) + gr.DestinationFormat = CSV + e := table.ExtractorTo(gr) + job, err := e.Run(ctx) + if err != nil { + t.Fatal(err) + } + conf, err := job.Config() + if err != nil { + t.Fatal(err) + } + config, ok := conf.(*ExtractConfig) + if !ok { + t.Fatalf("got %T, want ExtractConfig", conf) + } + diff := testutil.Diff(config, &e.ExtractConfig, + cmp.AllowUnexported(Table{}), + cmpopts.IgnoreUnexported(Client{})) + if diff != "" { + t.Errorf("got=-, want=+:\n%s", diff) + } + if err := wait(ctx, job); err != nil { + t.Fatal(err) + } + + edc := &ExternalDataConfig{ + SourceFormat: CSV, + SourceURIs: []string{uri}, + Schema: schema, + Options: &CSVOptions{SkipLeadingRows: 1}, + } + // Query that CSV file directly. + q := client.Query("SELECT * FROM csv") + q.TableDefinitions = map[string]ExternalData{"csv": edc} + wantRows := [][]Value{ + {"a", int64(1)}, + {"b", int64(2)}, + {"c", int64(3)}, + } + iter, err := q.Read(ctx) + if err != nil { + t.Fatal(err) + } + checkReadAndTotalRows(t, "external query", iter, wantRows) + + // Make a table pointing to the file, and query it. + // BigQuery does not allow a Table.Read on an external table. + table = dataset.Table(tableIDs.New()) + err = table.Create(context.Background(), &TableMetadata{ + Schema: schema, + ExpirationTime: testTableExpiration, + ExternalDataConfig: edc, + }) + if err != nil { + t.Fatal(err) + } + q = client.Query(fmt.Sprintf("SELECT * FROM %s.%s", table.DatasetID, table.TableID)) + iter, err = q.Read(ctx) + if err != nil { + t.Fatal(err) + } + checkReadAndTotalRows(t, "external table", iter, wantRows) + + // While we're here, check that the table metadata is correct. + md, err := table.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + // One difference: since BigQuery returns the schema as part of the ordinary + // table metadata, it does not populate ExternalDataConfig.Schema. + md.ExternalDataConfig.Schema = md.Schema + if diff := testutil.Diff(md.ExternalDataConfig, edc); diff != "" { + t.Errorf("got=-, want=+\n%s", diff) + } +} + +func TestIntegration_ReadNullIntoStruct(t *testing.T) { + // Reading a null into a struct field should return an error (not panic). + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + + upl := table.Uploader() + row := &ValuesSaver{ + Schema: schema, + Row: []Value{nil, []Value{}, []Value{nil}}, + } + if err := upl.Put(ctx, []*ValuesSaver{row}); err != nil { + t.Fatal(putError(err)) + } + if err := waitForRow(ctx, table); err != nil { + t.Fatal(err) + } + + q := client.Query(fmt.Sprintf("select name from %s", table.TableID)) + q.DefaultProjectID = dataset.ProjectID + q.DefaultDatasetID = dataset.DatasetID + it, err := q.Read(ctx) + if err != nil { + t.Fatal(err) + } + type S struct{ Name string } + var s S + if err := it.Next(&s); err == nil { + t.Fatal("got nil, want error") + } +} + +const ( + stdName = "`bigquery-public-data.samples.shakespeare`" + legacyName = "[bigquery-public-data:samples.shakespeare]" +) + +// These tests exploit the fact that the two SQL versions have different syntaxes for +// fully-qualified table names. +var useLegacySqlTests = []struct { + t string // name of table + std, legacy bool // use standard/legacy SQL + err bool // do we expect an error? +}{ + {t: legacyName, std: false, legacy: true, err: false}, + {t: legacyName, std: true, legacy: false, err: true}, + {t: legacyName, std: false, legacy: false, err: true}, // standard SQL is default + {t: legacyName, std: true, legacy: true, err: true}, + {t: stdName, std: false, legacy: true, err: true}, + {t: stdName, std: true, legacy: false, err: false}, + {t: stdName, std: false, legacy: false, err: false}, // standard SQL is default + {t: stdName, std: true, legacy: true, err: true}, +} + +func TestIntegration_QueryUseLegacySQL(t *testing.T) { + // Test the UseLegacySQL and UseStandardSQL options for queries. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + for _, test := range useLegacySqlTests { + q := client.Query(fmt.Sprintf("select word from %s limit 1", test.t)) + q.UseStandardSQL = test.std + q.UseLegacySQL = test.legacy + _, err := q.Read(ctx) + gotErr := err != nil + if gotErr && !test.err { + t.Errorf("%+v:\nunexpected error: %v", test, err) + } else if !gotErr && test.err { + t.Errorf("%+v:\nsucceeded, but want error", test) + } + } +} + +func TestIntegration_TableUseLegacySQL(t *testing.T) { + // Test UseLegacySQL and UseStandardSQL for Table.Create. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + table := newTable(t, schema) + defer table.Delete(ctx) + for i, test := range useLegacySqlTests { + view := dataset.Table(fmt.Sprintf("t_view_%d", i)) + tm := &TableMetadata{ + ViewQuery: fmt.Sprintf("SELECT word from %s", test.t), + UseStandardSQL: test.std, + UseLegacySQL: test.legacy, + } + err := view.Create(ctx, tm) + gotErr := err != nil + if gotErr && !test.err { + t.Errorf("%+v:\nunexpected error: %v", test, err) + } else if !gotErr && test.err { + t.Errorf("%+v:\nsucceeded, but want error", test) + } + _ = view.Delete(ctx) + } +} + +func TestIntegration_ListJobs(t *testing.T) { + // It's difficult to test the list of jobs, because we can't easily + // control what's in it. Also, there are many jobs in the test project, + // and it takes considerable time to list them all. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + + // About all we can do is list a few jobs. + const max = 20 + var jobs []*Job + it := client.Jobs(ctx) + for { + job, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + jobs = append(jobs, job) + if len(jobs) >= max { + break + } + } + // We expect that there is at least one job in the last few months. + if len(jobs) == 0 { + t.Fatal("did not get any jobs") + } +} + +const tokyo = "asia-northeast1" + +func TestIntegration_Location(t *testing.T) { + if client == nil { + t.Skip("Integration tests skipped") + } + client.Location = "" + testLocation(t, tokyo) + client.Location = tokyo + defer func() { + client.Location = "" + }() + testLocation(t, "") +} + +func testLocation(t *testing.T, loc string) { + ctx := context.Background() + tokyoDataset := client.Dataset("tokyo") + err := tokyoDataset.Create(ctx, &DatasetMetadata{Location: loc}) + if err != nil && !hasStatusCode(err, 409) { // 409 = already exists + t.Fatal(err) + } + md, err := tokyoDataset.Metadata(ctx) + if err != nil { + t.Fatal(err) + } + if md.Location != tokyo { + t.Fatalf("dataset location: got %s, want %s", md.Location, tokyo) + } + table := tokyoDataset.Table(tableIDs.New()) + err = table.Create(context.Background(), &TableMetadata{ + Schema: Schema{ + {Name: "name", Type: StringFieldType}, + {Name: "nums", Type: IntegerFieldType}, + }, + ExpirationTime: testTableExpiration, + }) + if err != nil { + t.Fatal(err) + } + defer table.Delete(ctx) + loader := table.LoaderFrom(NewReaderSource(strings.NewReader("a,0\nb,1\nc,2\n"))) + loader.Location = loc + job, err := loader.Run(ctx) + if err != nil { + t.Fatal("loader.Run", err) + } + if job.Location() != tokyo { + t.Fatalf("job location: got %s, want %s", job.Location(), tokyo) + } + _, err = client.JobFromID(ctx, job.ID()) + if client.Location == "" && err == nil { + t.Error("JobFromID with Tokyo job, no client location: want error, got nil") + } + if client.Location != "" && err != nil { + t.Errorf("JobFromID with Tokyo job, with client location: want nil, got %v", err) + } + _, err = client.JobFromIDLocation(ctx, job.ID(), "US") + if err == nil { + t.Error("JobFromIDLocation with US: want error, got nil") + } + job2, err := client.JobFromIDLocation(ctx, job.ID(), loc) + if loc == tokyo && err != nil { + t.Errorf("loc=tokyo: %v", err) + } + if loc == "" && err == nil { + t.Error("loc empty: got nil, want error") + } + if job2 != nil && (job2.ID() != job.ID() || job2.Location() != tokyo) { + t.Errorf("got id %s loc %s, want id%s loc %s", job2.ID(), job2.Location(), job.ID(), tokyo) + } + if err := wait(ctx, job); err != nil { + t.Fatal(err) + } + // Cancel should succeed even if the job is done. + if err := job.Cancel(ctx); err != nil { + t.Fatal(err) + } + + q := client.Query(fmt.Sprintf("SELECT * FROM %s.%s", table.DatasetID, table.TableID)) + q.Location = loc + iter, err := q.Read(ctx) + if err != nil { + t.Fatal(err) + } + wantRows := [][]Value{ + {"a", int64(0)}, + {"b", int64(1)}, + {"c", int64(2)}, + } + checkRead(t, "location", iter, wantRows) + + table2 := tokyoDataset.Table(tableIDs.New()) + copier := table2.CopierFrom(table) + copier.Location = loc + if _, err := copier.Run(ctx); err != nil { + t.Fatal(err) + } + bucketName := testutil.ProjID() + objectName := fmt.Sprintf("bq-test-%s.csv", table.TableID) + uri := fmt.Sprintf("gs://%s/%s", bucketName, objectName) + defer storageClient.Bucket(bucketName).Object(objectName).Delete(ctx) + gr := NewGCSReference(uri) + gr.DestinationFormat = CSV + e := table.ExtractorTo(gr) + e.Location = loc + if _, err := e.Run(ctx); err != nil { + t.Fatal(err) + } +} + +func TestIntegration_NumericErrors(t *testing.T) { + // Verify that the service returns an error for a big.Rat that's too large. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + schema := Schema{{Name: "n", Type: NumericFieldType}} + table := newTable(t, schema) + defer table.Delete(ctx) + tooBigRat := &big.Rat{} + if _, ok := tooBigRat.SetString("1e40"); !ok { + t.Fatal("big.Rat.SetString failed") + } + upl := table.Uploader() + err := upl.Put(ctx, []*ValuesSaver{{Schema: schema, Row: []Value{tooBigRat}}}) + if err == nil { + t.Fatal("got nil, want error") + } +} + +func TestIntegration_QueryErrors(t *testing.T) { + // Verify that a bad query returns an appropriate error. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + q := client.Query("blah blah broken") + _, err := q.Read(ctx) + const want = "invalidQuery" + if !strings.Contains(err.Error(), want) { + t.Fatalf("got %q, want substring %q", err, want) + } +} + +func TestIntegration_Model(t *testing.T) { + // Create an ML model. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + schema := Schema{ + {Name: "input", Type: IntegerFieldType}, + {Name: "label", Type: IntegerFieldType}, + } + table := newTable(t, schema) + defer table.Delete(ctx) + + // Insert table data. + tableName := fmt.Sprintf("%s.%s", table.DatasetID, table.TableID) + sql := fmt.Sprintf(`INSERT %s (input, label) + VALUES (1, 0), (2, 1), (3, 0), (4, 1)`, + tableName) + wantNumRows := 4 + if err := runDML(ctx, sql); err != nil { + t.Fatal(err) + } + + model := dataset.Table("my_model") + modelName := fmt.Sprintf("%s.%s", model.DatasetID, model.TableID) + sql = fmt.Sprintf(`CREATE MODEL %s OPTIONS (model_type='logistic_reg') AS SELECT input, label FROM %s`, + modelName, tableName) + if err := runDML(ctx, sql); err != nil { + t.Fatal(err) + } + defer model.Delete(ctx) + + sql = fmt.Sprintf(`SELECT * FROM ml.PREDICT(MODEL %s, TABLE %s)`, modelName, tableName) + q := client.Query(sql) + ri, err := q.Read(ctx) + if err != nil { + t.Fatal(err) + } + rows, _, _, err := readAll(ri) + if err != nil { + t.Fatal(err) + } + if got := len(rows); got != wantNumRows { + t.Fatalf("got %d rows in prediction table, want %d", got, wantNumRows) + } + iter := dataset.Tables(ctx) + seen := false + for { + tbl, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + if tbl.TableID == "my_model" { + seen = true + } + } + if !seen { + t.Fatal("model not listed in dataset") + } + if err := model.Delete(ctx); err != nil { + t.Fatal(err) + } +} + +// Creates a new, temporary table with a unique name and the given schema. +func newTable(t *testing.T, s Schema) *Table { + table := dataset.Table(tableIDs.New()) + err := table.Create(context.Background(), &TableMetadata{ + Schema: s, + ExpirationTime: testTableExpiration, + }) + if err != nil { + t.Fatal(err) + } + return table +} + +func checkRead(t *testing.T, msg string, it *RowIterator, want [][]Value) { + if msg2, ok := compareRead(it, want, false); !ok { + t.Errorf("%s: %s", msg, msg2) + } +} + +func checkReadAndTotalRows(t *testing.T, msg string, it *RowIterator, want [][]Value) { + if msg2, ok := compareRead(it, want, true); !ok { + t.Errorf("%s: %s", msg, msg2) + } +} + +func compareRead(it *RowIterator, want [][]Value, compareTotalRows bool) (msg string, ok bool) { + got, _, totalRows, err := readAll(it) + if err != nil { + return err.Error(), false + } + if len(got) != len(want) { + return fmt.Sprintf("got %d rows, want %d", len(got), len(want)), false + } + if compareTotalRows && len(got) != int(totalRows) { + return fmt.Sprintf("got %d rows, but totalRows = %d", len(got), totalRows), false + } + sort.Sort(byCol0(got)) + for i, r := range got { + gotRow := []Value(r) + wantRow := want[i] + if !testutil.Equal(gotRow, wantRow) { + return fmt.Sprintf("#%d: got %#v, want %#v", i, gotRow, wantRow), false + } + } + return "", true +} + +func readAll(it *RowIterator) ([][]Value, Schema, uint64, error) { + var ( + rows [][]Value + schema Schema + totalRows uint64 + ) + for { + var vals []Value + err := it.Next(&vals) + if err == iterator.Done { + return rows, schema, totalRows, nil + } + if err != nil { + return nil, nil, 0, err + } + rows = append(rows, vals) + schema = it.Schema + totalRows = it.TotalRows + } +} + +type byCol0 [][]Value + +func (b byCol0) Len() int { return len(b) } +func (b byCol0) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byCol0) Less(i, j int) bool { + switch a := b[i][0].(type) { + case string: + return a < b[j][0].(string) + case civil.Date: + return a.Before(b[j][0].(civil.Date)) + default: + panic("unknown type") + } +} + +func hasStatusCode(err error, code int) bool { + if e, ok := err.(*googleapi.Error); ok && e.Code == code { + return true + } + return false +} + +// wait polls the job until it is complete or an error is returned. +func wait(ctx context.Context, job *Job) error { + status, err := job.Wait(ctx) + if err != nil { + return err + } + if status.Err() != nil { + return fmt.Errorf("job status error: %#v", status.Err()) + } + if status.Statistics == nil { + return errors.New("nil Statistics") + } + if status.Statistics.EndTime.IsZero() { + return errors.New("EndTime is zero") + } + if status.Statistics.Details == nil { + return errors.New("nil Statistics.Details") + } + return nil +} + +// waitForRow polls the table until it contains a row. +// TODO(jba): use internal.Retry. +func waitForRow(ctx context.Context, table *Table) error { + for { + it := table.Read(ctx) + var v []Value + err := it.Next(&v) + if err == nil { + return nil + } + if err != iterator.Done { + return err + } + time.Sleep(1 * time.Second) + } +} + +func putError(err error) string { + pme, ok := err.(PutMultiError) + if !ok { + return err.Error() + } + var msgs []string + for _, err := range pme { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "\n") +} diff --git a/vendor/cloud.google.com/go/bigquery/iterator.go b/vendor/cloud.google.com/go/bigquery/iterator.go new file mode 100644 index 0000000000..f8894773f7 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/iterator.go @@ -0,0 +1,215 @@ +// Copyright 2015 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 bigquery + +import ( + "fmt" + "reflect" + + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" + "google.golang.org/api/iterator" +) + +func newRowIterator(ctx context.Context, t *Table, pf pageFetcher) *RowIterator { + it := &RowIterator{ + ctx: ctx, + table: t, + pf: pf, + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.rows) }, + func() interface{} { r := it.rows; it.rows = nil; return r }) + return it +} + +// A RowIterator provides access to the result of a BigQuery lookup. +type RowIterator struct { + ctx context.Context + table *Table + pf pageFetcher + pageInfo *iterator.PageInfo + nextFunc func() error + + // StartIndex can be set before the first call to Next. If PageInfo().Token + // is also set, StartIndex is ignored. + StartIndex uint64 + + // The schema of the table. Available after the first call to Next. + Schema Schema + + // The total number of rows in the result. Available after the first call to Next. + // May be zero just after rows were inserted. + TotalRows uint64 + + rows [][]Value + structLoader structLoader // used to populate a pointer to a struct +} + +// Next loads the next row into dst. Its return value is iterator.Done if there +// are no more results. Once Next returns iterator.Done, all subsequent calls +// will return iterator.Done. +// +// dst may implement ValueLoader, or may be a *[]Value, *map[string]Value, or struct pointer. +// +// If dst is a *[]Value, it will be set to to new []Value whose i'th element +// will be populated with the i'th column of the row. +// +// If dst is a *map[string]Value, a new map will be created if dst is nil. Then +// for each schema column name, the map key of that name will be set to the column's +// value. STRUCT types (RECORD types or nested schemas) become nested maps. +// +// If dst is pointer to a struct, each column in the schema will be matched +// with an exported field of the struct that has the same name, ignoring case. +// Unmatched schema columns and struct fields will be ignored. +// +// Each BigQuery column type corresponds to one or more Go types; a matching struct +// field must be of the correct type. The correspondences are: +// +// STRING string +// BOOL bool +// INTEGER int, int8, int16, int32, int64, uint8, uint16, uint32 +// FLOAT float32, float64 +// BYTES []byte +// TIMESTAMP time.Time +// DATE civil.Date +// TIME civil.Time +// DATETIME civil.DateTime +// +// A repeated field corresponds to a slice or array of the element type. A STRUCT +// type (RECORD or nested schema) corresponds to a nested struct or struct pointer. +// All calls to Next on the same iterator must use the same struct type. +// +// It is an error to attempt to read a BigQuery NULL value into a struct field, +// unless the field is of type []byte or is one of the special Null types: NullInt64, +// NullFloat64, NullBool, NullString, NullTimestamp, NullDate, NullTime or +// NullDateTime. You can also use a *[]Value or *map[string]Value to read from a +// table with NULLs. +func (it *RowIterator) Next(dst interface{}) error { + var vl ValueLoader + switch dst := dst.(type) { + case ValueLoader: + vl = dst + case *[]Value: + vl = (*valueList)(dst) + case *map[string]Value: + vl = (*valueMap)(dst) + default: + if !isStructPtr(dst) { + return fmt.Errorf("bigquery: cannot convert %T to ValueLoader (need pointer to []Value, map[string]Value, or struct)", dst) + } + } + if err := it.nextFunc(); err != nil { + return err + } + row := it.rows[0] + it.rows = it.rows[1:] + + if vl == nil { + // This can only happen if dst is a pointer to a struct. We couldn't + // set vl above because we need the schema. + if err := it.structLoader.set(dst, it.Schema); err != nil { + return err + } + vl = &it.structLoader + } + return vl.Load(row, it.Schema) +} + +func isStructPtr(x interface{}) bool { + t := reflect.TypeOf(x) + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *RowIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +func (it *RowIterator) fetch(pageSize int, pageToken string) (string, error) { + res, err := it.pf(it.ctx, it.table, it.Schema, it.StartIndex, int64(pageSize), pageToken) + if err != nil { + return "", err + } + it.rows = append(it.rows, res.rows...) + it.Schema = res.schema + it.TotalRows = res.totalRows + return res.pageToken, nil +} + +// A pageFetcher returns a page of rows from a destination table. +type pageFetcher func(ctx context.Context, _ *Table, _ Schema, startIndex uint64, pageSize int64, pageToken string) (*fetchPageResult, error) + +type fetchPageResult struct { + pageToken string + rows [][]Value + totalRows uint64 + schema Schema +} + +// fetchPage gets a page of rows from t. +func fetchPage(ctx context.Context, t *Table, schema Schema, startIndex uint64, pageSize int64, pageToken string) (*fetchPageResult, error) { + // Fetch the table schema in the background, if necessary. + errc := make(chan error, 1) + if schema != nil { + errc <- nil + } else { + go func() { + var bqt *bq.Table + err := runWithRetry(ctx, func() (err error) { + bqt, err = t.c.bqs.Tables.Get(t.ProjectID, t.DatasetID, t.TableID). + Fields("schema"). + Context(ctx). + Do() + return err + }) + if err == nil && bqt.Schema != nil { + schema = bqToSchema(bqt.Schema) + } + errc <- err + }() + } + call := t.c.bqs.Tabledata.List(t.ProjectID, t.DatasetID, t.TableID) + setClientHeader(call.Header()) + if pageToken != "" { + call.PageToken(pageToken) + } else { + call.StartIndex(startIndex) + } + if pageSize > 0 { + call.MaxResults(pageSize) + } + var res *bq.TableDataList + err := runWithRetry(ctx, func() (err error) { + res, err = call.Context(ctx).Do() + return err + }) + if err != nil { + return nil, err + } + err = <-errc + if err != nil { + return nil, err + } + rows, err := convertRows(res.Rows, schema) + if err != nil { + return nil, err + } + return &fetchPageResult{ + pageToken: res.PageToken, + rows: rows, + totalRows: uint64(res.TotalRows), + schema: schema, + }, nil +} diff --git a/vendor/cloud.google.com/go/bigquery/iterator_test.go b/vendor/cloud.google.com/go/bigquery/iterator_test.go new file mode 100644 index 0000000000..d4c931b7eb --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/iterator_test.go @@ -0,0 +1,363 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "fmt" + "testing" + + "cloud.google.com/go/internal/testutil" + + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +type fetchResponse struct { + result *fetchPageResult // The result to return. + err error // The error to return. +} + +// pageFetcherStub services fetch requests by returning data from an in-memory list of values. +type pageFetcherStub struct { + fetchResponses map[string]fetchResponse + err error +} + +func (pf *pageFetcherStub) fetchPage(ctx context.Context, _ *Table, _ Schema, _ uint64, _ int64, pageToken string) (*fetchPageResult, error) { + call, ok := pf.fetchResponses[pageToken] + if !ok { + pf.err = fmt.Errorf("Unexpected page token: %q", pageToken) + } + return call.result, call.err +} + +func TestIterator(t *testing.T) { + var ( + iiSchema = Schema{ + {Type: IntegerFieldType}, + {Type: IntegerFieldType}, + } + siSchema = Schema{ + {Type: StringFieldType}, + {Type: IntegerFieldType}, + } + ) + fetchFailure := errors.New("fetch failure") + + testCases := []struct { + desc string + pageToken string + fetchResponses map[string]fetchResponse + want [][]Value + wantErr error + wantSchema Schema + wantTotalRows uint64 + }{ + { + desc: "Iteration over single empty page", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{}, + schema: Schema{}, + }, + }, + }, + want: [][]Value{}, + wantSchema: Schema{}, + }, + { + desc: "Iteration over single page", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{{1, 2}, {11, 12}}, + schema: iiSchema, + totalRows: 4, + }, + }, + }, + want: [][]Value{{1, 2}, {11, 12}}, + wantSchema: iiSchema, + wantTotalRows: 4, + }, + { + desc: "Iteration over single page with different schema", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{{"1", 2}, {"11", 12}}, + schema: siSchema, + }, + }, + }, + want: [][]Value{{"1", 2}, {"11", 12}}, + wantSchema: siSchema, + }, + { + desc: "Iteration over two pages", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "a", + rows: [][]Value{{1, 2}, {11, 12}}, + schema: iiSchema, + totalRows: 4, + }, + }, + "a": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{{101, 102}, {111, 112}}, + schema: iiSchema, + totalRows: 4, + }, + }, + }, + want: [][]Value{{1, 2}, {11, 12}, {101, 102}, {111, 112}}, + wantSchema: iiSchema, + wantTotalRows: 4, + }, + { + desc: "Server response includes empty page", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "a", + rows: [][]Value{{1, 2}, {11, 12}}, + schema: iiSchema, + }, + }, + "a": { + result: &fetchPageResult{ + pageToken: "b", + rows: [][]Value{}, + schema: iiSchema, + }, + }, + "b": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{{101, 102}, {111, 112}}, + schema: iiSchema, + }, + }, + }, + want: [][]Value{{1, 2}, {11, 12}, {101, 102}, {111, 112}}, + wantSchema: iiSchema, + }, + { + desc: "Fetch error", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "a", + rows: [][]Value{{1, 2}, {11, 12}}, + schema: iiSchema, + }, + }, + "a": { + // We returns some data from this fetch, but also an error. + // So the end result should include only data from the previous fetch. + err: fetchFailure, + result: &fetchPageResult{ + pageToken: "b", + rows: [][]Value{{101, 102}, {111, 112}}, + schema: iiSchema, + }, + }, + }, + want: [][]Value{{1, 2}, {11, 12}}, + wantErr: fetchFailure, + wantSchema: iiSchema, + }, + + { + desc: "Skip over an entire page", + pageToken: "a", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "a", + rows: [][]Value{{1, 2}, {11, 12}}, + schema: iiSchema, + }, + }, + "a": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{{101, 102}, {111, 112}}, + schema: iiSchema, + }, + }, + }, + want: [][]Value{{101, 102}, {111, 112}}, + wantSchema: iiSchema, + }, + + { + desc: "Skip beyond all data", + pageToken: "b", + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "a", + rows: [][]Value{{1, 2}, {11, 12}}, + schema: iiSchema, + }, + }, + "a": { + result: &fetchPageResult{ + pageToken: "b", + rows: [][]Value{{101, 102}, {111, 112}}, + schema: iiSchema, + }, + }, + "b": { + result: &fetchPageResult{}, + }, + }, + // In this test case, Next will return false on its first call, + // so we won't even attempt to call Get. + want: [][]Value{}, + wantSchema: Schema{}, + }, + } + + for _, tc := range testCases { + pf := &pageFetcherStub{ + fetchResponses: tc.fetchResponses, + } + it := newRowIterator(context.Background(), nil, pf.fetchPage) + it.PageInfo().Token = tc.pageToken + values, schema, totalRows, err := consumeRowIterator(it) + if err != tc.wantErr { + t.Fatalf("%s: got %v, want %v", tc.desc, err, tc.wantErr) + } + if (len(values) != 0 || len(tc.want) != 0) && !testutil.Equal(values, tc.want) { + t.Errorf("%s: values:\ngot: %v\nwant:%v", tc.desc, values, tc.want) + } + if (len(schema) != 0 || len(tc.wantSchema) != 0) && !testutil.Equal(schema, tc.wantSchema) { + t.Errorf("%s: iterator.Schema:\ngot: %v\nwant: %v", tc.desc, schema, tc.wantSchema) + } + if totalRows != tc.wantTotalRows { + t.Errorf("%s: totalRows: got %d, want %d", tc.desc, totalRows, tc.wantTotalRows) + } + } +} + +// consumeRowIterator reads the schema and all values from a RowIterator and returns them. +func consumeRowIterator(it *RowIterator) ([][]Value, Schema, uint64, error) { + var ( + got [][]Value + schema Schema + totalRows uint64 + ) + for { + var vls []Value + err := it.Next(&vls) + if err == iterator.Done { + return got, schema, totalRows, nil + } + if err != nil { + return got, schema, totalRows, err + } + got = append(got, vls) + schema = it.Schema + totalRows = it.TotalRows + } +} + +func TestNextDuringErrorState(t *testing.T) { + pf := &pageFetcherStub{ + fetchResponses: map[string]fetchResponse{ + "": {err: errors.New("bang")}, + }, + } + it := newRowIterator(context.Background(), nil, pf.fetchPage) + var vals []Value + if err := it.Next(&vals); err == nil { + t.Errorf("Expected error after calling Next") + } + if err := it.Next(&vals); err == nil { + t.Errorf("Expected error calling Next again when iterator has a non-nil error.") + } +} + +func TestNextAfterFinished(t *testing.T) { + testCases := []struct { + fetchResponses map[string]fetchResponse + want [][]Value + }{ + { + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{{1, 2}, {11, 12}}, + }, + }, + }, + want: [][]Value{{1, 2}, {11, 12}}, + }, + { + fetchResponses: map[string]fetchResponse{ + "": { + result: &fetchPageResult{ + pageToken: "", + rows: [][]Value{}, + }, + }, + }, + want: [][]Value{}, + }, + } + + for _, tc := range testCases { + pf := &pageFetcherStub{ + fetchResponses: tc.fetchResponses, + } + it := newRowIterator(context.Background(), nil, pf.fetchPage) + + values, _, _, err := consumeRowIterator(it) + if err != nil { + t.Fatal(err) + } + if (len(values) != 0 || len(tc.want) != 0) && !testutil.Equal(values, tc.want) { + t.Errorf("values: got:\n%v\nwant:\n%v", values, tc.want) + } + // Try calling Get again. + var vals []Value + if err := it.Next(&vals); err != iterator.Done { + t.Errorf("Expected Done calling Next when there are no more values") + } + } +} + +func TestIteratorNextTypes(t *testing.T) { + it := newRowIterator(context.Background(), nil, nil) + for _, v := range []interface{}{3, "s", []int{}, &[]int{}, + map[string]Value{}, &map[string]interface{}{}, + struct{}{}, + } { + if err := it.Next(v); err == nil { + t.Errorf("%v: want error, got nil", v) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/job.go b/vendor/cloud.google.com/go/bigquery/job.go new file mode 100644 index 0000000000..7f5afddc7f --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/job.go @@ -0,0 +1,794 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "fmt" + "time" + + "cloud.google.com/go/internal" + "cloud.google.com/go/internal/trace" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" + "google.golang.org/api/googleapi" + "google.golang.org/api/iterator" +) + +// A Job represents an operation which has been submitted to BigQuery for processing. +type Job struct { + c *Client + projectID string + jobID string + location string + + config *bq.JobConfiguration + lastStatus *JobStatus +} + +// JobFromID creates a Job which refers to an existing BigQuery job. The job +// need not have been created by this package. For example, the job may have +// been created in the BigQuery console. +// +// For jobs whose location is other than "US" or "EU", set Client.Location or use +// JobFromIDLocation. +func (c *Client) JobFromID(ctx context.Context, id string) (*Job, error) { + return c.JobFromIDLocation(ctx, id, c.Location) +} + +// JobFromIDLocation creates a Job which refers to an existing BigQuery job. The job +// need not have been created by this package (for example, it may have +// been created in the BigQuery console), but it must exist in the specified location. +func (c *Client) JobFromIDLocation(ctx context.Context, id, location string) (j *Job, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.JobFromIDLocation") + defer func() { trace.EndSpan(ctx, err) }() + + bqjob, err := c.getJobInternal(ctx, id, location, "configuration", "jobReference", "status", "statistics") + if err != nil { + return nil, err + } + return bqToJob(bqjob, c) +} + +// ID returns the job's ID. +func (j *Job) ID() string { + return j.jobID +} + +// Location returns the job's location. +func (j *Job) Location() string { + return j.location +} + +// State is one of a sequence of states that a Job progresses through as it is processed. +type State int + +const ( + StateUnspecified State = iota // used only as a default in JobIterator + Pending + Running + Done +) + +// JobStatus contains the current State of a job, and errors encountered while processing that job. +type JobStatus struct { + State State + + err error + + // All errors encountered during the running of the job. + // Not all Errors are fatal, so errors here do not necessarily mean that the job has completed or was unsuccessful. + Errors []*Error + + // Statistics about the job. + Statistics *JobStatistics +} + +// JobConfig contains configuration information for a job. It is implemented by +// *CopyConfig, *ExtractConfig, *LoadConfig and *QueryConfig. +type JobConfig interface { + isJobConfig() +} + +func (*CopyConfig) isJobConfig() {} +func (*ExtractConfig) isJobConfig() {} +func (*LoadConfig) isJobConfig() {} +func (*QueryConfig) isJobConfig() {} + +// Config returns the configuration information for j. +func (j *Job) Config() (JobConfig, error) { + return bqToJobConfig(j.config, j.c) +} + +func bqToJobConfig(q *bq.JobConfiguration, c *Client) (JobConfig, error) { + switch { + case q == nil: + return nil, nil + case q.Copy != nil: + return bqToCopyConfig(q, c), nil + case q.Extract != nil: + return bqToExtractConfig(q, c), nil + case q.Load != nil: + return bqToLoadConfig(q, c), nil + case q.Query != nil: + return bqToQueryConfig(q, c) + default: + return nil, nil + } +} + +// JobIDConfig describes how to create an ID for a job. +type JobIDConfig struct { + // JobID is the ID to use for the job. If empty, a random job ID will be generated. + JobID string + + // If AddJobIDSuffix is true, then a random string will be appended to JobID. + AddJobIDSuffix bool + + // Location is the location for the job. + Location string +} + +// createJobRef creates a JobReference. +func (j *JobIDConfig) createJobRef(c *Client) *bq.JobReference { + // We don't check whether projectID is empty; the server will return an + // error when it encounters the resulting JobReference. + loc := j.Location + if loc == "" { // Use Client.Location as a default. + loc = c.Location + } + jr := &bq.JobReference{ProjectId: c.projectID, Location: loc} + if j.JobID == "" { + jr.JobId = randomIDFn() + } else if j.AddJobIDSuffix { + jr.JobId = j.JobID + "-" + randomIDFn() + } else { + jr.JobId = j.JobID + } + return jr +} + +// Done reports whether the job has completed. +// After Done returns true, the Err method will return an error if the job completed unsuccessfully. +func (s *JobStatus) Done() bool { + return s.State == Done +} + +// Err returns the error that caused the job to complete unsuccessfully (if any). +func (s *JobStatus) Err() error { + return s.err +} + +// Status retrieves the current status of the job from BigQuery. It fails if the Status could not be determined. +func (j *Job) Status(ctx context.Context) (js *JobStatus, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Job.Status") + defer func() { trace.EndSpan(ctx, err) }() + + bqjob, err := j.c.getJobInternal(ctx, j.jobID, j.location, "status", "statistics") + if err != nil { + return nil, err + } + if err := j.setStatus(bqjob.Status); err != nil { + return nil, err + } + j.setStatistics(bqjob.Statistics, j.c) + return j.lastStatus, nil +} + +// LastStatus returns the most recently retrieved status of the job. The status is +// retrieved when a new job is created, or when JobFromID or Job.Status is called. +// Call Job.Status to get the most up-to-date information about a job. +func (j *Job) LastStatus() *JobStatus { + return j.lastStatus +} + +// Cancel requests that a job be cancelled. This method returns without waiting for +// cancellation to take effect. To check whether the job has terminated, use Job.Status. +// Cancelled jobs may still incur costs. +func (j *Job) Cancel(ctx context.Context) error { + // Jobs.Cancel returns a job entity, but the only relevant piece of + // data it may contain (the status of the job) is unreliable. From the + // docs: "This call will return immediately, and the client will need + // to poll for the job status to see if the cancel completed + // successfully". So it would be misleading to return a status. + call := j.c.bqs.Jobs.Cancel(j.projectID, j.jobID). + Location(j.location). + Fields(). // We don't need any of the response data. + Context(ctx) + setClientHeader(call.Header()) + return runWithRetry(ctx, func() error { + _, err := call.Do() + return err + }) +} + +// Wait blocks until the job or the context is done. It returns the final status +// of the job. +// If an error occurs while retrieving the status, Wait returns that error. But +// Wait returns nil if the status was retrieved successfully, even if +// status.Err() != nil. So callers must check both errors. See the example. +func (j *Job) Wait(ctx context.Context) (js *JobStatus, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Job.Wait") + defer func() { trace.EndSpan(ctx, err) }() + + if j.isQuery() { + // We can avoid polling for query jobs. + if _, err := j.waitForQuery(ctx, j.projectID); err != nil { + return nil, err + } + // Note: extra RPC even if you just want to wait for the query to finish. + js, err := j.Status(ctx) + if err != nil { + return nil, err + } + return js, nil + } + // Non-query jobs must poll. + err = internal.Retry(ctx, gax.Backoff{}, func() (stop bool, err error) { + js, err = j.Status(ctx) + if err != nil { + return true, err + } + if js.Done() { + return true, nil + } + return false, nil + }) + if err != nil { + return nil, err + } + return js, nil +} + +// Read fetches the results of a query job. +// If j is not a query job, Read returns an error. +func (j *Job) Read(ctx context.Context) (ri *RowIterator, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Job.Read") + defer func() { trace.EndSpan(ctx, err) }() + + return j.read(ctx, j.waitForQuery, fetchPage) +} + +func (j *Job) read(ctx context.Context, waitForQuery func(context.Context, string) (Schema, error), pf pageFetcher) (*RowIterator, error) { + if !j.isQuery() { + return nil, errors.New("bigquery: cannot read from a non-query job") + } + destTable := j.config.Query.DestinationTable + // The destination table should only be nil if there was a query error. + projectID := j.projectID + if destTable != nil && projectID != destTable.ProjectId { + return nil, fmt.Errorf("bigquery: job project ID is %q, but destination table's is %q", projectID, destTable.ProjectId) + } + schema, err := waitForQuery(ctx, projectID) + if err != nil { + return nil, err + } + if destTable == nil { + return nil, errors.New("bigquery: query job missing destination table") + } + dt := bqToTable(destTable, j.c) + it := newRowIterator(ctx, dt, pf) + it.Schema = schema + return it, nil +} + +// waitForQuery waits for the query job to complete and returns its schema. +func (j *Job) waitForQuery(ctx context.Context, projectID string) (Schema, error) { + // Use GetQueryResults only to wait for completion, not to read results. + call := j.c.bqs.Jobs.GetQueryResults(projectID, j.jobID).Location(j.location).Context(ctx).MaxResults(0) + setClientHeader(call.Header()) + backoff := gax.Backoff{ + Initial: 1 * time.Second, + Multiplier: 2, + Max: 60 * time.Second, + } + var res *bq.GetQueryResultsResponse + err := internal.Retry(ctx, backoff, func() (stop bool, err error) { + res, err = call.Do() + if err != nil { + return !retryableError(err), err + } + if !res.JobComplete { // GetQueryResults may return early without error; retry. + return false, nil + } + return true, nil + }) + if err != nil { + return nil, err + } + return bqToSchema(res.Schema), nil +} + +// JobStatistics contains statistics about a job. +type JobStatistics struct { + CreationTime time.Time + StartTime time.Time + EndTime time.Time + TotalBytesProcessed int64 + + Details Statistics +} + +// Statistics is one of ExtractStatistics, LoadStatistics or QueryStatistics. +type Statistics interface { + implementsStatistics() +} + +// ExtractStatistics contains statistics about an extract job. +type ExtractStatistics struct { + // The number of files per destination URI or URI pattern specified in the + // extract configuration. These values will be in the same order as the + // URIs specified in the 'destinationUris' field. + DestinationURIFileCounts []int64 +} + +// LoadStatistics contains statistics about a load job. +type LoadStatistics struct { + // The number of bytes of source data in a load job. + InputFileBytes int64 + + // The number of source files in a load job. + InputFiles int64 + + // Size of the loaded data in bytes. Note that while a load job is in the + // running state, this value may change. + OutputBytes int64 + + // The number of rows imported in a load job. Note that while an import job is + // in the running state, this value may change. + OutputRows int64 +} + +// QueryStatistics contains statistics about a query job. +type QueryStatistics struct { + // Billing tier for the job. + BillingTier int64 + + // Whether the query result was fetched from the query cache. + CacheHit bool + + // The type of query statement, if valid. + StatementType string + + // Total bytes billed for the job. + TotalBytesBilled int64 + + // Total bytes processed for the job. + TotalBytesProcessed int64 + + // Describes execution plan for the query. + QueryPlan []*ExplainQueryStage + + // The number of rows affected by a DML statement. Present only for DML + // statements INSERT, UPDATE or DELETE. + NumDMLAffectedRows int64 + + // Describes a timeline of job execution. + Timeline []*QueryTimelineSample + + // ReferencedTables: [Output-only, Experimental] Referenced tables for + // the job. Queries that reference more than 50 tables will not have a + // complete list. + ReferencedTables []*Table + + // The schema of the results. Present only for successful dry run of + // non-legacy SQL queries. + Schema Schema + + // Slot-milliseconds consumed by this query job. + SlotMillis int64 + + // Standard SQL: list of undeclared query parameter names detected during a + // dry run validation. + UndeclaredQueryParameterNames []string + + // DDL target table. + DDLTargetTable *Table + + // DDL Operation performed on the target table. Used to report how the + // query impacted the DDL target table. + DDLOperationPerformed string +} + +// ExplainQueryStage describes one stage of a query. +type ExplainQueryStage struct { + // CompletedParallelInputs: Number of parallel input segments completed. + CompletedParallelInputs int64 + + // ComputeAvg: Duration the average shard spent on CPU-bound tasks. + ComputeAvg time.Duration + + // ComputeMax: Duration the slowest shard spent on CPU-bound tasks. + ComputeMax time.Duration + + // Relative amount of the total time the average shard spent on CPU-bound tasks. + ComputeRatioAvg float64 + + // Relative amount of the total time the slowest shard spent on CPU-bound tasks. + ComputeRatioMax float64 + + // EndTime: Stage end time. + EndTime time.Time + + // Unique ID for stage within plan. + ID int64 + + // InputStages: IDs for stages that are inputs to this stage. + InputStages []int64 + + // Human-readable name for stage. + Name string + + // ParallelInputs: Number of parallel input segments to be processed. + ParallelInputs int64 + + // ReadAvg: Duration the average shard spent reading input. + ReadAvg time.Duration + + // ReadMax: Duration the slowest shard spent reading input. + ReadMax time.Duration + + // Relative amount of the total time the average shard spent reading input. + ReadRatioAvg float64 + + // Relative amount of the total time the slowest shard spent reading input. + ReadRatioMax float64 + + // Number of records read into the stage. + RecordsRead int64 + + // Number of records written by the stage. + RecordsWritten int64 + + // ShuffleOutputBytes: Total number of bytes written to shuffle. + ShuffleOutputBytes int64 + + // ShuffleOutputBytesSpilled: Total number of bytes written to shuffle + // and spilled to disk. + ShuffleOutputBytesSpilled int64 + + // StartTime: Stage start time. + StartTime time.Time + + // Current status for the stage. + Status string + + // List of operations within the stage in dependency order (approximately + // chronological). + Steps []*ExplainQueryStep + + // WaitAvg: Duration the average shard spent waiting to be scheduled. + WaitAvg time.Duration + + // WaitMax: Duration the slowest shard spent waiting to be scheduled. + WaitMax time.Duration + + // Relative amount of the total time the average shard spent waiting to be scheduled. + WaitRatioAvg float64 + + // Relative amount of the total time the slowest shard spent waiting to be scheduled. + WaitRatioMax float64 + + // WriteAvg: Duration the average shard spent on writing output. + WriteAvg time.Duration + + // WriteMax: Duration the slowest shard spent on writing output. + WriteMax time.Duration + + // Relative amount of the total time the average shard spent on writing output. + WriteRatioAvg float64 + + // Relative amount of the total time the slowest shard spent on writing output. + WriteRatioMax float64 +} + +// ExplainQueryStep describes one step of a query stage. +type ExplainQueryStep struct { + // Machine-readable operation type. + Kind string + + // Human-readable stage descriptions. + Substeps []string +} + +// QueryTimelineSample represents a sample of execution statistics at a point in time. +type QueryTimelineSample struct { + + // Total number of units currently being processed by workers, represented as largest value since last sample. + ActiveUnits int64 + + // Total parallel units of work completed by this query. + CompletedUnits int64 + + // Time elapsed since start of query execution. + Elapsed time.Duration + + // Total parallel units of work remaining for the active stages. + PendingUnits int64 + + // Cumulative slot-milliseconds consumed by the query. + SlotMillis int64 +} + +func (*ExtractStatistics) implementsStatistics() {} +func (*LoadStatistics) implementsStatistics() {} +func (*QueryStatistics) implementsStatistics() {} + +// Jobs lists jobs within a project. +func (c *Client) Jobs(ctx context.Context) *JobIterator { + it := &JobIterator{ + ctx: ctx, + c: c, + ProjectID: c.projectID, + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// JobIterator iterates over jobs in a project. +type JobIterator struct { + ProjectID string // Project ID of the jobs to list. Default is the client's project. + AllUsers bool // Whether to list jobs owned by all users in the project, or just the current caller. + State State // List only jobs in the given state. Defaults to all states. + + ctx context.Context + c *Client + pageInfo *iterator.PageInfo + nextFunc func() error + items []*Job +} + +func (it *JobIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +func (it *JobIterator) Next() (*Job, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *JobIterator) fetch(pageSize int, pageToken string) (string, error) { + var st string + switch it.State { + case StateUnspecified: + st = "" + case Pending: + st = "pending" + case Running: + st = "running" + case Done: + st = "done" + default: + return "", fmt.Errorf("bigquery: invalid value for JobIterator.State: %d", it.State) + } + + req := it.c.bqs.Jobs.List(it.ProjectID). + Context(it.ctx). + PageToken(pageToken). + Projection("full"). + AllUsers(it.AllUsers) + if st != "" { + req.StateFilter(st) + } + setClientHeader(req.Header()) + if pageSize > 0 { + req.MaxResults(int64(pageSize)) + } + res, err := req.Do() + if err != nil { + return "", err + } + for _, j := range res.Jobs { + job, err := convertListedJob(j, it.c) + if err != nil { + return "", err + } + it.items = append(it.items, job) + } + return res.NextPageToken, nil +} + +func convertListedJob(j *bq.JobListJobs, c *Client) (*Job, error) { + return bqToJob2(j.JobReference, j.Configuration, j.Status, j.Statistics, c) +} + +func (c *Client) getJobInternal(ctx context.Context, jobID, location string, fields ...googleapi.Field) (*bq.Job, error) { + var job *bq.Job + call := c.bqs.Jobs.Get(c.projectID, jobID).Context(ctx) + if location != "" { + call = call.Location(location) + } + if len(fields) > 0 { + call = call.Fields(fields...) + } + setClientHeader(call.Header()) + err := runWithRetry(ctx, func() (err error) { + job, err = call.Do() + return err + }) + if err != nil { + return nil, err + } + return job, nil +} + +func bqToJob(q *bq.Job, c *Client) (*Job, error) { + return bqToJob2(q.JobReference, q.Configuration, q.Status, q.Statistics, c) +} + +func bqToJob2(qr *bq.JobReference, qc *bq.JobConfiguration, qs *bq.JobStatus, qt *bq.JobStatistics, c *Client) (*Job, error) { + j := &Job{ + projectID: qr.ProjectId, + jobID: qr.JobId, + location: qr.Location, + c: c, + } + j.setConfig(qc) + if err := j.setStatus(qs); err != nil { + return nil, err + } + j.setStatistics(qt, c) + return j, nil +} + +func (j *Job) setConfig(config *bq.JobConfiguration) { + if config == nil { + return + } + j.config = config +} + +func (j *Job) isQuery() bool { + return j.config != nil && j.config.Query != nil +} + +var stateMap = map[string]State{"PENDING": Pending, "RUNNING": Running, "DONE": Done} + +func (j *Job) setStatus(qs *bq.JobStatus) error { + if qs == nil { + return nil + } + state, ok := stateMap[qs.State] + if !ok { + return fmt.Errorf("unexpected job state: %v", qs.State) + } + j.lastStatus = &JobStatus{ + State: state, + err: nil, + } + if err := bqToError(qs.ErrorResult); state == Done && err != nil { + j.lastStatus.err = err + } + for _, ep := range qs.Errors { + j.lastStatus.Errors = append(j.lastStatus.Errors, bqToError(ep)) + } + return nil +} + +func (j *Job) setStatistics(s *bq.JobStatistics, c *Client) { + if s == nil || j.lastStatus == nil { + return + } + js := &JobStatistics{ + CreationTime: unixMillisToTime(s.CreationTime), + StartTime: unixMillisToTime(s.StartTime), + EndTime: unixMillisToTime(s.EndTime), + TotalBytesProcessed: s.TotalBytesProcessed, + } + switch { + case s.Extract != nil: + js.Details = &ExtractStatistics{ + DestinationURIFileCounts: []int64(s.Extract.DestinationUriFileCounts), + } + case s.Load != nil: + js.Details = &LoadStatistics{ + InputFileBytes: s.Load.InputFileBytes, + InputFiles: s.Load.InputFiles, + OutputBytes: s.Load.OutputBytes, + OutputRows: s.Load.OutputRows, + } + case s.Query != nil: + var names []string + for _, qp := range s.Query.UndeclaredQueryParameters { + names = append(names, qp.Name) + } + var tables []*Table + for _, tr := range s.Query.ReferencedTables { + tables = append(tables, bqToTable(tr, c)) + } + js.Details = &QueryStatistics{ + BillingTier: s.Query.BillingTier, + CacheHit: s.Query.CacheHit, + DDLTargetTable: bqToTable(s.Query.DdlTargetTable, c), + DDLOperationPerformed: s.Query.DdlOperationPerformed, + StatementType: s.Query.StatementType, + TotalBytesBilled: s.Query.TotalBytesBilled, + TotalBytesProcessed: s.Query.TotalBytesProcessed, + NumDMLAffectedRows: s.Query.NumDmlAffectedRows, + QueryPlan: queryPlanFromProto(s.Query.QueryPlan), + Schema: bqToSchema(s.Query.Schema), + SlotMillis: s.Query.TotalSlotMs, + Timeline: timelineFromProto(s.Query.Timeline), + ReferencedTables: tables, + UndeclaredQueryParameterNames: names, + } + } + j.lastStatus.Statistics = js +} + +func queryPlanFromProto(stages []*bq.ExplainQueryStage) []*ExplainQueryStage { + var res []*ExplainQueryStage + for _, s := range stages { + var steps []*ExplainQueryStep + for _, p := range s.Steps { + steps = append(steps, &ExplainQueryStep{ + Kind: p.Kind, + Substeps: p.Substeps, + }) + } + res = append(res, &ExplainQueryStage{ + CompletedParallelInputs: s.CompletedParallelInputs, + ComputeAvg: time.Duration(s.ComputeMsAvg) * time.Millisecond, + ComputeMax: time.Duration(s.ComputeMsMax) * time.Millisecond, + ComputeRatioAvg: s.ComputeRatioAvg, + ComputeRatioMax: s.ComputeRatioMax, + EndTime: time.Unix(0, s.EndMs*1e6), + ID: s.Id, + InputStages: s.InputStages, + Name: s.Name, + ParallelInputs: s.ParallelInputs, + ReadAvg: time.Duration(s.ReadMsAvg) * time.Millisecond, + ReadMax: time.Duration(s.ReadMsMax) * time.Millisecond, + ReadRatioAvg: s.ReadRatioAvg, + ReadRatioMax: s.ReadRatioMax, + RecordsRead: s.RecordsRead, + RecordsWritten: s.RecordsWritten, + ShuffleOutputBytes: s.ShuffleOutputBytes, + ShuffleOutputBytesSpilled: s.ShuffleOutputBytesSpilled, + StartTime: time.Unix(0, s.StartMs*1e6), + Status: s.Status, + Steps: steps, + WaitAvg: time.Duration(s.WaitMsAvg) * time.Millisecond, + WaitMax: time.Duration(s.WaitMsMax) * time.Millisecond, + WaitRatioAvg: s.WaitRatioAvg, + WaitRatioMax: s.WaitRatioMax, + WriteAvg: time.Duration(s.WriteMsAvg) * time.Millisecond, + WriteMax: time.Duration(s.WriteMsMax) * time.Millisecond, + WriteRatioAvg: s.WriteRatioAvg, + WriteRatioMax: s.WriteRatioMax, + }) + } + return res +} + +func timelineFromProto(timeline []*bq.QueryTimelineSample) []*QueryTimelineSample { + var res []*QueryTimelineSample + for _, s := range timeline { + res = append(res, &QueryTimelineSample{ + ActiveUnits: s.ActiveUnits, + CompletedUnits: s.CompletedUnits, + Elapsed: time.Duration(s.ElapsedMs) * time.Millisecond, + PendingUnits: s.PendingUnits, + SlotMillis: s.TotalSlotMs, + }) + } + return res +} diff --git a/vendor/cloud.google.com/go/bigquery/job_test.go b/vendor/cloud.google.com/go/bigquery/job_test.go new file mode 100644 index 0000000000..fbde305a85 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/job_test.go @@ -0,0 +1,95 @@ +// Copyright 2017 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 bigquery + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + bq "google.golang.org/api/bigquery/v2" +) + +func TestCreateJobRef(t *testing.T) { + defer fixRandomID("RANDOM")() + cNoLoc := &Client{projectID: "projectID"} + cLoc := &Client{projectID: "projectID", Location: "defaultLoc"} + for _, test := range []struct { + in JobIDConfig + client *Client + want *bq.JobReference + }{ + { + in: JobIDConfig{JobID: "foo"}, + want: &bq.JobReference{JobId: "foo"}, + }, + { + in: JobIDConfig{}, + want: &bq.JobReference{JobId: "RANDOM"}, + }, + { + in: JobIDConfig{AddJobIDSuffix: true}, + want: &bq.JobReference{JobId: "RANDOM"}, + }, + { + in: JobIDConfig{JobID: "foo", AddJobIDSuffix: true}, + want: &bq.JobReference{JobId: "foo-RANDOM"}, + }, + { + in: JobIDConfig{JobID: "foo", Location: "loc"}, + want: &bq.JobReference{JobId: "foo", Location: "loc"}, + }, + { + in: JobIDConfig{JobID: "foo"}, + client: cLoc, + want: &bq.JobReference{JobId: "foo", Location: "defaultLoc"}, + }, + { + in: JobIDConfig{JobID: "foo", Location: "loc"}, + client: cLoc, + want: &bq.JobReference{JobId: "foo", Location: "loc"}, + }, + } { + client := test.client + if client == nil { + client = cNoLoc + } + got := test.in.createJobRef(client) + test.want.ProjectId = "projectID" + if !testutil.Equal(got, test.want) { + t.Errorf("%+v: got %+v, want %+v", test.in, got, test.want) + } + } +} + +func fixRandomID(s string) func() { + prev := randomIDFn + randomIDFn = func() string { return s } + return func() { randomIDFn = prev } +} + +func checkJob(t *testing.T, i int, got, want *bq.Job) { + if got.JobReference == nil { + t.Errorf("#%d: empty job reference", i) + return + } + if got.JobReference.JobId == "" { + t.Errorf("#%d: empty job ID", i) + return + } + d := testutil.Diff(got, want) + if d != "" { + t.Errorf("#%d: (got=-, want=+) %s", i, d) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/load.go b/vendor/cloud.google.com/go/bigquery/load.go new file mode 100644 index 0000000000..4fcabda465 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/load.go @@ -0,0 +1,141 @@ +// 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 bigquery + +import ( + "io" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" +) + +// LoadConfig holds the configuration for a load job. +type LoadConfig struct { + // Src is the source from which data will be loaded. + Src LoadSource + + // Dst is the table into which the data will be loaded. + Dst *Table + + // CreateDisposition specifies the circumstances under which the destination table will be created. + // The default is CreateIfNeeded. + CreateDisposition TableCreateDisposition + + // WriteDisposition specifies how existing data in the destination table is treated. + // The default is WriteAppend. + WriteDisposition TableWriteDisposition + + // The labels associated with this job. + Labels map[string]string + + // If non-nil, the destination table is partitioned by time. + TimePartitioning *TimePartitioning + + // Custom encryption configuration (e.g., Cloud KMS keys). + DestinationEncryptionConfig *EncryptionConfig + + // Allows the schema of the destination table to be updated as a side effect of + // the load job. + SchemaUpdateOptions []string +} + +func (l *LoadConfig) toBQ() (*bq.JobConfiguration, io.Reader) { + config := &bq.JobConfiguration{ + Labels: l.Labels, + Load: &bq.JobConfigurationLoad{ + CreateDisposition: string(l.CreateDisposition), + WriteDisposition: string(l.WriteDisposition), + DestinationTable: l.Dst.toBQ(), + TimePartitioning: l.TimePartitioning.toBQ(), + DestinationEncryptionConfiguration: l.DestinationEncryptionConfig.toBQ(), + SchemaUpdateOptions: l.SchemaUpdateOptions, + }, + } + media := l.Src.populateLoadConfig(config.Load) + return config, media +} + +func bqToLoadConfig(q *bq.JobConfiguration, c *Client) *LoadConfig { + lc := &LoadConfig{ + Labels: q.Labels, + CreateDisposition: TableCreateDisposition(q.Load.CreateDisposition), + WriteDisposition: TableWriteDisposition(q.Load.WriteDisposition), + Dst: bqToTable(q.Load.DestinationTable, c), + TimePartitioning: bqToTimePartitioning(q.Load.TimePartitioning), + DestinationEncryptionConfig: bqToEncryptionConfig(q.Load.DestinationEncryptionConfiguration), + SchemaUpdateOptions: q.Load.SchemaUpdateOptions, + } + var fc *FileConfig + if len(q.Load.SourceUris) == 0 { + s := NewReaderSource(nil) + fc = &s.FileConfig + lc.Src = s + } else { + s := NewGCSReference(q.Load.SourceUris...) + fc = &s.FileConfig + lc.Src = s + } + bqPopulateFileConfig(q.Load, fc) + return lc +} + +// A Loader loads data from Google Cloud Storage into a BigQuery table. +type Loader struct { + JobIDConfig + LoadConfig + c *Client +} + +// A LoadSource represents a source of data that can be loaded into +// a BigQuery table. +// +// This package defines two LoadSources: GCSReference, for Google Cloud Storage +// objects, and ReaderSource, for data read from an io.Reader. +type LoadSource interface { + // populates config, returns media + populateLoadConfig(*bq.JobConfigurationLoad) io.Reader +} + +// LoaderFrom returns a Loader which can be used to load data into a BigQuery table. +// The returned Loader may optionally be further configured before its Run method is called. +// See GCSReference and ReaderSource for additional configuration options that +// affect loading. +func (t *Table) LoaderFrom(src LoadSource) *Loader { + return &Loader{ + c: t.c, + LoadConfig: LoadConfig{ + Src: src, + Dst: t, + }, + } +} + +// Run initiates a load job. +func (l *Loader) Run(ctx context.Context) (j *Job, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Load.Run") + defer func() { trace.EndSpan(ctx, err) }() + + job, media := l.newJob() + return l.c.insertJob(ctx, job, media) +} + +func (l *Loader) newJob() (*bq.Job, io.Reader) { + config, media := l.LoadConfig.toBQ() + return &bq.Job{ + JobReference: l.JobIDConfig.createJobRef(l.c), + Configuration: config, + }, media +} diff --git a/vendor/cloud.google.com/go/bigquery/load_test.go b/vendor/cloud.google.com/go/bigquery/load_test.go new file mode 100644 index 0000000000..a14beba770 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/load_test.go @@ -0,0 +1,260 @@ +// Copyright 2015 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 bigquery + +import ( + "strings" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + bq "google.golang.org/api/bigquery/v2" +) + +func defaultLoadJob() *bq.Job { + return &bq.Job{ + JobReference: &bq.JobReference{JobId: "RANDOM", ProjectId: "client-project-id"}, + Configuration: &bq.JobConfiguration{ + Load: &bq.JobConfigurationLoad{ + DestinationTable: &bq.TableReference{ + ProjectId: "client-project-id", + DatasetId: "dataset-id", + TableId: "table-id", + }, + SourceUris: []string{"uri"}, + }, + }, + } +} + +func stringFieldSchema() *FieldSchema { + return &FieldSchema{Name: "fieldname", Type: StringFieldType} +} + +func nestedFieldSchema() *FieldSchema { + return &FieldSchema{ + Name: "nested", + Type: RecordFieldType, + Schema: Schema{stringFieldSchema()}, + } +} + +func bqStringFieldSchema() *bq.TableFieldSchema { + return &bq.TableFieldSchema{ + Name: "fieldname", + Type: "STRING", + } +} + +func bqNestedFieldSchema() *bq.TableFieldSchema { + return &bq.TableFieldSchema{ + Name: "nested", + Type: "RECORD", + Fields: []*bq.TableFieldSchema{bqStringFieldSchema()}, + } +} + +func TestLoad(t *testing.T) { + defer fixRandomID("RANDOM")() + c := &Client{projectID: "client-project-id"} + + testCases := []struct { + dst *Table + src LoadSource + jobID string + location string + config LoadConfig + want *bq.Job + }{ + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: NewGCSReference("uri"), + want: defaultLoadJob(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: NewGCSReference("uri"), + location: "loc", + want: func() *bq.Job { + j := defaultLoadJob() + j.JobReference.Location = "loc" + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + jobID: "ajob", + config: LoadConfig{ + CreateDisposition: CreateNever, + WriteDisposition: WriteTruncate, + Labels: map[string]string{"a": "b"}, + TimePartitioning: &TimePartitioning{Expiration: 1234 * time.Millisecond}, + DestinationEncryptionConfig: &EncryptionConfig{KMSKeyName: "keyName"}, + SchemaUpdateOptions: []string{"ALLOW_FIELD_ADDITION"}, + }, + src: NewGCSReference("uri"), + want: func() *bq.Job { + j := defaultLoadJob() + j.Configuration.Labels = map[string]string{"a": "b"} + j.Configuration.Load.CreateDisposition = "CREATE_NEVER" + j.Configuration.Load.WriteDisposition = "WRITE_TRUNCATE" + j.Configuration.Load.TimePartitioning = &bq.TimePartitioning{ + Type: "DAY", + ExpirationMs: 1234, + } + j.Configuration.Load.DestinationEncryptionConfiguration = &bq.EncryptionConfiguration{KmsKeyName: "keyName"} + j.JobReference = &bq.JobReference{ + JobId: "ajob", + ProjectId: "client-project-id", + } + j.Configuration.Load.SchemaUpdateOptions = []string{"ALLOW_FIELD_ADDITION"} + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: func() *GCSReference { + g := NewGCSReference("uri") + g.MaxBadRecords = 1 + g.AllowJaggedRows = true + g.AllowQuotedNewlines = true + g.IgnoreUnknownValues = true + return g + }(), + want: func() *bq.Job { + j := defaultLoadJob() + j.Configuration.Load.MaxBadRecords = 1 + j.Configuration.Load.AllowJaggedRows = true + j.Configuration.Load.AllowQuotedNewlines = true + j.Configuration.Load.IgnoreUnknownValues = true + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: func() *GCSReference { + g := NewGCSReference("uri") + g.Schema = Schema{ + stringFieldSchema(), + nestedFieldSchema(), + } + return g + }(), + want: func() *bq.Job { + j := defaultLoadJob() + j.Configuration.Load.Schema = &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqStringFieldSchema(), + bqNestedFieldSchema(), + }} + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: func() *GCSReference { + g := NewGCSReference("uri") + g.SkipLeadingRows = 1 + g.SourceFormat = JSON + g.Encoding = UTF_8 + g.FieldDelimiter = "\t" + g.Quote = "-" + return g + }(), + want: func() *bq.Job { + j := defaultLoadJob() + j.Configuration.Load.SkipLeadingRows = 1 + j.Configuration.Load.SourceFormat = "NEWLINE_DELIMITED_JSON" + j.Configuration.Load.Encoding = "UTF-8" + j.Configuration.Load.FieldDelimiter = "\t" + hyphen := "-" + j.Configuration.Load.Quote = &hyphen + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: NewGCSReference("uri"), + want: func() *bq.Job { + j := defaultLoadJob() + // Quote is left unset in GCSReference, so should be nil here. + j.Configuration.Load.Quote = nil + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: func() *GCSReference { + g := NewGCSReference("uri") + g.ForceZeroQuote = true + return g + }(), + want: func() *bq.Job { + j := defaultLoadJob() + empty := "" + j.Configuration.Load.Quote = &empty + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: func() *ReaderSource { + r := NewReaderSource(strings.NewReader("foo")) + r.SkipLeadingRows = 1 + r.SourceFormat = JSON + r.Encoding = UTF_8 + r.FieldDelimiter = "\t" + r.Quote = "-" + return r + }(), + want: func() *bq.Job { + j := defaultLoadJob() + j.Configuration.Load.SourceUris = nil + j.Configuration.Load.SkipLeadingRows = 1 + j.Configuration.Load.SourceFormat = "NEWLINE_DELIMITED_JSON" + j.Configuration.Load.Encoding = "UTF-8" + j.Configuration.Load.FieldDelimiter = "\t" + hyphen := "-" + j.Configuration.Load.Quote = &hyphen + return j + }(), + }, + } + + for i, tc := range testCases { + loader := tc.dst.LoaderFrom(tc.src) + loader.JobID = tc.jobID + loader.Location = tc.location + tc.config.Src = tc.src + tc.config.Dst = tc.dst + loader.LoadConfig = tc.config + got, _ := loader.newJob() + checkJob(t, i, got, tc.want) + + jc, err := bqToJobConfig(got.Configuration, c) + if err != nil { + t.Fatalf("#%d: %v", i, err) + } + diff := testutil.Diff(jc.(*LoadConfig), &loader.LoadConfig, + cmp.AllowUnexported(Table{}, Client{}), + cmpopts.IgnoreUnexported(ReaderSource{})) + if diff != "" { + t.Errorf("#%d: (got=-, want=+:\n%s", i, diff) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/nulls.go b/vendor/cloud.google.com/go/bigquery/nulls.go new file mode 100644 index 0000000000..7c75314f3d --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/nulls.go @@ -0,0 +1,299 @@ +// Copyright 2015 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 bigquery + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strconv" + "time" + + "cloud.google.com/go/civil" +) + +// NullInt64 represents a BigQuery INT64 that may be NULL. +type NullInt64 struct { + Int64 int64 + Valid bool // Valid is true if Int64 is not NULL. +} + +func (n NullInt64) String() string { return nullstr(n.Valid, n.Int64) } + +// NullString represents a BigQuery STRING that may be NULL. +type NullString struct { + StringVal string + Valid bool // Valid is true if StringVal is not NULL. +} + +func (n NullString) String() string { return nullstr(n.Valid, n.StringVal) } + +// NullFloat64 represents a BigQuery FLOAT64 that may be NULL. +type NullFloat64 struct { + Float64 float64 + Valid bool // Valid is true if Float64 is not NULL. +} + +func (n NullFloat64) String() string { return nullstr(n.Valid, n.Float64) } + +// NullBool represents a BigQuery BOOL that may be NULL. +type NullBool struct { + Bool bool + Valid bool // Valid is true if Bool is not NULL. +} + +func (n NullBool) String() string { return nullstr(n.Valid, n.Bool) } + +// NullTimestamp represents a BigQuery TIMESTAMP that may be null. +type NullTimestamp struct { + Timestamp time.Time + Valid bool // Valid is true if Time is not NULL. +} + +func (n NullTimestamp) String() string { return nullstr(n.Valid, n.Timestamp) } + +// NullDate represents a BigQuery DATE that may be null. +type NullDate struct { + Date civil.Date + Valid bool // Valid is true if Date is not NULL. +} + +func (n NullDate) String() string { return nullstr(n.Valid, n.Date) } + +// NullTime represents a BigQuery TIME that may be null. +type NullTime struct { + Time civil.Time + Valid bool // Valid is true if Time is not NULL. +} + +func (n NullTime) String() string { + if !n.Valid { + return "" + } + return CivilTimeString(n.Time) +} + +// NullDateTime represents a BigQuery DATETIME that may be null. +type NullDateTime struct { + DateTime civil.DateTime + Valid bool // Valid is true if DateTime is not NULL. +} + +func (n NullDateTime) String() string { + if !n.Valid { + return "" + } + return CivilDateTimeString(n.DateTime) +} + +func (n NullInt64) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Int64) } +func (n NullFloat64) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Float64) } +func (n NullBool) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Bool) } +func (n NullString) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.StringVal) } +func (n NullTimestamp) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Timestamp) } +func (n NullDate) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Date) } + +func (n NullTime) MarshalJSON() ([]byte, error) { + if !n.Valid { + return jsonNull, nil + } + return []byte(`"` + CivilTimeString(n.Time) + `"`), nil +} + +func (n NullDateTime) MarshalJSON() ([]byte, error) { + if !n.Valid { + return jsonNull, nil + } + return []byte(`"` + CivilDateTimeString(n.DateTime) + `"`), nil +} + +func nullstr(valid bool, v interface{}) string { + if !valid { + return "NULL" + } + return fmt.Sprint(v) +} + +var jsonNull = []byte("null") + +func nulljson(valid bool, v interface{}) ([]byte, error) { + if !valid { + return jsonNull, nil + } + return json.Marshal(v) +} + +func (n *NullInt64) UnmarshalJSON(b []byte) error { + n.Valid = false + n.Int64 = 0 + if bytes.Equal(b, jsonNull) { + return nil + } + + if err := json.Unmarshal(b, &n.Int64); err != nil { + return err + } + n.Valid = true + return nil +} + +func (n *NullFloat64) UnmarshalJSON(b []byte) error { + n.Valid = false + n.Float64 = 0 + if bytes.Equal(b, jsonNull) { + return nil + } + + if err := json.Unmarshal(b, &n.Float64); err != nil { + return err + } + n.Valid = true + return nil +} + +func (n *NullBool) UnmarshalJSON(b []byte) error { + n.Valid = false + n.Bool = false + if bytes.Equal(b, jsonNull) { + return nil + } + + if err := json.Unmarshal(b, &n.Bool); err != nil { + return err + } + n.Valid = true + return nil +} + +func (n *NullString) UnmarshalJSON(b []byte) error { + n.Valid = false + n.StringVal = "" + if bytes.Equal(b, jsonNull) { + return nil + } + + if err := json.Unmarshal(b, &n.StringVal); err != nil { + return err + } + n.Valid = true + return nil +} + +func (n *NullTimestamp) UnmarshalJSON(b []byte) error { + n.Valid = false + n.Timestamp = time.Time{} + if bytes.Equal(b, jsonNull) { + return nil + } + + if err := json.Unmarshal(b, &n.Timestamp); err != nil { + return err + } + n.Valid = true + return nil +} + +func (n *NullDate) UnmarshalJSON(b []byte) error { + n.Valid = false + n.Date = civil.Date{} + if bytes.Equal(b, jsonNull) { + return nil + } + + if err := json.Unmarshal(b, &n.Date); err != nil { + return err + } + n.Valid = true + return nil +} + +func (n *NullTime) UnmarshalJSON(b []byte) error { + n.Valid = false + n.Time = civil.Time{} + if bytes.Equal(b, jsonNull) { + return nil + } + + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + + t, err := civil.ParseTime(s) + if err != nil { + return err + } + n.Time = t + + n.Valid = true + return nil +} + +func (n *NullDateTime) UnmarshalJSON(b []byte) error { + n.Valid = false + n.DateTime = civil.DateTime{} + if bytes.Equal(b, jsonNull) { + return nil + } + + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + + dt, err := parseCivilDateTime(s) + if err != nil { + return err + } + n.DateTime = dt + + n.Valid = true + return nil +} + +var ( + typeOfNullInt64 = reflect.TypeOf(NullInt64{}) + typeOfNullFloat64 = reflect.TypeOf(NullFloat64{}) + typeOfNullBool = reflect.TypeOf(NullBool{}) + typeOfNullString = reflect.TypeOf(NullString{}) + typeOfNullTimestamp = reflect.TypeOf(NullTimestamp{}) + typeOfNullDate = reflect.TypeOf(NullDate{}) + typeOfNullTime = reflect.TypeOf(NullTime{}) + typeOfNullDateTime = reflect.TypeOf(NullDateTime{}) +) + +func nullableFieldType(t reflect.Type) FieldType { + switch t { + case typeOfNullInt64: + return IntegerFieldType + case typeOfNullFloat64: + return FloatFieldType + case typeOfNullBool: + return BooleanFieldType + case typeOfNullString: + return StringFieldType + case typeOfNullTimestamp: + return TimestampFieldType + case typeOfNullDate: + return DateFieldType + case typeOfNullTime: + return TimeFieldType + case typeOfNullDateTime: + return DateTimeFieldType + default: + return "" + } +} diff --git a/vendor/cloud.google.com/go/bigquery/nulls_test.go b/vendor/cloud.google.com/go/bigquery/nulls_test.go new file mode 100644 index 0000000000..144e8e0e00 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/nulls_test.go @@ -0,0 +1,73 @@ +// Copyright 2015 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 bigquery + +import ( + "encoding/json" + "reflect" + "testing" + + "cloud.google.com/go/civil" + "cloud.google.com/go/internal/testutil" +) + +var ( + nullsTestTime = civil.Time{Hour: 7, Minute: 50, Second: 22, Nanosecond: 1000} + nullsTestDateTime = civil.DateTime{Date: civil.Date{Year: 2016, Month: 11, Day: 5}, Time: nullsTestTime} +) + +func TestNullsJSON(t *testing.T) { + for _, test := range []struct { + in interface{} + want string + }{ + {&NullInt64{Valid: true, Int64: 3}, `3`}, + {&NullFloat64{Valid: true, Float64: 3.14}, `3.14`}, + {&NullBool{Valid: true, Bool: true}, `true`}, + {&NullString{Valid: true, StringVal: "foo"}, `"foo"`}, + {&NullTimestamp{Valid: true, Timestamp: testTimestamp}, `"2016-11-05T07:50:22.000000008Z"`}, + {&NullDate{Valid: true, Date: testDate}, `"2016-11-05"`}, + {&NullTime{Valid: true, Time: nullsTestTime}, `"07:50:22.000001"`}, + {&NullDateTime{Valid: true, DateTime: nullsTestDateTime}, `"2016-11-05 07:50:22.000001"`}, + + {&NullInt64{}, `null`}, + {&NullFloat64{}, `null`}, + {&NullBool{}, `null`}, + {&NullString{}, `null`}, + {&NullTimestamp{}, `null`}, + {&NullDate{}, `null`}, + {&NullTime{}, `null`}, + {&NullDateTime{}, `null`}, + } { + bytes, err := json.Marshal(test.in) + if err != nil { + t.Fatal(err) + } + if got, want := string(bytes), test.want; got != want { + t.Errorf("%#v: got %s, want %s", test.in, got, want) + } + + typ := reflect.Indirect(reflect.ValueOf(test.in)).Type() + value := reflect.New(typ).Interface() + err = json.Unmarshal(bytes, value) + if err != nil { + t.Fatal(err) + } + + if !testutil.Equal(value, test.in) { + t.Errorf("%#v: got %#v, want %#v", test.in, value, test.in) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/oc_test.go b/vendor/cloud.google.com/go/bigquery/oc_test.go new file mode 100644 index 0000000000..b6523396af --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/oc_test.go @@ -0,0 +1,40 @@ +// 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. + +// +build go1.8 + +package bigquery + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" +) + +func TestOCTracing(t *testing.T) { + ctx := context.Background() + client := getClient(t) + defer client.Close() + + te := testutil.NewTestExporter() + defer te.Unregister() + + q := client.Query("select *") + q.Run(ctx) // Doesn't matter if we get an error; span should be created either way + + if len(te.Spans) == 0 { + t.Fatalf("Expected some spans to be created, but got %d", 0) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/params.go b/vendor/cloud.google.com/go/bigquery/params.go new file mode 100644 index 0000000000..a2383cc4d2 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/params.go @@ -0,0 +1,357 @@ +// 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 bigquery + +import ( + "encoding/base64" + "errors" + "fmt" + "math/big" + "reflect" + "regexp" + "time" + + "cloud.google.com/go/civil" + "cloud.google.com/go/internal/fields" + + bq "google.golang.org/api/bigquery/v2" +) + +var ( + // See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#timestamp-type. + timestampFormat = "2006-01-02 15:04:05.999999-07:00" + + // See https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#schema.fields.name + validFieldName = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]{0,127}$") +) + +const nullableTagOption = "nullable" + +func bqTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) { + name, keep, opts, err := fields.ParseStandardTag("bigquery", t) + if err != nil { + return "", false, nil, err + } + if name != "" && !validFieldName.MatchString(name) { + return "", false, nil, errInvalidFieldName + } + for _, opt := range opts { + if opt != nullableTagOption { + return "", false, nil, fmt.Errorf( + "bigquery: invalid tag option %q. The only valid option is %q", + opt, nullableTagOption) + } + } + return name, keep, opts, nil +} + +var fieldCache = fields.NewCache(bqTagParser, nil, nil) + +var ( + int64ParamType = &bq.QueryParameterType{Type: "INT64"} + float64ParamType = &bq.QueryParameterType{Type: "FLOAT64"} + boolParamType = &bq.QueryParameterType{Type: "BOOL"} + stringParamType = &bq.QueryParameterType{Type: "STRING"} + bytesParamType = &bq.QueryParameterType{Type: "BYTES"} + dateParamType = &bq.QueryParameterType{Type: "DATE"} + timeParamType = &bq.QueryParameterType{Type: "TIME"} + dateTimeParamType = &bq.QueryParameterType{Type: "DATETIME"} + timestampParamType = &bq.QueryParameterType{Type: "TIMESTAMP"} + numericParamType = &bq.QueryParameterType{Type: "NUMERIC"} +) + +var ( + typeOfDate = reflect.TypeOf(civil.Date{}) + typeOfTime = reflect.TypeOf(civil.Time{}) + typeOfDateTime = reflect.TypeOf(civil.DateTime{}) + typeOfGoTime = reflect.TypeOf(time.Time{}) + typeOfRat = reflect.TypeOf(&big.Rat{}) +) + +// A QueryParameter is a parameter to a query. +type QueryParameter struct { + // Name is used for named parameter mode. + // It must match the name in the query case-insensitively. + Name string + + // Value is the value of the parameter. + // + // When you create a QueryParameter to send to BigQuery, the following Go types + // are supported, with their corresponding Bigquery types: + // int, int8, int16, int32, int64, uint8, uint16, uint32: INT64 + // Note that uint, uint64 and uintptr are not supported, because + // they may contain values that cannot fit into a 64-bit signed integer. + // float32, float64: FLOAT64 + // bool: BOOL + // string: STRING + // []byte: BYTES + // time.Time: TIMESTAMP + // *big.Rat: NUMERIC + // Arrays and slices of the above. + // Structs of the above. Only the exported fields are used. + // + // When a QueryParameter is returned inside a QueryConfig from a call to + // Job.Config: + // Integers are of type int64. + // Floating-point values are of type float64. + // Arrays are of type []interface{}, regardless of the array element type. + // Structs are of type map[string]interface{}. + Value interface{} +} + +func (p QueryParameter) toBQ() (*bq.QueryParameter, error) { + pv, err := paramValue(reflect.ValueOf(p.Value)) + if err != nil { + return nil, err + } + pt, err := paramType(reflect.TypeOf(p.Value)) + if err != nil { + return nil, err + } + return &bq.QueryParameter{ + Name: p.Name, + ParameterValue: &pv, + ParameterType: pt, + }, nil +} + +func paramType(t reflect.Type) (*bq.QueryParameterType, error) { + if t == nil { + return nil, errors.New("bigquery: nil parameter") + } + switch t { + case typeOfDate: + return dateParamType, nil + case typeOfTime: + return timeParamType, nil + case typeOfDateTime: + return dateTimeParamType, nil + case typeOfGoTime: + return timestampParamType, nil + case typeOfRat: + return numericParamType, nil + } + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32: + return int64ParamType, nil + + case reflect.Float32, reflect.Float64: + return float64ParamType, nil + + case reflect.Bool: + return boolParamType, nil + + case reflect.String: + return stringParamType, nil + + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + return bytesParamType, nil + } + fallthrough + + case reflect.Array: + et, err := paramType(t.Elem()) + if err != nil { + return nil, err + } + return &bq.QueryParameterType{Type: "ARRAY", ArrayType: et}, nil + + case reflect.Ptr: + if t.Elem().Kind() != reflect.Struct { + break + } + t = t.Elem() + fallthrough + + case reflect.Struct: + var fts []*bq.QueryParameterTypeStructTypes + fields, err := fieldCache.Fields(t) + if err != nil { + return nil, err + } + for _, f := range fields { + pt, err := paramType(f.Type) + if err != nil { + return nil, err + } + fts = append(fts, &bq.QueryParameterTypeStructTypes{ + Name: f.Name, + Type: pt, + }) + } + return &bq.QueryParameterType{Type: "STRUCT", StructTypes: fts}, nil + } + return nil, fmt.Errorf("bigquery: Go type %s cannot be represented as a parameter type", t) +} + +func paramValue(v reflect.Value) (bq.QueryParameterValue, error) { + var res bq.QueryParameterValue + if !v.IsValid() { + return res, errors.New("bigquery: nil parameter") + } + t := v.Type() + switch t { + case typeOfDate: + res.Value = v.Interface().(civil.Date).String() + return res, nil + + case typeOfTime: + // civil.Time has nanosecond resolution, but BigQuery TIME only microsecond. + // (If we send nanoseconds, then when we try to read the result we get "query job + // missing destination table"). + res.Value = CivilTimeString(v.Interface().(civil.Time)) + return res, nil + + case typeOfDateTime: + res.Value = CivilDateTimeString(v.Interface().(civil.DateTime)) + return res, nil + + case typeOfGoTime: + res.Value = v.Interface().(time.Time).Format(timestampFormat) + return res, nil + + case typeOfRat: + res.Value = NumericString(v.Interface().(*big.Rat)) + return res, nil + } + switch t.Kind() { + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + res.Value = base64.StdEncoding.EncodeToString(v.Interface().([]byte)) + return res, nil + } + fallthrough + + case reflect.Array: + var vals []*bq.QueryParameterValue + for i := 0; i < v.Len(); i++ { + val, err := paramValue(v.Index(i)) + if err != nil { + return bq.QueryParameterValue{}, err + } + vals = append(vals, &val) + } + return bq.QueryParameterValue{ArrayValues: vals}, nil + + case reflect.Ptr: + if t.Elem().Kind() != reflect.Struct { + return res, fmt.Errorf("bigquery: Go type %s cannot be represented as a parameter value", t) + } + t = t.Elem() + v = v.Elem() + if !v.IsValid() { + // nil pointer becomes empty value + return res, nil + } + fallthrough + + case reflect.Struct: + fields, err := fieldCache.Fields(t) + if err != nil { + return bq.QueryParameterValue{}, err + } + res.StructValues = map[string]bq.QueryParameterValue{} + for _, f := range fields { + fv := v.FieldByIndex(f.Index) + fp, err := paramValue(fv) + if err != nil { + return bq.QueryParameterValue{}, err + } + res.StructValues[f.Name] = fp + } + return res, nil + } + // None of the above: assume a scalar type. (If it's not a valid type, + // paramType will catch the error.) + res.Value = fmt.Sprint(v.Interface()) + return res, nil +} + +func bqToQueryParameter(q *bq.QueryParameter) (QueryParameter, error) { + p := QueryParameter{Name: q.Name} + val, err := convertParamValue(q.ParameterValue, q.ParameterType) + if err != nil { + return QueryParameter{}, err + } + p.Value = val + return p, nil +} + +var paramTypeToFieldType = map[string]FieldType{ + int64ParamType.Type: IntegerFieldType, + float64ParamType.Type: FloatFieldType, + boolParamType.Type: BooleanFieldType, + stringParamType.Type: StringFieldType, + bytesParamType.Type: BytesFieldType, + dateParamType.Type: DateFieldType, + timeParamType.Type: TimeFieldType, + numericParamType.Type: NumericFieldType, +} + +// Convert a parameter value from the service to a Go value. This is similar to, but +// not quite the same as, converting data values. +func convertParamValue(qval *bq.QueryParameterValue, qtype *bq.QueryParameterType) (interface{}, error) { + switch qtype.Type { + case "ARRAY": + if qval == nil { + return []interface{}(nil), nil + } + return convertParamArray(qval.ArrayValues, qtype.ArrayType) + case "STRUCT": + if qval == nil { + return map[string]interface{}(nil), nil + } + return convertParamStruct(qval.StructValues, qtype.StructTypes) + case "TIMESTAMP": + return time.Parse(timestampFormat, qval.Value) + case "DATETIME": + return parseCivilDateTime(qval.Value) + default: + return convertBasicType(qval.Value, paramTypeToFieldType[qtype.Type]) + } +} + +// convertParamArray converts a query parameter array value to a Go value. It +// always returns a []interface{}. +func convertParamArray(elVals []*bq.QueryParameterValue, elType *bq.QueryParameterType) ([]interface{}, error) { + var vals []interface{} + for _, el := range elVals { + val, err := convertParamValue(el, elType) + if err != nil { + return nil, err + } + vals = append(vals, val) + } + return vals, nil +} + +// convertParamStruct converts a query parameter struct value into a Go value. It +// always returns a map[string]interface{}. +func convertParamStruct(sVals map[string]bq.QueryParameterValue, sTypes []*bq.QueryParameterTypeStructTypes) (map[string]interface{}, error) { + vals := map[string]interface{}{} + for _, st := range sTypes { + if sv, ok := sVals[st.Name]; ok { + val, err := convertParamValue(&sv, st.Type) + if err != nil { + return nil, err + } + vals[st.Name] = val + } else { + vals[st.Name] = nil + } + } + return vals, nil +} diff --git a/vendor/cloud.google.com/go/bigquery/params_test.go b/vendor/cloud.google.com/go/bigquery/params_test.go new file mode 100644 index 0000000000..5e5ea9a26f --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/params_test.go @@ -0,0 +1,363 @@ +// 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 bigquery + +import ( + "errors" + "math" + "math/big" + "reflect" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/civil" + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" +) + +var scalarTests = []struct { + val interface{} // The Go value + wantVal string // paramValue's desired output + wantType *bq.QueryParameterType // paramType's desired output +}{ + {int64(0), "0", int64ParamType}, + {3.14, "3.14", float64ParamType}, + {3.14159e-87, "3.14159e-87", float64ParamType}, + {true, "true", boolParamType}, + {"string", "string", stringParamType}, + {"\u65e5\u672c\u8a9e\n", "\u65e5\u672c\u8a9e\n", stringParamType}, + {math.NaN(), "NaN", float64ParamType}, + {[]byte("foo"), "Zm9v", bytesParamType}, // base64 encoding of "foo" + {time.Date(2016, 3, 20, 4, 22, 9, 5000, time.FixedZone("neg1-2", -3720)), + "2016-03-20 04:22:09.000005-01:02", + timestampParamType}, + {civil.Date{Year: 2016, Month: 3, Day: 20}, "2016-03-20", dateParamType}, + {civil.Time{Hour: 4, Minute: 5, Second: 6, Nanosecond: 789000000}, "04:05:06.789000", timeParamType}, + {civil.DateTime{Date: civil.Date{Year: 2016, Month: 3, Day: 20}, Time: civil.Time{Hour: 4, Minute: 5, Second: 6, Nanosecond: 789000000}}, + "2016-03-20 04:05:06.789000", + dateTimeParamType}, + {big.NewRat(12345, 1000), "12.345000000", numericParamType}, +} + +type ( + S1 struct { + A int + B *S2 + C bool + } + S2 struct { + D string + e int + } +) + +var ( + s1 = S1{ + A: 1, + B: &S2{D: "s"}, + C: true, + } + + s1ParamType = &bq.QueryParameterType{ + Type: "STRUCT", + StructTypes: []*bq.QueryParameterTypeStructTypes{ + {Name: "A", Type: int64ParamType}, + {Name: "B", Type: &bq.QueryParameterType{ + Type: "STRUCT", + StructTypes: []*bq.QueryParameterTypeStructTypes{ + {Name: "D", Type: stringParamType}, + }, + }}, + {Name: "C", Type: boolParamType}, + }, + } + + s1ParamValue = bq.QueryParameterValue{ + StructValues: map[string]bq.QueryParameterValue{ + "A": sval("1"), + "B": { + StructValues: map[string]bq.QueryParameterValue{ + "D": sval("s"), + }, + }, + "C": sval("true"), + }, + } + + s1ParamReturnValue = map[string]interface{}{ + "A": int64(1), + "B": map[string]interface{}{"D": "s"}, + "C": true, + } +) + +func sval(s string) bq.QueryParameterValue { + return bq.QueryParameterValue{Value: s} +} + +func TestParamValueScalar(t *testing.T) { + for _, test := range scalarTests { + got, err := paramValue(reflect.ValueOf(test.val)) + if err != nil { + t.Errorf("%v: got %v, want nil", test.val, err) + continue + } + want := sval(test.wantVal) + if !testutil.Equal(got, want) { + t.Errorf("%v:\ngot %+v\nwant %+v", test.val, got, want) + } + } +} + +func TestParamValueArray(t *testing.T) { + qpv := bq.QueryParameterValue{ArrayValues: []*bq.QueryParameterValue{ + {Value: "1"}, + {Value: "2"}, + }, + } + for _, test := range []struct { + val interface{} + want bq.QueryParameterValue + }{ + {[]int(nil), bq.QueryParameterValue{}}, + {[]int{}, bq.QueryParameterValue{}}, + {[]int{1, 2}, qpv}, + {[2]int{1, 2}, qpv}, + } { + got, err := paramValue(reflect.ValueOf(test.val)) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, test.want) { + t.Errorf("%#v:\ngot %+v\nwant %+v", test.val, got, test.want) + } + } +} + +func TestParamValueStruct(t *testing.T) { + got, err := paramValue(reflect.ValueOf(s1)) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, s1ParamValue) { + t.Errorf("got %+v\nwant %+v", got, s1ParamValue) + } +} + +func TestParamValueErrors(t *testing.T) { + // paramValue lets a few invalid types through, but paramType catches them. + // Since we never call one without the other that's fine. + for _, val := range []interface{}{nil, new([]int)} { + _, err := paramValue(reflect.ValueOf(val)) + if err == nil { + t.Errorf("%v (%T): got nil, want error", val, val) + } + } +} + +func TestParamType(t *testing.T) { + for _, test := range scalarTests { + got, err := paramType(reflect.TypeOf(test.val)) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, test.wantType) { + t.Errorf("%v (%T): got %v, want %v", test.val, test.val, got, test.wantType) + } + } + for _, test := range []struct { + val interface{} + want *bq.QueryParameterType + }{ + {uint32(32767), int64ParamType}, + {[]byte("foo"), bytesParamType}, + {[]int{}, &bq.QueryParameterType{Type: "ARRAY", ArrayType: int64ParamType}}, + {[3]bool{}, &bq.QueryParameterType{Type: "ARRAY", ArrayType: boolParamType}}, + {S1{}, s1ParamType}, + } { + got, err := paramType(reflect.TypeOf(test.val)) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, test.want) { + t.Errorf("%v (%T): got %v, want %v", test.val, test.val, got, test.want) + } + } +} + +func TestParamTypeErrors(t *testing.T) { + for _, val := range []interface{}{ + nil, uint(0), new([]int), make(chan int), + } { + _, err := paramType(reflect.TypeOf(val)) + if err == nil { + t.Errorf("%v (%T): got nil, want error", val, val) + } + } +} + +func TestConvertParamValue(t *testing.T) { + // Scalars. + for _, test := range scalarTests { + pval, err := paramValue(reflect.ValueOf(test.val)) + if err != nil { + t.Fatal(err) + } + ptype, err := paramType(reflect.TypeOf(test.val)) + if err != nil { + t.Fatal(err) + } + got, err := convertParamValue(&pval, ptype) + if err != nil { + t.Fatalf("convertParamValue(%+v, %+v): %v", pval, ptype, err) + } + if !testutil.Equal(got, test.val) { + t.Errorf("%#v: got %#v", test.val, got) + } + } + // Arrays. + for _, test := range []struct { + pval *bq.QueryParameterValue + want []interface{} + }{ + { + &bq.QueryParameterValue{}, + nil, + }, + { + &bq.QueryParameterValue{ + ArrayValues: []*bq.QueryParameterValue{{Value: "1"}, {Value: "2"}}, + }, + []interface{}{int64(1), int64(2)}, + }, + } { + ptype := &bq.QueryParameterType{Type: "ARRAY", ArrayType: int64ParamType} + got, err := convertParamValue(test.pval, ptype) + if err != nil { + t.Fatalf("%+v: %v", test.pval, err) + } + if !testutil.Equal(got, test.want) { + t.Errorf("%+v: got %+v, want %+v", test.pval, got, test.want) + } + } + // Structs. + got, err := convertParamValue(&s1ParamValue, s1ParamType) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, s1ParamReturnValue) { + t.Errorf("got %+v, want %+v", got, s1ParamReturnValue) + } +} + +func TestIntegration_ScalarParam(t *testing.T) { + roundToMicros := cmp.Transformer("RoundToMicros", + func(t time.Time) time.Time { return t.Round(time.Microsecond) }) + c := getClient(t) + for _, test := range scalarTests { + gotData, gotParam, err := paramRoundTrip(c, test.val) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(gotData, test.val, roundToMicros) { + t.Errorf("\ngot %#v (%T)\nwant %#v (%T)", gotData, gotData, test.val, test.val) + } + if !testutil.Equal(gotParam, test.val, roundToMicros) { + t.Errorf("\ngot %#v (%T)\nwant %#v (%T)", gotParam, gotParam, test.val, test.val) + } + } +} + +func TestIntegration_OtherParam(t *testing.T) { + c := getClient(t) + for _, test := range []struct { + val interface{} + wantData interface{} + wantParam interface{} + }{ + {[]int(nil), []Value(nil), []interface{}(nil)}, + {[]int{}, []Value(nil), []interface{}(nil)}, + { + []int{1, 2}, + []Value{int64(1), int64(2)}, + []interface{}{int64(1), int64(2)}, + }, + { + [3]int{1, 2, 3}, + []Value{int64(1), int64(2), int64(3)}, + []interface{}{int64(1), int64(2), int64(3)}, + }, + { + S1{}, + []Value{int64(0), nil, false}, + map[string]interface{}{ + "A": int64(0), + "B": nil, + "C": false, + }, + }, + { + s1, + []Value{int64(1), []Value{"s"}, true}, + s1ParamReturnValue, + }, + } { + gotData, gotParam, err := paramRoundTrip(c, test.val) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(gotData, test.wantData) { + t.Errorf("%#v:\ngot %#v (%T)\nwant %#v (%T)", + test.val, gotData, gotData, test.wantData, test.wantData) + } + if !testutil.Equal(gotParam, test.wantParam) { + t.Errorf("%#v:\ngot %#v (%T)\nwant %#v (%T)", + test.val, gotParam, gotParam, test.wantParam, test.wantParam) + } + } +} + +// paramRoundTrip passes x as a query parameter to BigQuery. It returns +// the resulting data value from running the query and the parameter value from +// the returned job configuration. +func paramRoundTrip(c *Client, x interface{}) (data Value, param interface{}, err error) { + ctx := context.Background() + q := c.Query("select ?") + q.Parameters = []QueryParameter{{Value: x}} + job, err := q.Run(ctx) + if err != nil { + return nil, nil, err + } + it, err := job.Read(ctx) + if err != nil { + return nil, nil, err + } + var val []Value + err = it.Next(&val) + if err != nil { + return nil, nil, err + } + if len(val) != 1 { + return nil, nil, errors.New("wrong number of values") + } + conf, err := job.Config() + if err != nil { + return nil, nil, err + } + return val[0], conf.(*QueryConfig).Parameters[0].Value, nil +} diff --git a/vendor/cloud.google.com/go/bigquery/query.go b/vendor/cloud.google.com/go/bigquery/query.go new file mode 100644 index 0000000000..a95daca82d --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/query.go @@ -0,0 +1,307 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" +) + +// QueryConfig holds the configuration for a query job. +type QueryConfig struct { + // Dst is the table into which the results of the query will be written. + // If this field is nil, a temporary table will be created. + Dst *Table + + // The query to execute. See https://cloud.google.com/bigquery/query-reference for details. + Q string + + // DefaultProjectID and DefaultDatasetID specify the dataset to use for unqualified table names in the query. + // If DefaultProjectID is set, DefaultDatasetID must also be set. + DefaultProjectID string + DefaultDatasetID string + + // TableDefinitions describes data sources outside of BigQuery. + // The map keys may be used as table names in the query string. + // + // When a QueryConfig is returned from Job.Config, the map values + // are always of type *ExternalDataConfig. + TableDefinitions map[string]ExternalData + + // CreateDisposition specifies the circumstances under which the destination table will be created. + // The default is CreateIfNeeded. + CreateDisposition TableCreateDisposition + + // WriteDisposition specifies how existing data in the destination table is treated. + // The default is WriteEmpty. + WriteDisposition TableWriteDisposition + + // DisableQueryCache prevents results being fetched from the query cache. + // If this field is false, results are fetched from the cache if they are available. + // The query cache is a best-effort cache that is flushed whenever tables in the query are modified. + // Cached results are only available when TableID is unspecified in the query's destination Table. + // For more information, see https://cloud.google.com/bigquery/querying-data#querycaching + DisableQueryCache bool + + // DisableFlattenedResults prevents results being flattened. + // If this field is false, results from nested and repeated fields are flattened. + // DisableFlattenedResults implies AllowLargeResults + // For more information, see https://cloud.google.com/bigquery/docs/data#nested + DisableFlattenedResults bool + + // AllowLargeResults allows the query to produce arbitrarily large result tables. + // The destination must be a table. + // When using this option, queries will take longer to execute, even if the result set is small. + // For additional limitations, see https://cloud.google.com/bigquery/querying-data#largequeryresults + AllowLargeResults bool + + // Priority specifies the priority with which to schedule the query. + // The default priority is InteractivePriority. + // For more information, see https://cloud.google.com/bigquery/querying-data#batchqueries + Priority QueryPriority + + // MaxBillingTier sets the maximum billing tier for a Query. + // Queries that have resource usage beyond this tier will fail (without + // incurring a charge). If this field is zero, the project default will be used. + MaxBillingTier int + + // MaxBytesBilled limits the number of bytes billed for + // this job. Queries that would exceed this limit will fail (without incurring + // a charge). + // If this field is less than 1, the project default will be + // used. + MaxBytesBilled int64 + + // UseStandardSQL causes the query to use standard SQL. The default. + // Deprecated: use UseLegacySQL. + UseStandardSQL bool + + // UseLegacySQL causes the query to use legacy SQL. + UseLegacySQL bool + + // Parameters is a list of query parameters. The presence of parameters + // implies the use of standard SQL. + // If the query uses positional syntax ("?"), then no parameter may have a name. + // If the query uses named syntax ("@p"), then all parameters must have names. + // It is illegal to mix positional and named syntax. + Parameters []QueryParameter + + // TimePartitioning specifies time-based partitioning + // for the destination table. + TimePartitioning *TimePartitioning + + // The labels associated with this job. + Labels map[string]string + + // If true, don't actually run this job. A valid query will return a mostly + // empty response with some processing statistics, while an invalid query will + // return the same error it would if it wasn't a dry run. + // + // Query.Read will fail with dry-run queries. Call Query.Run instead, and then + // call LastStatus on the returned job to get statistics. Calling Status on a + // dry-run job will fail. + DryRun bool + + // Custom encryption configuration (e.g., Cloud KMS keys). + DestinationEncryptionConfig *EncryptionConfig + + // Allows the schema of the destination table to be updated as a side effect of + // the query job. + SchemaUpdateOptions []string +} + +func (qc *QueryConfig) toBQ() (*bq.JobConfiguration, error) { + qconf := &bq.JobConfigurationQuery{ + Query: qc.Q, + CreateDisposition: string(qc.CreateDisposition), + WriteDisposition: string(qc.WriteDisposition), + AllowLargeResults: qc.AllowLargeResults, + Priority: string(qc.Priority), + MaximumBytesBilled: qc.MaxBytesBilled, + TimePartitioning: qc.TimePartitioning.toBQ(), + DestinationEncryptionConfiguration: qc.DestinationEncryptionConfig.toBQ(), + SchemaUpdateOptions: qc.SchemaUpdateOptions, + } + if len(qc.TableDefinitions) > 0 { + qconf.TableDefinitions = make(map[string]bq.ExternalDataConfiguration) + } + for name, data := range qc.TableDefinitions { + qconf.TableDefinitions[name] = data.toBQ() + } + if qc.DefaultProjectID != "" || qc.DefaultDatasetID != "" { + qconf.DefaultDataset = &bq.DatasetReference{ + DatasetId: qc.DefaultDatasetID, + ProjectId: qc.DefaultProjectID, + } + } + if tier := int64(qc.MaxBillingTier); tier > 0 { + qconf.MaximumBillingTier = &tier + } + f := false + if qc.DisableQueryCache { + qconf.UseQueryCache = &f + } + if qc.DisableFlattenedResults { + qconf.FlattenResults = &f + // DisableFlattenResults implies AllowLargeResults. + qconf.AllowLargeResults = true + } + if qc.UseStandardSQL && qc.UseLegacySQL { + return nil, errors.New("bigquery: cannot provide both UseStandardSQL and UseLegacySQL") + } + if len(qc.Parameters) > 0 && qc.UseLegacySQL { + return nil, errors.New("bigquery: cannot provide both Parameters (implying standard SQL) and UseLegacySQL") + } + ptrue := true + pfalse := false + if qc.UseLegacySQL { + qconf.UseLegacySql = &ptrue + } else { + qconf.UseLegacySql = &pfalse + } + if qc.Dst != nil && !qc.Dst.implicitTable() { + qconf.DestinationTable = qc.Dst.toBQ() + } + for _, p := range qc.Parameters { + qp, err := p.toBQ() + if err != nil { + return nil, err + } + qconf.QueryParameters = append(qconf.QueryParameters, qp) + } + return &bq.JobConfiguration{ + Labels: qc.Labels, + DryRun: qc.DryRun, + Query: qconf, + }, nil +} + +func bqToQueryConfig(q *bq.JobConfiguration, c *Client) (*QueryConfig, error) { + qq := q.Query + qc := &QueryConfig{ + Labels: q.Labels, + DryRun: q.DryRun, + Q: qq.Query, + CreateDisposition: TableCreateDisposition(qq.CreateDisposition), + WriteDisposition: TableWriteDisposition(qq.WriteDisposition), + AllowLargeResults: qq.AllowLargeResults, + Priority: QueryPriority(qq.Priority), + MaxBytesBilled: qq.MaximumBytesBilled, + UseLegacySQL: qq.UseLegacySql == nil || *qq.UseLegacySql, + TimePartitioning: bqToTimePartitioning(qq.TimePartitioning), + DestinationEncryptionConfig: bqToEncryptionConfig(qq.DestinationEncryptionConfiguration), + SchemaUpdateOptions: qq.SchemaUpdateOptions, + } + qc.UseStandardSQL = !qc.UseLegacySQL + + if len(qq.TableDefinitions) > 0 { + qc.TableDefinitions = make(map[string]ExternalData) + } + for name, qedc := range qq.TableDefinitions { + edc, err := bqToExternalDataConfig(&qedc) + if err != nil { + return nil, err + } + qc.TableDefinitions[name] = edc + } + if qq.DefaultDataset != nil { + qc.DefaultProjectID = qq.DefaultDataset.ProjectId + qc.DefaultDatasetID = qq.DefaultDataset.DatasetId + } + if qq.MaximumBillingTier != nil { + qc.MaxBillingTier = int(*qq.MaximumBillingTier) + } + if qq.UseQueryCache != nil && !*qq.UseQueryCache { + qc.DisableQueryCache = true + } + if qq.FlattenResults != nil && !*qq.FlattenResults { + qc.DisableFlattenedResults = true + } + if qq.DestinationTable != nil { + qc.Dst = bqToTable(qq.DestinationTable, c) + } + for _, qp := range qq.QueryParameters { + p, err := bqToQueryParameter(qp) + if err != nil { + return nil, err + } + qc.Parameters = append(qc.Parameters, p) + } + return qc, nil +} + +// QueryPriority specifies a priority with which a query is to be executed. +type QueryPriority string + +const ( + BatchPriority QueryPriority = "BATCH" + InteractivePriority QueryPriority = "INTERACTIVE" +) + +// A Query queries data from a BigQuery table. Use Client.Query to create a Query. +type Query struct { + JobIDConfig + QueryConfig + client *Client +} + +// Query creates a query with string q. +// The returned Query may optionally be further configured before its Run method is called. +func (c *Client) Query(q string) *Query { + return &Query{ + client: c, + QueryConfig: QueryConfig{Q: q}, + } +} + +// Run initiates a query job. +func (q *Query) Run(ctx context.Context) (j *Job, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Query.Run") + defer func() { trace.EndSpan(ctx, err) }() + + job, err := q.newJob() + if err != nil { + return nil, err + } + j, err = q.client.insertJob(ctx, job, nil) + if err != nil { + return nil, err + } + return j, nil +} + +func (q *Query) newJob() (*bq.Job, error) { + config, err := q.QueryConfig.toBQ() + if err != nil { + return nil, err + } + return &bq.Job{ + JobReference: q.JobIDConfig.createJobRef(q.client), + Configuration: config, + }, nil +} + +// Read submits a query for execution and returns the results via a RowIterator. +// It is a shorthand for Query.Run followed by Job.Read. +func (q *Query) Read(ctx context.Context) (*RowIterator, error) { + job, err := q.Run(ctx) + if err != nil { + return nil, err + } + return job.Read(ctx) +} diff --git a/vendor/cloud.google.com/go/bigquery/query_test.go b/vendor/cloud.google.com/go/bigquery/query_test.go new file mode 100644 index 0000000000..a24971d438 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/query_test.go @@ -0,0 +1,406 @@ +// Copyright 2015 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 bigquery + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/internal/testutil" + + bq "google.golang.org/api/bigquery/v2" +) + +func defaultQueryJob() *bq.Job { + pfalse := false + return &bq.Job{ + JobReference: &bq.JobReference{JobId: "RANDOM", ProjectId: "client-project-id"}, + Configuration: &bq.JobConfiguration{ + Query: &bq.JobConfigurationQuery{ + DestinationTable: &bq.TableReference{ + ProjectId: "client-project-id", + DatasetId: "dataset-id", + TableId: "table-id", + }, + Query: "query string", + DefaultDataset: &bq.DatasetReference{ + ProjectId: "def-project-id", + DatasetId: "def-dataset-id", + }, + UseLegacySql: &pfalse, + }, + }, + } +} + +var defaultQuery = &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", +} + +func TestQuery(t *testing.T) { + defer fixRandomID("RANDOM")() + c := &Client{ + projectID: "client-project-id", + } + testCases := []struct { + dst *Table + src *QueryConfig + jobIDConfig JobIDConfig + want *bq.Job + }{ + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: defaultQuery, + want: defaultQueryJob(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + Labels: map[string]string{"a": "b"}, + DryRun: true, + }, + want: func() *bq.Job { + j := defaultQueryJob() + j.Configuration.Labels = map[string]string{"a": "b"} + j.Configuration.DryRun = true + j.Configuration.Query.DefaultDataset = nil + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + jobIDConfig: JobIDConfig{JobID: "jobID", AddJobIDSuffix: true}, + src: &QueryConfig{Q: "query string"}, + want: func() *bq.Job { + j := defaultQueryJob() + j.Configuration.Query.DefaultDataset = nil + j.JobReference.JobId = "jobID-RANDOM" + return j + }(), + }, + { + dst: &Table{}, + src: defaultQuery, + want: func() *bq.Job { + j := defaultQueryJob() + j.Configuration.Query.DestinationTable = nil + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + TableDefinitions: map[string]ExternalData{ + "atable": func() *GCSReference { + g := NewGCSReference("uri") + g.AllowJaggedRows = true + g.AllowQuotedNewlines = true + g.Compression = Gzip + g.Encoding = UTF_8 + g.FieldDelimiter = ";" + g.IgnoreUnknownValues = true + g.MaxBadRecords = 1 + g.Quote = "'" + g.SkipLeadingRows = 2 + g.Schema = Schema{{Name: "name", Type: StringFieldType}} + return g + }(), + }, + }, + want: func() *bq.Job { + j := defaultQueryJob() + j.Configuration.Query.DefaultDataset = nil + td := make(map[string]bq.ExternalDataConfiguration) + quote := "'" + td["atable"] = bq.ExternalDataConfiguration{ + Compression: "GZIP", + IgnoreUnknownValues: true, + MaxBadRecords: 1, + SourceFormat: "CSV", // must be explicitly set. + SourceUris: []string{"uri"}, + CsvOptions: &bq.CsvOptions{ + AllowJaggedRows: true, + AllowQuotedNewlines: true, + Encoding: "UTF-8", + FieldDelimiter: ";", + SkipLeadingRows: 2, + Quote: "e, + }, + Schema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + {Name: "name", Type: "STRING"}, + }, + }, + } + j.Configuration.Query.TableDefinitions = td + return j + }(), + }, + { + dst: &Table{ + ProjectID: "project-id", + DatasetID: "dataset-id", + TableID: "table-id", + }, + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + CreateDisposition: CreateNever, + WriteDisposition: WriteTruncate, + }, + want: func() *bq.Job { + j := defaultQueryJob() + j.Configuration.Query.DestinationTable.ProjectId = "project-id" + j.Configuration.Query.WriteDisposition = "WRITE_TRUNCATE" + j.Configuration.Query.CreateDisposition = "CREATE_NEVER" + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + DisableQueryCache: true, + }, + want: func() *bq.Job { + j := defaultQueryJob() + f := false + j.Configuration.Query.UseQueryCache = &f + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + AllowLargeResults: true, + }, + want: func() *bq.Job { + j := defaultQueryJob() + j.Configuration.Query.AllowLargeResults = true + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + DisableFlattenedResults: true, + }, + want: func() *bq.Job { + j := defaultQueryJob() + f := false + j.Configuration.Query.FlattenResults = &f + j.Configuration.Query.AllowLargeResults = true + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + Priority: QueryPriority("low"), + }, + want: func() *bq.Job { + j := defaultQueryJob() + j.Configuration.Query.Priority = "low" + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + MaxBillingTier: 3, + MaxBytesBilled: 5, + }, + want: func() *bq.Job { + j := defaultQueryJob() + tier := int64(3) + j.Configuration.Query.MaximumBillingTier = &tier + j.Configuration.Query.MaximumBytesBilled = 5 + return j + }(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + UseStandardSQL: true, + }, + want: defaultQueryJob(), + }, + { + dst: c.Dataset("dataset-id").Table("table-id"), + src: &QueryConfig{ + Q: "query string", + DefaultProjectID: "def-project-id", + DefaultDatasetID: "def-dataset-id", + UseLegacySQL: true, + }, + want: func() *bq.Job { + j := defaultQueryJob() + ptrue := true + j.Configuration.Query.UseLegacySql = &ptrue + j.Configuration.Query.ForceSendFields = nil + return j + }(), + }, + } + for i, tc := range testCases { + query := c.Query("") + query.JobIDConfig = tc.jobIDConfig + query.QueryConfig = *tc.src + query.Dst = tc.dst + got, err := query.newJob() + if err != nil { + t.Errorf("#%d: err calling query: %v", i, err) + continue + } + checkJob(t, i, got, tc.want) + + // Round-trip. + jc, err := bqToJobConfig(got.Configuration, c) + if err != nil { + t.Fatalf("#%d: %v", i, err) + } + wantConfig := query.QueryConfig + // We set AllowLargeResults to true when DisableFlattenedResults is true. + if wantConfig.DisableFlattenedResults { + wantConfig.AllowLargeResults = true + } + // A QueryConfig with neither UseXXXSQL field set is equivalent + // to one where UseStandardSQL = true. + if !wantConfig.UseLegacySQL && !wantConfig.UseStandardSQL { + wantConfig.UseStandardSQL = true + } + // Treat nil and empty tables the same, and ignore the client. + tableEqual := func(t1, t2 *Table) bool { + if t1 == nil { + t1 = &Table{} + } + if t2 == nil { + t2 = &Table{} + } + return t1.ProjectID == t2.ProjectID && t1.DatasetID == t2.DatasetID && t1.TableID == t2.TableID + } + // A table definition that is a GCSReference round-trips as an ExternalDataConfig. + // TODO(jba): see if there is a way to express this with a transformer. + gcsRefToEDC := func(g *GCSReference) *ExternalDataConfig { + q := g.toBQ() + e, _ := bqToExternalDataConfig(&q) + return e + } + externalDataEqual := func(e1, e2 ExternalData) bool { + if r, ok := e1.(*GCSReference); ok { + e1 = gcsRefToEDC(r) + } + if r, ok := e2.(*GCSReference); ok { + e2 = gcsRefToEDC(r) + } + return cmp.Equal(e1, e2) + } + diff := testutil.Diff(jc.(*QueryConfig), &wantConfig, + cmp.Comparer(tableEqual), + cmp.Comparer(externalDataEqual), + ) + if diff != "" { + t.Errorf("#%d: (got=-, want=+:\n%s", i, diff) + } + } +} + +func TestConfiguringQuery(t *testing.T) { + c := &Client{ + projectID: "project-id", + } + + query := c.Query("q") + query.JobID = "ajob" + query.DefaultProjectID = "def-project-id" + query.DefaultDatasetID = "def-dataset-id" + query.TimePartitioning = &TimePartitioning{Expiration: 1234 * time.Second, Field: "f"} + query.DestinationEncryptionConfig = &EncryptionConfig{KMSKeyName: "keyName"} + query.SchemaUpdateOptions = []string{"ALLOW_FIELD_ADDITION"} + + // Note: Other configuration fields are tested in other tests above. + // A lot of that can be consolidated once Client.Copy is gone. + + pfalse := false + want := &bq.Job{ + Configuration: &bq.JobConfiguration{ + Query: &bq.JobConfigurationQuery{ + Query: "q", + DefaultDataset: &bq.DatasetReference{ + ProjectId: "def-project-id", + DatasetId: "def-dataset-id", + }, + UseLegacySql: &pfalse, + TimePartitioning: &bq.TimePartitioning{ExpirationMs: 1234000, Field: "f", Type: "DAY"}, + DestinationEncryptionConfiguration: &bq.EncryptionConfiguration{KmsKeyName: "keyName"}, + SchemaUpdateOptions: []string{"ALLOW_FIELD_ADDITION"}, + }, + }, + JobReference: &bq.JobReference{ + JobId: "ajob", + ProjectId: "project-id", + }, + } + + got, err := query.newJob() + if err != nil { + t.Fatalf("err calling Query.newJob: %v", err) + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("querying: -got +want:\n%s", diff) + } +} + +func TestQueryLegacySQL(t *testing.T) { + c := &Client{projectID: "project-id"} + q := c.Query("q") + q.UseStandardSQL = true + q.UseLegacySQL = true + _, err := q.newJob() + if err == nil { + t.Error("UseStandardSQL and UseLegacySQL: got nil, want error") + } + q = c.Query("q") + q.Parameters = []QueryParameter{{Name: "p", Value: 3}} + q.UseLegacySQL = true + _, err = q.newJob() + if err == nil { + t.Error("Parameters and UseLegacySQL: got nil, want error") + } +} diff --git a/vendor/cloud.google.com/go/bigquery/random.go b/vendor/cloud.google.com/go/bigquery/random.go new file mode 100644 index 0000000000..65f9384341 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/random.go @@ -0,0 +1,56 @@ +// 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 bigquery + +import ( + "math/rand" + "os" + "sync" + "time" +) + +// Support for random values (typically job IDs and insert IDs). + +const alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +var ( + rngMu sync.Mutex + rng = rand.New(rand.NewSource(time.Now().UnixNano() ^ int64(os.Getpid()))) +) + +// For testing. +var randomIDFn = randomID + +// As of August 2017, the BigQuery service uses 27 alphanumeric characters for +// suffixes. +const randomIDLen = 27 + +func randomID() string { + // This is used for both job IDs and insert IDs. + var b [randomIDLen]byte + rngMu.Lock() + for i := 0; i < len(b); i++ { + b[i] = alphanum[rng.Intn(len(alphanum))] + } + rngMu.Unlock() + return string(b[:]) +} + +// Seed seeds this package's random number generator, used for generating job and +// insert IDs. Use Seed to obtain repeatable, deterministic behavior from bigquery +// clients. Seed should be called before any clients are created. +func Seed(s int64) { + rng = rand.New(rand.NewSource(s)) +} diff --git a/vendor/cloud.google.com/go/bigquery/read_test.go b/vendor/cloud.google.com/go/bigquery/read_test.go new file mode 100644 index 0000000000..fb0f6e2ea4 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/read_test.go @@ -0,0 +1,235 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "testing" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/internal/testutil" + + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" + "google.golang.org/api/iterator" +) + +type pageFetcherArgs struct { + table *Table + schema Schema + startIndex uint64 + pageSize int64 + pageToken string +} + +// pageFetcherReadStub services read requests by returning data from an in-memory list of values. +type pageFetcherReadStub struct { + // values and pageTokens are used as sources of data to return in response to calls to readTabledata or readQuery. + values [][][]Value // contains pages / rows / columns. + pageTokens map[string]string // maps incoming page token to returned page token. + + // arguments are recorded for later inspection. + calls []pageFetcherArgs +} + +func (s *pageFetcherReadStub) fetchPage(ctx context.Context, t *Table, schema Schema, startIndex uint64, pageSize int64, pageToken string) (*fetchPageResult, error) { + s.calls = append(s.calls, + pageFetcherArgs{t, schema, startIndex, pageSize, pageToken}) + result := &fetchPageResult{ + pageToken: s.pageTokens[pageToken], + rows: s.values[0], + } + s.values = s.values[1:] + return result, nil +} + +func waitForQueryStub(context.Context, string) (Schema, error) { + return nil, nil +} + +func TestRead(t *testing.T) { + // The data for the service stub to return is populated for each test case in the testCases for loop. + ctx := context.Background() + c := &Client{projectID: "project-id"} + pf := &pageFetcherReadStub{} + queryJob := &Job{ + projectID: "project-id", + jobID: "job-id", + c: c, + config: &bq.JobConfiguration{ + Query: &bq.JobConfigurationQuery{ + DestinationTable: &bq.TableReference{ + ProjectId: "project-id", + DatasetId: "dataset-id", + TableId: "table-id", + }, + }, + }, + } + + for _, readFunc := range []func() *RowIterator{ + func() *RowIterator { + return c.Dataset("dataset-id").Table("table-id").read(ctx, pf.fetchPage) + }, + func() *RowIterator { + it, err := queryJob.read(ctx, waitForQueryStub, pf.fetchPage) + if err != nil { + t.Fatal(err) + } + return it + }, + } { + testCases := []struct { + data [][][]Value + pageTokens map[string]string + want [][]Value + }{ + { + data: [][][]Value{{{1, 2}, {11, 12}}, {{30, 40}, {31, 41}}}, + pageTokens: map[string]string{"": "a", "a": ""}, + want: [][]Value{{1, 2}, {11, 12}, {30, 40}, {31, 41}}, + }, + { + data: [][][]Value{{{1, 2}, {11, 12}}, {{30, 40}, {31, 41}}}, + pageTokens: map[string]string{"": ""}, // no more pages after first one. + want: [][]Value{{1, 2}, {11, 12}}, + }, + } + for _, tc := range testCases { + pf.values = tc.data + pf.pageTokens = tc.pageTokens + if got, ok := collectValues(t, readFunc()); ok { + if !testutil.Equal(got, tc.want) { + t.Errorf("reading: got:\n%v\nwant:\n%v", got, tc.want) + } + } + } + } +} + +func collectValues(t *testing.T, it *RowIterator) ([][]Value, bool) { + var got [][]Value + for { + var vals []Value + err := it.Next(&vals) + if err == iterator.Done { + break + } + if err != nil { + t.Errorf("err calling Next: %v", err) + return nil, false + } + got = append(got, vals) + } + return got, true +} + +func TestNoMoreValues(t *testing.T) { + c := &Client{projectID: "project-id"} + pf := &pageFetcherReadStub{ + values: [][][]Value{{{1, 2}, {11, 12}}}, + } + it := c.Dataset("dataset-id").Table("table-id").read(context.Background(), pf.fetchPage) + var vals []Value + // We expect to retrieve two values and then fail on the next attempt. + if err := it.Next(&vals); err != nil { + t.Fatalf("Next: got: %v: want: nil", err) + } + if err := it.Next(&vals); err != nil { + t.Fatalf("Next: got: %v: want: nil", err) + } + if err := it.Next(&vals); err != iterator.Done { + t.Fatalf("Next: got: %v: want: iterator.Done", err) + } +} + +var errBang = errors.New("bang!") + +func errorFetchPage(context.Context, *Table, Schema, uint64, int64, string) (*fetchPageResult, error) { + return nil, errBang +} + +func TestReadError(t *testing.T) { + // test that service read errors are propagated back to the caller. + c := &Client{projectID: "project-id"} + it := c.Dataset("dataset-id").Table("table-id").read(context.Background(), errorFetchPage) + var vals []Value + if err := it.Next(&vals); err != errBang { + t.Fatalf("Get: got: %v: want: %v", err, errBang) + } +} + +func TestReadTabledataOptions(t *testing.T) { + // test that read options are propagated. + s := &pageFetcherReadStub{ + values: [][][]Value{{{1, 2}}}, + } + c := &Client{projectID: "project-id"} + tr := c.Dataset("dataset-id").Table("table-id") + it := tr.read(context.Background(), s.fetchPage) + it.PageInfo().MaxSize = 5 + var vals []Value + if err := it.Next(&vals); err != nil { + t.Fatal(err) + } + want := []pageFetcherArgs{{ + table: tr, + pageSize: 5, + pageToken: "", + }} + if diff := testutil.Diff(s.calls, want, cmp.AllowUnexported(pageFetcherArgs{}, pageFetcherReadStub{}, Table{}, Client{})); diff != "" { + t.Errorf("reading (got=-, want=+):\n%s", diff) + } +} + +func TestReadQueryOptions(t *testing.T) { + // test that read options are propagated. + c := &Client{projectID: "project-id"} + pf := &pageFetcherReadStub{ + values: [][][]Value{{{1, 2}}}, + } + tr := &bq.TableReference{ + ProjectId: "project-id", + DatasetId: "dataset-id", + TableId: "table-id", + } + queryJob := &Job{ + projectID: "project-id", + jobID: "job-id", + c: c, + config: &bq.JobConfiguration{ + Query: &bq.JobConfigurationQuery{DestinationTable: tr}, + }, + } + it, err := queryJob.read(context.Background(), waitForQueryStub, pf.fetchPage) + if err != nil { + t.Fatalf("err calling Read: %v", err) + } + it.PageInfo().MaxSize = 5 + var vals []Value + if err := it.Next(&vals); err != nil { + t.Fatalf("Next: got: %v: want: nil", err) + } + + want := []pageFetcherArgs{{ + table: bqToTable(tr, c), + pageSize: 5, + pageToken: "", + }} + if !testutil.Equal(pf.calls, want, cmp.AllowUnexported(pageFetcherArgs{}, Table{}, Client{})) { + t.Errorf("reading: got:\n%v\nwant:\n%v", pf.calls, want) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/schema.go b/vendor/cloud.google.com/go/bigquery/schema.go new file mode 100644 index 0000000000..428adcaad1 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/schema.go @@ -0,0 +1,397 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "fmt" + "reflect" + + "cloud.google.com/go/internal/atomiccache" + bq "google.golang.org/api/bigquery/v2" +) + +// Schema describes the fields in a table or query result. +type Schema []*FieldSchema + +type FieldSchema struct { + // The field name. + // Must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_), + // and must start with a letter or underscore. + // The maximum length is 128 characters. + Name string + + // A description of the field. The maximum length is 16,384 characters. + Description string + + // Whether the field may contain multiple values. + Repeated bool + // Whether the field is required. Ignored if Repeated is true. + Required bool + + // The field data type. If Type is Record, then this field contains a nested schema, + // which is described by Schema. + Type FieldType + // Describes the nested schema if Type is set to Record. + Schema Schema +} + +func (fs *FieldSchema) toBQ() *bq.TableFieldSchema { + tfs := &bq.TableFieldSchema{ + Description: fs.Description, + Name: fs.Name, + Type: string(fs.Type), + } + + if fs.Repeated { + tfs.Mode = "REPEATED" + } else if fs.Required { + tfs.Mode = "REQUIRED" + } // else leave as default, which is interpreted as NULLABLE. + + for _, f := range fs.Schema { + tfs.Fields = append(tfs.Fields, f.toBQ()) + } + + return tfs +} + +func (s Schema) toBQ() *bq.TableSchema { + var fields []*bq.TableFieldSchema + for _, f := range s { + fields = append(fields, f.toBQ()) + } + return &bq.TableSchema{Fields: fields} +} + +func bqToFieldSchema(tfs *bq.TableFieldSchema) *FieldSchema { + fs := &FieldSchema{ + Description: tfs.Description, + Name: tfs.Name, + Repeated: tfs.Mode == "REPEATED", + Required: tfs.Mode == "REQUIRED", + Type: FieldType(tfs.Type), + } + + for _, f := range tfs.Fields { + fs.Schema = append(fs.Schema, bqToFieldSchema(f)) + } + return fs +} + +func bqToSchema(ts *bq.TableSchema) Schema { + if ts == nil { + return nil + } + var s Schema + for _, f := range ts.Fields { + s = append(s, bqToFieldSchema(f)) + } + return s +} + +type FieldType string + +const ( + StringFieldType FieldType = "STRING" + BytesFieldType FieldType = "BYTES" + IntegerFieldType FieldType = "INTEGER" + FloatFieldType FieldType = "FLOAT" + BooleanFieldType FieldType = "BOOLEAN" + TimestampFieldType FieldType = "TIMESTAMP" + RecordFieldType FieldType = "RECORD" + DateFieldType FieldType = "DATE" + TimeFieldType FieldType = "TIME" + DateTimeFieldType FieldType = "DATETIME" + NumericFieldType FieldType = "NUMERIC" +) + +var ( + errNoStruct = errors.New("bigquery: can only infer schema from struct or pointer to struct") + errUnsupportedFieldType = errors.New("bigquery: unsupported type of field in struct") + errInvalidFieldName = errors.New("bigquery: invalid name of field in struct") + errBadNullable = errors.New(`bigquery: use "nullable" only for []byte and struct pointers; for all other types, use a NullXXX type`) +) + +var typeOfByteSlice = reflect.TypeOf([]byte{}) + +// InferSchema tries to derive a BigQuery schema from the supplied struct value. +// Each exported struct field is mapped to a field in the schema. +// +// The following BigQuery types are inferred from the corresponding Go types. +// (This is the same mapping as that used for RowIterator.Next.) Fields inferred +// from these types are marked required (non-nullable). +// +// STRING string +// BOOL bool +// INTEGER int, int8, int16, int32, int64, uint8, uint16, uint32 +// FLOAT float32, float64 +// BYTES []byte +// TIMESTAMP time.Time +// DATE civil.Date +// TIME civil.Time +// DATETIME civil.DateTime +// NUMERIC *big.Rat +// +// The big.Rat type supports numbers of arbitrary size and precision. Values +// will be rounded to 9 digits after the decimal point before being transmitted +// to BigQuery. See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#numeric-type +// for more on NUMERIC. +// +// A Go slice or array type is inferred to be a BigQuery repeated field of the +// element type. The element type must be one of the above listed types. +// +// Nullable fields are inferred from the NullXXX types, declared in this package: +// +// STRING NullString +// BOOL NullBool +// INTEGER NullInt64 +// FLOAT NullFloat64 +// TIMESTAMP NullTimestamp +// DATE NullDate +// TIME NullTime +// DATETIME NullDateTime +// +// For a nullable BYTES field, use the type []byte and tag the field "nullable" (see below). +// For a nullable NUMERIC field, use the type *big.Rat and tag the field "nullable". +// +// A struct field that is of struct type is inferred to be a required field of type +// RECORD with a schema inferred recursively. For backwards compatibility, a field of +// type pointer to struct is also inferred to be required. To get a nullable RECORD +// field, use the "nullable" tag (see below). +// +// InferSchema returns an error if any of the examined fields is of type uint, +// uint64, uintptr, map, interface, complex64, complex128, func, or chan. Future +// versions may handle these cases without error. +// +// Recursively defined structs are also disallowed. +// +// Struct fields may be tagged in a way similar to the encoding/json package. +// A tag of the form +// bigquery:"name" +// uses "name" instead of the struct field name as the BigQuery field name. +// A tag of the form +// bigquery:"-" +// omits the field from the inferred schema. +// The "nullable" option marks the field as nullable (not required). It is only +// needed for []byte, *big.Rat and pointer-to-struct fields, and cannot appear on other +// fields. In this example, the Go name of the field is retained: +// bigquery:",nullable" +func InferSchema(st interface{}) (Schema, error) { + return inferSchemaReflectCached(reflect.TypeOf(st)) +} + +// TODO(jba): replace with sync.Map for Go 1.9. +var schemaCache atomiccache.Cache + +type cacheVal struct { + schema Schema + err error +} + +func inferSchemaReflectCached(t reflect.Type) (Schema, error) { + cv := schemaCache.Get(t, func() interface{} { + s, err := inferSchemaReflect(t) + return cacheVal{s, err} + }).(cacheVal) + return cv.schema, cv.err +} + +func inferSchemaReflect(t reflect.Type) (Schema, error) { + rec, err := hasRecursiveType(t, nil) + if err != nil { + return nil, err + } + if rec { + return nil, fmt.Errorf("bigquery: schema inference for recursive type %s", t) + } + return inferStruct(t) +} + +func inferStruct(t reflect.Type) (Schema, error) { + switch t.Kind() { + case reflect.Ptr: + if t.Elem().Kind() != reflect.Struct { + return nil, errNoStruct + } + t = t.Elem() + fallthrough + + case reflect.Struct: + return inferFields(t) + default: + return nil, errNoStruct + } +} + +// inferFieldSchema infers the FieldSchema for a Go type +func inferFieldSchema(rt reflect.Type, nullable bool) (*FieldSchema, error) { + // Only []byte and struct pointers can be tagged nullable. + if nullable && !(rt == typeOfByteSlice || rt.Kind() == reflect.Ptr && rt.Elem().Kind() == reflect.Struct) { + return nil, errBadNullable + } + switch rt { + case typeOfByteSlice: + return &FieldSchema{Required: !nullable, Type: BytesFieldType}, nil + case typeOfGoTime: + return &FieldSchema{Required: true, Type: TimestampFieldType}, nil + case typeOfDate: + return &FieldSchema{Required: true, Type: DateFieldType}, nil + case typeOfTime: + return &FieldSchema{Required: true, Type: TimeFieldType}, nil + case typeOfDateTime: + return &FieldSchema{Required: true, Type: DateTimeFieldType}, nil + case typeOfRat: + return &FieldSchema{Required: !nullable, Type: NumericFieldType}, nil + } + if ft := nullableFieldType(rt); ft != "" { + return &FieldSchema{Required: false, Type: ft}, nil + } + if isSupportedIntType(rt) || isSupportedUintType(rt) { + return &FieldSchema{Required: true, Type: IntegerFieldType}, nil + } + switch rt.Kind() { + case reflect.Slice, reflect.Array: + et := rt.Elem() + if et != typeOfByteSlice && (et.Kind() == reflect.Slice || et.Kind() == reflect.Array) { + // Multi dimensional slices/arrays are not supported by BigQuery + return nil, errUnsupportedFieldType + } + if nullableFieldType(et) != "" { + // Repeated nullable types are not supported by BigQuery. + return nil, errUnsupportedFieldType + } + f, err := inferFieldSchema(et, false) + if err != nil { + return nil, err + } + f.Repeated = true + f.Required = false + return f, nil + case reflect.Ptr: + if rt.Elem().Kind() != reflect.Struct { + return nil, errUnsupportedFieldType + } + fallthrough + case reflect.Struct: + nested, err := inferStruct(rt) + if err != nil { + return nil, err + } + return &FieldSchema{Required: !nullable, Type: RecordFieldType, Schema: nested}, nil + case reflect.String: + return &FieldSchema{Required: !nullable, Type: StringFieldType}, nil + case reflect.Bool: + return &FieldSchema{Required: !nullable, Type: BooleanFieldType}, nil + case reflect.Float32, reflect.Float64: + return &FieldSchema{Required: !nullable, Type: FloatFieldType}, nil + default: + return nil, errUnsupportedFieldType + } +} + +// inferFields extracts all exported field types from struct type. +func inferFields(rt reflect.Type) (Schema, error) { + var s Schema + fields, err := fieldCache.Fields(rt) + if err != nil { + return nil, err + } + for _, field := range fields { + var nullable bool + for _, opt := range field.ParsedTag.([]string) { + if opt == nullableTagOption { + nullable = true + break + } + } + f, err := inferFieldSchema(field.Type, nullable) + if err != nil { + return nil, err + } + f.Name = field.Name + s = append(s, f) + } + return s, nil +} + +// isSupportedIntType reports whether t is an int type that can be properly +// represented by the BigQuery INTEGER/INT64 type. +func isSupportedIntType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + default: + return false + } +} + +// isSupportedIntType reports whether t is a uint type that can be properly +// represented by the BigQuery INTEGER/INT64 type. +func isSupportedUintType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + return true + default: + return false + } +} + +// typeList is a linked list of reflect.Types. +type typeList struct { + t reflect.Type + next *typeList +} + +func (l *typeList) has(t reflect.Type) bool { + for l != nil { + if l.t == t { + return true + } + l = l.next + } + return false +} + +// hasRecursiveType reports whether t or any type inside t refers to itself, directly or indirectly, +// via exported fields. (Schema inference ignores unexported fields.) +func hasRecursiveType(t reflect.Type, seen *typeList) (bool, error) { + for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice || t.Kind() == reflect.Array { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return false, nil + } + if seen.has(t) { + return true, nil + } + fields, err := fieldCache.Fields(t) + if err != nil { + return false, err + } + seen = &typeList{t, seen} + // Because seen is a linked list, additions to it from one field's + // recursive call will not affect the value for subsequent fields' calls. + for _, field := range fields { + ok, err := hasRecursiveType(field.Type, seen) + if err != nil { + return false, err + } + if ok { + return true, nil + } + } + return false, nil +} diff --git a/vendor/cloud.google.com/go/bigquery/schema_test.go b/vendor/cloud.google.com/go/bigquery/schema_test.go new file mode 100644 index 0000000000..e433c0b2a1 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/schema_test.go @@ -0,0 +1,920 @@ +// Copyright 2015 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 bigquery + +import ( + "fmt" + "math/big" + "reflect" + "testing" + "time" + + "cloud.google.com/go/civil" + "cloud.google.com/go/internal/pretty" + "cloud.google.com/go/internal/testutil" + + bq "google.golang.org/api/bigquery/v2" +) + +func (fs *FieldSchema) GoString() string { + if fs == nil { + return "" + } + + return fmt.Sprintf("{Name:%s Description:%s Repeated:%t Required:%t Type:%s Schema:%s}", + fs.Name, + fs.Description, + fs.Repeated, + fs.Required, + fs.Type, + fmt.Sprintf("%#v", fs.Schema), + ) +} + +func bqTableFieldSchema(desc, name, typ, mode string) *bq.TableFieldSchema { + return &bq.TableFieldSchema{ + Description: desc, + Name: name, + Mode: mode, + Type: typ, + } +} + +func fieldSchema(desc, name, typ string, repeated, required bool) *FieldSchema { + return &FieldSchema{ + Description: desc, + Name: name, + Repeated: repeated, + Required: required, + Type: FieldType(typ), + } +} + +func TestSchemaConversion(t *testing.T) { + testCases := []struct { + schema Schema + bqSchema *bq.TableSchema + }{ + { + // required + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "STRING", "REQUIRED"), + }, + }, + schema: Schema{ + fieldSchema("desc", "name", "STRING", false, true), + }, + }, + { + // repeated + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "STRING", "REPEATED"), + }, + }, + schema: Schema{ + fieldSchema("desc", "name", "STRING", true, false), + }, + }, + { + // nullable, string + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "STRING", ""), + }, + }, + schema: Schema{ + fieldSchema("desc", "name", "STRING", false, false), + }, + }, + { + // integer + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "INTEGER", ""), + }, + }, + schema: Schema{ + fieldSchema("desc", "name", "INTEGER", false, false), + }, + }, + { + // float + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "FLOAT", ""), + }, + }, + schema: Schema{ + fieldSchema("desc", "name", "FLOAT", false, false), + }, + }, + { + // boolean + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "BOOLEAN", ""), + }, + }, + schema: Schema{ + fieldSchema("desc", "name", "BOOLEAN", false, false), + }, + }, + { + // timestamp + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "TIMESTAMP", ""), + }, + }, + schema: Schema{ + fieldSchema("desc", "name", "TIMESTAMP", false, false), + }, + }, + { + // civil times + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "f1", "TIME", ""), + bqTableFieldSchema("desc", "f2", "DATE", ""), + bqTableFieldSchema("desc", "f3", "DATETIME", ""), + }, + }, + schema: Schema{ + fieldSchema("desc", "f1", "TIME", false, false), + fieldSchema("desc", "f2", "DATE", false, false), + fieldSchema("desc", "f3", "DATETIME", false, false), + }, + }, + { + // numeric + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "n", "NUMERIC", ""), + }, + }, + schema: Schema{ + fieldSchema("desc", "n", "NUMERIC", false, false), + }, + }, + { + // nested + bqSchema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + { + Description: "An outer schema wrapping a nested schema", + Name: "outer", + Mode: "REQUIRED", + Type: "RECORD", + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("inner field", "inner", "STRING", ""), + }, + }, + }, + }, + schema: Schema{ + &FieldSchema{ + Description: "An outer schema wrapping a nested schema", + Name: "outer", + Required: true, + Type: "RECORD", + Schema: Schema{ + { + Description: "inner field", + Name: "inner", + Type: "STRING", + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + bqSchema := tc.schema.toBQ() + if !testutil.Equal(bqSchema, tc.bqSchema) { + t.Errorf("converting to TableSchema: got:\n%v\nwant:\n%v", + pretty.Value(bqSchema), pretty.Value(tc.bqSchema)) + } + schema := bqToSchema(tc.bqSchema) + if !testutil.Equal(schema, tc.schema) { + t.Errorf("converting to Schema: got:\n%v\nwant:\n%v", schema, tc.schema) + } + } +} + +type allStrings struct { + String string + ByteSlice []byte +} + +type allSignedIntegers struct { + Int64 int64 + Int32 int32 + Int16 int16 + Int8 int8 + Int int +} + +type allUnsignedIntegers struct { + Uint32 uint32 + Uint16 uint16 + Uint8 uint8 +} + +type allFloat struct { + Float64 float64 + Float32 float32 + // NOTE: Complex32 and Complex64 are unsupported by BigQuery +} + +type allBoolean struct { + Bool bool +} + +type allTime struct { + Timestamp time.Time + Time civil.Time + Date civil.Date + DateTime civil.DateTime +} + +type allNumeric struct { + Numeric *big.Rat +} + +func reqField(name, typ string) *FieldSchema { + return &FieldSchema{ + Name: name, + Type: FieldType(typ), + Required: true, + } +} + +func optField(name, typ string) *FieldSchema { + return &FieldSchema{ + Name: name, + Type: FieldType(typ), + Required: false, + } +} + +func TestSimpleInference(t *testing.T) { + testCases := []struct { + in interface{} + want Schema + }{ + { + in: allSignedIntegers{}, + want: Schema{ + reqField("Int64", "INTEGER"), + reqField("Int32", "INTEGER"), + reqField("Int16", "INTEGER"), + reqField("Int8", "INTEGER"), + reqField("Int", "INTEGER"), + }, + }, + { + in: allUnsignedIntegers{}, + want: Schema{ + reqField("Uint32", "INTEGER"), + reqField("Uint16", "INTEGER"), + reqField("Uint8", "INTEGER"), + }, + }, + { + in: allFloat{}, + want: Schema{ + reqField("Float64", "FLOAT"), + reqField("Float32", "FLOAT"), + }, + }, + { + in: allBoolean{}, + want: Schema{ + reqField("Bool", "BOOLEAN"), + }, + }, + { + in: &allBoolean{}, + want: Schema{ + reqField("Bool", "BOOLEAN"), + }, + }, + { + in: allTime{}, + want: Schema{ + reqField("Timestamp", "TIMESTAMP"), + reqField("Time", "TIME"), + reqField("Date", "DATE"), + reqField("DateTime", "DATETIME"), + }, + }, + { + in: &allNumeric{}, + want: Schema{ + reqField("Numeric", "NUMERIC"), + }, + }, + { + in: allStrings{}, + want: Schema{ + reqField("String", "STRING"), + reqField("ByteSlice", "BYTES"), + }, + }, + } + for _, tc := range testCases { + got, err := InferSchema(tc.in) + if err != nil { + t.Fatalf("%T: error inferring TableSchema: %v", tc.in, err) + } + if !testutil.Equal(got, tc.want) { + t.Errorf("%T: inferring TableSchema: got:\n%#v\nwant:\n%#v", tc.in, + pretty.Value(got), pretty.Value(tc.want)) + } + } +} + +type containsNested struct { + hidden string + NotNested int + Nested struct { + Inside int + } +} + +type containsDoubleNested struct { + NotNested int + Nested struct { + InsideNested struct { + Inside int + } + } +} + +type ptrNested struct { + Ptr *struct{ Inside int } +} + +type dup struct { // more than one field of the same struct type + A, B allBoolean +} + +func TestNestedInference(t *testing.T) { + testCases := []struct { + in interface{} + want Schema + }{ + { + in: containsNested{}, + want: Schema{ + reqField("NotNested", "INTEGER"), + &FieldSchema{ + Name: "Nested", + Required: true, + Type: "RECORD", + Schema: Schema{reqField("Inside", "INTEGER")}, + }, + }, + }, + { + in: containsDoubleNested{}, + want: Schema{ + reqField("NotNested", "INTEGER"), + &FieldSchema{ + Name: "Nested", + Required: true, + Type: "RECORD", + Schema: Schema{ + { + Name: "InsideNested", + Required: true, + Type: "RECORD", + Schema: Schema{reqField("Inside", "INTEGER")}, + }, + }, + }, + }, + }, + { + in: ptrNested{}, + want: Schema{ + &FieldSchema{ + Name: "Ptr", + Required: true, + Type: "RECORD", + Schema: Schema{reqField("Inside", "INTEGER")}, + }, + }, + }, + { + in: dup{}, + want: Schema{ + &FieldSchema{ + Name: "A", + Required: true, + Type: "RECORD", + Schema: Schema{reqField("Bool", "BOOLEAN")}, + }, + &FieldSchema{ + Name: "B", + Required: true, + Type: "RECORD", + Schema: Schema{reqField("Bool", "BOOLEAN")}, + }, + }, + }, + } + + for _, tc := range testCases { + got, err := InferSchema(tc.in) + if err != nil { + t.Fatalf("%T: error inferring TableSchema: %v", tc.in, err) + } + if !testutil.Equal(got, tc.want) { + t.Errorf("%T: inferring TableSchema: got:\n%#v\nwant:\n%#v", tc.in, + pretty.Value(got), pretty.Value(tc.want)) + } + } +} + +type repeated struct { + NotRepeated []byte + RepeatedByteSlice [][]byte + Slice []int + Array [5]bool +} + +type nestedRepeated struct { + NotRepeated int + Repeated []struct { + Inside int + } + RepeatedPtr []*struct{ Inside int } +} + +func repField(name, typ string) *FieldSchema { + return &FieldSchema{ + Name: name, + Type: FieldType(typ), + Repeated: true, + } +} + +func TestRepeatedInference(t *testing.T) { + testCases := []struct { + in interface{} + want Schema + }{ + { + in: repeated{}, + want: Schema{ + reqField("NotRepeated", "BYTES"), + repField("RepeatedByteSlice", "BYTES"), + repField("Slice", "INTEGER"), + repField("Array", "BOOLEAN"), + }, + }, + { + in: nestedRepeated{}, + want: Schema{ + reqField("NotRepeated", "INTEGER"), + { + Name: "Repeated", + Repeated: true, + Type: "RECORD", + Schema: Schema{reqField("Inside", "INTEGER")}, + }, + { + Name: "RepeatedPtr", + Repeated: true, + Type: "RECORD", + Schema: Schema{reqField("Inside", "INTEGER")}, + }, + }, + }, + } + + for i, tc := range testCases { + got, err := InferSchema(tc.in) + if err != nil { + t.Fatalf("%d: error inferring TableSchema: %v", i, err) + } + if !testutil.Equal(got, tc.want) { + t.Errorf("%d: inferring TableSchema: got:\n%#v\nwant:\n%#v", i, + pretty.Value(got), pretty.Value(tc.want)) + } + } +} + +type allNulls struct { + A NullInt64 + B NullFloat64 + C NullBool + D NullString + E NullTimestamp + F NullTime + G NullDate + H NullDateTime +} + +func TestNullInference(t *testing.T) { + got, err := InferSchema(allNulls{}) + if err != nil { + t.Fatal(err) + } + want := Schema{ + optField("A", "INTEGER"), + optField("B", "FLOAT"), + optField("C", "BOOLEAN"), + optField("D", "STRING"), + optField("E", "TIMESTAMP"), + optField("F", "TIME"), + optField("G", "DATE"), + optField("H", "DATETIME"), + } + if diff := testutil.Diff(got, want); diff != "" { + t.Error(diff) + } +} + +type Embedded struct { + Embedded int +} + +type embedded struct { + Embedded2 int +} + +type nestedEmbedded struct { + Embedded + embedded +} + +func TestEmbeddedInference(t *testing.T) { + got, err := InferSchema(nestedEmbedded{}) + if err != nil { + t.Fatal(err) + } + want := Schema{ + reqField("Embedded", "INTEGER"), + reqField("Embedded2", "INTEGER"), + } + if !testutil.Equal(got, want) { + t.Errorf("got %v, want %v", pretty.Value(got), pretty.Value(want)) + } +} + +func TestRecursiveInference(t *testing.T) { + type List struct { + Val int + Next *List + } + + _, err := InferSchema(List{}) + if err == nil { + t.Fatal("got nil, want error") + } +} + +type withTags struct { + NoTag int + ExcludeTag int `bigquery:"-"` + SimpleTag int `bigquery:"simple_tag"` + UnderscoreTag int `bigquery:"_id"` + MixedCase int `bigquery:"MIXEDcase"` + Nullable []byte `bigquery:",nullable"` + NullNumeric *big.Rat `bigquery:",nullable"` +} + +type withTagsNested struct { + Nested withTags `bigquery:"nested"` + NestedAnonymous struct { + ExcludeTag int `bigquery:"-"` + Inside int `bigquery:"inside"` + } `bigquery:"anon"` + PNested *struct{ X int } // not nullable, for backwards compatibility + PNestedNullable *struct{ X int } `bigquery:",nullable"` +} + +type withTagsRepeated struct { + Repeated []withTags `bigquery:"repeated"` + RepeatedAnonymous []struct { + ExcludeTag int `bigquery:"-"` + Inside int `bigquery:"inside"` + } `bigquery:"anon"` +} + +type withTagsEmbedded struct { + withTags +} + +var withTagsSchema = Schema{ + reqField("NoTag", "INTEGER"), + reqField("simple_tag", "INTEGER"), + reqField("_id", "INTEGER"), + reqField("MIXEDcase", "INTEGER"), + optField("Nullable", "BYTES"), + optField("NullNumeric", "NUMERIC"), +} + +func TestTagInference(t *testing.T) { + testCases := []struct { + in interface{} + want Schema + }{ + { + in: withTags{}, + want: withTagsSchema, + }, + { + in: withTagsNested{}, + want: Schema{ + &FieldSchema{ + Name: "nested", + Required: true, + Type: "RECORD", + Schema: withTagsSchema, + }, + &FieldSchema{ + Name: "anon", + Required: true, + Type: "RECORD", + Schema: Schema{reqField("inside", "INTEGER")}, + }, + &FieldSchema{ + Name: "PNested", + Required: true, + Type: "RECORD", + Schema: Schema{reqField("X", "INTEGER")}, + }, + &FieldSchema{ + Name: "PNestedNullable", + Required: false, + Type: "RECORD", + Schema: Schema{reqField("X", "INTEGER")}, + }, + }, + }, + { + in: withTagsRepeated{}, + want: Schema{ + &FieldSchema{ + Name: "repeated", + Repeated: true, + Type: "RECORD", + Schema: withTagsSchema, + }, + &FieldSchema{ + Name: "anon", + Repeated: true, + Type: "RECORD", + Schema: Schema{reqField("inside", "INTEGER")}, + }, + }, + }, + { + in: withTagsEmbedded{}, + want: withTagsSchema, + }, + } + for i, tc := range testCases { + got, err := InferSchema(tc.in) + if err != nil { + t.Fatalf("%d: error inferring TableSchema: %v", i, err) + } + if !testutil.Equal(got, tc.want) { + t.Errorf("%d: inferring TableSchema: got:\n%#v\nwant:\n%#v", i, + pretty.Value(got), pretty.Value(tc.want)) + } + } +} + +func TestTagInferenceErrors(t *testing.T) { + testCases := []struct { + in interface{} + err error + }{ + { + in: struct { + LongTag int `bigquery:"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy"` + }{}, + err: errInvalidFieldName, + }, + { + in: struct { + UnsupporedStartChar int `bigquery:"øab"` + }{}, + err: errInvalidFieldName, + }, + { + in: struct { + UnsupportedEndChar int `bigquery:"abø"` + }{}, + err: errInvalidFieldName, + }, + { + in: struct { + UnsupportedMiddleChar int `bigquery:"aøb"` + }{}, + err: errInvalidFieldName, + }, + { + in: struct { + StartInt int `bigquery:"1abc"` + }{}, + err: errInvalidFieldName, + }, + { + in: struct { + Hyphens int `bigquery:"a-b"` + }{}, + err: errInvalidFieldName, + }, + } + for i, tc := range testCases { + want := tc.err + _, got := InferSchema(tc.in) + if got != want { + t.Errorf("%d: inferring TableSchema: got:\n%#v\nwant:\n%#v", i, got, want) + } + } + + _, err := InferSchema(struct { + X int `bigquery:",optional"` + }{}) + if err == nil { + t.Error("got nil, want error") + } +} + +func TestSchemaErrors(t *testing.T) { + testCases := []struct { + in interface{} + err error + }{ + { + in: []byte{}, + err: errNoStruct, + }, + { + in: new(int), + err: errNoStruct, + }, + { + in: struct{ Uint uint }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ Uint64 uint64 }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ Uintptr uintptr }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ Complex complex64 }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ Map map[string]int }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ Chan chan bool }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ Ptr *int }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ Interface interface{} }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ MultiDimensional [][]int }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ MultiDimensional [][][]byte }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ SliceOfPointer []*int }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ SliceOfNull []NullInt64 }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ ChanSlice []chan bool }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ NestedChan struct{ Chan []chan bool } }{}, + err: errUnsupportedFieldType, + }, + { + in: struct { + X int `bigquery:",nullable"` + }{}, + err: errBadNullable, + }, + { + in: struct { + X bool `bigquery:",nullable"` + }{}, + err: errBadNullable, + }, + { + in: struct { + X struct{ N int } `bigquery:",nullable"` + }{}, + err: errBadNullable, + }, + { + in: struct { + X []int `bigquery:",nullable"` + }{}, + err: errBadNullable, + }, + { + in: struct{ X *[]byte }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ X *[]int }{}, + err: errUnsupportedFieldType, + }, + { + in: struct{ X *int }{}, + err: errUnsupportedFieldType, + }, + } + for _, tc := range testCases { + want := tc.err + _, got := InferSchema(tc.in) + if got != want { + t.Errorf("%#v: got:\n%#v\nwant:\n%#v", tc.in, got, want) + } + } +} + +func TestHasRecursiveType(t *testing.T) { + type ( + nonStruct int + nonRec struct{ A string } + dup struct{ A, B nonRec } + rec struct { + A int + B *rec + } + recUnexported struct { + A int + b *rec + } + hasRec struct { + A int + R *rec + } + recSlicePointer struct { + A []*recSlicePointer + } + ) + for _, test := range []struct { + in interface{} + want bool + }{ + {nonStruct(0), false}, + {nonRec{}, false}, + {dup{}, false}, + {rec{}, true}, + {recUnexported{}, false}, + {hasRec{}, true}, + {&recSlicePointer{}, true}, + } { + got, err := hasRecursiveType(reflect.TypeOf(test.in), nil) + if err != nil { + t.Fatal(err) + } + if got != test.want { + t.Errorf("%T: got %t, want %t", test.in, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/table.go b/vendor/cloud.google.com/go/bigquery/table.go new file mode 100644 index 0000000000..9d8c363616 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/table.go @@ -0,0 +1,531 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "fmt" + "time" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + + "cloud.google.com/go/internal/optional" + bq "google.golang.org/api/bigquery/v2" +) + +// A Table is a reference to a BigQuery table. +type Table struct { + // ProjectID, DatasetID and TableID may be omitted if the Table is the destination for a query. + // In this case the result will be stored in an ephemeral table. + ProjectID string + DatasetID string + // TableID must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_). + // The maximum length is 1,024 characters. + TableID string + + c *Client +} + +// TableMetadata contains information about a BigQuery table. +type TableMetadata struct { + // The following fields can be set when creating a table. + + // The user-friendly name for the table. + Name string + + // The user-friendly description of the table. + Description string + + // The table schema. If provided on create, ViewQuery must be empty. + Schema Schema + + // The query to use for a view. If provided on create, Schema must be nil. + ViewQuery string + + // Use Legacy SQL for the view query. + // At most one of UseLegacySQL and UseStandardSQL can be true. + UseLegacySQL bool + + // Use Legacy SQL for the view query. The default. + // At most one of UseLegacySQL and UseStandardSQL can be true. + // Deprecated: use UseLegacySQL. + UseStandardSQL bool + + // If non-nil, the table is partitioned by time. + TimePartitioning *TimePartitioning + + // The time when this table expires. If not set, the table will persist + // indefinitely. Expired tables will be deleted and their storage reclaimed. + ExpirationTime time.Time + + // User-provided labels. + Labels map[string]string + + // Information about a table stored outside of BigQuery. + ExternalDataConfig *ExternalDataConfig + + // Custom encryption configuration (e.g., Cloud KMS keys). + EncryptionConfig *EncryptionConfig + + // All the fields below are read-only. + + FullID string // An opaque ID uniquely identifying the table. + Type TableType + CreationTime time.Time + LastModifiedTime time.Time + + // The size of the table in bytes. + // This does not include data that is being buffered during a streaming insert. + NumBytes int64 + + // The number of rows of data in this table. + // This does not include data that is being buffered during a streaming insert. + NumRows uint64 + + // Contains information regarding this table's streaming buffer, if one is + // present. This field will be nil if the table is not being streamed to or if + // there is no data in the streaming buffer. + StreamingBuffer *StreamingBuffer + + // ETag is the ETag obtained when reading metadata. Pass it to Table.Update to + // ensure that the metadata hasn't changed since it was read. + ETag string +} + +// TableCreateDisposition specifies the circumstances under which destination table will be created. +// Default is CreateIfNeeded. +type TableCreateDisposition string + +const ( + // CreateIfNeeded will create the table if it does not already exist. + // Tables are created atomically on successful completion of a job. + CreateIfNeeded TableCreateDisposition = "CREATE_IF_NEEDED" + + // CreateNever ensures the table must already exist and will not be + // automatically created. + CreateNever TableCreateDisposition = "CREATE_NEVER" +) + +// TableWriteDisposition specifies how existing data in a destination table is treated. +// Default is WriteAppend. +type TableWriteDisposition string + +const ( + // WriteAppend will append to any existing data in the destination table. + // Data is appended atomically on successful completion of a job. + WriteAppend TableWriteDisposition = "WRITE_APPEND" + + // WriteTruncate overrides the existing data in the destination table. + // Data is overwritten atomically on successful completion of a job. + WriteTruncate TableWriteDisposition = "WRITE_TRUNCATE" + + // WriteEmpty fails writes if the destination table already contains data. + WriteEmpty TableWriteDisposition = "WRITE_EMPTY" +) + +// TableType is the type of table. +type TableType string + +const ( + RegularTable TableType = "TABLE" + ViewTable TableType = "VIEW" + ExternalTable TableType = "EXTERNAL" +) + +// TimePartitioning describes the time-based date partitioning on a table. +// For more information see: https://cloud.google.com/bigquery/docs/creating-partitioned-tables. +type TimePartitioning struct { + // The amount of time to keep the storage for a partition. + // If the duration is empty (0), the data in the partitions do not expire. + Expiration time.Duration + + // If empty, the table is partitioned by pseudo column '_PARTITIONTIME'; if set, the + // table is partitioned by this field. The field must be a top-level TIMESTAMP or + // DATE field. Its mode must be NULLABLE or REQUIRED. + Field string +} + +func (p *TimePartitioning) toBQ() *bq.TimePartitioning { + if p == nil { + return nil + } + return &bq.TimePartitioning{ + Type: "DAY", + ExpirationMs: int64(p.Expiration / time.Millisecond), + Field: p.Field, + } +} + +func bqToTimePartitioning(q *bq.TimePartitioning) *TimePartitioning { + if q == nil { + return nil + } + return &TimePartitioning{ + Expiration: time.Duration(q.ExpirationMs) * time.Millisecond, + Field: q.Field, + } +} + +// EncryptionConfig configures customer-managed encryption on tables. +type EncryptionConfig struct { + // Describes the Cloud KMS encryption key that will be used to protect + // destination BigQuery table. The BigQuery Service Account associated with your + // project requires access to this encryption key. + KMSKeyName string +} + +func (e *EncryptionConfig) toBQ() *bq.EncryptionConfiguration { + if e == nil { + return nil + } + return &bq.EncryptionConfiguration{ + KmsKeyName: e.KMSKeyName, + } +} + +func bqToEncryptionConfig(q *bq.EncryptionConfiguration) *EncryptionConfig { + if q == nil { + return nil + } + return &EncryptionConfig{ + KMSKeyName: q.KmsKeyName, + } +} + +// StreamingBuffer holds information about the streaming buffer. +type StreamingBuffer struct { + // A lower-bound estimate of the number of bytes currently in the streaming + // buffer. + EstimatedBytes uint64 + + // A lower-bound estimate of the number of rows currently in the streaming + // buffer. + EstimatedRows uint64 + + // The time of the oldest entry in the streaming buffer. + OldestEntryTime time.Time +} + +func (t *Table) toBQ() *bq.TableReference { + return &bq.TableReference{ + ProjectId: t.ProjectID, + DatasetId: t.DatasetID, + TableId: t.TableID, + } +} + +// FullyQualifiedName returns the ID of the table in projectID:datasetID.tableID format. +func (t *Table) FullyQualifiedName() string { + return fmt.Sprintf("%s:%s.%s", t.ProjectID, t.DatasetID, t.TableID) +} + +// implicitTable reports whether Table is an empty placeholder, which signifies that a new table should be created with an auto-generated Table ID. +func (t *Table) implicitTable() bool { + return t.ProjectID == "" && t.DatasetID == "" && t.TableID == "" +} + +// Create creates a table in the BigQuery service. +// Pass in a TableMetadata value to configure the table. +// If tm.View.Query is non-empty, the created table will be of type VIEW. +// Expiration can only be set during table creation. +// After table creation, a view can be modified only if its table was initially created +// with a view. +func (t *Table) Create(ctx context.Context, tm *TableMetadata) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Table.Create") + defer func() { trace.EndSpan(ctx, err) }() + + table, err := tm.toBQ() + if err != nil { + return err + } + table.TableReference = &bq.TableReference{ + ProjectId: t.ProjectID, + DatasetId: t.DatasetID, + TableId: t.TableID, + } + req := t.c.bqs.Tables.Insert(t.ProjectID, t.DatasetID, table).Context(ctx) + setClientHeader(req.Header()) + _, err = req.Do() + return err +} + +func (tm *TableMetadata) toBQ() (*bq.Table, error) { + t := &bq.Table{} + if tm == nil { + return t, nil + } + if tm.Schema != nil && tm.ViewQuery != "" { + return nil, errors.New("bigquery: provide Schema or ViewQuery, not both") + } + t.FriendlyName = tm.Name + t.Description = tm.Description + t.Labels = tm.Labels + if tm.Schema != nil { + t.Schema = tm.Schema.toBQ() + } + if tm.ViewQuery != "" { + if tm.UseStandardSQL && tm.UseLegacySQL { + return nil, errors.New("bigquery: cannot provide both UseStandardSQL and UseLegacySQL") + } + t.View = &bq.ViewDefinition{Query: tm.ViewQuery} + if tm.UseLegacySQL { + t.View.UseLegacySql = true + } else { + t.View.UseLegacySql = false + t.View.ForceSendFields = append(t.View.ForceSendFields, "UseLegacySql") + } + } else if tm.UseLegacySQL || tm.UseStandardSQL { + return nil, errors.New("bigquery: UseLegacy/StandardSQL requires ViewQuery") + } + t.TimePartitioning = tm.TimePartitioning.toBQ() + if !tm.ExpirationTime.IsZero() { + t.ExpirationTime = tm.ExpirationTime.UnixNano() / 1e6 + } + if tm.ExternalDataConfig != nil { + edc := tm.ExternalDataConfig.toBQ() + t.ExternalDataConfiguration = &edc + } + t.EncryptionConfiguration = tm.EncryptionConfig.toBQ() + if tm.FullID != "" { + return nil, errors.New("cannot set FullID on create") + } + if tm.Type != "" { + return nil, errors.New("cannot set Type on create") + } + if !tm.CreationTime.IsZero() { + return nil, errors.New("cannot set CreationTime on create") + } + if !tm.LastModifiedTime.IsZero() { + return nil, errors.New("cannot set LastModifiedTime on create") + } + if tm.NumBytes != 0 { + return nil, errors.New("cannot set NumBytes on create") + } + if tm.NumRows != 0 { + return nil, errors.New("cannot set NumRows on create") + } + if tm.StreamingBuffer != nil { + return nil, errors.New("cannot set StreamingBuffer on create") + } + if tm.ETag != "" { + return nil, errors.New("cannot set ETag on create") + } + return t, nil +} + +// Metadata fetches the metadata for the table. +func (t *Table) Metadata(ctx context.Context) (md *TableMetadata, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Table.Metadata") + defer func() { trace.EndSpan(ctx, err) }() + + req := t.c.bqs.Tables.Get(t.ProjectID, t.DatasetID, t.TableID).Context(ctx) + setClientHeader(req.Header()) + var table *bq.Table + err = runWithRetry(ctx, func() (err error) { + table, err = req.Do() + return err + }) + if err != nil { + return nil, err + } + return bqToTableMetadata(table) +} + +func bqToTableMetadata(t *bq.Table) (*TableMetadata, error) { + md := &TableMetadata{ + Description: t.Description, + Name: t.FriendlyName, + Type: TableType(t.Type), + FullID: t.Id, + Labels: t.Labels, + NumBytes: t.NumBytes, + NumRows: t.NumRows, + ExpirationTime: unixMillisToTime(t.ExpirationTime), + CreationTime: unixMillisToTime(t.CreationTime), + LastModifiedTime: unixMillisToTime(int64(t.LastModifiedTime)), + ETag: t.Etag, + EncryptionConfig: bqToEncryptionConfig(t.EncryptionConfiguration), + } + if t.Schema != nil { + md.Schema = bqToSchema(t.Schema) + } + if t.View != nil { + md.ViewQuery = t.View.Query + md.UseLegacySQL = t.View.UseLegacySql + } + md.TimePartitioning = bqToTimePartitioning(t.TimePartitioning) + if t.StreamingBuffer != nil { + md.StreamingBuffer = &StreamingBuffer{ + EstimatedBytes: t.StreamingBuffer.EstimatedBytes, + EstimatedRows: t.StreamingBuffer.EstimatedRows, + OldestEntryTime: unixMillisToTime(int64(t.StreamingBuffer.OldestEntryTime)), + } + } + if t.ExternalDataConfiguration != nil { + edc, err := bqToExternalDataConfig(t.ExternalDataConfiguration) + if err != nil { + return nil, err + } + md.ExternalDataConfig = edc + } + return md, nil +} + +// Delete deletes the table. +func (t *Table) Delete(ctx context.Context) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Table.Delete") + defer func() { trace.EndSpan(ctx, err) }() + + req := t.c.bqs.Tables.Delete(t.ProjectID, t.DatasetID, t.TableID).Context(ctx) + setClientHeader(req.Header()) + return req.Do() +} + +// Read fetches the contents of the table. +func (t *Table) Read(ctx context.Context) *RowIterator { + return t.read(ctx, fetchPage) +} + +func (t *Table) read(ctx context.Context, pf pageFetcher) *RowIterator { + return newRowIterator(ctx, t, pf) +} + +// Update modifies specific Table metadata fields. +func (t *Table) Update(ctx context.Context, tm TableMetadataToUpdate, etag string) (md *TableMetadata, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Table.Update") + defer func() { trace.EndSpan(ctx, err) }() + + bqt := tm.toBQ() + call := t.c.bqs.Tables.Patch(t.ProjectID, t.DatasetID, t.TableID, bqt).Context(ctx) + setClientHeader(call.Header()) + if etag != "" { + call.Header().Set("If-Match", etag) + } + var res *bq.Table + if err := runWithRetry(ctx, func() (err error) { + res, err = call.Do() + return err + }); err != nil { + return nil, err + } + return bqToTableMetadata(res) +} + +func (tm *TableMetadataToUpdate) toBQ() *bq.Table { + t := &bq.Table{} + forceSend := func(field string) { + t.ForceSendFields = append(t.ForceSendFields, field) + } + + if tm.Description != nil { + t.Description = optional.ToString(tm.Description) + forceSend("Description") + } + if tm.Name != nil { + t.FriendlyName = optional.ToString(tm.Name) + forceSend("FriendlyName") + } + if tm.Schema != nil { + t.Schema = tm.Schema.toBQ() + forceSend("Schema") + } + if !tm.ExpirationTime.IsZero() { + t.ExpirationTime = tm.ExpirationTime.UnixNano() / 1e6 + forceSend("ExpirationTime") + } + if tm.ViewQuery != nil { + t.View = &bq.ViewDefinition{ + Query: optional.ToString(tm.ViewQuery), + ForceSendFields: []string{"Query"}, + } + } + if tm.UseLegacySQL != nil { + if t.View == nil { + t.View = &bq.ViewDefinition{} + } + t.View.UseLegacySql = optional.ToBool(tm.UseLegacySQL) + t.View.ForceSendFields = append(t.View.ForceSendFields, "UseLegacySql") + } + labels, forces, nulls := tm.update() + t.Labels = labels + t.ForceSendFields = append(t.ForceSendFields, forces...) + t.NullFields = append(t.NullFields, nulls...) + return t +} + +// TableMetadataToUpdate is used when updating a table's metadata. +// Only non-nil fields will be updated. +type TableMetadataToUpdate struct { + // The user-friendly description of this table. + Description optional.String + + // The user-friendly name for this table. + Name optional.String + + // The table's schema. + // When updating a schema, you can add columns but not remove them. + Schema Schema + + // The time when this table expires. + ExpirationTime time.Time + + // The query to use for a view. + ViewQuery optional.String + + // Use Legacy SQL for the view query. + UseLegacySQL optional.Bool + + labelUpdater +} + +// labelUpdater contains common code for updating labels. +type labelUpdater struct { + setLabels map[string]string + deleteLabels map[string]bool +} + +// SetLabel causes a label to be added or modified on a call to Update. +func (u *labelUpdater) SetLabel(name, value string) { + if u.setLabels == nil { + u.setLabels = map[string]string{} + } + u.setLabels[name] = value +} + +// DeleteLabel causes a label to be deleted on a call to Update. +func (u *labelUpdater) DeleteLabel(name string) { + if u.deleteLabels == nil { + u.deleteLabels = map[string]bool{} + } + u.deleteLabels[name] = true +} + +func (u *labelUpdater) update() (labels map[string]string, forces, nulls []string) { + if u.setLabels == nil && u.deleteLabels == nil { + return nil, nil, nil + } + labels = map[string]string{} + for k, v := range u.setLabels { + labels[k] = v + } + if len(labels) == 0 && len(u.deleteLabels) > 0 { + forces = []string{"Labels"} + } + for l := range u.deleteLabels { + nulls = append(nulls, "Labels."+l) + } + return labels, forces, nulls +} diff --git a/vendor/cloud.google.com/go/bigquery/table_test.go b/vendor/cloud.google.com/go/bigquery/table_test.go new file mode 100644 index 0000000000..85db43ca4b --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/table_test.go @@ -0,0 +1,295 @@ +// Copyright 2017 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 bigquery + +import ( + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + bq "google.golang.org/api/bigquery/v2" +) + +func TestBQToTableMetadata(t *testing.T) { + aTime := time.Date(2017, 1, 26, 0, 0, 0, 0, time.Local) + aTimeMillis := aTime.UnixNano() / 1e6 + for _, test := range []struct { + in *bq.Table + want *TableMetadata + }{ + {&bq.Table{}, &TableMetadata{}}, // test minimal case + { + &bq.Table{ + CreationTime: aTimeMillis, + Description: "desc", + Etag: "etag", + ExpirationTime: aTimeMillis, + FriendlyName: "fname", + Id: "id", + LastModifiedTime: uint64(aTimeMillis), + Location: "loc", + NumBytes: 123, + NumLongTermBytes: 23, + NumRows: 7, + StreamingBuffer: &bq.Streamingbuffer{ + EstimatedBytes: 11, + EstimatedRows: 3, + OldestEntryTime: uint64(aTimeMillis), + }, + TimePartitioning: &bq.TimePartitioning{ + ExpirationMs: 7890, + Type: "DAY", + Field: "pfield", + }, + EncryptionConfiguration: &bq.EncryptionConfiguration{KmsKeyName: "keyName"}, + Type: "EXTERNAL", + View: &bq.ViewDefinition{Query: "view-query"}, + Labels: map[string]string{"a": "b"}, + ExternalDataConfiguration: &bq.ExternalDataConfiguration{ + SourceFormat: "GOOGLE_SHEETS", + }, + }, + &TableMetadata{ + Description: "desc", + Name: "fname", + ViewQuery: "view-query", + FullID: "id", + Type: ExternalTable, + Labels: map[string]string{"a": "b"}, + ExternalDataConfig: &ExternalDataConfig{SourceFormat: GoogleSheets}, + ExpirationTime: aTime.Truncate(time.Millisecond), + CreationTime: aTime.Truncate(time.Millisecond), + LastModifiedTime: aTime.Truncate(time.Millisecond), + NumBytes: 123, + NumRows: 7, + TimePartitioning: &TimePartitioning{ + Expiration: 7890 * time.Millisecond, + Field: "pfield", + }, + StreamingBuffer: &StreamingBuffer{ + EstimatedBytes: 11, + EstimatedRows: 3, + OldestEntryTime: aTime, + }, + EncryptionConfig: &EncryptionConfig{KMSKeyName: "keyName"}, + ETag: "etag", + }, + }, + } { + got, err := bqToTableMetadata(test.in) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(got, test.want); diff != "" { + t.Errorf("%+v:\n, -got, +want:\n%s", test.in, diff) + } + } +} + +func TestTableMetadataToBQ(t *testing.T) { + aTime := time.Date(2017, 1, 26, 0, 0, 0, 0, time.Local) + aTimeMillis := aTime.UnixNano() / 1e6 + sc := Schema{fieldSchema("desc", "name", "STRING", false, true)} + + for _, test := range []struct { + in *TableMetadata + want *bq.Table + }{ + {nil, &bq.Table{}}, + {&TableMetadata{}, &bq.Table{}}, + { + &TableMetadata{ + Name: "n", + Description: "d", + Schema: sc, + ExpirationTime: aTime, + Labels: map[string]string{"a": "b"}, + ExternalDataConfig: &ExternalDataConfig{SourceFormat: Bigtable}, + EncryptionConfig: &EncryptionConfig{KMSKeyName: "keyName"}, + }, + &bq.Table{ + FriendlyName: "n", + Description: "d", + Schema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "STRING", "REQUIRED"), + }, + }, + ExpirationTime: aTimeMillis, + Labels: map[string]string{"a": "b"}, + ExternalDataConfiguration: &bq.ExternalDataConfiguration{SourceFormat: "BIGTABLE"}, + EncryptionConfiguration: &bq.EncryptionConfiguration{KmsKeyName: "keyName"}, + }, + }, + { + &TableMetadata{ViewQuery: "q"}, + &bq.Table{ + View: &bq.ViewDefinition{ + Query: "q", + UseLegacySql: false, + ForceSendFields: []string{"UseLegacySql"}, + }, + }, + }, + { + &TableMetadata{ + ViewQuery: "q", + UseLegacySQL: true, + TimePartitioning: &TimePartitioning{}, + }, + &bq.Table{ + View: &bq.ViewDefinition{ + Query: "q", + UseLegacySql: true, + }, + TimePartitioning: &bq.TimePartitioning{ + Type: "DAY", + ExpirationMs: 0, + }, + }, + }, + { + &TableMetadata{ + ViewQuery: "q", + UseStandardSQL: true, + TimePartitioning: &TimePartitioning{ + Expiration: time.Second, + Field: "ofDreams", + }, + }, + &bq.Table{ + View: &bq.ViewDefinition{ + Query: "q", + UseLegacySql: false, + ForceSendFields: []string{"UseLegacySql"}, + }, + TimePartitioning: &bq.TimePartitioning{ + Type: "DAY", + ExpirationMs: 1000, + Field: "ofDreams", + }, + }, + }, + } { + got, err := test.in.toBQ() + if err != nil { + t.Fatalf("%+v: %v", test.in, err) + } + if diff := testutil.Diff(got, test.want); diff != "" { + t.Errorf("%+v:\n-got, +want:\n%s", test.in, diff) + } + } + + // Errors + for _, in := range []*TableMetadata{ + {Schema: sc, ViewQuery: "q"}, // can't have both schema and query + {UseLegacySQL: true}, // UseLegacySQL without query + {UseStandardSQL: true}, // UseStandardSQL without query + // read-only fields + {FullID: "x"}, + {Type: "x"}, + {CreationTime: aTime}, + {LastModifiedTime: aTime}, + {NumBytes: 1}, + {NumRows: 1}, + {StreamingBuffer: &StreamingBuffer{}}, + {ETag: "x"}, + } { + _, err := in.toBQ() + if err == nil { + t.Errorf("%+v: got nil, want error", in) + } + } +} + +func TestTableMetadataToUpdateToBQ(t *testing.T) { + aTime := time.Date(2017, 1, 26, 0, 0, 0, 0, time.Local) + for _, test := range []struct { + tm TableMetadataToUpdate + want *bq.Table + }{ + { + tm: TableMetadataToUpdate{}, + want: &bq.Table{}, + }, + { + tm: TableMetadataToUpdate{ + Description: "d", + Name: "n", + }, + want: &bq.Table{ + Description: "d", + FriendlyName: "n", + ForceSendFields: []string{"Description", "FriendlyName"}, + }, + }, + { + tm: TableMetadataToUpdate{ + Schema: Schema{fieldSchema("desc", "name", "STRING", false, true)}, + ExpirationTime: aTime, + }, + want: &bq.Table{ + Schema: &bq.TableSchema{ + Fields: []*bq.TableFieldSchema{ + bqTableFieldSchema("desc", "name", "STRING", "REQUIRED"), + }, + }, + ExpirationTime: aTime.UnixNano() / 1e6, + ForceSendFields: []string{"Schema", "ExpirationTime"}, + }, + }, + { + tm: TableMetadataToUpdate{ViewQuery: "q"}, + want: &bq.Table{ + View: &bq.ViewDefinition{Query: "q", ForceSendFields: []string{"Query"}}, + }, + }, + { + tm: TableMetadataToUpdate{UseLegacySQL: false}, + want: &bq.Table{ + View: &bq.ViewDefinition{ + UseLegacySql: false, + ForceSendFields: []string{"UseLegacySql"}, + }, + }, + }, + { + tm: TableMetadataToUpdate{ViewQuery: "q", UseLegacySQL: true}, + want: &bq.Table{ + View: &bq.ViewDefinition{ + Query: "q", + UseLegacySql: true, + ForceSendFields: []string{"Query", "UseLegacySql"}, + }, + }, + }, + { + tm: func() (tm TableMetadataToUpdate) { + tm.SetLabel("L", "V") + tm.DeleteLabel("D") + return tm + }(), + want: &bq.Table{ + Labels: map[string]string{"L": "V"}, + NullFields: []string{"Labels.D"}, + }, + }, + } { + got := test.tm.toBQ() + if !testutil.Equal(got, test.want) { + t.Errorf("%+v:\ngot %+v\nwant %+v", test.tm, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/uploader.go b/vendor/cloud.google.com/go/bigquery/uploader.go new file mode 100644 index 0000000000..a432ce588b --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/uploader.go @@ -0,0 +1,231 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "fmt" + "reflect" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + bq "google.golang.org/api/bigquery/v2" +) + +// An Uploader does streaming inserts into a BigQuery table. +// It is safe for concurrent use. +type Uploader struct { + t *Table + + // SkipInvalidRows causes rows containing invalid data to be silently + // ignored. The default value is false, which causes the entire request to + // fail if there is an attempt to insert an invalid row. + SkipInvalidRows bool + + // IgnoreUnknownValues causes values not matching the schema to be ignored. + // The default value is false, which causes records containing such values + // to be treated as invalid records. + IgnoreUnknownValues bool + + // A TableTemplateSuffix allows Uploaders to create tables automatically. + // + // Experimental: this option is experimental and may be modified or removed in future versions, + // regardless of any other documented package stability guarantees. + // + // When you specify a suffix, the table you upload data to + // will be used as a template for creating a new table, with the same schema, + // called + . + // + // More information is available at + // https://cloud.google.com/bigquery/streaming-data-into-bigquery#template-tables + TableTemplateSuffix string +} + +// Uploader returns an Uploader that can be used to append rows to t. +// The returned Uploader may optionally be further configured before its Put method is called. +// +// To stream rows into a date-partitioned table at a particular date, add the +// $yyyymmdd suffix to the table name when constructing the Table. +func (t *Table) Uploader() *Uploader { + return &Uploader{t: t} +} + +// Put uploads one or more rows to the BigQuery service. +// +// If src is ValueSaver, then its Save method is called to produce a row for uploading. +// +// If src is a struct or pointer to a struct, then a schema is inferred from it +// and used to create a StructSaver. The InsertID of the StructSaver will be +// empty. +// +// If src is a slice of ValueSavers, structs, or struct pointers, then each +// element of the slice is treated as above, and multiple rows are uploaded. +// +// Put returns a PutMultiError if one or more rows failed to be uploaded. +// The PutMultiError contains a RowInsertionError for each failed row. +// +// Put will retry on temporary errors (see +// https://cloud.google.com/bigquery/troubleshooting-errors). This can result +// in duplicate rows if you do not use insert IDs. Also, if the error persists, +// the call will run indefinitely. Pass a context with a timeout to prevent +// hanging calls. +func (u *Uploader) Put(ctx context.Context, src interface{}) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Uploader.Put") + defer func() { trace.EndSpan(ctx, err) }() + + savers, err := valueSavers(src) + if err != nil { + return err + } + return u.putMulti(ctx, savers) +} + +func valueSavers(src interface{}) ([]ValueSaver, error) { + saver, ok, err := toValueSaver(src) + if err != nil { + return nil, err + } + if ok { + return []ValueSaver{saver}, nil + } + srcVal := reflect.ValueOf(src) + if srcVal.Kind() != reflect.Slice { + return nil, fmt.Errorf("%T is not a ValueSaver, struct, struct pointer, or slice", src) + + } + var savers []ValueSaver + for i := 0; i < srcVal.Len(); i++ { + s := srcVal.Index(i).Interface() + saver, ok, err := toValueSaver(s) + if err != nil { + return nil, err + } + if !ok { + return nil, fmt.Errorf("src[%d] has type %T, which is not a ValueSaver, struct or struct pointer", i, s) + } + savers = append(savers, saver) + } + return savers, nil +} + +// Make a ValueSaver from x, which must implement ValueSaver already +// or be a struct or pointer to struct. +func toValueSaver(x interface{}) (ValueSaver, bool, error) { + if _, ok := x.(StructSaver); ok { + return nil, false, errors.New("bigquery: use &StructSaver, not StructSaver") + } + var insertID string + // Handle StructSavers specially so we can infer the schema if necessary. + if ss, ok := x.(*StructSaver); ok && ss.Schema == nil { + x = ss.Struct + insertID = ss.InsertID + // Fall through so we can infer the schema. + } + if saver, ok := x.(ValueSaver); ok { + return saver, ok, nil + } + v := reflect.ValueOf(x) + // Support Put with []interface{} + if v.Kind() == reflect.Interface { + v = v.Elem() + } + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return nil, false, nil + } + schema, err := inferSchemaReflectCached(v.Type()) + if err != nil { + return nil, false, err + } + return &StructSaver{ + Struct: x, + InsertID: insertID, + Schema: schema, + }, true, nil +} + +func (u *Uploader) putMulti(ctx context.Context, src []ValueSaver) error { + req, err := u.newInsertRequest(src) + if err != nil { + return err + } + if req == nil { + return nil + } + call := u.t.c.bqs.Tabledata.InsertAll(u.t.ProjectID, u.t.DatasetID, u.t.TableID, req) + call = call.Context(ctx) + setClientHeader(call.Header()) + var res *bq.TableDataInsertAllResponse + err = runWithRetry(ctx, func() (err error) { + res, err = call.Do() + return err + }) + if err != nil { + return err + } + return handleInsertErrors(res.InsertErrors, req.Rows) +} + +func (u *Uploader) newInsertRequest(savers []ValueSaver) (*bq.TableDataInsertAllRequest, error) { + if savers == nil { // If there are no rows, do nothing. + return nil, nil + } + req := &bq.TableDataInsertAllRequest{ + TemplateSuffix: u.TableTemplateSuffix, + IgnoreUnknownValues: u.IgnoreUnknownValues, + SkipInvalidRows: u.SkipInvalidRows, + } + for _, saver := range savers { + row, insertID, err := saver.Save() + if err != nil { + return nil, err + } + if insertID == "" { + insertID = randomIDFn() + } + m := make(map[string]bq.JsonValue) + for k, v := range row { + m[k] = bq.JsonValue(v) + } + req.Rows = append(req.Rows, &bq.TableDataInsertAllRequestRows{ + InsertId: insertID, + Json: m, + }) + } + return req, nil +} + +func handleInsertErrors(ierrs []*bq.TableDataInsertAllResponseInsertErrors, rows []*bq.TableDataInsertAllRequestRows) error { + if len(ierrs) == 0 { + return nil + } + var errs PutMultiError + for _, e := range ierrs { + if int(e.Index) > len(rows) { + return fmt.Errorf("internal error: unexpected row index: %v", e.Index) + } + rie := RowInsertionError{ + InsertID: rows[e.Index].InsertId, + RowIndex: int(e.Index), + } + for _, errp := range e.Errors { + rie.Errors = append(rie.Errors, bqToError(errp)) + } + errs = append(errs, rie) + } + return errs +} diff --git a/vendor/cloud.google.com/go/bigquery/uploader_test.go b/vendor/cloud.google.com/go/bigquery/uploader_test.go new file mode 100644 index 0000000000..078db1891b --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/uploader_test.go @@ -0,0 +1,211 @@ +// Copyright 2015 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 bigquery + +import ( + "errors" + "strconv" + "testing" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/internal/pretty" + "cloud.google.com/go/internal/testutil" + bq "google.golang.org/api/bigquery/v2" +) + +type testSaver struct { + row map[string]Value + insertID string + err error +} + +func (ts testSaver) Save() (map[string]Value, string, error) { + return ts.row, ts.insertID, ts.err +} + +func TestNewInsertRequest(t *testing.T) { + prev := randomIDFn + n := 0 + randomIDFn = func() string { n++; return strconv.Itoa(n) } + defer func() { randomIDFn = prev }() + + tests := []struct { + ul *Uploader + savers []ValueSaver + req *bq.TableDataInsertAllRequest + }{ + { + ul: &Uploader{}, + savers: nil, + req: nil, + }, + { + ul: &Uploader{}, + savers: []ValueSaver{ + testSaver{row: map[string]Value{"one": 1}}, + testSaver{row: map[string]Value{"two": 2}}, + }, + req: &bq.TableDataInsertAllRequest{ + Rows: []*bq.TableDataInsertAllRequestRows{ + {InsertId: "1", Json: map[string]bq.JsonValue{"one": 1}}, + {InsertId: "2", Json: map[string]bq.JsonValue{"two": 2}}, + }, + }, + }, + { + ul: &Uploader{ + TableTemplateSuffix: "suffix", + IgnoreUnknownValues: true, + SkipInvalidRows: true, + }, + savers: []ValueSaver{ + testSaver{insertID: "a", row: map[string]Value{"one": 1}}, + testSaver{insertID: "", row: map[string]Value{"two": 2}}, + }, + req: &bq.TableDataInsertAllRequest{ + Rows: []*bq.TableDataInsertAllRequestRows{ + {InsertId: "a", Json: map[string]bq.JsonValue{"one": 1}}, + {InsertId: "3", Json: map[string]bq.JsonValue{"two": 2}}, + }, + TemplateSuffix: "suffix", + SkipInvalidRows: true, + IgnoreUnknownValues: true, + }, + }, + } + for i, tc := range tests { + got, err := tc.ul.newInsertRequest(tc.savers) + if err != nil { + t.Fatal(err) + } + want := tc.req + if !testutil.Equal(got, want) { + t.Errorf("%d: %#v: got %#v, want %#v", i, tc.ul, got, want) + } + } +} + +func TestNewInsertRequestErrors(t *testing.T) { + var u Uploader + _, err := u.newInsertRequest([]ValueSaver{testSaver{err: errors.New("!")}}) + if err == nil { + t.Error("got nil, want error") + } +} + +func TestHandleInsertErrors(t *testing.T) { + rows := []*bq.TableDataInsertAllRequestRows{ + {InsertId: "a"}, + {InsertId: "b"}, + } + for _, test := range []struct { + in []*bq.TableDataInsertAllResponseInsertErrors + want error + }{ + { + in: nil, + want: nil, + }, + { + in: []*bq.TableDataInsertAllResponseInsertErrors{{Index: 1}}, + want: PutMultiError{RowInsertionError{InsertID: "b", RowIndex: 1}}, + }, + { + in: []*bq.TableDataInsertAllResponseInsertErrors{{Index: 1}}, + want: PutMultiError{RowInsertionError{InsertID: "b", RowIndex: 1}}, + }, + { + in: []*bq.TableDataInsertAllResponseInsertErrors{ + {Errors: []*bq.ErrorProto{{Message: "m0"}}, Index: 0}, + {Errors: []*bq.ErrorProto{{Message: "m1"}}, Index: 1}, + }, + want: PutMultiError{ + RowInsertionError{InsertID: "a", RowIndex: 0, Errors: []error{&Error{Message: "m0"}}}, + RowInsertionError{InsertID: "b", RowIndex: 1, Errors: []error{&Error{Message: "m1"}}}, + }, + }, + } { + got := handleInsertErrors(test.in, rows) + if !testutil.Equal(got, test.want) { + t.Errorf("%#v:\ngot\n%#v\nwant\n%#v", test.in, got, test.want) + } + } +} + +func TestValueSavers(t *testing.T) { + ts := &testSaver{} + type T struct{ I int } + schema, err := InferSchema(T{}) + if err != nil { + t.Fatal(err) + } + for _, test := range []struct { + in interface{} + want []ValueSaver + }{ + {[]interface{}(nil), nil}, + {[]interface{}{}, nil}, + {ts, []ValueSaver{ts}}, + {T{I: 1}, []ValueSaver{&StructSaver{Schema: schema, Struct: T{I: 1}}}}, + {[]ValueSaver{ts, ts}, []ValueSaver{ts, ts}}, + {[]interface{}{ts, ts}, []ValueSaver{ts, ts}}, + {[]T{{I: 1}, {I: 2}}, []ValueSaver{ + &StructSaver{Schema: schema, Struct: T{I: 1}}, + &StructSaver{Schema: schema, Struct: T{I: 2}}, + }}, + {[]interface{}{T{I: 1}, &T{I: 2}}, []ValueSaver{ + &StructSaver{Schema: schema, Struct: T{I: 1}}, + &StructSaver{Schema: schema, Struct: &T{I: 2}}, + }}, + {&StructSaver{Struct: T{I: 3}, InsertID: "foo"}, + []ValueSaver{ + &StructSaver{Schema: schema, Struct: T{I: 3}, InsertID: "foo"}, + }}, + } { + got, err := valueSavers(test.in) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, test.want, cmp.AllowUnexported(testSaver{})) { + t.Errorf("%+v: got %v, want %v", test.in, pretty.Value(got), pretty.Value(test.want)) + } + // Make sure Save is successful. + for i, vs := range got { + _, _, err := vs.Save() + if err != nil { + t.Fatalf("%+v, #%d: got error %v, want nil", test.in, i, err) + } + } + } +} + +func TestValueSaversErrors(t *testing.T) { + inputs := []interface{}{ + nil, + 1, + []int{1, 2}, + []interface{}{ + testSaver{row: map[string]Value{"one": 1}, insertID: "a"}, + 1, + }, + StructSaver{}, + } + for _, in := range inputs { + if _, err := valueSavers(in); err == nil { + t.Errorf("%#v: got nil, want error", in) + } + } +} diff --git a/vendor/cloud.google.com/go/bigquery/value.go b/vendor/cloud.google.com/go/bigquery/value.go new file mode 100644 index 0000000000..c6665ccb52 --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/value.go @@ -0,0 +1,871 @@ +// Copyright 2015 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 bigquery + +import ( + "encoding/base64" + "errors" + "fmt" + "math" + "math/big" + "reflect" + "strconv" + "strings" + "time" + + "cloud.google.com/go/civil" + + bq "google.golang.org/api/bigquery/v2" +) + +// Value stores the contents of a single cell from a BigQuery result. +type Value interface{} + +// ValueLoader stores a slice of Values representing a result row from a Read operation. +// See RowIterator.Next for more information. +type ValueLoader interface { + Load(v []Value, s Schema) error +} + +// valueList converts a []Value to implement ValueLoader. +type valueList []Value + +// Load stores a sequence of values in a valueList. +// It resets the slice length to zero, then appends each value to it. +func (vs *valueList) Load(v []Value, _ Schema) error { + *vs = append((*vs)[:0], v...) + return nil +} + +// valueMap converts a map[string]Value to implement ValueLoader. +type valueMap map[string]Value + +// Load stores a sequence of values in a valueMap. +func (vm *valueMap) Load(v []Value, s Schema) error { + if *vm == nil { + *vm = map[string]Value{} + } + loadMap(*vm, v, s) + return nil +} + +func loadMap(m map[string]Value, vals []Value, s Schema) { + for i, f := range s { + val := vals[i] + var v interface{} + switch { + case val == nil: + v = val + case f.Schema == nil: + v = val + case !f.Repeated: + m2 := map[string]Value{} + loadMap(m2, val.([]Value), f.Schema) + v = m2 + default: // repeated and nested + sval := val.([]Value) + vs := make([]Value, len(sval)) + for j, e := range sval { + m2 := map[string]Value{} + loadMap(m2, e.([]Value), f.Schema) + vs[j] = m2 + } + v = vs + } + + m[f.Name] = v + } +} + +type structLoader struct { + typ reflect.Type // type of struct + err error + ops []structLoaderOp + + vstructp reflect.Value // pointer to current struct value; changed by set +} + +// A setFunc is a function that sets a struct field or slice/array +// element to a value. +type setFunc func(v reflect.Value, val interface{}) error + +// A structLoaderOp instructs the loader to set a struct field to a row value. +type structLoaderOp struct { + fieldIndex []int + valueIndex int + setFunc setFunc + repeated bool +} + +var errNoNulls = errors.New("bigquery: NULL values cannot be read into structs") + +func setAny(v reflect.Value, x interface{}) error { + if x == nil { + return errNoNulls + } + v.Set(reflect.ValueOf(x)) + return nil +} + +func setInt(v reflect.Value, x interface{}) error { + if x == nil { + return errNoNulls + } + xx := x.(int64) + if v.OverflowInt(xx) { + return fmt.Errorf("bigquery: value %v overflows struct field of type %v", xx, v.Type()) + } + v.SetInt(xx) + return nil +} + +func setUint(v reflect.Value, x interface{}) error { + if x == nil { + return errNoNulls + } + xx := x.(int64) + if xx < 0 || v.OverflowUint(uint64(xx)) { + return fmt.Errorf("bigquery: value %v overflows struct field of type %v", xx, v.Type()) + } + v.SetUint(uint64(xx)) + return nil +} + +func setFloat(v reflect.Value, x interface{}) error { + if x == nil { + return errNoNulls + } + xx := x.(float64) + if v.OverflowFloat(xx) { + return fmt.Errorf("bigquery: value %v overflows struct field of type %v", xx, v.Type()) + } + v.SetFloat(xx) + return nil +} + +func setBool(v reflect.Value, x interface{}) error { + if x == nil { + return errNoNulls + } + v.SetBool(x.(bool)) + return nil +} + +func setString(v reflect.Value, x interface{}) error { + if x == nil { + return errNoNulls + } + v.SetString(x.(string)) + return nil +} + +func setBytes(v reflect.Value, x interface{}) error { + if x == nil { + v.SetBytes(nil) + } else { + v.SetBytes(x.([]byte)) + } + return nil +} + +func setNull(v reflect.Value, x interface{}, build func() interface{}) error { + if x == nil { + v.Set(reflect.Zero(v.Type())) + } else { + n := build() + v.Set(reflect.ValueOf(n)) + } + return nil +} + +// set remembers a value for the next call to Load. The value must be +// a pointer to a struct. (This is checked in RowIterator.Next.) +func (sl *structLoader) set(structp interface{}, schema Schema) error { + if sl.err != nil { + return sl.err + } + sl.vstructp = reflect.ValueOf(structp) + typ := sl.vstructp.Type().Elem() + if sl.typ == nil { + // First call: remember the type and compile the schema. + sl.typ = typ + ops, err := compileToOps(typ, schema) + if err != nil { + sl.err = err + return err + } + sl.ops = ops + } else if sl.typ != typ { + return fmt.Errorf("bigquery: struct type changed from %s to %s", sl.typ, typ) + } + return nil +} + +// compileToOps produces a sequence of operations that will set the fields of a +// value of structType to the contents of a row with schema. +func compileToOps(structType reflect.Type, schema Schema) ([]structLoaderOp, error) { + var ops []structLoaderOp + fields, err := fieldCache.Fields(structType) + if err != nil { + return nil, err + } + for i, schemaField := range schema { + // Look for an exported struct field with the same name as the schema + // field, ignoring case (BigQuery column names are case-insensitive, + // and we want to act like encoding/json anyway). + structField := fields.Match(schemaField.Name) + if structField == nil { + // Ignore schema fields with no corresponding struct field. + continue + } + op := structLoaderOp{ + fieldIndex: structField.Index, + valueIndex: i, + } + t := structField.Type + if schemaField.Repeated { + if t.Kind() != reflect.Slice && t.Kind() != reflect.Array { + return nil, fmt.Errorf("bigquery: repeated schema field %s requires slice or array, but struct field %s has type %s", + schemaField.Name, structField.Name, t) + } + t = t.Elem() + op.repeated = true + } + if schemaField.Type == RecordFieldType { + // Field can be a struct or a pointer to a struct. + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("bigquery: field %s has type %s, expected struct or *struct", + structField.Name, structField.Type) + } + nested, err := compileToOps(t, schemaField.Schema) + if err != nil { + return nil, err + } + op.setFunc = func(v reflect.Value, val interface{}) error { + return setNested(nested, v, val) + } + } else { + op.setFunc = determineSetFunc(t, schemaField.Type) + if op.setFunc == nil { + return nil, fmt.Errorf("bigquery: schema field %s of type %s is not assignable to struct field %s of type %s", + schemaField.Name, schemaField.Type, structField.Name, t) + } + } + ops = append(ops, op) + } + return ops, nil +} + +// determineSetFunc chooses the best function for setting a field of type ftype +// to a value whose schema field type is stype. It returns nil if stype +// is not assignable to ftype. +// determineSetFunc considers only basic types. See compileToOps for +// handling of repetition and nesting. +func determineSetFunc(ftype reflect.Type, stype FieldType) setFunc { + switch stype { + case StringFieldType: + if ftype.Kind() == reflect.String { + return setString + } + if ftype == typeOfNullString { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullString{StringVal: x.(string), Valid: true} + }) + } + } + + case BytesFieldType: + if ftype == typeOfByteSlice { + return setBytes + } + + case IntegerFieldType: + if isSupportedUintType(ftype) { + return setUint + } else if isSupportedIntType(ftype) { + return setInt + } + if ftype == typeOfNullInt64 { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullInt64{Int64: x.(int64), Valid: true} + }) + } + } + + case FloatFieldType: + switch ftype.Kind() { + case reflect.Float32, reflect.Float64: + return setFloat + } + if ftype == typeOfNullFloat64 { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullFloat64{Float64: x.(float64), Valid: true} + }) + } + } + + case BooleanFieldType: + if ftype.Kind() == reflect.Bool { + return setBool + } + if ftype == typeOfNullBool { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullBool{Bool: x.(bool), Valid: true} + }) + } + } + + case TimestampFieldType: + if ftype == typeOfGoTime { + return setAny + } + if ftype == typeOfNullTimestamp { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullTimestamp{Timestamp: x.(time.Time), Valid: true} + }) + } + } + + case DateFieldType: + if ftype == typeOfDate { + return setAny + } + if ftype == typeOfNullDate { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullDate{Date: x.(civil.Date), Valid: true} + }) + } + } + + case TimeFieldType: + if ftype == typeOfTime { + return setAny + } + if ftype == typeOfNullTime { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullTime{Time: x.(civil.Time), Valid: true} + }) + } + } + + case DateTimeFieldType: + if ftype == typeOfDateTime { + return setAny + } + if ftype == typeOfNullDateTime { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { + return NullDateTime{DateTime: x.(civil.DateTime), Valid: true} + }) + } + } + + case NumericFieldType: + if ftype == typeOfRat { + return func(v reflect.Value, x interface{}) error { + return setNull(v, x, func() interface{} { return x.(*big.Rat) }) + } + } + } + return nil +} + +func (sl *structLoader) Load(values []Value, _ Schema) error { + if sl.err != nil { + return sl.err + } + return runOps(sl.ops, sl.vstructp.Elem(), values) +} + +// runOps executes a sequence of ops, setting the fields of vstruct to the +// supplied values. +func runOps(ops []structLoaderOp, vstruct reflect.Value, values []Value) error { + for _, op := range ops { + field := vstruct.FieldByIndex(op.fieldIndex) + var err error + if op.repeated { + err = setRepeated(field, values[op.valueIndex].([]Value), op.setFunc) + } else { + err = op.setFunc(field, values[op.valueIndex]) + } + if err != nil { + return err + } + } + return nil +} + +func setNested(ops []structLoaderOp, v reflect.Value, val interface{}) error { + // v is either a struct or a pointer to a struct. + if v.Kind() == reflect.Ptr { + // If the value is nil, set the pointer to nil. + if val == nil { + v.Set(reflect.Zero(v.Type())) + return nil + } + // If the pointer is nil, set it to a zero struct value. + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + return runOps(ops, v, val.([]Value)) +} + +func setRepeated(field reflect.Value, vslice []Value, setElem setFunc) error { + vlen := len(vslice) + var flen int + switch field.Type().Kind() { + case reflect.Slice: + // Make a slice of the right size, avoiding allocation if possible. + switch { + case field.Len() < vlen: + field.Set(reflect.MakeSlice(field.Type(), vlen, vlen)) + case field.Len() > vlen: + field.SetLen(vlen) + } + flen = vlen + + case reflect.Array: + flen = field.Len() + if flen > vlen { + // Set extra elements to their zero value. + z := reflect.Zero(field.Type().Elem()) + for i := vlen; i < flen; i++ { + field.Index(i).Set(z) + } + } + default: + return fmt.Errorf("bigquery: impossible field type %s", field.Type()) + } + for i, val := range vslice { + if i < flen { // avoid writing past the end of a short array + if err := setElem(field.Index(i), val); err != nil { + return err + } + } + } + return nil +} + +// A ValueSaver returns a row of data to be inserted into a table. +type ValueSaver interface { + // Save returns a row to be inserted into a BigQuery table, represented + // as a map from field name to Value. + // If insertID is non-empty, BigQuery will use it to de-duplicate + // insertions of this row on a best-effort basis. + Save() (row map[string]Value, insertID string, err error) +} + +// ValuesSaver implements ValueSaver for a slice of Values. +type ValuesSaver struct { + Schema Schema + + // If non-empty, BigQuery will use InsertID to de-duplicate insertions + // of this row on a best-effort basis. + InsertID string + + Row []Value +} + +// Save implements ValueSaver. +func (vls *ValuesSaver) Save() (map[string]Value, string, error) { + m, err := valuesToMap(vls.Row, vls.Schema) + return m, vls.InsertID, err +} + +func valuesToMap(vs []Value, schema Schema) (map[string]Value, error) { + if len(vs) != len(schema) { + return nil, errors.New("Schema does not match length of row to be inserted") + } + + m := make(map[string]Value) + for i, fieldSchema := range schema { + if vs[i] == nil { + m[fieldSchema.Name] = nil + continue + } + if fieldSchema.Type != RecordFieldType { + m[fieldSchema.Name] = toUploadValue(vs[i], fieldSchema) + continue + } + // Nested record, possibly repeated. + vals, ok := vs[i].([]Value) + if !ok { + return nil, errors.New("nested record is not a []Value") + } + if !fieldSchema.Repeated { + value, err := valuesToMap(vals, fieldSchema.Schema) + if err != nil { + return nil, err + } + m[fieldSchema.Name] = value + continue + } + // A repeated nested field is converted into a slice of maps. + var maps []Value + for _, v := range vals { + sv, ok := v.([]Value) + if !ok { + return nil, errors.New("nested record in slice is not a []Value") + } + value, err := valuesToMap(sv, fieldSchema.Schema) + if err != nil { + return nil, err + } + maps = append(maps, value) + } + m[fieldSchema.Name] = maps + } + return m, nil +} + +// StructSaver implements ValueSaver for a struct. +// The struct is converted to a map of values by using the values of struct +// fields corresponding to schema fields. Additional and missing +// fields are ignored, as are nested struct pointers that are nil. +type StructSaver struct { + // Schema determines what fields of the struct are uploaded. It should + // match the table's schema. + // Schema is optional for StructSavers that are passed to Uploader.Put. + Schema Schema + + // If non-empty, BigQuery will use InsertID to de-duplicate insertions + // of this row on a best-effort basis. + InsertID string + + // Struct should be a struct or a pointer to a struct. + Struct interface{} +} + +// Save implements ValueSaver. +func (ss *StructSaver) Save() (row map[string]Value, insertID string, err error) { + vstruct := reflect.ValueOf(ss.Struct) + row, err = structToMap(vstruct, ss.Schema) + if err != nil { + return nil, "", err + } + return row, ss.InsertID, nil +} + +func structToMap(vstruct reflect.Value, schema Schema) (map[string]Value, error) { + if vstruct.Kind() == reflect.Ptr { + vstruct = vstruct.Elem() + } + if !vstruct.IsValid() { + return nil, nil + } + m := map[string]Value{} + if vstruct.Kind() != reflect.Struct { + return nil, fmt.Errorf("bigquery: type is %s, need struct or struct pointer", vstruct.Type()) + } + fields, err := fieldCache.Fields(vstruct.Type()) + if err != nil { + return nil, err + } + for _, schemaField := range schema { + // Look for an exported struct field with the same name as the schema + // field, ignoring case. + structField := fields.Match(schemaField.Name) + if structField == nil { + continue + } + val, err := structFieldToUploadValue(vstruct.FieldByIndex(structField.Index), schemaField) + if err != nil { + return nil, err + } + // Add the value to the map, unless it is nil. + if val != nil { + m[schemaField.Name] = val + } + } + return m, nil +} + +// structFieldToUploadValue converts a struct field to a value suitable for ValueSaver.Save, using +// the schemaField as a guide. +// structFieldToUploadValue is careful to return a true nil interface{} when needed, so its +// caller can easily identify a nil value. +func structFieldToUploadValue(vfield reflect.Value, schemaField *FieldSchema) (interface{}, error) { + if schemaField.Repeated && (vfield.Kind() != reflect.Slice && vfield.Kind() != reflect.Array) { + return nil, fmt.Errorf("bigquery: repeated schema field %s requires slice or array, but value has type %s", + schemaField.Name, vfield.Type()) + } + + // A non-nested field can be represented by its Go value, except for some types. + if schemaField.Type != RecordFieldType { + return toUploadValueReflect(vfield, schemaField), nil + } + // A non-repeated nested field is converted into a map[string]Value. + if !schemaField.Repeated { + m, err := structToMap(vfield, schemaField.Schema) + if err != nil { + return nil, err + } + if m == nil { + return nil, nil + } + return m, nil + } + // A repeated nested field is converted into a slice of maps. + if vfield.Len() == 0 { + return nil, nil + } + var vals []Value + for i := 0; i < vfield.Len(); i++ { + m, err := structToMap(vfield.Index(i), schemaField.Schema) + if err != nil { + return nil, err + } + vals = append(vals, m) + } + return vals, nil +} + +func toUploadValue(val interface{}, fs *FieldSchema) interface{} { + if fs.Type == TimeFieldType || fs.Type == DateTimeFieldType || fs.Type == NumericFieldType { + return toUploadValueReflect(reflect.ValueOf(val), fs) + } + return val +} + +func toUploadValueReflect(v reflect.Value, fs *FieldSchema) interface{} { + switch fs.Type { + case TimeFieldType: + if v.Type() == typeOfNullTime { + return v.Interface() + } + return formatUploadValue(v, fs, func(v reflect.Value) string { + return CivilTimeString(v.Interface().(civil.Time)) + }) + case DateTimeFieldType: + if v.Type() == typeOfNullDateTime { + return v.Interface() + } + return formatUploadValue(v, fs, func(v reflect.Value) string { + return CivilDateTimeString(v.Interface().(civil.DateTime)) + }) + case NumericFieldType: + if r, ok := v.Interface().(*big.Rat); ok && r == nil { + return nil + } + return formatUploadValue(v, fs, func(v reflect.Value) string { + return NumericString(v.Interface().(*big.Rat)) + }) + default: + if !fs.Repeated || v.Len() > 0 { + return v.Interface() + } + // The service treats a null repeated field as an error. Return + // nil to omit the field entirely. + return nil + } +} + +func formatUploadValue(v reflect.Value, fs *FieldSchema, cvt func(reflect.Value) string) interface{} { + if !fs.Repeated { + return cvt(v) + } + if v.Len() == 0 { + return nil + } + s := make([]string, v.Len()) + for i := 0; i < v.Len(); i++ { + s[i] = cvt(v.Index(i)) + } + return s +} + +// CivilTimeString returns a string representing a civil.Time in a format compatible +// with BigQuery SQL. It rounds the time to the nearest microsecond and returns a +// string with six digits of sub-second precision. +// +// Use CivilTimeString when using civil.Time in DML, for example in INSERT +// statements. +func CivilTimeString(t civil.Time) string { + if t.Nanosecond == 0 { + return t.String() + } else { + micro := (t.Nanosecond + 500) / 1000 // round to nearest microsecond + t.Nanosecond = 0 + return t.String() + fmt.Sprintf(".%06d", micro) + } +} + +// CivilDateTimeString returns a string representing a civil.DateTime in a format compatible +// with BigQuery SQL. It separate the date and time with a space, and formats the time +// with CivilTimeString. +// +// Use CivilDateTimeString when using civil.DateTime in DML, for example in INSERT +// statements. +func CivilDateTimeString(dt civil.DateTime) string { + return dt.Date.String() + " " + CivilTimeString(dt.Time) +} + +// parseCivilDateTime parses a date-time represented in a BigQuery SQL +// compatible format and returns a civil.DateTime. +func parseCivilDateTime(s string) (civil.DateTime, error) { + parts := strings.Fields(s) + if len(parts) != 2 { + return civil.DateTime{}, fmt.Errorf("bigquery: bad DATETIME value %q", s) + } + return civil.ParseDateTime(parts[0] + "T" + parts[1]) +} + +const ( + // The maximum number of digits in a NUMERIC value. + NumericPrecisionDigits = 38 + + // The maximum number of digits after the decimal point in a NUMERIC value. + NumericScaleDigits = 9 +) + +// NumericString returns a string representing a *big.Rat in a format compatible +// with BigQuery SQL. It returns a floating-point literal with 9 digits +// after the decimal point. +func NumericString(r *big.Rat) string { + return r.FloatString(NumericScaleDigits) +} + +// convertRows converts a series of TableRows into a series of Value slices. +// schema is used to interpret the data from rows; its length must match the +// length of each row. +func convertRows(rows []*bq.TableRow, schema Schema) ([][]Value, error) { + var rs [][]Value + for _, r := range rows { + row, err := convertRow(r, schema) + if err != nil { + return nil, err + } + rs = append(rs, row) + } + return rs, nil +} + +func convertRow(r *bq.TableRow, schema Schema) ([]Value, error) { + if len(schema) != len(r.F) { + return nil, errors.New("schema length does not match row length") + } + var values []Value + for i, cell := range r.F { + fs := schema[i] + v, err := convertValue(cell.V, fs.Type, fs.Schema) + if err != nil { + return nil, err + } + values = append(values, v) + } + return values, nil +} + +func convertValue(val interface{}, typ FieldType, schema Schema) (Value, error) { + switch val := val.(type) { + case nil: + return nil, nil + case []interface{}: + return convertRepeatedRecord(val, typ, schema) + case map[string]interface{}: + return convertNestedRecord(val, schema) + case string: + return convertBasicType(val, typ) + default: + return nil, fmt.Errorf("got value %v; expected a value of type %s", val, typ) + } +} + +func convertRepeatedRecord(vals []interface{}, typ FieldType, schema Schema) (Value, error) { + var values []Value + for _, cell := range vals { + // each cell contains a single entry, keyed by "v" + val := cell.(map[string]interface{})["v"] + v, err := convertValue(val, typ, schema) + if err != nil { + return nil, err + } + values = append(values, v) + } + return values, nil +} + +func convertNestedRecord(val map[string]interface{}, schema Schema) (Value, error) { + // convertNestedRecord is similar to convertRow, as a record has the same structure as a row. + + // Nested records are wrapped in a map with a single key, "f". + record := val["f"].([]interface{}) + if len(record) != len(schema) { + return nil, errors.New("schema length does not match record length") + } + + var values []Value + for i, cell := range record { + // each cell contains a single entry, keyed by "v" + val := cell.(map[string]interface{})["v"] + fs := schema[i] + v, err := convertValue(val, fs.Type, fs.Schema) + if err != nil { + return nil, err + } + values = append(values, v) + } + return values, nil +} + +// convertBasicType returns val as an interface with a concrete type specified by typ. +func convertBasicType(val string, typ FieldType) (Value, error) { + switch typ { + case StringFieldType: + return val, nil + case BytesFieldType: + return base64.StdEncoding.DecodeString(val) + case IntegerFieldType: + return strconv.ParseInt(val, 10, 64) + case FloatFieldType: + return strconv.ParseFloat(val, 64) + case BooleanFieldType: + return strconv.ParseBool(val) + case TimestampFieldType: + f, err := strconv.ParseFloat(val, 64) + if err != nil { + return nil, err + } + secs := math.Trunc(f) + nanos := (f - secs) * 1e9 + return Value(time.Unix(int64(secs), int64(nanos)).UTC()), nil + case DateFieldType: + return civil.ParseDate(val) + case TimeFieldType: + return civil.ParseTime(val) + case DateTimeFieldType: + return civil.ParseDateTime(val) + case NumericFieldType: + r, ok := (&big.Rat{}).SetString(val) + if !ok { + return nil, fmt.Errorf("bigquery: invalid NUMERIC value %q", val) + } + return Value(r), nil + default: + return nil, fmt.Errorf("unrecognized type: %s", typ) + } +} diff --git a/vendor/cloud.google.com/go/bigquery/value_test.go b/vendor/cloud.google.com/go/bigquery/value_test.go new file mode 100644 index 0000000000..863ec0fe9c --- /dev/null +++ b/vendor/cloud.google.com/go/bigquery/value_test.go @@ -0,0 +1,1210 @@ +// Copyright 2015 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 bigquery + +import ( + "encoding/base64" + "fmt" + "math" + "math/big" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/civil" + "cloud.google.com/go/internal/testutil" + + bq "google.golang.org/api/bigquery/v2" +) + +func TestConvertBasicValues(t *testing.T) { + schema := Schema{ + {Type: StringFieldType}, + {Type: IntegerFieldType}, + {Type: FloatFieldType}, + {Type: BooleanFieldType}, + {Type: BytesFieldType}, + {Type: NumericFieldType}, + } + row := &bq.TableRow{ + F: []*bq.TableCell{ + {V: "a"}, + {V: "1"}, + {V: "1.2"}, + {V: "true"}, + {V: base64.StdEncoding.EncodeToString([]byte("foo"))}, + {V: "123.123456789"}, + }, + } + got, err := convertRow(row, schema) + if err != nil { + t.Fatalf("error converting: %v", err) + } + + want := []Value{"a", int64(1), 1.2, true, []byte("foo"), big.NewRat(123123456789, 1e9)} + if !testutil.Equal(got, want) { + t.Errorf("converting basic values: got:\n%v\nwant:\n%v", got, want) + } +} + +func TestConvertTime(t *testing.T) { + schema := Schema{ + {Type: TimestampFieldType}, + {Type: DateFieldType}, + {Type: TimeFieldType}, + {Type: DateTimeFieldType}, + } + ts := testTimestamp.Round(time.Millisecond) + row := &bq.TableRow{ + F: []*bq.TableCell{ + {V: fmt.Sprintf("%.10f", float64(ts.UnixNano())/1e9)}, + {V: testDate.String()}, + {V: testTime.String()}, + {V: testDateTime.String()}, + }, + } + got, err := convertRow(row, schema) + if err != nil { + t.Fatalf("error converting: %v", err) + } + want := []Value{ts, testDate, testTime, testDateTime} + for i, g := range got { + w := want[i] + if !testutil.Equal(g, w) { + t.Errorf("#%d: got:\n%v\nwant:\n%v", i, g, w) + } + } + if got[0].(time.Time).Location() != time.UTC { + t.Errorf("expected time zone UTC: got:\n%v", got) + } +} + +func TestConvertSmallTimes(t *testing.T) { + for _, year := range []int{1600, 1066, 1} { + want := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC) + s := fmt.Sprintf("%.10f", float64(want.Unix())) + got, err := convertBasicType(s, TimestampFieldType) + if err != nil { + t.Fatal(err) + } + if !got.(time.Time).Equal(want) { + t.Errorf("got %v, want %v", got, want) + } + } +} + +func TestConvertNullValues(t *testing.T) { + schema := Schema{{Type: StringFieldType}} + row := &bq.TableRow{ + F: []*bq.TableCell{ + {V: nil}, + }, + } + got, err := convertRow(row, schema) + if err != nil { + t.Fatalf("error converting: %v", err) + } + want := []Value{nil} + if !testutil.Equal(got, want) { + t.Errorf("converting null values: got:\n%v\nwant:\n%v", got, want) + } +} + +func TestBasicRepetition(t *testing.T) { + schema := Schema{ + {Type: IntegerFieldType, Repeated: true}, + } + row := &bq.TableRow{ + F: []*bq.TableCell{ + { + V: []interface{}{ + map[string]interface{}{ + "v": "1", + }, + map[string]interface{}{ + "v": "2", + }, + map[string]interface{}{ + "v": "3", + }, + }, + }, + }, + } + got, err := convertRow(row, schema) + if err != nil { + t.Fatalf("error converting: %v", err) + } + want := []Value{[]Value{int64(1), int64(2), int64(3)}} + if !testutil.Equal(got, want) { + t.Errorf("converting basic repeated values: got:\n%v\nwant:\n%v", got, want) + } +} + +func TestNestedRecordContainingRepetition(t *testing.T) { + schema := Schema{ + { + Type: RecordFieldType, + Schema: Schema{ + {Type: IntegerFieldType, Repeated: true}, + }, + }, + } + row := &bq.TableRow{ + F: []*bq.TableCell{ + { + V: map[string]interface{}{ + "f": []interface{}{ + map[string]interface{}{ + "v": []interface{}{ + map[string]interface{}{"v": "1"}, + map[string]interface{}{"v": "2"}, + map[string]interface{}{"v": "3"}, + }, + }, + }, + }, + }, + }, + } + + got, err := convertRow(row, schema) + if err != nil { + t.Fatalf("error converting: %v", err) + } + want := []Value{[]Value{[]Value{int64(1), int64(2), int64(3)}}} + if !testutil.Equal(got, want) { + t.Errorf("converting basic repeated values: got:\n%v\nwant:\n%v", got, want) + } +} + +func TestRepeatedRecordContainingRepetition(t *testing.T) { + schema := Schema{ + { + Type: RecordFieldType, + Repeated: true, + Schema: Schema{ + {Type: IntegerFieldType, Repeated: true}, + }, + }, + } + row := &bq.TableRow{F: []*bq.TableCell{ + { + V: []interface{}{ // repeated records. + map[string]interface{}{ // first record. + "v": map[string]interface{}{ // pointless single-key-map wrapper. + "f": []interface{}{ // list of record fields. + map[string]interface{}{ // only record (repeated ints) + "v": []interface{}{ // pointless wrapper. + map[string]interface{}{ + "v": "1", + }, + map[string]interface{}{ + "v": "2", + }, + map[string]interface{}{ + "v": "3", + }, + }, + }, + }, + }, + }, + map[string]interface{}{ // second record. + "v": map[string]interface{}{ + "f": []interface{}{ + map[string]interface{}{ + "v": []interface{}{ + map[string]interface{}{ + "v": "4", + }, + map[string]interface{}{ + "v": "5", + }, + map[string]interface{}{ + "v": "6", + }, + }, + }, + }, + }, + }, + }, + }, + }} + + got, err := convertRow(row, schema) + if err != nil { + t.Fatalf("error converting: %v", err) + } + want := []Value{ // the row is a list of length 1, containing an entry for the repeated record. + []Value{ // the repeated record is a list of length 2, containing an entry for each repetition. + []Value{ // the record is a list of length 1, containing an entry for the repeated integer field. + []Value{int64(1), int64(2), int64(3)}, // the repeated integer field is a list of length 3. + }, + []Value{ // second record + []Value{int64(4), int64(5), int64(6)}, + }, + }, + } + if !testutil.Equal(got, want) { + t.Errorf("converting repeated records with repeated values: got:\n%v\nwant:\n%v", got, want) + } +} + +func TestRepeatedRecordContainingRecord(t *testing.T) { + schema := Schema{ + { + Type: RecordFieldType, + Repeated: true, + Schema: Schema{ + { + Type: StringFieldType, + }, + { + Type: RecordFieldType, + Schema: Schema{ + {Type: IntegerFieldType}, + {Type: StringFieldType}, + }, + }, + }, + }, + } + row := &bq.TableRow{F: []*bq.TableCell{ + { + V: []interface{}{ // repeated records. + map[string]interface{}{ // first record. + "v": map[string]interface{}{ // pointless single-key-map wrapper. + "f": []interface{}{ // list of record fields. + map[string]interface{}{ // first record field (name) + "v": "first repeated record", + }, + map[string]interface{}{ // second record field (nested record). + "v": map[string]interface{}{ // pointless single-key-map wrapper. + "f": []interface{}{ // nested record fields + map[string]interface{}{ + "v": "1", + }, + map[string]interface{}{ + "v": "two", + }, + }, + }, + }, + }, + }, + }, + map[string]interface{}{ // second record. + "v": map[string]interface{}{ + "f": []interface{}{ + map[string]interface{}{ + "v": "second repeated record", + }, + map[string]interface{}{ + "v": map[string]interface{}{ + "f": []interface{}{ + map[string]interface{}{ + "v": "3", + }, + map[string]interface{}{ + "v": "four", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }} + + got, err := convertRow(row, schema) + if err != nil { + t.Fatalf("error converting: %v", err) + } + // TODO: test with flattenresults. + want := []Value{ // the row is a list of length 1, containing an entry for the repeated record. + []Value{ // the repeated record is a list of length 2, containing an entry for each repetition. + []Value{ // record contains a string followed by a nested record. + "first repeated record", + []Value{ + int64(1), + "two", + }, + }, + []Value{ // second record. + "second repeated record", + []Value{ + int64(3), + "four", + }, + }, + }, + } + if !testutil.Equal(got, want) { + t.Errorf("converting repeated records containing record : got:\n%v\nwant:\n%v", got, want) + } +} + +func TestConvertRowErrors(t *testing.T) { + // mismatched lengths + if _, err := convertRow(&bq.TableRow{F: []*bq.TableCell{{V: ""}}}, Schema{}); err == nil { + t.Error("got nil, want error") + } + v3 := map[string]interface{}{"v": 3} + for _, test := range []struct { + value interface{} + fs FieldSchema + }{ + {3, FieldSchema{Type: IntegerFieldType}}, // not a string + {[]interface{}{v3}, // not a string, repeated + FieldSchema{Type: IntegerFieldType, Repeated: true}}, + {map[string]interface{}{"f": []interface{}{v3}}, // not a string, nested + FieldSchema{Type: RecordFieldType, Schema: Schema{{Type: IntegerFieldType}}}}, + {map[string]interface{}{"f": []interface{}{v3}}, // wrong length, nested + FieldSchema{Type: RecordFieldType, Schema: Schema{}}}, + } { + _, err := convertRow( + &bq.TableRow{F: []*bq.TableCell{{V: test.value}}}, + Schema{&test.fs}) + if err == nil { + t.Errorf("value %v, fs %v: got nil, want error", test.value, test.fs) + } + } + + // bad field type + if _, err := convertBasicType("", FieldType("BAD")); err == nil { + t.Error("got nil, want error") + } +} + +func TestValuesSaverConvertsToMap(t *testing.T) { + testCases := []struct { + vs ValuesSaver + wantInsertID string + wantRow map[string]Value + }{ + { + vs: ValuesSaver{ + Schema: Schema{ + {Name: "intField", Type: IntegerFieldType}, + {Name: "strField", Type: StringFieldType}, + {Name: "dtField", Type: DateTimeFieldType}, + {Name: "nField", Type: NumericFieldType}, + }, + InsertID: "iid", + Row: []Value{1, "a", + civil.DateTime{ + Date: civil.Date{Year: 1, Month: 2, Day: 3}, + Time: civil.Time{Hour: 4, Minute: 5, Second: 6, Nanosecond: 7000}}, + big.NewRat(123456789000, 1e9), + }, + }, + wantInsertID: "iid", + wantRow: map[string]Value{ + "intField": 1, + "strField": "a", + "dtField": "0001-02-03 04:05:06.000007", + "nField": "123.456789000", + }, + }, + { + vs: ValuesSaver{ + Schema: Schema{ + {Name: "intField", Type: IntegerFieldType}, + { + Name: "recordField", + Type: RecordFieldType, + Schema: Schema{ + {Name: "nestedInt", Type: IntegerFieldType, Repeated: true}, + }, + }, + }, + InsertID: "iid", + Row: []Value{1, []Value{[]Value{2, 3}}}, + }, + wantInsertID: "iid", + wantRow: map[string]Value{ + "intField": 1, + "recordField": map[string]Value{ + "nestedInt": []Value{2, 3}, + }, + }, + }, + { // repeated nested field + vs: ValuesSaver{ + Schema: Schema{ + { + Name: "records", + Type: RecordFieldType, + Schema: Schema{ + {Name: "x", Type: IntegerFieldType}, + {Name: "y", Type: IntegerFieldType}, + }, + Repeated: true, + }, + }, + InsertID: "iid", + Row: []Value{ // a row is a []Value + []Value{ // repeated field's value is a []Value + []Value{1, 2}, // first record of the repeated field + []Value{3, 4}, // second record + }, + }, + }, + wantInsertID: "iid", + wantRow: map[string]Value{ + "records": []Value{ + map[string]Value{"x": 1, "y": 2}, + map[string]Value{"x": 3, "y": 4}, + }, + }, + }, + } + for _, tc := range testCases { + gotRow, gotInsertID, err := tc.vs.Save() + if err != nil { + t.Errorf("Expected successful save; got: %v", err) + continue + } + if !testutil.Equal(gotRow, tc.wantRow) { + t.Errorf("%v row:\ngot:\n%+v\nwant:\n%+v", tc.vs, gotRow, tc.wantRow) + } + if !testutil.Equal(gotInsertID, tc.wantInsertID) { + t.Errorf("%v ID:\ngot:\n%+v\nwant:\n%+v", tc.vs, gotInsertID, tc.wantInsertID) + } + } +} + +func TestValuesToMapErrors(t *testing.T) { + for _, test := range []struct { + values []Value + schema Schema + }{ + { // mismatched length + []Value{1}, + Schema{}, + }, + { // nested record not a slice + []Value{1}, + Schema{{Type: RecordFieldType}}, + }, + { // nested record mismatched length + []Value{[]Value{1}}, + Schema{{Type: RecordFieldType}}, + }, + { // nested repeated record not a slice + []Value{[]Value{1}}, + Schema{{Type: RecordFieldType, Repeated: true}}, + }, + { // nested repeated record mismatched length + []Value{[]Value{[]Value{1}}}, + Schema{{Type: RecordFieldType, Repeated: true}}, + }, + } { + _, err := valuesToMap(test.values, test.schema) + if err == nil { + t.Errorf("%v, %v: got nil, want error", test.values, test.schema) + } + } +} + +func TestStructSaver(t *testing.T) { + schema := Schema{ + {Name: "s", Type: StringFieldType}, + {Name: "r", Type: IntegerFieldType, Repeated: true}, + {Name: "t", Type: TimeFieldType}, + {Name: "tr", Type: TimeFieldType, Repeated: true}, + {Name: "nested", Type: RecordFieldType, Schema: Schema{ + {Name: "b", Type: BooleanFieldType}, + }}, + {Name: "rnested", Type: RecordFieldType, Repeated: true, Schema: Schema{ + {Name: "b", Type: BooleanFieldType}, + }}, + {Name: "p", Type: IntegerFieldType, Required: false}, + {Name: "n", Type: NumericFieldType, Required: false}, + {Name: "nr", Type: NumericFieldType, Repeated: true}, + } + + type ( + N struct{ B bool } + T struct { + S string + R []int + T civil.Time + TR []civil.Time + Nested *N + Rnested []*N + P NullInt64 + N *big.Rat + NR []*big.Rat + } + ) + + check := func(msg string, in interface{}, want map[string]Value) { + ss := StructSaver{ + Schema: schema, + InsertID: "iid", + Struct: in, + } + got, gotIID, err := ss.Save() + if err != nil { + t.Fatalf("%s: %v", msg, err) + } + if wantIID := "iid"; gotIID != wantIID { + t.Errorf("%s: InsertID: got %q, want %q", msg, gotIID, wantIID) + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("%s: %s", msg, diff) + } + } + + ct1 := civil.Time{Hour: 1, Minute: 2, Second: 3, Nanosecond: 4000} + ct2 := civil.Time{Hour: 5, Minute: 6, Second: 7, Nanosecond: 8000} + in := T{ + S: "x", + R: []int{1, 2}, + T: ct1, + TR: []civil.Time{ct1, ct2}, + Nested: &N{B: true}, + Rnested: []*N{{true}, {false}}, + P: NullInt64{Valid: true, Int64: 17}, + N: big.NewRat(123456, 1000), + NR: []*big.Rat{big.NewRat(3, 1), big.NewRat(56789, 1e5)}, + } + want := map[string]Value{ + "s": "x", + "r": []int{1, 2}, + "t": "01:02:03.000004", + "tr": []string{"01:02:03.000004", "05:06:07.000008"}, + "nested": map[string]Value{"b": true}, + "rnested": []Value{map[string]Value{"b": true}, map[string]Value{"b": false}}, + "p": NullInt64{Valid: true, Int64: 17}, + "n": "123.456000000", + "nr": []string{"3.000000000", "0.567890000"}, + } + check("all values", in, want) + check("all values, ptr", &in, want) + check("empty struct", T{}, map[string]Value{"s": "", "t": "00:00:00", "p": NullInt64{}}) + + // Missing and extra fields ignored. + type T2 struct { + S string + // missing R, Nested, RNested + Extra int + } + check("missing and extra", T2{S: "x"}, map[string]Value{"s": "x"}) + + check("nils in slice", T{Rnested: []*N{{true}, nil, {false}}}, + map[string]Value{ + "s": "", + "t": "00:00:00", + "p": NullInt64{}, + "rnested": []Value{map[string]Value{"b": true}, map[string]Value(nil), map[string]Value{"b": false}}, + }) +} + +func TestStructSaverErrors(t *testing.T) { + type ( + badField struct { + I int `bigquery:"@"` + } + badR struct{ R int } + badRN struct{ R []int } + ) + + for i, test := range []struct { + struct_ interface{} + schema Schema + }{ + {0, nil}, // not a struct + {&badField{}, nil}, // bad field name + {&badR{}, Schema{{Name: "r", Repeated: true}}}, // repeated field has bad type + {&badR{}, Schema{{Name: "r", Type: RecordFieldType}}}, // nested field has bad type + {&badRN{[]int{0}}, // nested repeated field has bad type + Schema{{Name: "r", Type: RecordFieldType, Repeated: true}}}, + } { + ss := &StructSaver{Struct: test.struct_, Schema: test.schema} + _, _, err := ss.Save() + if err == nil { + t.Errorf("#%d, %v, %v: got nil, want error", i, test.struct_, test.schema) + } + } +} + +func TestNumericString(t *testing.T) { + for _, test := range []struct { + in *big.Rat + want string + }{ + {big.NewRat(2, 3), "0.666666667"}, // round to 9 places + {big.NewRat(1, 2), "0.500000000"}, + {big.NewRat(1, 2*1e8), "0.000000005"}, + {big.NewRat(5, 1e10), "0.000000001"}, // round up the 5 in the 10th decimal place + {big.NewRat(-5, 1e10), "-0.000000001"}, // round half away from zero + } { + got := NumericString(test.in) + if got != test.want { + t.Errorf("%v: got %q, want %q", test.in, got, test.want) + } + } +} + +func TestConvertRows(t *testing.T) { + schema := Schema{ + {Type: StringFieldType}, + {Type: IntegerFieldType}, + {Type: FloatFieldType}, + {Type: BooleanFieldType}, + } + rows := []*bq.TableRow{ + {F: []*bq.TableCell{ + {V: "a"}, + {V: "1"}, + {V: "1.2"}, + {V: "true"}, + }}, + {F: []*bq.TableCell{ + {V: "b"}, + {V: "2"}, + {V: "2.2"}, + {V: "false"}, + }}, + } + want := [][]Value{ + {"a", int64(1), 1.2, true}, + {"b", int64(2), 2.2, false}, + } + got, err := convertRows(rows, schema) + if err != nil { + t.Fatalf("got %v, want nil", err) + } + if !testutil.Equal(got, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } + + rows[0].F[0].V = 1 + _, err = convertRows(rows, schema) + if err == nil { + t.Error("got nil, want error") + } +} + +func TestValueList(t *testing.T) { + schema := Schema{ + {Name: "s", Type: StringFieldType}, + {Name: "i", Type: IntegerFieldType}, + {Name: "f", Type: FloatFieldType}, + {Name: "b", Type: BooleanFieldType}, + } + want := []Value{"x", 7, 3.14, true} + var got []Value + vl := (*valueList)(&got) + if err := vl.Load(want, schema); err != nil { + t.Fatal(err) + } + + if !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + // Load truncates, not appends. + // https://github.com/GoogleCloudPlatform/google-cloud-go/issues/437 + if err := vl.Load(want, schema); err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +func TestValueMap(t *testing.T) { + ns := Schema{ + {Name: "x", Type: IntegerFieldType}, + {Name: "y", Type: IntegerFieldType}, + } + schema := Schema{ + {Name: "s", Type: StringFieldType}, + {Name: "i", Type: IntegerFieldType}, + {Name: "f", Type: FloatFieldType}, + {Name: "b", Type: BooleanFieldType}, + {Name: "n", Type: RecordFieldType, Schema: ns}, + {Name: "rn", Type: RecordFieldType, Schema: ns, Repeated: true}, + } + in := []Value{"x", 7, 3.14, true, + []Value{1, 2}, + []Value{[]Value{3, 4}, []Value{5, 6}}, + } + var vm valueMap + if err := vm.Load(in, schema); err != nil { + t.Fatal(err) + } + want := map[string]Value{ + "s": "x", + "i": 7, + "f": 3.14, + "b": true, + "n": map[string]Value{"x": 1, "y": 2}, + "rn": []Value{ + map[string]Value{"x": 3, "y": 4}, + map[string]Value{"x": 5, "y": 6}, + }, + } + if !testutil.Equal(vm, valueMap(want)) { + t.Errorf("got\n%+v\nwant\n%+v", vm, want) + } + + in = make([]Value, len(schema)) + want = map[string]Value{ + "s": nil, + "i": nil, + "f": nil, + "b": nil, + "n": nil, + "rn": nil, + } + var vm2 valueMap + if err := vm2.Load(in, schema); err != nil { + t.Fatal(err) + } + if !testutil.Equal(vm2, valueMap(want)) { + t.Errorf("got\n%+v\nwant\n%+v", vm2, want) + } +} + +var ( + // For testing StructLoader + schema2 = Schema{ + {Name: "s", Type: StringFieldType}, + {Name: "s2", Type: StringFieldType}, + {Name: "by", Type: BytesFieldType}, + {Name: "I", Type: IntegerFieldType}, + {Name: "U", Type: IntegerFieldType}, + {Name: "F", Type: FloatFieldType}, + {Name: "B", Type: BooleanFieldType}, + {Name: "TS", Type: TimestampFieldType}, + {Name: "D", Type: DateFieldType}, + {Name: "T", Type: TimeFieldType}, + {Name: "DT", Type: DateTimeFieldType}, + {Name: "N", Type: NumericFieldType}, + {Name: "nested", Type: RecordFieldType, Schema: Schema{ + {Name: "nestS", Type: StringFieldType}, + {Name: "nestI", Type: IntegerFieldType}, + }}, + {Name: "t", Type: StringFieldType}, + } + + testTimestamp = time.Date(2016, 11, 5, 7, 50, 22, 8, time.UTC) + testDate = civil.Date{Year: 2016, Month: 11, Day: 5} + testTime = civil.Time{Hour: 7, Minute: 50, Second: 22, Nanosecond: 8} + testDateTime = civil.DateTime{Date: testDate, Time: testTime} + testNumeric = big.NewRat(123, 456) + + testValues = []Value{"x", "y", []byte{1, 2, 3}, int64(7), int64(8), 3.14, true, + testTimestamp, testDate, testTime, testDateTime, testNumeric, + []Value{"nested", int64(17)}, "z"} +) + +type testStruct1 struct { + B bool + I int + U uint16 + times + S string + S2 String + By []byte + s string + F float64 + N *big.Rat + Nested nested + Tagged string `bigquery:"t"` +} + +type String string + +type nested struct { + NestS string + NestI int +} + +type times struct { + TS time.Time + T civil.Time + D civil.Date + DT civil.DateTime +} + +func TestStructLoader(t *testing.T) { + var ts1 testStruct1 + mustLoad(t, &ts1, schema2, testValues) + // Note: the schema field named "s" gets matched to the exported struct + // field "S", not the unexported "s". + want := &testStruct1{ + B: true, + I: 7, + U: 8, + F: 3.14, + times: times{TS: testTimestamp, T: testTime, D: testDate, DT: testDateTime}, + S: "x", + S2: "y", + By: []byte{1, 2, 3}, + N: big.NewRat(123, 456), + Nested: nested{NestS: "nested", NestI: 17}, + Tagged: "z", + } + if diff := testutil.Diff(&ts1, want, cmp.AllowUnexported(testStruct1{})); diff != "" { + t.Error(diff) + } + + // Test pointers to nested structs. + type nestedPtr struct{ Nested *nested } + var np nestedPtr + mustLoad(t, &np, schema2, testValues) + want2 := &nestedPtr{Nested: &nested{NestS: "nested", NestI: 17}} + if diff := testutil.Diff(&np, want2); diff != "" { + t.Error(diff) + } + + // Existing values should be reused. + nst := &nested{NestS: "x", NestI: -10} + np = nestedPtr{Nested: nst} + mustLoad(t, &np, schema2, testValues) + if diff := testutil.Diff(&np, want2); diff != "" { + t.Error(diff) + } + if np.Nested != nst { + t.Error("nested struct pointers not equal") + } +} + +type repStruct struct { + Nums []int + ShortNums [2]int // to test truncation + LongNums [5]int // to test padding with zeroes + Nested []*nested +} + +var ( + repSchema = Schema{ + {Name: "nums", Type: IntegerFieldType, Repeated: true}, + {Name: "shortNums", Type: IntegerFieldType, Repeated: true}, + {Name: "longNums", Type: IntegerFieldType, Repeated: true}, + {Name: "nested", Type: RecordFieldType, Repeated: true, Schema: Schema{ + {Name: "nestS", Type: StringFieldType}, + {Name: "nestI", Type: IntegerFieldType}, + }}, + } + v123 = []Value{int64(1), int64(2), int64(3)} + repValues = []Value{v123, v123, v123, + []Value{ + []Value{"x", int64(1)}, + []Value{"y", int64(2)}, + }, + } +) + +func TestStructLoaderRepeated(t *testing.T) { + var r1 repStruct + mustLoad(t, &r1, repSchema, repValues) + want := repStruct{ + Nums: []int{1, 2, 3}, + ShortNums: [...]int{1, 2}, // extra values discarded + LongNums: [...]int{1, 2, 3, 0, 0}, + Nested: []*nested{{"x", 1}, {"y", 2}}, + } + if diff := testutil.Diff(r1, want); diff != "" { + t.Error(diff) + } + r2 := repStruct{ + Nums: []int{-1, -2, -3, -4, -5}, // truncated to zero and appended to + LongNums: [...]int{-1, -2, -3, -4, -5}, // unset elements are zeroed + } + mustLoad(t, &r2, repSchema, repValues) + if diff := testutil.Diff(r2, want); diff != "" { + t.Error(diff) + } + if got, want := cap(r2.Nums), 5; got != want { + t.Errorf("cap(r2.Nums) = %d, want %d", got, want) + } + + // Short slice case. + r3 := repStruct{Nums: []int{-1}} + mustLoad(t, &r3, repSchema, repValues) + if diff := testutil.Diff(r3, want); diff != "" { + t.Error(diff) + } + if got, want := cap(r3.Nums), 3; got != want { + t.Errorf("cap(r3.Nums) = %d, want %d", got, want) + } +} + +type testStructNullable struct { + String NullString + Bytes []byte + Integer NullInt64 + Float NullFloat64 + Boolean NullBool + Timestamp NullTimestamp + Date NullDate + Time NullTime + DateTime NullDateTime + Numeric *big.Rat + Record *subNullable +} + +type subNullable struct { + X NullInt64 +} + +var testStructNullableSchema = Schema{ + {Name: "String", Type: StringFieldType, Required: false}, + {Name: "Bytes", Type: BytesFieldType, Required: false}, + {Name: "Integer", Type: IntegerFieldType, Required: false}, + {Name: "Float", Type: FloatFieldType, Required: false}, + {Name: "Boolean", Type: BooleanFieldType, Required: false}, + {Name: "Timestamp", Type: TimestampFieldType, Required: false}, + {Name: "Date", Type: DateFieldType, Required: false}, + {Name: "Time", Type: TimeFieldType, Required: false}, + {Name: "DateTime", Type: DateTimeFieldType, Required: false}, + {Name: "Numeric", Type: NumericFieldType, Required: false}, + {Name: "Record", Type: RecordFieldType, Required: false, Schema: Schema{ + {Name: "X", Type: IntegerFieldType, Required: false}, + }}, +} + +func TestStructLoaderNullable(t *testing.T) { + var ts testStructNullable + nilVals := make([]Value, len(testStructNullableSchema)) + mustLoad(t, &ts, testStructNullableSchema, nilVals) + want := testStructNullable{} + if diff := testutil.Diff(ts, want); diff != "" { + t.Error(diff) + } + + nonnilVals := []Value{"x", []byte{1, 2, 3}, int64(1), 2.3, true, testTimestamp, testDate, testTime, + testDateTime, big.NewRat(1, 2), []Value{int64(4)}} + + // All ts fields are nil. Loading non-nil values will cause them all to + // be allocated. + mustLoad(t, &ts, testStructNullableSchema, nonnilVals) + want = testStructNullable{ + String: NullString{StringVal: "x", Valid: true}, + Bytes: []byte{1, 2, 3}, + Integer: NullInt64{Int64: 1, Valid: true}, + Float: NullFloat64{Float64: 2.3, Valid: true}, + Boolean: NullBool{Bool: true, Valid: true}, + Timestamp: NullTimestamp{Timestamp: testTimestamp, Valid: true}, + Date: NullDate{Date: testDate, Valid: true}, + Time: NullTime{Time: testTime, Valid: true}, + DateTime: NullDateTime{DateTime: testDateTime, Valid: true}, + Numeric: big.NewRat(1, 2), + Record: &subNullable{X: NullInt64{Int64: 4, Valid: true}}, + } + if diff := testutil.Diff(ts, want); diff != "" { + t.Error(diff) + } + + // Struct pointers are reused, byte slices are not. + want = ts + want.Bytes = []byte{17} + vals2 := []Value{nil, []byte{17}, nil, nil, nil, nil, nil, nil, nil, nil, []Value{int64(7)}} + mustLoad(t, &ts, testStructNullableSchema, vals2) + if ts.Record != want.Record { + t.Error("record pointers not identical") + } +} + +func TestStructLoaderOverflow(t *testing.T) { + type S struct { + I int16 + U uint16 + F float32 + } + schema := Schema{ + {Name: "I", Type: IntegerFieldType}, + {Name: "U", Type: IntegerFieldType}, + {Name: "F", Type: FloatFieldType}, + } + var s S + z64 := int64(0) + for _, vals := range [][]Value{ + {int64(math.MaxInt16 + 1), z64, 0}, + {z64, int64(math.MaxInt32), 0}, + {z64, int64(-1), 0}, + {z64, z64, math.MaxFloat32 * 2}, + } { + if err := load(&s, schema, vals); err == nil { + t.Errorf("%+v: got nil, want error", vals) + } + } +} + +func TestStructLoaderFieldOverlap(t *testing.T) { + // It's OK if the struct has fields that the schema does not, and vice versa. + type S1 struct { + I int + X [][]int // not in the schema; does not even correspond to a valid BigQuery type + // many schema fields missing + } + var s1 S1 + if err := load(&s1, schema2, testValues); err != nil { + t.Fatal(err) + } + want1 := S1{I: 7} + if diff := testutil.Diff(s1, want1); diff != "" { + t.Error(diff) + } + + // It's even valid to have no overlapping fields at all. + type S2 struct{ Z int } + + var s2 S2 + mustLoad(t, &s2, schema2, testValues) + want2 := S2{} + if diff := testutil.Diff(s2, want2); diff != "" { + t.Error(diff) + } +} + +func TestStructLoaderErrors(t *testing.T) { + check := func(sp interface{}) { + var sl structLoader + err := sl.set(sp, schema2) + if err == nil { + t.Errorf("%T: got nil, want error", sp) + } + } + + type bad1 struct{ F int32 } // wrong type for FLOAT column + check(&bad1{}) + + type bad2 struct{ I uint } // unsupported integer type + check(&bad2{}) + + type bad3 struct { + I int `bigquery:"@"` + } // bad field name + check(&bad3{}) + + type bad4 struct{ Nested int } // non-struct for nested field + check(&bad4{}) + + type bad5 struct{ Nested struct{ NestS int } } // bad nested struct + check(&bad5{}) + + bad6 := &struct{ Nums int }{} // non-slice for repeated field + sl := structLoader{} + err := sl.set(bad6, repSchema) + if err == nil { + t.Errorf("%T: got nil, want error", bad6) + } + + // sl.set's error is sticky, even with good input. + err2 := sl.set(&repStruct{}, repSchema) + if err2 != err { + t.Errorf("%v != %v, expected equal", err2, err) + } + // sl.Load is similarly sticky + err2 = sl.Load(nil, nil) + if err2 != err { + t.Errorf("%v != %v, expected equal", err2, err) + } + + // Null values. + schema := Schema{ + {Name: "i", Type: IntegerFieldType}, + {Name: "f", Type: FloatFieldType}, + {Name: "b", Type: BooleanFieldType}, + {Name: "s", Type: StringFieldType}, + {Name: "d", Type: DateFieldType}, + {Name: "r", Type: RecordFieldType, Schema: Schema{{Name: "X", Type: IntegerFieldType}}}, + } + type s struct { + I int + F float64 + B bool + S string + D civil.Date + } + vals := []Value{int64(0), 0.0, false, "", testDate} + mustLoad(t, &s{}, schema, vals) + for i, e := range vals { + vals[i] = nil + got := load(&s{}, schema, vals) + if got != errNoNulls { + t.Errorf("#%d: got %v, want %v", i, got, errNoNulls) + } + vals[i] = e + } + + // Using more than one struct type with the same structLoader. + type different struct { + B bool + I int + times + S string + s string + Nums []int + } + + sl = structLoader{} + if err := sl.set(&testStruct1{}, schema2); err != nil { + t.Fatal(err) + } + err = sl.set(&different{}, schema2) + if err == nil { + t.Error("different struct types: got nil, want error") + } +} + +func mustLoad(t *testing.T, pval interface{}, schema Schema, vals []Value) { + if err := load(pval, schema, vals); err != nil { + t.Fatalf("loading: %v", err) + } +} + +func load(pval interface{}, schema Schema, vals []Value) error { + var sl structLoader + if err := sl.set(pval, schema); err != nil { + return err + } + return sl.Load(vals, nil) +} + +func BenchmarkStructLoader_NoCompile(b *testing.B) { + benchmarkStructLoader(b, false) +} + +func BenchmarkStructLoader_Compile(b *testing.B) { + benchmarkStructLoader(b, true) +} + +func benchmarkStructLoader(b *testing.B, compile bool) { + var ts1 testStruct1 + for i := 0; i < b.N; i++ { + var sl structLoader + for j := 0; j < 10; j++ { + if err := load(&ts1, schema2, testValues); err != nil { + b.Fatal(err) + } + if !compile { + sl.typ = nil + } + } + } +} diff --git a/vendor/cloud.google.com/go/bigtable/admin.go b/vendor/cloud.google.com/go/bigtable/admin.go new file mode 100644 index 0000000000..eb9d7c09aa --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/admin.go @@ -0,0 +1,1092 @@ +/* +Copyright 2015 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 ( + "errors" + "fmt" + "math" + "regexp" + "strings" + "time" + + "cloud.google.com/go/bigtable/internal/gax" + btopt "cloud.google.com/go/bigtable/internal/option" + "cloud.google.com/go/iam" + "cloud.google.com/go/internal/optional" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/ptypes" + durpb "github.com/golang/protobuf/ptypes/duration" + "golang.org/x/net/context" + "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + gtransport "google.golang.org/api/transport/grpc" + btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2" + "google.golang.org/genproto/protobuf/field_mask" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +const adminAddr = "bigtableadmin.googleapis.com:443" + +// AdminClient is a client type for performing admin operations within a specific instance. +type AdminClient struct { + conn *grpc.ClientConn + tClient btapb.BigtableTableAdminClient + lroClient *lroauto.OperationsClient + + project, instance string + + // Metadata to be sent with each request. + md metadata.MD +} + +// NewAdminClient creates a new AdminClient for a given project and instance. +func NewAdminClient(ctx context.Context, project, instance string, opts ...option.ClientOption) (*AdminClient, error) { + o, err := btopt.DefaultClientOptions(adminAddr, AdminScope, clientUserAgent) + if err != nil { + return nil, err + } + // Need to add scopes for long running operations (for create table & snapshots) + o = append(o, option.WithScopes(cloudresourcemanager.CloudPlatformScope)) + o = append(o, opts...) + conn, err := gtransport.Dial(ctx, o...) + if err != nil { + return nil, fmt.Errorf("dialing: %v", err) + } + + lroClient, err := lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + + return &AdminClient{ + conn: conn, + tClient: btapb.NewBigtableTableAdminClient(conn), + lroClient: lroClient, + project: project, + instance: instance, + md: metadata.Pairs(resourcePrefixHeader, fmt.Sprintf("projects/%s/instances/%s", project, instance)), + }, nil +} + +// Close closes the AdminClient. +func (ac *AdminClient) Close() error { + return ac.conn.Close() +} + +func (ac *AdminClient) instancePrefix() string { + return fmt.Sprintf("projects/%s/instances/%s", ac.project, ac.instance) +} + +// Tables returns a list of the tables in the instance. +func (ac *AdminClient) Tables(ctx context.Context) ([]string, error) { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + req := &btapb.ListTablesRequest{ + Parent: prefix, + } + + var res *btapb.ListTablesResponse + err := gax.Invoke(ctx, func(ctx context.Context) error { + var err error + res, err = ac.tClient.ListTables(ctx, req) + return err + }, retryOptions...) + if err != nil { + return nil, err + } + + names := make([]string, 0, len(res.Tables)) + for _, tbl := range res.Tables { + names = append(names, strings.TrimPrefix(tbl.Name, prefix+"/tables/")) + } + return names, nil +} + +// TableConf contains all of the information necessary to create a table with column families. +type TableConf struct { + TableID string + SplitKeys []string + // Families is a map from family name to GCPolicy + Families map[string]GCPolicy +} + +// CreateTable creates a new table in the instance. +// This method may return before the table's creation is complete. +func (ac *AdminClient) CreateTable(ctx context.Context, table string) error { + return ac.CreateTableFromConf(ctx, &TableConf{TableID: table}) +} + +// CreatePresplitTable creates a new table in the instance. +// The list of row keys will be used to initially split the table into multiple tablets. +// Given two split keys, "s1" and "s2", three tablets will be created, +// spanning the key ranges: [, s1), [s1, s2), [s2, ). +// This method may return before the table's creation is complete. +func (ac *AdminClient) CreatePresplitTable(ctx context.Context, table string, splitKeys []string) error { + return ac.CreateTableFromConf(ctx, &TableConf{TableID: table, SplitKeys: splitKeys}) +} + +// CreateTableFromConf creates a new table in the instance from the given configuration. +func (ac *AdminClient) CreateTableFromConf(ctx context.Context, conf *TableConf) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + var req_splits []*btapb.CreateTableRequest_Split + for _, split := range conf.SplitKeys { + req_splits = append(req_splits, &btapb.CreateTableRequest_Split{Key: []byte(split)}) + } + var tbl btapb.Table + if conf.Families != nil { + tbl.ColumnFamilies = make(map[string]*btapb.ColumnFamily) + for fam, policy := range conf.Families { + tbl.ColumnFamilies[fam] = &btapb.ColumnFamily{GcRule: policy.proto()} + } + } + prefix := ac.instancePrefix() + req := &btapb.CreateTableRequest{ + Parent: prefix, + TableId: conf.TableID, + Table: &tbl, + InitialSplits: req_splits, + } + _, err := ac.tClient.CreateTable(ctx, req) + return err +} + +// CreateColumnFamily creates a new column family in a table. +func (ac *AdminClient) CreateColumnFamily(ctx context.Context, table, family string) error { + // TODO(dsymonds): Permit specifying gcexpr and any other family settings. + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + req := &btapb.ModifyColumnFamiliesRequest{ + Name: prefix + "/tables/" + table, + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: family, + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{Create: &btapb.ColumnFamily{}}, + }}, + } + _, err := ac.tClient.ModifyColumnFamilies(ctx, req) + return err +} + +// DeleteTable deletes a table and all of its data. +func (ac *AdminClient) DeleteTable(ctx context.Context, table string) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + req := &btapb.DeleteTableRequest{ + Name: prefix + "/tables/" + table, + } + _, err := ac.tClient.DeleteTable(ctx, req) + return err +} + +// DeleteColumnFamily deletes a column family in a table and all of its data. +func (ac *AdminClient) DeleteColumnFamily(ctx context.Context, table, family string) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + req := &btapb.ModifyColumnFamiliesRequest{ + Name: prefix + "/tables/" + table, + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: family, + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Drop{Drop: true}, + }}, + } + _, err := ac.tClient.ModifyColumnFamilies(ctx, req) + return err +} + +// TableInfo represents information about a table. +type TableInfo struct { + // DEPRECATED - This field is deprecated. Please use FamilyInfos instead. + Families []string + FamilyInfos []FamilyInfo +} + +// FamilyInfo represents information about a column family. +type FamilyInfo struct { + Name string + GCPolicy string +} + +// TableInfo retrieves information about a table. +func (ac *AdminClient) TableInfo(ctx context.Context, table string) (*TableInfo, error) { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + req := &btapb.GetTableRequest{ + Name: prefix + "/tables/" + table, + } + + var res *btapb.Table + + err := gax.Invoke(ctx, func(ctx context.Context) error { + var err error + res, err = ac.tClient.GetTable(ctx, req) + return err + }, retryOptions...) + if err != nil { + return nil, err + } + + ti := &TableInfo{} + for name, fam := range res.ColumnFamilies { + ti.Families = append(ti.Families, name) + ti.FamilyInfos = append(ti.FamilyInfos, FamilyInfo{Name: name, GCPolicy: GCRuleToString(fam.GcRule)}) + } + return ti, nil +} + +// SetGCPolicy specifies which cells in a column family should be garbage collected. +// GC executes opportunistically in the background; table reads may return data +// matching the GC policy. +func (ac *AdminClient) SetGCPolicy(ctx context.Context, table, family string, policy GCPolicy) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + req := &btapb.ModifyColumnFamiliesRequest{ + Name: prefix + "/tables/" + table, + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: family, + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Update{Update: &btapb.ColumnFamily{GcRule: policy.proto()}}, + }}, + } + _, err := ac.tClient.ModifyColumnFamilies(ctx, req) + return err +} + +// DropRowRange permanently deletes a row range from the specified table. +func (ac *AdminClient) DropRowRange(ctx context.Context, table, rowKeyPrefix string) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + req := &btapb.DropRowRangeRequest{ + Name: prefix + "/tables/" + table, + Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte(rowKeyPrefix)}, + } + _, err := ac.tClient.DropRowRange(ctx, req) + return err +} + +// CreateTableFromSnapshot creates a table from snapshot. +// The table will be created in the same cluster as the snapshot. +// +// This is a private alpha release of Cloud Bigtable snapshots. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (ac *AdminClient) CreateTableFromSnapshot(ctx context.Context, table, cluster, snapshot string) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + snapshotPath := prefix + "/clusters/" + cluster + "/snapshots/" + snapshot + + req := &btapb.CreateTableFromSnapshotRequest{ + Parent: prefix, + TableId: table, + SourceSnapshot: snapshotPath, + } + op, err := ac.tClient.CreateTableFromSnapshot(ctx, req) + if err != nil { + return err + } + resp := btapb.Table{} + return longrunning.InternalNewOperation(ac.lroClient, op).Wait(ctx, &resp) +} + +const DefaultSnapshotDuration time.Duration = 0 + +// Creates a new snapshot in the specified cluster from the specified source table. +// Setting the ttl to `DefaultSnapshotDuration` will use the server side default for the duration. +// +// This is a private alpha release of Cloud Bigtable snapshots. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (ac *AdminClient) SnapshotTable(ctx context.Context, table, cluster, snapshot string, ttl time.Duration) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + + var ttlProto *durpb.Duration + + if ttl > 0 { + ttlProto = ptypes.DurationProto(ttl) + } + + req := &btapb.SnapshotTableRequest{ + Name: prefix + "/tables/" + table, + Cluster: prefix + "/clusters/" + cluster, + SnapshotId: snapshot, + Ttl: ttlProto, + } + + op, err := ac.tClient.SnapshotTable(ctx, req) + if err != nil { + return err + } + resp := btapb.Snapshot{} + return longrunning.InternalNewOperation(ac.lroClient, op).Wait(ctx, &resp) +} + +// Snapshots returns a SnapshotIterator for iterating over the snapshots in a cluster. +// To list snapshots across all of the clusters in the instance specify "-" as the cluster. +// +// This is a private alpha release of Cloud Bigtable snapshots. This feature is not +// currently available to most Cloud Bigtable customers. This feature might be +// changed in backward-incompatible ways and is not recommended for production use. +// It is not subject to any SLA or deprecation policy. +func (ac *AdminClient) Snapshots(ctx context.Context, cluster string) *SnapshotIterator { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + clusterPath := prefix + "/clusters/" + cluster + + it := &SnapshotIterator{} + req := &btapb.ListSnapshotsRequest{ + Parent: clusterPath, + } + + fetch := func(pageSize int, pageToken string) (string, error) { + req.PageToken = pageToken + if pageSize > math.MaxInt32 { + req.PageSize = math.MaxInt32 + } else { + req.PageSize = int32(pageSize) + } + + resp, err := ac.tClient.ListSnapshots(ctx, req) + if err != nil { + return "", err + } + for _, s := range resp.Snapshots { + snapshotInfo, err := newSnapshotInfo(s) + if err != nil { + return "", fmt.Errorf("Failed to parse snapshot proto %v", err) + } + it.items = append(it.items, snapshotInfo) + } + return resp.NextPageToken, nil + } + bufLen := func() int { return len(it.items) } + takeBuf := func() interface{} { b := it.items; it.items = nil; return b } + + it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, bufLen, takeBuf) + + return it +} + +func newSnapshotInfo(snapshot *btapb.Snapshot) (*SnapshotInfo, error) { + nameParts := strings.Split(snapshot.Name, "/") + name := nameParts[len(nameParts)-1] + tablePathParts := strings.Split(snapshot.SourceTable.Name, "/") + tableID := tablePathParts[len(tablePathParts)-1] + + createTime, err := ptypes.Timestamp(snapshot.CreateTime) + if err != nil { + return nil, fmt.Errorf("Invalid createTime: %v", err) + } + + deleteTime, err := ptypes.Timestamp(snapshot.DeleteTime) + if err != nil { + return nil, fmt.Errorf("Invalid deleteTime: %v", err) + } + + return &SnapshotInfo{ + Name: name, + SourceTable: tableID, + DataSize: snapshot.DataSizeBytes, + CreateTime: createTime, + DeleteTime: deleteTime, + }, nil +} + +// An EntryIterator iterates over log entries. +// +// This is a private alpha release of Cloud Bigtable snapshots. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +type SnapshotIterator struct { + items []*SnapshotInfo + pageInfo *iterator.PageInfo + nextFunc func() error +} + +// PageInfo supports pagination. See https://godoc.org/google.golang.org/api/iterator package for details. +func (it *SnapshotIterator) PageInfo() *iterator.PageInfo { + return it.pageInfo +} + +// Next returns the next result. Its second return value is iterator.Done +// (https://godoc.org/google.golang.org/api/iterator) if there are no more +// results. Once Next returns Done, all subsequent calls will return Done. +func (it *SnapshotIterator) Next() (*SnapshotInfo, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +type SnapshotInfo struct { + Name string + SourceTable string + DataSize int64 + CreateTime time.Time + DeleteTime time.Time +} + +// Get snapshot metadata. +// +// This is a private alpha release of Cloud Bigtable snapshots. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (ac *AdminClient) SnapshotInfo(ctx context.Context, cluster, snapshot string) (*SnapshotInfo, error) { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + clusterPath := prefix + "/clusters/" + cluster + snapshotPath := clusterPath + "/snapshots/" + snapshot + + req := &btapb.GetSnapshotRequest{ + Name: snapshotPath, + } + + resp, err := ac.tClient.GetSnapshot(ctx, req) + if err != nil { + return nil, err + } + + return newSnapshotInfo(resp) +} + +// Delete a snapshot in a cluster. +// +// This is a private alpha release of Cloud Bigtable snapshots. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (ac *AdminClient) DeleteSnapshot(ctx context.Context, cluster, snapshot string) error { + ctx = mergeOutgoingMetadata(ctx, ac.md) + prefix := ac.instancePrefix() + clusterPath := prefix + "/clusters/" + cluster + snapshotPath := clusterPath + "/snapshots/" + snapshot + + req := &btapb.DeleteSnapshotRequest{ + Name: snapshotPath, + } + _, err := ac.tClient.DeleteSnapshot(ctx, req) + return err +} + +// getConsistencyToken gets the consistency token for a table. +func (ac *AdminClient) getConsistencyToken(ctx context.Context, tableName string) (string, error) { + req := &btapb.GenerateConsistencyTokenRequest{ + Name: tableName, + } + resp, err := ac.tClient.GenerateConsistencyToken(ctx, req) + if err != nil { + return "", err + } + return resp.GetConsistencyToken(), nil +} + +// isConsistent checks if a token is consistent for a table. +func (ac *AdminClient) isConsistent(ctx context.Context, tableName, token string) (bool, error) { + req := &btapb.CheckConsistencyRequest{ + Name: tableName, + ConsistencyToken: token, + } + var resp *btapb.CheckConsistencyResponse + + // Retry calls on retryable errors to avoid losing the token gathered before. + err := gax.Invoke(ctx, func(ctx context.Context) error { + var err error + resp, err = ac.tClient.CheckConsistency(ctx, req) + return err + }, retryOptions...) + if err != nil { + return false, err + } + return resp.GetConsistent(), nil +} + +// WaitForReplication waits until all the writes committed before the call started have been propagated to all the clusters in the instance via replication. +// +// This is a private alpha release of Cloud Bigtable replication. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (ac *AdminClient) WaitForReplication(ctx context.Context, table string) error { + // Get the token. + prefix := ac.instancePrefix() + tableName := prefix + "/tables/" + table + token, err := ac.getConsistencyToken(ctx, tableName) + if err != nil { + return err + } + + // Periodically check if the token is consistent. + timer := time.NewTicker(time.Second * 10) + defer timer.Stop() + for { + consistent, err := ac.isConsistent(ctx, tableName, token) + if err != nil { + return err + } + if consistent { + return nil + } + // Sleep for a bit or until the ctx is cancelled. + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + } + } +} + +const instanceAdminAddr = "bigtableadmin.googleapis.com:443" + +// InstanceAdminClient is a client type for performing admin operations on instances. +// These operations can be substantially more dangerous than those provided by AdminClient. +type InstanceAdminClient struct { + conn *grpc.ClientConn + iClient btapb.BigtableInstanceAdminClient + lroClient *lroauto.OperationsClient + + project string + + // Metadata to be sent with each request. + md metadata.MD +} + +// NewInstanceAdminClient creates a new InstanceAdminClient for a given project. +func NewInstanceAdminClient(ctx context.Context, project string, opts ...option.ClientOption) (*InstanceAdminClient, error) { + o, err := btopt.DefaultClientOptions(instanceAdminAddr, InstanceAdminScope, clientUserAgent) + if err != nil { + return nil, err + } + o = append(o, opts...) + conn, err := gtransport.Dial(ctx, o...) + if err != nil { + return nil, fmt.Errorf("dialing: %v", err) + } + + lroClient, err := lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + + return &InstanceAdminClient{ + conn: conn, + iClient: btapb.NewBigtableInstanceAdminClient(conn), + lroClient: lroClient, + + project: project, + md: metadata.Pairs(resourcePrefixHeader, "projects/"+project), + }, nil +} + +// Close closes the InstanceAdminClient. +func (iac *InstanceAdminClient) Close() error { + return iac.conn.Close() +} + +// StorageType is the type of storage used for all tables in an instance +type StorageType int + +const ( + SSD StorageType = iota + HDD +) + +func (st StorageType) proto() btapb.StorageType { + if st == HDD { + return btapb.StorageType_HDD + } + return btapb.StorageType_SSD +} + +// InstanceType is the type of the instance +type InstanceType int32 + +const ( + PRODUCTION InstanceType = InstanceType(btapb.Instance_PRODUCTION) + DEVELOPMENT = InstanceType(btapb.Instance_DEVELOPMENT) +) + +// InstanceInfo represents information about an instance +type InstanceInfo struct { + Name string // name of the instance + DisplayName string // display name for UIs +} + +// InstanceConf contains the information necessary to create an Instance +type InstanceConf struct { + InstanceId, DisplayName, ClusterId, Zone string + // NumNodes must not be specified for DEVELOPMENT instance types + NumNodes int32 + StorageType StorageType + InstanceType InstanceType +} + +// InstanceWithClustersConfig contains the information necessary to create an Instance +type InstanceWithClustersConfig struct { + InstanceID, DisplayName string + Clusters []ClusterConfig + InstanceType InstanceType +} + +var instanceNameRegexp = regexp.MustCompile(`^projects/([^/]+)/instances/([a-z][-a-z0-9]*)$`) + +// CreateInstance creates a new instance in the project. +// This method will return when the instance has been created or when an error occurs. +func (iac *InstanceAdminClient) CreateInstance(ctx context.Context, conf *InstanceConf) error { + newConfig := InstanceWithClustersConfig{ + InstanceID: conf.InstanceId, + DisplayName: conf.DisplayName, + InstanceType: conf.InstanceType, + Clusters: []ClusterConfig{ + { + InstanceID: conf.InstanceId, + ClusterID: conf.ClusterId, + Zone: conf.Zone, + NumNodes: conf.NumNodes, + StorageType: conf.StorageType, + }, + }, + } + return iac.CreateInstanceWithClusters(ctx, &newConfig) +} + +// CreateInstance creates a new instance with configured clusters in the project. +// This method will return when the instance has been created or when an error occurs. +// +// Instances with multiple clusters are part of a private alpha release of Cloud Bigtable replication. +// This feature is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (iac *InstanceAdminClient) CreateInstanceWithClusters(ctx context.Context, conf *InstanceWithClustersConfig) error { + ctx = mergeOutgoingMetadata(ctx, iac.md) + clusters := make(map[string]*btapb.Cluster) + for _, cluster := range conf.Clusters { + clusters[cluster.ClusterID] = cluster.proto(iac.project) + } + + req := &btapb.CreateInstanceRequest{ + Parent: "projects/" + iac.project, + InstanceId: conf.InstanceID, + Instance: &btapb.Instance{DisplayName: conf.DisplayName, Type: btapb.Instance_Type(conf.InstanceType)}, + Clusters: clusters, + } + + lro, err := iac.iClient.CreateInstance(ctx, req) + if err != nil { + return err + } + resp := btapb.Instance{} + return longrunning.InternalNewOperation(iac.lroClient, lro).Wait(ctx, &resp) +} + +// DeleteInstance deletes an instance from the project. +func (iac *InstanceAdminClient) DeleteInstance(ctx context.Context, instanceID string) error { + ctx = mergeOutgoingMetadata(ctx, iac.md) + req := &btapb.DeleteInstanceRequest{Name: "projects/" + iac.project + "/instances/" + instanceID} + _, err := iac.iClient.DeleteInstance(ctx, req) + return err +} + +// Instances returns a list of instances in the project. +func (iac *InstanceAdminClient) Instances(ctx context.Context) ([]*InstanceInfo, error) { + ctx = mergeOutgoingMetadata(ctx, iac.md) + req := &btapb.ListInstancesRequest{ + Parent: "projects/" + iac.project, + } + res, err := iac.iClient.ListInstances(ctx, req) + if err != nil { + return nil, err + } + if len(res.FailedLocations) > 0 { + // We don't have a good way to return a partial result in the face of some zones being unavailable. + // Fail the entire request. + return nil, status.Errorf(codes.Unavailable, "Failed locations: %v", res.FailedLocations) + } + + var is []*InstanceInfo + for _, i := range res.Instances { + m := instanceNameRegexp.FindStringSubmatch(i.Name) + if m == nil { + return nil, fmt.Errorf("malformed instance name %q", i.Name) + } + is = append(is, &InstanceInfo{ + Name: m[2], + DisplayName: i.DisplayName, + }) + } + return is, nil +} + +// InstanceInfo returns information about an instance. +func (iac *InstanceAdminClient) InstanceInfo(ctx context.Context, instanceID string) (*InstanceInfo, error) { + ctx = mergeOutgoingMetadata(ctx, iac.md) + req := &btapb.GetInstanceRequest{ + Name: "projects/" + iac.project + "/instances/" + instanceID, + } + res, err := iac.iClient.GetInstance(ctx, req) + if err != nil { + return nil, err + } + + m := instanceNameRegexp.FindStringSubmatch(res.Name) + if m == nil { + return nil, fmt.Errorf("malformed instance name %q", res.Name) + } + return &InstanceInfo{ + Name: m[2], + DisplayName: res.DisplayName, + }, nil +} + +// ClusterConfig contains the information necessary to create a cluster +type ClusterConfig struct { + InstanceID, ClusterID, Zone string + NumNodes int32 + StorageType StorageType +} + +func (cc *ClusterConfig) proto(project string) *btapb.Cluster { + return &btapb.Cluster{ + ServeNodes: cc.NumNodes, + DefaultStorageType: cc.StorageType.proto(), + Location: "projects/" + project + "/locations/" + cc.Zone, + } +} + +// ClusterInfo represents information about a cluster. +type ClusterInfo struct { + Name string // name of the cluster + Zone string // GCP zone of the cluster (e.g. "us-central1-a") + ServeNodes int // number of allocated serve nodes + State string // state of the cluster +} + +// CreateCluster creates a new cluster in an instance. +// This method will return when the cluster has been created or when an error occurs. +// +// This is a private alpha release of Cloud Bigtable replication. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (iac *InstanceAdminClient) CreateCluster(ctx context.Context, conf *ClusterConfig) error { + ctx = mergeOutgoingMetadata(ctx, iac.md) + + req := &btapb.CreateClusterRequest{ + Parent: "projects/" + iac.project + "/instances/" + conf.InstanceID, + ClusterId: conf.ClusterID, + Cluster: conf.proto(iac.project), + } + + lro, err := iac.iClient.CreateCluster(ctx, req) + if err != nil { + return err + } + resp := btapb.Cluster{} + return longrunning.InternalNewOperation(iac.lroClient, lro).Wait(ctx, &resp) +} + +// DeleteCluster deletes a cluster from an instance. +// +// This is a private alpha release of Cloud Bigtable replication. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (iac *InstanceAdminClient) DeleteCluster(ctx context.Context, instanceID, clusterID string) error { + ctx = mergeOutgoingMetadata(ctx, iac.md) + req := &btapb.DeleteClusterRequest{Name: "projects/" + iac.project + "/instances/" + instanceID + "/clusters/" + clusterID} + _, err := iac.iClient.DeleteCluster(ctx, req) + return err +} + +// UpdateCluster updates attributes of a cluster +func (iac *InstanceAdminClient) UpdateCluster(ctx context.Context, instanceID, clusterID string, serveNodes int32) error { + ctx = mergeOutgoingMetadata(ctx, iac.md) + cluster := &btapb.Cluster{ + Name: "projects/" + iac.project + "/instances/" + instanceID + "/clusters/" + clusterID, + ServeNodes: serveNodes} + lro, err := iac.iClient.UpdateCluster(ctx, cluster) + if err != nil { + return err + } + return longrunning.InternalNewOperation(iac.lroClient, lro).Wait(ctx, nil) +} + +// Clusters lists the clusters in an instance. +func (iac *InstanceAdminClient) Clusters(ctx context.Context, instanceID string) ([]*ClusterInfo, error) { + ctx = mergeOutgoingMetadata(ctx, iac.md) + req := &btapb.ListClustersRequest{Parent: "projects/" + iac.project + "/instances/" + instanceID} + res, err := iac.iClient.ListClusters(ctx, req) + if err != nil { + return nil, err + } + // TODO(garyelliott): Deal with failed_locations. + var cis []*ClusterInfo + for _, c := range res.Clusters { + nameParts := strings.Split(c.Name, "/") + locParts := strings.Split(c.Location, "/") + cis = append(cis, &ClusterInfo{ + Name: nameParts[len(nameParts)-1], + Zone: locParts[len(locParts)-1], + ServeNodes: int(c.ServeNodes), + State: c.State.String(), + }) + } + return cis, nil +} + +// GetCluster fetches a cluster in an instance +func (iac *InstanceAdminClient) GetCluster(ctx context.Context, instanceID, clusterID string) (*ClusterInfo, error) { + ctx = mergeOutgoingMetadata(ctx, iac.md) + req := &btapb.GetClusterRequest{Name: "projects/" + iac.project + "/instances/" + instanceID + "/clusters/" + clusterID} + c, err := iac.iClient.GetCluster(ctx, req) + if err != nil { + return nil, err + } + + nameParts := strings.Split(c.Name, "/") + locParts := strings.Split(c.Location, "/") + cis := &ClusterInfo{ + Name: nameParts[len(nameParts)-1], + Zone: locParts[len(locParts)-1], + ServeNodes: int(c.ServeNodes), + State: c.State.String(), + } + return cis, nil +} + +func (iac *InstanceAdminClient) InstanceIAM(instanceID string) *iam.Handle { + return iam.InternalNewHandleGRPCClient(iac.iClient, "projects/"+iac.project+"/instances/"+instanceID) + +} + +// Routing policies. +const ( + MultiClusterRouting = "multi_cluster_routing_use_any" + SingleClusterRouting = "single_cluster_routing" +) + +// ProfileConf contains the information necessary to create an profile +type ProfileConf struct { + Name string + ProfileID string + InstanceID string + Etag string + Description string + RoutingPolicy string + ClusterID string + AllowTransactionalWrites bool +} + +type ProfileIterator struct { + items []*btapb.AppProfile + pageInfo *iterator.PageInfo + nextFunc func() error +} + +//set this to patch app profile. If unset, no fields will be replaced. +type ProfileAttrsToUpdate struct { + // If set, updates the description. + Description optional.String + + //If set, updates the routing policy. + RoutingPolicy optional.String + + //If RoutingPolicy is updated to SingleClusterRouting, set these fields as well. + ClusterID string + AllowTransactionalWrites bool +} + +func (p *ProfileAttrsToUpdate) GetFieldMaskPath() []string { + path := make([]string, 0) + if p.Description != nil { + path = append(path, "description") + } + + if p.RoutingPolicy != nil { + path = append(path, optional.ToString(p.RoutingPolicy)) + } + return path +} + +// PageInfo supports pagination. See https://godoc.org/google.golang.org/api/iterator package for details. +func (it *ProfileIterator) PageInfo() *iterator.PageInfo { + return it.pageInfo +} + +// Next returns the next result. Its second return value is iterator.Done +// (https://godoc.org/google.golang.org/api/iterator) if there are no more +// results. Once Next returns Done, all subsequent calls will return Done. +func (it *ProfileIterator) Next() (*btapb.AppProfile, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +// CreateAppProfile creates an app profile within an instance. +func (iac *InstanceAdminClient) CreateAppProfile(ctx context.Context, profile ProfileConf) (*btapb.AppProfile, error) { + ctx = mergeOutgoingMetadata(ctx, iac.md) + parent := "projects/" + iac.project + "/instances/" + profile.InstanceID + appProfile := &btapb.AppProfile{ + Etag: profile.Etag, + Description: profile.Description, + } + + if profile.RoutingPolicy == "" { + return nil, errors.New("invalid routing policy") + } + + switch profile.RoutingPolicy { + case MultiClusterRouting: + appProfile.RoutingPolicy = &btapb.AppProfile_MultiClusterRoutingUseAny_{ + MultiClusterRoutingUseAny: &btapb.AppProfile_MultiClusterRoutingUseAny{}, + } + case SingleClusterRouting: + appProfile.RoutingPolicy = &btapb.AppProfile_SingleClusterRouting_{ + SingleClusterRouting: &btapb.AppProfile_SingleClusterRouting{ + ClusterId: profile.ClusterID, + AllowTransactionalWrites: profile.AllowTransactionalWrites, + }, + } + default: + return nil, errors.New("invalid routing policy") + } + + return iac.iClient.CreateAppProfile(ctx, &btapb.CreateAppProfileRequest{ + Parent: parent, + AppProfile: appProfile, + AppProfileId: profile.ProfileID, + IgnoreWarnings: true, + }) +} + +// GetAppProfile gets information about an app profile. +func (iac *InstanceAdminClient) GetAppProfile(ctx context.Context, instanceID, name string) (*btapb.AppProfile, error) { + ctx = mergeOutgoingMetadata(ctx, iac.md) + profileRequest := &btapb.GetAppProfileRequest{ + Name: "projects/" + iac.project + "/instances/" + instanceID + "/appProfiles/" + name, + } + return iac.iClient.GetAppProfile(ctx, profileRequest) + +} + +// ListAppProfiles lists information about app profiles in an instance. +func (iac *InstanceAdminClient) ListAppProfiles(ctx context.Context, instanceID string) *ProfileIterator { + ctx = mergeOutgoingMetadata(ctx, iac.md) + listRequest := &btapb.ListAppProfilesRequest{ + Parent: "projects/" + iac.project + "/instances/" + instanceID, + } + + pit := &ProfileIterator{} + fetch := func(pageSize int, pageToken string) (string, error) { + listRequest.PageToken = pageToken + profileRes, err := iac.iClient.ListAppProfiles(ctx, listRequest) + if err != nil { + return "", err + } + + for _, a := range profileRes.AppProfiles { + pit.items = append(pit.items, a) + } + return profileRes.NextPageToken, nil + } + + bufLen := func() int { return len(pit.items) } + takeBuf := func() interface{} { b := pit.items; pit.items = nil; return b } + pit.pageInfo, pit.nextFunc = iterator.NewPageInfo(fetch, bufLen, takeBuf) + return pit + +} + +// UpdateAppProfile updates an app profile within an instance. +// updateAttrs should be set. If unset, all fields will be replaced. +func (iac *InstanceAdminClient) UpdateAppProfile(ctx context.Context, instanceID, profileID string, updateAttrs ProfileAttrsToUpdate) error { + ctx = mergeOutgoingMetadata(ctx, iac.md) + + profile := &btapb.AppProfile{ + Name: "projects/" + iac.project + "/instances/" + instanceID + "/appProfiles/" + profileID, + } + + if updateAttrs.Description != nil { + profile.Description = optional.ToString(updateAttrs.Description) + } + if updateAttrs.RoutingPolicy != nil { + switch optional.ToString(updateAttrs.RoutingPolicy) { + case MultiClusterRouting: + profile.RoutingPolicy = &btapb.AppProfile_MultiClusterRoutingUseAny_{ + MultiClusterRoutingUseAny: &btapb.AppProfile_MultiClusterRoutingUseAny{}, + } + case SingleClusterRouting: + profile.RoutingPolicy = &btapb.AppProfile_SingleClusterRouting_{ + SingleClusterRouting: &btapb.AppProfile_SingleClusterRouting{ + ClusterId: updateAttrs.ClusterID, + AllowTransactionalWrites: updateAttrs.AllowTransactionalWrites, + }, + } + default: + return errors.New("invalid routing policy") + } + } + patchRequest := &btapb.UpdateAppProfileRequest{ + AppProfile: profile, + UpdateMask: &field_mask.FieldMask{ + Paths: updateAttrs.GetFieldMaskPath(), + }, + IgnoreWarnings: true, + } + updateRequest, err := iac.iClient.UpdateAppProfile(ctx, patchRequest) + if err != nil { + return err + } + + return longrunning.InternalNewOperation(iac.lroClient, updateRequest).Wait(ctx, nil) + +} + +// DeleteAppProfile deletes an app profile from an instance. +func (iac *InstanceAdminClient) DeleteAppProfile(ctx context.Context, instanceID, name string) error { + ctx = mergeOutgoingMetadata(ctx, iac.md) + deleteProfileRequest := &btapb.DeleteAppProfileRequest{ + Name: "projects/" + iac.project + "/instances/" + instanceID + "/appProfiles/" + name, + IgnoreWarnings: true, + } + _, err := iac.iClient.DeleteAppProfile(ctx, deleteProfileRequest) + return err + +} diff --git a/vendor/cloud.google.com/go/bigtable/admin_test.go b/vendor/cloud.google.com/go/bigtable/admin_test.go new file mode 100644 index 0000000000..1f105f286e --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/admin_test.go @@ -0,0 +1,578 @@ +// Copyright 2015 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 ( + "fmt" + "math" + "sort" + "strings" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2" +) + +func TestAdminIntegration(t *testing.T) { + testEnv, err := NewIntegrationEnv() + if err != nil { + t.Fatalf("IntegrationEnv: %v", err) + } + defer testEnv.Close() + + timeout := 2 * time.Second + if testEnv.Config().UseProd { + timeout = 5 * time.Minute + } + ctx, _ := context.WithTimeout(context.Background(), timeout) + + adminClient, err := testEnv.NewAdminClient() + if err != nil { + t.Fatalf("NewAdminClient: %v", err) + } + defer adminClient.Close() + + iAdminClient, err := testEnv.NewInstanceAdminClient() + if err != nil { + t.Fatalf("NewInstanceAdminClient: %v", err) + } + if iAdminClient != nil { + defer iAdminClient.Close() + + iInfo, err := iAdminClient.InstanceInfo(ctx, adminClient.instance) + if err != nil { + t.Errorf("InstanceInfo: %v", err) + } + if iInfo.Name != adminClient.instance { + t.Errorf("InstanceInfo returned name %#v, want %#v", iInfo.Name, adminClient.instance) + } + } + + list := func() []string { + tbls, err := adminClient.Tables(ctx) + if err != nil { + t.Fatalf("Fetching list of tables: %v", err) + } + sort.Strings(tbls) + return tbls + } + containsAll := func(got, want []string) bool { + gotSet := make(map[string]bool) + + for _, s := range got { + gotSet[s] = true + } + for _, s := range want { + if !gotSet[s] { + return false + } + } + return true + } + + defer adminClient.DeleteTable(ctx, "mytable") + + if err := adminClient.CreateTable(ctx, "mytable"); err != nil { + t.Fatalf("Creating table: %v", err) + } + + defer adminClient.DeleteTable(ctx, "myothertable") + + if err := adminClient.CreateTable(ctx, "myothertable"); err != nil { + t.Fatalf("Creating table: %v", err) + } + + if got, want := list(), []string{"myothertable", "mytable"}; !containsAll(got, want) { + t.Errorf("adminClient.Tables returned %#v, want %#v", got, want) + } + + must(adminClient.WaitForReplication(ctx, "mytable")) + + if err := adminClient.DeleteTable(ctx, "myothertable"); err != nil { + t.Fatalf("Deleting table: %v", err) + } + tables := list() + if got, want := tables, []string{"mytable"}; !containsAll(got, want) { + t.Errorf("adminClient.Tables returned %#v, want %#v", got, want) + } + if got, unwanted := tables, []string{"myothertable"}; containsAll(got, unwanted) { + t.Errorf("adminClient.Tables return %#v. unwanted %#v", got, unwanted) + } + + tblConf := TableConf{ + TableID: "conftable", + Families: map[string]GCPolicy{ + "fam1": MaxVersionsPolicy(1), + "fam2": MaxVersionsPolicy(2), + }, + } + if err := adminClient.CreateTableFromConf(ctx, &tblConf); err != nil { + t.Fatalf("Creating table from TableConf: %v", err) + } + defer adminClient.DeleteTable(ctx, tblConf.TableID) + + tblInfo, err := adminClient.TableInfo(ctx, tblConf.TableID) + if err != nil { + t.Fatalf("Getting table info: %v", err) + } + sort.Strings(tblInfo.Families) + wantFams := []string{"fam1", "fam2"} + if !testutil.Equal(tblInfo.Families, wantFams) { + t.Errorf("Column family mismatch, got %v, want %v", tblInfo.Families, wantFams) + } + + // Populate mytable and drop row ranges + if err = adminClient.CreateColumnFamily(ctx, "mytable", "cf"); err != nil { + t.Fatalf("Creating column family: %v", err) + } + + client, err := testEnv.NewClient() + if err != nil { + t.Fatalf("NewClient: %v", err) + } + defer client.Close() + + tbl := client.Open("mytable") + + prefixes := []string{"a", "b", "c"} + for _, prefix := range prefixes { + for i := 0; i < 5; i++ { + mut := NewMutation() + mut.Set("cf", "col", 1000, []byte("1")) + if err := tbl.Apply(ctx, fmt.Sprintf("%v-%v", prefix, i), mut); err != nil { + t.Fatalf("Mutating row: %v", err) + } + } + } + + if err = adminClient.DropRowRange(ctx, "mytable", "a"); err != nil { + t.Errorf("DropRowRange a: %v", err) + } + if err = adminClient.DropRowRange(ctx, "mytable", "c"); err != nil { + t.Errorf("DropRowRange c: %v", err) + } + if err = adminClient.DropRowRange(ctx, "mytable", "x"); err != nil { + t.Errorf("DropRowRange x: %v", err) + } + + var gotRowCount int + must(tbl.ReadRows(ctx, RowRange{}, func(row Row) bool { + gotRowCount += 1 + if !strings.HasPrefix(row.Key(), "b") { + t.Errorf("Invalid row after dropping range: %v", row) + } + return true + })) + if gotRowCount != 5 { + t.Errorf("Invalid row count after dropping range: got %v, want %v", gotRowCount, 5) + } +} + +func TestInstanceUpdate(t *testing.T) { + testEnv, err := NewIntegrationEnv() + if err != nil { + t.Fatalf("IntegrationEnv: %v", err) + } + defer testEnv.Close() + + timeout := 2 * time.Second + if testEnv.Config().UseProd { + timeout = 5 * time.Minute + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + adminClient, err := testEnv.NewAdminClient() + if err != nil { + t.Fatalf("NewAdminClient: %v", err) + } + + defer adminClient.Close() + + iAdminClient, err := testEnv.NewInstanceAdminClient() + if err != nil { + t.Fatalf("NewInstanceAdminClient: %v", err) + } + + if iAdminClient == nil { + return + } + + defer iAdminClient.Close() + + iInfo, err := iAdminClient.InstanceInfo(ctx, adminClient.instance) + if err != nil { + t.Errorf("InstanceInfo: %v", err) + } + + if iInfo.Name != adminClient.instance { + t.Errorf("InstanceInfo returned name %#v, want %#v", iInfo.Name, adminClient.instance) + } + + if iInfo.DisplayName != adminClient.instance { + t.Errorf("InstanceInfo returned name %#v, want %#v", iInfo.Name, adminClient.instance) + } + + const numNodes = 4 + // update cluster nodes + if err := iAdminClient.UpdateCluster(ctx, adminClient.instance, testEnv.Config().Cluster, int32(numNodes)); err != nil { + t.Errorf("UpdateCluster: %v", err) + } + + // get cluster after updating + cis, err := iAdminClient.GetCluster(ctx, adminClient.instance, testEnv.Config().Cluster) + if err != nil { + t.Errorf("GetCluster %v", err) + } + if cis.ServeNodes != int(numNodes) { + t.Errorf("ServeNodes returned %d, want %d", cis.ServeNodes, int(numNodes)) + } +} + +func TestAdminSnapshotIntegration(t *testing.T) { + testEnv, err := NewIntegrationEnv() + if err != nil { + t.Fatalf("IntegrationEnv: %v", err) + } + defer testEnv.Close() + + if !testEnv.Config().UseProd { + t.Skip("emulator doesn't support snapshots") + } + + timeout := 2 * time.Second + if testEnv.Config().UseProd { + timeout = 5 * time.Minute + } + ctx, _ := context.WithTimeout(context.Background(), timeout) + + adminClient, err := testEnv.NewAdminClient() + if err != nil { + t.Fatalf("NewAdminClient: %v", err) + } + defer adminClient.Close() + + table := testEnv.Config().Table + cluster := testEnv.Config().Cluster + + list := func(cluster string) ([]*SnapshotInfo, error) { + infos := []*SnapshotInfo(nil) + + it := adminClient.Snapshots(ctx, cluster) + for { + s, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return nil, err + } + infos = append(infos, s) + } + return infos, err + } + + // Delete the table at the end of the test. Schedule ahead of time + // in case the client fails + defer adminClient.DeleteTable(ctx, table) + + if err := adminClient.CreateTable(ctx, table); err != nil { + t.Fatalf("Creating table: %v", err) + } + + // Precondition: no snapshots + snapshots, err := list(cluster) + if err != nil { + t.Fatalf("Initial snapshot list: %v", err) + } + if got, want := len(snapshots), 0; got != want { + t.Fatalf("Initial snapshot list len: %d, want: %d", got, want) + } + + // Create snapshot + defer adminClient.DeleteSnapshot(ctx, cluster, "mysnapshot") + + if err = adminClient.SnapshotTable(ctx, table, cluster, "mysnapshot", 5*time.Hour); err != nil { + t.Fatalf("Creating snaphot: %v", err) + } + + // List snapshot + snapshots, err = list(cluster) + if err != nil { + t.Fatalf("Listing snapshots: %v", err) + } + if got, want := len(snapshots), 1; got != want { + t.Fatalf("Listing snapshot count: %d, want: %d", got, want) + } + if got, want := snapshots[0].Name, "mysnapshot"; got != want { + t.Fatalf("Snapshot name: %s, want: %s", got, want) + } + if got, want := snapshots[0].SourceTable, table; got != want { + t.Fatalf("Snapshot SourceTable: %s, want: %s", got, want) + } + if got, want := snapshots[0].DeleteTime, snapshots[0].CreateTime.Add(5*time.Hour); math.Abs(got.Sub(want).Minutes()) > 1 { + t.Fatalf("Snapshot DeleteTime: %s, want: %s", got, want) + } + + // Get snapshot + snapshot, err := adminClient.SnapshotInfo(ctx, cluster, "mysnapshot") + if err != nil { + t.Fatalf("SnapshotInfo: %v", snapshot) + } + if got, want := *snapshot, *snapshots[0]; got != want { + t.Fatalf("SnapshotInfo: %v, want: %v", got, want) + } + + // Restore + restoredTable := table + "-restored" + defer adminClient.DeleteTable(ctx, restoredTable) + if err = adminClient.CreateTableFromSnapshot(ctx, restoredTable, cluster, "mysnapshot"); err != nil { + t.Fatalf("CreateTableFromSnapshot: %v", err) + } + if _, err := adminClient.TableInfo(ctx, restoredTable); err != nil { + t.Fatalf("Restored TableInfo: %v", err) + } + + // Delete snapshot + if err = adminClient.DeleteSnapshot(ctx, cluster, "mysnapshot"); err != nil { + t.Fatalf("DeleteSnapshot: %v", err) + } + snapshots, err = list(cluster) + if err != nil { + t.Fatalf("List after Delete: %v", err) + } + if got, want := len(snapshots), 0; got != want { + t.Fatalf("List after delete len: %d, want: %d", got, want) + } +} + +func TestGranularity(t *testing.T) { + testEnv, err := NewIntegrationEnv() + if err != nil { + t.Fatalf("IntegrationEnv: %v", err) + } + defer testEnv.Close() + + timeout := 2 * time.Second + if testEnv.Config().UseProd { + timeout = 5 * time.Minute + } + ctx, _ := context.WithTimeout(context.Background(), timeout) + + adminClient, err := testEnv.NewAdminClient() + if err != nil { + t.Fatalf("NewAdminClient: %v", err) + } + defer adminClient.Close() + + list := func() []string { + tbls, err := adminClient.Tables(ctx) + if err != nil { + t.Fatalf("Fetching list of tables: %v", err) + } + sort.Strings(tbls) + return tbls + } + containsAll := func(got, want []string) bool { + gotSet := make(map[string]bool) + + for _, s := range got { + gotSet[s] = true + } + for _, s := range want { + if !gotSet[s] { + return false + } + } + return true + } + + defer adminClient.DeleteTable(ctx, "mytable") + + if err := adminClient.CreateTable(ctx, "mytable"); err != nil { + t.Fatalf("Creating table: %v", err) + } + + tables := list() + if got, want := tables, []string{"mytable"}; !containsAll(got, want) { + t.Errorf("adminClient.Tables returned %#v, want %#v", got, want) + } + + // calling ModifyColumnFamilies to check the granularity of table + prefix := adminClient.instancePrefix() + req := &btapb.ModifyColumnFamiliesRequest{ + Name: prefix + "/tables/" + "mytable", + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: "cf", + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{&btapb.ColumnFamily{}}, + }}, + } + table, err := adminClient.tClient.ModifyColumnFamilies(ctx, req) + if err != nil { + t.Fatalf("Creating column family: %v", err) + } + if table.Granularity != btapb.Table_TimestampGranularity(btapb.Table_MILLIS) { + t.Errorf("ModifyColumnFamilies returned granularity %#v, want %#v", table.Granularity, btapb.Table_TimestampGranularity(btapb.Table_MILLIS)) + } +} + +func TestInstanceAdminClient_AppProfile(t *testing.T) { + testEnv, err := NewIntegrationEnv() + if err != nil { + t.Fatalf("IntegrationEnv: %v", err) + } + defer testEnv.Close() + + timeout := 2 * time.Second + if testEnv.Config().UseProd { + timeout = 5 * time.Minute + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + adminClient, err := testEnv.NewAdminClient() + if err != nil { + t.Fatalf("NewAdminClient: %v", err) + } + defer adminClient.Close() + + iAdminClient, err := testEnv.NewInstanceAdminClient() + if err != nil { + t.Fatalf("NewInstanceAdminClient: %v", err) + } + + if iAdminClient == nil { + return + } + + defer iAdminClient.Close() + profile := ProfileConf{ + ProfileID: "app_profile1", + InstanceID: adminClient.instance, + ClusterID: testEnv.Config().Cluster, + Description: "creating new app profile 1", + RoutingPolicy: SingleClusterRouting, + } + + createdProfile, err := iAdminClient.CreateAppProfile(ctx, profile) + if err != nil { + t.Fatalf("Creating app profile: %v", err) + + } + + gotProfile, err := iAdminClient.GetAppProfile(ctx, adminClient.instance, "app_profile1") + + if err != nil { + t.Fatalf("Get app profile: %v", err) + } + + if !proto.Equal(createdProfile, gotProfile) { + t.Fatalf("created profile: %s, got profile: %s", createdProfile.Name, gotProfile.Name) + + } + + list := func(instanceID string) ([]*btapb.AppProfile, error) { + profiles := []*btapb.AppProfile(nil) + + it := iAdminClient.ListAppProfiles(ctx, instanceID) + for { + s, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return nil, err + } + profiles = append(profiles, s) + } + return profiles, err + } + + profiles, err := list(adminClient.instance) + if err != nil { + t.Fatalf("List app profile: %v", err) + } + + if got, want := len(profiles), 1; got != want { + t.Fatalf("Initial app profile list len: %d, want: %d", got, want) + } + + for _, test := range []struct { + desc string + uattrs ProfileAttrsToUpdate + want *btapb.AppProfile // nil means error + }{ + { + desc: "empty update", + uattrs: ProfileAttrsToUpdate{}, + want: nil, + }, + + { + desc: "empty description update", + uattrs: ProfileAttrsToUpdate{Description: ""}, + want: &btapb.AppProfile{ + Name: gotProfile.Name, + Description: "", + RoutingPolicy: gotProfile.RoutingPolicy, + Etag: gotProfile.Etag}, + }, + { + desc: "routing update", + uattrs: ProfileAttrsToUpdate{ + RoutingPolicy: SingleClusterRouting, + ClusterID: testEnv.Config().Cluster, + }, + want: &btapb.AppProfile{ + Name: gotProfile.Name, + Description: "", + Etag: gotProfile.Etag, + RoutingPolicy: &btapb.AppProfile_SingleClusterRouting_{ + SingleClusterRouting: &btapb.AppProfile_SingleClusterRouting{ + ClusterId: testEnv.Config().Cluster, + }}, + }, + }, + } { + err = iAdminClient.UpdateAppProfile(ctx, adminClient.instance, "app_profile1", test.uattrs) + if err != nil { + if test.want != nil { + t.Errorf("%s: %v", test.desc, err) + } + continue + } + if err == nil && test.want == nil { + t.Errorf("%s: got nil, want error", test.desc) + continue + } + + got, _ := iAdminClient.GetAppProfile(ctx, adminClient.instance, "app_profile1") + + if !proto.Equal(got, test.want) { + t.Fatalf("%s : got profile : %v, want profile: %v", test.desc, gotProfile, test.want) + } + + } + + err = iAdminClient.DeleteAppProfile(ctx, adminClient.instance, "app_profile1") + if err != nil { + t.Fatalf("Delete app profile: %v", err) + } + +} diff --git a/vendor/cloud.google.com/go/bigtable/bigtable.go b/vendor/cloud.google.com/go/bigtable/bigtable.go new file mode 100644 index 0000000000..5e0967f207 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/bigtable.go @@ -0,0 +1,887 @@ +/* +Copyright 2015 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 "cloud.google.com/go/bigtable" + +import ( + "errors" + "fmt" + "io" + "strconv" + "time" + + "cloud.google.com/go/bigtable/internal/gax" + btopt "cloud.google.com/go/bigtable/internal/option" + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + "google.golang.org/api/option" + gtransport "google.golang.org/api/transport/grpc" + btpb "google.golang.org/genproto/googleapis/bigtable/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +const prodAddr = "bigtable.googleapis.com:443" + +// Client is a client for reading and writing data to tables in an instance. +// +// A Client is safe to use concurrently, except for its Close method. +type Client struct { + conn *grpc.ClientConn + client btpb.BigtableClient + project, instance string + // App Profiles are part of the private alpha release of Cloud Bigtable replication. + // This feature + // is not currently available to most Cloud Bigtable customers. This feature + // might be changed in backward-incompatible ways and is not recommended for + // production use. It is not subject to any SLA or deprecation policy. + appProfile string +} + +// ClientConfig has configurations for the client. +type ClientConfig struct { + // The id of the app profile to associate with all data operations sent from this client. + // If unspecified, the default app profile for the instance will be used. + AppProfile string +} + +// NewClient creates a new Client for a given project and instance. +// The default ClientConfig will be used. +func NewClient(ctx context.Context, project, instance string, opts ...option.ClientOption) (*Client, error) { + return NewClientWithConfig(ctx, project, instance, ClientConfig{}, opts...) +} + +func NewClientWithConfig(ctx context.Context, project, instance string, config ClientConfig, opts ...option.ClientOption) (*Client, error) { + o, err := btopt.DefaultClientOptions(prodAddr, Scope, clientUserAgent) + if err != nil { + return nil, err + } + // Default to a small connection pool that can be overridden. + o = append(o, + option.WithGRPCConnectionPool(4), + // Set the max size to correspond to server-side limits. + option.WithGRPCDialOption(grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(100<<20), grpc.MaxCallRecvMsgSize(100<<20))), + // TODO(grpc/grpc-go#1388) using connection pool without WithBlock + // can cause RPCs to fail randomly. We can delete this after the issue is fixed. + option.WithGRPCDialOption(grpc.WithBlock())) + o = append(o, opts...) + conn, err := gtransport.Dial(ctx, o...) + if err != nil { + return nil, fmt.Errorf("dialing: %v", err) + } + + return &Client{ + conn: conn, + client: btpb.NewBigtableClient(conn), + project: project, + instance: instance, + appProfile: config.AppProfile, + }, nil +} + +// Close closes the Client. +func (c *Client) Close() error { + return c.conn.Close() +} + +var ( + idempotentRetryCodes = []codes.Code{codes.DeadlineExceeded, codes.Unavailable, codes.Aborted} + isIdempotentRetryCode = make(map[codes.Code]bool) + retryOptions = []gax.CallOption{ + gax.WithDelayTimeoutSettings(100*time.Millisecond, 2000*time.Millisecond, 1.2), + gax.WithRetryCodes(idempotentRetryCodes), + } +) + +func init() { + for _, code := range idempotentRetryCodes { + isIdempotentRetryCode[code] = true + } +} + +func (c *Client) fullTableName(table string) string { + return fmt.Sprintf("projects/%s/instances/%s/tables/%s", c.project, c.instance, table) +} + +// A Table refers to a table. +// +// A Table is safe to use concurrently. +type Table struct { + c *Client + table string + + // Metadata to be sent with each request. + md metadata.MD +} + +// Open opens a table. +func (c *Client) Open(table string) *Table { + return &Table{ + c: c, + table: table, + md: metadata.Pairs(resourcePrefixHeader, c.fullTableName(table)), + } +} + +// TODO(dsymonds): Read method that returns a sequence of ReadItems. + +// ReadRows reads rows from a table. f is called for each row. +// If f returns false, the stream is shut down and ReadRows returns. +// f owns its argument, and f is called serially in order by row key. +// +// By default, the yielded rows will contain all values in all cells. +// Use RowFilter to limit the cells returned. +func (t *Table) ReadRows(ctx context.Context, arg RowSet, f func(Row) bool, opts ...ReadOption) error { + ctx = mergeOutgoingMetadata(ctx, t.md) + + var prevRowKey string + var err error + ctx = traceStartSpan(ctx, "cloud.google.com/go/bigtable.ReadRows") + defer func() { traceEndSpan(ctx, err) }() + attrMap := make(map[string]interface{}) + err = gax.Invoke(ctx, func(ctx context.Context) error { + if !arg.valid() { + // Empty row set, no need to make an API call. + // NOTE: we must return early if arg == RowList{} because reading + // an empty RowList from bigtable returns all rows from that table. + return nil + } + req := &btpb.ReadRowsRequest{ + TableName: t.c.fullTableName(t.table), + AppProfileId: t.c.appProfile, + Rows: arg.proto(), + } + for _, opt := range opts { + opt.set(req) + } + ctx, cancel := context.WithCancel(ctx) // for aborting the stream + defer cancel() + + startTime := time.Now() + stream, err := t.c.client.ReadRows(ctx, req) + if err != nil { + return err + } + cr := newChunkReader() + for { + res, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // Reset arg for next Invoke call. + arg = arg.retainRowsAfter(prevRowKey) + attrMap["rowKey"] = prevRowKey + attrMap["error"] = err.Error() + attrMap["time_secs"] = time.Since(startTime).Seconds() + tracePrintf(ctx, attrMap, "Retry details in ReadRows") + return err + } + attrMap["time_secs"] = time.Since(startTime).Seconds() + attrMap["rowCount"] = len(res.Chunks) + tracePrintf(ctx, attrMap, "Details in ReadRows") + + for _, cc := range res.Chunks { + row, err := cr.Process(cc) + if err != nil { + // No need to prepare for a retry, this is an unretryable error. + return err + } + if row == nil { + continue + } + prevRowKey = row.Key() + if !f(row) { + // Cancel and drain stream. + cancel() + for { + if _, err := stream.Recv(); err != nil { + // The stream has ended. We don't return an error + // because the caller has intentionally interrupted the scan. + return nil + } + } + } + } + if err := cr.Close(); err != nil { + // No need to prepare for a retry, this is an unretryable error. + return err + } + } + return err + }, retryOptions...) + + return err +} + +// ReadRow is a convenience implementation of a single-row reader. +// A missing row will return a zero-length map and a nil error. +func (t *Table) ReadRow(ctx context.Context, row string, opts ...ReadOption) (Row, error) { + var r Row + err := t.ReadRows(ctx, SingleRow(row), func(rr Row) bool { + r = rr + return true + }, opts...) + return r, err +} + +// decodeFamilyProto adds the cell data from f to the given row. +func decodeFamilyProto(r Row, row string, f *btpb.Family) { + fam := f.Name // does not have colon + for _, col := range f.Columns { + for _, cell := range col.Cells { + ri := ReadItem{ + Row: row, + Column: fam + ":" + string(col.Qualifier), + Timestamp: Timestamp(cell.TimestampMicros), + Value: cell.Value, + } + r[fam] = append(r[fam], ri) + } + } +} + +// RowSet is a set of rows to be read. It is satisfied by RowList, RowRange and RowRangeList. +// The serialized size of the RowSet must be no larger than 1MiB. +type RowSet interface { + proto() *btpb.RowSet + + // retainRowsAfter returns a new RowSet that does not include the + // given row key or any row key lexicographically less than it. + retainRowsAfter(lastRowKey string) RowSet + + // Valid reports whether this set can cover at least one row. + valid() bool +} + +// RowList is a sequence of row keys. +type RowList []string + +func (r RowList) proto() *btpb.RowSet { + keys := make([][]byte, len(r)) + for i, row := range r { + keys[i] = []byte(row) + } + return &btpb.RowSet{RowKeys: keys} +} + +func (r RowList) retainRowsAfter(lastRowKey string) RowSet { + var retryKeys RowList + for _, key := range r { + if key > lastRowKey { + retryKeys = append(retryKeys, key) + } + } + return retryKeys +} + +func (r RowList) valid() bool { + return len(r) > 0 +} + +// A RowRange is a half-open interval [Start, Limit) encompassing +// all the rows with keys at least as large as Start, and less than Limit. +// (Bigtable string comparison is the same as Go's.) +// A RowRange can be unbounded, encompassing all keys at least as large as Start. +type RowRange struct { + start string + limit string +} + +// NewRange returns the new RowRange [begin, end). +func NewRange(begin, end string) RowRange { + return RowRange{ + start: begin, + limit: end, + } +} + +// Unbounded tests whether a RowRange is unbounded. +func (r RowRange) Unbounded() bool { + return r.limit == "" +} + +// Contains says whether the RowRange contains the key. +func (r RowRange) Contains(row string) bool { + return r.start <= row && (r.limit == "" || r.limit > row) +} + +// String provides a printable description of a RowRange. +func (r RowRange) String() string { + a := strconv.Quote(r.start) + if r.Unbounded() { + return fmt.Sprintf("[%s,∞)", a) + } + return fmt.Sprintf("[%s,%q)", a, r.limit) +} + +func (r RowRange) proto() *btpb.RowSet { + rr := &btpb.RowRange{ + StartKey: &btpb.RowRange_StartKeyClosed{StartKeyClosed: []byte(r.start)}, + } + if !r.Unbounded() { + rr.EndKey = &btpb.RowRange_EndKeyOpen{EndKeyOpen: []byte(r.limit)} + } + return &btpb.RowSet{RowRanges: []*btpb.RowRange{rr}} +} + +func (r RowRange) retainRowsAfter(lastRowKey string) RowSet { + if lastRowKey == "" || lastRowKey < r.start { + return r + } + // Set the beginning of the range to the row after the last scanned. + start := lastRowKey + "\x00" + if r.Unbounded() { + return InfiniteRange(start) + } + return NewRange(start, r.limit) +} + +func (r RowRange) valid() bool { + return r.Unbounded() || r.start < r.limit +} + +// RowRangeList is a sequence of RowRanges representing the union of the ranges. +type RowRangeList []RowRange + +func (r RowRangeList) proto() *btpb.RowSet { + ranges := make([]*btpb.RowRange, len(r)) + for i, rr := range r { + // RowRange.proto() returns a RowSet with a single element RowRange array + ranges[i] = rr.proto().RowRanges[0] + } + return &btpb.RowSet{RowRanges: ranges} +} + +func (r RowRangeList) retainRowsAfter(lastRowKey string) RowSet { + if lastRowKey == "" { + return r + } + // Return a list of any range that has not yet been completely processed + var ranges RowRangeList + for _, rr := range r { + retained := rr.retainRowsAfter(lastRowKey) + if retained.valid() { + ranges = append(ranges, retained.(RowRange)) + } + } + return ranges +} + +func (r RowRangeList) valid() bool { + for _, rr := range r { + if rr.valid() { + return true + } + } + return false +} + +// SingleRow returns a RowSet for reading a single row. +func SingleRow(row string) RowSet { + return RowList{row} +} + +// PrefixRange returns a RowRange consisting of all keys starting with the prefix. +func PrefixRange(prefix string) RowRange { + return RowRange{ + start: prefix, + limit: prefixSuccessor(prefix), + } +} + +// InfiniteRange returns the RowRange consisting of all keys at least as +// large as start. +func InfiniteRange(start string) RowRange { + return RowRange{ + start: start, + limit: "", + } +} + +// prefixSuccessor returns the lexically smallest string greater than the +// prefix, if it exists, or "" otherwise. In either case, it is the string +// needed for the Limit of a RowRange. +func prefixSuccessor(prefix string) string { + if prefix == "" { + return "" // infinite range + } + n := len(prefix) + for n--; n >= 0 && prefix[n] == '\xff'; n-- { + } + if n == -1 { + return "" + } + ans := []byte(prefix[:n]) + ans = append(ans, prefix[n]+1) + return string(ans) +} + +// A ReadOption is an optional argument to ReadRows. +type ReadOption interface { + set(req *btpb.ReadRowsRequest) +} + +// RowFilter returns a ReadOption that applies f to the contents of read rows. +// +// If multiple RowFilters are provided, only the last is used. To combine filters, +// use ChainFilters or InterleaveFilters instead. +func RowFilter(f Filter) ReadOption { return rowFilter{f} } + +type rowFilter struct{ f Filter } + +func (rf rowFilter) set(req *btpb.ReadRowsRequest) { req.Filter = rf.f.proto() } + +// LimitRows returns a ReadOption that will limit the number of rows to be read. +func LimitRows(limit int64) ReadOption { return limitRows{limit} } + +type limitRows struct{ limit int64 } + +func (lr limitRows) set(req *btpb.ReadRowsRequest) { req.RowsLimit = lr.limit } + +// mutationsAreRetryable returns true if all mutations are idempotent +// and therefore retryable. A mutation is idempotent iff all cell timestamps +// have an explicit timestamp set and do not rely on the timestamp being set on the server. +func mutationsAreRetryable(muts []*btpb.Mutation) bool { + serverTime := int64(ServerTime) + for _, mut := range muts { + setCell := mut.GetSetCell() + if setCell != nil && setCell.TimestampMicros == serverTime { + return false + } + } + return true +} + +// Apply applies a Mutation to a specific row. +func (t *Table) Apply(ctx context.Context, row string, m *Mutation, opts ...ApplyOption) error { + ctx = mergeOutgoingMetadata(ctx, t.md) + after := func(res proto.Message) { + for _, o := range opts { + o.after(res) + } + } + + var err error + ctx = traceStartSpan(ctx, "cloud.google.com/go/bigtable/Apply") + defer func() { traceEndSpan(ctx, err) }() + var callOptions []gax.CallOption + if m.cond == nil { + req := &btpb.MutateRowRequest{ + TableName: t.c.fullTableName(t.table), + AppProfileId: t.c.appProfile, + RowKey: []byte(row), + Mutations: m.ops, + } + if mutationsAreRetryable(m.ops) { + callOptions = retryOptions + } + var res *btpb.MutateRowResponse + err := gax.Invoke(ctx, func(ctx context.Context) error { + var err error + res, err = t.c.client.MutateRow(ctx, req) + return err + }, callOptions...) + if err == nil { + after(res) + } + return err + } + + req := &btpb.CheckAndMutateRowRequest{ + TableName: t.c.fullTableName(t.table), + AppProfileId: t.c.appProfile, + RowKey: []byte(row), + PredicateFilter: m.cond.proto(), + } + if m.mtrue != nil { + if m.mtrue.cond != nil { + return errors.New("bigtable: conditional mutations cannot be nested") + } + req.TrueMutations = m.mtrue.ops + } + if m.mfalse != nil { + if m.mfalse.cond != nil { + return errors.New("bigtable: conditional mutations cannot be nested") + } + req.FalseMutations = m.mfalse.ops + } + if mutationsAreRetryable(req.TrueMutations) && mutationsAreRetryable(req.FalseMutations) { + callOptions = retryOptions + } + var cmRes *btpb.CheckAndMutateRowResponse + err = gax.Invoke(ctx, func(ctx context.Context) error { + var err error + cmRes, err = t.c.client.CheckAndMutateRow(ctx, req) + return err + }, callOptions...) + if err == nil { + after(cmRes) + } + return err +} + +// An ApplyOption is an optional argument to Apply. +type ApplyOption interface { + after(res proto.Message) +} + +type applyAfterFunc func(res proto.Message) + +func (a applyAfterFunc) after(res proto.Message) { a(res) } + +// GetCondMutationResult returns an ApplyOption that reports whether the conditional +// mutation's condition matched. +func GetCondMutationResult(matched *bool) ApplyOption { + return applyAfterFunc(func(res proto.Message) { + if res, ok := res.(*btpb.CheckAndMutateRowResponse); ok { + *matched = res.PredicateMatched + } + }) +} + +// Mutation represents a set of changes for a single row of a table. +type Mutation struct { + ops []*btpb.Mutation + + // for conditional mutations + cond Filter + mtrue, mfalse *Mutation +} + +// NewMutation returns a new mutation. +func NewMutation() *Mutation { + return new(Mutation) +} + +// NewCondMutation returns a conditional mutation. +// The given row filter determines which mutation is applied: +// If the filter matches any cell in the row, mtrue is applied; +// otherwise, mfalse is applied. +// Either given mutation may be nil. +func NewCondMutation(cond Filter, mtrue, mfalse *Mutation) *Mutation { + return &Mutation{cond: cond, mtrue: mtrue, mfalse: mfalse} +} + +// Set sets a value in a specified column, with the given timestamp. +// The timestamp will be truncated to millisecond granularity. +// A timestamp of ServerTime means to use the server timestamp. +func (m *Mutation) Set(family, column string, ts Timestamp, value []byte) { + m.ops = append(m.ops, &btpb.Mutation{Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{ + FamilyName: family, + ColumnQualifier: []byte(column), + TimestampMicros: int64(ts.TruncateToMilliseconds()), + Value: value, + }}}) +} + +// DeleteCellsInColumn will delete all the cells whose columns are family:column. +func (m *Mutation) DeleteCellsInColumn(family, column string) { + m.ops = append(m.ops, &btpb.Mutation{Mutation: &btpb.Mutation_DeleteFromColumn_{DeleteFromColumn: &btpb.Mutation_DeleteFromColumn{ + FamilyName: family, + ColumnQualifier: []byte(column), + }}}) +} + +// DeleteTimestampRange deletes all cells whose columns are family:column +// and whose timestamps are in the half-open interval [start, end). +// If end is zero, it will be interpreted as infinity. +// The timestamps will be truncated to millisecond granularity. +func (m *Mutation) DeleteTimestampRange(family, column string, start, end Timestamp) { + m.ops = append(m.ops, &btpb.Mutation{Mutation: &btpb.Mutation_DeleteFromColumn_{DeleteFromColumn: &btpb.Mutation_DeleteFromColumn{ + FamilyName: family, + ColumnQualifier: []byte(column), + TimeRange: &btpb.TimestampRange{ + StartTimestampMicros: int64(start.TruncateToMilliseconds()), + EndTimestampMicros: int64(end.TruncateToMilliseconds()), + }, + }}}) +} + +// DeleteCellsInFamily will delete all the cells whose columns are family:*. +func (m *Mutation) DeleteCellsInFamily(family string) { + m.ops = append(m.ops, &btpb.Mutation{Mutation: &btpb.Mutation_DeleteFromFamily_{DeleteFromFamily: &btpb.Mutation_DeleteFromFamily{ + FamilyName: family, + }}}) +} + +// DeleteRow deletes the entire row. +func (m *Mutation) DeleteRow() { + m.ops = append(m.ops, &btpb.Mutation{Mutation: &btpb.Mutation_DeleteFromRow_{DeleteFromRow: &btpb.Mutation_DeleteFromRow{}}}) +} + +// entryErr is a container that combines an entry with the error that was returned for it. +// Err may be nil if no error was returned for the Entry, or if the Entry has not yet been processed. +type entryErr struct { + Entry *btpb.MutateRowsRequest_Entry + Err error +} + +// ApplyBulk applies multiple Mutations, up to a maximum of 100,000. +// Each mutation is individually applied atomically, +// but the set of mutations may be applied in any order. +// +// Two types of failures may occur. If the entire process +// fails, (nil, err) will be returned. If specific mutations +// fail to apply, ([]err, nil) will be returned, and the errors +// will correspond to the relevant rowKeys/muts arguments. +// +// Conditional mutations cannot be applied in bulk and providing one will result in an error. +func (t *Table) ApplyBulk(ctx context.Context, rowKeys []string, muts []*Mutation, opts ...ApplyOption) ([]error, error) { + ctx = mergeOutgoingMetadata(ctx, t.md) + if len(rowKeys) != len(muts) { + return nil, fmt.Errorf("mismatched rowKeys and mutation array lengths: %d, %d", len(rowKeys), len(muts)) + } + + origEntries := make([]*entryErr, len(rowKeys)) + for i, key := range rowKeys { + mut := muts[i] + if mut.cond != nil { + return nil, errors.New("conditional mutations cannot be applied in bulk") + } + origEntries[i] = &entryErr{Entry: &btpb.MutateRowsRequest_Entry{RowKey: []byte(key), Mutations: mut.ops}} + } + + // entries will be reduced after each invocation to just what needs to be retried. + entries := make([]*entryErr, len(rowKeys)) + copy(entries, origEntries) + var err error + ctx = traceStartSpan(ctx, "cloud.google.com/go/bigtable/ApplyBulk") + defer func() { traceEndSpan(ctx, err) }() + attrMap := make(map[string]interface{}) + err = gax.Invoke(ctx, func(ctx context.Context) error { + attrMap["rowCount"] = len(entries) + tracePrintf(ctx, attrMap, "Row count in ApplyBulk") + err := t.doApplyBulk(ctx, entries, opts...) + if err != nil { + // We want to retry the entire request with the current entries + return err + } + entries = t.getApplyBulkRetries(entries) + if len(entries) > 0 && len(idempotentRetryCodes) > 0 { + // We have at least one mutation that needs to be retried. + // Return an arbitrary error that is retryable according to callOptions. + return status.Errorf(idempotentRetryCodes[0], "Synthetic error: partial failure of ApplyBulk") + } + return nil + }, retryOptions...) + if err != nil { + return nil, err + } + + // Accumulate all of the errors into an array to return, interspersed with nils for successful + // entries. The absence of any errors means we should return nil. + var errs []error + var foundErr bool + for _, entry := range origEntries { + if entry.Err != nil { + foundErr = true + } + errs = append(errs, entry.Err) + } + if foundErr { + return errs, nil + } + return nil, nil +} + +// getApplyBulkRetries returns the entries that need to be retried +func (t *Table) getApplyBulkRetries(entries []*entryErr) []*entryErr { + var retryEntries []*entryErr + for _, entry := range entries { + err := entry.Err + if err != nil && isIdempotentRetryCode[grpc.Code(err)] && mutationsAreRetryable(entry.Entry.Mutations) { + // There was an error and the entry is retryable. + retryEntries = append(retryEntries, entry) + } + } + return retryEntries +} + +// doApplyBulk does the work of a single ApplyBulk invocation +func (t *Table) doApplyBulk(ctx context.Context, entryErrs []*entryErr, opts ...ApplyOption) error { + after := func(res proto.Message) { + for _, o := range opts { + o.after(res) + } + } + + entries := make([]*btpb.MutateRowsRequest_Entry, len(entryErrs)) + for i, entryErr := range entryErrs { + entries[i] = entryErr.Entry + } + req := &btpb.MutateRowsRequest{ + TableName: t.c.fullTableName(t.table), + AppProfileId: t.c.appProfile, + Entries: entries, + } + stream, err := t.c.client.MutateRows(ctx, req) + if err != nil { + return err + } + for { + res, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + return err + } + + for i, entry := range res.Entries { + s := entry.Status + if s.Code == int32(codes.OK) { + entryErrs[i].Err = nil + } else { + entryErrs[i].Err = status.Errorf(codes.Code(s.Code), s.Message) + } + } + after(res) + } + return nil +} + +// Timestamp is in units of microseconds since 1 January 1970. +type Timestamp int64 + +// ServerTime is a specific Timestamp that may be passed to (*Mutation).Set. +// It indicates that the server's timestamp should be used. +const ServerTime Timestamp = -1 + +// Time converts a time.Time into a Timestamp. +func Time(t time.Time) Timestamp { return Timestamp(t.UnixNano() / 1e3) } + +// Now returns the Timestamp representation of the current time on the client. +func Now() Timestamp { return Time(time.Now()) } + +// Time converts a Timestamp into a time.Time. +func (ts Timestamp) Time() time.Time { return time.Unix(0, int64(ts)*1e3) } + +// TruncateToMilliseconds truncates a Timestamp to millisecond granularity, +// which is currently the only granularity supported. +func (ts Timestamp) TruncateToMilliseconds() Timestamp { + if ts == ServerTime { + return ts + } + return ts - ts%1000 +} + +// ApplyReadModifyWrite applies a ReadModifyWrite to a specific row. +// It returns the newly written cells. +func (t *Table) ApplyReadModifyWrite(ctx context.Context, row string, m *ReadModifyWrite) (Row, error) { + ctx = mergeOutgoingMetadata(ctx, t.md) + req := &btpb.ReadModifyWriteRowRequest{ + TableName: t.c.fullTableName(t.table), + AppProfileId: t.c.appProfile, + RowKey: []byte(row), + Rules: m.ops, + } + res, err := t.c.client.ReadModifyWriteRow(ctx, req) + if err != nil { + return nil, err + } + if res.Row == nil { + return nil, errors.New("unable to apply ReadModifyWrite: res.Row=nil") + } + r := make(Row) + for _, fam := range res.Row.Families { // res is *btpb.Row, fam is *btpb.Family + decodeFamilyProto(r, row, fam) + } + return r, nil +} + +// ReadModifyWrite represents a set of operations on a single row of a table. +// It is like Mutation but for non-idempotent changes. +// When applied, these operations operate on the latest values of the row's cells, +// and result in a new value being written to the relevant cell with a timestamp +// that is max(existing timestamp, current server time). +// +// The application of a ReadModifyWrite is atomic; concurrent ReadModifyWrites will +// be executed serially by the server. +type ReadModifyWrite struct { + ops []*btpb.ReadModifyWriteRule +} + +// NewReadModifyWrite returns a new ReadModifyWrite. +func NewReadModifyWrite() *ReadModifyWrite { return new(ReadModifyWrite) } + +// AppendValue appends a value to a specific cell's value. +// If the cell is unset, it will be treated as an empty value. +func (m *ReadModifyWrite) AppendValue(family, column string, v []byte) { + m.ops = append(m.ops, &btpb.ReadModifyWriteRule{ + FamilyName: family, + ColumnQualifier: []byte(column), + Rule: &btpb.ReadModifyWriteRule_AppendValue{AppendValue: v}, + }) +} + +// Increment interprets the value in a specific cell as a 64-bit big-endian signed integer, +// and adds a value to it. If the cell is unset, it will be treated as zero. +// If the cell is set and is not an 8-byte value, the entire ApplyReadModifyWrite +// operation will fail. +func (m *ReadModifyWrite) Increment(family, column string, delta int64) { + m.ops = append(m.ops, &btpb.ReadModifyWriteRule{ + FamilyName: family, + ColumnQualifier: []byte(column), + Rule: &btpb.ReadModifyWriteRule_IncrementAmount{IncrementAmount: delta}, + }) +} + +// mergeOutgoingMetadata returns a context populated by the existing outgoing metadata, +// if any, joined with internal metadata. +func mergeOutgoingMetadata(ctx context.Context, md metadata.MD) context.Context { + mdCopy, _ := metadata.FromOutgoingContext(ctx) + return metadata.NewOutgoingContext(ctx, metadata.Join(mdCopy, md)) +} + +func (t *Table) SampleRowKeys(ctx context.Context) ([]string, error) { + ctx = mergeOutgoingMetadata(ctx, t.md) + var sampledRowKeys []string + err := gax.Invoke(ctx, func(ctx context.Context) error { + sampledRowKeys = nil + req := &btpb.SampleRowKeysRequest{ + TableName: t.c.fullTableName(t.table), + AppProfileId: t.c.appProfile, + } + ctx, cancel := context.WithCancel(ctx) // for aborting the stream + defer cancel() + + stream, err := t.c.client.SampleRowKeys(ctx, req) + if err != nil { + return err + } + for { + res, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + return err + } + + key := string(res.RowKey) + if key == "" { + continue + } + + sampledRowKeys = append(sampledRowKeys, key) + } + return nil + }, retryOptions...) + return sampledRowKeys, err +} diff --git a/vendor/cloud.google.com/go/bigtable/bigtable_test.go b/vendor/cloud.google.com/go/bigtable/bigtable_test.go new file mode 100644 index 0000000000..06b702def7 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/bigtable_test.go @@ -0,0 +1,1159 @@ +/* +Copyright 2015 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 ( + "fmt" + "math/rand" + "strings" + "sync" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +func TestPrefix(t *testing.T) { + tests := []struct { + prefix, succ string + }{ + {"", ""}, + {"\xff", ""}, // when used, "" means Infinity + {"x\xff", "y"}, + {"\xfe", "\xff"}, + } + for _, tc := range tests { + got := prefixSuccessor(tc.prefix) + if got != tc.succ { + t.Errorf("prefixSuccessor(%q) = %q, want %s", tc.prefix, got, tc.succ) + continue + } + r := PrefixRange(tc.prefix) + if tc.succ == "" && r.limit != "" { + t.Errorf("PrefixRange(%q) got limit %q", tc.prefix, r.limit) + } + if tc.succ != "" && r.limit != tc.succ { + t.Errorf("PrefixRange(%q) got limit %q, want %q", tc.prefix, r.limit, tc.succ) + } + } +} + +func TestApplyErrors(t *testing.T) { + ctx := context.Background() + table := &Table{ + c: &Client{ + project: "P", + instance: "I", + }, + table: "t", + } + f := ColumnFilter("C") + m := NewMutation() + m.DeleteRow() + // Test nested conditional mutations. + cm := NewCondMutation(f, NewCondMutation(f, m, nil), nil) + if err := table.Apply(ctx, "x", cm); err == nil { + t.Error("got nil, want error") + } + cm = NewCondMutation(f, nil, NewCondMutation(f, m, nil)) + if err := table.Apply(ctx, "x", cm); err == nil { + t.Error("got nil, want error") + } +} + +func TestClientIntegration(t *testing.T) { + start := time.Now() + lastCheckpoint := start + checkpoint := func(s string) { + n := time.Now() + t.Logf("[%s] %v since start, %v since last checkpoint", s, n.Sub(start), n.Sub(lastCheckpoint)) + lastCheckpoint = n + } + + testEnv, err := NewIntegrationEnv() + if err != nil { + t.Fatalf("IntegrationEnv: %v", err) + } + + var timeout time.Duration + if testEnv.Config().UseProd { + timeout = 10 * time.Minute + t.Logf("Running test against production") + } else { + timeout = 1 * time.Minute + t.Logf("bttest.Server running on %s", testEnv.Config().AdminEndpoint) + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + client, err := testEnv.NewClient() + if err != nil { + t.Fatalf("Client: %v", err) + } + defer client.Close() + checkpoint("dialed Client") + + adminClient, err := testEnv.NewAdminClient() + if err != nil { + t.Fatalf("AdminClient: %v", err) + } + defer adminClient.Close() + checkpoint("dialed AdminClient") + + table := testEnv.Config().Table + + // Delete the table at the end of the test. + // Do this even before creating the table so that if this is running + // against production and CreateTable fails there's a chance of cleaning it up. + defer adminClient.DeleteTable(ctx, table) + + if err := adminClient.CreateTable(ctx, table); err != nil { + t.Fatalf("Creating table: %v", err) + } + checkpoint("created table") + if err := adminClient.CreateColumnFamily(ctx, table, "follows"); err != nil { + t.Fatalf("Creating column family: %v", err) + } + checkpoint(`created "follows" column family`) + + tbl := client.Open(table) + + // Insert some data. + initialData := map[string][]string{ + "wmckinley": {"tjefferson"}, + "gwashington": {"jadams"}, + "tjefferson": {"gwashington", "jadams"}, // wmckinley set conditionally below + "jadams": {"gwashington", "tjefferson"}, + } + for row, ss := range initialData { + mut := NewMutation() + for _, name := range ss { + mut.Set("follows", name, 1000, []byte("1")) + } + if err := tbl.Apply(ctx, row, mut); err != nil { + t.Errorf("Mutating row %q: %v", row, err) + } + } + checkpoint("inserted initial data") + + // TODO(igorbernstein): re-enable this when ready + //if err := adminClient.WaitForReplication(ctx, table); err != nil { + // t.Errorf("Waiting for replication for table %q: %v", table, err) + //} + //checkpoint("waited for replication") + + // Do a conditional mutation with a complex filter. + mutTrue := NewMutation() + mutTrue.Set("follows", "wmckinley", 1000, []byte("1")) + filter := ChainFilters(ColumnFilter("gwash[iz].*"), ValueFilter(".")) + mut := NewCondMutation(filter, mutTrue, nil) + if err := tbl.Apply(ctx, "tjefferson", mut); err != nil { + t.Errorf("Conditionally mutating row: %v", err) + } + // Do a second condition mutation with a filter that does not match, + // and thus no changes should be made. + mutTrue = NewMutation() + mutTrue.DeleteRow() + filter = ColumnFilter("snoop.dogg") + mut = NewCondMutation(filter, mutTrue, nil) + if err := tbl.Apply(ctx, "tjefferson", mut); err != nil { + t.Errorf("Conditionally mutating row: %v", err) + } + checkpoint("did two conditional mutations") + + // Fetch a row. + row, err := tbl.ReadRow(ctx, "jadams") + if err != nil { + t.Fatalf("Reading a row: %v", err) + } + wantRow := Row{ + "follows": []ReadItem{ + {Row: "jadams", Column: "follows:gwashington", Timestamp: 1000, Value: []byte("1")}, + {Row: "jadams", Column: "follows:tjefferson", Timestamp: 1000, Value: []byte("1")}, + }, + } + if !testutil.Equal(row, wantRow) { + t.Errorf("Read row mismatch.\n got %#v\nwant %#v", row, wantRow) + } + checkpoint("tested ReadRow") + + // Do a bunch of reads with filters. + readTests := []struct { + desc string + rr RowSet + filter Filter // may be nil + limit ReadOption // may be nil + + // We do the read, grab all the cells, turn them into "--", + // and join with a comma. + want string + }{ + { + desc: "read all, unfiltered", + rr: RowRange{}, + want: "gwashington-jadams-1,jadams-gwashington-1,jadams-tjefferson-1,tjefferson-gwashington-1,tjefferson-jadams-1,tjefferson-wmckinley-1,wmckinley-tjefferson-1", + }, + { + desc: "read with InfiniteRange, unfiltered", + rr: InfiniteRange("tjefferson"), + want: "tjefferson-gwashington-1,tjefferson-jadams-1,tjefferson-wmckinley-1,wmckinley-tjefferson-1", + }, + { + desc: "read with NewRange, unfiltered", + rr: NewRange("gargamel", "hubbard"), + want: "gwashington-jadams-1", + }, + { + desc: "read with PrefixRange, unfiltered", + rr: PrefixRange("jad"), + want: "jadams-gwashington-1,jadams-tjefferson-1", + }, + { + desc: "read with SingleRow, unfiltered", + rr: SingleRow("wmckinley"), + want: "wmckinley-tjefferson-1", + }, + { + desc: "read all, with ColumnFilter", + rr: RowRange{}, + filter: ColumnFilter(".*j.*"), // matches "jadams" and "tjefferson" + want: "gwashington-jadams-1,jadams-tjefferson-1,tjefferson-jadams-1,wmckinley-tjefferson-1", + }, + { + desc: "read range, with ColumnRangeFilter", + rr: RowRange{}, + filter: ColumnRangeFilter("follows", "h", "k"), + want: "gwashington-jadams-1,tjefferson-jadams-1", + }, + { + desc: "read range from empty, with ColumnRangeFilter", + rr: RowRange{}, + filter: ColumnRangeFilter("follows", "", "u"), + want: "gwashington-jadams-1,jadams-gwashington-1,jadams-tjefferson-1,tjefferson-gwashington-1,tjefferson-jadams-1,wmckinley-tjefferson-1", + }, + { + desc: "read range from start to empty, with ColumnRangeFilter", + rr: RowRange{}, + filter: ColumnRangeFilter("follows", "h", ""), + want: "gwashington-jadams-1,jadams-tjefferson-1,tjefferson-jadams-1,tjefferson-wmckinley-1,wmckinley-tjefferson-1", + }, + { + desc: "read with RowKeyFilter", + rr: RowRange{}, + filter: RowKeyFilter(".*wash.*"), + want: "gwashington-jadams-1", + }, + { + desc: "read with RowKeyFilter, no matches", + rr: RowRange{}, + filter: RowKeyFilter(".*xxx.*"), + want: "", + }, + { + desc: "read with FamilyFilter, no matches", + rr: RowRange{}, + filter: FamilyFilter(".*xxx.*"), + want: "", + }, + { + desc: "read with ColumnFilter + row limit", + rr: RowRange{}, + filter: ColumnFilter(".*j.*"), // matches "jadams" and "tjefferson" + limit: LimitRows(2), + want: "gwashington-jadams-1,jadams-tjefferson-1", + }, + { + desc: "read all, strip values", + rr: RowRange{}, + filter: StripValueFilter(), + want: "gwashington-jadams-,jadams-gwashington-,jadams-tjefferson-,tjefferson-gwashington-,tjefferson-jadams-,tjefferson-wmckinley-,wmckinley-tjefferson-", + }, + { + desc: "read with ColumnFilter + row limit + strip values", + rr: RowRange{}, + filter: ChainFilters(ColumnFilter(".*j.*"), StripValueFilter()), // matches "jadams" and "tjefferson" + limit: LimitRows(2), + want: "gwashington-jadams-,jadams-tjefferson-", + }, + { + desc: "read with condition, strip values on true", + rr: RowRange{}, + filter: ConditionFilter(ColumnFilter(".*j.*"), StripValueFilter(), nil), + want: "gwashington-jadams-,jadams-gwashington-,jadams-tjefferson-,tjefferson-gwashington-,tjefferson-jadams-,tjefferson-wmckinley-,wmckinley-tjefferson-", + }, + { + desc: "read with condition, strip values on false", + rr: RowRange{}, + filter: ConditionFilter(ColumnFilter(".*xxx.*"), nil, StripValueFilter()), + want: "gwashington-jadams-,jadams-gwashington-,jadams-tjefferson-,tjefferson-gwashington-,tjefferson-jadams-,tjefferson-wmckinley-,wmckinley-tjefferson-", + }, + { + desc: "read with ValueRangeFilter + row limit", + rr: RowRange{}, + filter: ValueRangeFilter([]byte("1"), []byte("5")), // matches our value of "1" + limit: LimitRows(2), + want: "gwashington-jadams-1,jadams-gwashington-1,jadams-tjefferson-1", + }, + { + desc: "read with ValueRangeFilter, no match on exclusive end", + rr: RowRange{}, + filter: ValueRangeFilter([]byte("0"), []byte("1")), // no match + want: "", + }, + { + desc: "read with ValueRangeFilter, no matches", + rr: RowRange{}, + filter: ValueRangeFilter([]byte("3"), []byte("5")), // matches nothing + want: "", + }, + { + desc: "read with InterleaveFilter, no matches on all filters", + rr: RowRange{}, + filter: InterleaveFilters(ColumnFilter(".*x.*"), ColumnFilter(".*z.*")), + want: "", + }, + { + desc: "read with InterleaveFilter, no duplicate cells", + rr: RowRange{}, + filter: InterleaveFilters(ColumnFilter(".*g.*"), ColumnFilter(".*j.*")), + want: "gwashington-jadams-1,jadams-gwashington-1,jadams-tjefferson-1,tjefferson-gwashington-1,tjefferson-jadams-1,wmckinley-tjefferson-1", + }, + { + desc: "read with InterleaveFilter, with duplicate cells", + rr: RowRange{}, + filter: InterleaveFilters(ColumnFilter(".*g.*"), ColumnFilter(".*g.*")), + want: "jadams-gwashington-1,jadams-gwashington-1,tjefferson-gwashington-1,tjefferson-gwashington-1", + }, + { + desc: "read with a RowRangeList and no filter", + rr: RowRangeList{NewRange("gargamel", "hubbard"), InfiniteRange("wmckinley")}, + want: "gwashington-jadams-1,wmckinley-tjefferson-1", + }, + { + desc: "chain that excludes rows and matches nothing, in a condition", + rr: RowRange{}, + filter: ConditionFilter(ChainFilters(ColumnFilter(".*j.*"), ColumnFilter(".*mckinley.*")), StripValueFilter(), nil), + want: "", + }, + { + desc: "chain that ends with an interleave that has no match. covers #804", + rr: RowRange{}, + filter: ConditionFilter(ChainFilters(ColumnFilter(".*j.*"), InterleaveFilters(ColumnFilter(".*x.*"), ColumnFilter(".*z.*"))), StripValueFilter(), nil), + want: "", + }, + } + for _, tc := range readTests { + var opts []ReadOption + if tc.filter != nil { + opts = append(opts, RowFilter(tc.filter)) + } + if tc.limit != nil { + opts = append(opts, tc.limit) + } + var elt []string + err := tbl.ReadRows(context.Background(), tc.rr, func(r Row) bool { + for _, ris := range r { + for _, ri := range ris { + elt = append(elt, formatReadItem(ri)) + } + } + return true + }, opts...) + if err != nil { + t.Errorf("%s: %v", tc.desc, err) + continue + } + if got := strings.Join(elt, ","); got != tc.want { + t.Errorf("%s: wrong reads.\n got %q\nwant %q", tc.desc, got, tc.want) + } + } + // Read a RowList + var elt []string + keys := RowList{"wmckinley", "gwashington", "jadams"} + want := "gwashington-jadams-1,jadams-gwashington-1,jadams-tjefferson-1,wmckinley-tjefferson-1" + err = tbl.ReadRows(ctx, keys, func(r Row) bool { + for _, ris := range r { + for _, ri := range ris { + elt = append(elt, formatReadItem(ri)) + } + } + return true + }) + if err != nil { + t.Errorf("read RowList: %v", err) + } + + if got := strings.Join(elt, ","); got != want { + t.Errorf("bulk read: wrong reads.\n got %q\nwant %q", got, want) + } + checkpoint("tested ReadRows in a few ways") + + // Do a scan and stop part way through. + // Verify that the ReadRows callback doesn't keep running. + stopped := false + err = tbl.ReadRows(ctx, InfiniteRange(""), func(r Row) bool { + if r.Key() < "h" { + return true + } + if !stopped { + stopped = true + return false + } + t.Errorf("ReadRows kept scanning to row %q after being told to stop", r.Key()) + return false + }) + if err != nil { + t.Errorf("Partial ReadRows: %v", err) + } + checkpoint("did partial ReadRows test") + + // Delete a row and check it goes away. + mut = NewMutation() + mut.DeleteRow() + if err := tbl.Apply(ctx, "wmckinley", mut); err != nil { + t.Errorf("Apply DeleteRow: %v", err) + } + row, err = tbl.ReadRow(ctx, "wmckinley") + if err != nil { + t.Fatalf("Reading a row after DeleteRow: %v", err) + } + if len(row) != 0 { + t.Fatalf("Read non-zero row after DeleteRow: %v", row) + } + checkpoint("exercised DeleteRow") + + // Check ReadModifyWrite. + + if err := adminClient.CreateColumnFamily(ctx, table, "counter"); err != nil { + t.Fatalf("Creating column family: %v", err) + } + + appendRMW := func(b []byte) *ReadModifyWrite { + rmw := NewReadModifyWrite() + rmw.AppendValue("counter", "likes", b) + return rmw + } + incRMW := func(n int64) *ReadModifyWrite { + rmw := NewReadModifyWrite() + rmw.Increment("counter", "likes", n) + return rmw + } + rmwSeq := []struct { + desc string + rmw *ReadModifyWrite + want []byte + }{ + { + desc: "append #1", + rmw: appendRMW([]byte{0, 0, 0}), + want: []byte{0, 0, 0}, + }, + { + desc: "append #2", + rmw: appendRMW([]byte{0, 0, 0, 0, 17}), // the remaining 40 bits to make a big-endian 17 + want: []byte{0, 0, 0, 0, 0, 0, 0, 17}, + }, + { + desc: "increment", + rmw: incRMW(8), + want: []byte{0, 0, 0, 0, 0, 0, 0, 25}, + }, + } + for _, step := range rmwSeq { + row, err := tbl.ApplyReadModifyWrite(ctx, "gwashington", step.rmw) + if err != nil { + t.Fatalf("ApplyReadModifyWrite %+v: %v", step.rmw, err) + } + // Make sure the modified cell returned by the RMW operation has a timestamp. + if row["counter"][0].Timestamp == 0 { + t.Errorf("RMW returned cell timestamp: got %v, want > 0", row["counter"][0].Timestamp) + } + clearTimestamps(row) + wantRow := Row{"counter": []ReadItem{{Row: "gwashington", Column: "counter:likes", Value: step.want}}} + if !testutil.Equal(row, wantRow) { + t.Fatalf("After %s,\n got %v\nwant %v", step.desc, row, wantRow) + } + } + + // Check for google-cloud-go/issues/723. RMWs that insert new rows should keep row order sorted in the emulator. + row, err = tbl.ApplyReadModifyWrite(ctx, "issue-723-2", appendRMW([]byte{0})) + if err != nil { + t.Fatalf("ApplyReadModifyWrite null string: %v", err) + } + row, err = tbl.ApplyReadModifyWrite(ctx, "issue-723-1", appendRMW([]byte{0})) + if err != nil { + t.Fatalf("ApplyReadModifyWrite null string: %v", err) + } + // Get only the correct row back on read. + r, err := tbl.ReadRow(ctx, "issue-723-1") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + if r.Key() != "issue-723-1" { + t.Errorf("ApplyReadModifyWrite: incorrect read after RMW,\n got %v\nwant %v", r.Key(), "issue-723-1") + } + checkpoint("tested ReadModifyWrite") + + // Test arbitrary timestamps more thoroughly. + if err := adminClient.CreateColumnFamily(ctx, table, "ts"); err != nil { + t.Fatalf("Creating column family: %v", err) + } + const numVersions = 4 + mut = NewMutation() + for i := 1; i < numVersions; i++ { + // Timestamps are used in thousands because the server + // only permits that granularity. + mut.Set("ts", "col", Timestamp(i*1000), []byte(fmt.Sprintf("val-%d", i))) + mut.Set("ts", "col2", Timestamp(i*1000), []byte(fmt.Sprintf("val-%d", i))) + } + if err := tbl.Apply(ctx, "testrow", mut); err != nil { + t.Fatalf("Mutating row: %v", err) + } + r, err = tbl.ReadRow(ctx, "testrow") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + // These should be returned in descending timestamp order. + {Row: "testrow", Column: "ts:col", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col", Timestamp: 1000, Value: []byte("val-1")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 1000, Value: []byte("val-1")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell with multiple versions,\n got %v\nwant %v", r, wantRow) + } + // Do the same read, but filter to the latest two versions. + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(LatestNFilter(2))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "testrow", Column: "ts:col", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 2000, Value: []byte("val-2")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell with multiple versions and LatestNFilter(2),\n got %v\nwant %v", r, wantRow) + } + // Check cell offset / limit + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(CellsPerRowLimitFilter(3))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "testrow", Column: "ts:col", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col", Timestamp: 1000, Value: []byte("val-1")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell with multiple versions and CellsPerRowLimitFilter(3),\n got %v\nwant %v", r, wantRow) + } + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(CellsPerRowOffsetFilter(3))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "testrow", Column: "ts:col2", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 1000, Value: []byte("val-1")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell with multiple versions and CellsPerRowOffsetFilter(3),\n got %v\nwant %v", r, wantRow) + } + // Check timestamp range filtering (with truncation) + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(TimestampRangeFilterMicros(1001, 3000))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "testrow", Column: "ts:col", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col", Timestamp: 1000, Value: []byte("val-1")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 1000, Value: []byte("val-1")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell with multiple versions and TimestampRangeFilter(1000, 3000),\n got %v\nwant %v", r, wantRow) + } + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(TimestampRangeFilterMicros(1000, 0))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "testrow", Column: "ts:col", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col", Timestamp: 1000, Value: []byte("val-1")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 2000, Value: []byte("val-2")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 1000, Value: []byte("val-1")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell with multiple versions and TimestampRangeFilter(1000, 0),\n got %v\nwant %v", r, wantRow) + } + // Delete non-existing cells, no such column family in this row + // Should not delete anything + if err := adminClient.CreateColumnFamily(ctx, table, "non-existing"); err != nil { + t.Fatalf("Creating column family: %v", err) + } + mut = NewMutation() + mut.DeleteTimestampRange("non-existing", "col", 2000, 3000) // half-open interval + if err := tbl.Apply(ctx, "testrow", mut); err != nil { + t.Fatalf("Mutating row: %v", err) + } + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(LatestNFilter(3))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell was deleted unexpectly,\n got %v\nwant %v", r, wantRow) + } + // Delete non-existing cells, no such column in this column family + // Should not delete anything + mut = NewMutation() + mut.DeleteTimestampRange("ts", "non-existing", 2000, 3000) // half-open interval + if err := tbl.Apply(ctx, "testrow", mut); err != nil { + t.Fatalf("Mutating row: %v", err) + } + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(LatestNFilter(3))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell was deleted unexpectly,\n got %v\nwant %v", r, wantRow) + } + // Delete the cell with timestamp 2000 and repeat the last read, + // checking that we get ts 3000 and ts 1000. + mut = NewMutation() + mut.DeleteTimestampRange("ts", "col", 2001, 3000) // half-open interval + if err := tbl.Apply(ctx, "testrow", mut); err != nil { + t.Fatalf("Mutating row: %v", err) + } + r, err = tbl.ReadRow(ctx, "testrow", RowFilter(LatestNFilter(2))) + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "testrow", Column: "ts:col", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col", Timestamp: 1000, Value: []byte("val-1")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 3000, Value: []byte("val-3")}, + {Row: "testrow", Column: "ts:col2", Timestamp: 2000, Value: []byte("val-2")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Cell with multiple versions and LatestNFilter(2), after deleting timestamp 2000,\n got %v\nwant %v", r, wantRow) + } + checkpoint("tested multiple versions in a cell") + + // Check DeleteCellsInFamily + if err := adminClient.CreateColumnFamily(ctx, table, "status"); err != nil { + t.Fatalf("Creating column family: %v", err) + } + + mut = NewMutation() + mut.Set("status", "start", 2000, []byte("2")) + mut.Set("status", "end", 3000, []byte("3")) + mut.Set("ts", "col", 1000, []byte("1")) + if err := tbl.Apply(ctx, "row1", mut); err != nil { + t.Errorf("Mutating row: %v", err) + } + if err := tbl.Apply(ctx, "row2", mut); err != nil { + t.Errorf("Mutating row: %v", err) + } + + mut = NewMutation() + mut.DeleteCellsInFamily("status") + if err := tbl.Apply(ctx, "row1", mut); err != nil { + t.Errorf("Delete cf: %v", err) + } + + // ColumnFamily removed + r, err = tbl.ReadRow(ctx, "row1") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "row1", Column: "ts:col", Timestamp: 1000, Value: []byte("1")}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("column family was not deleted.\n got %v\n want %v", r, wantRow) + } + + // ColumnFamily not removed + r, err = tbl.ReadRow(ctx, "row2") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{ + "ts": []ReadItem{ + {Row: "row2", Column: "ts:col", Timestamp: 1000, Value: []byte("1")}, + }, + "status": []ReadItem{ + {Row: "row2", Column: "status:end", Timestamp: 3000, Value: []byte("3")}, + {Row: "row2", Column: "status:start", Timestamp: 2000, Value: []byte("2")}, + }, + } + if !testutil.Equal(r, wantRow) { + t.Errorf("Column family was deleted unexpectly.\n got %v\n want %v", r, wantRow) + } + checkpoint("tested family delete") + + // Check DeleteCellsInColumn + mut = NewMutation() + mut.Set("status", "start", 2000, []byte("2")) + mut.Set("status", "middle", 3000, []byte("3")) + mut.Set("status", "end", 1000, []byte("1")) + if err := tbl.Apply(ctx, "row3", mut); err != nil { + t.Errorf("Mutating row: %v", err) + } + mut = NewMutation() + mut.DeleteCellsInColumn("status", "middle") + if err := tbl.Apply(ctx, "row3", mut); err != nil { + t.Errorf("Delete column: %v", err) + } + r, err = tbl.ReadRow(ctx, "row3") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{ + "status": []ReadItem{ + {Row: "row3", Column: "status:end", Timestamp: 1000, Value: []byte("1")}, + {Row: "row3", Column: "status:start", Timestamp: 2000, Value: []byte("2")}, + }, + } + if !testutil.Equal(r, wantRow) { + t.Errorf("Column was not deleted.\n got %v\n want %v", r, wantRow) + } + mut = NewMutation() + mut.DeleteCellsInColumn("status", "start") + if err := tbl.Apply(ctx, "row3", mut); err != nil { + t.Errorf("Delete column: %v", err) + } + r, err = tbl.ReadRow(ctx, "row3") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + wantRow = Row{ + "status": []ReadItem{ + {Row: "row3", Column: "status:end", Timestamp: 1000, Value: []byte("1")}, + }, + } + if !testutil.Equal(r, wantRow) { + t.Errorf("Column was not deleted.\n got %v\n want %v", r, wantRow) + } + mut = NewMutation() + mut.DeleteCellsInColumn("status", "end") + if err := tbl.Apply(ctx, "row3", mut); err != nil { + t.Errorf("Delete column: %v", err) + } + r, err = tbl.ReadRow(ctx, "row3") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + if len(r) != 0 { + t.Errorf("Delete column: got %v, want empty row", r) + } + // Add same cell after delete + mut = NewMutation() + mut.Set("status", "end", 1000, []byte("1")) + if err := tbl.Apply(ctx, "row3", mut); err != nil { + t.Errorf("Mutating row: %v", err) + } + r, err = tbl.ReadRow(ctx, "row3") + if err != nil { + t.Fatalf("Reading row: %v", err) + } + if !testutil.Equal(r, wantRow) { + t.Errorf("Column was not deleted correctly.\n got %v\n want %v", r, wantRow) + } + checkpoint("tested column delete") + + // Do highly concurrent reads/writes. + // TODO(dsymonds): Raise this to 1000 when https://github.com/grpc/grpc-go/issues/205 is resolved. + const maxConcurrency = 100 + var wg sync.WaitGroup + for i := 0; i < maxConcurrency; i++ { + wg.Add(1) + go func() { + defer wg.Done() + switch r := rand.Intn(100); { // r ∈ [0,100) + case 0 <= r && r < 30: + // Do a read. + _, err := tbl.ReadRow(ctx, "testrow", RowFilter(LatestNFilter(1))) + if err != nil { + t.Errorf("Concurrent read: %v", err) + } + case 30 <= r && r < 100: + // Do a write. + mut := NewMutation() + mut.Set("ts", "col", 1000, []byte("data")) + if err := tbl.Apply(ctx, "testrow", mut); err != nil { + t.Errorf("Concurrent write: %v", err) + } + } + }() + } + wg.Wait() + checkpoint("tested high concurrency") + + // Large reads, writes and scans. + bigBytes := make([]byte, 5<<20) // 5 MB is larger than current default gRPC max of 4 MB, but less than the max we set. + nonsense := []byte("lorem ipsum dolor sit amet, ") + fill(bigBytes, nonsense) + mut = NewMutation() + mut.Set("ts", "col", 1000, bigBytes) + if err := tbl.Apply(ctx, "bigrow", mut); err != nil { + t.Errorf("Big write: %v", err) + } + r, err = tbl.ReadRow(ctx, "bigrow") + if err != nil { + t.Errorf("Big read: %v", err) + } + wantRow = Row{"ts": []ReadItem{ + {Row: "bigrow", Column: "ts:col", Timestamp: 1000, Value: bigBytes}, + }} + if !testutil.Equal(r, wantRow) { + t.Errorf("Big read returned incorrect bytes: %v", r) + } + // Now write 1000 rows, each with 82 KB values, then scan them all. + medBytes := make([]byte, 82<<10) + fill(medBytes, nonsense) + sem := make(chan int, 50) // do up to 50 mutations at a time. + for i := 0; i < 1000; i++ { + mut := NewMutation() + mut.Set("ts", "big-scan", 1000, medBytes) + row := fmt.Sprintf("row-%d", i) + wg.Add(1) + go func() { + defer wg.Done() + defer func() { <-sem }() + sem <- 1 + if err := tbl.Apply(ctx, row, mut); err != nil { + t.Errorf("Preparing large scan: %v", err) + } + }() + } + wg.Wait() + n := 0 + err = tbl.ReadRows(ctx, PrefixRange("row-"), func(r Row) bool { + for _, ris := range r { + for _, ri := range ris { + n += len(ri.Value) + } + } + return true + }, RowFilter(ColumnFilter("big-scan"))) + if err != nil { + t.Errorf("Doing large scan: %v", err) + } + if want := 1000 * len(medBytes); n != want { + t.Errorf("Large scan returned %d bytes, want %d", n, want) + } + // Scan a subset of the 1000 rows that we just created, using a LimitRows ReadOption. + rc := 0 + wantRc := 3 + err = tbl.ReadRows(ctx, PrefixRange("row-"), func(r Row) bool { + rc++ + return true + }, LimitRows(int64(wantRc))) + if rc != wantRc { + t.Errorf("Scan with row limit returned %d rows, want %d", rc, wantRc) + } + checkpoint("tested big read/write/scan") + + // Test bulk mutations + if err := adminClient.CreateColumnFamily(ctx, table, "bulk"); err != nil { + t.Fatalf("Creating column family: %v", err) + } + bulkData := map[string][]string{ + "red sox": {"2004", "2007", "2013"}, + "patriots": {"2001", "2003", "2004", "2014"}, + "celtics": {"1981", "1984", "1986", "2008"}, + } + var rowKeys []string + var muts []*Mutation + for row, ss := range bulkData { + mut := NewMutation() + for _, name := range ss { + mut.Set("bulk", name, 1000, []byte("1")) + } + rowKeys = append(rowKeys, row) + muts = append(muts, mut) + } + status, err := tbl.ApplyBulk(ctx, rowKeys, muts) + if err != nil { + t.Fatalf("Bulk mutating rows %q: %v", rowKeys, err) + } + if status != nil { + t.Errorf("non-nil errors: %v", err) + } + checkpoint("inserted bulk data") + + // Read each row back + for rowKey, ss := range bulkData { + row, err := tbl.ReadRow(ctx, rowKey) + if err != nil { + t.Fatalf("Reading a bulk row: %v", err) + } + var wantItems []ReadItem + for _, val := range ss { + wantItems = append(wantItems, ReadItem{Row: rowKey, Column: "bulk:" + val, Timestamp: 1000, Value: []byte("1")}) + } + wantRow := Row{"bulk": wantItems} + if !testutil.Equal(row, wantRow) { + t.Errorf("Read row mismatch.\n got %#v\nwant %#v", row, wantRow) + } + } + checkpoint("tested reading from bulk insert") + + // Test bulk write errors. + // Note: Setting timestamps as ServerTime makes sure the mutations are not retried on error. + badMut := NewMutation() + badMut.Set("badfamily", "col", ServerTime, nil) + badMut2 := NewMutation() + badMut2.Set("badfamily2", "goodcol", ServerTime, []byte("1")) + status, err = tbl.ApplyBulk(ctx, []string{"badrow", "badrow2"}, []*Mutation{badMut, badMut2}) + if err != nil { + t.Fatalf("Bulk mutating rows %q: %v", rowKeys, err) + } + if status == nil { + t.Errorf("No errors for bad bulk mutation") + } else if status[0] == nil || status[1] == nil { + t.Errorf("No error for bad bulk mutation") + } +} + +type requestCountingInterceptor struct { + grpc.ClientStream + requestCallback func() +} + +func (i *requestCountingInterceptor) SendMsg(m interface{}) error { + i.requestCallback() + return i.ClientStream.SendMsg(m) +} + +func (i *requestCountingInterceptor) RecvMsg(m interface{}) error { + return i.ClientStream.RecvMsg(m) +} + +func requestCallback(callback func()) func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + clientStream, err := streamer(ctx, desc, cc, method, opts...) + return &requestCountingInterceptor{ + ClientStream: clientStream, + requestCallback: callback, + }, err + } +} + +// TestReadRowsInvalidRowSet verifies that the client doesn't send ReadRows() requests with invalid RowSets. +func TestReadRowsInvalidRowSet(t *testing.T) { + testEnv, err := NewEmulatedEnv(IntegrationTestConfig{}) + if err != nil { + t.Fatalf("NewEmulatedEnv failed: %v", err) + } + var requestCount int + incrementRequestCount := func() { requestCount++ } + conn, err := grpc.Dial(testEnv.server.Addr, grpc.WithInsecure(), grpc.WithBlock(), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(100<<20), grpc.MaxCallRecvMsgSize(100<<20)), + grpc.WithStreamInterceptor(requestCallback(incrementRequestCount)), + ) + if err != nil { + t.Fatalf("grpc.Dial failed: %v", err) + } + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + adminClient, err := NewAdminClient(ctx, testEnv.config.Project, testEnv.config.Instance, option.WithGRPCConn(conn)) + if err != nil { + t.Fatalf("NewClient failed: %v", err) + } + defer adminClient.Close() + if err := adminClient.CreateTable(ctx, testEnv.config.Table); err != nil { + t.Fatalf("CreateTable(%v) failed: %v", testEnv.config.Table, err) + } + client, err := NewClient(ctx, testEnv.config.Project, testEnv.config.Instance, option.WithGRPCConn(conn)) + if err != nil { + t.Fatalf("NewClient failed: %v", err) + } + defer client.Close() + table := client.Open(testEnv.config.Table) + tests := []struct { + rr RowSet + valid bool + }{ + { + rr: RowRange{}, + valid: true, + }, + { + rr: RowRange{start: "b"}, + valid: true, + }, + { + rr: RowRange{start: "b", limit: "c"}, + valid: true, + }, + { + rr: RowRange{start: "b", limit: "a"}, + valid: false, + }, + { + rr: RowList{"a"}, + valid: true, + }, + { + rr: RowList{}, + valid: false, + }, + } + for _, test := range tests { + requestCount = 0 + err = table.ReadRows(ctx, test.rr, func(r Row) bool { return true }) + if err != nil { + t.Fatalf("ReadRows(%v) failed: %v", test.rr, err) + } + requestValid := requestCount != 0 + if requestValid != test.valid { + t.Errorf("%s: got %v, want %v", test.rr, requestValid, test.valid) + } + } +} + +func formatReadItem(ri ReadItem) string { + // Use the column qualifier only to make the test data briefer. + col := ri.Column[strings.Index(ri.Column, ":")+1:] + return fmt.Sprintf("%s-%s-%s", ri.Row, col, ri.Value) +} + +func fill(b, sub []byte) { + for len(b) > len(sub) { + n := copy(b, sub) + b = b[n:] + } +} + +func clearTimestamps(r Row) { + for _, ris := range r { + for i := range ris { + ris[i].Timestamp = 0 + } + } +} + +func TestSampleRowKeys(t *testing.T) { + start := time.Now() + lastCheckpoint := start + checkpoint := func(s string) { + n := time.Now() + t.Logf("[%s] %v since start, %v since last checkpoint", s, n.Sub(start), n.Sub(lastCheckpoint)) + lastCheckpoint = n + } + ctx := context.Background() + client, adminClient, table, err := doSetup(ctx) + if err != nil { + t.Fatalf("%v", err) + } + defer client.Close() + defer adminClient.Close() + tbl := client.Open(table) + // Delete the table at the end of the test. + // Do this even before creating the table so that if this is running + // against production and CreateTable fails there's a chance of cleaning it up. + defer adminClient.DeleteTable(ctx, table) + + // Insert some data. + initialData := map[string][]string{ + "wmckinley11": {"tjefferson11"}, + "gwashington77": {"jadams77"}, + "tjefferson0": {"gwashington0", "jadams0"}, + } + + for row, ss := range initialData { + mut := NewMutation() + for _, name := range ss { + mut.Set("follows", name, 1000, []byte("1")) + } + if err := tbl.Apply(ctx, row, mut); err != nil { + t.Errorf("Mutating row %q: %v", row, err) + } + } + checkpoint("inserted initial data") + sampleKeys, err := tbl.SampleRowKeys(context.Background()) + if err != nil { + t.Errorf("%s: %v", "SampleRowKeys:", err) + } + if len(sampleKeys) == 0 { + t.Error("SampleRowKeys length 0") + } + checkpoint("tested SampleRowKeys.") +} + +func doSetup(ctx context.Context) (*Client, *AdminClient, string, error) { + start := time.Now() + lastCheckpoint := start + checkpoint := func(s string) { + n := time.Now() + fmt.Printf("[%s] %v since start, %v since last checkpoint", s, n.Sub(start), n.Sub(lastCheckpoint)) + lastCheckpoint = n + } + + testEnv, err := NewIntegrationEnv() + if err != nil { + return nil, nil, "", fmt.Errorf("IntegrationEnv: %v", err) + } + + var timeout time.Duration + if testEnv.Config().UseProd { + timeout = 10 * time.Minute + fmt.Printf("Running test against production") + } else { + timeout = 1 * time.Minute + fmt.Printf("bttest.Server running on %s", testEnv.Config().AdminEndpoint) + } + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + client, err := testEnv.NewClient() + if err != nil { + return nil, nil, "", fmt.Errorf("Client: %v", err) + } + checkpoint("dialed Client") + + adminClient, err := testEnv.NewAdminClient() + if err != nil { + return nil, nil, "", fmt.Errorf("AdminClient: %v", err) + } + checkpoint("dialed AdminClient") + + table := testEnv.Config().Table + if err := adminClient.CreateTable(ctx, table); err != nil { + return nil, nil, "", fmt.Errorf("Creating table: %v", err) + } + checkpoint("created table") + if err := adminClient.CreateColumnFamily(ctx, table, "follows"); err != nil { + return nil, nil, "", fmt.Errorf("Creating column family: %v", err) + } + checkpoint(`created "follows" column family`) + + return client, adminClient, table, nil +} diff --git a/vendor/cloud.google.com/go/bigtable/bttest/example_test.go b/vendor/cloud.google.com/go/bigtable/bttest/example_test.go new file mode 100644 index 0000000000..f979db50bb --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/bttest/example_test.go @@ -0,0 +1,83 @@ +/* +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 bttest_test + +import ( + "fmt" + "log" + + "cloud.google.com/go/bigtable" + "cloud.google.com/go/bigtable/bttest" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +func ExampleNewServer() { + + srv, err := bttest.NewServer("localhost:0") + + if err != nil { + log.Fatalln(err) + } + + ctx := context.Background() + + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + if err != nil { + log.Fatalln(err) + } + + proj, instance := "proj", "instance" + + adminClient, err := bigtable.NewAdminClient(ctx, proj, instance, option.WithGRPCConn(conn)) + if err != nil { + log.Fatalln(err) + } + + if err = adminClient.CreateTable(ctx, "example"); err != nil { + log.Fatalln(err) + } + + if err = adminClient.CreateColumnFamily(ctx, "example", "links"); err != nil { + log.Fatalln(err) + } + + client, err := bigtable.NewClient(ctx, proj, instance, option.WithGRPCConn(conn)) + if err != nil { + log.Fatalln(err) + } + tbl := client.Open("example") + + mut := bigtable.NewMutation() + mut.Set("links", "golang.org", bigtable.Now(), []byte("Gophers!")) + if err = tbl.Apply(ctx, "com.google.cloud", mut); err != nil { + log.Fatalln(err) + } + + if row, err := tbl.ReadRow(ctx, "com.google.cloud"); err != nil { + log.Fatalln(err) + } else { + for _, column := range row["links"] { + fmt.Println(column.Column) + fmt.Println(string(column.Value)) + } + } + + // Output: + // links:golang.org + // Gophers! +} diff --git a/vendor/cloud.google.com/go/bigtable/bttest/inmem.go b/vendor/cloud.google.com/go/bigtable/bttest/inmem.go new file mode 100644 index 0000000000..52a7205509 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/bttest/inmem.go @@ -0,0 +1,1329 @@ +/* +Copyright 2015 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 bttest contains test helpers for working with the bigtable package. + +To use a Server, create it, and then connect to it with no security: +(The project/instance values are ignored.) + srv, err := bttest.NewServer("localhost:0") + ... + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + ... + client, err := bigtable.NewClient(ctx, proj, instance, + option.WithGRPCConn(conn)) + ... +*/ +package bttest // import "cloud.google.com/go/bigtable/bttest" + +import ( + "encoding/binary" + "fmt" + "log" + "math/rand" + "net" + "regexp" + "sort" + "strings" + "sync" + "time" + + "bytes" + + emptypb "github.com/golang/protobuf/ptypes/empty" + "github.com/golang/protobuf/ptypes/wrappers" + "github.com/google/btree" + "golang.org/x/net/context" + btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2" + btpb "google.golang.org/genproto/googleapis/bigtable/v2" + statpb "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + // MilliSeconds field of the minimum valid Timestamp. + minValidMilliSeconds = 0 + + // MilliSeconds field of the max valid Timestamp. + maxValidMilliSeconds = int64(time.Millisecond) * 253402300800 +) + +// Server is an in-memory Cloud Bigtable fake. +// It is unauthenticated, and only a rough approximation. +type Server struct { + Addr string + + l net.Listener + srv *grpc.Server + s *server +} + +// server is the real implementation of the fake. +// It is a separate and unexported type so the API won't be cluttered with +// methods that are only relevant to the fake's implementation. +type server struct { + mu sync.Mutex + tables map[string]*table // keyed by fully qualified name + gcc chan int // set when gcloop starts, closed when server shuts down + + // Any unimplemented methods will cause a panic. + btapb.BigtableTableAdminServer + btpb.BigtableServer +} + +// NewServer creates a new Server. +// The Server will be listening for gRPC connections, without TLS, +// on the provided address. The resolved address is named by the Addr field. +func NewServer(laddr string, opt ...grpc.ServerOption) (*Server, error) { + l, err := net.Listen("tcp", laddr) + if err != nil { + return nil, err + } + + s := &Server{ + Addr: l.Addr().String(), + l: l, + srv: grpc.NewServer(opt...), + s: &server{ + tables: make(map[string]*table), + }, + } + btapb.RegisterBigtableTableAdminServer(s.srv, s.s) + btpb.RegisterBigtableServer(s.srv, s.s) + + go s.srv.Serve(s.l) + + return s, nil +} + +// Close shuts down the server. +func (s *Server) Close() { + s.s.mu.Lock() + if s.s.gcc != nil { + close(s.s.gcc) + } + s.s.mu.Unlock() + + s.srv.Stop() + s.l.Close() +} + +func (s *server) CreateTable(ctx context.Context, req *btapb.CreateTableRequest) (*btapb.Table, error) { + tbl := req.Parent + "/tables/" + req.TableId + + s.mu.Lock() + if _, ok := s.tables[tbl]; ok { + s.mu.Unlock() + return nil, status.Errorf(codes.AlreadyExists, "table %q already exists", tbl) + } + s.tables[tbl] = newTable(req) + s.mu.Unlock() + + return &btapb.Table{Name: tbl}, nil +} + +func (s *server) ListTables(ctx context.Context, req *btapb.ListTablesRequest) (*btapb.ListTablesResponse, error) { + res := &btapb.ListTablesResponse{} + prefix := req.Parent + "/tables/" + + s.mu.Lock() + for tbl := range s.tables { + if strings.HasPrefix(tbl, prefix) { + res.Tables = append(res.Tables, &btapb.Table{Name: tbl}) + } + } + s.mu.Unlock() + + return res, nil +} + +func (s *server) GetTable(ctx context.Context, req *btapb.GetTableRequest) (*btapb.Table, error) { + tbl := req.Name + + s.mu.Lock() + tblIns, ok := s.tables[tbl] + s.mu.Unlock() + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", tbl) + } + + return &btapb.Table{ + Name: tbl, + ColumnFamilies: toColumnFamilies(tblIns.columnFamilies()), + }, nil +} + +func (s *server) DeleteTable(ctx context.Context, req *btapb.DeleteTableRequest) (*emptypb.Empty, error) { + s.mu.Lock() + defer s.mu.Unlock() + if _, ok := s.tables[req.Name]; !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.Name) + } + delete(s.tables, req.Name) + return &emptypb.Empty{}, nil +} + +func (s *server) ModifyColumnFamilies(ctx context.Context, req *btapb.ModifyColumnFamiliesRequest) (*btapb.Table, error) { + s.mu.Lock() + tbl, ok := s.tables[req.Name] + s.mu.Unlock() + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.Name) + } + + tbl.mu.Lock() + defer tbl.mu.Unlock() + + for _, mod := range req.Modifications { + if create := mod.GetCreate(); create != nil { + if _, ok := tbl.families[mod.Id]; ok { + return nil, status.Errorf(codes.AlreadyExists, "family %q already exists", mod.Id) + } + newcf := &columnFamily{ + name: req.Name + "/columnFamilies/" + mod.Id, + order: tbl.counter, + gcRule: create.GcRule, + } + tbl.counter++ + tbl.families[mod.Id] = newcf + } else if mod.GetDrop() { + if _, ok := tbl.families[mod.Id]; !ok { + return nil, fmt.Errorf("can't delete unknown family %q", mod.Id) + } + delete(tbl.families, mod.Id) + } else if modify := mod.GetUpdate(); modify != nil { + if _, ok := tbl.families[mod.Id]; !ok { + return nil, fmt.Errorf("no such family %q", mod.Id) + } + newcf := &columnFamily{ + name: req.Name + "/columnFamilies/" + mod.Id, + gcRule: modify.GcRule, + } + // assume that we ALWAYS want to replace by the new setting + // we may need partial update through + tbl.families[mod.Id] = newcf + } + } + + s.needGC() + return &btapb.Table{ + Name: req.Name, + ColumnFamilies: toColumnFamilies(tbl.families), + Granularity: btapb.Table_TimestampGranularity(btapb.Table_MILLIS), + }, nil +} + +func (s *server) DropRowRange(ctx context.Context, req *btapb.DropRowRangeRequest) (*emptypb.Empty, error) { + s.mu.Lock() + defer s.mu.Unlock() + tbl, ok := s.tables[req.Name] + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.Name) + } + + if req.GetDeleteAllDataFromTable() { + tbl.rows = btree.New(btreeDegree) + } else { + // Delete rows by prefix. + prefixBytes := req.GetRowKeyPrefix() + if prefixBytes == nil { + return nil, fmt.Errorf("missing row key prefix") + } + prefix := string(prefixBytes) + + // The BTree does not specify what happens if rows are deleted during + // iteration, and it provides no "delete range" method. + // So we collect the rows first, then delete them one by one. + var rowsToDelete []*row + tbl.rows.AscendGreaterOrEqual(btreeKey(prefix), func(i btree.Item) bool { + r := i.(*row) + if strings.HasPrefix(r.key, prefix) { + rowsToDelete = append(rowsToDelete, r) + return true + } else { + return false // stop iteration + } + }) + for _, r := range rowsToDelete { + tbl.rows.Delete(r) + } + } + return &emptypb.Empty{}, nil +} + +// This is a private alpha release of Cloud Bigtable replication. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (s *server) GenerateConsistencyToken(ctx context.Context, req *btapb.GenerateConsistencyTokenRequest) (*btapb.GenerateConsistencyTokenResponse, error) { + // Check that the table exists. + _, ok := s.tables[req.Name] + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.Name) + } + + return &btapb.GenerateConsistencyTokenResponse{ + ConsistencyToken: "TokenFor-" + req.Name, + }, nil +} + +// This is a private alpha release of Cloud Bigtable replication. This feature +// is not currently available to most Cloud Bigtable customers. This feature +// might be changed in backward-incompatible ways and is not recommended for +// production use. It is not subject to any SLA or deprecation policy. +func (s *server) CheckConsistency(ctx context.Context, req *btapb.CheckConsistencyRequest) (*btapb.CheckConsistencyResponse, error) { + // Check that the table exists. + _, ok := s.tables[req.Name] + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.Name) + } + + // Check this is the right token. + if req.ConsistencyToken != "TokenFor-"+req.Name { + return nil, status.Errorf(codes.InvalidArgument, "token %q not valid", req.ConsistencyToken) + } + + // Single cluster instances are always consistent. + return &btapb.CheckConsistencyResponse{ + Consistent: true, + }, nil +} + +func (s *server) ReadRows(req *btpb.ReadRowsRequest, stream btpb.Bigtable_ReadRowsServer) error { + s.mu.Lock() + tbl, ok := s.tables[req.TableName] + s.mu.Unlock() + if !ok { + return status.Errorf(codes.NotFound, "table %q not found", req.TableName) + } + + // Rows to read can be specified by a set of row keys and/or a set of row ranges. + // Output is a stream of sorted, de-duped rows. + tbl.mu.RLock() + rowSet := make(map[string]*row) + + addRow := func(i btree.Item) bool { + r := i.(*row) + rowSet[r.key] = r + return true + } + + if req.Rows != nil && + len(req.Rows.RowKeys)+len(req.Rows.RowRanges) > 0 { + // Add the explicitly given keys + for _, key := range req.Rows.RowKeys { + k := string(key) + if i := tbl.rows.Get(btreeKey(k)); i != nil { + addRow(i) + } + } + + // Add keys from row ranges + for _, rr := range req.Rows.RowRanges { + var start, end string + switch sk := rr.StartKey.(type) { + case *btpb.RowRange_StartKeyClosed: + start = string(sk.StartKeyClosed) + case *btpb.RowRange_StartKeyOpen: + start = string(sk.StartKeyOpen) + "\x00" + } + switch ek := rr.EndKey.(type) { + case *btpb.RowRange_EndKeyClosed: + end = string(ek.EndKeyClosed) + "\x00" + case *btpb.RowRange_EndKeyOpen: + end = string(ek.EndKeyOpen) + } + switch { + case start == "" && end == "": + tbl.rows.Ascend(addRow) // all rows + case start == "": + tbl.rows.AscendLessThan(btreeKey(end), addRow) + case end == "": + tbl.rows.AscendGreaterOrEqual(btreeKey(start), addRow) + default: + tbl.rows.AscendRange(btreeKey(start), btreeKey(end), addRow) + } + } + } else { + // Read all rows + tbl.rows.Ascend(addRow) + } + tbl.mu.RUnlock() + + rows := make([]*row, 0, len(rowSet)) + for _, r := range rowSet { + rows = append(rows, r) + } + sort.Sort(byRowKey(rows)) + + limit := int(req.RowsLimit) + count := 0 + for _, r := range rows { + if limit > 0 && count >= limit { + return nil + } + streamed, err := streamRow(stream, r, req.Filter) + if err != nil { + return err + } + if streamed { + count++ + } + } + return nil +} + +// streamRow filters the given row and sends it via the given stream. +// Returns true if at least one cell matched the filter and was streamed, false otherwise. +func streamRow(stream btpb.Bigtable_ReadRowsServer, r *row, f *btpb.RowFilter) (bool, error) { + r.mu.Lock() + nr := r.copy() + r.mu.Unlock() + r = nr + + if !filterRow(f, r) { + return false, nil + } + + rrr := &btpb.ReadRowsResponse{} + families := r.sortedFamilies() + for _, fam := range families { + for _, colName := range fam.colNames { + cells := fam.cells[colName] + if len(cells) == 0 { + continue + } + // TODO(dsymonds): Apply transformers. + for _, cell := range cells { + rrr.Chunks = append(rrr.Chunks, &btpb.ReadRowsResponse_CellChunk{ + RowKey: []byte(r.key), + FamilyName: &wrappers.StringValue{Value: fam.name}, + Qualifier: &wrappers.BytesValue{Value: []byte(colName)}, + TimestampMicros: cell.ts, + Value: cell.value, + }) + } + } + } + // We can't have a cell with just COMMIT set, which would imply a new empty cell. + // So modify the last cell to have the COMMIT flag set. + if len(rrr.Chunks) > 0 { + rrr.Chunks[len(rrr.Chunks)-1].RowStatus = &btpb.ReadRowsResponse_CellChunk_CommitRow{CommitRow: true} + } + + return true, stream.Send(rrr) +} + +// filterRow modifies a row with the given filter. Returns true if at least one cell from the row matches, +// false otherwise. +func filterRow(f *btpb.RowFilter, r *row) bool { + if f == nil { + return true + } + // Handle filters that apply beyond just including/excluding cells. + switch f := f.Filter.(type) { + case *btpb.RowFilter_BlockAllFilter: + return !f.BlockAllFilter + case *btpb.RowFilter_PassAllFilter: + return f.PassAllFilter + case *btpb.RowFilter_Chain_: + for _, sub := range f.Chain.Filters { + if !filterRow(sub, r) { + return false + } + } + return true + case *btpb.RowFilter_Interleave_: + srs := make([]*row, 0, len(f.Interleave.Filters)) + for _, sub := range f.Interleave.Filters { + sr := r.copy() + filterRow(sub, sr) + srs = append(srs, sr) + } + // merge + // TODO(dsymonds): is this correct? + r.families = make(map[string]*family) + for _, sr := range srs { + for _, fam := range sr.families { + f := r.getOrCreateFamily(fam.name, fam.order) + for colName, cs := range fam.cells { + f.cells[colName] = append(f.cellsByColumn(colName), cs...) + } + } + } + var count int + for _, fam := range r.families { + for _, cs := range fam.cells { + sort.Sort(byDescTS(cs)) + count += len(cs) + } + } + return count > 0 + case *btpb.RowFilter_CellsPerColumnLimitFilter: + lim := int(f.CellsPerColumnLimitFilter) + for _, fam := range r.families { + for col, cs := range fam.cells { + if len(cs) > lim { + fam.cells[col] = cs[:lim] + } + } + } + return true + case *btpb.RowFilter_Condition_: + if filterRow(f.Condition.PredicateFilter, r.copy()) { + if f.Condition.TrueFilter == nil { + return false + } + return filterRow(f.Condition.TrueFilter, r) + } + if f.Condition.FalseFilter == nil { + return false + } + return filterRow(f.Condition.FalseFilter, r) + case *btpb.RowFilter_RowKeyRegexFilter: + pat := string(f.RowKeyRegexFilter) + rx, err := regexp.Compile(pat) + if err != nil { + log.Printf("Bad rowkey_regex_filter pattern %q: %v", pat, err) + return false + } + if !rx.MatchString(r.key) { + return false + } + case *btpb.RowFilter_CellsPerRowLimitFilter: + // Grab the first n cells in the row. + lim := int(f.CellsPerRowLimitFilter) + for _, fam := range r.families { + for _, col := range fam.colNames { + cs := fam.cells[col] + if len(cs) > lim { + fam.cells[col] = cs[:lim] + lim = 0 + } else { + lim -= len(cs) + } + } + } + return true + case *btpb.RowFilter_CellsPerRowOffsetFilter: + // Skip the first n cells in the row. + offset := int(f.CellsPerRowOffsetFilter) + for _, fam := range r.families { + for _, col := range fam.colNames { + cs := fam.cells[col] + if len(cs) > offset { + fam.cells[col] = cs[offset:] + offset = 0 + return true + } else { + fam.cells[col] = cs[:0] + offset -= len(cs) + } + } + } + return true + } + + // Any other case, operate on a per-cell basis. + cellCount := 0 + for _, fam := range r.families { + for colName, cs := range fam.cells { + fam.cells[colName] = filterCells(f, fam.name, colName, cs) + cellCount += len(fam.cells[colName]) + } + } + return cellCount > 0 +} + +func filterCells(f *btpb.RowFilter, fam, col string, cs []cell) []cell { + var ret []cell + for _, cell := range cs { + if includeCell(f, fam, col, cell) { + cell = modifyCell(f, cell) + ret = append(ret, cell) + } + } + return ret +} + +func modifyCell(f *btpb.RowFilter, c cell) cell { + if f == nil { + return c + } + // Consider filters that may modify the cell contents + switch f.Filter.(type) { + case *btpb.RowFilter_StripValueTransformer: + return cell{ts: c.ts} + default: + return c + } +} + +func includeCell(f *btpb.RowFilter, fam, col string, cell cell) bool { + if f == nil { + return true + } + // TODO(dsymonds): Implement many more filters. + switch f := f.Filter.(type) { + case *btpb.RowFilter_CellsPerColumnLimitFilter: + // Don't log, row-level filter + return true + case *btpb.RowFilter_RowKeyRegexFilter: + // Don't log, row-level filter + return true + case *btpb.RowFilter_StripValueTransformer: + // Don't log, cell-modifying filter + return true + default: + log.Printf("WARNING: don't know how to handle filter of type %T (ignoring it)", f) + return true + case *btpb.RowFilter_FamilyNameRegexFilter: + pat := string(f.FamilyNameRegexFilter) + rx, err := regexp.Compile(pat) + if err != nil { + log.Printf("Bad family_name_regex_filter pattern %q: %v", pat, err) + return false + } + return rx.MatchString(fam) + case *btpb.RowFilter_ColumnQualifierRegexFilter: + pat := string(f.ColumnQualifierRegexFilter) + rx, err := regexp.Compile(pat) + if err != nil { + log.Printf("Bad column_qualifier_regex_filter pattern %q: %v", pat, err) + return false + } + return rx.MatchString(col) + case *btpb.RowFilter_ValueRegexFilter: + pat := string(f.ValueRegexFilter) + rx, err := regexp.Compile(pat) + if err != nil { + log.Printf("Bad value_regex_filter pattern %q: %v", pat, err) + return false + } + return rx.Match(cell.value) + case *btpb.RowFilter_ColumnRangeFilter: + if fam != f.ColumnRangeFilter.FamilyName { + return false + } + // Start qualifier defaults to empty string closed + inRangeStart := func() bool { return col >= "" } + switch sq := f.ColumnRangeFilter.StartQualifier.(type) { + case *btpb.ColumnRange_StartQualifierOpen: + inRangeStart = func() bool { return col > string(sq.StartQualifierOpen) } + case *btpb.ColumnRange_StartQualifierClosed: + inRangeStart = func() bool { return col >= string(sq.StartQualifierClosed) } + } + // End qualifier defaults to no upper boundary + inRangeEnd := func() bool { return true } + switch eq := f.ColumnRangeFilter.EndQualifier.(type) { + case *btpb.ColumnRange_EndQualifierClosed: + inRangeEnd = func() bool { return col <= string(eq.EndQualifierClosed) } + case *btpb.ColumnRange_EndQualifierOpen: + inRangeEnd = func() bool { return col < string(eq.EndQualifierOpen) } + } + return inRangeStart() && inRangeEnd() + case *btpb.RowFilter_TimestampRangeFilter: + // Lower bound is inclusive and defaults to 0, upper bound is exclusive and defaults to infinity. + return cell.ts >= f.TimestampRangeFilter.StartTimestampMicros && + (f.TimestampRangeFilter.EndTimestampMicros == 0 || cell.ts < f.TimestampRangeFilter.EndTimestampMicros) + case *btpb.RowFilter_ValueRangeFilter: + v := cell.value + // Start value defaults to empty string closed + inRangeStart := func() bool { return bytes.Compare(v, []byte{}) >= 0 } + switch sv := f.ValueRangeFilter.StartValue.(type) { + case *btpb.ValueRange_StartValueOpen: + inRangeStart = func() bool { return bytes.Compare(v, sv.StartValueOpen) > 0 } + case *btpb.ValueRange_StartValueClosed: + inRangeStart = func() bool { return bytes.Compare(v, sv.StartValueClosed) >= 0 } + } + // End value defaults to no upper boundary + inRangeEnd := func() bool { return true } + switch ev := f.ValueRangeFilter.EndValue.(type) { + case *btpb.ValueRange_EndValueClosed: + inRangeEnd = func() bool { return bytes.Compare(v, ev.EndValueClosed) <= 0 } + case *btpb.ValueRange_EndValueOpen: + inRangeEnd = func() bool { return bytes.Compare(v, ev.EndValueOpen) < 0 } + } + return inRangeStart() && inRangeEnd() + } +} + +func (s *server) MutateRow(ctx context.Context, req *btpb.MutateRowRequest) (*btpb.MutateRowResponse, error) { + s.mu.Lock() + tbl, ok := s.tables[req.TableName] + s.mu.Unlock() + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.TableName) + } + fs := tbl.columnFamilies() + r := tbl.mutableRow(string(req.RowKey)) + r.mu.Lock() + defer r.mu.Unlock() + if err := applyMutations(tbl, r, req.Mutations, fs); err != nil { + return nil, err + } + return &btpb.MutateRowResponse{}, nil +} + +func (s *server) MutateRows(req *btpb.MutateRowsRequest, stream btpb.Bigtable_MutateRowsServer) error { + s.mu.Lock() + tbl, ok := s.tables[req.TableName] + s.mu.Unlock() + if !ok { + return status.Errorf(codes.NotFound, "table %q not found", req.TableName) + } + res := &btpb.MutateRowsResponse{Entries: make([]*btpb.MutateRowsResponse_Entry, len(req.Entries))} + + fs := tbl.columnFamilies() + + for i, entry := range req.Entries { + r := tbl.mutableRow(string(entry.RowKey)) + r.mu.Lock() + code, msg := int32(codes.OK), "" + if err := applyMutations(tbl, r, entry.Mutations, fs); err != nil { + code = int32(codes.Internal) + msg = err.Error() + } + res.Entries[i] = &btpb.MutateRowsResponse_Entry{ + Index: int64(i), + Status: &statpb.Status{Code: code, Message: msg}, + } + r.mu.Unlock() + } + return stream.Send(res) +} + +func (s *server) CheckAndMutateRow(ctx context.Context, req *btpb.CheckAndMutateRowRequest) (*btpb.CheckAndMutateRowResponse, error) { + s.mu.Lock() + tbl, ok := s.tables[req.TableName] + s.mu.Unlock() + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.TableName) + } + res := &btpb.CheckAndMutateRowResponse{} + + fs := tbl.columnFamilies() + + r := tbl.mutableRow(string(req.RowKey)) + r.mu.Lock() + defer r.mu.Unlock() + + // Figure out which mutation to apply. + whichMut := false + if req.PredicateFilter == nil { + // Use true_mutations iff row contains any cells. + whichMut = !r.isEmpty() + } else { + // Use true_mutations iff any cells in the row match the filter. + // TODO(dsymonds): This could be cheaper. + nr := r.copy() + filterRow(req.PredicateFilter, nr) + whichMut = !nr.isEmpty() + } + res.PredicateMatched = whichMut + muts := req.FalseMutations + if whichMut { + muts = req.TrueMutations + } + + if err := applyMutations(tbl, r, muts, fs); err != nil { + return nil, err + } + return res, nil +} + +// applyMutations applies a sequence of mutations to a row. +// fam should be a snapshot of the keys of tbl.families. +// It assumes r.mu is locked. +func applyMutations(tbl *table, r *row, muts []*btpb.Mutation, fs map[string]*columnFamily) error { + for _, mut := range muts { + switch mut := mut.Mutation.(type) { + default: + return fmt.Errorf("can't handle mutation type %T", mut) + case *btpb.Mutation_SetCell_: + set := mut.SetCell + if _, ok := fs[set.FamilyName]; !ok { + return fmt.Errorf("unknown family %q", set.FamilyName) + } + ts := set.TimestampMicros + if ts == -1 { // bigtable.ServerTime + ts = newTimestamp() + } + if !tbl.validTimestamp(ts) { + return fmt.Errorf("invalid timestamp %d", ts) + } + fam := set.FamilyName + col := string(set.ColumnQualifier) + + newCell := cell{ts: ts, value: set.Value} + f := r.getOrCreateFamily(fam, fs[fam].order) + f.cells[col] = appendOrReplaceCell(f.cellsByColumn(col), newCell) + case *btpb.Mutation_DeleteFromColumn_: + del := mut.DeleteFromColumn + if _, ok := fs[del.FamilyName]; !ok { + return fmt.Errorf("unknown family %q", del.FamilyName) + } + fam := del.FamilyName + col := string(del.ColumnQualifier) + if _, ok := r.families[fam]; ok { + cs := r.families[fam].cells[col] + if del.TimeRange != nil { + tsr := del.TimeRange + if !tbl.validTimestamp(tsr.StartTimestampMicros) { + return fmt.Errorf("invalid timestamp %d", tsr.StartTimestampMicros) + } + if !tbl.validTimestamp(tsr.EndTimestampMicros) { + return fmt.Errorf("invalid timestamp %d", tsr.EndTimestampMicros) + } + // Find half-open interval to remove. + // Cells are in descending timestamp order, + // so the predicates to sort.Search are inverted. + si, ei := 0, len(cs) + if tsr.StartTimestampMicros > 0 { + ei = sort.Search(len(cs), func(i int) bool { return cs[i].ts < tsr.StartTimestampMicros }) + } + if tsr.EndTimestampMicros > 0 { + si = sort.Search(len(cs), func(i int) bool { return cs[i].ts < tsr.EndTimestampMicros }) + } + if si < ei { + copy(cs[si:], cs[ei:]) + cs = cs[:len(cs)-(ei-si)] + } + } else { + cs = nil + } + if len(cs) == 0 { + delete(r.families[fam].cells, col) + colNames := r.families[fam].colNames + i := sort.Search(len(colNames), func(i int) bool { return colNames[i] >= col }) + if i < len(colNames) && colNames[i] == col { + r.families[fam].colNames = append(colNames[:i], colNames[i+1:]...) + } + if len(r.families[fam].cells) == 0 { + delete(r.families, fam) + } + } else { + r.families[fam].cells[col] = cs + } + } + case *btpb.Mutation_DeleteFromRow_: + r.families = make(map[string]*family) + case *btpb.Mutation_DeleteFromFamily_: + fampre := mut.DeleteFromFamily.FamilyName + delete(r.families, fampre) + } + } + return nil +} + +func maxTimestamp(x, y int64) int64 { + if x > y { + return x + } + return y +} + +func newTimestamp() int64 { + ts := time.Now().UnixNano() / 1e3 + ts -= ts % 1000 // round to millisecond granularity + return ts +} + +func appendOrReplaceCell(cs []cell, newCell cell) []cell { + replaced := false + for i, cell := range cs { + if cell.ts == newCell.ts { + cs[i] = newCell + replaced = true + break + } + } + if !replaced { + cs = append(cs, newCell) + } + sort.Sort(byDescTS(cs)) + return cs +} + +func (s *server) ReadModifyWriteRow(ctx context.Context, req *btpb.ReadModifyWriteRowRequest) (*btpb.ReadModifyWriteRowResponse, error) { + s.mu.Lock() + tbl, ok := s.tables[req.TableName] + s.mu.Unlock() + if !ok { + return nil, status.Errorf(codes.NotFound, "table %q not found", req.TableName) + } + + fs := tbl.columnFamilies() + + rowKey := string(req.RowKey) + r := tbl.mutableRow(rowKey) + resultRow := newRow(rowKey) // copy of updated cells + + // This must be done before the row lock, acquired below, is released. + r.mu.Lock() + defer r.mu.Unlock() + // Assume all mutations apply to the most recent version of the cell. + // TODO(dsymonds): Verify this assumption and document it in the proto. + for _, rule := range req.Rules { + if _, ok := fs[rule.FamilyName]; !ok { + return nil, fmt.Errorf("unknown family %q", rule.FamilyName) + } + + fam := rule.FamilyName + col := string(rule.ColumnQualifier) + isEmpty := false + f := r.getOrCreateFamily(fam, fs[fam].order) + cs := f.cells[col] + isEmpty = len(cs) == 0 + + ts := newTimestamp() + var newCell, prevCell cell + if !isEmpty { + cells := r.families[fam].cells[col] + prevCell = cells[0] + + // ts is the max of now or the prev cell's timestamp in case the + // prev cell is in the future + ts = maxTimestamp(ts, prevCell.ts) + } + + switch rule := rule.Rule.(type) { + default: + return nil, fmt.Errorf("unknown RMW rule oneof %T", rule) + case *btpb.ReadModifyWriteRule_AppendValue: + newCell = cell{ts: ts, value: append(prevCell.value, rule.AppendValue...)} + case *btpb.ReadModifyWriteRule_IncrementAmount: + var v int64 + if !isEmpty { + prevVal := prevCell.value + if len(prevVal) != 8 { + return nil, fmt.Errorf("increment on non-64-bit value") + } + v = int64(binary.BigEndian.Uint64(prevVal)) + } + v += rule.IncrementAmount + var val [8]byte + binary.BigEndian.PutUint64(val[:], uint64(v)) + newCell = cell{ts: ts, value: val[:]} + } + + // Store the new cell + f.cells[col] = appendOrReplaceCell(f.cellsByColumn(col), newCell) + + // Store a copy for the result row + resultFamily := resultRow.getOrCreateFamily(fam, fs[fam].order) + resultFamily.cellsByColumn(col) // create the column + resultFamily.cells[col] = []cell{newCell} // overwrite the cells + } + + // Build the response using the result row + res := &btpb.Row{ + Key: req.RowKey, + Families: make([]*btpb.Family, len(resultRow.families)), + } + + for i, family := range resultRow.sortedFamilies() { + res.Families[i] = &btpb.Family{ + Name: family.name, + Columns: make([]*btpb.Column, len(family.colNames)), + } + + for j, colName := range family.colNames { + res.Families[i].Columns[j] = &btpb.Column{ + Qualifier: []byte(colName), + Cells: []*btpb.Cell{{ + TimestampMicros: family.cells[colName][0].ts, + Value: family.cells[colName][0].value, + }}, + } + } + } + return &btpb.ReadModifyWriteRowResponse{Row: res}, nil +} + +func (s *server) SampleRowKeys(req *btpb.SampleRowKeysRequest, stream btpb.Bigtable_SampleRowKeysServer) error { + s.mu.Lock() + tbl, ok := s.tables[req.TableName] + s.mu.Unlock() + if !ok { + return status.Errorf(codes.NotFound, "table %q not found", req.TableName) + } + + tbl.mu.RLock() + defer tbl.mu.RUnlock() + + // The return value of SampleRowKeys is very loosely defined. Return at least the + // final row key in the table and choose other row keys randomly. + var offset int64 + var err error + i := 0 + tbl.rows.Ascend(func(it btree.Item) bool { + row := it.(*row) + if i == tbl.rows.Len()-1 || rand.Int31n(100) == 0 { + resp := &btpb.SampleRowKeysResponse{ + RowKey: []byte(row.key), + OffsetBytes: offset, + } + err = stream.Send(resp) + if err != nil { + return false + } + } + offset += int64(row.size()) + i++ + return true + }) + return err +} + +// needGC is invoked whenever the server needs gcloop running. +func (s *server) needGC() { + s.mu.Lock() + if s.gcc == nil { + s.gcc = make(chan int) + go s.gcloop(s.gcc) + } + s.mu.Unlock() +} + +func (s *server) gcloop(done <-chan int) { + const ( + minWait = 500 // ms + maxWait = 1500 // ms + ) + + for { + // Wait for a random time interval. + d := time.Duration(minWait+rand.Intn(maxWait-minWait)) * time.Millisecond + select { + case <-time.After(d): + case <-done: + return // server has been closed + } + + // Do a GC pass over all tables. + var tables []*table + s.mu.Lock() + for _, tbl := range s.tables { + tables = append(tables, tbl) + } + s.mu.Unlock() + for _, tbl := range tables { + tbl.gc() + } + } +} + +type table struct { + mu sync.RWMutex + counter uint64 // increment by 1 when a new family is created + families map[string]*columnFamily // keyed by plain family name + rows *btree.BTree // indexed by row key +} + +const btreeDegree = 16 + +func newTable(ctr *btapb.CreateTableRequest) *table { + fams := make(map[string]*columnFamily) + c := uint64(0) + if ctr.Table != nil { + for id, cf := range ctr.Table.ColumnFamilies { + fams[id] = &columnFamily{ + name: ctr.Parent + "/columnFamilies/" + id, + order: c, + gcRule: cf.GcRule, + } + c++ + } + } + return &table{ + families: fams, + counter: c, + rows: btree.New(btreeDegree), + } +} + +func (t *table) validTimestamp(ts int64) bool { + if ts <= minValidMilliSeconds || ts >= maxValidMilliSeconds { + return false + } + + // Assume millisecond granularity is required. + return ts%1000 == 0 +} + +func (t *table) columnFamilies() map[string]*columnFamily { + cp := make(map[string]*columnFamily) + t.mu.RLock() + for fam, cf := range t.families { + cp[fam] = cf + } + t.mu.RUnlock() + return cp +} + +func (t *table) mutableRow(key string) *row { + bkey := btreeKey(key) + // Try fast path first. + t.mu.RLock() + i := t.rows.Get(bkey) + t.mu.RUnlock() + if i != nil { + return i.(*row) + } + + // We probably need to create the row. + t.mu.Lock() + defer t.mu.Unlock() + i = t.rows.Get(bkey) + if i != nil { + return i.(*row) + } + r := newRow(key) + t.rows.ReplaceOrInsert(r) + return r +} + +func (t *table) gc() { + // This method doesn't add or remove rows, so we only need a read lock for the table. + t.mu.RLock() + defer t.mu.RUnlock() + + // Gather GC rules we'll apply. + rules := make(map[string]*btapb.GcRule) // keyed by "fam" + for fam, cf := range t.families { + if cf.gcRule != nil { + rules[fam] = cf.gcRule + } + } + if len(rules) == 0 { + return + } + + t.rows.Ascend(func(i btree.Item) bool { + r := i.(*row) + r.mu.Lock() + r.gc(rules) + r.mu.Unlock() + return true + }) +} + +type byRowKey []*row + +func (b byRowKey) Len() int { return len(b) } +func (b byRowKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byRowKey) Less(i, j int) bool { return b[i].key < b[j].key } + +type row struct { + key string + + mu sync.Mutex + families map[string]*family // keyed by family name +} + +func newRow(key string) *row { + return &row{ + key: key, + families: make(map[string]*family), + } +} + +// copy returns a copy of the row. +// Cell values are aliased. +// r.mu should be held. +func (r *row) copy() *row { + nr := newRow(r.key) + for _, fam := range r.families { + nr.families[fam.name] = &family{ + name: fam.name, + order: fam.order, + colNames: fam.colNames, + cells: make(map[string][]cell), + } + for col, cs := range fam.cells { + // Copy the []cell slice, but not the []byte inside each cell. + nr.families[fam.name].cells[col] = append([]cell(nil), cs...) + } + } + return nr +} + +// isEmpty returns true if a row doesn't contain any cell +func (r *row) isEmpty() bool { + for _, fam := range r.families { + for _, cs := range fam.cells { + if len(cs) > 0 { + return false + } + } + } + return true +} + +// sortedFamilies returns a column family set +// sorted in ascending creation order in a row. +func (r *row) sortedFamilies() []*family { + var families []*family + for _, fam := range r.families { + families = append(families, fam) + } + sort.Sort(byCreationOrder(families)) + return families +} + +func (r *row) getOrCreateFamily(name string, order uint64) *family { + if _, ok := r.families[name]; !ok { + r.families[name] = &family{ + name: name, + order: order, + cells: make(map[string][]cell), + } + } + return r.families[name] +} + +// gc applies the given GC rules to the row. +// r.mu should be held. +func (r *row) gc(rules map[string]*btapb.GcRule) { + for _, fam := range r.families { + rule, ok := rules[fam.name] + if !ok { + continue + } + for col, cs := range fam.cells { + r.families[fam.name].cells[col] = applyGC(cs, rule) + } + } +} + +// size returns the total size of all cell values in the row. +func (r *row) size() int { + size := 0 + for _, fam := range r.families { + for _, cells := range fam.cells { + for _, cell := range cells { + size += len(cell.value) + } + } + } + return size +} + +// Less implements btree.Less. +func (r *row) Less(i btree.Item) bool { + return r.key < i.(*row).key +} + +// btreeKey returns a row for use as a key into the BTree. +func btreeKey(s string) *row { return &row{key: s} } + +func (r *row) String() string { + return r.key +} + +var gcTypeWarn sync.Once + +// applyGC applies the given GC rule to the cells. +func applyGC(cells []cell, rule *btapb.GcRule) []cell { + switch rule := rule.Rule.(type) { + default: + // TODO(dsymonds): Support GcRule_Intersection_ + gcTypeWarn.Do(func() { + log.Printf("Unsupported GC rule type %T", rule) + }) + case *btapb.GcRule_Union_: + for _, sub := range rule.Union.Rules { + cells = applyGC(cells, sub) + } + return cells + case *btapb.GcRule_MaxAge: + // Timestamps are in microseconds. + cutoff := time.Now().UnixNano() / 1e3 + cutoff -= rule.MaxAge.Seconds * 1e6 + cutoff -= int64(rule.MaxAge.Nanos) / 1e3 + // The slice of cells in in descending timestamp order. + // This sort.Search will return the index of the first cell whose timestamp is chronologically before the cutoff. + si := sort.Search(len(cells), func(i int) bool { return cells[i].ts < cutoff }) + if si < len(cells) { + log.Printf("bttest: GC MaxAge(%v) deleted %d cells.", rule.MaxAge, len(cells)-si) + } + return cells[:si] + case *btapb.GcRule_MaxNumVersions: + n := int(rule.MaxNumVersions) + if len(cells) > n { + cells = cells[:n] + } + return cells + } + return cells +} + +type family struct { + name string // Column family name + order uint64 // Creation order of column family + colNames []string // Column names are sorted in lexicographical ascending order + cells map[string][]cell // Keyed by column name; cells are in descending timestamp order +} + +type byCreationOrder []*family + +func (b byCreationOrder) Len() int { return len(b) } +func (b byCreationOrder) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byCreationOrder) Less(i, j int) bool { return b[i].order < b[j].order } + +// cellsByColumn adds the column name to colNames set if it does not exist +// and returns all cells within a column +func (f *family) cellsByColumn(name string) []cell { + if _, ok := f.cells[name]; !ok { + f.colNames = append(f.colNames, name) + sort.Strings(f.colNames) + } + return f.cells[name] +} + +type cell struct { + ts int64 + value []byte +} + +type byDescTS []cell + +func (b byDescTS) Len() int { return len(b) } +func (b byDescTS) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byDescTS) Less(i, j int) bool { return b[i].ts > b[j].ts } + +type columnFamily struct { + name string + order uint64 // Creation order of column family + gcRule *btapb.GcRule +} + +func (c *columnFamily) proto() *btapb.ColumnFamily { + return &btapb.ColumnFamily{ + GcRule: c.gcRule, + } +} + +func toColumnFamilies(families map[string]*columnFamily) map[string]*btapb.ColumnFamily { + fs := make(map[string]*btapb.ColumnFamily) + for k, v := range families { + fs[k] = v.proto() + } + return fs +} diff --git a/vendor/cloud.google.com/go/bigtable/bttest/inmem_test.go b/vendor/cloud.google.com/go/bigtable/bttest/inmem_test.go new file mode 100644 index 0000000000..5bc9907433 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/bttest/inmem_test.go @@ -0,0 +1,805 @@ +// 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 bttest + +import ( + "fmt" + "math/rand" + "strconv" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "golang.org/x/net/context" + btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2" + btpb "google.golang.org/genproto/googleapis/bigtable/v2" + "google.golang.org/grpc" +) + +func TestConcurrentMutationsReadModifyAndGC(t *testing.T) { + s := &server{ + tables: make(map[string]*table), + } + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + if _, err := s.CreateTable( + ctx, + &btapb.CreateTableRequest{Parent: "cluster", TableId: "t"}); err != nil { + t.Fatal(err) + } + const name = `cluster/tables/t` + tbl := s.tables[name] + req := &btapb.ModifyColumnFamiliesRequest{ + Name: name, + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: "cf", + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{Create: &btapb.ColumnFamily{}}, + }}, + } + _, err := s.ModifyColumnFamilies(ctx, req) + if err != nil { + t.Fatal(err) + } + req = &btapb.ModifyColumnFamiliesRequest{ + Name: name, + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: "cf", + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Update{Update: &btapb.ColumnFamily{ + GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}, + }}, + }}, + } + if _, err := s.ModifyColumnFamilies(ctx, req); err != nil { + t.Fatal(err) + } + + var wg sync.WaitGroup + var ts int64 + ms := func() []*btpb.Mutation { + return []*btpb.Mutation{{ + Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{ + FamilyName: "cf", + ColumnQualifier: []byte(`col`), + TimestampMicros: atomic.AddInt64(&ts, 1000), + }}, + }} + } + + rmw := func() *btpb.ReadModifyWriteRowRequest { + return &btpb.ReadModifyWriteRowRequest{ + TableName: name, + RowKey: []byte(fmt.Sprint(rand.Intn(100))), + Rules: []*btpb.ReadModifyWriteRule{{ + FamilyName: "cf", + ColumnQualifier: []byte("col"), + Rule: &btpb.ReadModifyWriteRule_IncrementAmount{IncrementAmount: 1}, + }}, + } + } + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for ctx.Err() == nil { + req := &btpb.MutateRowRequest{ + TableName: name, + RowKey: []byte(fmt.Sprint(rand.Intn(100))), + Mutations: ms(), + } + if _, err := s.MutateRow(ctx, req); err != nil { + panic(err) // can't use t.Fatal in goroutine + } + } + }() + wg.Add(1) + go func() { + defer wg.Done() + for ctx.Err() == nil { + _, _ = s.ReadModifyWriteRow(ctx, rmw()) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + tbl.gc() + }() + } + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + select { + case <-done: + case <-time.After(1 * time.Second): + t.Error("Concurrent mutations and GCs haven't completed after 1s") + } +} + +func TestCreateTableWithFamily(t *testing.T) { + // The Go client currently doesn't support creating a table with column families + // in one operation but it is allowed by the API. This must still be supported by the + // fake server so this test lives here instead of in the main bigtable + // integration test. + s := &server{ + tables: make(map[string]*table), + } + ctx := context.Background() + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf1": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 123}}}, + "cf2": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 456}}}, + }, + } + cTbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + t.Fatalf("Creating table: %v", err) + } + tbl, err := s.GetTable(ctx, &btapb.GetTableRequest{Name: cTbl.Name}) + if err != nil { + t.Fatalf("Getting table: %v", err) + } + cf := tbl.ColumnFamilies["cf1"] + if cf == nil { + t.Fatalf("Missing col family cf1") + } + if got, want := cf.GcRule.GetMaxNumVersions(), int32(123); got != want { + t.Errorf("Invalid MaxNumVersions: wanted:%d, got:%d", want, got) + } + cf = tbl.ColumnFamilies["cf2"] + if cf == nil { + t.Fatalf("Missing col family cf2") + } + if got, want := cf.GcRule.GetMaxNumVersions(), int32(456); got != want { + t.Errorf("Invalid MaxNumVersions: wanted:%d, got:%d", want, got) + } +} + +type MockSampleRowKeysServer struct { + responses []*btpb.SampleRowKeysResponse + grpc.ServerStream +} + +func (s *MockSampleRowKeysServer) Send(resp *btpb.SampleRowKeysResponse) error { + s.responses = append(s.responses, resp) + return nil +} + +func TestSampleRowKeys(t *testing.T) { + s := &server{ + tables: make(map[string]*table), + } + ctx := context.Background() + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}}, + }, + } + tbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + t.Fatalf("Creating table: %v", err) + } + + // Populate the table + val := []byte("value") + rowCount := 1000 + for i := 0; i < rowCount; i++ { + req := &btpb.MutateRowRequest{ + TableName: tbl.Name, + RowKey: []byte("row-" + strconv.Itoa(i)), + Mutations: []*btpb.Mutation{{ + Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{ + FamilyName: "cf", + ColumnQualifier: []byte("col"), + TimestampMicros: 1000, + Value: val, + }}, + }}, + } + if _, err := s.MutateRow(ctx, req); err != nil { + t.Fatalf("Populating table: %v", err) + } + } + + mock := &MockSampleRowKeysServer{} + if err := s.SampleRowKeys(&btpb.SampleRowKeysRequest{TableName: tbl.Name}, mock); err != nil { + t.Errorf("SampleRowKeys error: %v", err) + } + if len(mock.responses) == 0 { + t.Fatal("Response count: got 0, want > 0") + } + // Make sure the offset of the final response is the offset of the final row + got := mock.responses[len(mock.responses)-1].OffsetBytes + want := int64((rowCount - 1) * len(val)) + if got != want { + t.Errorf("Invalid offset: got %d, want %d", got, want) + } +} + +func TestDropRowRange(t *testing.T) { + s := &server{ + tables: make(map[string]*table), + } + ctx := context.Background() + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}}, + }, + } + tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + t.Fatalf("Creating table: %v", err) + } + + tbl := s.tables[tblInfo.Name] + + // Populate the table + prefixes := []string{"AAA", "BBB", "CCC", "DDD"} + count := 3 + doWrite := func() { + for _, prefix := range prefixes { + for i := 0; i < count; i++ { + req := &btpb.MutateRowRequest{ + TableName: tblInfo.Name, + RowKey: []byte(prefix + strconv.Itoa(i)), + Mutations: []*btpb.Mutation{{ + Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{ + FamilyName: "cf", + ColumnQualifier: []byte("col"), + TimestampMicros: 1000, + Value: []byte{}, + }}, + }}, + } + if _, err := s.MutateRow(ctx, req); err != nil { + t.Fatalf("Populating table: %v", err) + } + } + } + } + + doWrite() + tblSize := tbl.rows.Len() + req := &btapb.DropRowRangeRequest{ + Name: tblInfo.Name, + Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("AAA")}, + } + if _, err = s.DropRowRange(ctx, req); err != nil { + t.Fatalf("Dropping first range: %v", err) + } + got, want := tbl.rows.Len(), tblSize-count + if got != want { + t.Errorf("Row count after first drop: got %d (%v), want %d", got, tbl.rows, want) + } + + req = &btapb.DropRowRangeRequest{ + Name: tblInfo.Name, + Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("DDD")}, + } + if _, err = s.DropRowRange(ctx, req); err != nil { + t.Fatalf("Dropping second range: %v", err) + } + got, want = tbl.rows.Len(), tblSize-(2*count) + if got != want { + t.Errorf("Row count after second drop: got %d (%v), want %d", got, tbl.rows, want) + } + + req = &btapb.DropRowRangeRequest{ + Name: tblInfo.Name, + Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("XXX")}, + } + if _, err = s.DropRowRange(ctx, req); err != nil { + t.Fatalf("Dropping invalid range: %v", err) + } + got, want = tbl.rows.Len(), tblSize-(2*count) + if got != want { + t.Errorf("Row count after invalid drop: got %d (%v), want %d", got, tbl.rows, want) + } + + req = &btapb.DropRowRangeRequest{ + Name: tblInfo.Name, + Target: &btapb.DropRowRangeRequest_DeleteAllDataFromTable{DeleteAllDataFromTable: true}, + } + if _, err = s.DropRowRange(ctx, req); err != nil { + t.Fatalf("Dropping all data: %v", err) + } + got, want = tbl.rows.Len(), 0 + if got != want { + t.Errorf("Row count after drop all: got %d, want %d", got, want) + } + + // Test that we can write rows, delete some and then write them again. + count = 1 + doWrite() + + req = &btapb.DropRowRangeRequest{ + Name: tblInfo.Name, + Target: &btapb.DropRowRangeRequest_DeleteAllDataFromTable{DeleteAllDataFromTable: true}, + } + if _, err = s.DropRowRange(ctx, req); err != nil { + t.Fatalf("Dropping all data: %v", err) + } + got, want = tbl.rows.Len(), 0 + if got != want { + t.Errorf("Row count after drop all: got %d, want %d", got, want) + } + + doWrite() + got, want = tbl.rows.Len(), len(prefixes) + if got != want { + t.Errorf("Row count after rewrite: got %d, want %d", got, want) + } + + req = &btapb.DropRowRangeRequest{ + Name: tblInfo.Name, + Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("BBB")}, + } + if _, err = s.DropRowRange(ctx, req); err != nil { + t.Fatalf("Dropping range: %v", err) + } + doWrite() + got, want = tbl.rows.Len(), len(prefixes) + if got != want { + t.Errorf("Row count after drop range: got %d, want %d", got, want) + } +} + +type MockReadRowsServer struct { + responses []*btpb.ReadRowsResponse + grpc.ServerStream +} + +func (s *MockReadRowsServer) Send(resp *btpb.ReadRowsResponse) error { + s.responses = append(s.responses, resp) + return nil +} + +func TestReadRows(t *testing.T) { + ctx := context.Background() + s := &server{ + tables: make(map[string]*table), + } + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}}, + }, + } + tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + t.Fatalf("Creating table: %v", err) + } + mreq := &btpb.MutateRowRequest{ + TableName: tblInfo.Name, + RowKey: []byte("row"), + Mutations: []*btpb.Mutation{{ + Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{ + FamilyName: "cf0", + ColumnQualifier: []byte("col"), + TimestampMicros: 1000, + Value: []byte{}, + }}, + }}, + } + if _, err := s.MutateRow(ctx, mreq); err != nil { + t.Fatalf("Populating table: %v", err) + } + + for _, rowset := range []*btpb.RowSet{ + {RowKeys: [][]byte{[]byte("row")}}, + {RowRanges: []*btpb.RowRange{{StartKey: &btpb.RowRange_StartKeyClosed{StartKeyClosed: []byte("")}}}}, + {RowRanges: []*btpb.RowRange{{StartKey: &btpb.RowRange_StartKeyClosed{StartKeyClosed: []byte("r")}}}}, + {RowRanges: []*btpb.RowRange{{ + StartKey: &btpb.RowRange_StartKeyClosed{StartKeyClosed: []byte("")}, + EndKey: &btpb.RowRange_EndKeyOpen{EndKeyOpen: []byte("s")}, + }}}, + } { + mock := &MockReadRowsServer{} + req := &btpb.ReadRowsRequest{TableName: tblInfo.Name, Rows: rowset} + if err = s.ReadRows(req, mock); err != nil { + t.Fatalf("ReadRows error: %v", err) + } + if got, want := len(mock.responses), 1; got != want { + t.Errorf("%+v: response count: got %d, want %d", rowset, got, want) + } + } +} + +func TestReadRowsOrder(t *testing.T) { + s := &server{ + tables: make(map[string]*table), + } + ctx := context.Background() + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}}, + }, + } + tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + t.Fatalf("Creating table: %v", err) + } + count := 3 + mcf := func(i int) *btapb.ModifyColumnFamiliesRequest { + return &btapb.ModifyColumnFamiliesRequest{ + Name: tblInfo.Name, + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: "cf" + strconv.Itoa(i), + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{Create: &btapb.ColumnFamily{}}, + }}, + } + } + for i := 1; i <= count; i++ { + _, err = s.ModifyColumnFamilies(ctx, mcf(i)) + if err != nil { + t.Fatal(err) + } + } + // Populate the table + for fc := 0; fc < count; fc++ { + for cc := count; cc > 0; cc-- { + for tc := 0; tc < count; tc++ { + req := &btpb.MutateRowRequest{ + TableName: tblInfo.Name, + RowKey: []byte("row"), + Mutations: []*btpb.Mutation{{ + Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{ + FamilyName: "cf" + strconv.Itoa(fc), + ColumnQualifier: []byte("col" + strconv.Itoa(cc)), + TimestampMicros: int64((tc + 1) * 1000), + Value: []byte{}, + }}, + }}, + } + if _, err := s.MutateRow(ctx, req); err != nil { + t.Fatalf("Populating table: %v", err) + } + } + } + } + req := &btpb.ReadRowsRequest{ + TableName: tblInfo.Name, + Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}}, + } + mock := &MockReadRowsServer{} + if err = s.ReadRows(req, mock); err != nil { + t.Errorf("ReadRows error: %v", err) + } + if len(mock.responses) == 0 { + t.Fatal("Response count: got 0, want > 0") + } + if len(mock.responses[0].Chunks) != 27 { + t.Fatalf("Chunk count: got %d, want 27", len(mock.responses[0].Chunks)) + } + testOrder := func(ms *MockReadRowsServer) { + var prevFam, prevCol string + var prevTime int64 + for _, cc := range ms.responses[0].Chunks { + if prevFam == "" { + prevFam = cc.FamilyName.Value + prevCol = string(cc.Qualifier.Value) + prevTime = cc.TimestampMicros + continue + } + if cc.FamilyName.Value < prevFam { + t.Errorf("Family order is not correct: got %s < %s", cc.FamilyName.Value, prevFam) + } else if cc.FamilyName.Value == prevFam { + if string(cc.Qualifier.Value) < prevCol { + t.Errorf("Column order is not correct: got %s < %s", string(cc.Qualifier.Value), prevCol) + } else if string(cc.Qualifier.Value) == prevCol { + if cc.TimestampMicros > prevTime { + t.Errorf("cell order is not correct: got %d > %d", cc.TimestampMicros, prevTime) + } + } + } + prevFam = cc.FamilyName.Value + prevCol = string(cc.Qualifier.Value) + prevTime = cc.TimestampMicros + } + } + testOrder(mock) + + // Read with interleave filter + inter := &btpb.RowFilter_Interleave{} + fnr := &btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{FamilyNameRegexFilter: "1"}} + cqr := &btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{ColumnQualifierRegexFilter: []byte("2")}} + inter.Filters = append(inter.Filters, fnr, cqr) + req = &btpb.ReadRowsRequest{ + TableName: tblInfo.Name, + Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}}, + Filter: &btpb.RowFilter{ + Filter: &btpb.RowFilter_Interleave_{Interleave: inter}, + }, + } + + mock = &MockReadRowsServer{} + if err = s.ReadRows(req, mock); err != nil { + t.Errorf("ReadRows error: %v", err) + } + if len(mock.responses) == 0 { + t.Fatal("Response count: got 0, want > 0") + } + if len(mock.responses[0].Chunks) != 18 { + t.Fatalf("Chunk count: got %d, want 18", len(mock.responses[0].Chunks)) + } + testOrder(mock) + + // Check order after ReadModifyWriteRow + rmw := func(i int) *btpb.ReadModifyWriteRowRequest { + return &btpb.ReadModifyWriteRowRequest{ + TableName: tblInfo.Name, + RowKey: []byte("row"), + Rules: []*btpb.ReadModifyWriteRule{{ + FamilyName: "cf3", + ColumnQualifier: []byte("col" + strconv.Itoa(i)), + Rule: &btpb.ReadModifyWriteRule_IncrementAmount{IncrementAmount: 1}, + }}, + } + } + for i := count; i > 0; i-- { + if _, err := s.ReadModifyWriteRow(ctx, rmw(i)); err != nil { + t.Fatal(err) + } + } + req = &btpb.ReadRowsRequest{ + TableName: tblInfo.Name, + Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}}, + } + mock = &MockReadRowsServer{} + if err = s.ReadRows(req, mock); err != nil { + t.Errorf("ReadRows error: %v", err) + } + if len(mock.responses) == 0 { + t.Fatal("Response count: got 0, want > 0") + } + if len(mock.responses[0].Chunks) != 30 { + t.Fatalf("Chunk count: got %d, want 30", len(mock.responses[0].Chunks)) + } + testOrder(mock) +} + +func TestCheckAndMutateRowWithoutPredicate(t *testing.T) { + s := &server{ + tables: make(map[string]*table), + } + ctx := context.Background() + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}}, + }, + } + tbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + t.Fatalf("Creating table: %v", err) + } + + // Populate the table + val := []byte("value") + mrreq := &btpb.MutateRowRequest{ + TableName: tbl.Name, + RowKey: []byte("row-present"), + Mutations: []*btpb.Mutation{{ + Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{ + FamilyName: "cf", + ColumnQualifier: []byte("col"), + TimestampMicros: 1000, + Value: val, + }}, + }}, + } + if _, err := s.MutateRow(ctx, mrreq); err != nil { + t.Fatalf("Populating table: %v", err) + } + + req := &btpb.CheckAndMutateRowRequest{ + TableName: tbl.Name, + RowKey: []byte("row-not-present"), + } + if res, err := s.CheckAndMutateRow(ctx, req); err != nil { + t.Errorf("CheckAndMutateRow error: %v", err) + } else if got, want := res.PredicateMatched, false; got != want { + t.Errorf("Invalid PredicateMatched value: got %t, want %t", got, want) + } + + req = &btpb.CheckAndMutateRowRequest{ + TableName: tbl.Name, + RowKey: []byte("row-present"), + } + if res, err := s.CheckAndMutateRow(ctx, req); err != nil { + t.Errorf("CheckAndMutateRow error: %v", err) + } else if got, want := res.PredicateMatched, true; got != want { + t.Errorf("Invalid PredicateMatched value: got %t, want %t", got, want) + } +} + +func TestServer_ReadModifyWriteRow(t *testing.T) { + s := &server{ + tables: make(map[string]*table), + } + + ctx := context.Background() + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}}, + }, + } + tbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + t.Fatalf("Creating table: %v", err) + } + + req := &btpb.ReadModifyWriteRowRequest{ + TableName: tbl.Name, + RowKey: []byte("row-key"), + Rules: []*btpb.ReadModifyWriteRule{ + { + FamilyName: "cf", + ColumnQualifier: []byte("q1"), + Rule: &btpb.ReadModifyWriteRule_AppendValue{ + AppendValue: []byte("a"), + }, + }, + // multiple ops for same cell + { + FamilyName: "cf", + ColumnQualifier: []byte("q1"), + Rule: &btpb.ReadModifyWriteRule_AppendValue{ + AppendValue: []byte("b"), + }, + }, + // different cell whose qualifier should sort before the prior rules + { + FamilyName: "cf", + ColumnQualifier: []byte("q0"), + Rule: &btpb.ReadModifyWriteRule_IncrementAmount{ + IncrementAmount: 1, + }, + }, + }, + } + + got, err := s.ReadModifyWriteRow(ctx, req) + + if err != nil { + t.Fatalf("ReadModifyWriteRow error: %v", err) + } + + want := &btpb.ReadModifyWriteRowResponse{ + Row: &btpb.Row{ + Key: []byte("row-key"), + Families: []*btpb.Family{{ + Name: "cf", + Columns: []*btpb.Column{ + { + Qualifier: []byte("q0"), + Cells: []*btpb.Cell{{ + Value: []byte{0, 0, 0, 0, 0, 0, 0, 1}, + }}, + }, + { + Qualifier: []byte("q1"), + Cells: []*btpb.Cell{{ + Value: []byte("ab"), + }}, + }, + }, + }}, + }, + } + + diff := cmp.Diff(got, want, cmpopts.IgnoreFields(btpb.Cell{}, "TimestampMicros")) + if diff != "" { + t.Errorf("unexpected response: %s", diff) + } +} + +// helper function to populate table data +func populateTable(ctx context.Context, s *server) (*btapb.Table, error) { + newTbl := btapb.Table{ + ColumnFamilies: map[string]*btapb.ColumnFamily{ + "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{1}}}, + }, + } + tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl}) + if err != nil { + return nil, err + } + count := 3 + mcf := func(i int) *btapb.ModifyColumnFamiliesRequest { + return &btapb.ModifyColumnFamiliesRequest{ + Name: tblInfo.Name, + Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ + Id: "cf" + strconv.Itoa(i), + Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{&btapb.ColumnFamily{}}, + }}, + } + } + for i := 1; i <= count; i++ { + _, err = s.ModifyColumnFamilies(ctx, mcf(i)) + if err != nil { + return nil, err + } + } + // Populate the table + for fc := 0; fc < count; fc++ { + for cc := count; cc > 0; cc-- { + for tc := 0; tc < count; tc++ { + req := &btpb.MutateRowRequest{ + TableName: tblInfo.Name, + RowKey: []byte("row"), + Mutations: []*btpb.Mutation{{ + Mutation: &btpb.Mutation_SetCell_{&btpb.Mutation_SetCell{ + FamilyName: "cf" + strconv.Itoa(fc), + ColumnQualifier: []byte("col" + strconv.Itoa(cc)), + TimestampMicros: int64((tc + 1) * 1000), + Value: []byte{}, + }}, + }}, + } + if _, err := s.MutateRow(ctx, req); err != nil { + return nil, err + } + } + } + } + + return tblInfo, nil +} + +func TestFilters(t *testing.T) { + tests := []struct { + in *btpb.RowFilter + out int + }{ + {in: &btpb.RowFilter{Filter: &btpb.RowFilter_BlockAllFilter{true}}, out: 0}, + {in: &btpb.RowFilter{Filter: &btpb.RowFilter_BlockAllFilter{false}}, out: 1}, + {in: &btpb.RowFilter{Filter: &btpb.RowFilter_PassAllFilter{true}}, out: 1}, + {in: &btpb.RowFilter{Filter: &btpb.RowFilter_PassAllFilter{false}}, out: 0}, + } + + ctx := context.Background() + + s := &server{ + tables: make(map[string]*table), + } + + tblInfo, err := populateTable(ctx, s) + if err != nil { + t.Fatal(err) + } + + req := &btpb.ReadRowsRequest{ + TableName: tblInfo.Name, + Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}}, + } + + for _, tc := range tests { + req.Filter = tc.in + + mock := &MockReadRowsServer{} + if err = s.ReadRows(req, mock); err != nil { + t.Errorf("ReadRows error: %v", err) + continue + } + + if len(mock.responses) != tc.out { + t.Errorf("Response count: got %d, want %d", len(mock.responses), tc.out) + continue + } + } +} diff --git a/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbt.go b/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbt.go new file mode 100644 index 0000000000..034b666db3 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbt.go @@ -0,0 +1,1607 @@ +/* +Copyright 2015 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 main + +// Command docs are in cbtdoc.go. + +import ( + "bytes" + "flag" + "fmt" + "go/format" + "io" + "log" + "os" + "regexp" + "sort" + "strconv" + "strings" + "text/tabwriter" + "text/template" + "time" + + "encoding/csv" + + "cloud.google.com/go/bigtable" + "cloud.google.com/go/bigtable/internal/cbtconfig" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +var ( + oFlag = flag.String("o", "", "if set, redirect stdout to this file") + + config *cbtconfig.Config + client *bigtable.Client + adminClient *bigtable.AdminClient + instanceAdminClient *bigtable.InstanceAdminClient + + version = "" + revision = "" + revisionDate = "" + cliUserAgent = "cbt-cli-go/unknown" +) + +func getCredentialOpts(opts []option.ClientOption) []option.ClientOption { + if ts := config.TokenSource; ts != nil { + opts = append(opts, option.WithTokenSource(ts)) + } + if tlsCreds := config.TLSCreds; tlsCreds != nil { + opts = append(opts, option.WithGRPCDialOption(grpc.WithTransportCredentials(tlsCreds))) + } + return opts +} + +func getClient(clientConf bigtable.ClientConfig) *bigtable.Client { + if client == nil { + var opts []option.ClientOption + if ep := config.DataEndpoint; ep != "" { + opts = append(opts, option.WithEndpoint(ep)) + } + opts = append(opts, option.WithUserAgent(cliUserAgent)) + opts = getCredentialOpts(opts) + var err error + client, err = bigtable.NewClientWithConfig(context.Background(), config.Project, config.Instance, clientConf, opts...) + if err != nil { + log.Fatalf("Making bigtable.Client: %v", err) + } + opts = append(opts, option.WithUserAgent(cliUserAgent)) + } + return client +} + +func getAdminClient() *bigtable.AdminClient { + if adminClient == nil { + var opts []option.ClientOption + if ep := config.AdminEndpoint; ep != "" { + opts = append(opts, option.WithEndpoint(ep)) + } + opts = append(opts, option.WithUserAgent(cliUserAgent)) + opts = getCredentialOpts(opts) + var err error + adminClient, err = bigtable.NewAdminClient(context.Background(), config.Project, config.Instance, opts...) + if err != nil { + log.Fatalf("Making bigtable.AdminClient: %v", err) + } + } + return adminClient +} + +func getInstanceAdminClient() *bigtable.InstanceAdminClient { + if instanceAdminClient == nil { + var opts []option.ClientOption + if ep := config.AdminEndpoint; ep != "" { + opts = append(opts, option.WithEndpoint(ep)) + } + opts = getCredentialOpts(opts) + var err error + instanceAdminClient, err = bigtable.NewInstanceAdminClient(context.Background(), config.Project, opts...) + if err != nil { + log.Fatalf("Making bigtable.InstanceAdminClient: %v", err) + } + } + return instanceAdminClient +} + +func main() { + var err error + config, err = cbtconfig.Load() + if err != nil { + log.Fatal(err) + } + config.RegisterFlags() + + flag.Usage = func() { usage(os.Stderr) } + flag.Parse() + if flag.NArg() == 0 { + usage(os.Stderr) + os.Exit(1) + } + + if *oFlag != "" { + f, err := os.Create(*oFlag) + if err != nil { + log.Fatal(err) + } + defer func() { + if err := f.Close(); err != nil { + log.Fatal(err) + } + }() + os.Stdout = f + } + + if config.UserAgent != "" { + cliUserAgent = config.UserAgent + } + + ctx := context.Background() + for _, cmd := range commands { + if cmd.Name == flag.Arg(0) { + if err := config.CheckFlags(cmd.Required); err != nil { + log.Fatal(err) + } + cmd.do(ctx, flag.Args()[1:]...) + return + } + } + log.Fatalf("Unknown command %q", flag.Arg(0)) +} + +func usage(w io.Writer) { + fmt.Fprintf(w, "Usage: %s [flags] ...\n", os.Args[0]) + flag.CommandLine.SetOutput(w) + flag.CommandLine.PrintDefaults() + fmt.Fprintf(w, "\n%s", cmdSummary) +} + +var cmdSummary string // generated in init, below + +func init() { + var buf bytes.Buffer + tw := tabwriter.NewWriter(&buf, 10, 8, 4, '\t', 0) + for _, cmd := range commands { + fmt.Fprintf(tw, "cbt %s\t%s\n", cmd.Name, cmd.Desc) + } + tw.Flush() + buf.WriteString(configHelp) + buf.WriteString("\ncbt " + version + " " + revision + " " + revisionDate + "\n") + cmdSummary = buf.String() +} + +var configHelp = ` +Alpha features are not currently available to most Cloud Bigtable customers. The +features might be changed in backward-incompatible ways and are not recommended +for production use. They are not subject to any SLA or deprecation policy. + +For convenience, values of the -project, -instance, -creds, +-admin-endpoint and -data-endpoint flags may be specified in +~/.cbtrc in this format: + project = my-project-123 + instance = my-instance + creds = path-to-account-key.json + admin-endpoint = hostname:port + data-endpoint = hostname:port +All values are optional, and all will be overridden by flags. +` + +var commands = []struct { + Name, Desc string + do func(context.Context, ...string) + Usage string + Required cbtconfig.RequiredFlags +}{ + { + Name: "count", + Desc: "Count rows in a table", + do: doCount, + Usage: "cbt count
", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "createinstance", + Desc: "Create an instance with an initial cluster", + do: doCreateInstance, + Usage: "cbt createinstance \n" + + " instance-id Permanent, unique id for the instance\n" + + " display-name Description of the instance\n" + + " cluster-id Permanent, unique id for the cluster in the instance\n" + + " zone The zone in which to create the cluster\n" + + " num-nodes The number of nodes to create\n" + + " storage-type SSD or HDD\n", + Required: cbtconfig.ProjectRequired, + }, + { + Name: "createcluster", + Desc: "Create a cluster in the configured instance ", + do: doCreateCluster, + Usage: "cbt createcluster \n" + + " cluster-id Permanent, unique id for the cluster in the instance\n" + + " zone The zone in which to create the cluster\n" + + " num-nodes The number of nodes to create\n" + + " storage-type SSD or HDD\n", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "createfamily", + Desc: "Create a column family", + do: doCreateFamily, + Usage: "cbt createfamily
", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "createtable", + Desc: "Create a table", + do: doCreateTable, + Usage: "cbt createtable
[families=family[:(maxage= | maxversions=)],...] [splits=split,...]\n" + + " families: Column families and their associated GC policies. See \"setgcpolicy\".\n" + + " Example: families=family1:maxage=1w,family2:maxversions=1\n" + + " splits: Row key to be used to initially split the table", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "updatecluster", + Desc: "Update a cluster in the configured instance", + do: doUpdateCluster, + Usage: "cbt updatecluster [num-nodes=num-nodes]\n" + + " cluster-id Permanent, unique id for the cluster in the instance\n" + + " num-nodes The number of nodes to update to", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "deleteinstance", + Desc: "Delete an instance", + do: doDeleteInstance, + Usage: "cbt deleteinstance ", + Required: cbtconfig.ProjectRequired, + }, + { + Name: "deletecluster", + Desc: "Delete a cluster from the configured instance ", + do: doDeleteCluster, + Usage: "cbt deletecluster ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "deletecolumn", + Desc: "Delete all cells in a column", + do: doDeleteColumn, + Usage: "cbt deletecolumn
[app-profile=]\n" + + " app-profile= The app profile id to use for the request\n", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "deletefamily", + Desc: "Delete a column family", + do: doDeleteFamily, + Usage: "cbt deletefamily
", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "deleterow", + Desc: "Delete a row", + do: doDeleteRow, + Usage: "cbt deleterow
[app-profile=]\n" + + " app-profile= The app profile id to use for the request\n", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "deletetable", + Desc: "Delete a table", + do: doDeleteTable, + Usage: "cbt deletetable
", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "doc", + Desc: "Print godoc-suitable documentation for cbt", + do: doDoc, + Usage: "cbt doc", + Required: cbtconfig.NoneRequired, + }, + { + Name: "help", + Desc: "Print help text", + do: doHelp, + Usage: "cbt help [command]", + Required: cbtconfig.NoneRequired, + }, + { + Name: "listinstances", + Desc: "List instances in a project", + do: doListInstances, + Usage: "cbt listinstances", + Required: cbtconfig.ProjectRequired, + }, + { + Name: "listclusters", + Desc: "List clusters in an instance", + do: doListClusters, + Usage: "cbt listclusters", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "lookup", + Desc: "Read from a single row", + do: doLookup, + Usage: "cbt lookup
[columns=[family]:[qualifier],...] [cells-per-column=] " + + "[app-profile=]\n" + + " columns=[family]:[qualifier],... Read only these columns, comma-separated\n" + + " cells-per-column= Read only this many cells per column\n" + + " app-profile= The app profile id to use for the request\n", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "ls", + Desc: "List tables and column families", + do: doLS, + Usage: "cbt ls List tables\n" + + "cbt ls
List column families in
", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "mddoc", + Desc: "Print documentation for cbt in Markdown format", + do: doMDDoc, + Usage: "cbt mddoc", + Required: cbtconfig.NoneRequired, + }, + { + Name: "read", + Desc: "Read rows", + do: doRead, + Usage: "cbt read
[start=] [end=] [prefix=]" + + " [regex=] [columns=[family]:[qualifier],...] [count=] [cells-per-column=]" + + " [app-profile=]\n" + + " start= Start reading at this row\n" + + " end= Stop reading before this row\n" + + " prefix= Read rows with this prefix\n" + + " regex= Read rows with keys matching this regex\n" + + " columns=[family]:[qualifier],... Read only these columns, comma-separated\n" + + " count= Read only this many rows\n" + + " cells-per-column= Read only this many cells per column\n" + + " app-profile= The app profile id to use for the request\n", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "set", + Desc: "Set value of a cell", + do: doSet, + Usage: "cbt set
[app-profile=] family:column=val[@ts] ...\n" + + " app-profile= The app profile id to use for the request\n" + + " family:column=val[@ts] may be repeated to set multiple cells.\n" + + "\n" + + " ts is an optional integer timestamp.\n" + + " If it cannot be parsed, the `@ts` part will be\n" + + " interpreted as part of the value.", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "setgcpolicy", + Desc: "Set the GC policy for a column family", + do: doSetGCPolicy, + Usage: "cbt setgcpolicy
( maxage= | maxversions= | never)\n" + + "\n" + + ` maxage= Maximum timestamp age to preserve (e.g. "1h", "4d")` + "\n" + + " maxversions= Maximum number of versions to preserve", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "waitforreplication", + Desc: "Block until all the completed writes have been replicated to all the clusters", + do: doWaitForReplicaiton, + Usage: "cbt waitforreplication
", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "createtablefromsnapshot", + Desc: "Create a table from a snapshot (snapshots alpha)", + do: doCreateTableFromSnapshot, + Usage: "cbt createtablefromsnapshot
\n" + + " table The name of the table to create\n" + + " cluster The cluster where the snapshot is located\n" + + " snapshot The snapshot to restore", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "createsnapshot", + Desc: "Create a snapshot from a source table (snapshots alpha)", + do: doSnapshotTable, + Usage: "cbt createsnapshot
[ttl=]\n" + + "\n" + + ` [ttl=] Lifespan of the snapshot (e.g. "1h", "4d")` + "\n", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "listsnapshots", + Desc: "List snapshots in a cluster (snapshots alpha)", + do: doListSnapshots, + Usage: "cbt listsnapshots []", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "getsnapshot", + Desc: "Get snapshot info (snapshots alpha)", + do: doGetSnapshot, + Usage: "cbt getsnapshot ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "deletesnapshot", + Desc: "Delete snapshot in a cluster (snapshots alpha)", + do: doDeleteSnapshot, + Usage: "cbt deletesnapshot ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "version", + Desc: "Print the current cbt version", + do: doVersion, + Usage: "cbt version", + Required: cbtconfig.NoneRequired, + }, + { + Name: "createappprofile", + Desc: "Creates app profile for an instance", + do: doCreateAppProfile, + Usage: "cbt createappprofile \n" + + "[cluster-id=] [allow-transactional-writes=] \n" + + "set multi_cluster_routing_use_any or single_cluster_routing as possible values for routing policy \n" + + "provide cluster-id=clusterID and allow-transactional-writes=true or false in case of single_cluster_routing ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "getappprofile", + Desc: "Reads app profile for an instance", + do: doGetAppProfile, + Usage: "cbt getappprofile ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "listappprofile", + Desc: "Lists app profile for an instance", + do: doListAppProfiles, + Usage: "cbt listappprofile ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "updateappprofile", + Desc: "Updates app profile for an instance", + do: doUpdateAppProfile, + Usage: "cbt updateappprofile " + + "[cluster-id=] [allow-transactional-writes=] \n" + + "set multi_cluster_routing_use_any or single_cluster_routing as possible values for routing policy \n" + + "provide cluster-id=clusterID and allow-transactional-writes=true or false in case of single_cluster_routing ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, + { + Name: "deleteappprofile", + Desc: "Deletes app profile for an instance", + do: doDeleteAppProfile, + Usage: "cbt deleteappprofile ", + Required: cbtconfig.ProjectAndInstanceRequired, + }, +} + +func doCount(ctx context.Context, args ...string) { + if len(args) != 1 { + log.Fatal("usage: cbt count
") + } + tbl := getClient(bigtable.ClientConfig{}).Open(args[0]) + + n := 0 + err := tbl.ReadRows(ctx, bigtable.InfiniteRange(""), func(_ bigtable.Row) bool { + n++ + return true + }, bigtable.RowFilter(bigtable.StripValueFilter())) + if err != nil { + log.Fatalf("Reading rows: %v", err) + } +} + +func doCreateTable(ctx context.Context, args ...string) { + if len(args) < 1 { + log.Fatal("usage: cbt createtable
[families=family[:gcpolicy],...] [splits=split,...]") + } + + tblConf := bigtable.TableConf{TableID: args[0]} + parsed, err := parseArgs(args[1:], []string{"families", "splits"}) + if err != nil { + log.Fatal(err) + } + for key, val := range parsed { + chunks, err := csv.NewReader(strings.NewReader(val)).Read() + if err != nil { + log.Fatalf("Invalid %s arg format: %v", key, err) + } + switch key { + case "families": + tblConf.Families = make(map[string]bigtable.GCPolicy) + for _, family := range chunks { + famPolicy := strings.Split(family, ":") + var gcPolicy bigtable.GCPolicy + if len(famPolicy) < 2 { + gcPolicy = bigtable.MaxVersionsPolicy(1) + log.Printf("Using default GC Policy of %v for family %v", gcPolicy, family) + } else { + gcPolicy, err = parseGCPolicy(famPolicy[1]) + if err != nil { + log.Fatal(err) + } + } + tblConf.Families[famPolicy[0]] = gcPolicy + } + case "splits": + tblConf.SplitKeys = chunks + } + } + + if err := getAdminClient().CreateTableFromConf(ctx, &tblConf); err != nil { + log.Fatalf("Creating table: %v", err) + } +} + +func doCreateFamily(ctx context.Context, args ...string) { + if len(args) != 2 { + log.Fatal("usage: cbt createfamily
") + } + err := getAdminClient().CreateColumnFamily(ctx, args[0], args[1]) + if err != nil { + log.Fatalf("Creating column family: %v", err) + } +} + +func doCreateInstance(ctx context.Context, args ...string) { + if len(args) < 6 { + log.Fatal("cbt createinstance ") + } + + numNodes, err := strconv.ParseInt(args[4], 0, 32) + if err != nil { + log.Fatalf("Bad num-nodes %q: %v", args[4], err) + } + + sType, err := parseStorageType(args[5]) + if err != nil { + log.Fatal(err) + } + + ic := bigtable.InstanceWithClustersConfig{ + InstanceID: args[0], + DisplayName: args[1], + Clusters: []bigtable.ClusterConfig{{ + ClusterID: args[2], + Zone: args[3], + NumNodes: int32(numNodes), + StorageType: sType, + }}, + } + err = getInstanceAdminClient().CreateInstanceWithClusters(ctx, &ic) + if err != nil { + log.Fatalf("Creating instance: %v", err) + } +} + +func doCreateCluster(ctx context.Context, args ...string) { + if len(args) < 4 { + log.Fatal("usage: cbt createcluster ") + } + + numNodes, err := strconv.ParseInt(args[2], 0, 32) + if err != nil { + log.Fatalf("Bad num_nodes %q: %v", args[2], err) + } + + sType, err := parseStorageType(args[3]) + if err != nil { + log.Fatal(err) + } + + cc := bigtable.ClusterConfig{ + InstanceID: config.Instance, + ClusterID: args[0], + Zone: args[1], + NumNodes: int32(numNodes), + StorageType: sType, + } + err = getInstanceAdminClient().CreateCluster(ctx, &cc) + if err != nil { + log.Fatalf("Creating cluster: %v", err) + } +} + +func doUpdateCluster(ctx context.Context, args ...string) { + if len(args) < 2 { + log.Fatal("cbt updatecluster [num-nodes=num-nodes]") + } + + numNodes := int64(0) + parsed, err := parseArgs(args[1:], []string{"num-nodes"}) + if err != nil { + log.Fatal(err) + } + if val, ok := parsed["num-nodes"]; ok { + numNodes, err = strconv.ParseInt(val, 0, 32) + if err != nil { + log.Fatalf("Bad num-nodes %q: %v", val, err) + } + } + if numNodes > 0 { + err = getInstanceAdminClient().UpdateCluster(ctx, config.Instance, args[0], int32(numNodes)) + if err != nil { + log.Fatalf("Updating cluster: %v", err) + } + } else { + log.Fatal("Updating cluster: nothing to update") + } +} + +func doDeleteInstance(ctx context.Context, args ...string) { + if len(args) != 1 { + log.Fatal("usage: cbt deleteinstance ") + } + err := getInstanceAdminClient().DeleteInstance(ctx, args[0]) + if err != nil { + log.Fatalf("Deleting instance: %v", err) + } +} + +func doDeleteCluster(ctx context.Context, args ...string) { + if len(args) != 1 { + log.Fatal("usage: cbt deletecluster ") + } + err := getInstanceAdminClient().DeleteCluster(ctx, config.Instance, args[0]) + if err != nil { + log.Fatalf("Deleting cluster: %v", err) + } +} + +func doDeleteColumn(ctx context.Context, args ...string) { + usage := "usage: cbt deletecolumn
[app-profile=]" + if len(args) != 4 && len(args) != 5 { + log.Fatal(usage) + } + var appProfile string + if len(args) == 5 { + if !strings.HasPrefix(args[4], "app-profile=") { + log.Fatal(usage) + } + appProfile = strings.Split(args[4], "=")[1] + } + tbl := getClient(bigtable.ClientConfig{AppProfile: appProfile}).Open(args[0]) + mut := bigtable.NewMutation() + mut.DeleteCellsInColumn(args[2], args[3]) + if err := tbl.Apply(ctx, args[1], mut); err != nil { + log.Fatalf("Deleting cells in column: %v", err) + } +} + +func doDeleteFamily(ctx context.Context, args ...string) { + if len(args) != 2 { + log.Fatal("usage: cbt deletefamily
") + } + err := getAdminClient().DeleteColumnFamily(ctx, args[0], args[1]) + if err != nil { + log.Fatalf("Deleting column family: %v", err) + } +} + +func doDeleteRow(ctx context.Context, args ...string) { + usage := "usage: cbt deleterow
[app-profile=]" + if len(args) != 2 && len(args) != 3 { + log.Fatal(usage) + } + var appProfile string + if len(args) == 3 { + if !strings.HasPrefix(args[2], "app-profile=") { + log.Fatal(usage) + } + appProfile = strings.Split(args[2], "=")[1] + } + tbl := getClient(bigtable.ClientConfig{AppProfile: appProfile}).Open(args[0]) + mut := bigtable.NewMutation() + mut.DeleteRow() + if err := tbl.Apply(ctx, args[1], mut); err != nil { + log.Fatalf("Deleting row: %v", err) + } +} + +func doDeleteTable(ctx context.Context, args ...string) { + if len(args) != 1 { + log.Fatalf("Can't do `cbt deletetable %s`", args) + } + err := getAdminClient().DeleteTable(ctx, args[0]) + if err != nil { + log.Fatalf("Deleting table: %v", err) + } +} + +// to break circular dependencies +var ( + doDocFn func(ctx context.Context, args ...string) + doHelpFn func(ctx context.Context, args ...string) + doMDDocFn func(ctx context.Context, args ...string) +) + +func init() { + doDocFn = doDocReal + doHelpFn = doHelpReal + doMDDocFn = doMDDocReal +} + +func doDoc(ctx context.Context, args ...string) { doDocFn(ctx, args...) } +func doHelp(ctx context.Context, args ...string) { doHelpFn(ctx, args...) } +func doMDDoc(ctx context.Context, args ...string) { doMDDocFn(ctx, args...) } + +func docFlags() []*flag.Flag { + // Only include specific flags, in a specific order. + var flags []*flag.Flag + for _, name := range []string{"project", "instance", "creds"} { + f := flag.Lookup(name) + if f == nil { + log.Fatalf("Flag not linked: -%s", name) + } + flags = append(flags, f) + } + return flags +} + +func doDocReal(ctx context.Context, args ...string) { + data := map[string]interface{}{ + "Commands": commands, + "Flags": docFlags(), + "ConfigHelp": configHelp, + } + var buf bytes.Buffer + if err := docTemplate.Execute(&buf, data); err != nil { + log.Fatalf("Bad doc template: %v", err) + } + out, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatalf("Bad doc output: %v", err) + } + os.Stdout.Write(out) +} + +func indentLines(s, ind string) string { + ss := strings.Split(s, "\n") + for i, p := range ss { + ss[i] = ind + p + } + return strings.Join(ss, "\n") +} + +var docTemplate = template.Must(template.New("doc").Funcs(template.FuncMap{ + "indent": indentLines, +}). + Parse(` +// 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. + +// DO NOT EDIT. THIS IS AUTOMATICALLY GENERATED. +// Run "go generate" to regenerate. +//go:generate go run cbt.go -o cbtdoc.go doc + +/* +Cbt is a tool for doing basic interactions with Cloud Bigtable. To learn how to +install the cbt tool, see the +[cbt overview](https://cloud.google.com/bigtable/docs/go/cbt-overview). + +Usage: + + cbt [options] command [arguments] + +The commands are: +{{range .Commands}} + {{printf "%-25s %s" .Name .Desc}}{{end}} + +Use "cbt help " for more information about a command. + +The options are: +{{range .Flags}} + -{{.Name}} string + {{.Usage}}{{end}} + +{{.ConfigHelp}} + +{{range .Commands}} +{{.Desc}} + +Usage: +{{indent .Usage "\t"}} + + + +{{end}} +*/ +package main +`)) + +func doHelpReal(ctx context.Context, args ...string) { + if len(args) == 0 { + usage(os.Stdout) + return + } + for _, cmd := range commands { + if cmd.Name == args[0] { + fmt.Println(cmd.Usage) + return + } + } + log.Fatalf("Don't know command %q", args[0]) +} + +func doListInstances(ctx context.Context, args ...string) { + if len(args) != 0 { + log.Fatalf("usage: cbt listinstances") + } + is, err := getInstanceAdminClient().Instances(ctx) + if err != nil { + log.Fatalf("Getting list of instances: %v", err) + } + tw := tabwriter.NewWriter(os.Stdout, 10, 8, 4, '\t', 0) + fmt.Fprintf(tw, "Instance Name\tInfo\n") + fmt.Fprintf(tw, "-------------\t----\n") + for _, i := range is { + fmt.Fprintf(tw, "%s\t%s\n", i.Name, i.DisplayName) + } + tw.Flush() +} + +func doListClusters(ctx context.Context, args ...string) { + if len(args) != 0 { + log.Fatalf("usage: cbt listclusters") + } + cis, err := getInstanceAdminClient().Clusters(ctx, config.Instance) + if err != nil { + log.Fatalf("Getting list of clusters: %v", err) + } + tw := tabwriter.NewWriter(os.Stdout, 10, 8, 4, '\t', 0) + fmt.Fprintf(tw, "Cluster Name\tZone\tState\n") + fmt.Fprintf(tw, "------------\t----\t----\n") + for _, ci := range cis { + fmt.Fprintf(tw, "%s\t%s\t%s (%d serve nodes)\n", ci.Name, ci.Zone, ci.State, ci.ServeNodes) + } + tw.Flush() +} + +func doLookup(ctx context.Context, args ...string) { + if len(args) < 2 { + log.Fatalf("usage: cbt lookup
[columns=...] [cells-per-column=] " + + "[app-profile=]") + } + + parsed, err := parseArgs(args[2:], []string{"columns", "cells-per-column", "app-profile"}) + if err != nil { + log.Fatal(err) + } + var opts []bigtable.ReadOption + var filters []bigtable.Filter + if cellsPerColumn := parsed["cells-per-column"]; cellsPerColumn != "" { + n, err := strconv.Atoi(cellsPerColumn) + if err != nil { + log.Fatalf("Bad number of cells per column %q: %v", cellsPerColumn, err) + } + filters = append(filters, bigtable.LatestNFilter(n)) + } + if columns := parsed["columns"]; columns != "" { + columnFilters, err := parseColumnsFilter(columns) + if err != nil { + log.Fatal(err) + } + filters = append(filters, columnFilters) + } + + if len(filters) > 1 { + opts = append(opts, bigtable.RowFilter(bigtable.ChainFilters(filters...))) + } else if len(filters) == 1 { + opts = append(opts, bigtable.RowFilter(filters[0])) + } + + table, row := args[0], args[1] + tbl := getClient(bigtable.ClientConfig{AppProfile: parsed["app-profile"]}).Open(table) + r, err := tbl.ReadRow(ctx, row, opts...) + if err != nil { + log.Fatalf("Reading row: %v", err) + } + printRow(r) +} + +func printRow(r bigtable.Row) { + fmt.Println(strings.Repeat("-", 40)) + fmt.Println(r.Key()) + + var fams []string + for fam := range r { + fams = append(fams, fam) + } + sort.Strings(fams) + for _, fam := range fams { + ris := r[fam] + sort.Sort(byColumn(ris)) + for _, ri := range ris { + ts := time.Unix(0, int64(ri.Timestamp)*1e3) + fmt.Printf(" %-40s @ %s\n", ri.Column, ts.Format("2006/01/02-15:04:05.000000")) + fmt.Printf(" %q\n", ri.Value) + } + } +} + +type byColumn []bigtable.ReadItem + +func (b byColumn) Len() int { return len(b) } +func (b byColumn) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byColumn) Less(i, j int) bool { return b[i].Column < b[j].Column } + +type byFamilyName []bigtable.FamilyInfo + +func (b byFamilyName) Len() int { return len(b) } +func (b byFamilyName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byFamilyName) Less(i, j int) bool { return b[i].Name < b[j].Name } + +func doLS(ctx context.Context, args ...string) { + switch len(args) { + default: + log.Fatalf("Can't do `cbt ls %s`", args) + case 0: + tables, err := getAdminClient().Tables(ctx) + if err != nil { + log.Fatalf("Getting list of tables: %v", err) + } + sort.Strings(tables) + for _, table := range tables { + fmt.Println(table) + } + case 1: + table := args[0] + ti, err := getAdminClient().TableInfo(ctx, table) + if err != nil { + log.Fatalf("Getting table info: %v", err) + } + sort.Sort(byFamilyName(ti.FamilyInfos)) + tw := tabwriter.NewWriter(os.Stdout, 10, 8, 4, '\t', 0) + fmt.Fprintf(tw, "Family Name\tGC Policy\n") + fmt.Fprintf(tw, "-----------\t---------\n") + for _, fam := range ti.FamilyInfos { + fmt.Fprintf(tw, "%s\t%s\n", fam.Name, fam.GCPolicy) + } + tw.Flush() + } +} + +func doMDDocReal(ctx context.Context, args ...string) { + data := map[string]interface{}{ + "Commands": commands, + "Flags": docFlags(), + "ConfigHelp": configHelp, + } + var buf bytes.Buffer + if err := mddocTemplate.Execute(&buf, data); err != nil { + log.Fatalf("Bad mddoc template: %v", err) + } + io.Copy(os.Stdout, &buf) +} + +var mddocTemplate = template.Must(template.New("mddoc").Funcs(template.FuncMap{ + "indent": indentLines, +}). + Parse(` +Cbt is a tool for doing basic interactions with Cloud Bigtable. + +Usage: + + cbt [options] command [arguments] + +The commands are: +{{range .Commands}} + {{printf "%-25s %s" .Name .Desc}}{{end}} + +Use "cbt help " for more information about a command. + +The options are: +{{range .Flags}} + -{{.Name}} string + {{.Usage}}{{end}} + +{{.ConfigHelp}} + +{{range .Commands}} +## {{.Desc}} + +{{indent .Usage "\t"}} + + + +{{end}} +`)) + +func doRead(ctx context.Context, args ...string) { + if len(args) < 1 { + log.Fatalf("usage: cbt read
[args ...]") + } + + parsed, err := parseArgs(args[1:], []string{ + "start", "end", "prefix", "columns", "count", "cells-per-column", "regex", "app-profile", "limit", + }) + if err != nil { + log.Fatal(err) + } + if _, ok := parsed["limit"]; ok { + // Be nicer; we used to support this, but renamed it to "end". + log.Fatal("Unknown arg key 'limit'; did you mean 'end'?") + } + if (parsed["start"] != "" || parsed["end"] != "") && parsed["prefix"] != "" { + log.Fatal(`"start"/"end" may not be mixed with "prefix"`) + } + + var rr bigtable.RowRange + if start, end := parsed["start"], parsed["end"]; end != "" { + rr = bigtable.NewRange(start, end) + } else if start != "" { + rr = bigtable.InfiniteRange(start) + } + if prefix := parsed["prefix"]; prefix != "" { + rr = bigtable.PrefixRange(prefix) + } + + var opts []bigtable.ReadOption + if count := parsed["count"]; count != "" { + n, err := strconv.ParseInt(count, 0, 64) + if err != nil { + log.Fatalf("Bad count %q: %v", count, err) + } + opts = append(opts, bigtable.LimitRows(n)) + } + + var filters []bigtable.Filter + if cellsPerColumn := parsed["cells-per-column"]; cellsPerColumn != "" { + n, err := strconv.Atoi(cellsPerColumn) + if err != nil { + log.Fatalf("Bad number of cells per column %q: %v", cellsPerColumn, err) + } + filters = append(filters, bigtable.LatestNFilter(n)) + } + if regex := parsed["regex"]; regex != "" { + filters = append(filters, bigtable.RowKeyFilter(regex)) + } + if columns := parsed["columns"]; columns != "" { + columnFilters, err := parseColumnsFilter(columns) + if err != nil { + log.Fatal(err) + } + filters = append(filters, columnFilters) + } + + if len(filters) > 1 { + opts = append(opts, bigtable.RowFilter(bigtable.ChainFilters(filters...))) + } else if len(filters) == 1 { + opts = append(opts, bigtable.RowFilter(filters[0])) + } + + // TODO(dsymonds): Support filters. + tbl := getClient(bigtable.ClientConfig{AppProfile: parsed["app-profile"]}).Open(args[0]) + err = tbl.ReadRows(ctx, rr, func(r bigtable.Row) bool { + printRow(r) + return true + }, opts...) + if err != nil { + log.Fatalf("Reading rows: %v", err) + } +} + +var setArg = regexp.MustCompile(`([^:]+):([^=]*)=(.*)`) + +func doSet(ctx context.Context, args ...string) { + if len(args) < 3 { + log.Fatalf("usage: cbt set
[app-profile=] family:[column]=val[@ts] ...") + } + var appProfile string + row := args[1] + mut := bigtable.NewMutation() + for _, arg := range args[2:] { + if strings.HasPrefix(arg, "app-profile=") { + appProfile = strings.Split(arg, "=")[1] + continue + } + m := setArg.FindStringSubmatch(arg) + if m == nil { + log.Fatalf("Bad set arg %q", arg) + } + val := m[3] + ts := bigtable.Now() + if i := strings.LastIndex(val, "@"); i >= 0 { + // Try parsing a timestamp. + n, err := strconv.ParseInt(val[i+1:], 0, 64) + if err == nil { + val = val[:i] + ts = bigtable.Timestamp(n) + } + } + mut.Set(m[1], m[2], ts, []byte(val)) + } + tbl := getClient(bigtable.ClientConfig{AppProfile: appProfile}).Open(args[0]) + if err := tbl.Apply(ctx, row, mut); err != nil { + log.Fatalf("Applying mutation: %v", err) + } +} + +func doSetGCPolicy(ctx context.Context, args ...string) { + if len(args) < 3 { + log.Fatalf("usage: cbt setgcpolicy
( maxage= | maxversions= | maxage= (and|or) maxversions= | never )") + } + table := args[0] + fam := args[1] + + pol, err := parseGCPolicy(strings.Join(args[2:], " ")) + if err != nil { + log.Fatal(err) + } + if err := getAdminClient().SetGCPolicy(ctx, table, fam, pol); err != nil { + log.Fatalf("Setting GC policy: %v", err) + } +} + +func doWaitForReplicaiton(ctx context.Context, args ...string) { + if len(args) != 1 { + log.Fatalf("usage: cbt waitforreplication
") + } + table := args[0] + + fmt.Printf("Waiting for all writes up to %s to be replicated.\n", time.Now().Format("2006/01/02-15:04:05")) + if err := getAdminClient().WaitForReplication(ctx, table); err != nil { + log.Fatalf("Waiting for replication: %v", err) + } +} + +func parseGCPolicy(policyStr string) (bigtable.GCPolicy, error) { + words := strings.Fields(policyStr) + + switch len(words) { + case 1: + return parseSinglePolicy(words[0]) + case 3: + p1, err := parseSinglePolicy(words[0]) + if err != nil { + return nil, err + } + p2, err := parseSinglePolicy(words[2]) + if err != nil { + return nil, err + } + switch words[1] { + case "and": + return bigtable.IntersectionPolicy(p1, p2), nil + case "or": + return bigtable.UnionPolicy(p1, p2), nil + default: + return nil, fmt.Errorf("Expected 'and' or 'or', saw %q", words[1]) + } + default: + return nil, fmt.Errorf("Expected '1' or '3' parameter count, saw %d", len(words)) + } + return nil, nil +} + +func parseSinglePolicy(s string) (bigtable.GCPolicy, error) { + words := strings.Split(s, "=") + if len(words) != 2 && words[0] != "never" { + return nil, fmt.Errorf("Expected 'name=value ', got %q", words) + } + + switch words[0] { + case "never": + if len(words) != 1 { + return nil, fmt.Errorf("Expected 'never', got %q", s) + } + return bigtable.NoGcPolicy(), nil + case "maxage": + d, err := parseDuration(words[1]) + if err != nil { + return nil, err + } + return bigtable.MaxAgePolicy(d), nil + case "maxversions": + n, err := strconv.ParseUint(words[1], 10, 16) + if err != nil { + return nil, err + } + return bigtable.MaxVersionsPolicy(int(n)), nil + default: + return nil, fmt.Errorf("Expected 'maxage' or 'maxversions', got %q", words[len(words)-1]) + } + return nil, nil +} + +func parseStorageType(storageTypeStr string) (bigtable.StorageType, error) { + switch storageTypeStr { + case "SSD": + return bigtable.SSD, nil + case "HDD": + return bigtable.HDD, nil + } + return -1, fmt.Errorf("Invalid storage type: %v, must be SSD or HDD", storageTypeStr) +} + +func doCreateTableFromSnapshot(ctx context.Context, args ...string) { + if len(args) != 3 { + log.Fatal("usage: cbt createtablefromsnapshot
") + } + tableName := args[0] + clusterName := args[1] + snapshotName := args[2] + err := getAdminClient().CreateTableFromSnapshot(ctx, tableName, clusterName, snapshotName) + + if err != nil { + log.Fatalf("Creating table: %v", err) + } +} + +func doSnapshotTable(ctx context.Context, args ...string) { + if len(args) != 3 && len(args) != 4 { + log.Fatal("usage: cbt createsnapshot
[ttl=]") + } + clusterName := args[0] + snapshotName := args[1] + tableName := args[2] + ttl := bigtable.DefaultSnapshotDuration + + parsed, err := parseArgs(args[3:], []string{"ttl"}) + if err != nil { + log.Fatal(err) + } + if val, ok := parsed["ttl"]; ok { + var err error + ttl, err = parseDuration(val) + if err != nil { + log.Fatalf("Invalid snapshot ttl value %q: %v", val, err) + } + } + + err = getAdminClient().SnapshotTable(ctx, tableName, clusterName, snapshotName, ttl) + if err != nil { + log.Fatalf("Failed to create Snapshot: %v", err) + } +} + +func doListSnapshots(ctx context.Context, args ...string) { + if len(args) != 0 && len(args) != 1 { + log.Fatal("usage: cbt listsnapshots []") + } + + var cluster string + + if len(args) == 0 { + cluster = "-" + } else { + cluster = args[0] + } + + it := getAdminClient().Snapshots(ctx, cluster) + + tw := tabwriter.NewWriter(os.Stdout, 10, 8, 4, '\t', 0) + fmt.Fprintf(tw, "Snapshot\tSource Table\tCreated At\tExpires At\n") + fmt.Fprintf(tw, "--------\t------------\t----------\t----------\n") + timeLayout := "2006-01-02 15:04 MST" + + for { + snapshot, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + log.Fatalf("Failed to fetch snapshots %v", err) + } + fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", snapshot.Name, snapshot.SourceTable, snapshot.CreateTime.Format(timeLayout), snapshot.DeleteTime.Format(timeLayout)) + } + tw.Flush() +} + +func doGetSnapshot(ctx context.Context, args ...string) { + if len(args) != 2 { + log.Fatalf("usage: cbt getsnapshot ") + } + clusterName := args[0] + snapshotName := args[1] + + snapshot, err := getAdminClient().SnapshotInfo(ctx, clusterName, snapshotName) + if err != nil { + log.Fatalf("Failed to get snapshot: %v", err) + } + + timeLayout := "2006-01-02 15:04 MST" + + fmt.Printf("Name: %s\n", snapshot.Name) + fmt.Printf("Source table: %s\n", snapshot.SourceTable) + fmt.Printf("Created at: %s\n", snapshot.CreateTime.Format(timeLayout)) + fmt.Printf("Expires at: %s\n", snapshot.DeleteTime.Format(timeLayout)) +} + +func doDeleteSnapshot(ctx context.Context, args ...string) { + if len(args) != 2 { + log.Fatal("usage: cbt deletesnapshot ") + } + cluster := args[0] + snapshot := args[1] + + err := getAdminClient().DeleteSnapshot(ctx, cluster, snapshot) + + if err != nil { + log.Fatalf("Failed to delete snapshot: %v", err) + } +} + +func doCreateAppProfile(ctx context.Context, args ...string) { + if len(args) < 5 { + log.Fatal("usage: cbt createappprofile \n" + + "[cluster-id=] [allow-transactional-writes=] \n" + + "set multi_cluster_routing_use_any or single_cluster_routing as possible values for routing policy \n" + + "provide cluster-id=clusterID and allow-transactional-writes=true or false in case of single_cluster_routing ") + } + + routingPolicy := args[4] + config := bigtable.ProfileConf{ + RoutingPolicy: routingPolicy, + InstanceID: args[0], + ProfileID: args[1], + Description: args[2], + Etag: args[3], + } + if routingPolicy == bigtable.SingleClusterRouting { + parsed, err := parseArgs(args[4:], []string{ + "cluster-id", "allow-transactional-writes", + }) + if err != nil { + log.Fatal(err) + } + + transactionWrites, err := strconv.ParseBool(parsed["allow-transactional-writes"]) + if err != nil { + log.Fatal(err) + } + + config.ClusterID = parsed["cluster-id"] + config.AllowTransactionalWrites = transactionWrites + } + + profile, err := getInstanceAdminClient().CreateAppProfile(ctx, config) + if err != nil { + log.Fatalf("Failed to create app profile : %v", err) + } + + fmt.Printf("Name: %s\n", profile.Name) + fmt.Printf("Etag: %v\n", profile.GetEtag()) + fmt.Printf("Description: %s\n", profile.Description) + fmt.Printf("RoutingPolicy: %v\n", profile.GetRoutingPolicy()) +} + +func doGetAppProfile(ctx context.Context, args ...string) { + if len(args) != 2 { + log.Fatalln("usage: cbt getappprofile ") + } + + instanceID := args[0] + profileID := args[1] + profile, err := getInstanceAdminClient().GetAppProfile(ctx, instanceID, profileID) + if err != nil { + log.Fatalf("Failed to get app profile : %v", err) + } + + fmt.Printf("Name: %s\n", profile.Name) + fmt.Printf("Etag: %s\n", profile.Etag) + fmt.Printf("Description: %s\n", profile.Description) + fmt.Printf("RoutingPolicy: %v\n", profile.RoutingPolicy) +} + +func doListAppProfiles(ctx context.Context, args ...string) { + if len(args) != 1 { + log.Fatalln("usage: cbt listappprofile ") + } + + instance := args[0] + + it := getInstanceAdminClient().ListAppProfiles(ctx, instance) + + tw := tabwriter.NewWriter(os.Stdout, 10, 8, 4, '\t', 0) + fmt.Fprintf(tw, "AppProfile\tProfile Description\tProfile Etag\tProfile Routing Policy\n") + fmt.Fprintf(tw, "-----------\t--------------------\t------------\t----------------------\n") + + for { + profile, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + log.Fatalf("Failed to fetch app profile %v", err) + } + fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", profile.Name, profile.Description, profile.Etag, profile.RoutingPolicy) + } + tw.Flush() +} + +func doUpdateAppProfile(ctx context.Context, args ...string) { + + if len(args) < 4 { + log.Fatal("usage: cbt updateappprofile [cluster-id=] [allow-transactional-writes=]") + } + + routingPolicy := args[3] + InstanceID := args[0] + ProfileID := args[1] + config := bigtable.ProfileAttrsToUpdate{ + RoutingPolicy: routingPolicy, + Description: args[2], + } + if routingPolicy == bigtable.SingleClusterRouting { + parsed, err := parseArgs(args[3:], []string{ + "cluster-id", "allow-transactional-writes", + }) + if err != nil { + log.Fatal(err) + } + + transactionWrites, err := strconv.ParseBool(parsed["allow-transactional-writes"]) + if err != nil { + log.Fatal(err) + } + + config.ClusterID = parsed["cluster-id"] + config.AllowTransactionalWrites = transactionWrites + } + + err := getInstanceAdminClient().UpdateAppProfile(ctx, InstanceID, ProfileID, config) + if err != nil { + log.Fatalf("Failed to update app profile : %v", err) + } +} + +func doDeleteAppProfile(ctx context.Context, args ...string) { + if len(args) != 2 { + log.Println("usage: cbt deleteappprofile ") + } + + err := getInstanceAdminClient().DeleteAppProfile(ctx, args[0], args[1]) + if err != nil { + log.Fatalf("Failed to delete app profile : %v", err) + } +} + +// parseDuration parses a duration string. +// It is similar to Go's time.ParseDuration, except with a different set of supported units, +// and only simple formats supported. +func parseDuration(s string) (time.Duration, error) { + // [0-9]+[a-z]+ + + // Split [0-9]+ from [a-z]+. + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + } + ds, u := s[:i], s[i:] + if ds == "" || u == "" { + return 0, fmt.Errorf("invalid duration %q", s) + } + // Parse them. + d, err := strconv.ParseUint(ds, 10, 32) + if err != nil { + return 0, fmt.Errorf("invalid duration %q: %v", s, err) + } + unit, ok := unitMap[u] + if !ok { + return 0, fmt.Errorf("unknown unit %q in duration %q", u, s) + } + if d > uint64((1<<63-1)/unit) { + // overflow + return 0, fmt.Errorf("invalid duration %q overflows", s) + } + return time.Duration(d) * unit, nil +} + +var unitMap = map[string]time.Duration{ + "ms": time.Millisecond, + "s": time.Second, + "m": time.Minute, + "h": time.Hour, + "d": 24 * time.Hour, +} + +func doVersion(ctx context.Context, args ...string) { + fmt.Printf("%s %s %s\n", version, revision, revisionDate) +} + +// parseArgs takes a slice of arguments of the form key=value and returns a map from +// key to value. It returns an error if an argument is malformed or a key is not in +// the valid slice. +func parseArgs(args []string, valid []string) (map[string]string, error) { + parsed := make(map[string]string) + for _, arg := range args { + i := strings.Index(arg, "=") + if i < 0 { + return nil, fmt.Errorf("Bad arg %q", arg) + } + key, val := arg[:i], arg[i+1:] + if !stringInSlice(key, valid) { + return nil, fmt.Errorf("Unknown arg key %q", key) + } + parsed[key] = val + } + return parsed, nil +} + +func stringInSlice(s string, list []string) bool { + for _, e := range list { + if s == e { + return true + } + } + return false +} + +func parseColumnsFilter(columns string) (bigtable.Filter, error) { + splitColumns := strings.FieldsFunc(columns, func(c rune) bool { return c == ',' }) + if len(splitColumns) == 1 { + filter, err := columnFilter(splitColumns[0]) + if err != nil { + return nil, err + } + return filter, nil + } + + var columnFilters []bigtable.Filter + for _, column := range splitColumns { + filter, err := columnFilter(column) + if err != nil { + return nil, err + } + columnFilters = append(columnFilters, filter) + } + return bigtable.InterleaveFilters(columnFilters...), nil +} + +func columnFilter(column string) (bigtable.Filter, error) { + splitColumn := strings.Split(column, ":") + if len(splitColumn) == 1 { + return bigtable.ColumnFilter(splitColumn[0]), nil + } else if len(splitColumn) == 2 { + if strings.HasSuffix(column, ":") { + return bigtable.FamilyFilter(splitColumn[0]), nil + } else if strings.HasPrefix(column, ":") { + return bigtable.ColumnFilter(splitColumn[1]), nil + } else { + familyFilter := bigtable.FamilyFilter(splitColumn[0]) + qualifierFilter := bigtable.ColumnFilter(splitColumn[1]) + return bigtable.ChainFilters(familyFilter, qualifierFilter), nil + } + } else { + return nil, fmt.Errorf("Bad format for column %q", column) + } +} diff --git a/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbt_test.go b/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbt_test.go new file mode 100644 index 0000000000..83bf94dea7 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbt_test.go @@ -0,0 +1,226 @@ +// 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 main + +import ( + "testing" + "time" + + "cloud.google.com/go/bigtable" + "cloud.google.com/go/internal/testutil" + "github.com/google/go-cmp/cmp" +) + +func TestParseDuration(t *testing.T) { + tests := []struct { + in string + // out or fail are mutually exclusive + out time.Duration + fail bool + }{ + {in: "10ms", out: 10 * time.Millisecond}, + {in: "3s", out: 3 * time.Second}, + {in: "60m", out: 60 * time.Minute}, + {in: "12h", out: 12 * time.Hour}, + {in: "7d", out: 168 * time.Hour}, + + {in: "", fail: true}, + {in: "0", fail: true}, + {in: "7ns", fail: true}, + {in: "14mo", fail: true}, + {in: "3.5h", fail: true}, + {in: "106752d", fail: true}, // overflow + } + for _, tc := range tests { + got, err := parseDuration(tc.in) + if !tc.fail && err != nil { + t.Errorf("parseDuration(%q) unexpectedly failed: %v", tc.in, err) + continue + } + if tc.fail && err == nil { + t.Errorf("parseDuration(%q) did not fail", tc.in) + continue + } + if tc.fail { + continue + } + if got != tc.out { + t.Errorf("parseDuration(%q) = %v, want %v", tc.in, got, tc.out) + } + } +} + +func TestParseGCPolicy(t *testing.T) { + tests := []struct { + in string + out bigtable.GCPolicy + fail bool + }{ + {in: "maxage=1h", out: bigtable.MaxAgePolicy(time.Hour * 1)}, + {in: "maxversions=2", out: bigtable.MaxVersionsPolicy(int(2))}, + {in: "maxversions=2 and maxage=1h", out: bigtable.IntersectionPolicy([]bigtable.GCPolicy{bigtable.MaxVersionsPolicy(int(2)), bigtable.MaxAgePolicy(time.Hour * 1)}...)}, + {in: "maxversions=2 or maxage=1h", out: bigtable.UnionPolicy([]bigtable.GCPolicy{bigtable.MaxVersionsPolicy(int(2)), bigtable.MaxAgePolicy(time.Hour * 1)}...)}, + + {in: "maxage=1", fail: true}, + {in: "maxage = 1h", fail: true}, + {in: "maxage =1h", fail: true}, + {in: "maxage= 1h", fail: true}, + {in: "foomaxage=1h", fail: true}, + {in: "maxversions=1h", fail: true}, + {in: "maxversions= 1", fail: true}, + {in: "maxversions = 1", fail: true}, + {in: "maxversions =1", fail: true}, + {in: "barmaxversions=1", fail: true}, + {in: "maxage = 1h or maxversions=1h", fail: true}, + {in: "foomaxversions=2 or maxage=1h", fail: true}, + {in: "maxversions=2 or barmaxage=1h", fail: true}, + {in: "foomaxversions=2 or barmaxage=1h", fail: true}, + {in: "maxage = 1h and maxversions=1h", fail: true}, + {in: "foomaxage=1h and maxversions=1", fail: true}, + {in: "maxage=1h and barmaxversions=1", fail: true}, + {in: "foomaxage=1h and barmaxversions=1", fail: true}, + {in: "never", out: bigtable.NoGcPolicy()}, + {in: "never=never", fail: true}, + } + for _, tc := range tests { + got, err := parseGCPolicy(tc.in) + if !tc.fail && err != nil { + t.Errorf("parseGCPolicy(%q) unexpectedly failed: %v", tc.in, err) + continue + } + if tc.fail && err == nil { + t.Errorf("parseGCPolicy(%q) did not fail", tc.in) + continue + } + if tc.fail { + continue + } + var cmpOpts cmp.Options + cmpOpts = append(cmpOpts, cmp.AllowUnexported(bigtable.IntersectionPolicy([]bigtable.GCPolicy{}...)), cmp.AllowUnexported(bigtable.UnionPolicy([]bigtable.GCPolicy{}...))) + if !cmp.Equal(got, tc.out, cmpOpts) { + t.Errorf("parseGCPolicy(%q) =%v, want %v", tc.in, got, tc.out) + } + } +} + +func TestParseArgs(t *testing.T) { + got, err := parseArgs([]string{"a=1", "b=2"}, []string{"a", "b"}) + if err != nil { + t.Fatal(err) + } + want := map[string]string{"a": "1", "b": "2"} + if !testutil.Equal(got, want) { + t.Fatalf("got %v, want %v", got, want) + } + + if _, err := parseArgs([]string{"a1"}, []string{"a1"}); err == nil { + t.Error("malformed: got nil, want error") + } + if _, err := parseArgs([]string{"a=1"}, []string{"b"}); err == nil { + t.Error("invalid: got nil, want error") + } +} + +func TestParseColumnsFilter(t *testing.T) { + tests := []struct { + in string + out bigtable.Filter + fail bool + }{ + { + in: "columnA", + out: bigtable.ColumnFilter("columnA"), + }, + { + in: "familyA:columnA", + out: bigtable.ChainFilters(bigtable.FamilyFilter("familyA"), bigtable.ColumnFilter("columnA")), + }, + { + in: "columnA,columnB", + out: bigtable.InterleaveFilters(bigtable.ColumnFilter("columnA"), bigtable.ColumnFilter("columnB")), + }, + { + in: "familyA:columnA,columnB", + out: bigtable.InterleaveFilters( + bigtable.ChainFilters(bigtable.FamilyFilter("familyA"), bigtable.ColumnFilter("columnA")), + bigtable.ColumnFilter("columnB"), + ), + }, + { + in: "columnA,familyB:columnB", + out: bigtable.InterleaveFilters( + bigtable.ColumnFilter("columnA"), + bigtable.ChainFilters(bigtable.FamilyFilter("familyB"), bigtable.ColumnFilter("columnB")), + ), + }, + { + in: "familyA:columnA,familyB:columnB", + out: bigtable.InterleaveFilters( + bigtable.ChainFilters(bigtable.FamilyFilter("familyA"), bigtable.ColumnFilter("columnA")), + bigtable.ChainFilters(bigtable.FamilyFilter("familyB"), bigtable.ColumnFilter("columnB")), + ), + }, + { + in: "familyA:", + out: bigtable.FamilyFilter("familyA"), + }, + { + in: ":columnA", + out: bigtable.ColumnFilter("columnA"), + }, + { + in: ",:columnA,,familyB:columnB,", + out: bigtable.InterleaveFilters( + bigtable.ColumnFilter("columnA"), + bigtable.ChainFilters(bigtable.FamilyFilter("familyB"), bigtable.ColumnFilter("columnB")), + ), + }, + { + in: "familyA:columnA:cellA", + fail: true, + }, + { + in: "familyA::columnA", + fail: true, + }, + } + + for _, tc := range tests { + got, err := parseColumnsFilter(tc.in) + + if !tc.fail && err != nil { + t.Errorf("parseColumnsFilter(%q) unexpectedly failed: %v", tc.in, err) + continue + } + if tc.fail && err == nil { + t.Errorf("parseColumnsFilter(%q) did not fail", tc.in) + continue + } + if tc.fail { + continue + } + + var cmpOpts cmp.Options + cmpOpts = + append( + cmpOpts, + cmp.AllowUnexported(bigtable.ChainFilters([]bigtable.Filter{}...)), + cmp.AllowUnexported(bigtable.InterleaveFilters([]bigtable.Filter{}...))) + + if !cmp.Equal(got, tc.out, cmpOpts) { + t.Errorf("parseColumnsFilter(%q) = %v, want %v", tc.in, got, tc.out) + } + } +} diff --git a/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbtdoc.go b/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbtdoc.go new file mode 100644 index 0000000000..68d671dda4 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/cmd/cbt/cbtdoc.go @@ -0,0 +1,421 @@ +// 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. + +// DO NOT EDIT. THIS IS AUTOMATICALLY GENERATED. +// Run "go generate" to regenerate. +//go:generate go run cbt.go -o cbtdoc.go doc + +/* +Cbt is a tool for doing basic interactions with Cloud Bigtable. To learn how to +install the cbt tool, see the +[cbt overview](https://cloud.google.com/bigtable/docs/go/cbt-overview). + +Usage: + + cbt [options] command [arguments] + +The commands are: + + count Count rows in a table + createinstance Create an instance with an initial cluster + createcluster Create a cluster in the configured instance + createfamily Create a column family + createtable Create a table + updatecluster Update a cluster in the configured instance + deleteinstance Delete an instance + deletecluster Delete a cluster from the configured instance + deletecolumn Delete all cells in a column + deletefamily Delete a column family + deleterow Delete a row + deletetable Delete a table + doc Print godoc-suitable documentation for cbt + help Print help text + listinstances List instances in a project + listclusters List clusters in an instance + lookup Read from a single row + ls List tables and column families + mddoc Print documentation for cbt in Markdown format + read Read rows + set Set value of a cell + setgcpolicy Set the GC policy for a column family + waitforreplication Block until all the completed writes have been replicated to all the clusters + createtablefromsnapshot Create a table from a snapshot (snapshots alpha) + createsnapshot Create a snapshot from a source table (snapshots alpha) + listsnapshots List snapshots in a cluster (snapshots alpha) + getsnapshot Get snapshot info (snapshots alpha) + deletesnapshot Delete snapshot in a cluster (snapshots alpha) + version Print the current cbt version + createappprofile Creates app profile for an instance + getappprofile Reads app profile for an instance + listappprofile Lists app profile for an instance + updateappprofile Updates app profile for an instance + deleteappprofile Deletes app profile for an instance + +Use "cbt help " for more information about a command. + +The options are: + + -project string + project ID, if unset uses gcloud configured project + -instance string + Cloud Bigtable instance + -creds string + if set, use application credentials in this file + + +Alpha features are not currently available to most Cloud Bigtable customers. The +features might be changed in backward-incompatible ways and are not recommended +for production use. They are not subject to any SLA or deprecation policy. + +For convenience, values of the -project, -instance, -creds, +-admin-endpoint and -data-endpoint flags may be specified in +~/.cbtrc in this format: + project = my-project-123 + instance = my-instance + creds = path-to-account-key.json + admin-endpoint = hostname:port + data-endpoint = hostname:port +All values are optional, and all will be overridden by flags. + + + +Count rows in a table + +Usage: + cbt count
+ + + + +Create an instance with an initial cluster + +Usage: + cbt createinstance + instance-id Permanent, unique id for the instance + display-name Description of the instance + cluster-id Permanent, unique id for the cluster in the instance + zone The zone in which to create the cluster + num-nodes The number of nodes to create + storage-type SSD or HDD + + + + + +Create a cluster in the configured instance + +Usage: + cbt createcluster + cluster-id Permanent, unique id for the cluster in the instance + zone The zone in which to create the cluster + num-nodes The number of nodes to create + storage-type SSD or HDD + + + + + +Create a column family + +Usage: + cbt createfamily
+ + + + +Create a table + +Usage: + cbt createtable
[families=family[:(maxage= | maxversions=)],...] [splits=split,...] + families: Column families and their associated GC policies. See "setgcpolicy". + Example: families=family1:maxage=1w,family2:maxversions=1 + splits: Row key to be used to initially split the table + + + + +Update a cluster in the configured instance + +Usage: + cbt updatecluster [num-nodes=num-nodes] + cluster-id Permanent, unique id for the cluster in the instance + num-nodes The number of nodes to update to + + + + +Delete an instance + +Usage: + cbt deleteinstance + + + + +Delete a cluster from the configured instance + +Usage: + cbt deletecluster + + + + +Delete all cells in a column + +Usage: + cbt deletecolumn
[app-profile=] + app-profile= The app profile id to use for the request + + + + + +Delete a column family + +Usage: + cbt deletefamily
+ + + + +Delete a row + +Usage: + cbt deleterow
[app-profile=] + app-profile= The app profile id to use for the request + + + + + +Delete a table + +Usage: + cbt deletetable
+ + + + +Print godoc-suitable documentation for cbt + +Usage: + cbt doc + + + + +Print help text + +Usage: + cbt help [command] + + + + +List instances in a project + +Usage: + cbt listinstances + + + + +List clusters in an instance + +Usage: + cbt listclusters + + + + +Read from a single row + +Usage: + cbt lookup
[columns=[family]:[qualifier],...] [cells-per-column=] [app-profile=] + columns=[family]:[qualifier],... Read only these columns, comma-separated + cells-per-column= Read only this many cells per column + app-profile= The app profile id to use for the request + + + + + +List tables and column families + +Usage: + cbt ls List tables + cbt ls
List column families in
+ + + + +Print documentation for cbt in Markdown format + +Usage: + cbt mddoc + + + + +Read rows + +Usage: + cbt read
[start=] [end=] [prefix=] [regex=] [columns=[family]:[qualifier],...] [count=] [cells-per-column=] [app-profile=] + start= Start reading at this row + end= Stop reading before this row + prefix= Read rows with this prefix + regex= Read rows with keys matching this regex + columns=[family]:[qualifier],... Read only these columns, comma-separated + count= Read only this many rows + cells-per-column= Read only this many cells per column + app-profile= The app profile id to use for the request + + + + + +Set value of a cell + +Usage: + cbt set
[app-profile=] family:column=val[@ts] ... + app-profile= The app profile id to use for the request + family:column=val[@ts] may be repeated to set multiple cells. + + ts is an optional integer timestamp. + If it cannot be parsed, the `@ts` part will be + interpreted as part of the value. + + + + +Set the GC policy for a column family + +Usage: + cbt setgcpolicy
( maxage= | maxversions= | never) + + maxage= Maximum timestamp age to preserve (e.g. "1h", "4d") + maxversions= Maximum number of versions to preserve + + + + +Block until all the completed writes have been replicated to all the clusters + +Usage: + cbt waitforreplication
+ + + + +Create a table from a snapshot (snapshots alpha) + +Usage: + cbt createtablefromsnapshot
+ table The name of the table to create + cluster The cluster where the snapshot is located + snapshot The snapshot to restore + + + + +Create a snapshot from a source table (snapshots alpha) + +Usage: + cbt createsnapshot
[ttl=] + + [ttl=] Lifespan of the snapshot (e.g. "1h", "4d") + + + + + +List snapshots in a cluster (snapshots alpha) + +Usage: + cbt listsnapshots [] + + + + +Get snapshot info (snapshots alpha) + +Usage: + cbt getsnapshot + + + + +Delete snapshot in a cluster (snapshots alpha) + +Usage: + cbt deletesnapshot + + + + +Print the current cbt version + +Usage: + cbt version + + + + +Creates app profile for an instance + +Usage: + cbt createappprofile + [cluster-id=] [allow-transactional-writes=] + set multi_cluster_routing_use_any or single_cluster_routing as possible values for routing policy + provide cluster-id=clusterID and allow-transactional-writes=true or false in case of single_cluster_routing + + + + +Reads app profile for an instance + +Usage: + cbt getappprofile + + + + +Lists app profile for an instance + +Usage: + cbt listappprofile + + + + +Updates app profile for an instance + +Usage: + cbt updateappprofile [cluster-id=] [allow-transactional-writes=] + set multi_cluster_routing_use_any or single_cluster_routing as possible values for routing policy + provide cluster-id=clusterID and allow-transactional-writes=true or false in case of single_cluster_routing + + + + +Deletes app profile for an instance + +Usage: + cbt deleteappprofile + + + + +*/ +package main diff --git a/vendor/cloud.google.com/go/bigtable/cmd/emulator/cbtemulator.go b/vendor/cloud.google.com/go/bigtable/cmd/emulator/cbtemulator.go new file mode 100644 index 0000000000..2477a64205 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/cmd/emulator/cbtemulator.go @@ -0,0 +1,44 @@ +// 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. + +/* +cbtemulator launches the in-memory Cloud Bigtable server on the given address. +*/ +package main + +import ( + "flag" + "fmt" + "log" + + "cloud.google.com/go/bigtable/bttest" + "google.golang.org/grpc" +) + +var ( + host = flag.String("host", "localhost", "the address to bind to on the local machine") + port = flag.Int("port", 9000, "the port number to bind to on the local machine") +) + +func main() { + grpc.EnableTracing = false + flag.Parse() + srv, err := bttest.NewServer(fmt.Sprintf("%s:%d", *host, *port)) + if err != nil { + log.Fatalf("failed to start emulator: %v", err) + } + + fmt.Printf("Cloud Bigtable emulator running on %s\n", srv.Addr) + select {} +} diff --git a/vendor/cloud.google.com/go/bigtable/cmd/loadtest/loadtest.go b/vendor/cloud.google.com/go/bigtable/cmd/loadtest/loadtest.go new file mode 100644 index 0000000000..8d4dc2c05b --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/cmd/loadtest/loadtest.go @@ -0,0 +1,205 @@ +/* +Copyright 2015 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. +*/ + +/* +Loadtest does some load testing through the Go client library for Cloud Bigtable. +*/ +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "math/rand" + "os" + "os/signal" + "sync" + "sync/atomic" + "time" + + "cloud.google.com/go/bigtable" + "cloud.google.com/go/bigtable/internal/cbtconfig" + "cloud.google.com/go/bigtable/internal/stat" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +var ( + runFor = flag.Duration("run_for", 5*time.Second, + "how long to run the load test for; 0 to run forever until SIGTERM") + scratchTable = flag.String("scratch_table", "loadtest-scratch", "name of table to use; should not already exist") + csvOutput = flag.String("csv_output", "", + "output path for statistics in .csv format. If this file already exists it will be overwritten.") + poolSize = flag.Int("pool_size", 1, "size of the gRPC connection pool to use for the data client") + reqCount = flag.Int("req_count", 100, "number of concurrent requests") + + config *cbtconfig.Config + client *bigtable.Client + adminClient *bigtable.AdminClient +) + +func main() { + var err error + config, err = cbtconfig.Load() + if err != nil { + log.Fatal(err) + } + config.RegisterFlags() + + flag.Parse() + if err := config.CheckFlags(cbtconfig.ProjectAndInstanceRequired); err != nil { + log.Fatal(err) + } + if config.Creds != "" { + os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", config.Creds) + } + if flag.NArg() != 0 { + flag.Usage() + os.Exit(1) + } + + var options []option.ClientOption + if *poolSize > 1 { + options = append(options, + option.WithGRPCConnectionPool(*poolSize), + + // TODO(grpc/grpc-go#1388) using connection pool without WithBlock + // can cause RPCs to fail randomly. We can delete this after the issue is fixed. + option.WithGRPCDialOption(grpc.WithBlock())) + } + + var csvFile *os.File + if *csvOutput != "" { + csvFile, err = os.Create(*csvOutput) + if err != nil { + log.Fatalf("creating csv output file: %v", err) + } + defer csvFile.Close() + log.Printf("Writing statistics to %q ...", *csvOutput) + } + + log.Printf("Dialing connections...") + client, err = bigtable.NewClient(context.Background(), config.Project, config.Instance, options...) + if err != nil { + log.Fatalf("Making bigtable.Client: %v", err) + } + defer client.Close() + adminClient, err = bigtable.NewAdminClient(context.Background(), config.Project, config.Instance) + if err != nil { + log.Fatalf("Making bigtable.AdminClient: %v", err) + } + defer adminClient.Close() + + // Create a scratch table. + log.Printf("Setting up scratch table...") + tblConf := bigtable.TableConf{ + TableID: *scratchTable, + Families: map[string]bigtable.GCPolicy{"f": bigtable.MaxVersionsPolicy(1)}, + } + if err := adminClient.CreateTableFromConf(context.Background(), &tblConf); err != nil { + log.Fatalf("Making scratch table %q: %v", *scratchTable, err) + } + // Upon a successful run, delete the table. Don't bother checking for errors. + defer adminClient.DeleteTable(context.Background(), *scratchTable) + + // Also delete the table on SIGTERM. + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + s := <-c + log.Printf("Caught %v, cleaning scratch table.", s) + _ = adminClient.DeleteTable(context.Background(), *scratchTable) + os.Exit(1) + }() + + log.Printf("Starting load test... (run for %v)", *runFor) + tbl := client.Open(*scratchTable) + sem := make(chan int, *reqCount) // limit the number of requests happening at once + var reads, writes stats + stopTime := time.Now().Add(*runFor) + var wg sync.WaitGroup + for time.Now().Before(stopTime) || *runFor == 0 { + sem <- 1 + wg.Add(1) + go func() { + defer wg.Done() + defer func() { <-sem }() + + ok := true + opStart := time.Now() + var stats *stats + defer func() { + stats.Record(ok, time.Since(opStart)) + }() + + row := fmt.Sprintf("row%d", rand.Intn(100)) // operate on 1 of 100 rows + + switch rand.Intn(10) { + default: + // read + stats = &reads + _, err := tbl.ReadRow(context.Background(), row, bigtable.RowFilter(bigtable.LatestNFilter(1))) + if err != nil { + log.Printf("Error doing read: %v", err) + ok = false + } + case 0, 1, 2, 3, 4: + // write + stats = &writes + mut := bigtable.NewMutation() + mut.Set("f", "col", bigtable.Now(), bytes.Repeat([]byte("0"), 1<<10)) // 1 KB write + if err := tbl.Apply(context.Background(), row, mut); err != nil { + log.Printf("Error doing mutation: %v", err) + ok = false + } + } + }() + } + wg.Wait() + + readsAgg := stat.NewAggregate("reads", reads.ds, reads.tries-reads.ok) + writesAgg := stat.NewAggregate("writes", writes.ds, writes.tries-writes.ok) + log.Printf("Reads (%d ok / %d tries):\n%v", reads.ok, reads.tries, readsAgg) + log.Printf("Writes (%d ok / %d tries):\n%v", writes.ok, writes.tries, writesAgg) + + if csvFile != nil { + stat.WriteCSV([]*stat.Aggregate{readsAgg, writesAgg}, csvFile) + } +} + +var allStats int64 // atomic + +type stats struct { + mu sync.Mutex + tries, ok int + ds []time.Duration +} + +func (s *stats) Record(ok bool, d time.Duration) { + s.mu.Lock() + s.tries++ + if ok { + s.ok++ + } + s.ds = append(s.ds, d) + s.mu.Unlock() + + if n := atomic.AddInt64(&allStats, 1); n%1000 == 0 { + log.Printf("Progress: done %d ops", n) + } +} diff --git a/vendor/cloud.google.com/go/bigtable/cmd/scantest/scantest.go b/vendor/cloud.google.com/go/bigtable/cmd/scantest/scantest.go new file mode 100644 index 0000000000..d087b380ff --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/cmd/scantest/scantest.go @@ -0,0 +1,155 @@ +/* +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. +*/ + +/* +Scantest does scan-related load testing against Cloud Bigtable. The logic here +mimics a similar test written using the Java client. +*/ +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "math/rand" + "os" + "sync" + "sync/atomic" + "text/tabwriter" + "time" + + "cloud.google.com/go/bigtable" + "cloud.google.com/go/bigtable/internal/cbtconfig" + "cloud.google.com/go/bigtable/internal/stat" + "golang.org/x/net/context" +) + +var ( + runFor = flag.Duration("run_for", 5*time.Second, "how long to run the load test for") + numScans = flag.Int("concurrent_scans", 1, "number of concurrent scans") + rowLimit = flag.Int("row_limit", 10000, "max number of records per scan") + + config *cbtconfig.Config + client *bigtable.Client +) + +func main() { + flag.Usage = func() { + fmt.Printf("Usage: scantest [options] \n\n") + flag.PrintDefaults() + } + + var err error + config, err = cbtconfig.Load() + if err != nil { + log.Fatal(err) + } + config.RegisterFlags() + + flag.Parse() + if err := config.CheckFlags(cbtconfig.ProjectAndInstanceRequired); err != nil { + log.Fatal(err) + } + if config.Creds != "" { + os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", config.Creds) + } + if flag.NArg() != 1 { + flag.Usage() + os.Exit(1) + } + + table := flag.Arg(0) + + log.Printf("Dialing connections...") + client, err = bigtable.NewClient(context.Background(), config.Project, config.Instance) + if err != nil { + log.Fatalf("Making bigtable.Client: %v", err) + } + defer client.Close() + + log.Printf("Starting scan test... (run for %v)", *runFor) + tbl := client.Open(table) + sem := make(chan int, *numScans) // limit the number of requests happening at once + var scans stats + + stopTime := time.Now().Add(*runFor) + var wg sync.WaitGroup + for time.Now().Before(stopTime) { + sem <- 1 + wg.Add(1) + go func() { + defer wg.Done() + defer func() { <-sem }() + + ok := true + opStart := time.Now() + defer func() { + scans.Record(ok, time.Since(opStart)) + }() + + // Start at a random row key + key := fmt.Sprintf("user%d", rand.Int63()) + limit := bigtable.LimitRows(int64(*rowLimit)) + noop := func(bigtable.Row) bool { return true } + if err := tbl.ReadRows(context.Background(), bigtable.NewRange(key, ""), noop, limit); err != nil { + log.Printf("Error during scan: %v", err) + ok = false + } + }() + } + wg.Wait() + + agg := stat.NewAggregate("scans", scans.ds, scans.tries-scans.ok) + log.Printf("Scans (%d ok / %d tries):\nscan times:\n%v\nthroughput (rows/second):\n%v", + scans.ok, scans.tries, agg, throughputString(agg)) +} + +func throughputString(agg *stat.Aggregate) string { + var buf bytes.Buffer + tw := tabwriter.NewWriter(&buf, 0, 0, 1, ' ', 0) // one-space padding + rowLimitF := float64(*rowLimit) + fmt.Fprintf( + tw, + "min:\t%.2f\nmedian:\t%.2f\nmax:\t%.2f\n", + rowLimitF/agg.Max.Seconds(), + rowLimitF/agg.Median.Seconds(), + rowLimitF/agg.Min.Seconds()) + tw.Flush() + return buf.String() +} + +var allStats int64 // atomic + +type stats struct { + mu sync.Mutex + tries, ok int + ds []time.Duration +} + +func (s *stats) Record(ok bool, d time.Duration) { + s.mu.Lock() + s.tries++ + if ok { + s.ok++ + } + s.ds = append(s.ds, d) + s.mu.Unlock() + + if n := atomic.AddInt64(&allStats, 1); n%1000 == 0 { + log.Printf("Progress: done %d ops", n) + } +} diff --git a/vendor/cloud.google.com/go/bigtable/doc.go b/vendor/cloud.google.com/go/bigtable/doc.go new file mode 100644 index 0000000000..1b75ff7ce9 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/doc.go @@ -0,0 +1,123 @@ +/* +Copyright 2015 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 is an API to Google Cloud Bigtable. + +See https://cloud.google.com/bigtable/docs/ for general product documentation. + +See https://godoc.org/cloud.google.com/go for authentication, timeouts, +connection pooling and similar aspects of this package. + + +Setup and Credentials + +Use NewClient or NewAdminClient to create a client that can be used to access +the data or admin APIs respectively. Both require credentials that have permission +to access the Cloud Bigtable API. + +If your program is run on Google App Engine or Google Compute Engine, using the Application Default Credentials +(https://developers.google.com/accounts/docs/application-default-credentials) +is the simplest option. Those credentials will be used by default when NewClient or NewAdminClient are called. + +To use alternate credentials, pass them to NewClient or NewAdminClient using option.WithTokenSource. +For instance, you can use service account credentials by visiting +https://cloud.google.com/console/project/MYPROJECT/apiui/credential, +creating a new OAuth "Client ID", storing the JSON key somewhere accessible, and writing + jsonKey, err := ioutil.ReadFile(pathToKeyFile) + ... + config, err := google.JWTConfigFromJSON(jsonKey, bigtable.Scope) // or bigtable.AdminScope, etc. + ... + client, err := bigtable.NewClient(ctx, project, instance, option.WithTokenSource(config.TokenSource(ctx))) + ... +Here, `google` means the golang.org/x/oauth2/google package +and `option` means the google.golang.org/api/option package. + +Reading + +The principal way to read from a Bigtable is to use the ReadRows method on *Table. +A RowRange specifies a contiguous portion of a table. A Filter may be provided through +RowFilter to limit or transform the data that is returned. + tbl := client.Open("mytable") + ... + // Read all the rows starting with "com.google.", + // but only fetch the columns in the "links" family. + rr := bigtable.PrefixRange("com.google.") + err := tbl.ReadRows(ctx, rr, func(r Row) bool { + // do something with r + return true // keep going + }, bigtable.RowFilter(bigtable.FamilyFilter("links"))) + ... + +To read a single row, use the ReadRow helper method. + r, err := tbl.ReadRow(ctx, "com.google.cloud") // "com.google.cloud" is the entire row key + ... + +Writing + +This API exposes two distinct forms of writing to a Bigtable: a Mutation and a ReadModifyWrite. +The former expresses idempotent operations. +The latter expresses non-idempotent operations and returns the new values of updated cells. +These operations are performed by creating a Mutation or ReadModifyWrite (with NewMutation or NewReadModifyWrite), +building up one or more operations on that, and then using the Apply or ApplyReadModifyWrite +methods on a Table. + +For instance, to set a couple of cells in a table, + tbl := client.Open("mytable") + mut := bigtable.NewMutation() + mut.Set("links", "maps.google.com", bigtable.Now(), []byte("1")) + mut.Set("links", "golang.org", bigtable.Now(), []byte("1")) + err := tbl.Apply(ctx, "com.google.cloud", mut) + ... + +To increment an encoded value in one cell, + tbl := client.Open("mytable") + rmw := bigtable.NewReadModifyWrite() + rmw.Increment("links", "golang.org", 12) // add 12 to the cell in column "links:golang.org" + r, err := tbl.ApplyReadModifyWrite(ctx, "com.google.cloud", rmw) + ... + +Retries + +If a read or write operation encounters a transient error it will be retried until a successful +response, an unretryable error or the context deadline is reached. Non-idempotent writes (where +the timestamp is set to ServerTime) will not be retried. In the case of ReadRows, retried calls +will not re-scan rows that have already been processed. +*/ +package bigtable // import "cloud.google.com/go/bigtable" + +// Scope constants for authentication credentials. +// These should be used when using credential creation functions such as oauth.NewServiceAccountFromFile. +const ( + // Scope is the OAuth scope for Cloud Bigtable data operations. + Scope = "https://www.googleapis.com/auth/bigtable.data" + // ReadonlyScope is the OAuth scope for Cloud Bigtable read-only data operations. + ReadonlyScope = "https://www.googleapis.com/auth/bigtable.readonly" + + // AdminScope is the OAuth scope for Cloud Bigtable table admin operations. + AdminScope = "https://www.googleapis.com/auth/bigtable.admin.table" + + // InstanceAdminScope is the OAuth scope for Cloud Bigtable instance (and cluster) admin operations. + InstanceAdminScope = "https://www.googleapis.com/auth/bigtable.admin.cluster" +) + +// clientUserAgent identifies the version of this package. +// It should be bumped upon significant changes only. +const clientUserAgent = "cbt-go/20180601" + +// resourcePrefixHeader is the name of the metadata header used to indicate +// the resource being operated on. +const resourcePrefixHeader = "google-cloud-resource-prefix" diff --git a/vendor/cloud.google.com/go/bigtable/export_test.go b/vendor/cloud.google.com/go/bigtable/export_test.go new file mode 100644 index 0000000000..d93dd5c4c3 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/export_test.go @@ -0,0 +1,222 @@ +/* +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 ( + "errors" + "flag" + "fmt" + "strings" + "time" + + "cloud.google.com/go/bigtable/bttest" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +var legacyUseProd string +var integrationConfig IntegrationTestConfig + +func init() { + c := &integrationConfig + + flag.BoolVar(&c.UseProd, "it.use-prod", false, "Use remote bigtable instead of local emulator") + flag.StringVar(&c.AdminEndpoint, "it.admin-endpoint", "", "Admin api host and port") + flag.StringVar(&c.DataEndpoint, "it.data-endpoint", "", "Data api host and port") + flag.StringVar(&c.Project, "it.project", "", "Project to use for integration test") + flag.StringVar(&c.Instance, "it.instance", "", "Bigtable instance to use") + flag.StringVar(&c.Cluster, "it.cluster", "", "Bigtable cluster to use") + flag.StringVar(&c.Table, "it.table", "", "Bigtable table to create") + + // Backwards compat + flag.StringVar(&legacyUseProd, "use_prod", "", `DEPRECATED: if set to "proj,instance,table", run integration test against production`) + +} + +// IntegrationTestConfig contains parameters to pick and setup a IntegrationEnv for testing +type IntegrationTestConfig struct { + UseProd bool + AdminEndpoint string + DataEndpoint string + Project string + Instance string + Cluster string + Table string +} + +// IntegrationEnv represents a testing environment. +// The environment can be implemented using production or an emulator +type IntegrationEnv interface { + Config() IntegrationTestConfig + NewAdminClient() (*AdminClient, error) + // NewInstanceAdminClient will return nil if instance administration is unsupported in this environment + NewInstanceAdminClient() (*InstanceAdminClient, error) + NewClient() (*Client, error) + Close() +} + +// NewIntegrationEnv creates a new environment based on the command line args +func NewIntegrationEnv() (IntegrationEnv, error) { + c := integrationConfig + + if legacyUseProd != "" { + fmt.Println("WARNING: using legacy commandline arg -use_prod, please switch to -it.*") + parts := strings.SplitN(legacyUseProd, ",", 3) + c.UseProd = true + c.Project = parts[0] + c.Instance = parts[1] + c.Table = parts[2] + } + + if integrationConfig.UseProd { + return NewProdEnv(c) + } else { + return NewEmulatedEnv(c) + } +} + +// EmulatedEnv encapsulates the state of an emulator +type EmulatedEnv struct { + config IntegrationTestConfig + server *bttest.Server +} + +// NewEmulatedEnv builds and starts the emulator based environment +func NewEmulatedEnv(config IntegrationTestConfig) (*EmulatedEnv, error) { + srv, err := bttest.NewServer("localhost:0", grpc.MaxRecvMsgSize(200<<20), grpc.MaxSendMsgSize(100<<20)) + if err != nil { + return nil, err + } + + if config.Project == "" { + config.Project = "project" + } + if config.Instance == "" { + config.Instance = "instance" + } + if config.Table == "" { + config.Table = "mytable" + } + config.AdminEndpoint = srv.Addr + config.DataEndpoint = srv.Addr + + env := &EmulatedEnv{ + config: config, + server: srv, + } + return env, nil +} + +// Close stops & cleans up the emulator +func (e *EmulatedEnv) Close() { + e.server.Close() +} + +// Config gets the config used to build this environment +func (e *EmulatedEnv) Config() IntegrationTestConfig { + return e.config +} + +// NewAdminClient builds a new connected admin client for this environment +func (e *EmulatedEnv) NewAdminClient() (*AdminClient, error) { + timeout := 20 * time.Second + ctx, _ := context.WithTimeout(context.Background(), timeout) + conn, err := grpc.Dial(e.server.Addr, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return nil, err + } + return NewAdminClient(ctx, e.config.Project, e.config.Instance, option.WithGRPCConn(conn)) +} + +// NewInstanceAdminClient returns nil for the emulated environment since the API is not implemented. +func (e *EmulatedEnv) NewInstanceAdminClient() (*InstanceAdminClient, error) { + return nil, nil +} + +// NewClient builds a new connected data client for this environment +func (e *EmulatedEnv) NewClient() (*Client, error) { + timeout := 20 * time.Second + ctx, _ := context.WithTimeout(context.Background(), timeout) + conn, err := grpc.Dial(e.server.Addr, grpc.WithInsecure(), grpc.WithBlock(), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(100<<20), grpc.MaxCallRecvMsgSize(100<<20))) + if err != nil { + return nil, err + } + return NewClient(ctx, e.config.Project, e.config.Instance, option.WithGRPCConn(conn)) +} + +// ProdEnv encapsulates the state necessary to connect to the external Bigtable service +type ProdEnv struct { + config IntegrationTestConfig +} + +// NewProdEnv builds the environment representation +func NewProdEnv(config IntegrationTestConfig) (*ProdEnv, error) { + if config.Project == "" { + return nil, errors.New("Project not set") + } + if config.Instance == "" { + return nil, errors.New("Instance not set") + } + if config.Table == "" { + return nil, errors.New("Table not set") + } + + return &ProdEnv{config}, nil +} + +// Close is a no-op for production environments +func (e *ProdEnv) Close() {} + +// Config gets the config used to build this environment +func (e *ProdEnv) Config() IntegrationTestConfig { + return e.config +} + +// NewAdminClient builds a new connected admin client for this environment +func (e *ProdEnv) NewAdminClient() (*AdminClient, error) { + timeout := 20 * time.Second + ctx, _ := context.WithTimeout(context.Background(), timeout) + var clientOpts []option.ClientOption + if endpoint := e.config.AdminEndpoint; endpoint != "" { + clientOpts = append(clientOpts, option.WithEndpoint(endpoint)) + } + return NewAdminClient(ctx, e.config.Project, e.config.Instance, clientOpts...) +} + +// NewInstanceAdminClient returns a new connected instance admin client for this environment +func (e *ProdEnv) NewInstanceAdminClient() (*InstanceAdminClient, error) { + timeout := 20 * time.Second + ctx, _ := context.WithTimeout(context.Background(), timeout) + var clientOpts []option.ClientOption + if endpoint := e.config.AdminEndpoint; endpoint != "" { + clientOpts = append(clientOpts, option.WithEndpoint(endpoint)) + } + return NewInstanceAdminClient(ctx, e.config.Project, clientOpts...) +} + +// NewClient builds a connected data client for this environment +func (e *ProdEnv) NewClient() (*Client, error) { + timeout := 20 * time.Second + ctx, _ := context.WithTimeout(context.Background(), timeout) + var clientOpts []option.ClientOption + if endpoint := e.config.DataEndpoint; endpoint != "" { + clientOpts = append(clientOpts, option.WithEndpoint(endpoint)) + } + return NewClient(ctx, e.config.Project, e.config.Instance, clientOpts...) +} diff --git a/vendor/cloud.google.com/go/bigtable/filter.go b/vendor/cloud.google.com/go/bigtable/filter.go new file mode 100644 index 0000000000..9ef7137474 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/filter.go @@ -0,0 +1,317 @@ +/* +Copyright 2015 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 ( + "fmt" + "strings" + "time" + + btpb "google.golang.org/genproto/googleapis/bigtable/v2" +) + +// A Filter represents a row filter. +type Filter interface { + String() string + proto() *btpb.RowFilter +} + +// ChainFilters returns a filter that applies a sequence of filters. +func ChainFilters(sub ...Filter) Filter { return chainFilter{sub} } + +type chainFilter struct { + sub []Filter +} + +func (cf chainFilter) String() string { + var ss []string + for _, sf := range cf.sub { + ss = append(ss, sf.String()) + } + return "(" + strings.Join(ss, " | ") + ")" +} + +func (cf chainFilter) proto() *btpb.RowFilter { + chain := &btpb.RowFilter_Chain{} + for _, sf := range cf.sub { + chain.Filters = append(chain.Filters, sf.proto()) + } + return &btpb.RowFilter{ + Filter: &btpb.RowFilter_Chain_{Chain: chain}, + } +} + +// InterleaveFilters returns a filter that applies a set of filters in parallel +// and interleaves the results. +func InterleaveFilters(sub ...Filter) Filter { return interleaveFilter{sub} } + +type interleaveFilter struct { + sub []Filter +} + +func (ilf interleaveFilter) String() string { + var ss []string + for _, sf := range ilf.sub { + ss = append(ss, sf.String()) + } + return "(" + strings.Join(ss, " + ") + ")" +} + +func (ilf interleaveFilter) proto() *btpb.RowFilter { + inter := &btpb.RowFilter_Interleave{} + for _, sf := range ilf.sub { + inter.Filters = append(inter.Filters, sf.proto()) + } + return &btpb.RowFilter{ + Filter: &btpb.RowFilter_Interleave_{Interleave: inter}, + } +} + +// RowKeyFilter returns a filter that matches cells from rows whose +// key matches the provided RE2 pattern. +// See https://github.com/google/re2/wiki/Syntax for the accepted syntax. +func RowKeyFilter(pattern string) Filter { return rowKeyFilter(pattern) } + +type rowKeyFilter string + +func (rkf rowKeyFilter) String() string { return fmt.Sprintf("row(%s)", string(rkf)) } + +func (rkf rowKeyFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_RowKeyRegexFilter{RowKeyRegexFilter: []byte(rkf)}} +} + +// FamilyFilter returns a filter that matches cells whose family name +// matches the provided RE2 pattern. +// See https://github.com/google/re2/wiki/Syntax for the accepted syntax. +func FamilyFilter(pattern string) Filter { return familyFilter(pattern) } + +type familyFilter string + +func (ff familyFilter) String() string { return fmt.Sprintf("col(%s:)", string(ff)) } + +func (ff familyFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{FamilyNameRegexFilter: string(ff)}} +} + +// ColumnFilter returns a filter that matches cells whose column name +// matches the provided RE2 pattern. +// See https://github.com/google/re2/wiki/Syntax for the accepted syntax. +func ColumnFilter(pattern string) Filter { return columnFilter(pattern) } + +type columnFilter string + +func (cf columnFilter) String() string { return fmt.Sprintf("col(.*:%s)", string(cf)) } + +func (cf columnFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{ColumnQualifierRegexFilter: []byte(cf)}} +} + +// ValueFilter returns a filter that matches cells whose value +// matches the provided RE2 pattern. +// See https://github.com/google/re2/wiki/Syntax for the accepted syntax. +func ValueFilter(pattern string) Filter { return valueFilter(pattern) } + +type valueFilter string + +func (vf valueFilter) String() string { return fmt.Sprintf("value_match(%s)", string(vf)) } + +func (vf valueFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{ValueRegexFilter: []byte(vf)}} +} + +// LatestNFilter returns a filter that matches the most recent N cells in each column. +func LatestNFilter(n int) Filter { return latestNFilter(n) } + +type latestNFilter int32 + +func (lnf latestNFilter) String() string { return fmt.Sprintf("col(*,%d)", lnf) } + +func (lnf latestNFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_CellsPerColumnLimitFilter{CellsPerColumnLimitFilter: int32(lnf)}} +} + +// StripValueFilter returns a filter that replaces each value with the empty string. +func StripValueFilter() Filter { return stripValueFilter{} } + +type stripValueFilter struct{} + +func (stripValueFilter) String() string { return "strip_value()" } +func (stripValueFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_StripValueTransformer{StripValueTransformer: true}} +} + +// TimestampRangeFilter returns a filter that matches any cells whose timestamp is within the given time bounds. A zero +// time means no bound. +// The timestamp will be truncated to millisecond granularity. +func TimestampRangeFilter(startTime time.Time, endTime time.Time) Filter { + trf := timestampRangeFilter{} + if !startTime.IsZero() { + trf.startTime = Time(startTime) + } + if !endTime.IsZero() { + trf.endTime = Time(endTime) + } + return trf +} + +// TimestampRangeFilterMicros returns a filter that matches any cells whose timestamp is within the given time bounds, +// specified in units of microseconds since 1 January 1970. A zero value for the end time is interpreted as no bound. +// The timestamp will be truncated to millisecond granularity. +func TimestampRangeFilterMicros(startTime Timestamp, endTime Timestamp) Filter { + return timestampRangeFilter{startTime, endTime} +} + +type timestampRangeFilter struct { + startTime Timestamp + endTime Timestamp +} + +func (trf timestampRangeFilter) String() string { + return fmt.Sprintf("timestamp_range(%v,%v)", trf.startTime, trf.endTime) +} + +func (trf timestampRangeFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{ + Filter: &btpb.RowFilter_TimestampRangeFilter{TimestampRangeFilter: &btpb.TimestampRange{ + StartTimestampMicros: int64(trf.startTime.TruncateToMilliseconds()), + EndTimestampMicros: int64(trf.endTime.TruncateToMilliseconds()), + }, + }} +} + +// ColumnRangeFilter returns a filter that matches a contiguous range of columns within a single +// family, as specified by an inclusive start qualifier and exclusive end qualifier. +func ColumnRangeFilter(family, start, end string) Filter { + return columnRangeFilter{family, start, end} +} + +type columnRangeFilter struct { + family string + start string + end string +} + +func (crf columnRangeFilter) String() string { + return fmt.Sprintf("columnRangeFilter(%s,%s,%s)", crf.family, crf.start, crf.end) +} + +func (crf columnRangeFilter) proto() *btpb.RowFilter { + r := &btpb.ColumnRange{FamilyName: crf.family} + if crf.start != "" { + r.StartQualifier = &btpb.ColumnRange_StartQualifierClosed{StartQualifierClosed: []byte(crf.start)} + } + if crf.end != "" { + r.EndQualifier = &btpb.ColumnRange_EndQualifierOpen{EndQualifierOpen: []byte(crf.end)} + } + return &btpb.RowFilter{Filter: &btpb.RowFilter_ColumnRangeFilter{ColumnRangeFilter: r}} +} + +// ValueRangeFilter returns a filter that matches cells with values that fall within +// the given range, as specified by an inclusive start value and exclusive end value. +func ValueRangeFilter(start, end []byte) Filter { + return valueRangeFilter{start, end} +} + +type valueRangeFilter struct { + start []byte + end []byte +} + +func (vrf valueRangeFilter) String() string { + return fmt.Sprintf("valueRangeFilter(%s,%s)", vrf.start, vrf.end) +} + +func (vrf valueRangeFilter) proto() *btpb.RowFilter { + r := &btpb.ValueRange{} + if vrf.start != nil { + r.StartValue = &btpb.ValueRange_StartValueClosed{StartValueClosed: vrf.start} + } + if vrf.end != nil { + r.EndValue = &btpb.ValueRange_EndValueOpen{EndValueOpen: vrf.end} + } + return &btpb.RowFilter{Filter: &btpb.RowFilter_ValueRangeFilter{ValueRangeFilter: r}} +} + +// ConditionFilter returns a filter that evaluates to one of two possible filters depending +// on whether or not the given predicate filter matches at least one cell. +// If the matched filter is nil then no results will be returned. +// IMPORTANT NOTE: The predicate filter does not execute atomically with the +// true and false filters, which may lead to inconsistent or unexpected +// results. Additionally, condition filters have poor performance, especially +// when filters are set for the false condition. +func ConditionFilter(predicateFilter, trueFilter, falseFilter Filter) Filter { + return conditionFilter{predicateFilter, trueFilter, falseFilter} +} + +type conditionFilter struct { + predicateFilter Filter + trueFilter Filter + falseFilter Filter +} + +func (cf conditionFilter) String() string { + return fmt.Sprintf("conditionFilter(%s,%s,%s)", cf.predicateFilter, cf.trueFilter, cf.falseFilter) +} + +func (cf conditionFilter) proto() *btpb.RowFilter { + var tf *btpb.RowFilter + var ff *btpb.RowFilter + if cf.trueFilter != nil { + tf = cf.trueFilter.proto() + } + if cf.falseFilter != nil { + ff = cf.falseFilter.proto() + } + return &btpb.RowFilter{ + Filter: &btpb.RowFilter_Condition_{Condition: &btpb.RowFilter_Condition{ + PredicateFilter: cf.predicateFilter.proto(), + TrueFilter: tf, + FalseFilter: ff, + }}} +} + +// CellsPerRowOffsetFilter returns a filter that skips the first N cells of each row, matching all subsequent cells. +func CellsPerRowOffsetFilter(n int) Filter { + return cellsPerRowOffsetFilter(n) +} + +type cellsPerRowOffsetFilter int32 + +func (cof cellsPerRowOffsetFilter) String() string { + return fmt.Sprintf("cells_per_row_offset(%d)", cof) +} + +func (cof cellsPerRowOffsetFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_CellsPerRowOffsetFilter{CellsPerRowOffsetFilter: int32(cof)}} +} + +// CellsPerRowLimitFilter returns a filter that matches only the first N cells of each row. +func CellsPerRowLimitFilter(n int) Filter { + return cellsPerRowLimitFilter(n) +} + +type cellsPerRowLimitFilter int32 + +func (clf cellsPerRowLimitFilter) String() string { + return fmt.Sprintf("cells_per_row_limit(%d)", clf) +} + +func (clf cellsPerRowLimitFilter) proto() *btpb.RowFilter { + return &btpb.RowFilter{Filter: &btpb.RowFilter_CellsPerRowLimitFilter{CellsPerRowLimitFilter: int32(clf)}} +} + +// TODO(dsymonds): More filters: sampling diff --git a/vendor/cloud.google.com/go/bigtable/gc.go b/vendor/cloud.google.com/go/bigtable/gc.go new file mode 100644 index 0000000000..dce4c823f2 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/gc.go @@ -0,0 +1,167 @@ +/* +Copyright 2015 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 ( + "fmt" + "strings" + "time" + + durpb "github.com/golang/protobuf/ptypes/duration" + bttdpb "google.golang.org/genproto/googleapis/bigtable/admin/v2" +) + +// A GCPolicy represents a rule that determines which cells are eligible for garbage collection. +type GCPolicy interface { + String() string + proto() *bttdpb.GcRule +} + +// IntersectionPolicy returns a GC policy that only applies when all its sub-policies apply. +func IntersectionPolicy(sub ...GCPolicy) GCPolicy { return intersectionPolicy{sub} } + +type intersectionPolicy struct { + sub []GCPolicy +} + +func (ip intersectionPolicy) String() string { + var ss []string + for _, sp := range ip.sub { + ss = append(ss, sp.String()) + } + return "(" + strings.Join(ss, " && ") + ")" +} + +func (ip intersectionPolicy) proto() *bttdpb.GcRule { + inter := &bttdpb.GcRule_Intersection{} + for _, sp := range ip.sub { + inter.Rules = append(inter.Rules, sp.proto()) + } + return &bttdpb.GcRule{ + Rule: &bttdpb.GcRule_Intersection_{Intersection: inter}, + } +} + +// UnionPolicy returns a GC policy that applies when any of its sub-policies apply. +func UnionPolicy(sub ...GCPolicy) GCPolicy { return unionPolicy{sub} } + +type unionPolicy struct { + sub []GCPolicy +} + +func (up unionPolicy) String() string { + var ss []string + for _, sp := range up.sub { + ss = append(ss, sp.String()) + } + return "(" + strings.Join(ss, " || ") + ")" +} + +func (up unionPolicy) proto() *bttdpb.GcRule { + union := &bttdpb.GcRule_Union{} + for _, sp := range up.sub { + union.Rules = append(union.Rules, sp.proto()) + } + return &bttdpb.GcRule{ + Rule: &bttdpb.GcRule_Union_{Union: union}, + } +} + +// MaxVersionsPolicy returns a GC policy that applies to all versions of a cell +// except for the most recent n. +func MaxVersionsPolicy(n int) GCPolicy { return maxVersionsPolicy(n) } + +type maxVersionsPolicy int + +func (mvp maxVersionsPolicy) String() string { return fmt.Sprintf("versions() > %d", int(mvp)) } + +func (mvp maxVersionsPolicy) proto() *bttdpb.GcRule { + return &bttdpb.GcRule{Rule: &bttdpb.GcRule_MaxNumVersions{MaxNumVersions: int32(mvp)}} +} + +// MaxAgePolicy returns a GC policy that applies to all cells +// older than the given age. +func MaxAgePolicy(d time.Duration) GCPolicy { return maxAgePolicy(d) } + +type maxAgePolicy time.Duration + +var units = []struct { + d time.Duration + suffix string +}{ + {24 * time.Hour, "d"}, + {time.Hour, "h"}, + {time.Minute, "m"}, +} + +func (ma maxAgePolicy) String() string { + d := time.Duration(ma) + for _, u := range units { + if d%u.d == 0 { + return fmt.Sprintf("age() > %d%s", d/u.d, u.suffix) + } + } + return fmt.Sprintf("age() > %d", d/time.Microsecond) +} + +func (ma maxAgePolicy) proto() *bttdpb.GcRule { + // This doesn't handle overflows, etc. + // Fix this if people care about GC policies over 290 years. + ns := time.Duration(ma).Nanoseconds() + return &bttdpb.GcRule{ + Rule: &bttdpb.GcRule_MaxAge{MaxAge: &durpb.Duration{ + Seconds: ns / 1e9, + Nanos: int32(ns % 1e9), + }}, + } +} + +type noGCPolicy struct{} + +func (n noGCPolicy) String() string { return "" } + +func (n noGCPolicy) proto() *bttdpb.GcRule { return &bttdpb.GcRule{Rule: nil} } + +// NoGcPolicy applies to all cells setting maxage and maxversions to nil implies no gc policies +func NoGcPolicy() GCPolicy { return noGCPolicy{} } + +// GCRuleToString converts the given GcRule proto to a user-visible string. +func GCRuleToString(rule *bttdpb.GcRule) string { + if rule == nil { + return "" + } + switch r := rule.Rule.(type) { + case *bttdpb.GcRule_MaxNumVersions: + return MaxVersionsPolicy(int(r.MaxNumVersions)).String() + case *bttdpb.GcRule_MaxAge: + return MaxAgePolicy(time.Duration(r.MaxAge.Seconds) * time.Second).String() + case *bttdpb.GcRule_Intersection_: + return joinRules(r.Intersection.Rules, " && ") + case *bttdpb.GcRule_Union_: + return joinRules(r.Union.Rules, " || ") + default: + return "" + } +} + +func joinRules(rules []*bttdpb.GcRule, sep string) string { + var chunks []string + for _, r := range rules { + chunks = append(chunks, GCRuleToString(r)) + } + return "(" + strings.Join(chunks, sep) + ")" +} diff --git a/vendor/cloud.google.com/go/bigtable/gc_test.go b/vendor/cloud.google.com/go/bigtable/gc_test.go new file mode 100644 index 0000000000..70849ab412 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/gc_test.go @@ -0,0 +1,46 @@ +/* +Copyright 2017 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 ( + "testing" + "time" + + bttdpb "google.golang.org/genproto/googleapis/bigtable/admin/v2" +) + +func TestGcRuleToString(t *testing.T) { + + intersection := IntersectionPolicy(MaxVersionsPolicy(5), MaxVersionsPolicy(10), MaxAgePolicy(16*time.Hour)) + + var tests = []struct { + proto *bttdpb.GcRule + want string + }{ + {MaxAgePolicy(72 * time.Hour).proto(), "age() > 3d"}, + {MaxVersionsPolicy(5).proto(), "versions() > 5"}, + {intersection.proto(), "(versions() > 5 && versions() > 10 && age() > 16h)"}, + {UnionPolicy(intersection, MaxAgePolicy(72*time.Hour)).proto(), + "((versions() > 5 && versions() > 10 && age() > 16h) || age() > 3d)"}, + } + + for _, test := range tests { + got := GCRuleToString(test.proto) + if got != test.want { + t.Errorf("got gc rule string: %v, wanted: %v", got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/bigtable/go18.go b/vendor/cloud.google.com/go/bigtable/go18.go new file mode 100644 index 0000000000..9b1d632fd2 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/go18.go @@ -0,0 +1,68 @@ +// 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. + +// +build go1.8 + +package bigtable + +import ( + "fmt" + + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/trace" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +func openCensusOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithGRPCDialOption(grpc.WithStatsHandler(&ocgrpc.ClientHandler{})), + } +} + +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...) +} diff --git a/vendor/cloud.google.com/go/bigtable/internal/cbtconfig/cbtconfig.go b/vendor/cloud.google.com/go/bigtable/internal/cbtconfig/cbtconfig.go new file mode 100644 index 0000000000..919b28f3b0 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/internal/cbtconfig/cbtconfig.go @@ -0,0 +1,252 @@ +/* +Copyright 2015 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 cbtconfig encapsulates common code for reading configuration from .cbtrc and gcloud. +package cbtconfig + +import ( + "bufio" + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" + + "golang.org/x/oauth2" + "google.golang.org/grpc/credentials" +) + +// Config represents a configuration. +type Config struct { + Project, Instance string // required + Creds string // optional + AdminEndpoint string // optional + DataEndpoint string // optional + CertFile string // optional + UserAgent string // optional + TokenSource oauth2.TokenSource // derived + TLSCreds credentials.TransportCredentials // derived +} + +type RequiredFlags uint + +const NoneRequired RequiredFlags = 0 +const ( + ProjectRequired RequiredFlags = 1 << iota + InstanceRequired +) +const ProjectAndInstanceRequired RequiredFlags = ProjectRequired | InstanceRequired + +// RegisterFlags registers a set of standard flags for this config. +// It should be called before flag.Parse. +func (c *Config) RegisterFlags() { + flag.StringVar(&c.Project, "project", c.Project, "project ID, if unset uses gcloud configured project") + flag.StringVar(&c.Instance, "instance", c.Instance, "Cloud Bigtable instance") + flag.StringVar(&c.Creds, "creds", c.Creds, "if set, use application credentials in this file") + flag.StringVar(&c.AdminEndpoint, "admin-endpoint", c.AdminEndpoint, "Override the admin api endpoint") + flag.StringVar(&c.DataEndpoint, "data-endpoint", c.DataEndpoint, "Override the data api endpoint") + flag.StringVar(&c.CertFile, "cert-file", c.CertFile, "Override the TLS certificates file") + flag.StringVar(&c.UserAgent, "user-agent", c.UserAgent, "Override the user agent string") +} + +// CheckFlags checks that the required config values are set. +func (c *Config) CheckFlags(required RequiredFlags) error { + var missing []string + if c.CertFile != "" { + b, err := ioutil.ReadFile(c.CertFile) + if err != nil { + return fmt.Errorf("Failed to load certificates from %s: %v", c.CertFile, err) + } + + cp := x509.NewCertPool() + if !cp.AppendCertsFromPEM(b) { + return fmt.Errorf("Failed to append certificates from %s", c.CertFile) + } + + c.TLSCreds = credentials.NewTLS(&tls.Config{RootCAs: cp}) + } + if required != NoneRequired { + c.SetFromGcloud() + } + if required&ProjectRequired != 0 && c.Project == "" { + missing = append(missing, "-project") + } + if required&InstanceRequired != 0 && c.Instance == "" { + missing = append(missing, "-instance") + } + if len(missing) > 0 { + return fmt.Errorf("Missing %s", strings.Join(missing, " and ")) + } + return nil +} + +// Filename returns the filename consulted for standard configuration. +func Filename() string { + // TODO(dsymonds): Might need tweaking for Windows. + return filepath.Join(os.Getenv("HOME"), ".cbtrc") +} + +// Load loads a .cbtrc file. +// If the file is not present, an empty config is returned. +func Load() (*Config, error) { + filename := Filename() + data, err := ioutil.ReadFile(filename) + if err != nil { + // silent fail if the file isn't there + if os.IsNotExist(err) { + return &Config{}, nil + } + return nil, fmt.Errorf("Reading %s: %v", filename, err) + } + c := new(Config) + s := bufio.NewScanner(bytes.NewReader(data)) + for s.Scan() { + line := s.Text() + i := strings.Index(line, "=") + if i < 0 { + return nil, fmt.Errorf("Bad line in %s: %q", filename, line) + } + key, val := strings.TrimSpace(line[:i]), strings.TrimSpace(line[i+1:]) + switch key { + default: + return nil, fmt.Errorf("Unknown key in %s: %q", filename, key) + case "project": + c.Project = val + case "instance": + c.Instance = val + case "creds": + c.Creds = val + case "admin-endpoint": + c.AdminEndpoint = val + case "data-endpoint": + c.DataEndpoint = val + case "cert-file": + c.CertFile = val + case "user-agent": + c.UserAgent = val + } + + } + return c, s.Err() +} + +type GcloudCredential struct { + AccessToken string `json:"access_token"` + Expiry time.Time `json:"token_expiry"` +} + +func (cred *GcloudCredential) Token() *oauth2.Token { + return &oauth2.Token{AccessToken: cred.AccessToken, TokenType: "Bearer", Expiry: cred.Expiry} +} + +type GcloudConfig struct { + Configuration struct { + Properties struct { + Core struct { + Project string `json:"project"` + } `json:"core"` + } `json:"properties"` + } `json:"configuration"` + Credential GcloudCredential `json:"credential"` +} + +type GcloudCmdTokenSource struct { + Command string + Args []string +} + +// Token implements the oauth2.TokenSource interface +func (g *GcloudCmdTokenSource) Token() (*oauth2.Token, error) { + gcloudConfig, err := LoadGcloudConfig(g.Command, g.Args) + if err != nil { + return nil, err + } + return gcloudConfig.Credential.Token(), nil +} + +// LoadGcloudConfig retrieves the gcloud configuration values we need use via the +// 'config-helper' command +func LoadGcloudConfig(gcloudCmd string, gcloudCmdArgs []string) (*GcloudConfig, error) { + out, err := exec.Command(gcloudCmd, gcloudCmdArgs...).Output() + if err != nil { + return nil, fmt.Errorf("Could not retrieve gcloud configuration") + } + + var gcloudConfig GcloudConfig + if err := json.Unmarshal(out, &gcloudConfig); err != nil { + return nil, fmt.Errorf("Could not parse gcloud configuration") + } + + return &gcloudConfig, nil +} + +// SetFromGcloud retrieves and sets any missing config values from the gcloud +// configuration if possible possible +func (c *Config) SetFromGcloud() error { + + if c.Creds == "" { + c.Creds = os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") + if c.Creds == "" { + log.Printf("-creds flag unset, will use gcloud credential") + } + } else { + os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", c.Creds) + } + + if c.Project == "" { + log.Printf("-project flag unset, will use gcloud active project") + } + + if c.Creds != "" && c.Project != "" { + return nil + } + + gcloudCmd := "gcloud" + if runtime.GOOS == "windows" { + gcloudCmd = gcloudCmd + ".cmd" + } + + gcloudCmdArgs := []string{"config", "config-helper", + "--format=json(configuration.properties.core.project,credential)"} + + gcloudConfig, err := LoadGcloudConfig(gcloudCmd, gcloudCmdArgs) + if err != nil { + return err + } + + if c.Project == "" && gcloudConfig.Configuration.Properties.Core.Project != "" { + log.Printf("gcloud active project is \"%s\"", + gcloudConfig.Configuration.Properties.Core.Project) + c.Project = gcloudConfig.Configuration.Properties.Core.Project + } + + if c.Creds == "" { + c.TokenSource = oauth2.ReuseTokenSource( + gcloudConfig.Credential.Token(), + &GcloudCmdTokenSource{Command: gcloudCmd, Args: gcloudCmdArgs}) + } + + return nil +} diff --git a/vendor/cloud.google.com/go/bigtable/internal/gax/call_option.go b/vendor/cloud.google.com/go/bigtable/internal/gax/call_option.go new file mode 100644 index 0000000000..e538eb9330 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/internal/gax/call_option.go @@ -0,0 +1,106 @@ +/* +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. +*/ + +// This is ia snapshot from github.com/googleapis/gax-go with minor modifications. +package gax + +import ( + "time" + + "google.golang.org/grpc/codes" +) + +type CallOption interface { + Resolve(*CallSettings) +} + +type callOptions []CallOption + +func (opts callOptions) Resolve(s *CallSettings) *CallSettings { + for _, opt := range opts { + opt.Resolve(s) + } + return s +} + +// Encapsulates the call settings for a particular API call. +type CallSettings struct { + Timeout time.Duration + RetrySettings RetrySettings +} + +// Per-call configurable settings for retrying upon transient failure. +type RetrySettings struct { + RetryCodes map[codes.Code]bool + BackoffSettings BackoffSettings +} + +// Parameters to the exponential backoff algorithm for retrying. +type BackoffSettings struct { + DelayTimeoutSettings MultipliableDuration + RPCTimeoutSettings MultipliableDuration +} + +type MultipliableDuration struct { + Initial time.Duration + Max time.Duration + Multiplier float64 +} + +func (w CallSettings) Resolve(s *CallSettings) { + s.Timeout = w.Timeout + s.RetrySettings = w.RetrySettings + + s.RetrySettings.RetryCodes = make(map[codes.Code]bool, len(w.RetrySettings.RetryCodes)) + for key, value := range w.RetrySettings.RetryCodes { + s.RetrySettings.RetryCodes[key] = value + } +} + +type withRetryCodes []codes.Code + +func (w withRetryCodes) Resolve(s *CallSettings) { + s.RetrySettings.RetryCodes = make(map[codes.Code]bool) + for _, code := range w { + s.RetrySettings.RetryCodes[code] = true + } +} + +// WithRetryCodes sets a list of Google API canonical error codes upon which a +// retry should be attempted. +func WithRetryCodes(retryCodes []codes.Code) CallOption { + return withRetryCodes(retryCodes) +} + +type withDelayTimeoutSettings MultipliableDuration + +func (w withDelayTimeoutSettings) Resolve(s *CallSettings) { + s.RetrySettings.BackoffSettings.DelayTimeoutSettings = MultipliableDuration(w) +} + +// WithDelayTimeoutSettings specifies: +// - The initial delay time, in milliseconds, between the completion of +// the first failed request and the initiation of the first retrying +// request. +// - The multiplier by which to increase the delay time between the +// completion of failed requests, and the initiation of the subsequent +// retrying request. +// - The maximum delay time, in milliseconds, between requests. When this +// value is reached, `RetryDelayMultiplier` will no longer be used to +// increase delay time. +func WithDelayTimeoutSettings(initial time.Duration, max time.Duration, multiplier float64) CallOption { + return withDelayTimeoutSettings(MultipliableDuration{initial, max, multiplier}) +} diff --git a/vendor/cloud.google.com/go/bigtable/internal/gax/invoke.go b/vendor/cloud.google.com/go/bigtable/internal/gax/invoke.go new file mode 100644 index 0000000000..454dcb786d --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/internal/gax/invoke.go @@ -0,0 +1,87 @@ +/* +Copyright 2015 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. +*/ + +// This is ia snapshot from github.com/googleapis/gax-go with minor modifications. +package gax + +import ( + "math/rand" + "time" + + "log" + "os" + + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var Logger *log.Logger = log.New(os.Stderr, "", log.LstdFlags) + +// A user defined call stub. +type APICall func(context.Context) error + +// scaleDuration returns the product of a and mult. +func scaleDuration(a time.Duration, mult float64) time.Duration { + ns := float64(a) * mult + return time.Duration(ns) +} + +// invokeWithRetry calls stub using an exponential backoff retry mechanism +// based on the values provided in callSettings. +func invokeWithRetry(ctx context.Context, stub APICall, callSettings CallSettings) error { + retrySettings := callSettings.RetrySettings + backoffSettings := callSettings.RetrySettings.BackoffSettings + delay := backoffSettings.DelayTimeoutSettings.Initial + for { + // If the deadline is exceeded... + if ctx.Err() != nil { + return ctx.Err() + } + err := stub(ctx) + code := grpc.Code(err) + if code == codes.OK { + return nil + } + + if !retrySettings.RetryCodes[code] { + return err + } + + // Sleep a random amount up to the current delay + d := time.Duration(rand.Int63n(int64(delay))) + delayCtx, _ := context.WithTimeout(ctx, delay) + if Logger != nil { + Logger.Printf("Retryable error: %v, retrying in %v", err, d) + } + <-delayCtx.Done() + + delay = scaleDuration(delay, backoffSettings.DelayTimeoutSettings.Multiplier) + if delay > backoffSettings.DelayTimeoutSettings.Max { + delay = backoffSettings.DelayTimeoutSettings.Max + } + } +} + +// Invoke calls stub with a child of context modified by the specified options. +func Invoke(ctx context.Context, stub APICall, opts ...CallOption) error { + settings := &CallSettings{} + callOptions(opts).Resolve(settings) + if len(settings.RetrySettings.RetryCodes) > 0 { + return invokeWithRetry(ctx, stub, *settings) + } + return stub(ctx) +} diff --git a/vendor/cloud.google.com/go/bigtable/internal/gax/invoke_test.go b/vendor/cloud.google.com/go/bigtable/internal/gax/invoke_test.go new file mode 100644 index 0000000000..6b006d459a --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/internal/gax/invoke_test.go @@ -0,0 +1,49 @@ +/* +Copyright 2015 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 gax + +import ( + "testing" + "time" + + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestRandomizedDelays(t *testing.T) { + max := 200 * time.Millisecond + settings := []CallOption{ + WithRetryCodes([]codes.Code{codes.Unavailable, codes.DeadlineExceeded}), + WithDelayTimeoutSettings(10*time.Millisecond, max, 1.5), + } + + deadline := time.Now().Add(1 * time.Second) + ctx, _ := context.WithDeadline(context.Background(), deadline) + var invokeTime time.Time + _ = Invoke(ctx, func(childCtx context.Context) error { + // Keep failing, make sure we never slept more than max (plus a fudge factor) + if !invokeTime.IsZero() { + if got, want := time.Since(invokeTime), max; got > (want + 20*time.Millisecond) { + t.Logf("Slept too long. Got: %v, want: %v", got, max) + } + } + invokeTime = time.Now() + // Workaround for `go vet`: https://github.com/grpc/grpc-go/issues/90 + errf := status.Errorf + return errf(codes.Unavailable, "") + }, settings...) +} diff --git a/vendor/cloud.google.com/go/bigtable/internal/option/option.go b/vendor/cloud.google.com/go/bigtable/internal/option/option.go new file mode 100644 index 0000000000..d086986319 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/internal/option/option.go @@ -0,0 +1,48 @@ +/* +Copyright 2015 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 option contains common code for dealing with client options. +package option + +import ( + "fmt" + "os" + + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +// DefaultClientOptions returns the default client options to use for the +// client's gRPC connection. +func DefaultClientOptions(endpoint, scope, userAgent string) ([]option.ClientOption, error) { + var o []option.ClientOption + // Check the environment variables for the bigtable emulator. + // Dial it directly and don't pass any credentials. + if addr := os.Getenv("BIGTABLE_EMULATOR_HOST"); addr != "" { + conn, err := grpc.Dial(addr, grpc.WithInsecure()) + if err != nil { + return nil, fmt.Errorf("emulator grpc.Dial: %v", err) + } + o = []option.ClientOption{option.WithGRPCConn(conn)} + } else { + o = []option.ClientOption{ + option.WithEndpoint(endpoint), + option.WithScopes(scope), + option.WithUserAgent(userAgent), + } + } + return o, nil +} diff --git a/vendor/cloud.google.com/go/bigtable/internal/stat/stats.go b/vendor/cloud.google.com/go/bigtable/internal/stat/stats.go new file mode 100644 index 0000000000..c90355eca7 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/internal/stat/stats.go @@ -0,0 +1,149 @@ +// 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 stat + +import ( + "bytes" + "encoding/csv" + "fmt" + "io" + "math" + "sort" + "strconv" + "text/tabwriter" + "time" +) + +type byDuration []time.Duration + +func (data byDuration) Len() int { return len(data) } +func (data byDuration) Swap(i, j int) { data[i], data[j] = data[j], data[i] } +func (data byDuration) Less(i, j int) bool { return data[i] < data[j] } + +// quantile returns a value representing the kth of q quantiles. +// May alter the order of data. +func quantile(data []time.Duration, k, q int) (quantile time.Duration, ok bool) { + if len(data) < 1 { + return 0, false + } + if k > q { + return 0, false + } + if k < 0 || q < 1 { + return 0, false + } + + sort.Sort(byDuration(data)) + + if k == 0 { + return data[0], true + } + if k == q { + return data[len(data)-1], true + } + + bucketSize := float64(len(data)-1) / float64(q) + i := float64(k) * bucketSize + + lower := int(math.Trunc(i)) + var upper int + if i > float64(lower) && lower+1 < len(data) { + // If the quantile lies between two elements + upper = lower + 1 + } else { + upper = lower + } + weightUpper := i - float64(lower) + weightLower := 1 - weightUpper + return time.Duration(weightLower*float64(data[lower]) + weightUpper*float64(data[upper])), true +} + +type Aggregate struct { + Name string + Count, Errors int + Min, Median, Max time.Duration + P75, P90, P95, P99 time.Duration // percentiles +} + +// NewAggregate constructs an aggregate from latencies. Returns nil if latencies does not contain aggregateable data. +func NewAggregate(name string, latencies []time.Duration, errorCount int) *Aggregate { + agg := Aggregate{Name: name, Count: len(latencies), Errors: errorCount} + + if len(latencies) == 0 { + return nil + } + var ok bool + if agg.Min, ok = quantile(latencies, 0, 2); !ok { + return nil + } + if agg.Median, ok = quantile(latencies, 1, 2); !ok { + return nil + } + if agg.Max, ok = quantile(latencies, 2, 2); !ok { + return nil + } + if agg.P75, ok = quantile(latencies, 75, 100); !ok { + return nil + } + if agg.P90, ok = quantile(latencies, 90, 100); !ok { + return nil + } + if agg.P95, ok = quantile(latencies, 95, 100); !ok { + return nil + } + if agg.P99, ok = quantile(latencies, 99, 100); !ok { + return nil + } + return &agg +} + +func (agg *Aggregate) String() string { + if agg == nil { + return "no data" + } + var buf bytes.Buffer + tw := tabwriter.NewWriter(&buf, 0, 0, 1, ' ', 0) // one-space padding + fmt.Fprintf(tw, "min:\t%v\nmedian:\t%v\nmax:\t%v\n95th percentile:\t%v\n99th percentile:\t%v\n", + agg.Min, agg.Median, agg.Max, agg.P95, agg.P99) + tw.Flush() + return buf.String() +} + +// WriteCSV writes a csv file to the given Writer, +// with a header row and one row per aggregate. +func WriteCSV(aggs []*Aggregate, iow io.Writer) (err error) { + w := csv.NewWriter(iow) + defer func() { + w.Flush() + if err == nil { + err = w.Error() + } + }() + err = w.Write([]string{"name", "count", "errors", "min", "median", "max", "p75", "p90", "p95", "p99"}) + if err != nil { + return err + } + for _, agg := range aggs { + err = w.Write([]string{ + agg.Name, strconv.Itoa(agg.Count), strconv.Itoa(agg.Errors), + agg.Min.String(), agg.Median.String(), agg.Max.String(), + agg.P75.String(), agg.P90.String(), agg.P95.String(), agg.P99.String(), + }) + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/cloud.google.com/go/bigtable/not_go18.go b/vendor/cloud.google.com/go/bigtable/not_go18.go new file mode 100644 index 0000000000..b5b58b0400 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/not_go18.go @@ -0,0 +1,36 @@ +// Copyright 2017 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 !go1.8 + +package bigtable + +import ( + "golang.org/x/net/context" + "google.golang.org/api/option" +) + +// OpenCensus only supports go 1.8 and higher. + +func openCensusOptions() []option.ClientOption { return nil } + +func traceStartSpan(ctx context.Context, _ string) context.Context { + return ctx +} + +func traceEndSpan(context.Context, error) { +} + +func tracePrintf(context.Context, map[string]interface{}, string, ...interface{}) { +} diff --git a/vendor/cloud.google.com/go/bigtable/reader.go b/vendor/cloud.google.com/go/bigtable/reader.go new file mode 100644 index 0000000000..1170a732d5 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/reader.go @@ -0,0 +1,250 @@ +/* +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 ( + "bytes" + "fmt" + + btpb "google.golang.org/genproto/googleapis/bigtable/v2" +) + +// A Row is returned by ReadRows. The map is keyed by column family (the prefix +// of the column name before the colon). The values are the returned ReadItems +// for that column family in the order returned by Read. +type Row map[string][]ReadItem + +// Key returns the row's key, or "" if the row is empty. +func (r Row) Key() string { + for _, items := range r { + if len(items) > 0 { + return items[0].Row + } + } + return "" +} + +// A ReadItem is returned by Read. A ReadItem contains data from a specific row and column. +type ReadItem struct { + Row, Column string + Timestamp Timestamp + Value []byte +} + +// The current state of the read rows state machine. +type rrState int64 + +const ( + newRow rrState = iota + rowInProgress + cellInProgress +) + +// chunkReader handles cell chunks from the read rows response and combines +// them into full Rows. +type chunkReader struct { + state rrState + curKey []byte + curFam string + curQual []byte + curTS int64 + curVal []byte + curRow Row + lastKey string +} + +// newChunkReader returns a new chunkReader for handling read rows responses. +func newChunkReader() *chunkReader { + return &chunkReader{state: newRow} +} + +// Process takes a cell chunk and returns a new Row if the given chunk +// completes a Row, or nil otherwise. +func (cr *chunkReader) Process(cc *btpb.ReadRowsResponse_CellChunk) (Row, error) { + var row Row + switch cr.state { + case newRow: + if err := cr.validateNewRow(cc); err != nil { + return nil, err + } + + cr.curRow = make(Row) + cr.curKey = cc.RowKey + cr.curFam = cc.FamilyName.Value + cr.curQual = cc.Qualifier.Value + cr.curTS = cc.TimestampMicros + row = cr.handleCellValue(cc) + + case rowInProgress: + if err := cr.validateRowInProgress(cc); err != nil { + return nil, err + } + + if cc.GetResetRow() { + cr.resetToNewRow() + return nil, nil + } + + if cc.FamilyName != nil { + cr.curFam = cc.FamilyName.Value + } + if cc.Qualifier != nil { + cr.curQual = cc.Qualifier.Value + } + cr.curTS = cc.TimestampMicros + row = cr.handleCellValue(cc) + + case cellInProgress: + if err := cr.validateCellInProgress(cc); err != nil { + return nil, err + } + if cc.GetResetRow() { + cr.resetToNewRow() + return nil, nil + } + row = cr.handleCellValue(cc) + } + + return row, nil +} + +// Close must be called after all cell chunks from the response +// have been processed. An error will be returned if the reader is +// in an invalid state, in which case the error should be propagated to the caller. +func (cr *chunkReader) Close() error { + if cr.state != newRow { + return fmt.Errorf("invalid state for end of stream %q", cr.state) + } + return nil +} + +// handleCellValue returns a Row if the cell value includes a commit, otherwise nil. +func (cr *chunkReader) handleCellValue(cc *btpb.ReadRowsResponse_CellChunk) Row { + if cc.ValueSize > 0 { + // ValueSize is specified so expect a split value of ValueSize bytes + if cr.curVal == nil { + cr.curVal = make([]byte, 0, cc.ValueSize) + } + cr.curVal = append(cr.curVal, cc.Value...) + cr.state = cellInProgress + } else { + // This cell is either the complete value or the last chunk of a split + if cr.curVal == nil { + cr.curVal = cc.Value + } else { + cr.curVal = append(cr.curVal, cc.Value...) + } + cr.finishCell() + + if cc.GetCommitRow() { + return cr.commitRow() + } else { + cr.state = rowInProgress + } + } + + return nil +} + +func (cr *chunkReader) finishCell() { + ri := ReadItem{ + Row: string(cr.curKey), + Column: string(cr.curFam) + ":" + string(cr.curQual), + Timestamp: Timestamp(cr.curTS), + Value: cr.curVal, + } + cr.curRow[cr.curFam] = append(cr.curRow[cr.curFam], ri) + cr.curVal = nil +} + +func (cr *chunkReader) commitRow() Row { + row := cr.curRow + cr.lastKey = cr.curRow.Key() + cr.resetToNewRow() + return row +} + +func (cr *chunkReader) resetToNewRow() { + cr.curKey = nil + cr.curFam = "" + cr.curQual = nil + cr.curVal = nil + cr.curRow = nil + cr.curTS = 0 + cr.state = newRow +} + +func (cr *chunkReader) validateNewRow(cc *btpb.ReadRowsResponse_CellChunk) error { + if cc.GetResetRow() { + return fmt.Errorf("reset_row not allowed between rows") + } + if cc.RowKey == nil || cc.FamilyName == nil || cc.Qualifier == nil { + return fmt.Errorf("missing key field for new row %v", cc) + } + if cr.lastKey != "" && cr.lastKey >= string(cc.RowKey) { + return fmt.Errorf("out of order row key: %q, %q", cr.lastKey, string(cc.RowKey)) + } + return nil +} + +func (cr *chunkReader) validateRowInProgress(cc *btpb.ReadRowsResponse_CellChunk) error { + if err := cr.validateRowStatus(cc); err != nil { + return err + } + if cc.RowKey != nil && !bytes.Equal(cc.RowKey, cr.curKey) { + return fmt.Errorf("received new row key %q during existing row %q", cc.RowKey, cr.curKey) + } + if cc.FamilyName != nil && cc.Qualifier == nil { + return fmt.Errorf("family name %q specified without a qualifier", cc.FamilyName) + } + return nil +} + +func (cr *chunkReader) validateCellInProgress(cc *btpb.ReadRowsResponse_CellChunk) error { + if err := cr.validateRowStatus(cc); err != nil { + return err + } + if cr.curVal == nil { + return fmt.Errorf("no cached cell while CELL_IN_PROGRESS %v", cc) + } + if cc.GetResetRow() == false && cr.isAnyKeyPresent(cc) { + return fmt.Errorf("cell key components found while CELL_IN_PROGRESS %v", cc) + } + return nil +} + +func (cr *chunkReader) isAnyKeyPresent(cc *btpb.ReadRowsResponse_CellChunk) bool { + return cc.RowKey != nil || + cc.FamilyName != nil || + cc.Qualifier != nil || + cc.TimestampMicros != 0 +} + +// Validate a RowStatus, commit or reset, if present. +func (cr *chunkReader) validateRowStatus(cc *btpb.ReadRowsResponse_CellChunk) error { + // Resets can't be specified with any other part of a cell + if cc.GetResetRow() && (cr.isAnyKeyPresent(cc) || + cc.Value != nil || + cc.ValueSize != 0 || + cc.Labels != nil) { + return fmt.Errorf("reset must not be specified with other fields %v", cc) + } + if cc.GetCommitRow() && cc.ValueSize > 0 { + return fmt.Errorf("commit row found in between chunks in a cell") + } + return nil +} diff --git a/vendor/cloud.google.com/go/bigtable/reader_test.go b/vendor/cloud.google.com/go/bigtable/reader_test.go new file mode 100644 index 0000000000..b5bab84aa6 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/reader_test.go @@ -0,0 +1,351 @@ +/* +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}} +} diff --git a/vendor/cloud.google.com/go/bigtable/retry_test.go b/vendor/cloud.google.com/go/bigtable/retry_test.go new file mode 100644 index 0000000000..7e3223d907 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/retry_test.go @@ -0,0 +1,381 @@ +/* +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 ( + "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" + "golang.org/x/net/context" + "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) + } +} diff --git a/vendor/cloud.google.com/go/bigtable/testdata/read-rows-acceptance-test.json b/vendor/cloud.google.com/go/bigtable/testdata/read-rows-acceptance-test.json new file mode 100644 index 0000000000..4973831f49 --- /dev/null +++ b/vendor/cloud.google.com/go/bigtable/testdata/read-rows-acceptance-test.json @@ -0,0 +1,1178 @@ +{ + "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 diff --git a/vendor/cloud.google.com/go/civil/civil.go b/vendor/cloud.google.com/go/civil/civil.go new file mode 100644 index 0000000000..29272ef26a --- /dev/null +++ b/vendor/cloud.google.com/go/civil/civil.go @@ -0,0 +1,277 @@ +// 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 +} diff --git a/vendor/cloud.google.com/go/civil/civil_test.go b/vendor/cloud.google.com/go/civil/civil_test.go new file mode 100644 index 0000000000..0720092e21 --- /dev/null +++ b/vendor/cloud.google.com/go/civil/civil_test.go @@ -0,0 +1,442 @@ +// 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) + } + } +} diff --git a/vendor/cloud.google.com/go/cloud.go b/vendor/cloud.google.com/go/cloud.go new file mode 100644 index 0000000000..be9dfdc093 --- /dev/null +++ b/vendor/cloud.google.com/go/cloud.go @@ -0,0 +1,76 @@ +// 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 gPRC 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" diff --git a/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/cloud_tasks_client.go b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/cloud_tasks_client.go new file mode 100644 index 0000000000..3dd8828c4e --- /dev/null +++ b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/cloud_tasks_client.go @@ -0,0 +1,781 @@ +// 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 ( + "fmt" + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "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 /cloud-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 /cloud-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 /cloud-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 /cloud-tasks/pdfs/managing-cloud-tasks-scaling-risks-2017-06-05.pdf). +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 /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 /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. +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 (at google.cloud.tasks.v2beta2.AppEngineHttpTarget), +// the maximum task size is 100KB. +// +// For pull queues (at google.cloud.tasks.v2beta2.PullTarget), this +// 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 +} diff --git a/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/cloud_tasks_client_example_test.go b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/cloud_tasks_client_example_test.go new file mode 100644 index 0000000000..b954225b88 --- /dev/null +++ b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/cloud_tasks_client_example_test.go @@ -0,0 +1,401 @@ +// 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 ( + "cloud.google.com/go/cloudtasks/apiv2beta2" + "golang.org/x/net/context" + "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 +} diff --git a/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/doc.go b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/doc.go new file mode 100644 index 0000000000..aa41e28603 --- /dev/null +++ b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/doc.go @@ -0,0 +1,46 @@ +// 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 ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/mock_test.go b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/mock_test.go new file mode 100644 index 0000000000..9119fa9c5b --- /dev/null +++ b/vendor/cloud.google.com/go/cloudtasks/apiv2beta2/mock_test.go @@ -0,0 +1,1554 @@ +// 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 ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 = ×tamppb.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 = ×tamppb.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 = ×tamppb.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 = ×tamppb.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 = ×tamppb.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 = ×tamppb.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 +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/debuglet.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/debuglet.go new file mode 100644 index 0000000000..0088dd1a8e --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/debuglet.go @@ -0,0 +1,450 @@ +// 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 ( + "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/net/context" + "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...] -- 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 +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go new file mode 100644 index 0000000000..351def954e --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go @@ -0,0 +1,174 @@ +// 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 +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go new file mode 100644 index 0000000000..54ef383929 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go @@ -0,0 +1,168 @@ +// 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 +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client.go new file mode 100644 index 0000000000..84962c5400 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client.go @@ -0,0 +1,291 @@ +// 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 ( + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "log" + "sync" + + "golang.org/x/net/context" + "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 +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client_test.go new file mode 100644 index 0000000000..318c5b176f --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client_test.go @@ -0,0 +1,254 @@ +// 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" + "errors" + "fmt" + "strconv" + "testing" + + "golang.org/x/net/context" + "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) + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/arch/arch.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/arch/arch.go new file mode 100644 index 0000000000..3ec2b0299b --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/arch/arch.go @@ -0,0 +1,186 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 arch contains architecture-specific definitions. +package arch + +import ( + "encoding/binary" + "math" +) + +const MaxBreakpointSize = 4 // TODO + +// Architecture defines the architecture-specific details for a given machine. +type Architecture struct { + // BreakpointSize is the size of a breakpoint instruction, in bytes. + BreakpointSize int + // IntSize is the size of the int type, in bytes. + IntSize int + // PointerSize is the size of a pointer, in bytes. + PointerSize int + // ByteOrder is the byte order for ints and pointers. + ByteOrder binary.ByteOrder + // FloatByteOrder is the byte order for floats. + FloatByteOrder binary.ByteOrder + BreakpointInstr [MaxBreakpointSize]byte +} + +func (a *Architecture) Int(buf []byte) int64 { + return int64(a.Uint(buf)) +} + +func (a *Architecture) Uint(buf []byte) uint64 { + if len(buf) != a.IntSize { + panic("bad IntSize") + } + switch a.IntSize { + case 4: + return uint64(a.ByteOrder.Uint32(buf[:4])) + case 8: + return a.ByteOrder.Uint64(buf[:8]) + } + panic("no IntSize") +} + +func (a *Architecture) Int16(buf []byte) int16 { + return int16(a.Uint16(buf)) +} + +func (a *Architecture) Int32(buf []byte) int32 { + return int32(a.Uint32(buf)) +} + +func (a *Architecture) Int64(buf []byte) int64 { + return int64(a.Uint64(buf)) +} + +func (a *Architecture) Uint16(buf []byte) uint16 { + return a.ByteOrder.Uint16(buf) +} + +func (a *Architecture) Uint32(buf []byte) uint32 { + return a.ByteOrder.Uint32(buf) +} + +func (a *Architecture) Uint64(buf []byte) uint64 { + return a.ByteOrder.Uint64(buf) +} + +func (a *Architecture) IntN(buf []byte) int64 { + if len(buf) == 0 { + return 0 + } + x := int64(0) + if a.ByteOrder == binary.LittleEndian { + i := len(buf) - 1 + x = int64(int8(buf[i])) // sign-extended + for i--; i >= 0; i-- { + x <<= 8 + x |= int64(buf[i]) // not sign-extended + } + } else { + x = int64(int8(buf[0])) // sign-extended + for i := 1; i < len(buf); i++ { + x <<= 8 + x |= int64(buf[i]) // not sign-extended + } + } + return x +} + +func (a *Architecture) UintN(buf []byte) uint64 { + u := uint64(0) + if a.ByteOrder == binary.LittleEndian { + shift := uint(0) + for _, c := range buf { + u |= uint64(c) << shift + shift += 8 + } + } else { + for _, c := range buf { + u <<= 8 + u |= uint64(c) + } + } + return u +} + +func (a *Architecture) Uintptr(buf []byte) uint64 { + if len(buf) != a.PointerSize { + panic("bad PointerSize") + } + switch a.PointerSize { + case 4: + return uint64(a.ByteOrder.Uint32(buf[:4])) + case 8: + return a.ByteOrder.Uint64(buf[:8]) + } + panic("no PointerSize") +} + +func (a *Architecture) Float32(buf []byte) float32 { + if len(buf) != 4 { + panic("bad float32 size") + } + return math.Float32frombits(a.FloatByteOrder.Uint32(buf)) +} + +func (a *Architecture) Float64(buf []byte) float64 { + if len(buf) != 8 { + panic("bad float64 size") + } + return math.Float64frombits(a.FloatByteOrder.Uint64(buf)) +} + +func (a *Architecture) Complex64(buf []byte) complex64 { + if len(buf) != 8 { + panic("bad complex64 size") + } + return complex(a.Float32(buf[0:4]), a.Float32(buf[4:8])) +} + +func (a *Architecture) Complex128(buf []byte) complex128 { + if len(buf) != 16 { + panic("bad complex128 size") + } + return complex(a.Float64(buf[0:8]), a.Float64(buf[8:16])) +} + +var AMD64 = Architecture{ + BreakpointSize: 1, + IntSize: 8, + PointerSize: 8, + ByteOrder: binary.LittleEndian, + FloatByteOrder: binary.LittleEndian, + BreakpointInstr: [MaxBreakpointSize]byte{0xCC}, // INT 3 +} + +var X86 = Architecture{ + BreakpointSize: 1, + IntSize: 4, + PointerSize: 4, + ByteOrder: binary.LittleEndian, + FloatByteOrder: binary.LittleEndian, + BreakpointInstr: [MaxBreakpointSize]byte{0xCC}, // INT 3 +} + +var ARM = Architecture{ + BreakpointSize: 4, // TODO + IntSize: 4, + PointerSize: 4, + ByteOrder: binary.LittleEndian, + FloatByteOrder: binary.LittleEndian, // TODO + BreakpointInstr: [MaxBreakpointSize]byte{0x00, 0x00, 0x00, 0x00}, // TODO +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/cmd/debugproxy/main.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/cmd/debugproxy/main.go new file mode 100644 index 0000000000..c8c2f75f4b --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/cmd/debugproxy/main.go @@ -0,0 +1,85 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// debugproxy connects to the target binary, and serves an RPC interface using +// the types in server/protocol to access and control it. + +// +build linux + +package main + +import ( + "flag" + "fmt" + "log" + "net/rpc" + "os" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server" +) + +var ( + textFlag = flag.String("text", "", "file name of binary being debugged") +) + +func main() { + log.SetFlags(0) + log.SetPrefix("debugproxy: ") + flag.Parse() + if *textFlag == "" { + flag.Usage() + os.Exit(2) + } + s, err := server.New(*textFlag) + if err != nil { + fmt.Printf("server.New: %v\n", err) + os.Exit(2) + } + err = rpc.Register(s) + if err != nil { + fmt.Printf("rpc.Register: %v\n", err) + os.Exit(2) + } + fmt.Println("OK") + log.Print("starting server") + rpc.ServeConn(&rwc{ + os.Stdin, + os.Stdout, + }) + log.Print("server finished") +} + +// rwc creates a single io.ReadWriteCloser from a read side and a write side. +// It allows us to do RPC using standard in and standard out. +type rwc struct { + r *os.File + w *os.File +} + +func (rwc *rwc) Read(p []byte) (int, error) { + return rwc.r.Read(p) +} + +func (rwc *rwc) Write(p []byte) (int, error) { + return rwc.w.Write(p) +} + +func (rwc *rwc) Close() error { + rerr := rwc.r.Close() + werr := rwc.w.Close() + if rerr != nil { + return rerr + } + return werr +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/doc/ptrace-nptl.txt b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/doc/ptrace-nptl.txt new file mode 100644 index 0000000000..81f6a10694 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/doc/ptrace-nptl.txt @@ -0,0 +1,128 @@ +ptrace and NTPL, the missing manpage + +== Signals == + +A signal sent to a ptrace'd process or thread causes only the thread +that receives it to stop and report to the attached process. + +Use tgkill to target a signal (for example, SIGSTOP) at a particular +thread. If you use kill, the signal could be delivered to another +thread in the same process. + +Note that SIGSTOP differs from its usual behavior when a process is +being traced. Usually, a SIGSTOP sent to any thread in a thread group +will stop all threads in the thread group. When a thread is traced, +however, a SIGSTOP affects only the receiving thread (and any other +threads in the thread group that are not traced). + +SIGKILL behaves like it does for non-traced processes. It affects all +threads in the process and terminates them without the WSTOPSIG event +generated by other signals. However, if PTRACE_O_TRACEEXIT is set, +the attached process will still receive PTRACE_EVENT_EXIT events +before receiving WIFSIGNALED events. + +See "Following thread death" for a caveat regarding signal delivery to +zombie threads. + +== Waiting on threads == + +Cloned threads in ptrace'd processes are treated similarly to cloned +threads in your own process. Thus, you must use the __WALL option in +order to receive notifications from threads created by the child +process. Similarly, the __WCLONE option will wait only on +notifications from threads created by the child process and *not* on +notifications from the initial child thread. + +Even when waiting on a specific thread's PID using waitpid or similar, +__WALL or __WCLONE is necessary or waitpid will return ECHILD. + +== Attaching to existing threads == + +libthread_db (which gdb uses), attaches to existing threads by pulling +the pthread data structures out of the traced process. The much +easier way is to traverse the /proc/PID/task directory, though it's +unclear how the semantics of these two approaches differ. + +Unfortunately, if the main thread has exited (but the overall process +has not), it sticks around as a zombie process. This zombie will +appear in the /proc/PID/task directory, but trying to attach to it +will yield EPERM. In this case, the third field of the +/proc/PID/task/PID/stat file will be "Z". Attempting to open the stat +file is also a convenient way to detect races between listing the task +directory and the thread exiting. Coincidentally, gdb will simply +fail to attach to a process whose main thread is a zombie. + +Because new threads may be created while the debugger is in the +process of attaching to existing threads, the debugger must repeatedly +re-list the task directory until it has attached to (and thus stopped) +every thread listed. + +In order to follow new threads created by existing threads, +PTRACE_O_TRACECLONE must be set on each thread attached to. + +== Following new threads == + +With the child process stopped, use PTRACE_SETOPTIONS to set the +PTRACE_O_TRACECLONE option. This option is per-thread, and thus must +be set on each existing thread individually. When an existing thread +with PTRACE_O_TRACECLONE set spawns a new thread, the existing thread +will stop with (SIGTRAP | PTRACE_EVENT_CLONE << 8) and the PID of the +new thread can be retrieved with PTRACE_GETEVENTMSG on the creating +thread. At this time, the new thread will exist, but will initially +be stopped with a SIGSTOP. The new thread will automatically be +traced and will inherit the PTRACE_O_TRACECLONE option from its +parent. The attached process should wait on the new thread to receive +the SIGSTOP notification. + +When using waitpid(-1, ...), don't rely on the parent thread reporting +a SIGTRAP before receiving the SIGSTOP from the new child thread. + +Without PTRACE_O_TRACECLONE, newly cloned threads will not be +ptrace'd. As a result, signals received by new threads will be +handled in the usual way, which may affect the parent and in turn +appear to the attached process, but attributed to the parent (possibly +in unexpected ways). + +== Following thread death == + +If any thread with the PTRACE_O_TRACEEXIT option set exits (either by +returning or pthread_exit'ing), the tracing process will receive an +immediate PTRACE_EVENT_EXIT. At this point, the thread will still +exist. The exit status, encoded as for wait, can be queried using +PTRACE_GETEVENTMSG on the exiting thread's PID. The thread should be +continued so it can actually exit, after which its wait behavior is +the same as for a thread without the PTRACE_O_TRACEEXIT option. + +If a non-main thread exits (either by returning or pthread_exit'ing), +its corresponding process will also exit, producing a WIFEXITED event +(after the process is continued from a possible PTRACE_EVENT_EXIT +event). It is *not* necessary for another thread to ptrace_join for +this to happen. + +If the main thread exits by returning, then all threads will exit, +first generating a PTRACE_EVENT_EXIT event for each thread if +appropriate, then producing a WIFEXITED event for each thread. + +If the main thread exits using pthread_exit, then it enters a +non-waitable zombie state. It will still produce an immediate +PTRACE_O_TRACEEXIT event, but the WIFEXITED event will be delayed +until the entire process exits. This state exists so that shells +don't think the process is done until all of the threads have exited. +Unfortunately, signals cannot be delivered to non-waitable zombies. +Most notably, SIGSTOP cannot be delivered; as a result, when you +broadcast SIGSTOP to all of the threads, you must not wait for +non-waitable zombies to stop. Furthermore, any ptrace command on a +non-waitable zombie, including PTRACE_DETACH, will return ESRCH. + +== Multi-threaded debuggers == + +If the debugger itself is multi-threaded, ptrace calls must come from +the same thread that originally attached to the remote thread. The +kernel simply compares the PID of the caller of ptrace against the +tracer PID of the process passed to ptrace. Because each debugger +thread has a different PID, calling ptrace from a different thread +might as well be calling it from a different process and the kernel +will return ESRCH. + +wait, on the other hand, does not have this restriction. Any debugger +thread can wait on any thread in the attached process. diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/buf.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/buf.go new file mode 100644 index 0000000000..df4dcadec2 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/buf.go @@ -0,0 +1,213 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// Buffered reading and decoding of DWARF data streams. + +package dwarf + +import ( + "encoding/binary" + "fmt" + "strconv" +) + +// Data buffer being decoded. +type buf struct { + dwarf *Data + order binary.ByteOrder + format dataFormat + name string + off Offset + data []byte + err error +} + +// Data format, other than byte order. This affects the handling of +// certain field formats. +type dataFormat interface { + // DWARF version number. Zero means unknown. + version() int + + // 64-bit DWARF format? + dwarf64() (dwarf64 bool, isKnown bool) + + // Size of an address, in bytes. Zero means unknown. + addrsize() int +} + +// Some parts of DWARF have no data format, e.g., abbrevs. +type unknownFormat struct{} + +func (u unknownFormat) version() int { + return 0 +} + +func (u unknownFormat) dwarf64() (bool, bool) { + return false, false +} + +func (u unknownFormat) addrsize() int { + return 0 +} + +func makeBuf(d *Data, format dataFormat, name string, off Offset, data []byte) buf { + return buf{d, d.order, format, name, off, data, nil} +} + +func (b *buf) slice(length int) buf { + n := *b + data := b.data + b.skip(length) // Will validate length. + n.data = data[:length] + return n +} + +func (b *buf) uint8() uint8 { + if len(b.data) < 1 { + b.error("underflow") + return 0 + } + val := b.data[0] + b.data = b.data[1:] + b.off++ + return val +} + +func (b *buf) bytes(n int) []byte { + if len(b.data) < n { + b.error("underflow") + return nil + } + data := b.data[0:n] + b.data = b.data[n:] + b.off += Offset(n) + return data +} + +func (b *buf) skip(n int) { b.bytes(n) } + +// string returns the NUL-terminated (C-like) string at the start of the buffer. +// The terminal NUL is discarded. +func (b *buf) string() string { + for i := 0; i < len(b.data); i++ { + if b.data[i] == 0 { + s := string(b.data[0:i]) + b.data = b.data[i+1:] + b.off += Offset(i + 1) + return s + } + } + b.error("underflow") + return "" +} + +func (b *buf) uint16() uint16 { + a := b.bytes(2) + if a == nil { + return 0 + } + return b.order.Uint16(a) +} + +func (b *buf) uint32() uint32 { + a := b.bytes(4) + if a == nil { + return 0 + } + return b.order.Uint32(a) +} + +func (b *buf) uint64() uint64 { + a := b.bytes(8) + if a == nil { + return 0 + } + return b.order.Uint64(a) +} + +// Read a varint, which is 7 bits per byte, little endian. +// the 0x80 bit means read another byte. +func (b *buf) varint() (c uint64, bits uint) { + for i := 0; i < len(b.data); i++ { + byte := b.data[i] + c |= uint64(byte&0x7F) << bits + bits += 7 + if byte&0x80 == 0 { + b.off += Offset(i + 1) + b.data = b.data[i+1:] + return c, bits + } + } + return 0, 0 +} + +// Unsigned int is just a varint. +func (b *buf) uint() uint64 { + x, _ := b.varint() + return x +} + +// Signed int is a sign-extended varint. +func (b *buf) int() int64 { + ux, bits := b.varint() + x := int64(ux) + if x&(1<<(bits-1)) != 0 { + x |= -1 << bits + } + return x +} + +// Address-sized uint. +func (b *buf) addr() uint64 { + switch b.format.addrsize() { + case 1: + return uint64(b.uint8()) + case 2: + return uint64(b.uint16()) + case 4: + return uint64(b.uint32()) + case 8: + return uint64(b.uint64()) + } + b.error("unknown address size") + return 0 +} + +// assertEmpty checks that everything has been read from b. +func (b *buf) assertEmpty() { + if len(b.data) == 0 { + return + } + if len(b.data) > 5 { + b.error(fmt.Sprintf("unexpected extra data: %x...", b.data[0:5])) + } + b.error(fmt.Sprintf("unexpected extra data: %x", b.data)) +} + +func (b *buf) error(s string) { + if b.err == nil { + b.data = nil + b.err = DecodeError{b.name, b.off, s} + } +} + +type DecodeError struct { + Name string + Offset Offset + Err string +} + +func (e DecodeError) Error() string { + return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/cache.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/cache.go new file mode 100644 index 0000000000..3ff4c193c4 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/cache.go @@ -0,0 +1,259 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf + +import ( + "sort" +) + +// pcToFuncEntries maps PC ranges to function entries. +// +// Each element contains a *Entry for a function and its corresponding start PC. +// If we know the address one past the last instruction of a function, and it is +// not equal to the start address of the next function, we mark that with +// another element containing that address and a nil entry. The elements are +// sorted by PC. Among elements with the same PC, those with non-nil *Entry +// are put earlier. +type pcToFuncEntries []pcToFuncEntry +type pcToFuncEntry struct { + pc uint64 + entry *Entry +} + +func (p pcToFuncEntries) Len() int { return len(p) } +func (p pcToFuncEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p pcToFuncEntries) Less(i, j int) bool { + if p[i].pc != p[j].pc { + return p[i].pc < p[j].pc + } + return p[i].entry != nil && p[j].entry == nil +} + +// nameCache maps each symbol name to a linked list of the entries with that name. +type nameCache map[string]*nameCacheEntry +type nameCacheEntry struct { + entry *Entry + link *nameCacheEntry +} + +// pcToLineEntries maps PCs to line numbers. +// +// It is a slice of (PC, line, file number) triples, sorted by PC. The file +// number is an index into the source files slice. +// If (PC1, line1, file1) and (PC2, line2, file2) are two consecutive elements, +// then the span of addresses [PC1, PC2) belongs to (line1, file1). If an +// element's file number is zero, it only marks the end of a span. +// +// TODO: could save memory by changing pcToLineEntries and lineToPCEntries to use +// interval trees containing references into .debug_line. +type pcToLineEntries []pcToLineEntry +type pcToLineEntry struct { + pc uint64 + line uint64 + file uint64 +} + +func (p pcToLineEntries) Len() int { return len(p) } +func (p pcToLineEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p pcToLineEntries) Less(i, j int) bool { + if p[i].pc != p[j].pc { + return p[i].pc < p[j].pc + } + return p[i].file > p[j].file +} + +// byFileLine is used temporarily while building lineToPCEntries. +type byFileLine []pcToLineEntry + +func (b byFileLine) Len() int { return len(b) } +func (b byFileLine) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byFileLine) Less(i, j int) bool { + if b[i].file != b[j].file { + return b[i].file < b[j].file + } + return b[i].line < b[j].line +} + +// lineToPCEntries maps line numbers to breakpoint addresses. +// +// The slice contains, for each source file in Data, a slice of (line, PC) +// pairs, sorted by line. Note that there may be more than one PC for a line. +type lineToPCEntries [][]lineToPCEntry +type lineToPCEntry struct { + line uint64 + pc uint64 +} + +func (d *Data) buildLineToPCCache(pclfs pcToLineEntries) { + // TODO: only include lines where is_stmt is true + sort.Sort(byFileLine(pclfs)) + // Make a slice of (line, PC) pairs for each (non-zero) file. + var ( + c = make(lineToPCEntries, len(d.sourceFiles)) + curSlice []lineToPCEntry + ) + for i, pclf := range pclfs { + if pclf.file == 0 { + // This entry indicated the end of an instruction sequence, not a breakpoint. + continue + } + curSlice = append(curSlice, lineToPCEntry{line: pclf.line, pc: pclf.pc}) + if i+1 == len(pclfs) || pclf.file != pclfs[i+1].file { + // curSlice now contains all of the entries for pclf.file. + if pclf.file > 0 && pclf.file < uint64(len(c)) { + c[pclf.file] = curSlice + } + curSlice = nil + } + } + d.lineToPCEntries = c +} + +func (d *Data) buildPCToLineCache(cache pcToLineEntries) { + // Sort cache by PC (in increasing order), then by file number (in decreasing order). + sort.Sort(cache) + + // Build a copy without redundant entries. + var out pcToLineEntries + for i, pclf := range cache { + if i > 0 && pclf.pc == cache[i-1].pc { + // This entry is for the same PC as the previous entry. + continue + } + if i > 0 && pclf.file == cache[i-1].file && pclf.line == cache[i-1].line { + // This entry is for the same file and line as the previous entry. + continue + } + out = append(out, pclf) + } + d.pcToLineEntries = out +} + +// buildLineCaches constructs d.sourceFiles, d.lineToPCEntries, d.pcToLineEntries. +func (d *Data) buildLineCaches() { + if len(d.line) == 0 { + return + } + var m lineMachine + // Assume the address_size in the first unit applies to the whole program. + // TODO: we could handle executables containing code for multiple address + // sizes using DW_AT_stmt_list attributes. + if len(d.unit) == 0 { + return + } + buf := makeBuf(d, &d.unit[0], "line", 0, d.line) + if err := m.parseHeader(&buf); err != nil { + return + } + for _, f := range m.header.file { + d.sourceFiles = append(d.sourceFiles, f.name) + } + var cache pcToLineEntries + fn := func(m *lineMachine) bool { + if m.endSequence { + cache = append(cache, pcToLineEntry{ + pc: m.address, + line: 0, + file: 0, + }) + } else { + cache = append(cache, pcToLineEntry{ + pc: m.address, + line: m.line, + file: m.file, + }) + } + return true + } + m.evalCompilationUnit(&buf, fn) + d.buildLineToPCCache(cache) + d.buildPCToLineCache(cache) +} + +// buildInfoCaches initializes nameCache and pcToFuncEntries by walking the +// top-level entries under each compile unit. It swallows any errors in parsing. +func (d *Data) buildInfoCaches() { + // TODO: record errors somewhere? + d.nameCache = make(map[string]*nameCacheEntry) + + var pcToFuncEntries pcToFuncEntries + + r := d.Reader() +loop: + for { + entry, err := r.Next() + if entry == nil || err != nil { + break loop + } + if entry.Tag != TagCompileUnit /* DW_TAG_compile_unit */ { + r.SkipChildren() + continue + } + for { + entry, err := r.Next() + if entry == nil || err != nil { + break loop + } + if entry.Tag == 0 { + // End of children of current compile unit. + break + } + r.SkipChildren() + // Update name-to-entry cache. + if name, ok := entry.Val(AttrName).(string); ok { + d.nameCache[name] = &nameCacheEntry{entry: entry, link: d.nameCache[name]} + } + + // If this entry is a function, update PC-to-containing-function cache. + if entry.Tag != TagSubprogram /* DW_TAG_subprogram */ { + continue + } + + // DW_AT_low_pc, if present, is the address of the first instruction of + // the function. + lowpc, ok := entry.Val(AttrLowpc).(uint64) + if !ok { + continue + } + pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{lowpc, entry}) + + // DW_AT_high_pc, if present (TODO: and of class address) is the address + // one past the last instruction of the function. + highpc, ok := entry.Val(AttrHighpc).(uint64) + if !ok { + continue + } + pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{highpc, nil}) + } + } + // Sort elements by PC. If there are multiple elements with the same PC, + // those with non-nil *Entry are placed earlier. + sort.Sort(pcToFuncEntries) + + // Copy only the first element for each PC to out. + n := 0 + for i, ce := range pcToFuncEntries { + if i == 0 || ce.pc != pcToFuncEntries[i-1].pc { + n++ + } + } + out := make([]pcToFuncEntry, 0, n) + for i, ce := range pcToFuncEntries { + if i == 0 || ce.pc != pcToFuncEntries[i-1].pc { + out = append(out, ce) + } + } + d.pcToFuncEntries = out +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/const.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/const.go new file mode 100644 index 0000000000..adf7882fc9 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/const.go @@ -0,0 +1,480 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// Constants + +package dwarf + +import "strconv" + +// An Attr identifies the attribute type in a DWARF Entry's Field. +type Attr uint32 + +const ( + AttrSibling Attr = 0x01 + AttrLocation Attr = 0x02 + AttrName Attr = 0x03 + AttrOrdering Attr = 0x09 + AttrByteSize Attr = 0x0B + AttrBitOffset Attr = 0x0C + AttrBitSize Attr = 0x0D + AttrStmtList Attr = 0x10 + AttrLowpc Attr = 0x11 + AttrHighpc Attr = 0x12 + AttrLanguage Attr = 0x13 + AttrDiscr Attr = 0x15 + AttrDiscrValue Attr = 0x16 + AttrVisibility Attr = 0x17 + AttrImport Attr = 0x18 + AttrStringLength Attr = 0x19 + AttrCommonRef Attr = 0x1A + AttrCompDir Attr = 0x1B + AttrConstValue Attr = 0x1C + AttrContainingType Attr = 0x1D + AttrDefaultValue Attr = 0x1E + AttrInline Attr = 0x20 + AttrIsOptional Attr = 0x21 + AttrLowerBound Attr = 0x22 + AttrProducer Attr = 0x25 + AttrPrototyped Attr = 0x27 + AttrReturnAddr Attr = 0x2A + AttrStartScope Attr = 0x2C + AttrStrideSize Attr = 0x2E + AttrUpperBound Attr = 0x2F + AttrAbstractOrigin Attr = 0x31 + AttrAccessibility Attr = 0x32 + AttrAddrClass Attr = 0x33 + AttrArtificial Attr = 0x34 + AttrBaseTypes Attr = 0x35 + AttrCalling Attr = 0x36 + AttrCount Attr = 0x37 + AttrDataMemberLoc Attr = 0x38 + AttrDeclColumn Attr = 0x39 + AttrDeclFile Attr = 0x3A + AttrDeclLine Attr = 0x3B + AttrDeclaration Attr = 0x3C + AttrDiscrList Attr = 0x3D + AttrEncoding Attr = 0x3E + AttrExternal Attr = 0x3F + AttrFrameBase Attr = 0x40 + AttrFriend Attr = 0x41 + AttrIdentifierCase Attr = 0x42 + AttrMacroInfo Attr = 0x43 + AttrNamelistItem Attr = 0x44 + AttrPriority Attr = 0x45 + AttrSegment Attr = 0x46 + AttrSpecification Attr = 0x47 + AttrStaticLink Attr = 0x48 + AttrType Attr = 0x49 + AttrUseLocation Attr = 0x4A + AttrVarParam Attr = 0x4B + AttrVirtuality Attr = 0x4C + AttrVtableElemLoc Attr = 0x4D + AttrAllocated Attr = 0x4E + AttrAssociated Attr = 0x4F + AttrDataLocation Attr = 0x50 + AttrStride Attr = 0x51 + AttrEntrypc Attr = 0x52 + AttrUseUTF8 Attr = 0x53 + AttrExtension Attr = 0x54 + AttrRanges Attr = 0x55 + AttrTrampoline Attr = 0x56 + AttrCallColumn Attr = 0x57 + AttrCallFile Attr = 0x58 + AttrCallLine Attr = 0x59 + AttrDescription Attr = 0x5A + + // Go-specific attributes. + AttrGoKind Attr = 0x2900 + AttrGoKey Attr = 0x2901 + AttrGoElem Attr = 0x2902 + AttrGoEmbeddedField Attr = 0x2903 +) + +var attrNames = [...]string{ + AttrSibling: "Sibling", + AttrLocation: "Location", + AttrName: "Name", + AttrOrdering: "Ordering", + AttrByteSize: "ByteSize", + AttrBitOffset: "BitOffset", + AttrBitSize: "BitSize", + AttrStmtList: "StmtList", + AttrLowpc: "Lowpc", + AttrHighpc: "Highpc", + AttrLanguage: "Language", + AttrDiscr: "Discr", + AttrDiscrValue: "DiscrValue", + AttrVisibility: "Visibility", + AttrImport: "Import", + AttrStringLength: "StringLength", + AttrCommonRef: "CommonRef", + AttrCompDir: "CompDir", + AttrConstValue: "ConstValue", + AttrContainingType: "ContainingType", + AttrDefaultValue: "DefaultValue", + AttrInline: "Inline", + AttrIsOptional: "IsOptional", + AttrLowerBound: "LowerBound", + AttrProducer: "Producer", + AttrPrototyped: "Prototyped", + AttrReturnAddr: "ReturnAddr", + AttrStartScope: "StartScope", + AttrStrideSize: "StrideSize", + AttrUpperBound: "UpperBound", + AttrAbstractOrigin: "AbstractOrigin", + AttrAccessibility: "Accessibility", + AttrAddrClass: "AddrClass", + AttrArtificial: "Artificial", + AttrBaseTypes: "BaseTypes", + AttrCalling: "Calling", + AttrCount: "Count", + AttrDataMemberLoc: "DataMemberLoc", + AttrDeclColumn: "DeclColumn", + AttrDeclFile: "DeclFile", + AttrDeclLine: "DeclLine", + AttrDeclaration: "Declaration", + AttrDiscrList: "DiscrList", + AttrEncoding: "Encoding", + AttrExternal: "External", + AttrFrameBase: "FrameBase", + AttrFriend: "Friend", + AttrIdentifierCase: "IdentifierCase", + AttrMacroInfo: "MacroInfo", + AttrNamelistItem: "NamelistItem", + AttrPriority: "Priority", + AttrSegment: "Segment", + AttrSpecification: "Specification", + AttrStaticLink: "StaticLink", + AttrType: "Type", + AttrUseLocation: "UseLocation", + AttrVarParam: "VarParam", + AttrVirtuality: "Virtuality", + AttrVtableElemLoc: "VtableElemLoc", + AttrAllocated: "Allocated", + AttrAssociated: "Associated", + AttrDataLocation: "DataLocation", + AttrStride: "Stride", + AttrEntrypc: "Entrypc", + AttrUseUTF8: "UseUTF8", + AttrExtension: "Extension", + AttrRanges: "Ranges", + AttrTrampoline: "Trampoline", + AttrCallColumn: "CallColumn", + AttrCallFile: "CallFile", + AttrCallLine: "CallLine", + AttrDescription: "Description", +} + +func (a Attr) String() string { + if int(a) < len(attrNames) { + s := attrNames[a] + if s != "" { + return s + } + } + switch a { + case AttrGoKind: + return "GoKind" + case AttrGoKey: + return "GoKey" + case AttrGoElem: + return "GoElem" + case AttrGoEmbeddedField: + return "GoEmbeddedField" + } + return strconv.Itoa(int(a)) +} + +func (a Attr) GoString() string { + if int(a) < len(attrNames) { + s := attrNames[a] + if s != "" { + return "dwarf.Attr" + s + } + } + return "dwarf.Attr(" + strconv.FormatInt(int64(a), 10) + ")" +} + +// A format is a DWARF data encoding format. +type format uint32 + +const ( + // value formats + formAddr format = 0x01 + formDwarfBlock2 format = 0x03 + formDwarfBlock4 format = 0x04 + formData2 format = 0x05 + formData4 format = 0x06 + formData8 format = 0x07 + formString format = 0x08 + formDwarfBlock format = 0x09 + formDwarfBlock1 format = 0x0A + formData1 format = 0x0B + formFlag format = 0x0C + formSdata format = 0x0D + formStrp format = 0x0E + formUdata format = 0x0F + formRefAddr format = 0x10 + formRef1 format = 0x11 + formRef2 format = 0x12 + formRef4 format = 0x13 + formRef8 format = 0x14 + formRefUdata format = 0x15 + formIndirect format = 0x16 + // The following are new in DWARF 4. + formSecOffset format = 0x17 + formExprloc format = 0x18 + formFlagPresent format = 0x19 + formRefSig8 format = 0x20 + // Extensions for multi-file compression (.dwz) + // http://www.dwarfstd.org/ShowIssue.php?issue=120604.1 + formGnuRefAlt format = 0x1f20 + formGnuStrpAlt format = 0x1f21 +) + +// A Tag is the classification (the type) of an Entry. +type Tag uint32 + +const ( + TagArrayType Tag = 0x01 + TagClassType Tag = 0x02 + TagEntryPoint Tag = 0x03 + TagEnumerationType Tag = 0x04 + TagFormalParameter Tag = 0x05 + TagImportedDeclaration Tag = 0x08 + TagLabel Tag = 0x0A + TagLexDwarfBlock Tag = 0x0B + TagMember Tag = 0x0D + TagPointerType Tag = 0x0F + TagReferenceType Tag = 0x10 + TagCompileUnit Tag = 0x11 + TagStringType Tag = 0x12 + TagStructType Tag = 0x13 + TagSubroutineType Tag = 0x15 + TagTypedef Tag = 0x16 + TagUnionType Tag = 0x17 + TagUnspecifiedParameters Tag = 0x18 + TagVariant Tag = 0x19 + TagCommonDwarfBlock Tag = 0x1A + TagCommonInclusion Tag = 0x1B + TagInheritance Tag = 0x1C + TagInlinedSubroutine Tag = 0x1D + TagModule Tag = 0x1E + TagPtrToMemberType Tag = 0x1F + TagSetType Tag = 0x20 + TagSubrangeType Tag = 0x21 + TagWithStmt Tag = 0x22 + TagAccessDeclaration Tag = 0x23 + TagBaseType Tag = 0x24 + TagCatchDwarfBlock Tag = 0x25 + TagConstType Tag = 0x26 + TagConstant Tag = 0x27 + TagEnumerator Tag = 0x28 + TagFileType Tag = 0x29 + TagFriend Tag = 0x2A + TagNamelist Tag = 0x2B + TagNamelistItem Tag = 0x2C + TagPackedType Tag = 0x2D + TagSubprogram Tag = 0x2E + TagTemplateTypeParameter Tag = 0x2F + TagTemplateValueParameter Tag = 0x30 + TagThrownType Tag = 0x31 + TagTryDwarfBlock Tag = 0x32 + TagVariantPart Tag = 0x33 + TagVariable Tag = 0x34 + TagVolatileType Tag = 0x35 + // The following are new in DWARF 3. + TagDwarfProcedure Tag = 0x36 + TagRestrictType Tag = 0x37 + TagInterfaceType Tag = 0x38 + TagNamespace Tag = 0x39 + TagImportedModule Tag = 0x3A + TagUnspecifiedType Tag = 0x3B + TagPartialUnit Tag = 0x3C + TagImportedUnit Tag = 0x3D + TagMutableType Tag = 0x3E // Later removed from DWARF. + TagCondition Tag = 0x3F + TagSharedType Tag = 0x40 + // The following are new in DWARF 4. + TagTypeUnit Tag = 0x41 + TagRvalueReferenceType Tag = 0x42 + TagTemplateAlias Tag = 0x43 +) + +var tagNames = [...]string{ + TagArrayType: "ArrayType", + TagClassType: "ClassType", + TagEntryPoint: "EntryPoint", + TagEnumerationType: "EnumerationType", + TagFormalParameter: "FormalParameter", + TagImportedDeclaration: "ImportedDeclaration", + TagLabel: "Label", + TagLexDwarfBlock: "LexDwarfBlock", + TagMember: "Member", + TagPointerType: "PointerType", + TagReferenceType: "ReferenceType", + TagCompileUnit: "CompileUnit", + TagStringType: "StringType", + TagStructType: "StructType", + TagSubroutineType: "SubroutineType", + TagTypedef: "Typedef", + TagUnionType: "UnionType", + TagUnspecifiedParameters: "UnspecifiedParameters", + TagVariant: "Variant", + TagCommonDwarfBlock: "CommonDwarfBlock", + TagCommonInclusion: "CommonInclusion", + TagInheritance: "Inheritance", + TagInlinedSubroutine: "InlinedSubroutine", + TagModule: "Module", + TagPtrToMemberType: "PtrToMemberType", + TagSetType: "SetType", + TagSubrangeType: "SubrangeType", + TagWithStmt: "WithStmt", + TagAccessDeclaration: "AccessDeclaration", + TagBaseType: "BaseType", + TagCatchDwarfBlock: "CatchDwarfBlock", + TagConstType: "ConstType", + TagConstant: "Constant", + TagEnumerator: "Enumerator", + TagFileType: "FileType", + TagFriend: "Friend", + TagNamelist: "Namelist", + TagNamelistItem: "NamelistItem", + TagPackedType: "PackedType", + TagSubprogram: "Subprogram", + TagTemplateTypeParameter: "TemplateTypeParameter", + TagTemplateValueParameter: "TemplateValueParameter", + TagThrownType: "ThrownType", + TagTryDwarfBlock: "TryDwarfBlock", + TagVariantPart: "VariantPart", + TagVariable: "Variable", + TagVolatileType: "VolatileType", + TagDwarfProcedure: "DwarfProcedure", + TagRestrictType: "RestrictType", + TagInterfaceType: "InterfaceType", + TagNamespace: "Namespace", + TagImportedModule: "ImportedModule", + TagUnspecifiedType: "UnspecifiedType", + TagPartialUnit: "PartialUnit", + TagImportedUnit: "ImportedUnit", + TagMutableType: "MutableType", + TagCondition: "Condition", + TagSharedType: "SharedType", + TagTypeUnit: "TypeUnit", + TagRvalueReferenceType: "RvalueReferenceType", + TagTemplateAlias: "TemplateAlias", +} + +func (t Tag) String() string { + if int(t) < len(tagNames) { + s := tagNames[t] + if s != "" { + return s + } + } + return strconv.Itoa(int(t)) +} + +func (t Tag) GoString() string { + if int(t) < len(tagNames) { + s := tagNames[t] + if s != "" { + return "dwarf.Tag" + s + } + } + return "dwarf.Tag(" + strconv.FormatInt(int64(t), 10) + ")" +} + +// Location expression operators. +// The debug info encodes value locations like 8(R3) +// as a sequence of these op codes. +// This package does not implement full expressions; +// the opPlusUconst operator is expected by the type parser. +const ( + opAddr = 0x03 /* 1 op, const addr */ + opDeref = 0x06 + opConst1u = 0x08 /* 1 op, 1 byte const */ + opConst1s = 0x09 /* " signed */ + opConst2u = 0x0A /* 1 op, 2 byte const */ + opConst2s = 0x0B /* " signed */ + opConst4u = 0x0C /* 1 op, 4 byte const */ + opConst4s = 0x0D /* " signed */ + opConst8u = 0x0E /* 1 op, 8 byte const */ + opConst8s = 0x0F /* " signed */ + opConstu = 0x10 /* 1 op, LEB128 const */ + opConsts = 0x11 /* " signed */ + opDup = 0x12 + opDrop = 0x13 + opOver = 0x14 + opPick = 0x15 /* 1 op, 1 byte stack index */ + opSwap = 0x16 + opRot = 0x17 + opXderef = 0x18 + opAbs = 0x19 + opAnd = 0x1A + opDiv = 0x1B + opMinus = 0x1C + opMod = 0x1D + opMul = 0x1E + opNeg = 0x1F + opNot = 0x20 + opOr = 0x21 + opPlus = 0x22 + opPlusUconst = 0x23 /* 1 op, ULEB128 addend */ + opShl = 0x24 + opShr = 0x25 + opShra = 0x26 + opXor = 0x27 + opSkip = 0x2F /* 1 op, signed 2-byte constant */ + opBra = 0x28 /* 1 op, signed 2-byte constant */ + opEq = 0x29 + opGe = 0x2A + opGt = 0x2B + opLe = 0x2C + opLt = 0x2D + opNe = 0x2E + opLit0 = 0x30 + /* OpLitN = OpLit0 + N for N = 0..31 */ + opReg0 = 0x50 + /* OpRegN = OpReg0 + N for N = 0..31 */ + opBreg0 = 0x70 /* 1 op, signed LEB128 constant */ + /* OpBregN = OpBreg0 + N for N = 0..31 */ + opRegx = 0x90 /* 1 op, ULEB128 register */ + opFbreg = 0x91 /* 1 op, SLEB128 offset */ + opBregx = 0x92 /* 2 op, ULEB128 reg; SLEB128 off */ + opPiece = 0x93 /* 1 op, ULEB128 size of piece */ + opDerefSize = 0x94 /* 1-byte size of data retrieved */ + opXderefSize = 0x95 /* 1-byte size of data retrieved */ + opNop = 0x96 + /* next four new in Dwarf v3 */ + opPushObjAddr = 0x97 + opCall2 = 0x98 /* 2-byte offset of DIE */ + opCall4 = 0x99 /* 4-byte offset of DIE */ + opCallRef = 0x9A /* 4- or 8- byte offset of DIE */ + /* 0xE0-0xFF reserved for user-specific */ +) + +// Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry. +const ( + encAddress = 0x01 + encBoolean = 0x02 + encComplexFloat = 0x03 + encFloat = 0x04 + encSigned = 0x05 + encSignedChar = 0x06 + encUnsigned = 0x07 + encUnsignedChar = 0x08 + encImaginaryFloat = 0x09 +) diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/entry.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/entry.go new file mode 100644 index 0000000000..ecf2a08451 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/entry.go @@ -0,0 +1,417 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// DWARF debug information entry parser. +// An entry is a sequence of data items of a given format. +// The first word in the entry is an index into what DWARF +// calls the ``abbreviation table.'' An abbreviation is really +// just a type descriptor: it's an array of attribute tag/value format pairs. + +package dwarf + +import ( + "errors" + "strconv" +) + +// a single entry's description: a sequence of attributes +type abbrev struct { + tag Tag + children bool + field []afield +} + +type afield struct { + attr Attr + fmt format +} + +// a map from entry format ids to their descriptions +type abbrevTable map[uint32]abbrev + +// ParseAbbrev returns the abbreviation table that starts at byte off +// in the .debug_abbrev section. +func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { + if m, ok := d.abbrevCache[off]; ok { + return m, nil + } + + data := d.abbrev + if off > uint32(len(data)) { + data = nil + } else { + data = data[off:] + } + b := makeBuf(d, unknownFormat{}, "abbrev", 0, data) + + // Error handling is simplified by the buf getters + // returning an endless stream of 0s after an error. + m := make(abbrevTable) + for { + // Table ends with id == 0. + id := uint32(b.uint()) + if id == 0 { + break + } + + // Walk over attributes, counting. + n := 0 + b1 := b // Read from copy of b. + b1.uint() + b1.uint8() + for { + tag := b1.uint() + fmt := b1.uint() + if tag == 0 && fmt == 0 { + break + } + n++ + } + if b1.err != nil { + return nil, b1.err + } + + // Walk over attributes again, this time writing them down. + var a abbrev + a.tag = Tag(b.uint()) + a.children = b.uint8() != 0 + a.field = make([]afield, n) + for i := range a.field { + a.field[i].attr = Attr(b.uint()) + a.field[i].fmt = format(b.uint()) + } + b.uint() + b.uint() + + m[id] = a + } + if b.err != nil { + return nil, b.err + } + d.abbrevCache[off] = m + return m, nil +} + +// An entry is a sequence of attribute/value pairs. +type Entry struct { + Offset Offset // offset of Entry in DWARF info + Tag Tag // tag (kind of Entry) + Children bool // whether Entry is followed by children + Field []Field +} + +// A Field is a single attribute/value pair in an Entry. +type Field struct { + Attr Attr + Val interface{} +} + +// Val returns the value associated with attribute Attr in Entry, +// or nil if there is no such attribute. +// +// A common idiom is to merge the check for nil return with +// the check that the value has the expected dynamic type, as in: +// v, ok := e.Val(AttrSibling).(int64); +// +func (e *Entry) Val(a Attr) interface{} { + for _, f := range e.Field { + if f.Attr == a { + return f.Val + } + } + return nil +} + +// An Offset represents the location of an Entry within the DWARF info. +// (See Reader.Seek.) +type Offset uint32 + +// Entry reads a single entry from buf, decoding +// according to the given abbreviation table. +func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { + off := b.off + id := uint32(b.uint()) + if id == 0 { + return &Entry{} + } + a, ok := atab[id] + if !ok { + b.error("unknown abbreviation table index") + return nil + } + e := &Entry{ + Offset: off, + Tag: a.tag, + Children: a.children, + Field: make([]Field, len(a.field)), + } + for i := range e.Field { + e.Field[i].Attr = a.field[i].attr + fmt := a.field[i].fmt + if fmt == formIndirect { + fmt = format(b.uint()) + } + var val interface{} + switch fmt { + default: + b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16)) + + // address + case formAddr: + val = b.addr() + + // block + case formDwarfBlock1: + val = b.bytes(int(b.uint8())) + case formDwarfBlock2: + val = b.bytes(int(b.uint16())) + case formDwarfBlock4: + val = b.bytes(int(b.uint32())) + case formDwarfBlock: + val = b.bytes(int(b.uint())) + + // constant + case formData1: + val = int64(b.uint8()) + case formData2: + val = int64(b.uint16()) + case formData4: + val = int64(b.uint32()) + case formData8: + val = int64(b.uint64()) + case formSdata: + val = int64(b.int()) + case formUdata: + val = int64(b.uint()) + + // flag + case formFlag: + val = b.uint8() == 1 + // New in DWARF 4. + case formFlagPresent: + // The attribute is implicitly indicated as present, and no value is + // encoded in the debugging information entry itself. + val = true + + // reference to other entry + case formRefAddr: + vers := b.format.version() + if vers == 0 { + b.error("unknown version for DW_FORM_ref_addr") + } else if vers == 2 { + val = Offset(b.addr()) + } else { + is64, known := b.format.dwarf64() + if !known { + b.error("unknown size for DW_FORM_ref_addr") + } else if is64 { + val = Offset(b.uint64()) + } else { + val = Offset(b.uint32()) + } + } + case formRef1: + val = Offset(b.uint8()) + ubase + case formRef2: + val = Offset(b.uint16()) + ubase + case formRef4: + val = Offset(b.uint32()) + ubase + case formRef8: + val = Offset(b.uint64()) + ubase + case formRefUdata: + val = Offset(b.uint()) + ubase + + // string + case formString: + val = b.string() + case formStrp: + off := b.uint32() // offset into .debug_str + if b.err != nil { + return nil + } + b1 := makeBuf(b.dwarf, unknownFormat{}, "str", 0, b.dwarf.str) + b1.skip(int(off)) + val = b1.string() + if b1.err != nil { + b.err = b1.err + return nil + } + + // lineptr, loclistptr, macptr, rangelistptr + // New in DWARF 4, but clang can generate them with -gdwarf-2. + // Section reference, replacing use of formData4 and formData8. + case formSecOffset, formGnuRefAlt, formGnuStrpAlt: + is64, known := b.format.dwarf64() + if !known { + b.error("unknown size for form 0x" + strconv.FormatInt(int64(fmt), 16)) + } else if is64 { + val = int64(b.uint64()) + } else { + val = int64(b.uint32()) + } + + // exprloc + // New in DWARF 4. + case formExprloc: + val = b.bytes(int(b.uint())) + + // reference + // New in DWARF 4. + case formRefSig8: + // 64-bit type signature. + val = b.uint64() + } + e.Field[i].Val = val + } + if b.err != nil { + return nil + } + return e +} + +// A Reader allows reading Entry structures from a DWARF ``info'' section. +// The Entry structures are arranged in a tree. The Reader's Next function +// return successive entries from a pre-order traversal of the tree. +// If an entry has children, its Children field will be true, and the children +// follow, terminated by an Entry with Tag 0. +type Reader struct { + b buf + d *Data + err error + unit int + lastChildren bool // .Children of last entry returned by Next + lastSibling Offset // .Val(AttrSibling) of last entry returned by Next +} + +// Reader returns a new Reader for Data. +// The reader is positioned at byte offset 0 in the DWARF ``info'' section. +func (d *Data) Reader() *Reader { + r := &Reader{d: d} + r.Seek(0) + return r +} + +// AddressSize returns the size in bytes of addresses in the current compilation +// unit. +func (r *Reader) AddressSize() int { + return r.d.unit[r.unit].asize +} + +// Seek positions the Reader at offset off in the encoded entry stream. +// Offset 0 can be used to denote the first entry. +func (r *Reader) Seek(off Offset) { + d := r.d + r.err = nil + r.lastChildren = false + if off == 0 { + if len(d.unit) == 0 { + return + } + u := &d.unit[0] + r.unit = 0 + r.b = makeBuf(r.d, u, "info", u.off, u.data) + return + } + + // TODO(rsc): binary search (maybe a new package) + var i int + var u *unit + for i = range d.unit { + u = &d.unit[i] + if u.off <= off && off < u.off+Offset(len(u.data)) { + r.unit = i + r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:]) + return + } + } + r.err = errors.New("offset out of range") +} + +// maybeNextUnit advances to the next unit if this one is finished. +func (r *Reader) maybeNextUnit() { + for len(r.b.data) == 0 && r.unit+1 < len(r.d.unit) { + r.unit++ + u := &r.d.unit[r.unit] + r.b = makeBuf(r.d, u, "info", u.off, u.data) + } +} + +// Next reads the next entry from the encoded entry stream. +// It returns nil, nil when it reaches the end of the section. +// It returns an error if the current offset is invalid or the data at the +// offset cannot be decoded as a valid Entry. +func (r *Reader) Next() (*Entry, error) { + if r.err != nil { + return nil, r.err + } + r.maybeNextUnit() + if len(r.b.data) == 0 { + return nil, nil + } + u := &r.d.unit[r.unit] + e := r.b.entry(u.atable, u.base) + if r.b.err != nil { + r.err = r.b.err + return nil, r.err + } + if e != nil { + r.lastChildren = e.Children + if r.lastChildren { + r.lastSibling, _ = e.Val(AttrSibling).(Offset) + } + } else { + r.lastChildren = false + } + return e, nil +} + +// SkipChildren skips over the child entries associated with +// the last Entry returned by Next. If that Entry did not have +// children or Next has not been called, SkipChildren is a no-op. +func (r *Reader) SkipChildren() { + if r.err != nil || !r.lastChildren { + return + } + + // If the last entry had a sibling attribute, + // that attribute gives the offset of the next + // sibling, so we can avoid decoding the + // child subtrees. + if r.lastSibling >= r.b.off { + r.Seek(r.lastSibling) + return + } + + for { + e, err := r.Next() + if err != nil || e == nil || e.Tag == 0 { + break + } + if e.Children { + r.SkipChildren() + } + } +} + +// clone returns a copy of the reader. This is used by the typeReader +// interface. +func (r *Reader) clone() typeReader { + return r.d.Reader() +} + +// offset returns the current buffer offset. This is used by the +// typeReader interface. +func (r *Reader) offset() Offset { + return r.b.off +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame.go new file mode 100644 index 0000000000..56cae8d8e9 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame.go @@ -0,0 +1,309 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// Mapping from PC to SP offset (called CFA - Canonical Frame Address - in DWARF). +// This value is the offset from the stack pointer to the virtual frame pointer +// (address of zeroth argument) at each PC value in the program. + +package dwarf + +import "fmt" + +// http://www.dwarfstd.org/doc/DWARF4.pdf Section 6.4 page 126 +// We implement only the CFA column of the table, not the location +// information about other registers. In other words, we implement +// only what we need to understand Go programs compiled by gc. + +// PCToSPOffset returns the offset, at the specified PC, to add to the +// SP to reach the virtual frame pointer, which corresponds to the +// address of the zeroth argument of the function, the word on the +// stack immediately above the return PC. +func (d *Data) PCToSPOffset(pc uint64) (offset int64, err error) { + if len(d.frame) == 0 { + return 0, fmt.Errorf("PCToSPOffset: no frame table") + } + var m frameMachine + // Assume the first info unit is the same as us. Extremely likely. TODO? + if len(d.unit) == 0 { + return 0, fmt.Errorf("PCToSPOffset: no info section") + } + buf := makeBuf(d, &d.unit[0], "frame", 0, d.frame) + for len(buf.data) > 0 { + offset, err := m.evalCompilationUnit(&buf, pc) + if err != nil { + return 0, err + } + return offset, nil + } + return 0, fmt.Errorf("PCToSPOffset: no frame defined for PC %#x", pc) +} + +// Call Frame instructions. Figure 40, page 181. +// Structure is high two bits plus low 6 bits specified by + in comment. +// Some take one or two operands. +const ( + frameNop = 0<<6 + 0x00 + frameAdvanceLoc = 1<<6 + 0x00 // + delta + frameOffset = 2<<6 + 0x00 // + register op: ULEB128 offset + frameRestore = 3<<6 + 0x00 // + register + frameSetLoc = 0<<6 + 0x01 // op: address + frameAdvanceLoc1 = 0<<6 + 0x02 // op: 1-byte delta + frameAdvanceLoc2 = 0<<6 + 0x03 // op: 2-byte delta + frameAdvanceLoc4 = 0<<6 + 0x04 // op: 4-byte delta + frameOffsetExtended = 0<<6 + 0x05 // ops: ULEB128 register ULEB128 offset + frameRestoreExtended = 0<<6 + 0x06 // op: ULEB128 register + frameUndefined = 0<<6 + 0x07 // op: ULEB128 register + frameSameValue = 0<<6 + 0x08 // op: ULEB128 register + frameRegister = 0<<6 + 0x09 // op: ULEB128 register ULEB128 register + frameRememberState = 0<<6 + 0x0a + frameRestoreState = 0<<6 + 0x0b + frameDefCFA = 0<<6 + 0x0c // op: ULEB128 register ULEB128 offset + frameDefCFARegister = 0<<6 + 0x0d // op: ULEB128 register + frameDefCFAOffset = 0<<6 + 0x0e // op: ULEB128 offset + frameDefCFAExpression = 0<<6 + 0x0f // op: BLOCK + frameExpression = 0<<6 + 0x10 // op: ULEB128 register BLOCK + frameOffsetExtendedSf = 0<<6 + 0x11 // op: ULEB128 register SLEB128 offset + frameDefCFASf = 0<<6 + 0x12 // op: ULEB128 register SLEB128 offset + frameDefCFAOffsetSf = 0<<6 + 0x13 // op: SLEB128 offset + frameValOffset = 0<<6 + 0x14 // op: ULEB128 ULEB128 + frameValOffsetSf = 0<<6 + 0x15 // op: ULEB128 SLEB128 + frameValExpression = 0<<6 + 0x16 // op: ULEB128 BLOCK + frameLoUser = 0<<6 + 0x1c + frameHiUser = 0<<6 + 0x3f +) + +// frameMachine represents the PC/SP engine. +// Section 6.4, page 129. +type frameMachine struct { + // Initial values from CIE. + version uint8 // Version number, "independent of DWARF version" + augmentation string // Augmentation; treated as unexpected for now. TODO. + addressSize uint8 // In DWARF v4 and above. Size of a target address. + segmentSize uint8 // In DWARF v4 and above. Size of a segment selector. + codeAlignmentFactor uint64 // Unit of code size in advance instructions. + dataAlignmentFactor int64 // Unit of data size in certain offset instructions. + returnAddressRegister int // Pseudo-register (actually data column) representing return address. + returnRegisterOffset int64 // Offset to saved PC from CFA in bytes. + // CFA definition. + cfaRegister int // Which register represents the SP. + cfaOffset int64 // CFA offset value. + // Running machine. + location uint64 +} + +// evalCompilationUnit scans the frame data for one compilation unit to retrieve +// the offset information for the specified pc. +func (m *frameMachine) evalCompilationUnit(b *buf, pc uint64) (int64, error) { + err := m.parseCIE(b) + if err != nil { + return 0, err + } + for { + offset, found, err := m.scanFDE(b, pc) + if err != nil { + return 0, err + } + if found { + return offset, nil + } + } +} + +// parseCIE assumes the incoming buffer starts with a CIE block and parses it +// to initialize a frameMachine. +func (m *frameMachine) parseCIE(allBuf *buf) error { + length := int(allBuf.uint32()) + if len(allBuf.data) < length { + return fmt.Errorf("CIE parse error: too short") + } + // Create buffer for just this section. + b := allBuf.slice(length) + cie := b.uint32() + if cie != 0xFFFFFFFF { + return fmt.Errorf("CIE parse error: not CIE: %x", cie) + } + m.version = b.uint8() + if m.version != 3 && m.version != 4 { + return fmt.Errorf("CIE parse error: unsupported version %d", m.version) + } + m.augmentation = b.string() + if len(m.augmentation) > 0 { + return fmt.Errorf("CIE: can't handled augmentation string %q", m.augmentation) + } + if m.version >= 4 { + m.addressSize = b.uint8() + m.segmentSize = b.uint8() + } else { + // Unused. Gc generates version 3, so these values will not be + // set, but they are also not used so it's OK. + } + m.codeAlignmentFactor = b.uint() + m.dataAlignmentFactor = b.int() + m.returnAddressRegister = int(b.uint()) + + // Initial instructions. At least for Go, establishes SP register number + // and initial value of CFA offset at start of function. + _, err := m.run(&b, ^uint64(0)) + if err != nil { + return err + } + + // There's padding, but we can ignore it. + return nil +} + +// scanFDE assumes the incoming buffer starts with a FDE block and parses it +// to run a frameMachine and, if the PC is represented in its range, return +// the CFA offset for that PC. The boolean returned reports whether the +// PC is in range for this FDE. +func (m *frameMachine) scanFDE(allBuf *buf, pc uint64) (int64, bool, error) { + length := int(allBuf.uint32()) + if len(allBuf.data) < length { + return 0, false, fmt.Errorf("FDE parse error: too short") + } + if length <= 0 { + if length == 0 { + // EOF. + return 0, false, fmt.Errorf("PC %#x not found in PC/SP table", pc) + } + return 0, false, fmt.Errorf("bad FDE length %d", length) + } + // Create buffer for just this section. + b := allBuf.slice(length) + cieOffset := b.uint32() // TODO assumes 32 bits. + // Expect 0: first CIE in this segment. TODO. + if cieOffset != 0 { + return 0, false, fmt.Errorf("FDE parse error: bad CIE offset: %.2x", cieOffset) + } + // Initial location. + m.location = b.addr() + addressRange := b.addr() + // If the PC is not in this function, there's no point in executing the instructions. + if pc < m.location || m.location+addressRange <= pc { + return 0, false, nil + } + // The PC appears in this FDE. Scan to find the location. + offset, err := m.run(&b, pc) + if err != nil { + return 0, false, err + } + + // There's padding, but we can ignore it. + return offset, true, nil +} + +// run executes the instructions in the buffer, which has been sliced to contain +// only the data for this block. When we run out of data, we return. +// Since we are only called when we know the PC is in this block, reaching +// EOF is not an error, it just means the final CFA definition matches the +// tail of the block that holds the PC. +// The return value is the CFA at the end of the block or the PC, whichever +// comes first. +func (m *frameMachine) run(b *buf, pc uint64) (int64, error) { + // We run the machine at location == PC because if the PC is at the first + // instruction of a block, the definition of its offset arrives as an + // offset-defining operand after the PC is set to that location. + for m.location <= pc && len(b.data) > 0 { + op := b.uint8() + // Ops with embedded operands + switch op & 0xC0 { + case frameAdvanceLoc: // (6.4.2.1) + // delta in low bits + m.location += uint64(op & 0x3F) + continue + case frameOffset: // (6.4.2.3) + // Register in low bits; ULEB128 offset. + // For Go binaries we only see this in the CIE for the return address register. + if int(op&0x3F) != m.returnAddressRegister { + return 0, fmt.Errorf("invalid frameOffset register R%d should be R%d", op&0x3f, m.returnAddressRegister) + } + m.returnRegisterOffset = int64(b.uint()) * m.dataAlignmentFactor + continue + case frameRestore: // (6.4.2.3) + // register in low bits + return 0, fmt.Errorf("unimplemented frameRestore(R%d)\n", op&0x3F) + } + + // The remaining ops do not have embedded operands. + + switch op { + // Row creation instructions (6.4.2.1) + case frameNop: + case frameSetLoc: // op: address + return 0, fmt.Errorf("unimplemented setloc") // what size is operand? + case frameAdvanceLoc1: // op: 1-byte delta + m.location += uint64(b.uint8()) + case frameAdvanceLoc2: // op: 2-byte delta + m.location += uint64(b.uint16()) + case frameAdvanceLoc4: // op: 4-byte delta + m.location += uint64(b.uint32()) + + // CFA definition instructions (6.4.2.2) + case frameDefCFA: // op: ULEB128 register ULEB128 offset + m.cfaRegister = int(b.int()) + m.cfaOffset = int64(b.uint()) + case frameDefCFASf: // op: ULEB128 register SLEB128 offset + return 0, fmt.Errorf("unimplemented frameDefCFASf") + case frameDefCFARegister: // op: ULEB128 register + return 0, fmt.Errorf("unimplemented frameDefCFARegister") + case frameDefCFAOffset: // op: ULEB128 offset + return 0, fmt.Errorf("unimplemented frameDefCFAOffset") + case frameDefCFAOffsetSf: // op: SLEB128 offset + offset := b.int() + m.cfaOffset = offset * m.dataAlignmentFactor + // TODO: Verify we are using a factored offset. + case frameDefCFAExpression: // op: BLOCK + return 0, fmt.Errorf("unimplemented frameDefCFAExpression") + + // Register Rule instructions (6.4.2.3) + case frameOffsetExtended: // ops: ULEB128 register ULEB128 offset + // The same as frameOffset, but with the register specified in an operand. + reg := b.uint() + // For Go binaries we only see this in the CIE for the return address register. + if reg != uint64(m.returnAddressRegister) { + return 0, fmt.Errorf("invalid frameOffsetExtended: register R%d should be R%d", reg, m.returnAddressRegister) + } + m.returnRegisterOffset = int64(b.uint()) * m.dataAlignmentFactor + case frameRestoreExtended: // op: ULEB128 register + return 0, fmt.Errorf("unimplemented frameRestoreExtended") + case frameUndefined: // op: ULEB128 register; unimplemented + return 0, fmt.Errorf("unimplemented frameUndefined") + case frameSameValue: // op: ULEB128 register + return 0, fmt.Errorf("unimplemented frameSameValue") + case frameRegister: // op: ULEB128 register ULEB128 register + return 0, fmt.Errorf("unimplemented frameRegister") + case frameRememberState: + return 0, fmt.Errorf("unimplemented frameRememberState") + case frameRestoreState: + return 0, fmt.Errorf("unimplemented frameRestoreState") + case frameExpression: // op: ULEB128 register BLOCK + return 0, fmt.Errorf("unimplemented frameExpression") + case frameOffsetExtendedSf: // op: ULEB128 register SLEB128 offset + return 0, fmt.Errorf("unimplemented frameOffsetExtended_sf") + case frameValOffset: // op: ULEB128 ULEB128 + return 0, fmt.Errorf("unimplemented frameValOffset") + case frameValOffsetSf: // op: ULEB128 SLEB128 + return 0, fmt.Errorf("unimplemented frameValOffsetSf") + case frameValExpression: // op: ULEB128 BLOCK + return 0, fmt.Errorf("unimplemented frameValExpression") + + default: + if frameLoUser <= op && op <= frameHiUser { + return 0, fmt.Errorf("unknown user-defined frame op %#x", op) + } + return 0, fmt.Errorf("unknown frame op %#x", op) + } + } + return m.cfaOffset, nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame_test.go new file mode 100644 index 0000000000..ffe50d3619 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame_test.go @@ -0,0 +1,140 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf_test + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +var ( + pcspTempDir string + pcsptestBinary string +) + +func doPCToSPTest(self bool) bool { + // For now, only works on amd64 platforms. + if runtime.GOARCH != "amd64" { + return false + } + // Self test reads test binary; only works on Linux or Mac. + if self { + if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { + return false + } + } + // Command below expects "sh", so Unix. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + return false + } + if pcsptestBinary != "" { + return true + } + var err error + pcspTempDir, err = ioutil.TempDir("", "pcsptest") + if err != nil { + panic(err) + } + if strings.Contains(pcspTempDir, " ") { + panic("unexpected space in tempdir") + } + // This command builds pcsptest from testdata/pcsptest.go. + pcsptestBinary = filepath.Join(pcspTempDir, "pcsptest") + command := fmt.Sprintf("go tool compile -o %s.6 testdata/pcsptest.go && go tool link -H %s -o %s %s.6", + pcsptestBinary, runtime.GOOS, pcsptestBinary, pcsptestBinary) + cmd := exec.Command("sh", "-c", command) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + return true +} + +func endPCToSPTest() { + if pcspTempDir != "" { + os.RemoveAll(pcspTempDir) + pcspTempDir = "" + pcsptestBinary = "" + } +} + +func TestPCToSPOffset(t *testing.T) { + t.Skip("gets a stack layout it doesn't expect") + + if !doPCToSPTest(false) { + return + } + defer endPCToSPTest() + + data, err := getData(pcsptestBinary) + if err != nil { + t.Fatal(err) + } + entry, err := data.LookupFunction("main.test") + if err != nil { + t.Fatal("lookup startPC:", err) + } + startPC, ok := entry.Val(dwarf.AttrLowpc).(uint64) + if !ok { + t.Fatal(`DWARF data for function "main.test" has no low PC`) + } + endPC, ok := entry.Val(dwarf.AttrHighpc).(uint64) + if !ok { + t.Fatal(`DWARF data for function "main.test" has no high PC`) + } + + const addrSize = 8 // TODO: Assumes amd64. + const argSize = 8 // Defined by int64 arguments in test binary. + + // On 64-bit machines, the first offset must be one address size, + // for the return PC. + offset, err := data.PCToSPOffset(startPC) + if err != nil { + t.Fatal("startPC:", err) + } + if offset != addrSize { + t.Fatalf("expected %d at start of function; got %d", addrSize, offset) + } + // On 64-bit machines, expect some 8s and some 32s. (See the + // comments in testdata/pcsptest.go. + // TODO: The test could be stronger, but not much unless we + // disassemble the binary. + count := make(map[int64]int) + for pc := startPC; pc < endPC; pc++ { + offset, err := data.PCToSPOffset(pc) + if err != nil { + t.Fatal("scanning function:", err) + } + count[offset]++ + } + if len(count) != 2 { + t.Errorf("expected 2 offset values, got %d; counts are: %v", len(count), count) + } + if count[addrSize] == 0 { + t.Errorf("expected some values at offset %d; got %v", addrSize, count) + } + if count[addrSize+3*argSize] == 0 { + t.Errorf("expected some values at offset %d; got %v", addrSize+3*argSize, count) + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/line.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/line.go new file mode 100644 index 0000000000..b1d9c7a673 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/line.go @@ -0,0 +1,458 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf + +// This file implements the mapping from PC to lines. +// TODO: Find a way to test this properly. + +// http://www.dwarfstd.org/doc/DWARF4.pdf Section 6.2 page 108 + +import ( + "fmt" + "sort" + "strings" +) + +// PCToLine returns the file and line number corresponding to the PC value. +// It returns an error if a correspondence cannot be found. +func (d *Data) PCToLine(pc uint64) (file string, line uint64, err error) { + c := d.pcToLineEntries + if len(c) == 0 { + return "", 0, fmt.Errorf("PCToLine: no line table") + } + i := sort.Search(len(c), func(i int) bool { return c[i].pc > pc }) - 1 + // c[i] is now the entry in pcToLineEntries with the largest pc that is not + // larger than the query pc. + // The search has failed if: + // - All pcs in c were larger than the query pc (i == -1). + // - c[i] marked the end of a sequence of instructions (c[i].file == 0). + // - c[i] is the last element of c, and isn't the end of a sequence of + // instructions, and the search pc is much larger than c[i].pc. In this + // case, we don't know the range of the last instruction, but the search + // pc is probably past it. + if i == -1 || c[i].file == 0 || (i+1 == len(c) && pc-c[i].pc > 1024) { + return "", 0, fmt.Errorf("no source line defined for PC %#x", pc) + } + if c[i].file >= uint64(len(d.sourceFiles)) { + return "", 0, fmt.Errorf("invalid file number in DWARF data") + } + return d.sourceFiles[c[i].file], c[i].line, nil +} + +// LineToBreakpointPCs returns the PCs that should be used as breakpoints +// corresponding to the given file and line number. +// It returns an empty slice if no PCs were found. +func (d *Data) LineToBreakpointPCs(file string, line uint64) ([]uint64, error) { + compDir := d.compilationDirectory() + + // Find the closest match in the executable for the specified file. + // We choose the file with the largest number of path components matching + // at the end of the name. If there is a tie, we prefer files that are + // under the compilation directory. If there is still a tie, we choose + // the file with the shortest name. + // TODO: handle duplicate file names in the DWARF? + var bestFile struct { + fileNum uint64 // Index of the file in the DWARF data. + components int // Number of matching path components. + length int // Length of the filename. + underComp bool // File is under the compilation directory. + } + for filenum, filename := range d.sourceFiles { + c := matchingPathComponentSuffixSize(filename, file) + underComp := strings.HasPrefix(filename, compDir) + better := false + if c != bestFile.components { + better = c > bestFile.components + } else if underComp != bestFile.underComp { + better = underComp + } else { + better = len(filename) < bestFile.length + } + if better { + bestFile.fileNum = uint64(filenum) + bestFile.components = c + bestFile.length = len(filename) + bestFile.underComp = underComp + } + } + if bestFile.components == 0 { + return nil, fmt.Errorf("couldn't find file %q", file) + } + + c := d.lineToPCEntries[bestFile.fileNum] + // c contains all (pc, line) pairs for the appropriate file. + start := sort.Search(len(c), func(i int) bool { return c[i].line >= line }) + end := sort.Search(len(c), func(i int) bool { return c[i].line > line }) + // c[i].line == line for all i in the range [start, end). + pcs := make([]uint64, 0, end-start) + for i := start; i < end; i++ { + pcs = append(pcs, c[i].pc) + } + return pcs, nil +} + +// compilationDirectory finds the first compilation unit entry in d and returns +// the compilation directory contained in it. +// If it fails, it returns the empty string. +func (d *Data) compilationDirectory() string { + r := d.Reader() + for { + entry, err := r.Next() + if entry == nil || err != nil { + return "" + } + if entry.Tag == TagCompileUnit { + name, _ := entry.Val(AttrCompDir).(string) + return name + } + } +} + +// matchingPathComponentSuffixSize returns the largest n such that the last n +// components of the paths p1 and p2 are equal. +// e.g. matchingPathComponentSuffixSize("a/b/x/y.go", "b/a/x/y.go") returns 2. +func matchingPathComponentSuffixSize(p1, p2 string) int { + // TODO: deal with other path separators. + c1 := strings.Split(p1, "/") + c2 := strings.Split(p2, "/") + min := len(c1) + if len(c2) < min { + min = len(c2) + } + var n int + for n = 0; n < min; n++ { + if c1[len(c1)-1-n] != c2[len(c2)-1-n] { + break + } + } + return n +} + +// Standard opcodes. Figure 37, page 178. +// If an opcode >= lineMachine.prologue.opcodeBase, it is a special +// opcode rather than the opcode defined in this table. +const ( + lineStdCopy = 0x01 + lineStdAdvancePC = 0x02 + lineStdAdvanceLine = 0x03 + lineStdSetFile = 0x04 + lineStdSetColumn = 0x05 + lineStdNegateStmt = 0x06 + lineStdSetBasicBlock = 0x07 + lineStdConstAddPC = 0x08 + lineStdFixedAdvancePC = 0x09 + lineStdSetPrologueEnd = 0x0a + lineStdSetEpilogueBegin = 0x0b + lineStdSetISA = 0x0c +) + +// Extended opcodes. Figure 38, page 179. +const ( + lineStartExtendedOpcode = 0x00 // Not defined as a named constant in the spec. + lineExtEndSequence = 0x01 + lineExtSetAddress = 0x02 + lineExtDefineFile = 0x03 + lineExtSetDiscriminator = 0x04 // New in version 4. + lineExtLoUser = 0x80 + lineExtHiUser = 0xff +) + +// lineHeader holds the information stored in the header of the line table for a +// single compilation unit. +// Section 6.2.4, page 112. +type lineHeader struct { + unitLength int + version int + headerLength int + minInstructionLength int + maxOpsPerInstruction int + defaultIsStmt bool + lineBase int + lineRange int + opcodeBase byte + stdOpcodeLengths []byte + include []string // entry 0 is empty; means current directory + file []lineFile // entry 0 is empty. +} + +// lineFile represents a file name stored in the PC/line table, usually in the header. +type lineFile struct { + name string + index int // index into include directories + time int // implementation-defined time of last modification + length int // length in bytes, 0 if not available. +} + +// lineMachine holds the registers evaluated during executing of the PC/line mapping engine. +// Section 6.2.2, page 109. +type lineMachine struct { + // The program-counter value corresponding to a machine instruction generated by the compiler. + address uint64 + + // An unsigned integer representing the index of an operation within a VLIW + // instruction. The index of the first operation is 0. For non-VLIW + // architectures, this register will always be 0. + // The address and op_index registers, taken together, form an operation + // pointer that can reference any individual operation with the instruction + // stream. + opIndex uint64 + + // An unsigned integer indicating the identity of the source file corresponding to a machine instruction. + file uint64 + + // An unsigned integer indicating a source line number. Lines are numbered + // beginning at 1. The compiler may emit the value 0 in cases where an + // instruction cannot be attributed to any source line. + line uint64 + + // An unsigned integer indicating a column number within a source line. + // Columns are numbered beginning at 1. The value 0 is reserved to indicate + // that a statement begins at the “left edge” of the line. + column uint64 + + // A boolean indicating that the current instruction is a recommended + // breakpoint location. A recommended breakpoint location is intended to + // “represent” a line, a statement and/or a semantically distinct subpart of a + // statement. + isStmt bool + + // A boolean indicating that the current instruction is the beginning of a basic + // block. + basicBlock bool + + // A boolean indicating that the current address is that of the first byte after + // the end of a sequence of target machine instructions. end_sequence + // terminates a sequence of lines; therefore other information in the same + // row is not meaningful. + endSequence bool + + // A boolean indicating that the current address is one (of possibly many) + // where execution should be suspended for an entry breakpoint of a + // function. + prologueEnd bool + + // A boolean indicating that the current address is one (of possibly many) + // where execution should be suspended for an exit breakpoint of a function. + epilogueBegin bool + + // An unsigned integer whose value encodes the applicable instruction set + // architecture for the current instruction. + // The encoding of instruction sets should be shared by all users of a given + // architecture. It is recommended that this encoding be defined by the ABI + // authoring committee for each architecture. + isa uint64 + + // An unsigned integer identifying the block to which the current instruction + // belongs. Discriminator values are assigned arbitrarily by the DWARF + // producer and serve to distinguish among multiple blocks that may all be + // associated with the same source file, line, and column. Where only one + // block exists for a given source position, the discriminator value should be + // zero. + discriminator uint64 + + // The header for the current compilation unit. + // Not an actual register, but stored here for cleanliness. + header lineHeader +} + +// parseHeader parses the header describing the compilation unit in the line +// table starting at the specified offset. +func (m *lineMachine) parseHeader(b *buf) error { + m.header = lineHeader{} + m.header.unitLength = int(b.uint32()) // Note: We are assuming 32-bit DWARF format. + if m.header.unitLength > len(b.data) { + return fmt.Errorf("DWARF: bad PC/line header length") + } + m.header.version = int(b.uint16()) + m.header.headerLength = int(b.uint32()) + m.header.minInstructionLength = int(b.uint8()) + if m.header.version >= 4 { + m.header.maxOpsPerInstruction = int(b.uint8()) + } else { + m.header.maxOpsPerInstruction = 1 + } + m.header.defaultIsStmt = b.uint8() != 0 + m.header.lineBase = int(int8(b.uint8())) + m.header.lineRange = int(b.uint8()) + m.header.opcodeBase = b.uint8() + m.header.stdOpcodeLengths = make([]byte, m.header.opcodeBase-1) + copy(m.header.stdOpcodeLengths, b.bytes(int(m.header.opcodeBase-1))) + m.header.include = make([]string, 1) // First entry is empty; file index entries are 1-indexed. + // Includes + for { + name := b.string() + if name == "" { + break + } + m.header.include = append(m.header.include, name) + } + // Files + m.header.file = make([]lineFile, 1, 10) // entries are 1-indexed in line number program. + for { + name := b.string() + if name == "" { + break + } + index := b.uint() + time := b.uint() + length := b.uint() + f := lineFile{ + name: name, + index: int(index), + time: int(time), + length: int(length), + } + m.header.file = append(m.header.file, f) + } + return nil +} + +// Special opcodes, page 117. +// There are seven steps to processing special opcodes. We break them up here +// because the caller needs to output a row between steps 2 and 4, and because +// we need to perform just step 2 for the opcode DW_LNS_const_add_pc. + +func (m *lineMachine) specialOpcodeStep1(opcode byte) { + adjustedOpcode := int(opcode - m.header.opcodeBase) + lineAdvance := m.header.lineBase + (adjustedOpcode % m.header.lineRange) + m.line += uint64(lineAdvance) +} + +func (m *lineMachine) specialOpcodeStep2(opcode byte) { + adjustedOpcode := int(opcode - m.header.opcodeBase) + advance := adjustedOpcode / m.header.lineRange + delta := (int(m.opIndex) + advance) / m.header.maxOpsPerInstruction + m.address += uint64(m.header.minInstructionLength * delta) + m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.header.maxOpsPerInstruction) +} + +func (m *lineMachine) specialOpcodeSteps4To7() { + m.basicBlock = false + m.prologueEnd = false + m.epilogueBegin = false + m.discriminator = 0 +} + +// evalCompilationUnit reads the next compilation unit and calls f at each output row. +// Line machine execution continues while f returns true. +func (m *lineMachine) evalCompilationUnit(b *buf, f func(m *lineMachine) (cont bool)) error { + m.reset() + for len(b.data) > 0 { + op := b.uint8() + if op >= m.header.opcodeBase { + m.specialOpcodeStep1(op) + m.specialOpcodeStep2(op) + // Step 3 is to output a row, so we call f here. + if !f(m) { + return nil + } + m.specialOpcodeSteps4To7() + continue + } + switch op { + case lineStartExtendedOpcode: + if len(b.data) == 0 { + return fmt.Errorf("DWARF: short extended opcode (1)") + } + size := b.uint() + if uint64(len(b.data)) < size { + return fmt.Errorf("DWARF: short extended opcode (2)") + } + op = b.uint8() + switch op { + case lineExtEndSequence: + m.endSequence = true + if !f(m) { + return nil + } + if len(b.data) == 0 { + return nil + } + m.reset() + case lineExtSetAddress: + m.address = b.addr() + m.opIndex = 0 + case lineExtDefineFile: + return fmt.Errorf("DWARF: unimplemented define_file op") + case lineExtSetDiscriminator: + discriminator := b.uint() + m.discriminator = discriminator + default: + return fmt.Errorf("DWARF: unknown extended opcode %#x", op) + } + case lineStdCopy: + if !f(m) { + return nil + } + m.discriminator = 0 + m.basicBlock = false + m.prologueEnd = false + m.epilogueBegin = false + case lineStdAdvancePC: + advance := b.uint() + delta := (int(m.opIndex) + int(advance)) / m.header.maxOpsPerInstruction + m.address += uint64(m.header.minInstructionLength * delta) + m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.header.maxOpsPerInstruction) + m.basicBlock = false + m.prologueEnd = false + m.epilogueBegin = false + m.discriminator = 0 + case lineStdAdvanceLine: + advance := b.int() + m.line = uint64(int64(m.line) + advance) + case lineStdSetFile: + index := b.uint() + m.file = index + case lineStdSetColumn: + column := b.uint() + m.column = column + case lineStdNegateStmt: + m.isStmt = !m.isStmt + case lineStdSetBasicBlock: + m.basicBlock = true + case lineStdFixedAdvancePC: + m.address += uint64(b.uint16()) + m.opIndex = 0 + case lineStdSetPrologueEnd: + m.prologueEnd = true + case lineStdSetEpilogueBegin: + m.epilogueBegin = true + case lineStdSetISA: + m.isa = b.uint() + case lineStdConstAddPC: + // Update the the address and op_index registers. + m.specialOpcodeStep2(255) + default: + panic("not reached") + } + } + return fmt.Errorf("DWARF: unexpected end of line number information") +} + +// reset sets the machine's registers to the initial state. Page 111. +func (m *lineMachine) reset() { + m.address = 0 + m.opIndex = 0 + m.file = 1 + m.line = 1 + m.column = 0 + m.isStmt = m.header.defaultIsStmt + m.basicBlock = false + m.endSequence = false + m.prologueEnd = false + m.epilogueBegin = false + m.isa = 0 + m.discriminator = 0 +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/open.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/open.go new file mode 100644 index 0000000000..b22b53d875 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/open.go @@ -0,0 +1,104 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf provides access to DWARF debugging information loaded from +// executable files, as defined in the DWARF 2.0 Standard at +// http://dwarfstd.org/doc/dwarf-2.0.0.pdf +package dwarf // import "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" + +import "encoding/binary" + +// Data represents the DWARF debugging information +// loaded from an executable file (for example, an ELF or Mach-O executable). +type Data struct { + // raw data + abbrev []byte + aranges []byte + frame []byte + info []byte + line []byte + pubnames []byte + ranges []byte + str []byte + + // parsed data + abbrevCache map[uint32]abbrevTable + order binary.ByteOrder + typeCache map[Offset]Type + typeSigs map[uint64]*typeUnit + unit []unit + sourceFiles []string // source files listed in .debug_line. + nameCache // map from name to top-level entries in .debug_info. + pcToFuncEntries // cache of .debug_info data for function bounds. + pcToLineEntries // cache of .debug_line data, used for efficient PC-to-line mapping. + lineToPCEntries // cache of .debug_line data, used for efficient line-to-[]PC mapping. +} + +// New returns a new Data object initialized from the given parameters. +// Rather than calling this function directly, clients should typically use +// the DWARF method of the File type of the appropriate package debug/elf, +// debug/macho, or debug/pe. +// +// The []byte arguments are the data from the corresponding debug section +// in the object file; for example, for an ELF object, abbrev is the contents of +// the ".debug_abbrev" section. +func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, error) { + d := &Data{ + abbrev: abbrev, + aranges: aranges, + frame: frame, + info: info, + line: line, + pubnames: pubnames, + ranges: ranges, + str: str, + abbrevCache: make(map[uint32]abbrevTable), + typeCache: make(map[Offset]Type), + typeSigs: make(map[uint64]*typeUnit), + } + + // Sniff .debug_info to figure out byte order. + // bytes 4:6 are the version, a tiny 16-bit number (1, 2, 3). + if len(d.info) < 6 { + return nil, DecodeError{"info", Offset(len(d.info)), "too short"} + } + x, y := d.info[4], d.info[5] + switch { + case x == 0 && y == 0: + return nil, DecodeError{"info", 4, "unsupported version 0"} + case x == 0: + d.order = binary.BigEndian + case y == 0: + d.order = binary.LittleEndian + default: + return nil, DecodeError{"info", 4, "cannot determine byte order"} + } + + u, err := d.parseUnits() + if err != nil { + return nil, err + } + d.unit = u + d.buildInfoCaches() + d.buildLineCaches() + return d, nil +} + +// AddTypes will add one .debug_types section to the DWARF data. A +// typical object with DWARF version 4 debug info will have multiple +// .debug_types sections. The name is used for error reporting only, +// and serves to distinguish one .debug_types section from another. +func (d *Data) AddTypes(name string, types []byte) error { + return d.parseTypes(name, types) +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/pclntab_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/pclntab_test.go new file mode 100644 index 0000000000..ba8633dba8 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/pclntab_test.go @@ -0,0 +1,168 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf_test + +// Stripped-down, simplified version of ../../gosym/pclntab_test.go + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + + . "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho" +) + +var ( + pclineTempDir string + pclinetestBinary string +) + +func dotest(self bool) bool { + // For now, only works on amd64 platforms. + if runtime.GOARCH != "amd64" { + return false + } + // Self test reads test binary; only works on Linux or Mac. + if self { + if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { + return false + } + } + // Command below expects "sh", so Unix. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + return false + } + if pclinetestBinary != "" { + return true + } + var err error + pclineTempDir, err = ioutil.TempDir("", "pclinetest") + if err != nil { + panic(err) + } + if strings.Contains(pclineTempDir, " ") { + panic("unexpected space in tempdir") + } + // This command builds pclinetest from ../../gosym/pclinetest.asm; + // the resulting binary looks like it was built from pclinetest.s, + // but we have renamed it to keep it away from the go tool. + pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest") + command := fmt.Sprintf("go tool asm -o %s.6 ../gosym/pclinetest.asm && go tool link -H %s -E main -o %s %s.6", + pclinetestBinary, runtime.GOOS, pclinetestBinary, pclinetestBinary) + cmd := exec.Command("sh", "-c", command) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + return true +} + +func endtest() { + if pclineTempDir != "" { + os.RemoveAll(pclineTempDir) + pclineTempDir = "" + pclinetestBinary = "" + } +} + +func getData(file string) (*Data, error) { + switch runtime.GOOS { + case "linux": + f, err := elf.Open(file) + if err != nil { + return nil, err + } + dwarf, err := f.DWARF() + if err != nil { + return nil, err + } + f.Close() + return dwarf, nil + case "darwin": + f, err := macho.Open(file) + if err != nil { + return nil, err + } + dwarf, err := f.DWARF() + if err != nil { + return nil, err + } + f.Close() + return dwarf, nil + } + panic("unimplemented DWARF for GOOS=" + runtime.GOOS) +} + +func TestPCToLine(t *testing.T) { + t.Skip("linker complains while building test binary") + + if !dotest(false) { + return + } + defer endtest() + + data, err := getData(pclinetestBinary) + if err != nil { + t.Fatal(err) + } + + // Test PCToLine. + entry, err := data.LookupFunction("linefrompc") + if err != nil { + t.Fatal(err) + } + pc, ok := entry.Val(AttrLowpc).(uint64) + if !ok { + t.Fatal(`DWARF data for function "linefrompc" has no PC`) + } + for _, tt := range []struct { + offset, want uint64 + }{ + {0, 2}, + {1, 3}, + {2, 4}, + {3, 4}, + {4, 5}, + {6, 5}, + {7, 6}, + {11, 6}, + {12, 7}, + {19, 7}, + {20, 8}, + {32, 8}, + {33, 9}, + {53, 9}, + {54, 10}, + } { + file, line, err := data.PCToLine(pc + tt.offset) + if err != nil { + t.Fatal(err) + } + if !strings.HasSuffix(file, "/pclinetest.asm") { + t.Errorf("got %s; want %s", file, ".../pclinetest.asm") + } + if line != tt.want { + t.Errorf("line for offset %d: got %d; want %d", tt.offset, line, tt.want) + } + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/symbol.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/symbol.go new file mode 100644 index 0000000000..7bbdf48dc0 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/symbol.go @@ -0,0 +1,129 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf + +// This file provides simple methods to access the symbol table by name and address. + +import ( + "fmt" + "regexp" + "sort" +) + +// lookupEntry returns the first Entry for the name. +// If tag is non-zero, only entries with that tag are considered. +func (d *Data) lookupEntry(name string, tag Tag) (*Entry, error) { + x, ok := d.nameCache[name] + if !ok { + return nil, fmt.Errorf("DWARF entry for %q not found", name) + } + for ; x != nil; x = x.link { + if tag == 0 || x.entry.Tag == tag { + return x.entry, nil + } + } + return nil, fmt.Errorf("no DWARF entry for %q with tag %s", name, tag) +} + +// LookupMatchingSymbols returns the names of all top-level entries matching +// the given regular expression. +func (d *Data) LookupMatchingSymbols(nameRE *regexp.Regexp) (result []string, err error) { + for name := range d.nameCache { + if nameRE.MatchString(name) { + result = append(result, name) + } + } + return result, nil +} + +// LookupEntry returns the Entry for the named symbol. +func (d *Data) LookupEntry(name string) (*Entry, error) { + return d.lookupEntry(name, 0) +} + +// LookupFunction returns the entry for a function. +func (d *Data) LookupFunction(name string) (*Entry, error) { + return d.lookupEntry(name, TagSubprogram) +} + +// LookupVariable returns the entry for a (global) variable. +func (d *Data) LookupVariable(name string) (*Entry, error) { + return d.lookupEntry(name, TagVariable) +} + +// EntryLocation returns the address of the object referred to by the given Entry. +func (d *Data) EntryLocation(e *Entry) (uint64, error) { + loc, _ := e.Val(AttrLocation).([]byte) + if len(loc) == 0 { + return 0, fmt.Errorf("DWARF entry has no Location attribute") + } + // TODO: implement the DWARF Location bytecode. What we have here only + // recognizes a program with a single literal opAddr bytecode. + if asize := d.unit[0].asize; loc[0] == opAddr && len(loc) == 1+asize { + switch asize { + case 1: + return uint64(loc[1]), nil + case 2: + return uint64(d.order.Uint16(loc[1:])), nil + case 4: + return uint64(d.order.Uint32(loc[1:])), nil + case 8: + return d.order.Uint64(loc[1:]), nil + } + } + return 0, fmt.Errorf("DWARF entry has an unimplemented Location op") +} + +// EntryType returns the Type for an Entry. +func (d *Data) EntryType(e *Entry) (Type, error) { + off, err := d.EntryTypeOffset(e) + if err != nil { + return nil, err + } + return d.Type(off) +} + +// EntryTypeOffset returns the offset in the given Entry's type attribute. +func (d *Data) EntryTypeOffset(e *Entry) (Offset, error) { + v := e.Val(AttrType) + if v == nil { + return 0, fmt.Errorf("DWARF entry has no Type attribute") + } + off, ok := v.(Offset) + if !ok { + return 0, fmt.Errorf("DWARF entry has an invalid Type attribute") + } + return off, nil +} + +// PCToFunction returns the entry and address for the function containing the +// specified PC. +func (d *Data) PCToFunction(pc uint64) (entry *Entry, lowpc uint64, err error) { + p := d.pcToFuncEntries + if len(p) == 0 { + return nil, 0, fmt.Errorf("no function addresses loaded") + } + i := sort.Search(len(p), func(i int) bool { return p[i].pc > pc }) - 1 + // The search failed if: + // - pc was before the start of any function. + // - The largest function bound not larger than pc was the end of a function, + // not the start of one. + // - The largest function bound not larger than pc was the start of a function + // that we don't know the end of, and the PC is much larger than the start. + if i == -1 || p[i].entry == nil || (i+1 == len(p) && pc-p[i].pc >= 1<<20) { + return nil, 0, fmt.Errorf("no function at %x", pc) + } + return p[i].entry, p[i].pc, nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/pcsptest.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/pcsptest.go new file mode 100644 index 0000000000..96202ffe28 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/pcsptest.go @@ -0,0 +1,39 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 main + +import "fmt" + +func main() { + test(1, 2, 3) +} + +// This is the function we examine. After the preamble its stack should be +// pulled down 1*addrSize for the return PC plus 3*8 for the three +// arguments. That will be (1+3)*8=32 on 64-bit machines. +func test(a, b, c int64) int64 { + // Put in enough code that it's not inlined. + for a = 0; a < 100; a++ { + b += c + } + afterTest(a, b, c) + return b +} + +// This function follows test in the binary. We use it to force arguments +// onto the stack and as a delimiter in the text we scan in the test. +func afterTest(a, b, c int64) { + fmt.Println(a, b, c) +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.c b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.c new file mode 100644 index 0000000000..a9659b30be --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.c @@ -0,0 +1,95 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +/* +Linux ELF: +gcc -gdwarf-2 -m64 -c typedef.c && gcc -gdwarf-2 -m64 -o typedef.elf typedef.o + +OS X Mach-O: +gcc -gdwarf-2 -m64 -c typedef.c -o typedef.macho +*/ +#include + +typedef volatile int* t_ptr_volatile_int; +typedef const char *t_ptr_const_char; +typedef long t_long; +typedef unsigned short t_ushort; +typedef int t_func_int_of_float_double(float, double); +typedef int (*t_ptr_func_int_of_float_double)(float, double); +typedef int (*t_ptr_func_int_of_float_complex)(float complex); +typedef int (*t_ptr_func_int_of_double_complex)(double complex); +typedef int (*t_ptr_func_int_of_long_double_complex)(long double complex); +typedef int *t_func_ptr_int_of_char_schar_uchar(char, signed char, unsigned char); +typedef void t_func_void_of_char(char); +typedef void t_func_void_of_void(void); +typedef void t_func_void_of_ptr_char_dots(char*, ...); +typedef struct my_struct { + volatile int vi; + char x : 1; + int y : 4; + int z[0]; + long long array[40]; + int zz[0]; +} t_my_struct; +typedef struct my_struct1 { + int zz [1]; +} t_my_struct1; +typedef union my_union { + volatile int vi; + char x : 1; + int y : 4; + long long array[40]; +} t_my_union; +typedef enum my_enum { + e1 = 1, + e2 = 2, + e3 = -5, + e4 = 1000000000000000LL, +} t_my_enum; + +typedef struct list t_my_list; +struct list { + short val; + t_my_list *next; +}; + +typedef struct tree { + struct tree *left, *right; + unsigned long long val; +} t_my_tree; + +t_ptr_volatile_int *a2; +t_ptr_const_char **a3a; +t_long *a4; +t_ushort *a5; +t_func_int_of_float_double *a6; +t_ptr_func_int_of_float_double *a7; +t_func_ptr_int_of_char_schar_uchar *a8; +t_func_void_of_char *a9; +t_func_void_of_void *a10; +t_func_void_of_ptr_char_dots *a11; +t_my_struct *a12; +t_my_struct1 *a12a; +t_my_union *a12b; +t_my_enum *a13; +t_my_list *a14; +t_my_tree *a15; +t_ptr_func_int_of_float_complex *a16; +t_ptr_func_int_of_double_complex *a17; +t_ptr_func_int_of_long_double_complex *a18; + +int main() +{ + return 0; +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf new file mode 100755 index 0000000000..b2062d2c4b Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf4 b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf4 new file mode 100644 index 0000000000..3d5a5a1b16 Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf4 differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.macho b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.macho new file mode 100644 index 0000000000..f75afcccbf Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.macho differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/type.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/type.go new file mode 100644 index 0000000000..04722caec1 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/type.go @@ -0,0 +1,877 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// DWARF type information structures. +// The format is heavily biased toward C, but for simplicity +// the String methods use a pseudo-Go syntax. + +package dwarf + +import ( + "fmt" + "reflect" + "strconv" +) + +// A Type conventionally represents a pointer to any of the +// specific Type structures (CharType, StructType, etc.). +type Type interface { + Common() *CommonType + String() string + Size() int64 +} + +// A CommonType holds fields common to multiple types. +// If a field is not known or not applicable for a given type, +// the zero value is used. +type CommonType struct { + ByteSize int64 // size of value of this type, in bytes + Name string // name that can be used to refer to type + ReflectKind reflect.Kind // the reflect kind of the type. + Offset Offset // the offset at which this type was read +} + +func (c *CommonType) Common() *CommonType { return c } + +func (c *CommonType) Size() int64 { return c.ByteSize } + +// Basic types + +// A BasicType holds fields common to all basic types. +type BasicType struct { + CommonType + BitSize int64 + BitOffset int64 +} + +func (b *BasicType) Basic() *BasicType { return b } + +func (t *BasicType) String() string { + if t.Name != "" { + return t.Name + } + return "?" +} + +// A CharType represents a signed character type. +type CharType struct { + BasicType +} + +// A UcharType represents an unsigned character type. +type UcharType struct { + BasicType +} + +// An IntType represents a signed integer type. +type IntType struct { + BasicType +} + +// A UintType represents an unsigned integer type. +type UintType struct { + BasicType +} + +// A FloatType represents a floating point type. +type FloatType struct { + BasicType +} + +// A ComplexType represents a complex floating point type. +type ComplexType struct { + BasicType +} + +// A BoolType represents a boolean type. +type BoolType struct { + BasicType +} + +// An AddrType represents a machine address type. +type AddrType struct { + BasicType +} + +// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type. +type UnspecifiedType struct { + BasicType +} + +// qualifiers + +// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier. +type QualType struct { + CommonType + Qual string + Type Type +} + +func (t *QualType) String() string { return t.Qual + " " + t.Type.String() } + +func (t *QualType) Size() int64 { return t.Type.Size() } + +// An ArrayType represents a fixed size array type. +type ArrayType struct { + CommonType + Type Type + StrideBitSize int64 // if > 0, number of bits to hold each element + Count int64 // if == -1, an incomplete array, like char x[]. +} + +func (t *ArrayType) String() string { + return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String() +} + +func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() } + +// A VoidType represents the C void type. +type VoidType struct { + CommonType +} + +func (t *VoidType) String() string { return "void" } + +// A PtrType represents a pointer type. +type PtrType struct { + CommonType + Type Type +} + +func (t *PtrType) String() string { return "*" + t.Type.String() } + +// A StructType represents a struct, union, or C++ class type. +type StructType struct { + CommonType + StructName string + Kind string // "struct", "union", or "class". + Field []*StructField + Incomplete bool // if true, struct, union, class is declared but not defined +} + +// A StructField represents a field in a struct, union, or C++ class type. +type StructField struct { + Name string + Type Type + ByteOffset int64 + ByteSize int64 + BitOffset int64 // within the ByteSize bytes at ByteOffset + BitSize int64 // zero if not a bit field + Embedded bool +} + +func (t *StructType) String() string { + if t.StructName != "" { + return t.Kind + " " + t.StructName + } + return t.Defn() +} + +func (t *StructType) Defn() string { + s := t.Kind + if t.StructName != "" { + s += " " + t.StructName + } + if t.Incomplete { + s += " /*incomplete*/" + return s + } + s += " {" + for i, f := range t.Field { + if i > 0 { + s += "; " + } + s += f.Name + " " + f.Type.String() + s += "@" + strconv.FormatInt(f.ByteOffset, 10) + if f.BitSize > 0 { + s += " : " + strconv.FormatInt(f.BitSize, 10) + s += "@" + strconv.FormatInt(f.BitOffset, 10) + } + } + s += "}" + return s +} + +// A SliceType represents a Go slice type. It looks like a StructType, describing +// the runtime-internal structure, with extra fields. +type SliceType struct { + StructType + ElemType Type +} + +func (t *SliceType) String() string { + if t.Name != "" { + return t.Name + } + return "[]" + t.ElemType.String() +} + +// A StringType represents a Go string type. It looks like a StructType, describing +// the runtime-internal structure, but we wrap it for neatness. +type StringType struct { + StructType +} + +func (t *StringType) String() string { + if t.Name != "" { + return t.Name + } + return "string" +} + +// An InterfaceType represents a Go interface. +type InterfaceType struct { + TypedefType +} + +func (t *InterfaceType) String() string { + if t.Name != "" { + return t.Name + } + return "Interface" +} + +// An EnumType represents an enumerated type. +// The only indication of its native integer type is its ByteSize +// (inside CommonType). +type EnumType struct { + CommonType + EnumName string + Val []*EnumValue +} + +// An EnumValue represents a single enumeration value. +type EnumValue struct { + Name string + Val int64 +} + +func (t *EnumType) String() string { + s := "enum" + if t.EnumName != "" { + s += " " + t.EnumName + } + s += " {" + for i, v := range t.Val { + if i > 0 { + s += "; " + } + s += v.Name + "=" + strconv.FormatInt(v.Val, 10) + } + s += "}" + return s +} + +// A FuncType represents a function type. +type FuncType struct { + CommonType + ReturnType Type + ParamType []Type +} + +func (t *FuncType) String() string { + s := "func(" + for i, t := range t.ParamType { + if i > 0 { + s += ", " + } + s += t.String() + } + s += ")" + if t.ReturnType != nil { + s += " " + t.ReturnType.String() + } + return s +} + +// A DotDotDotType represents the variadic ... function parameter. +type DotDotDotType struct { + CommonType +} + +func (t *DotDotDotType) String() string { return "..." } + +// A TypedefType represents a named type. +type TypedefType struct { + CommonType + Type Type +} + +func (t *TypedefType) String() string { return t.Name } + +func (t *TypedefType) Size() int64 { return t.Type.Size() } + +// A MapType represents a Go map type. It looks like a TypedefType, describing +// the runtime-internal structure, with extra fields. +type MapType struct { + TypedefType + KeyType Type + ElemType Type +} + +func (t *MapType) String() string { + if t.Name != "" { + return t.Name + } + return "map[" + t.KeyType.String() + "]" + t.ElemType.String() +} + +// A ChanType represents a Go channel type. +type ChanType struct { + TypedefType + ElemType Type +} + +func (t *ChanType) String() string { + if t.Name != "" { + return t.Name + } + return "chan " + t.ElemType.String() +} + +// typeReader is used to read from either the info section or the +// types section. +type typeReader interface { + Seek(Offset) + Next() (*Entry, error) + clone() typeReader + offset() Offset + // AddressSize returns the size in bytes of addresses in the current + // compilation unit. + AddressSize() int +} + +// Type reads the type at off in the DWARF ``info'' section. +func (d *Data) Type(off Offset) (Type, error) { + return d.readType("info", d.Reader(), off, d.typeCache) +} + +func getKind(e *Entry) reflect.Kind { + integer, _ := e.Val(AttrGoKind).(int64) + return reflect.Kind(integer) +} + +// readType reads a type from r at off of name using and updating a +// type cache. +func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type) (Type, error) { + if t, ok := typeCache[off]; ok { + return t, nil + } + r.Seek(off) + e, err := r.Next() + if err != nil { + return nil, err + } + addressSize := r.AddressSize() + if e == nil || e.Offset != off { + return nil, DecodeError{name, off, "no type at offset"} + } + + // Parse type from Entry. + // Must always set typeCache[off] before calling + // d.Type recursively, to handle circular types correctly. + var typ Type + + nextDepth := 0 + + // Get next child; set err if error happens. + next := func() *Entry { + if !e.Children { + return nil + } + // Only return direct children. + // Skip over composite entries that happen to be nested + // inside this one. Most DWARF generators wouldn't generate + // such a thing, but clang does. + // See golang.org/issue/6472. + for { + kid, err1 := r.Next() + if err1 != nil { + err = err1 + return nil + } + if kid == nil { + err = DecodeError{name, r.offset(), "unexpected end of DWARF entries"} + return nil + } + if kid.Tag == 0 { + if nextDepth > 0 { + nextDepth-- + continue + } + return nil + } + if kid.Children { + nextDepth++ + } + if nextDepth > 0 { + continue + } + return kid + } + } + + // Get Type referred to by Entry's attr. + // Set err if error happens. Not having a type is an error. + typeOf := func(e *Entry, attr Attr) Type { + tval := e.Val(attr) + var t Type + switch toff := tval.(type) { + case Offset: + if t, err = d.readType(name, r.clone(), toff, typeCache); err != nil { + return nil + } + case uint64: + if t, err = d.sigToType(toff); err != nil { + return nil + } + default: + // It appears that no Type means "void". + return new(VoidType) + } + return t + } + + switch e.Tag { + case TagArrayType: + // Multi-dimensional array. (DWARF v2 §5.4) + // Attributes: + // AttrType:subtype [required] + // AttrStrideSize: distance in bits between each element of the array + // AttrStride: distance in bytes between each element of the array + // AttrByteSize: size of entire array + // Children: + // TagSubrangeType or TagEnumerationType giving one dimension. + // dimensions are in left to right order. + t := new(ArrayType) + t.Name, _ = e.Val(AttrName).(string) + t.ReflectKind = getKind(e) + typ = t + typeCache[off] = t + if t.Type = typeOf(e, AttrType); err != nil { + goto Error + } + if bytes, ok := e.Val(AttrStride).(int64); ok { + t.StrideBitSize = 8 * bytes + } else if bits, ok := e.Val(AttrStrideSize).(int64); ok { + t.StrideBitSize = bits + } else { + // If there's no stride specified, assume it's the size of the + // array's element type. + t.StrideBitSize = 8 * t.Type.Size() + } + + // Accumulate dimensions, + ndim := 0 + for kid := next(); kid != nil; kid = next() { + // TODO(rsc): Can also be TagEnumerationType + // but haven't seen that in the wild yet. + switch kid.Tag { + case TagSubrangeType: + count, ok := kid.Val(AttrCount).(int64) + if !ok { + // Old binaries may have an upper bound instead. + count, ok = kid.Val(AttrUpperBound).(int64) + if ok { + count++ // Length is one more than upper bound. + } else { + count = -1 // As in x[]. + } + } + if ndim == 0 { + t.Count = count + } else { + // Multidimensional array. + // Create new array type underneath this one. + t.Type = &ArrayType{Type: t.Type, Count: count} + } + ndim++ + case TagEnumerationType: + err = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"} + goto Error + } + } + if ndim == 0 { + // LLVM generates this for x[]. + t.Count = -1 + } + + case TagBaseType: + // Basic type. (DWARF v2 §5.1) + // Attributes: + // AttrName: name of base type in programming language of the compilation unit [required] + // AttrEncoding: encoding value for type (encFloat etc) [required] + // AttrByteSize: size of type in bytes [required] + // AttrBitOffset: for sub-byte types, size in bits + // AttrBitSize: for sub-byte types, bit offset of high order bit in the AttrByteSize bytes + name, _ := e.Val(AttrName).(string) + enc, ok := e.Val(AttrEncoding).(int64) + if !ok { + err = DecodeError{name, e.Offset, "missing encoding attribute for " + name} + goto Error + } + switch enc { + default: + err = DecodeError{name, e.Offset, "unrecognized encoding attribute value"} + goto Error + + case encAddress: + typ = new(AddrType) + case encBoolean: + typ = new(BoolType) + case encComplexFloat: + typ = new(ComplexType) + if name == "complex" { + // clang writes out 'complex' instead of 'complex float' or 'complex double'. + // clang also writes out a byte size that we can use to distinguish. + // See issue 8694. + switch byteSize, _ := e.Val(AttrByteSize).(int64); byteSize { + case 8: + name = "complex float" + case 16: + name = "complex double" + } + } + case encFloat: + typ = new(FloatType) + case encSigned: + typ = new(IntType) + case encUnsigned: + typ = new(UintType) + case encSignedChar: + typ = new(CharType) + case encUnsignedChar: + typ = new(UcharType) + } + typeCache[off] = typ + t := typ.(interface { + Basic() *BasicType + }).Basic() + t.Name = name + t.BitSize, _ = e.Val(AttrBitSize).(int64) + t.BitOffset, _ = e.Val(AttrBitOffset).(int64) + t.ReflectKind = getKind(e) + + case TagClassType, TagStructType, TagUnionType: + // Structure, union, or class type. (DWARF v2 §5.5) + // Also Slices and Strings (Go-specific). + // Attributes: + // AttrName: name of struct, union, or class + // AttrByteSize: byte size [required] + // AttrDeclaration: if true, struct/union/class is incomplete + // AttrGoElem: present for slices only. + // Children: + // TagMember to describe one member. + // AttrName: name of member [required] + // AttrType: type of member [required] + // AttrByteSize: size in bytes + // AttrBitOffset: bit offset within bytes for bit fields + // AttrBitSize: bit size for bit fields + // AttrDataMemberLoc: location within struct [required for struct, class] + // There is much more to handle C++, all ignored for now. + t := new(StructType) + t.ReflectKind = getKind(e) + switch t.ReflectKind { + case reflect.Slice: + slice := new(SliceType) + slice.ElemType = typeOf(e, AttrGoElem) + t = &slice.StructType + typ = slice + case reflect.String: + str := new(StringType) + t = &str.StructType + typ = str + default: + typ = t + } + typeCache[off] = typ + switch e.Tag { + case TagClassType: + t.Kind = "class" + case TagStructType: + t.Kind = "struct" + case TagUnionType: + t.Kind = "union" + } + t.Name, _ = e.Val(AttrName).(string) + t.StructName, _ = e.Val(AttrName).(string) + t.Incomplete = e.Val(AttrDeclaration) != nil + t.Field = make([]*StructField, 0, 8) + var lastFieldType Type + var lastFieldBitOffset int64 + for kid := next(); kid != nil; kid = next() { + if kid.Tag == TagMember { + f := new(StructField) + if f.Type = typeOf(kid, AttrType); err != nil { + goto Error + } + switch loc := kid.Val(AttrDataMemberLoc).(type) { + case []byte: + // TODO: Should have original compilation + // unit here, not unknownFormat. + if len(loc) == 0 { + // Empty exprloc. f.ByteOffset=0. + break + } + b := makeBuf(d, unknownFormat{}, "location", 0, loc) + op := b.uint8() + switch op { + case opPlusUconst: + // Handle opcode sequence [DW_OP_plus_uconst ] + f.ByteOffset = int64(b.uint()) + b.assertEmpty() + case opConsts: + // Handle opcode sequence [DW_OP_consts DW_OP_plus] + f.ByteOffset = b.int() + op = b.uint8() + if op != opPlus { + err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)} + goto Error + } + b.assertEmpty() + default: + err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)} + goto Error + } + if b.err != nil { + err = b.err + goto Error + } + case int64: + f.ByteOffset = loc + } + + haveBitOffset := false + f.Name, _ = kid.Val(AttrName).(string) + f.ByteSize, _ = kid.Val(AttrByteSize).(int64) + f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64) + f.BitSize, _ = kid.Val(AttrBitSize).(int64) + f.Embedded, _ = kid.Val(AttrGoEmbeddedField).(bool) + t.Field = append(t.Field, f) + + bito := f.BitOffset + if !haveBitOffset { + bito = f.ByteOffset * 8 + } + if bito == lastFieldBitOffset && t.Kind != "union" { + // Last field was zero width. Fix array length. + // (DWARF writes out 0-length arrays as if they were 1-length arrays.) + zeroArray(lastFieldType) + } + lastFieldType = f.Type + lastFieldBitOffset = bito + } + } + if t.Kind != "union" { + b, ok := e.Val(AttrByteSize).(int64) + if ok && b*8 == lastFieldBitOffset { + // Final field must be zero width. Fix array length. + zeroArray(lastFieldType) + } + } + + case TagConstType, TagVolatileType, TagRestrictType: + // Type modifier (DWARF v2 §5.2) + // Attributes: + // AttrType: subtype + t := new(QualType) + t.Name, _ = e.Val(AttrName).(string) + t.ReflectKind = getKind(e) + typ = t + typeCache[off] = t + if t.Type = typeOf(e, AttrType); err != nil { + goto Error + } + switch e.Tag { + case TagConstType: + t.Qual = "const" + case TagRestrictType: + t.Qual = "restrict" + case TagVolatileType: + t.Qual = "volatile" + } + + case TagEnumerationType: + // Enumeration type (DWARF v2 §5.6) + // Attributes: + // AttrName: enum name if any + // AttrByteSize: bytes required to represent largest value + // Children: + // TagEnumerator: + // AttrName: name of constant + // AttrConstValue: value of constant + t := new(EnumType) + t.ReflectKind = getKind(e) + typ = t + typeCache[off] = t + t.Name, _ = e.Val(AttrName).(string) + t.EnumName, _ = e.Val(AttrName).(string) + t.Val = make([]*EnumValue, 0, 8) + for kid := next(); kid != nil; kid = next() { + if kid.Tag == TagEnumerator { + f := new(EnumValue) + f.Name, _ = kid.Val(AttrName).(string) + f.Val, _ = kid.Val(AttrConstValue).(int64) + n := len(t.Val) + if n >= cap(t.Val) { + val := make([]*EnumValue, n, n*2) + copy(val, t.Val) + t.Val = val + } + t.Val = t.Val[0 : n+1] + t.Val[n] = f + } + } + + case TagPointerType: + // Type modifier (DWARF v2 §5.2) + // Attributes: + // AttrType: subtype [not required! void* has no AttrType] + // AttrAddrClass: address class [ignored] + t := new(PtrType) + t.Name, _ = e.Val(AttrName).(string) + t.ReflectKind = getKind(e) + typ = t + typeCache[off] = t + if e.Val(AttrType) == nil { + t.Type = &VoidType{} + break + } + t.Type = typeOf(e, AttrType) + + case TagSubroutineType: + // Subroutine type. (DWARF v2 §5.7) + // Attributes: + // AttrType: type of return value if any + // AttrName: possible name of type [ignored] + // AttrPrototyped: whether used ANSI C prototype [ignored] + // Children: + // TagFormalParameter: typed parameter + // AttrType: type of parameter + // TagUnspecifiedParameter: final ... + t := new(FuncType) + t.Name, _ = e.Val(AttrName).(string) + t.ReflectKind = getKind(e) + typ = t + typeCache[off] = t + if t.ReturnType = typeOf(e, AttrType); err != nil { + goto Error + } + t.ParamType = make([]Type, 0, 8) + for kid := next(); kid != nil; kid = next() { + var tkid Type + switch kid.Tag { + default: + continue + case TagFormalParameter: + if tkid = typeOf(kid, AttrType); err != nil { + goto Error + } + case TagUnspecifiedParameters: + tkid = &DotDotDotType{} + } + t.ParamType = append(t.ParamType, tkid) + } + + case TagTypedef: + // Typedef (DWARF v2 §5.3) + // Also maps and channels (Go-specific). + // Attributes: + // AttrName: name [required] + // AttrType: type definition [required] + // AttrGoKey: present for maps. + // AttrGoElem: present for maps and channels. + t := new(TypedefType) + t.ReflectKind = getKind(e) + switch t.ReflectKind { + case reflect.Map: + m := new(MapType) + m.KeyType = typeOf(e, AttrGoKey) + m.ElemType = typeOf(e, AttrGoElem) + t = &m.TypedefType + typ = m + case reflect.Chan: + c := new(ChanType) + c.ElemType = typeOf(e, AttrGoElem) + t = &c.TypedefType + typ = c + case reflect.Interface: + it := new(InterfaceType) + t = &it.TypedefType + typ = it + default: + typ = t + } + typeCache[off] = typ + t.Name, _ = e.Val(AttrName).(string) + t.Type = typeOf(e, AttrType) + + case TagUnspecifiedType: + // Unspecified type (DWARF v3 §5.2) + // Attributes: + // AttrName: name + t := new(UnspecifiedType) + typ = t + typeCache[off] = t + t.Name, _ = e.Val(AttrName).(string) + + default: + err = DecodeError{name, off, "unsupported type tag"} + } + + if err != nil { + goto Error + } + + typ.Common().Offset = off + + { + b, ok := e.Val(AttrByteSize).(int64) + if !ok { + b = -1 + switch t := typ.(type) { + case *TypedefType: + b = t.Type.Size() + case *MapType: + b = t.Type.Size() + case *ChanType: + b = t.Type.Size() + case *InterfaceType: + b = t.Type.Size() + case *PtrType: + b = int64(addressSize) + } + } + typ.Common().ByteSize = b + } + return typ, nil + +Error: + // If the parse fails, take the type out of the cache + // so that the next call with this offset doesn't hit + // the cache and return success. + delete(typeCache, off) + return nil, err +} + +func zeroArray(t Type) { + for { + at, ok := t.(*ArrayType) + if !ok { + break + } + at.Count = 0 + t = at.Type + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/type_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/type_test.go new file mode 100644 index 0000000000..e2efeb88d9 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/type_test.go @@ -0,0 +1,148 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf_test + +import ( + "testing" + + . "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho" +) + +var typedefTests = map[string]string{ + "t_ptr_volatile_int": "*volatile int", + "t_ptr_const_char": "*const char", + "t_long": "long int", + "t_ushort": "short unsigned int", + "t_func_int_of_float_double": "func(float, double) int", + "t_ptr_func_int_of_float_double": "*func(float, double) int", + "t_ptr_func_int_of_float_complex": "*func(complex float) int", + "t_ptr_func_int_of_double_complex": "*func(complex double) int", + "t_ptr_func_int_of_long_double_complex": "*func(complex long double) int", + "t_func_ptr_int_of_char_schar_uchar": "func(char, signed char, unsigned char) *int", + "t_func_void_of_char": "func(char) void", + "t_func_void_of_void": "func() void", + "t_func_void_of_ptr_char_dots": "func(*char, ...) void", + "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}", + "t_my_struct1": "struct my_struct1 {zz [1]int@0}", + "t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}", + "t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}", + "t_my_list": "struct list {val short int@0; next *t_my_list@8}", + "t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}", +} + +// As Apple converts gcc to a clang-based front end +// they keep breaking the DWARF output. This map lists the +// conversion from real answer to Apple answer. +var machoBug = map[string]string{ + "func(*char, ...) void": "func(*char) void", + "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}", +} + +func elfData(t *testing.T, name string) *Data { + f, err := elf.Open(name) + if err != nil { + t.Fatal(err) + } + + d, err := f.DWARF() + if err != nil { + t.Fatal(err) + } + return d +} + +func machoData(t *testing.T, name string) *Data { + f, err := macho.Open(name) + if err != nil { + t.Fatal(err) + } + + d, err := f.DWARF() + if err != nil { + t.Fatal(err) + } + return d +} + +func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") } + +func TestTypedefsMachO(t *testing.T) { + testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho") +} + +func TestTypedefsELFDwarf4(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf4"), "elf") } + +func testTypedefs(t *testing.T, d *Data, kind string) { + r := d.Reader() + seen := make(map[string]bool) + for { + e, err := r.Next() + if err != nil { + t.Fatal("r.Next:", err) + } + if e == nil { + break + } + if e.Tag == TagTypedef { + typ, err := d.Type(e.Offset) + if err != nil { + t.Fatal("d.Type:", err) + } + t1 := typ.(*TypedefType) + var typstr string + if ts, ok := t1.Type.(*StructType); ok { + typstr = ts.Defn() + } else { + typstr = t1.Type.String() + } + + if want, ok := typedefTests[t1.Name]; ok { + if seen[t1.Name] { + t.Errorf("multiple definitions for %s", t1.Name) + } + seen[t1.Name] = true + if typstr != want && (kind != "macho" || typstr != machoBug[want]) { + t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want) + } + } + } + if e.Tag != TagCompileUnit { + r.SkipChildren() + } + } + + for k := range typedefTests { + if !seen[k] { + t.Errorf("missing %s", k) + } + } +} + +func TestTypeForNonTypeEntry(t *testing.T) { + d := elfData(t, "testdata/typedef.elf") + + // The returned entry will be a Subprogram. + ent, err := d.LookupFunction("main") + if err != nil { + t.Fatal("d.LookupFunction:", err) + } + + _, err = d.Type(ent.Offset) + if err == nil { + t.Fatal("nil error for unreadable entry") + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/typeunit.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/typeunit.go new file mode 100644 index 0000000000..eeb99add1e --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/typeunit.go @@ -0,0 +1,181 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf + +import ( + "fmt" + "strconv" +) + +// Parse the type units stored in a DWARF4 .debug_types section. Each +// type unit defines a single primary type and an 8-byte signature. +// Other sections may then use formRefSig8 to refer to the type. + +// The typeUnit format is a single type with a signature. It holds +// the same data as a compilation unit. +type typeUnit struct { + unit + toff Offset // Offset to signature type within data. + name string // Name of .debug_type section. + cache Type // Cache the type, nil to start. +} + +// Parse a .debug_types section. +func (d *Data) parseTypes(name string, types []byte) error { + b := makeBuf(d, unknownFormat{}, name, 0, types) + for len(b.data) > 0 { + base := b.off + dwarf64 := false + n := b.uint32() + if n == 0xffffffff { + n64 := b.uint64() + if n64 != uint64(uint32(n64)) { + b.error("type unit length overflow") + return b.err + } + n = uint32(n64) + dwarf64 = true + } + hdroff := b.off + vers := b.uint16() + if vers != 4 { + b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) + return b.err + } + var ao uint32 + if !dwarf64 { + ao = b.uint32() + } else { + ao64 := b.uint64() + if ao64 != uint64(uint32(ao64)) { + b.error("type unit abbrev offset overflow") + return b.err + } + ao = uint32(ao64) + } + atable, err := d.parseAbbrev(ao) + if err != nil { + return err + } + asize := b.uint8() + sig := b.uint64() + + var toff uint32 + if !dwarf64 { + toff = b.uint32() + } else { + to64 := b.uint64() + if to64 != uint64(uint32(to64)) { + b.error("type unit type offset overflow") + return b.err + } + toff = uint32(to64) + } + + boff := b.off + d.typeSigs[sig] = &typeUnit{ + unit: unit{ + base: base, + off: boff, + data: b.bytes(int(Offset(n) - (b.off - hdroff))), + atable: atable, + asize: int(asize), + vers: int(vers), + is64: dwarf64, + }, + toff: Offset(toff), + name: name, + } + if b.err != nil { + return b.err + } + } + return nil +} + +// Return the type for a type signature. +func (d *Data) sigToType(sig uint64) (Type, error) { + tu := d.typeSigs[sig] + if tu == nil { + return nil, fmt.Errorf("no type unit with signature %v", sig) + } + if tu.cache != nil { + return tu.cache, nil + } + + b := makeBuf(d, tu, tu.name, tu.off, tu.data) + r := &typeUnitReader{d: d, tu: tu, b: b} + t, err := d.readType(tu.name, r, Offset(tu.toff), make(map[Offset]Type)) + if err != nil { + return nil, err + } + + tu.cache = t + return t, nil +} + +// typeUnitReader is a typeReader for a tagTypeUnit. +type typeUnitReader struct { + d *Data + tu *typeUnit + b buf + err error +} + +// Seek to a new position in the type unit. +func (tur *typeUnitReader) Seek(off Offset) { + tur.err = nil + doff := off - tur.tu.off + if doff < 0 || doff >= Offset(len(tur.tu.data)) { + tur.err = fmt.Errorf("%s: offset %d out of range; max %d", tur.tu.name, doff, len(tur.tu.data)) + return + } + tur.b = makeBuf(tur.d, tur.tu, tur.tu.name, off, tur.tu.data[doff:]) +} + +// AddressSize returns the size in bytes of addresses in the current type unit. +func (tur *typeUnitReader) AddressSize() int { + return tur.tu.unit.asize +} + +// Next reads the next Entry from the type unit. +func (tur *typeUnitReader) Next() (*Entry, error) { + if tur.err != nil { + return nil, tur.err + } + if len(tur.tu.data) == 0 { + return nil, nil + } + e := tur.b.entry(tur.tu.atable, tur.tu.base) + if tur.b.err != nil { + tur.err = tur.b.err + return nil, tur.err + } + return e, nil +} + +// clone returns a new reader for the type unit. +func (tur *typeUnitReader) clone() typeReader { + return &typeUnitReader{ + d: tur.d, + tu: tur.tu, + b: makeBuf(tur.d, tur.tu, tur.tu.name, tur.tu.off, tur.tu.data), + } +} + +// offset returns the current offset. +func (tur *typeUnitReader) offset() Offset { + return tur.b.off +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/unit.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/unit.go new file mode 100644 index 0000000000..ddbfd1dc6d --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf/unit.go @@ -0,0 +1,100 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 dwarf + +import "strconv" + +// DWARF debug info is split into a sequence of compilation units. +// Each unit has its own abbreviation table and address size. + +type unit struct { + base Offset // byte offset of header within the aggregate info + off Offset // byte offset of data within the aggregate info + data []byte + atable abbrevTable + asize int + vers int + is64 bool // True for 64-bit DWARF format +} + +// Implement the dataFormat interface. + +func (u *unit) version() int { + return u.vers +} + +func (u *unit) dwarf64() (bool, bool) { + return u.is64, true +} + +func (u *unit) addrsize() int { + return u.asize +} + +func (d *Data) parseUnits() ([]unit, error) { + // Count units. + nunit := 0 + b := makeBuf(d, unknownFormat{}, "info", 0, d.info) + for len(b.data) > 0 { + len := b.uint32() + if len == 0xffffffff { + len64 := b.uint64() + if len64 != uint64(uint32(len64)) { + b.error("unit length overflow") + break + } + len = uint32(len64) + } + b.skip(int(len)) + nunit++ + } + if b.err != nil { + return nil, b.err + } + + // Again, this time writing them down. + b = makeBuf(d, unknownFormat{}, "info", 0, d.info) + units := make([]unit, nunit) + for i := range units { + u := &units[i] + u.base = b.off + n := b.uint32() + if n == 0xffffffff { + u.is64 = true + n = uint32(b.uint64()) + } + vers := b.uint16() + if vers != 2 && vers != 3 && vers != 4 { + b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) + break + } + u.vers = int(vers) + atable, err := d.parseAbbrev(b.uint32()) + if err != nil { + if b.err == nil { + b.err = err + } + break + } + u.atable = atable + u.asize = int(b.uint8()) + u.off = b.off + u.data = b.bytes(int(n - (2 + 4 + 1))) + } + if b.err != nil { + return nil, b.err + } + return units, nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/elf.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/elf.go new file mode 100644 index 0000000000..048ab3b845 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/elf.go @@ -0,0 +1,1521 @@ +/* + * ELF constants and data structures + * + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2018 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package elf // import "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf" + +import "strconv" + +/* + * Constants + */ + +// Indexes into the Header.Ident array. +const ( + EI_CLASS = 4 /* Class of machine. */ + EI_DATA = 5 /* Data format. */ + EI_VERSION = 6 /* ELF format version. */ + EI_OSABI = 7 /* Operating system / ABI identification */ + EI_ABIVERSION = 8 /* ABI version */ + EI_PAD = 9 /* Start of padding (per SVR4 ABI). */ + EI_NIDENT = 16 /* Size of e_ident array. */ +) + +// Initial magic number for ELF files. +const ELFMAG = "\177ELF" + +// Version is found in Header.Ident[EI_VERSION] and Header.Version. +type Version byte + +const ( + EV_NONE Version = 0 + EV_CURRENT Version = 1 +) + +var versionStrings = []intName{ + {0, "EV_NONE"}, + {1, "EV_CURRENT"}, +} + +func (i Version) String() string { return stringName(uint32(i), versionStrings, false) } +func (i Version) GoString() string { return stringName(uint32(i), versionStrings, true) } + +// Class is found in Header.Ident[EI_CLASS] and Header.Class. +type Class byte + +const ( + ELFCLASSNONE Class = 0 /* Unknown class. */ + ELFCLASS32 Class = 1 /* 32-bit architecture. */ + ELFCLASS64 Class = 2 /* 64-bit architecture. */ +) + +var classStrings = []intName{ + {0, "ELFCLASSNONE"}, + {1, "ELFCLASS32"}, + {2, "ELFCLASS64"}, +} + +func (i Class) String() string { return stringName(uint32(i), classStrings, false) } +func (i Class) GoString() string { return stringName(uint32(i), classStrings, true) } + +// Data is found in Header.Ident[EI_DATA] and Header.Data. +type Data byte + +const ( + ELFDATANONE Data = 0 /* Unknown data format. */ + ELFDATA2LSB Data = 1 /* 2's complement little-endian. */ + ELFDATA2MSB Data = 2 /* 2's complement big-endian. */ +) + +var dataStrings = []intName{ + {0, "ELFDATANONE"}, + {1, "ELFDATA2LSB"}, + {2, "ELFDATA2MSB"}, +} + +func (i Data) String() string { return stringName(uint32(i), dataStrings, false) } +func (i Data) GoString() string { return stringName(uint32(i), dataStrings, true) } + +// OSABI is found in Header.Ident[EI_OSABI] and Header.OSABI. +type OSABI byte + +const ( + ELFOSABI_NONE OSABI = 0 /* UNIX System V ABI */ + ELFOSABI_HPUX OSABI = 1 /* HP-UX operating system */ + ELFOSABI_NETBSD OSABI = 2 /* NetBSD */ + ELFOSABI_LINUX OSABI = 3 /* GNU/Linux */ + ELFOSABI_HURD OSABI = 4 /* GNU/Hurd */ + ELFOSABI_86OPEN OSABI = 5 /* 86Open common IA32 ABI */ + ELFOSABI_SOLARIS OSABI = 6 /* Solaris */ + ELFOSABI_AIX OSABI = 7 /* AIX */ + ELFOSABI_IRIX OSABI = 8 /* IRIX */ + ELFOSABI_FREEBSD OSABI = 9 /* FreeBSD */ + ELFOSABI_TRU64 OSABI = 10 /* TRU64 UNIX */ + ELFOSABI_MODESTO OSABI = 11 /* Novell Modesto */ + ELFOSABI_OPENBSD OSABI = 12 /* OpenBSD */ + ELFOSABI_OPENVMS OSABI = 13 /* Open VMS */ + ELFOSABI_NSK OSABI = 14 /* HP Non-Stop Kernel */ + ELFOSABI_ARM OSABI = 97 /* ARM */ + ELFOSABI_STANDALONE OSABI = 255 /* Standalone (embedded) application */ +) + +var osabiStrings = []intName{ + {0, "ELFOSABI_NONE"}, + {1, "ELFOSABI_HPUX"}, + {2, "ELFOSABI_NETBSD"}, + {3, "ELFOSABI_LINUX"}, + {4, "ELFOSABI_HURD"}, + {5, "ELFOSABI_86OPEN"}, + {6, "ELFOSABI_SOLARIS"}, + {7, "ELFOSABI_AIX"}, + {8, "ELFOSABI_IRIX"}, + {9, "ELFOSABI_FREEBSD"}, + {10, "ELFOSABI_TRU64"}, + {11, "ELFOSABI_MODESTO"}, + {12, "ELFOSABI_OPENBSD"}, + {13, "ELFOSABI_OPENVMS"}, + {14, "ELFOSABI_NSK"}, + {97, "ELFOSABI_ARM"}, + {255, "ELFOSABI_STANDALONE"}, +} + +func (i OSABI) String() string { return stringName(uint32(i), osabiStrings, false) } +func (i OSABI) GoString() string { return stringName(uint32(i), osabiStrings, true) } + +// Type is found in Header.Type. +type Type uint16 + +const ( + ET_NONE Type = 0 /* Unknown type. */ + ET_REL Type = 1 /* Relocatable. */ + ET_EXEC Type = 2 /* Executable. */ + ET_DYN Type = 3 /* Shared object. */ + ET_CORE Type = 4 /* Core file. */ + ET_LOOS Type = 0xfe00 /* First operating system specific. */ + ET_HIOS Type = 0xfeff /* Last operating system-specific. */ + ET_LOPROC Type = 0xff00 /* First processor-specific. */ + ET_HIPROC Type = 0xffff /* Last processor-specific. */ +) + +var typeStrings = []intName{ + {0, "ET_NONE"}, + {1, "ET_REL"}, + {2, "ET_EXEC"}, + {3, "ET_DYN"}, + {4, "ET_CORE"}, + {0xfe00, "ET_LOOS"}, + {0xfeff, "ET_HIOS"}, + {0xff00, "ET_LOPROC"}, + {0xffff, "ET_HIPROC"}, +} + +func (i Type) String() string { return stringName(uint32(i), typeStrings, false) } +func (i Type) GoString() string { return stringName(uint32(i), typeStrings, true) } + +// Machine is found in Header.Machine. +type Machine uint16 + +const ( + EM_NONE Machine = 0 /* Unknown machine. */ + EM_M32 Machine = 1 /* AT&T WE32100. */ + EM_SPARC Machine = 2 /* Sun SPARC. */ + EM_386 Machine = 3 /* Intel i386. */ + EM_68K Machine = 4 /* Motorola 68000. */ + EM_88K Machine = 5 /* Motorola 88000. */ + EM_860 Machine = 7 /* Intel i860. */ + EM_MIPS Machine = 8 /* MIPS R3000 Big-Endian only. */ + EM_S370 Machine = 9 /* IBM System/370. */ + EM_MIPS_RS3_LE Machine = 10 /* MIPS R3000 Little-Endian. */ + EM_PARISC Machine = 15 /* HP PA-RISC. */ + EM_VPP500 Machine = 17 /* Fujitsu VPP500. */ + EM_SPARC32PLUS Machine = 18 /* SPARC v8plus. */ + EM_960 Machine = 19 /* Intel 80960. */ + EM_PPC Machine = 20 /* PowerPC 32-bit. */ + EM_PPC64 Machine = 21 /* PowerPC 64-bit. */ + EM_S390 Machine = 22 /* IBM System/390. */ + EM_V800 Machine = 36 /* NEC V800. */ + EM_FR20 Machine = 37 /* Fujitsu FR20. */ + EM_RH32 Machine = 38 /* TRW RH-32. */ + EM_RCE Machine = 39 /* Motorola RCE. */ + EM_ARM Machine = 40 /* ARM. */ + EM_SH Machine = 42 /* Hitachi SH. */ + EM_SPARCV9 Machine = 43 /* SPARC v9 64-bit. */ + EM_TRICORE Machine = 44 /* Siemens TriCore embedded processor. */ + EM_ARC Machine = 45 /* Argonaut RISC Core. */ + EM_H8_300 Machine = 46 /* Hitachi H8/300. */ + EM_H8_300H Machine = 47 /* Hitachi H8/300H. */ + EM_H8S Machine = 48 /* Hitachi H8S. */ + EM_H8_500 Machine = 49 /* Hitachi H8/500. */ + EM_IA_64 Machine = 50 /* Intel IA-64 Processor. */ + EM_MIPS_X Machine = 51 /* Stanford MIPS-X. */ + EM_COLDFIRE Machine = 52 /* Motorola ColdFire. */ + EM_68HC12 Machine = 53 /* Motorola M68HC12. */ + EM_MMA Machine = 54 /* Fujitsu MMA. */ + EM_PCP Machine = 55 /* Siemens PCP. */ + EM_NCPU Machine = 56 /* Sony nCPU. */ + EM_NDR1 Machine = 57 /* Denso NDR1 microprocessor. */ + EM_STARCORE Machine = 58 /* Motorola Star*Core processor. */ + EM_ME16 Machine = 59 /* Toyota ME16 processor. */ + EM_ST100 Machine = 60 /* STMicroelectronics ST100 processor. */ + EM_TINYJ Machine = 61 /* Advanced Logic Corp. TinyJ processor. */ + EM_X86_64 Machine = 62 /* Advanced Micro Devices x86-64 */ + + /* Non-standard or deprecated. */ + EM_486 Machine = 6 /* Intel i486. */ + EM_MIPS_RS4_BE Machine = 10 /* MIPS R4000 Big-Endian */ + EM_ALPHA_STD Machine = 41 /* Digital Alpha (standard value). */ + EM_ALPHA Machine = 0x9026 /* Alpha (written in the absence of an ABI) */ +) + +var machineStrings = []intName{ + {0, "EM_NONE"}, + {1, "EM_M32"}, + {2, "EM_SPARC"}, + {3, "EM_386"}, + {4, "EM_68K"}, + {5, "EM_88K"}, + {7, "EM_860"}, + {8, "EM_MIPS"}, + {9, "EM_S370"}, + {10, "EM_MIPS_RS3_LE"}, + {15, "EM_PARISC"}, + {17, "EM_VPP500"}, + {18, "EM_SPARC32PLUS"}, + {19, "EM_960"}, + {20, "EM_PPC"}, + {21, "EM_PPC64"}, + {22, "EM_S390"}, + {36, "EM_V800"}, + {37, "EM_FR20"}, + {38, "EM_RH32"}, + {39, "EM_RCE"}, + {40, "EM_ARM"}, + {42, "EM_SH"}, + {43, "EM_SPARCV9"}, + {44, "EM_TRICORE"}, + {45, "EM_ARC"}, + {46, "EM_H8_300"}, + {47, "EM_H8_300H"}, + {48, "EM_H8S"}, + {49, "EM_H8_500"}, + {50, "EM_IA_64"}, + {51, "EM_MIPS_X"}, + {52, "EM_COLDFIRE"}, + {53, "EM_68HC12"}, + {54, "EM_MMA"}, + {55, "EM_PCP"}, + {56, "EM_NCPU"}, + {57, "EM_NDR1"}, + {58, "EM_STARCORE"}, + {59, "EM_ME16"}, + {60, "EM_ST100"}, + {61, "EM_TINYJ"}, + {62, "EM_X86_64"}, + + /* Non-standard or deprecated. */ + {6, "EM_486"}, + {10, "EM_MIPS_RS4_BE"}, + {41, "EM_ALPHA_STD"}, + {0x9026, "EM_ALPHA"}, +} + +func (i Machine) String() string { return stringName(uint32(i), machineStrings, false) } +func (i Machine) GoString() string { return stringName(uint32(i), machineStrings, true) } + +// Special section indices. +type SectionIndex int + +const ( + SHN_UNDEF SectionIndex = 0 /* Undefined, missing, irrelevant. */ + SHN_LORESERVE SectionIndex = 0xff00 /* First of reserved range. */ + SHN_LOPROC SectionIndex = 0xff00 /* First processor-specific. */ + SHN_HIPROC SectionIndex = 0xff1f /* Last processor-specific. */ + SHN_LOOS SectionIndex = 0xff20 /* First operating system-specific. */ + SHN_HIOS SectionIndex = 0xff3f /* Last operating system-specific. */ + SHN_ABS SectionIndex = 0xfff1 /* Absolute values. */ + SHN_COMMON SectionIndex = 0xfff2 /* Common data. */ + SHN_XINDEX SectionIndex = 0xffff /* Escape -- index stored elsewhere. */ + SHN_HIRESERVE SectionIndex = 0xffff /* Last of reserved range. */ +) + +var shnStrings = []intName{ + {0, "SHN_UNDEF"}, + {0xff00, "SHN_LOPROC"}, + {0xff20, "SHN_LOOS"}, + {0xfff1, "SHN_ABS"}, + {0xfff2, "SHN_COMMON"}, + {0xffff, "SHN_XINDEX"}, +} + +func (i SectionIndex) String() string { return stringName(uint32(i), shnStrings, false) } +func (i SectionIndex) GoString() string { return stringName(uint32(i), shnStrings, true) } + +// Section type. +type SectionType uint32 + +const ( + SHT_NULL SectionType = 0 /* inactive */ + SHT_PROGBITS SectionType = 1 /* program defined information */ + SHT_SYMTAB SectionType = 2 /* symbol table section */ + SHT_STRTAB SectionType = 3 /* string table section */ + SHT_RELA SectionType = 4 /* relocation section with addends */ + SHT_HASH SectionType = 5 /* symbol hash table section */ + SHT_DYNAMIC SectionType = 6 /* dynamic section */ + SHT_NOTE SectionType = 7 /* note section */ + SHT_NOBITS SectionType = 8 /* no space section */ + SHT_REL SectionType = 9 /* relocation section - no addends */ + SHT_SHLIB SectionType = 10 /* reserved - purpose unknown */ + SHT_DYNSYM SectionType = 11 /* dynamic symbol table section */ + SHT_INIT_ARRAY SectionType = 14 /* Initialization function pointers. */ + SHT_FINI_ARRAY SectionType = 15 /* Termination function pointers. */ + SHT_PREINIT_ARRAY SectionType = 16 /* Pre-initialization function ptrs. */ + SHT_GROUP SectionType = 17 /* Section group. */ + SHT_SYMTAB_SHNDX SectionType = 18 /* Section indexes (see SHN_XINDEX). */ + SHT_LOOS SectionType = 0x60000000 /* First of OS specific semantics */ + SHT_GNU_ATTRIBUTES SectionType = 0x6ffffff5 /* GNU object attributes */ + SHT_GNU_HASH SectionType = 0x6ffffff6 /* GNU hash table */ + SHT_GNU_LIBLIST SectionType = 0x6ffffff7 /* GNU prelink library list */ + SHT_GNU_VERDEF SectionType = 0x6ffffffd /* GNU version definition section */ + SHT_GNU_VERNEED SectionType = 0x6ffffffe /* GNU version needs section */ + SHT_GNU_VERSYM SectionType = 0x6fffffff /* GNU version symbol table */ + SHT_HIOS SectionType = 0x6fffffff /* Last of OS specific semantics */ + SHT_LOPROC SectionType = 0x70000000 /* reserved range for processor */ + SHT_HIPROC SectionType = 0x7fffffff /* specific section header types */ + SHT_LOUSER SectionType = 0x80000000 /* reserved range for application */ + SHT_HIUSER SectionType = 0xffffffff /* specific indexes */ +) + +var shtStrings = []intName{ + {0, "SHT_NULL"}, + {1, "SHT_PROGBITS"}, + {2, "SHT_SYMTAB"}, + {3, "SHT_STRTAB"}, + {4, "SHT_RELA"}, + {5, "SHT_HASH"}, + {6, "SHT_DYNAMIC"}, + {7, "SHT_NOTE"}, + {8, "SHT_NOBITS"}, + {9, "SHT_REL"}, + {10, "SHT_SHLIB"}, + {11, "SHT_DYNSYM"}, + {14, "SHT_INIT_ARRAY"}, + {15, "SHT_FINI_ARRAY"}, + {16, "SHT_PREINIT_ARRAY"}, + {17, "SHT_GROUP"}, + {18, "SHT_SYMTAB_SHNDX"}, + {0x60000000, "SHT_LOOS"}, + {0x6ffffff5, "SHT_GNU_ATTRIBUTES"}, + {0x6ffffff6, "SHT_GNU_HASH"}, + {0x6ffffff7, "SHT_GNU_LIBLIST"}, + {0x6ffffffd, "SHT_GNU_VERDEF"}, + {0x6ffffffe, "SHT_GNU_VERNEED"}, + {0x6fffffff, "SHT_GNU_VERSYM"}, + {0x70000000, "SHT_LOPROC"}, + {0x7fffffff, "SHT_HIPROC"}, + {0x80000000, "SHT_LOUSER"}, + {0xffffffff, "SHT_HIUSER"}, +} + +func (i SectionType) String() string { return stringName(uint32(i), shtStrings, false) } +func (i SectionType) GoString() string { return stringName(uint32(i), shtStrings, true) } + +// Section flags. +type SectionFlag uint32 + +const ( + SHF_WRITE SectionFlag = 0x1 /* Section contains writable data. */ + SHF_ALLOC SectionFlag = 0x2 /* Section occupies memory. */ + SHF_EXECINSTR SectionFlag = 0x4 /* Section contains instructions. */ + SHF_MERGE SectionFlag = 0x10 /* Section may be merged. */ + SHF_STRINGS SectionFlag = 0x20 /* Section contains strings. */ + SHF_INFO_LINK SectionFlag = 0x40 /* sh_info holds section index. */ + SHF_LINK_ORDER SectionFlag = 0x80 /* Special ordering requirements. */ + SHF_OS_NONCONFORMING SectionFlag = 0x100 /* OS-specific processing required. */ + SHF_GROUP SectionFlag = 0x200 /* Member of section group. */ + SHF_TLS SectionFlag = 0x400 /* Section contains TLS data. */ + SHF_MASKOS SectionFlag = 0x0ff00000 /* OS-specific semantics. */ + SHF_MASKPROC SectionFlag = 0xf0000000 /* Processor-specific semantics. */ +) + +var shfStrings = []intName{ + {0x1, "SHF_WRITE"}, + {0x2, "SHF_ALLOC"}, + {0x4, "SHF_EXECINSTR"}, + {0x10, "SHF_MERGE"}, + {0x20, "SHF_STRINGS"}, + {0x40, "SHF_INFO_LINK"}, + {0x80, "SHF_LINK_ORDER"}, + {0x100, "SHF_OS_NONCONFORMING"}, + {0x200, "SHF_GROUP"}, + {0x400, "SHF_TLS"}, +} + +func (i SectionFlag) String() string { return flagName(uint32(i), shfStrings, false) } +func (i SectionFlag) GoString() string { return flagName(uint32(i), shfStrings, true) } + +// Prog.Type +type ProgType int + +const ( + PT_NULL ProgType = 0 /* Unused entry. */ + PT_LOAD ProgType = 1 /* Loadable segment. */ + PT_DYNAMIC ProgType = 2 /* Dynamic linking information segment. */ + PT_INTERP ProgType = 3 /* Pathname of interpreter. */ + PT_NOTE ProgType = 4 /* Auxiliary information. */ + PT_SHLIB ProgType = 5 /* Reserved (not used). */ + PT_PHDR ProgType = 6 /* Location of program header itself. */ + PT_TLS ProgType = 7 /* Thread local storage segment */ + PT_LOOS ProgType = 0x60000000 /* First OS-specific. */ + PT_HIOS ProgType = 0x6fffffff /* Last OS-specific. */ + PT_LOPROC ProgType = 0x70000000 /* First processor-specific type. */ + PT_HIPROC ProgType = 0x7fffffff /* Last processor-specific type. */ +) + +var ptStrings = []intName{ + {0, "PT_NULL"}, + {1, "PT_LOAD"}, + {2, "PT_DYNAMIC"}, + {3, "PT_INTERP"}, + {4, "PT_NOTE"}, + {5, "PT_SHLIB"}, + {6, "PT_PHDR"}, + {7, "PT_TLS"}, + {0x60000000, "PT_LOOS"}, + {0x6fffffff, "PT_HIOS"}, + {0x70000000, "PT_LOPROC"}, + {0x7fffffff, "PT_HIPROC"}, +} + +func (i ProgType) String() string { return stringName(uint32(i), ptStrings, false) } +func (i ProgType) GoString() string { return stringName(uint32(i), ptStrings, true) } + +// Prog.Flag +type ProgFlag uint32 + +const ( + PF_X ProgFlag = 0x1 /* Executable. */ + PF_W ProgFlag = 0x2 /* Writable. */ + PF_R ProgFlag = 0x4 /* Readable. */ + PF_MASKOS ProgFlag = 0x0ff00000 /* Operating system-specific. */ + PF_MASKPROC ProgFlag = 0xf0000000 /* Processor-specific. */ +) + +var pfStrings = []intName{ + {0x1, "PF_X"}, + {0x2, "PF_W"}, + {0x4, "PF_R"}, +} + +func (i ProgFlag) String() string { return flagName(uint32(i), pfStrings, false) } +func (i ProgFlag) GoString() string { return flagName(uint32(i), pfStrings, true) } + +// Dyn.Tag +type DynTag int + +const ( + DT_NULL DynTag = 0 /* Terminating entry. */ + DT_NEEDED DynTag = 1 /* String table offset of a needed shared library. */ + DT_PLTRELSZ DynTag = 2 /* Total size in bytes of PLT relocations. */ + DT_PLTGOT DynTag = 3 /* Processor-dependent address. */ + DT_HASH DynTag = 4 /* Address of symbol hash table. */ + DT_STRTAB DynTag = 5 /* Address of string table. */ + DT_SYMTAB DynTag = 6 /* Address of symbol table. */ + DT_RELA DynTag = 7 /* Address of ElfNN_Rela relocations. */ + DT_RELASZ DynTag = 8 /* Total size of ElfNN_Rela relocations. */ + DT_RELAENT DynTag = 9 /* Size of each ElfNN_Rela relocation entry. */ + DT_STRSZ DynTag = 10 /* Size of string table. */ + DT_SYMENT DynTag = 11 /* Size of each symbol table entry. */ + DT_INIT DynTag = 12 /* Address of initialization function. */ + DT_FINI DynTag = 13 /* Address of finalization function. */ + DT_SONAME DynTag = 14 /* String table offset of shared object name. */ + DT_RPATH DynTag = 15 /* String table offset of library path. [sup] */ + DT_SYMBOLIC DynTag = 16 /* Indicates "symbolic" linking. [sup] */ + DT_REL DynTag = 17 /* Address of ElfNN_Rel relocations. */ + DT_RELSZ DynTag = 18 /* Total size of ElfNN_Rel relocations. */ + DT_RELENT DynTag = 19 /* Size of each ElfNN_Rel relocation. */ + DT_PLTREL DynTag = 20 /* Type of relocation used for PLT. */ + DT_DEBUG DynTag = 21 /* Reserved (not used). */ + DT_TEXTREL DynTag = 22 /* Indicates there may be relocations in non-writable segments. [sup] */ + DT_JMPREL DynTag = 23 /* Address of PLT relocations. */ + DT_BIND_NOW DynTag = 24 /* [sup] */ + DT_INIT_ARRAY DynTag = 25 /* Address of the array of pointers to initialization functions */ + DT_FINI_ARRAY DynTag = 26 /* Address of the array of pointers to termination functions */ + DT_INIT_ARRAYSZ DynTag = 27 /* Size in bytes of the array of initialization functions. */ + DT_FINI_ARRAYSZ DynTag = 28 /* Size in bytes of the array of terminationfunctions. */ + DT_RUNPATH DynTag = 29 /* String table offset of a null-terminated library search path string. */ + DT_FLAGS DynTag = 30 /* Object specific flag values. */ + DT_ENCODING DynTag = 32 /* Values greater than or equal to DT_ENCODING + and less than DT_LOOS follow the rules for + the interpretation of the d_un union + as follows: even == 'd_ptr', even == 'd_val' + or none */ + DT_PREINIT_ARRAY DynTag = 32 /* Address of the array of pointers to pre-initialization functions. */ + DT_PREINIT_ARRAYSZ DynTag = 33 /* Size in bytes of the array of pre-initialization functions. */ + DT_LOOS DynTag = 0x6000000d /* First OS-specific */ + DT_HIOS DynTag = 0x6ffff000 /* Last OS-specific */ + DT_VERSYM DynTag = 0x6ffffff0 + DT_VERNEED DynTag = 0x6ffffffe + DT_VERNEEDNUM DynTag = 0x6fffffff + DT_LOPROC DynTag = 0x70000000 /* First processor-specific type. */ + DT_HIPROC DynTag = 0x7fffffff /* Last processor-specific type. */ +) + +var dtStrings = []intName{ + {0, "DT_NULL"}, + {1, "DT_NEEDED"}, + {2, "DT_PLTRELSZ"}, + {3, "DT_PLTGOT"}, + {4, "DT_HASH"}, + {5, "DT_STRTAB"}, + {6, "DT_SYMTAB"}, + {7, "DT_RELA"}, + {8, "DT_RELASZ"}, + {9, "DT_RELAENT"}, + {10, "DT_STRSZ"}, + {11, "DT_SYMENT"}, + {12, "DT_INIT"}, + {13, "DT_FINI"}, + {14, "DT_SONAME"}, + {15, "DT_RPATH"}, + {16, "DT_SYMBOLIC"}, + {17, "DT_REL"}, + {18, "DT_RELSZ"}, + {19, "DT_RELENT"}, + {20, "DT_PLTREL"}, + {21, "DT_DEBUG"}, + {22, "DT_TEXTREL"}, + {23, "DT_JMPREL"}, + {24, "DT_BIND_NOW"}, + {25, "DT_INIT_ARRAY"}, + {26, "DT_FINI_ARRAY"}, + {27, "DT_INIT_ARRAYSZ"}, + {28, "DT_FINI_ARRAYSZ"}, + {29, "DT_RUNPATH"}, + {30, "DT_FLAGS"}, + {32, "DT_ENCODING"}, + {32, "DT_PREINIT_ARRAY"}, + {33, "DT_PREINIT_ARRAYSZ"}, + {0x6000000d, "DT_LOOS"}, + {0x6ffff000, "DT_HIOS"}, + {0x6ffffff0, "DT_VERSYM"}, + {0x6ffffffe, "DT_VERNEED"}, + {0x6fffffff, "DT_VERNEEDNUM"}, + {0x70000000, "DT_LOPROC"}, + {0x7fffffff, "DT_HIPROC"}, +} + +func (i DynTag) String() string { return stringName(uint32(i), dtStrings, false) } +func (i DynTag) GoString() string { return stringName(uint32(i), dtStrings, true) } + +// DT_FLAGS values. +type DynFlag int + +const ( + DF_ORIGIN DynFlag = 0x0001 /* Indicates that the object being loaded may + make reference to the + $ORIGIN substitution string */ + DF_SYMBOLIC DynFlag = 0x0002 /* Indicates "symbolic" linking. */ + DF_TEXTREL DynFlag = 0x0004 /* Indicates there may be relocations in non-writable segments. */ + DF_BIND_NOW DynFlag = 0x0008 /* Indicates that the dynamic linker should + process all relocations for the object + containing this entry before transferring + control to the program. */ + DF_STATIC_TLS DynFlag = 0x0010 /* Indicates that the shared object or + executable contains code using a static + thread-local storage scheme. */ +) + +var dflagStrings = []intName{ + {0x0001, "DF_ORIGIN"}, + {0x0002, "DF_SYMBOLIC"}, + {0x0004, "DF_TEXTREL"}, + {0x0008, "DF_BIND_NOW"}, + {0x0010, "DF_STATIC_TLS"}, +} + +func (i DynFlag) String() string { return flagName(uint32(i), dflagStrings, false) } +func (i DynFlag) GoString() string { return flagName(uint32(i), dflagStrings, true) } + +// NType values; used in core files. +type NType int + +const ( + NT_PRSTATUS NType = 1 /* Process status. */ + NT_FPREGSET NType = 2 /* Floating point registers. */ + NT_PRPSINFO NType = 3 /* Process state info. */ +) + +var ntypeStrings = []intName{ + {1, "NT_PRSTATUS"}, + {2, "NT_FPREGSET"}, + {3, "NT_PRPSINFO"}, +} + +func (i NType) String() string { return stringName(uint32(i), ntypeStrings, false) } +func (i NType) GoString() string { return stringName(uint32(i), ntypeStrings, true) } + +/* Symbol Binding - ELFNN_ST_BIND - st_info */ +type SymBind int + +const ( + STB_LOCAL SymBind = 0 /* Local symbol */ + STB_GLOBAL SymBind = 1 /* Global symbol */ + STB_WEAK SymBind = 2 /* like global - lower precedence */ + STB_LOOS SymBind = 10 /* Reserved range for operating system */ + STB_HIOS SymBind = 12 /* specific semantics. */ + STB_LOPROC SymBind = 13 /* reserved range for processor */ + STB_HIPROC SymBind = 15 /* specific semantics. */ +) + +var stbStrings = []intName{ + {0, "STB_LOCAL"}, + {1, "STB_GLOBAL"}, + {2, "STB_WEAK"}, + {10, "STB_LOOS"}, + {12, "STB_HIOS"}, + {13, "STB_LOPROC"}, + {15, "STB_HIPROC"}, +} + +func (i SymBind) String() string { return stringName(uint32(i), stbStrings, false) } +func (i SymBind) GoString() string { return stringName(uint32(i), stbStrings, true) } + +/* Symbol type - ELFNN_ST_TYPE - st_info */ +type SymType int + +const ( + STT_NOTYPE SymType = 0 /* Unspecified type. */ + STT_OBJECT SymType = 1 /* Data object. */ + STT_FUNC SymType = 2 /* Function. */ + STT_SECTION SymType = 3 /* Section. */ + STT_FILE SymType = 4 /* Source file. */ + STT_COMMON SymType = 5 /* Uninitialized common block. */ + STT_TLS SymType = 6 /* TLS object. */ + STT_LOOS SymType = 10 /* Reserved range for operating system */ + STT_HIOS SymType = 12 /* specific semantics. */ + STT_LOPROC SymType = 13 /* reserved range for processor */ + STT_HIPROC SymType = 15 /* specific semantics. */ +) + +var sttStrings = []intName{ + {0, "STT_NOTYPE"}, + {1, "STT_OBJECT"}, + {2, "STT_FUNC"}, + {3, "STT_SECTION"}, + {4, "STT_FILE"}, + {5, "STT_COMMON"}, + {6, "STT_TLS"}, + {10, "STT_LOOS"}, + {12, "STT_HIOS"}, + {13, "STT_LOPROC"}, + {15, "STT_HIPROC"}, +} + +func (i SymType) String() string { return stringName(uint32(i), sttStrings, false) } +func (i SymType) GoString() string { return stringName(uint32(i), sttStrings, true) } + +/* Symbol visibility - ELFNN_ST_VISIBILITY - st_other */ +type SymVis int + +const ( + STV_DEFAULT SymVis = 0x0 /* Default visibility (see binding). */ + STV_INTERNAL SymVis = 0x1 /* Special meaning in relocatable objects. */ + STV_HIDDEN SymVis = 0x2 /* Not visible. */ + STV_PROTECTED SymVis = 0x3 /* Visible but not preemptible. */ +) + +var stvStrings = []intName{ + {0x0, "STV_DEFAULT"}, + {0x1, "STV_INTERNAL"}, + {0x2, "STV_HIDDEN"}, + {0x3, "STV_PROTECTED"}, +} + +func (i SymVis) String() string { return stringName(uint32(i), stvStrings, false) } +func (i SymVis) GoString() string { return stringName(uint32(i), stvStrings, true) } + +/* + * Relocation types. + */ + +// Relocation types for x86-64. +type R_X86_64 int + +const ( + R_X86_64_NONE R_X86_64 = 0 /* No relocation. */ + R_X86_64_64 R_X86_64 = 1 /* Add 64 bit symbol value. */ + R_X86_64_PC32 R_X86_64 = 2 /* PC-relative 32 bit signed sym value. */ + R_X86_64_GOT32 R_X86_64 = 3 /* PC-relative 32 bit GOT offset. */ + R_X86_64_PLT32 R_X86_64 = 4 /* PC-relative 32 bit PLT offset. */ + R_X86_64_COPY R_X86_64 = 5 /* Copy data from shared object. */ + R_X86_64_GLOB_DAT R_X86_64 = 6 /* Set GOT entry to data address. */ + R_X86_64_JMP_SLOT R_X86_64 = 7 /* Set GOT entry to code address. */ + R_X86_64_RELATIVE R_X86_64 = 8 /* Add load address of shared object. */ + R_X86_64_GOTPCREL R_X86_64 = 9 /* Add 32 bit signed pcrel offset to GOT. */ + R_X86_64_32 R_X86_64 = 10 /* Add 32 bit zero extended symbol value */ + R_X86_64_32S R_X86_64 = 11 /* Add 32 bit sign extended symbol value */ + R_X86_64_16 R_X86_64 = 12 /* Add 16 bit zero extended symbol value */ + R_X86_64_PC16 R_X86_64 = 13 /* Add 16 bit signed extended pc relative symbol value */ + R_X86_64_8 R_X86_64 = 14 /* Add 8 bit zero extended symbol value */ + R_X86_64_PC8 R_X86_64 = 15 /* Add 8 bit signed extended pc relative symbol value */ + R_X86_64_DTPMOD64 R_X86_64 = 16 /* ID of module containing symbol */ + R_X86_64_DTPOFF64 R_X86_64 = 17 /* Offset in TLS block */ + R_X86_64_TPOFF64 R_X86_64 = 18 /* Offset in static TLS block */ + R_X86_64_TLSGD R_X86_64 = 19 /* PC relative offset to GD GOT entry */ + R_X86_64_TLSLD R_X86_64 = 20 /* PC relative offset to LD GOT entry */ + R_X86_64_DTPOFF32 R_X86_64 = 21 /* Offset in TLS block */ + R_X86_64_GOTTPOFF R_X86_64 = 22 /* PC relative offset to IE GOT entry */ + R_X86_64_TPOFF32 R_X86_64 = 23 /* Offset in static TLS block */ +) + +var rx86_64Strings = []intName{ + {0, "R_X86_64_NONE"}, + {1, "R_X86_64_64"}, + {2, "R_X86_64_PC32"}, + {3, "R_X86_64_GOT32"}, + {4, "R_X86_64_PLT32"}, + {5, "R_X86_64_COPY"}, + {6, "R_X86_64_GLOB_DAT"}, + {7, "R_X86_64_JMP_SLOT"}, + {8, "R_X86_64_RELATIVE"}, + {9, "R_X86_64_GOTPCREL"}, + {10, "R_X86_64_32"}, + {11, "R_X86_64_32S"}, + {12, "R_X86_64_16"}, + {13, "R_X86_64_PC16"}, + {14, "R_X86_64_8"}, + {15, "R_X86_64_PC8"}, + {16, "R_X86_64_DTPMOD64"}, + {17, "R_X86_64_DTPOFF64"}, + {18, "R_X86_64_TPOFF64"}, + {19, "R_X86_64_TLSGD"}, + {20, "R_X86_64_TLSLD"}, + {21, "R_X86_64_DTPOFF32"}, + {22, "R_X86_64_GOTTPOFF"}, + {23, "R_X86_64_TPOFF32"}, +} + +func (i R_X86_64) String() string { return stringName(uint32(i), rx86_64Strings, false) } +func (i R_X86_64) GoString() string { return stringName(uint32(i), rx86_64Strings, true) } + +// Relocation types for Alpha. +type R_ALPHA int + +const ( + R_ALPHA_NONE R_ALPHA = 0 /* No reloc */ + R_ALPHA_REFLONG R_ALPHA = 1 /* Direct 32 bit */ + R_ALPHA_REFQUAD R_ALPHA = 2 /* Direct 64 bit */ + R_ALPHA_GPREL32 R_ALPHA = 3 /* GP relative 32 bit */ + R_ALPHA_LITERAL R_ALPHA = 4 /* GP relative 16 bit w/optimization */ + R_ALPHA_LITUSE R_ALPHA = 5 /* Optimization hint for LITERAL */ + R_ALPHA_GPDISP R_ALPHA = 6 /* Add displacement to GP */ + R_ALPHA_BRADDR R_ALPHA = 7 /* PC+4 relative 23 bit shifted */ + R_ALPHA_HINT R_ALPHA = 8 /* PC+4 relative 16 bit shifted */ + R_ALPHA_SREL16 R_ALPHA = 9 /* PC relative 16 bit */ + R_ALPHA_SREL32 R_ALPHA = 10 /* PC relative 32 bit */ + R_ALPHA_SREL64 R_ALPHA = 11 /* PC relative 64 bit */ + R_ALPHA_OP_PUSH R_ALPHA = 12 /* OP stack push */ + R_ALPHA_OP_STORE R_ALPHA = 13 /* OP stack pop and store */ + R_ALPHA_OP_PSUB R_ALPHA = 14 /* OP stack subtract */ + R_ALPHA_OP_PRSHIFT R_ALPHA = 15 /* OP stack right shift */ + R_ALPHA_GPVALUE R_ALPHA = 16 + R_ALPHA_GPRELHIGH R_ALPHA = 17 + R_ALPHA_GPRELLOW R_ALPHA = 18 + R_ALPHA_IMMED_GP_16 R_ALPHA = 19 + R_ALPHA_IMMED_GP_HI32 R_ALPHA = 20 + R_ALPHA_IMMED_SCN_HI32 R_ALPHA = 21 + R_ALPHA_IMMED_BR_HI32 R_ALPHA = 22 + R_ALPHA_IMMED_LO32 R_ALPHA = 23 + R_ALPHA_COPY R_ALPHA = 24 /* Copy symbol at runtime */ + R_ALPHA_GLOB_DAT R_ALPHA = 25 /* Create GOT entry */ + R_ALPHA_JMP_SLOT R_ALPHA = 26 /* Create PLT entry */ + R_ALPHA_RELATIVE R_ALPHA = 27 /* Adjust by program base */ +) + +var ralphaStrings = []intName{ + {0, "R_ALPHA_NONE"}, + {1, "R_ALPHA_REFLONG"}, + {2, "R_ALPHA_REFQUAD"}, + {3, "R_ALPHA_GPREL32"}, + {4, "R_ALPHA_LITERAL"}, + {5, "R_ALPHA_LITUSE"}, + {6, "R_ALPHA_GPDISP"}, + {7, "R_ALPHA_BRADDR"}, + {8, "R_ALPHA_HINT"}, + {9, "R_ALPHA_SREL16"}, + {10, "R_ALPHA_SREL32"}, + {11, "R_ALPHA_SREL64"}, + {12, "R_ALPHA_OP_PUSH"}, + {13, "R_ALPHA_OP_STORE"}, + {14, "R_ALPHA_OP_PSUB"}, + {15, "R_ALPHA_OP_PRSHIFT"}, + {16, "R_ALPHA_GPVALUE"}, + {17, "R_ALPHA_GPRELHIGH"}, + {18, "R_ALPHA_GPRELLOW"}, + {19, "R_ALPHA_IMMED_GP_16"}, + {20, "R_ALPHA_IMMED_GP_HI32"}, + {21, "R_ALPHA_IMMED_SCN_HI32"}, + {22, "R_ALPHA_IMMED_BR_HI32"}, + {23, "R_ALPHA_IMMED_LO32"}, + {24, "R_ALPHA_COPY"}, + {25, "R_ALPHA_GLOB_DAT"}, + {26, "R_ALPHA_JMP_SLOT"}, + {27, "R_ALPHA_RELATIVE"}, +} + +func (i R_ALPHA) String() string { return stringName(uint32(i), ralphaStrings, false) } +func (i R_ALPHA) GoString() string { return stringName(uint32(i), ralphaStrings, true) } + +// Relocation types for ARM. +type R_ARM int + +const ( + R_ARM_NONE R_ARM = 0 /* No relocation. */ + R_ARM_PC24 R_ARM = 1 + R_ARM_ABS32 R_ARM = 2 + R_ARM_REL32 R_ARM = 3 + R_ARM_PC13 R_ARM = 4 + R_ARM_ABS16 R_ARM = 5 + R_ARM_ABS12 R_ARM = 6 + R_ARM_THM_ABS5 R_ARM = 7 + R_ARM_ABS8 R_ARM = 8 + R_ARM_SBREL32 R_ARM = 9 + R_ARM_THM_PC22 R_ARM = 10 + R_ARM_THM_PC8 R_ARM = 11 + R_ARM_AMP_VCALL9 R_ARM = 12 + R_ARM_SWI24 R_ARM = 13 + R_ARM_THM_SWI8 R_ARM = 14 + R_ARM_XPC25 R_ARM = 15 + R_ARM_THM_XPC22 R_ARM = 16 + R_ARM_COPY R_ARM = 20 /* Copy data from shared object. */ + R_ARM_GLOB_DAT R_ARM = 21 /* Set GOT entry to data address. */ + R_ARM_JUMP_SLOT R_ARM = 22 /* Set GOT entry to code address. */ + R_ARM_RELATIVE R_ARM = 23 /* Add load address of shared object. */ + R_ARM_GOTOFF R_ARM = 24 /* Add GOT-relative symbol address. */ + R_ARM_GOTPC R_ARM = 25 /* Add PC-relative GOT table address. */ + R_ARM_GOT32 R_ARM = 26 /* Add PC-relative GOT offset. */ + R_ARM_PLT32 R_ARM = 27 /* Add PC-relative PLT offset. */ + R_ARM_GNU_VTENTRY R_ARM = 100 + R_ARM_GNU_VTINHERIT R_ARM = 101 + R_ARM_RSBREL32 R_ARM = 250 + R_ARM_THM_RPC22 R_ARM = 251 + R_ARM_RREL32 R_ARM = 252 + R_ARM_RABS32 R_ARM = 253 + R_ARM_RPC24 R_ARM = 254 + R_ARM_RBASE R_ARM = 255 +) + +var rarmStrings = []intName{ + {0, "R_ARM_NONE"}, + {1, "R_ARM_PC24"}, + {2, "R_ARM_ABS32"}, + {3, "R_ARM_REL32"}, + {4, "R_ARM_PC13"}, + {5, "R_ARM_ABS16"}, + {6, "R_ARM_ABS12"}, + {7, "R_ARM_THM_ABS5"}, + {8, "R_ARM_ABS8"}, + {9, "R_ARM_SBREL32"}, + {10, "R_ARM_THM_PC22"}, + {11, "R_ARM_THM_PC8"}, + {12, "R_ARM_AMP_VCALL9"}, + {13, "R_ARM_SWI24"}, + {14, "R_ARM_THM_SWI8"}, + {15, "R_ARM_XPC25"}, + {16, "R_ARM_THM_XPC22"}, + {20, "R_ARM_COPY"}, + {21, "R_ARM_GLOB_DAT"}, + {22, "R_ARM_JUMP_SLOT"}, + {23, "R_ARM_RELATIVE"}, + {24, "R_ARM_GOTOFF"}, + {25, "R_ARM_GOTPC"}, + {26, "R_ARM_GOT32"}, + {27, "R_ARM_PLT32"}, + {100, "R_ARM_GNU_VTENTRY"}, + {101, "R_ARM_GNU_VTINHERIT"}, + {250, "R_ARM_RSBREL32"}, + {251, "R_ARM_THM_RPC22"}, + {252, "R_ARM_RREL32"}, + {253, "R_ARM_RABS32"}, + {254, "R_ARM_RPC24"}, + {255, "R_ARM_RBASE"}, +} + +func (i R_ARM) String() string { return stringName(uint32(i), rarmStrings, false) } +func (i R_ARM) GoString() string { return stringName(uint32(i), rarmStrings, true) } + +// Relocation types for 386. +type R_386 int + +const ( + R_386_NONE R_386 = 0 /* No relocation. */ + R_386_32 R_386 = 1 /* Add symbol value. */ + R_386_PC32 R_386 = 2 /* Add PC-relative symbol value. */ + R_386_GOT32 R_386 = 3 /* Add PC-relative GOT offset. */ + R_386_PLT32 R_386 = 4 /* Add PC-relative PLT offset. */ + R_386_COPY R_386 = 5 /* Copy data from shared object. */ + R_386_GLOB_DAT R_386 = 6 /* Set GOT entry to data address. */ + R_386_JMP_SLOT R_386 = 7 /* Set GOT entry to code address. */ + R_386_RELATIVE R_386 = 8 /* Add load address of shared object. */ + R_386_GOTOFF R_386 = 9 /* Add GOT-relative symbol address. */ + R_386_GOTPC R_386 = 10 /* Add PC-relative GOT table address. */ + R_386_TLS_TPOFF R_386 = 14 /* Negative offset in static TLS block */ + R_386_TLS_IE R_386 = 15 /* Absolute address of GOT for -ve static TLS */ + R_386_TLS_GOTIE R_386 = 16 /* GOT entry for negative static TLS block */ + R_386_TLS_LE R_386 = 17 /* Negative offset relative to static TLS */ + R_386_TLS_GD R_386 = 18 /* 32 bit offset to GOT (index,off) pair */ + R_386_TLS_LDM R_386 = 19 /* 32 bit offset to GOT (index,zero) pair */ + R_386_TLS_GD_32 R_386 = 24 /* 32 bit offset to GOT (index,off) pair */ + R_386_TLS_GD_PUSH R_386 = 25 /* pushl instruction for Sun ABI GD sequence */ + R_386_TLS_GD_CALL R_386 = 26 /* call instruction for Sun ABI GD sequence */ + R_386_TLS_GD_POP R_386 = 27 /* popl instruction for Sun ABI GD sequence */ + R_386_TLS_LDM_32 R_386 = 28 /* 32 bit offset to GOT (index,zero) pair */ + R_386_TLS_LDM_PUSH R_386 = 29 /* pushl instruction for Sun ABI LD sequence */ + R_386_TLS_LDM_CALL R_386 = 30 /* call instruction for Sun ABI LD sequence */ + R_386_TLS_LDM_POP R_386 = 31 /* popl instruction for Sun ABI LD sequence */ + R_386_TLS_LDO_32 R_386 = 32 /* 32 bit offset from start of TLS block */ + R_386_TLS_IE_32 R_386 = 33 /* 32 bit offset to GOT static TLS offset entry */ + R_386_TLS_LE_32 R_386 = 34 /* 32 bit offset within static TLS block */ + R_386_TLS_DTPMOD32 R_386 = 35 /* GOT entry containing TLS index */ + R_386_TLS_DTPOFF32 R_386 = 36 /* GOT entry containing TLS offset */ + R_386_TLS_TPOFF32 R_386 = 37 /* GOT entry of -ve static TLS offset */ +) + +var r386Strings = []intName{ + {0, "R_386_NONE"}, + {1, "R_386_32"}, + {2, "R_386_PC32"}, + {3, "R_386_GOT32"}, + {4, "R_386_PLT32"}, + {5, "R_386_COPY"}, + {6, "R_386_GLOB_DAT"}, + {7, "R_386_JMP_SLOT"}, + {8, "R_386_RELATIVE"}, + {9, "R_386_GOTOFF"}, + {10, "R_386_GOTPC"}, + {14, "R_386_TLS_TPOFF"}, + {15, "R_386_TLS_IE"}, + {16, "R_386_TLS_GOTIE"}, + {17, "R_386_TLS_LE"}, + {18, "R_386_TLS_GD"}, + {19, "R_386_TLS_LDM"}, + {24, "R_386_TLS_GD_32"}, + {25, "R_386_TLS_GD_PUSH"}, + {26, "R_386_TLS_GD_CALL"}, + {27, "R_386_TLS_GD_POP"}, + {28, "R_386_TLS_LDM_32"}, + {29, "R_386_TLS_LDM_PUSH"}, + {30, "R_386_TLS_LDM_CALL"}, + {31, "R_386_TLS_LDM_POP"}, + {32, "R_386_TLS_LDO_32"}, + {33, "R_386_TLS_IE_32"}, + {34, "R_386_TLS_LE_32"}, + {35, "R_386_TLS_DTPMOD32"}, + {36, "R_386_TLS_DTPOFF32"}, + {37, "R_386_TLS_TPOFF32"}, +} + +func (i R_386) String() string { return stringName(uint32(i), r386Strings, false) } +func (i R_386) GoString() string { return stringName(uint32(i), r386Strings, true) } + +// Relocation types for PowerPC. +type R_PPC int + +const ( + R_PPC_NONE R_PPC = 0 /* No relocation. */ + R_PPC_ADDR32 R_PPC = 1 + R_PPC_ADDR24 R_PPC = 2 + R_PPC_ADDR16 R_PPC = 3 + R_PPC_ADDR16_LO R_PPC = 4 + R_PPC_ADDR16_HI R_PPC = 5 + R_PPC_ADDR16_HA R_PPC = 6 + R_PPC_ADDR14 R_PPC = 7 + R_PPC_ADDR14_BRTAKEN R_PPC = 8 + R_PPC_ADDR14_BRNTAKEN R_PPC = 9 + R_PPC_REL24 R_PPC = 10 + R_PPC_REL14 R_PPC = 11 + R_PPC_REL14_BRTAKEN R_PPC = 12 + R_PPC_REL14_BRNTAKEN R_PPC = 13 + R_PPC_GOT16 R_PPC = 14 + R_PPC_GOT16_LO R_PPC = 15 + R_PPC_GOT16_HI R_PPC = 16 + R_PPC_GOT16_HA R_PPC = 17 + R_PPC_PLTREL24 R_PPC = 18 + R_PPC_COPY R_PPC = 19 + R_PPC_GLOB_DAT R_PPC = 20 + R_PPC_JMP_SLOT R_PPC = 21 + R_PPC_RELATIVE R_PPC = 22 + R_PPC_LOCAL24PC R_PPC = 23 + R_PPC_UADDR32 R_PPC = 24 + R_PPC_UADDR16 R_PPC = 25 + R_PPC_REL32 R_PPC = 26 + R_PPC_PLT32 R_PPC = 27 + R_PPC_PLTREL32 R_PPC = 28 + R_PPC_PLT16_LO R_PPC = 29 + R_PPC_PLT16_HI R_PPC = 30 + R_PPC_PLT16_HA R_PPC = 31 + R_PPC_SDAREL16 R_PPC = 32 + R_PPC_SECTOFF R_PPC = 33 + R_PPC_SECTOFF_LO R_PPC = 34 + R_PPC_SECTOFF_HI R_PPC = 35 + R_PPC_SECTOFF_HA R_PPC = 36 + R_PPC_TLS R_PPC = 67 + R_PPC_DTPMOD32 R_PPC = 68 + R_PPC_TPREL16 R_PPC = 69 + R_PPC_TPREL16_LO R_PPC = 70 + R_PPC_TPREL16_HI R_PPC = 71 + R_PPC_TPREL16_HA R_PPC = 72 + R_PPC_TPREL32 R_PPC = 73 + R_PPC_DTPREL16 R_PPC = 74 + R_PPC_DTPREL16_LO R_PPC = 75 + R_PPC_DTPREL16_HI R_PPC = 76 + R_PPC_DTPREL16_HA R_PPC = 77 + R_PPC_DTPREL32 R_PPC = 78 + R_PPC_GOT_TLSGD16 R_PPC = 79 + R_PPC_GOT_TLSGD16_LO R_PPC = 80 + R_PPC_GOT_TLSGD16_HI R_PPC = 81 + R_PPC_GOT_TLSGD16_HA R_PPC = 82 + R_PPC_GOT_TLSLD16 R_PPC = 83 + R_PPC_GOT_TLSLD16_LO R_PPC = 84 + R_PPC_GOT_TLSLD16_HI R_PPC = 85 + R_PPC_GOT_TLSLD16_HA R_PPC = 86 + R_PPC_GOT_TPREL16 R_PPC = 87 + R_PPC_GOT_TPREL16_LO R_PPC = 88 + R_PPC_GOT_TPREL16_HI R_PPC = 89 + R_PPC_GOT_TPREL16_HA R_PPC = 90 + R_PPC_EMB_NADDR32 R_PPC = 101 + R_PPC_EMB_NADDR16 R_PPC = 102 + R_PPC_EMB_NADDR16_LO R_PPC = 103 + R_PPC_EMB_NADDR16_HI R_PPC = 104 + R_PPC_EMB_NADDR16_HA R_PPC = 105 + R_PPC_EMB_SDAI16 R_PPC = 106 + R_PPC_EMB_SDA2I16 R_PPC = 107 + R_PPC_EMB_SDA2REL R_PPC = 108 + R_PPC_EMB_SDA21 R_PPC = 109 + R_PPC_EMB_MRKREF R_PPC = 110 + R_PPC_EMB_RELSEC16 R_PPC = 111 + R_PPC_EMB_RELST_LO R_PPC = 112 + R_PPC_EMB_RELST_HI R_PPC = 113 + R_PPC_EMB_RELST_HA R_PPC = 114 + R_PPC_EMB_BIT_FLD R_PPC = 115 + R_PPC_EMB_RELSDA R_PPC = 116 +) + +var rppcStrings = []intName{ + {0, "R_PPC_NONE"}, + {1, "R_PPC_ADDR32"}, + {2, "R_PPC_ADDR24"}, + {3, "R_PPC_ADDR16"}, + {4, "R_PPC_ADDR16_LO"}, + {5, "R_PPC_ADDR16_HI"}, + {6, "R_PPC_ADDR16_HA"}, + {7, "R_PPC_ADDR14"}, + {8, "R_PPC_ADDR14_BRTAKEN"}, + {9, "R_PPC_ADDR14_BRNTAKEN"}, + {10, "R_PPC_REL24"}, + {11, "R_PPC_REL14"}, + {12, "R_PPC_REL14_BRTAKEN"}, + {13, "R_PPC_REL14_BRNTAKEN"}, + {14, "R_PPC_GOT16"}, + {15, "R_PPC_GOT16_LO"}, + {16, "R_PPC_GOT16_HI"}, + {17, "R_PPC_GOT16_HA"}, + {18, "R_PPC_PLTREL24"}, + {19, "R_PPC_COPY"}, + {20, "R_PPC_GLOB_DAT"}, + {21, "R_PPC_JMP_SLOT"}, + {22, "R_PPC_RELATIVE"}, + {23, "R_PPC_LOCAL24PC"}, + {24, "R_PPC_UADDR32"}, + {25, "R_PPC_UADDR16"}, + {26, "R_PPC_REL32"}, + {27, "R_PPC_PLT32"}, + {28, "R_PPC_PLTREL32"}, + {29, "R_PPC_PLT16_LO"}, + {30, "R_PPC_PLT16_HI"}, + {31, "R_PPC_PLT16_HA"}, + {32, "R_PPC_SDAREL16"}, + {33, "R_PPC_SECTOFF"}, + {34, "R_PPC_SECTOFF_LO"}, + {35, "R_PPC_SECTOFF_HI"}, + {36, "R_PPC_SECTOFF_HA"}, + + {67, "R_PPC_TLS"}, + {68, "R_PPC_DTPMOD32"}, + {69, "R_PPC_TPREL16"}, + {70, "R_PPC_TPREL16_LO"}, + {71, "R_PPC_TPREL16_HI"}, + {72, "R_PPC_TPREL16_HA"}, + {73, "R_PPC_TPREL32"}, + {74, "R_PPC_DTPREL16"}, + {75, "R_PPC_DTPREL16_LO"}, + {76, "R_PPC_DTPREL16_HI"}, + {77, "R_PPC_DTPREL16_HA"}, + {78, "R_PPC_DTPREL32"}, + {79, "R_PPC_GOT_TLSGD16"}, + {80, "R_PPC_GOT_TLSGD16_LO"}, + {81, "R_PPC_GOT_TLSGD16_HI"}, + {82, "R_PPC_GOT_TLSGD16_HA"}, + {83, "R_PPC_GOT_TLSLD16"}, + {84, "R_PPC_GOT_TLSLD16_LO"}, + {85, "R_PPC_GOT_TLSLD16_HI"}, + {86, "R_PPC_GOT_TLSLD16_HA"}, + {87, "R_PPC_GOT_TPREL16"}, + {88, "R_PPC_GOT_TPREL16_LO"}, + {89, "R_PPC_GOT_TPREL16_HI"}, + {90, "R_PPC_GOT_TPREL16_HA"}, + + {101, "R_PPC_EMB_NADDR32"}, + {102, "R_PPC_EMB_NADDR16"}, + {103, "R_PPC_EMB_NADDR16_LO"}, + {104, "R_PPC_EMB_NADDR16_HI"}, + {105, "R_PPC_EMB_NADDR16_HA"}, + {106, "R_PPC_EMB_SDAI16"}, + {107, "R_PPC_EMB_SDA2I16"}, + {108, "R_PPC_EMB_SDA2REL"}, + {109, "R_PPC_EMB_SDA21"}, + {110, "R_PPC_EMB_MRKREF"}, + {111, "R_PPC_EMB_RELSEC16"}, + {112, "R_PPC_EMB_RELST_LO"}, + {113, "R_PPC_EMB_RELST_HI"}, + {114, "R_PPC_EMB_RELST_HA"}, + {115, "R_PPC_EMB_BIT_FLD"}, + {116, "R_PPC_EMB_RELSDA"}, +} + +func (i R_PPC) String() string { return stringName(uint32(i), rppcStrings, false) } +func (i R_PPC) GoString() string { return stringName(uint32(i), rppcStrings, true) } + +// Relocation types for SPARC. +type R_SPARC int + +const ( + R_SPARC_NONE R_SPARC = 0 + R_SPARC_8 R_SPARC = 1 + R_SPARC_16 R_SPARC = 2 + R_SPARC_32 R_SPARC = 3 + R_SPARC_DISP8 R_SPARC = 4 + R_SPARC_DISP16 R_SPARC = 5 + R_SPARC_DISP32 R_SPARC = 6 + R_SPARC_WDISP30 R_SPARC = 7 + R_SPARC_WDISP22 R_SPARC = 8 + R_SPARC_HI22 R_SPARC = 9 + R_SPARC_22 R_SPARC = 10 + R_SPARC_13 R_SPARC = 11 + R_SPARC_LO10 R_SPARC = 12 + R_SPARC_GOT10 R_SPARC = 13 + R_SPARC_GOT13 R_SPARC = 14 + R_SPARC_GOT22 R_SPARC = 15 + R_SPARC_PC10 R_SPARC = 16 + R_SPARC_PC22 R_SPARC = 17 + R_SPARC_WPLT30 R_SPARC = 18 + R_SPARC_COPY R_SPARC = 19 + R_SPARC_GLOB_DAT R_SPARC = 20 + R_SPARC_JMP_SLOT R_SPARC = 21 + R_SPARC_RELATIVE R_SPARC = 22 + R_SPARC_UA32 R_SPARC = 23 + R_SPARC_PLT32 R_SPARC = 24 + R_SPARC_HIPLT22 R_SPARC = 25 + R_SPARC_LOPLT10 R_SPARC = 26 + R_SPARC_PCPLT32 R_SPARC = 27 + R_SPARC_PCPLT22 R_SPARC = 28 + R_SPARC_PCPLT10 R_SPARC = 29 + R_SPARC_10 R_SPARC = 30 + R_SPARC_11 R_SPARC = 31 + R_SPARC_64 R_SPARC = 32 + R_SPARC_OLO10 R_SPARC = 33 + R_SPARC_HH22 R_SPARC = 34 + R_SPARC_HM10 R_SPARC = 35 + R_SPARC_LM22 R_SPARC = 36 + R_SPARC_PC_HH22 R_SPARC = 37 + R_SPARC_PC_HM10 R_SPARC = 38 + R_SPARC_PC_LM22 R_SPARC = 39 + R_SPARC_WDISP16 R_SPARC = 40 + R_SPARC_WDISP19 R_SPARC = 41 + R_SPARC_GLOB_JMP R_SPARC = 42 + R_SPARC_7 R_SPARC = 43 + R_SPARC_5 R_SPARC = 44 + R_SPARC_6 R_SPARC = 45 + R_SPARC_DISP64 R_SPARC = 46 + R_SPARC_PLT64 R_SPARC = 47 + R_SPARC_HIX22 R_SPARC = 48 + R_SPARC_LOX10 R_SPARC = 49 + R_SPARC_H44 R_SPARC = 50 + R_SPARC_M44 R_SPARC = 51 + R_SPARC_L44 R_SPARC = 52 + R_SPARC_REGISTER R_SPARC = 53 + R_SPARC_UA64 R_SPARC = 54 + R_SPARC_UA16 R_SPARC = 55 +) + +var rsparcStrings = []intName{ + {0, "R_SPARC_NONE"}, + {1, "R_SPARC_8"}, + {2, "R_SPARC_16"}, + {3, "R_SPARC_32"}, + {4, "R_SPARC_DISP8"}, + {5, "R_SPARC_DISP16"}, + {6, "R_SPARC_DISP32"}, + {7, "R_SPARC_WDISP30"}, + {8, "R_SPARC_WDISP22"}, + {9, "R_SPARC_HI22"}, + {10, "R_SPARC_22"}, + {11, "R_SPARC_13"}, + {12, "R_SPARC_LO10"}, + {13, "R_SPARC_GOT10"}, + {14, "R_SPARC_GOT13"}, + {15, "R_SPARC_GOT22"}, + {16, "R_SPARC_PC10"}, + {17, "R_SPARC_PC22"}, + {18, "R_SPARC_WPLT30"}, + {19, "R_SPARC_COPY"}, + {20, "R_SPARC_GLOB_DAT"}, + {21, "R_SPARC_JMP_SLOT"}, + {22, "R_SPARC_RELATIVE"}, + {23, "R_SPARC_UA32"}, + {24, "R_SPARC_PLT32"}, + {25, "R_SPARC_HIPLT22"}, + {26, "R_SPARC_LOPLT10"}, + {27, "R_SPARC_PCPLT32"}, + {28, "R_SPARC_PCPLT22"}, + {29, "R_SPARC_PCPLT10"}, + {30, "R_SPARC_10"}, + {31, "R_SPARC_11"}, + {32, "R_SPARC_64"}, + {33, "R_SPARC_OLO10"}, + {34, "R_SPARC_HH22"}, + {35, "R_SPARC_HM10"}, + {36, "R_SPARC_LM22"}, + {37, "R_SPARC_PC_HH22"}, + {38, "R_SPARC_PC_HM10"}, + {39, "R_SPARC_PC_LM22"}, + {40, "R_SPARC_WDISP16"}, + {41, "R_SPARC_WDISP19"}, + {42, "R_SPARC_GLOB_JMP"}, + {43, "R_SPARC_7"}, + {44, "R_SPARC_5"}, + {45, "R_SPARC_6"}, + {46, "R_SPARC_DISP64"}, + {47, "R_SPARC_PLT64"}, + {48, "R_SPARC_HIX22"}, + {49, "R_SPARC_LOX10"}, + {50, "R_SPARC_H44"}, + {51, "R_SPARC_M44"}, + {52, "R_SPARC_L44"}, + {53, "R_SPARC_REGISTER"}, + {54, "R_SPARC_UA64"}, + {55, "R_SPARC_UA16"}, +} + +func (i R_SPARC) String() string { return stringName(uint32(i), rsparcStrings, false) } +func (i R_SPARC) GoString() string { return stringName(uint32(i), rsparcStrings, true) } + +// Magic number for the elf trampoline, chosen wisely to be an immediate value. +const ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 + +// ELF32 File header. +type Header32 struct { + Ident [EI_NIDENT]byte /* File identification. */ + Type uint16 /* File type. */ + Machine uint16 /* Machine architecture. */ + Version uint32 /* ELF format version. */ + Entry uint32 /* Entry point. */ + Phoff uint32 /* Program header file offset. */ + Shoff uint32 /* Section header file offset. */ + Flags uint32 /* Architecture-specific flags. */ + Ehsize uint16 /* Size of ELF header in bytes. */ + Phentsize uint16 /* Size of program header entry. */ + Phnum uint16 /* Number of program header entries. */ + Shentsize uint16 /* Size of section header entry. */ + Shnum uint16 /* Number of section header entries. */ + Shstrndx uint16 /* Section name strings section. */ +} + +// ELF32 Section header. +type Section32 struct { + Name uint32 /* Section name (index into the section header string table). */ + Type uint32 /* Section type. */ + Flags uint32 /* Section flags. */ + Addr uint32 /* Address in memory image. */ + Off uint32 /* Offset in file. */ + Size uint32 /* Size in bytes. */ + Link uint32 /* Index of a related section. */ + Info uint32 /* Depends on section type. */ + Addralign uint32 /* Alignment in bytes. */ + Entsize uint32 /* Size of each entry in section. */ +} + +// ELF32 Program header. +type Prog32 struct { + Type uint32 /* Entry type. */ + Off uint32 /* File offset of contents. */ + Vaddr uint32 /* Virtual address in memory image. */ + Paddr uint32 /* Physical address (not used). */ + Filesz uint32 /* Size of contents in file. */ + Memsz uint32 /* Size of contents in memory. */ + Flags uint32 /* Access permission flags. */ + Align uint32 /* Alignment in memory and file. */ +} + +// ELF32 Dynamic structure. The ".dynamic" section contains an array of them. +type Dyn32 struct { + Tag int32 /* Entry type. */ + Val uint32 /* Integer/Address value. */ +} + +/* + * Relocation entries. + */ + +// ELF32 Relocations that don't need an addend field. +type Rel32 struct { + Off uint32 /* Location to be relocated. */ + Info uint32 /* Relocation type and symbol index. */ +} + +// ELF32 Relocations that need an addend field. +type Rela32 struct { + Off uint32 /* Location to be relocated. */ + Info uint32 /* Relocation type and symbol index. */ + Addend int32 /* Addend. */ +} + +func R_SYM32(info uint32) uint32 { return uint32(info >> 8) } +func R_TYPE32(info uint32) uint32 { return uint32(info & 0xff) } +func R_INFO32(sym, typ uint32) uint32 { return sym<<8 | typ } + +// ELF32 Symbol. +type Sym32 struct { + Name uint32 + Value uint32 + Size uint32 + Info uint8 + Other uint8 + Shndx uint16 +} + +const Sym32Size = 16 + +func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) } +func ST_TYPE(info uint8) SymType { return SymType(info & 0xF) } +func ST_INFO(bind SymBind, typ SymType) uint8 { + return uint8(bind)<<4 | uint8(typ)&0xf +} +func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) } + +/* + * ELF64 + */ + +// ELF64 file header. +type Header64 struct { + Ident [EI_NIDENT]byte /* File identification. */ + Type uint16 /* File type. */ + Machine uint16 /* Machine architecture. */ + Version uint32 /* ELF format version. */ + Entry uint64 /* Entry point. */ + Phoff uint64 /* Program header file offset. */ + Shoff uint64 /* Section header file offset. */ + Flags uint32 /* Architecture-specific flags. */ + Ehsize uint16 /* Size of ELF header in bytes. */ + Phentsize uint16 /* Size of program header entry. */ + Phnum uint16 /* Number of program header entries. */ + Shentsize uint16 /* Size of section header entry. */ + Shnum uint16 /* Number of section header entries. */ + Shstrndx uint16 /* Section name strings section. */ +} + +// ELF64 Section header. +type Section64 struct { + Name uint32 /* Section name (index into the section header string table). */ + Type uint32 /* Section type. */ + Flags uint64 /* Section flags. */ + Addr uint64 /* Address in memory image. */ + Off uint64 /* Offset in file. */ + Size uint64 /* Size in bytes. */ + Link uint32 /* Index of a related section. */ + Info uint32 /* Depends on section type. */ + Addralign uint64 /* Alignment in bytes. */ + Entsize uint64 /* Size of each entry in section. */ +} + +// ELF64 Program header. +type Prog64 struct { + Type uint32 /* Entry type. */ + Flags uint32 /* Access permission flags. */ + Off uint64 /* File offset of contents. */ + Vaddr uint64 /* Virtual address in memory image. */ + Paddr uint64 /* Physical address (not used). */ + Filesz uint64 /* Size of contents in file. */ + Memsz uint64 /* Size of contents in memory. */ + Align uint64 /* Alignment in memory and file. */ +} + +// ELF64 Dynamic structure. The ".dynamic" section contains an array of them. +type Dyn64 struct { + Tag int64 /* Entry type. */ + Val uint64 /* Integer/address value */ +} + +/* + * Relocation entries. + */ + +/* ELF64 relocations that don't need an addend field. */ +type Rel64 struct { + Off uint64 /* Location to be relocated. */ + Info uint64 /* Relocation type and symbol index. */ +} + +/* ELF64 relocations that need an addend field. */ +type Rela64 struct { + Off uint64 /* Location to be relocated. */ + Info uint64 /* Relocation type and symbol index. */ + Addend int64 /* Addend. */ +} + +func R_SYM64(info uint64) uint32 { return uint32(info >> 32) } +func R_TYPE64(info uint64) uint32 { return uint32(info) } +func R_INFO(sym, typ uint32) uint64 { return uint64(sym)<<32 | uint64(typ) } + +// ELF64 symbol table entries. +type Sym64 struct { + Name uint32 /* String table index of name. */ + Info uint8 /* Type and binding information. */ + Other uint8 /* Reserved (not used). */ + Shndx uint16 /* Section index of symbol. */ + Value uint64 /* Symbol value. */ + Size uint64 /* Size of associated object. */ +} + +const Sym64Size = 24 + +type intName struct { + i uint32 + s string +} + +func stringName(i uint32, names []intName, goSyntax bool) string { + for _, n := range names { + if n.i == i { + if goSyntax { + return "elf." + n.s + } + return n.s + } + } + + // second pass - look for smaller to add with. + // assume sorted already + for j := len(names) - 1; j >= 0; j-- { + n := names[j] + if n.i < i { + s := n.s + if goSyntax { + s = "elf." + s + } + return s + "+" + strconv.FormatUint(uint64(i-n.i), 10) + } + } + + return strconv.FormatUint(uint64(i), 10) +} + +func flagName(i uint32, names []intName, goSyntax bool) string { + s := "" + for _, n := range names { + if n.i&i == n.i { + if len(s) > 0 { + s += "+" + } + if goSyntax { + s += "elf." + } + s += n.s + i -= n.i + } + } + if len(s) == 0 { + return "0x" + strconv.FormatUint(uint64(i), 16) + } + if i != 0 { + s += "+0x" + strconv.FormatUint(uint64(i), 16) + } + return s +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/elf_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/elf_test.go new file mode 100644 index 0000000000..da761c7b31 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/elf_test.go @@ -0,0 +1,59 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 elf + +import ( + "fmt" + "testing" +) + +type nameTest struct { + val interface{} + str string +} + +var nameTests = []nameTest{ + {ELFOSABI_LINUX, "ELFOSABI_LINUX"}, + {ET_EXEC, "ET_EXEC"}, + {EM_860, "EM_860"}, + {SHN_LOPROC, "SHN_LOPROC"}, + {SHT_PROGBITS, "SHT_PROGBITS"}, + {SHF_MERGE + SHF_TLS, "SHF_MERGE+SHF_TLS"}, + {PT_LOAD, "PT_LOAD"}, + {PF_W + PF_R + 0x50, "PF_W+PF_R+0x50"}, + {DT_SYMBOLIC, "DT_SYMBOLIC"}, + {DF_BIND_NOW, "DF_BIND_NOW"}, + {NT_FPREGSET, "NT_FPREGSET"}, + {STB_GLOBAL, "STB_GLOBAL"}, + {STT_COMMON, "STT_COMMON"}, + {STV_HIDDEN, "STV_HIDDEN"}, + {R_X86_64_PC32, "R_X86_64_PC32"}, + {R_ALPHA_OP_PUSH, "R_ALPHA_OP_PUSH"}, + {R_ARM_THM_ABS5, "R_ARM_THM_ABS5"}, + {R_386_GOT32, "R_386_GOT32"}, + {R_PPC_GOT16_HI, "R_PPC_GOT16_HI"}, + {R_SPARC_GOT22, "R_SPARC_GOT22"}, + {ET_LOOS + 5, "ET_LOOS+5"}, + {ProgFlag(0x50), "0x50"}, +} + +func TestNames(t *testing.T) { + for i, tt := range nameTests { + s := fmt.Sprint(tt.val) + if s != tt.str { + t.Errorf("#%d: Sprint(%d) = %q, want %q", i, tt.val, s, tt.str) + } + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/file.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/file.go new file mode 100644 index 0000000000..c0dc9e2088 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/file.go @@ -0,0 +1,839 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 elf implements access to ELF object files. +package elf + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "os" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +// TODO: error reporting detail + +/* + * Internal ELF representation + */ + +// A FileHeader represents an ELF file header. +type FileHeader struct { + Class Class + Data Data + Version Version + OSABI OSABI + ABIVersion uint8 + ByteOrder binary.ByteOrder + Type Type + Machine Machine + Entry uint64 +} + +// A File represents an open ELF file. +type File struct { + FileHeader + Sections []*Section + Progs []*Prog + closer io.Closer + gnuNeed []verneed + gnuVersym []byte +} + +// A SectionHeader represents a single ELF section header. +type SectionHeader struct { + Name string + Type SectionType + Flags SectionFlag + Addr uint64 + Offset uint64 + Size uint64 + Link uint32 + Info uint32 + Addralign uint64 + Entsize uint64 +} + +// A Section represents a single section in an ELF file. +type Section struct { + SectionHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the ELF section. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// stringTable reads and returns the string table given by the +// specified link value. +func (f *File) stringTable(link uint32) ([]byte, error) { + if link <= 0 || link >= uint32(len(f.Sections)) { + return nil, errors.New("section has invalid string table link") + } + return f.Sections[link].Data() +} + +// Open returns a new ReadSeeker reading the ELF section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +// A ProgHeader represents a single ELF program header. +type ProgHeader struct { + Type ProgType + Flags ProgFlag + Off uint64 + Vaddr uint64 + Paddr uint64 + Filesz uint64 + Memsz uint64 + Align uint64 +} + +// A Prog represents a single ELF program header in an ELF binary. +type Prog struct { + ProgHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Open returns a new ReadSeeker reading the ELF program body. +func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) } + +// A Symbol represents an entry in an ELF symbol table section. +type Symbol struct { + Name string + Info, Other byte + Section SectionIndex + Value, Size uint64 +} + +/* + * ELF reader + */ + +type FormatError struct { + off int64 + msg string + val interface{} +} + +func (e *FormatError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v' ", e.val) + } + msg += fmt.Sprintf("in record at byte %#x", e.off) + return msg +} + +// Open opens the named file using os.Open and prepares it for use as an ELF binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// SectionByType returns the first section in f with the +// given type, or nil if there is no such section. +func (f *File) SectionByType(typ SectionType) *Section { + for _, s := range f.Sections { + if s.Type == typ { + return s + } + } + return nil +} + +// NewFile creates a new File for accessing an ELF binary in an underlying reader. +// The ELF binary is expected to start at position 0 in the ReaderAt. +func NewFile(r io.ReaderAt) (*File, error) { + sr := io.NewSectionReader(r, 0, 1<<63-1) + // Read and decode ELF identifier + var ident [16]uint8 + if _, err := r.ReadAt(ident[0:], 0); err != nil { + return nil, err + } + if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' { + return nil, &FormatError{0, "bad magic number", ident[0:4]} + } + + f := new(File) + f.Class = Class(ident[EI_CLASS]) + switch f.Class { + case ELFCLASS32: + case ELFCLASS64: + // ok + default: + return nil, &FormatError{0, "unknown ELF class", f.Class} + } + + f.Data = Data(ident[EI_DATA]) + switch f.Data { + case ELFDATA2LSB: + f.ByteOrder = binary.LittleEndian + case ELFDATA2MSB: + f.ByteOrder = binary.BigEndian + default: + return nil, &FormatError{0, "unknown ELF data encoding", f.Data} + } + + f.Version = Version(ident[EI_VERSION]) + if f.Version != EV_CURRENT { + return nil, &FormatError{0, "unknown ELF version", f.Version} + } + + f.OSABI = OSABI(ident[EI_OSABI]) + f.ABIVersion = ident[EI_ABIVERSION] + + // Read ELF file header + var phoff int64 + var phentsize, phnum int + var shoff int64 + var shentsize, shnum, shstrndx int + shstrndx = -1 + switch f.Class { + case ELFCLASS32: + hdr := new(Header32) + sr.Seek(0, os.SEEK_SET) + if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { + return nil, err + } + f.Type = Type(hdr.Type) + f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) + if v := Version(hdr.Version); v != f.Version { + return nil, &FormatError{0, "mismatched ELF version", v} + } + phoff = int64(hdr.Phoff) + phentsize = int(hdr.Phentsize) + phnum = int(hdr.Phnum) + shoff = int64(hdr.Shoff) + shentsize = int(hdr.Shentsize) + shnum = int(hdr.Shnum) + shstrndx = int(hdr.Shstrndx) + case ELFCLASS64: + hdr := new(Header64) + sr.Seek(0, os.SEEK_SET) + if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { + return nil, err + } + f.Type = Type(hdr.Type) + f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) + if v := Version(hdr.Version); v != f.Version { + return nil, &FormatError{0, "mismatched ELF version", v} + } + phoff = int64(hdr.Phoff) + phentsize = int(hdr.Phentsize) + phnum = int(hdr.Phnum) + shoff = int64(hdr.Shoff) + shentsize = int(hdr.Shentsize) + shnum = int(hdr.Shnum) + shstrndx = int(hdr.Shstrndx) + } + + if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) { + return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx} + } + + // Read program headers + f.Progs = make([]*Prog, phnum) + for i := 0; i < phnum; i++ { + off := phoff + int64(i)*int64(phentsize) + sr.Seek(off, os.SEEK_SET) + p := new(Prog) + switch f.Class { + case ELFCLASS32: + ph := new(Prog32) + if err := binary.Read(sr, f.ByteOrder, ph); err != nil { + return nil, err + } + p.ProgHeader = ProgHeader{ + Type: ProgType(ph.Type), + Flags: ProgFlag(ph.Flags), + Off: uint64(ph.Off), + Vaddr: uint64(ph.Vaddr), + Paddr: uint64(ph.Paddr), + Filesz: uint64(ph.Filesz), + Memsz: uint64(ph.Memsz), + Align: uint64(ph.Align), + } + case ELFCLASS64: + ph := new(Prog64) + if err := binary.Read(sr, f.ByteOrder, ph); err != nil { + return nil, err + } + p.ProgHeader = ProgHeader{ + Type: ProgType(ph.Type), + Flags: ProgFlag(ph.Flags), + Off: uint64(ph.Off), + Vaddr: uint64(ph.Vaddr), + Paddr: uint64(ph.Paddr), + Filesz: uint64(ph.Filesz), + Memsz: uint64(ph.Memsz), + Align: uint64(ph.Align), + } + } + p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz)) + p.ReaderAt = p.sr + f.Progs[i] = p + } + + // Read section headers + f.Sections = make([]*Section, shnum) + names := make([]uint32, shnum) + for i := 0; i < shnum; i++ { + off := shoff + int64(i)*int64(shentsize) + sr.Seek(off, os.SEEK_SET) + s := new(Section) + switch f.Class { + case ELFCLASS32: + sh := new(Section32) + if err := binary.Read(sr, f.ByteOrder, sh); err != nil { + return nil, err + } + names[i] = sh.Name + s.SectionHeader = SectionHeader{ + Type: SectionType(sh.Type), + Flags: SectionFlag(sh.Flags), + Addr: uint64(sh.Addr), + Offset: uint64(sh.Off), + Size: uint64(sh.Size), + Link: uint32(sh.Link), + Info: uint32(sh.Info), + Addralign: uint64(sh.Addralign), + Entsize: uint64(sh.Entsize), + } + case ELFCLASS64: + sh := new(Section64) + if err := binary.Read(sr, f.ByteOrder, sh); err != nil { + return nil, err + } + names[i] = sh.Name + s.SectionHeader = SectionHeader{ + Type: SectionType(sh.Type), + Flags: SectionFlag(sh.Flags), + Offset: uint64(sh.Off), + Size: uint64(sh.Size), + Addr: uint64(sh.Addr), + Link: uint32(sh.Link), + Info: uint32(sh.Info), + Addralign: uint64(sh.Addralign), + Entsize: uint64(sh.Entsize), + } + } + s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size)) + s.ReaderAt = s.sr + f.Sections[i] = s + } + + if len(f.Sections) == 0 { + return f, nil + } + + // Load section header string table. + shstrtab, err := f.Sections[shstrndx].Data() + if err != nil { + return nil, err + } + for i, s := range f.Sections { + var ok bool + s.Name, ok = getString(shstrtab, int(names[i])) + if !ok { + return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]} + } + } + + return f, nil +} + +// getSymbols returns a slice of Symbols from parsing the symbol table +// with the given type, along with the associated string table. +func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) { + switch f.Class { + case ELFCLASS64: + return f.getSymbols64(typ) + + case ELFCLASS32: + return f.getSymbols32(typ) + } + + return nil, nil, errors.New("not implemented") +} + +func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { + symtabSection := f.SectionByType(typ) + if symtabSection == nil { + return nil, nil, errors.New("no symbol section") + } + + data, err := symtabSection.Data() + if err != nil { + return nil, nil, errors.New("cannot load symbol section") + } + symtab := bytes.NewReader(data) + if symtab.Len()%Sym32Size != 0 { + return nil, nil, errors.New("length of symbol section is not a multiple of SymSize") + } + + strdata, err := f.stringTable(symtabSection.Link) + if err != nil { + return nil, nil, errors.New("cannot load string table section") + } + + // The first entry is all zeros. + var skip [Sym32Size]byte + symtab.Read(skip[:]) + + symbols := make([]Symbol, symtab.Len()/Sym32Size) + + i := 0 + var sym Sym32 + for symtab.Len() > 0 { + binary.Read(symtab, f.ByteOrder, &sym) + str, _ := getString(strdata, int(sym.Name)) + symbols[i].Name = str + symbols[i].Info = sym.Info + symbols[i].Other = sym.Other + symbols[i].Section = SectionIndex(sym.Shndx) + symbols[i].Value = uint64(sym.Value) + symbols[i].Size = uint64(sym.Size) + i++ + } + + return symbols, strdata, nil +} + +func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { + symtabSection := f.SectionByType(typ) + if symtabSection == nil { + return nil, nil, errors.New("no symbol section") + } + + data, err := symtabSection.Data() + if err != nil { + return nil, nil, errors.New("cannot load symbol section") + } + symtab := bytes.NewReader(data) + if symtab.Len()%Sym64Size != 0 { + return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size") + } + + strdata, err := f.stringTable(symtabSection.Link) + if err != nil { + return nil, nil, errors.New("cannot load string table section") + } + + // The first entry is all zeros. + var skip [Sym64Size]byte + symtab.Read(skip[:]) + + symbols := make([]Symbol, symtab.Len()/Sym64Size) + + i := 0 + var sym Sym64 + for symtab.Len() > 0 { + binary.Read(symtab, f.ByteOrder, &sym) + str, _ := getString(strdata, int(sym.Name)) + symbols[i].Name = str + symbols[i].Info = sym.Info + symbols[i].Other = sym.Other + symbols[i].Section = SectionIndex(sym.Shndx) + symbols[i].Value = sym.Value + symbols[i].Size = sym.Size + i++ + } + + return symbols, strdata, nil +} + +// getString extracts a string from an ELF string table. +func getString(section []byte, start int) (string, bool) { + if start < 0 || start >= len(section) { + return "", false + } + + for end := start; end < len(section); end++ { + if section[end] == 0 { + return string(section[start:end]), true + } + } + return "", false +} + +// Section returns a section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s + } + } + return nil +} + +// applyRelocations applies relocations to dst. rels is a relocations section +// in RELA format. +func (f *File) applyRelocations(dst []byte, rels []byte) error { + if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 { + return f.applyRelocationsAMD64(dst, rels) + } + + return errors.New("not implemented") +} + +func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { + if len(rels)%Sym64Size != 0 { + return errors.New("length of relocation section is not a multiple of Sym64Size") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + symNo := rela.Info >> 32 + t := R_X86_64(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + if SymType(sym.Info&0xf) != STT_SECTION { + // We don't handle non-section relocations for now. + continue + } + + switch t { + case R_X86_64_64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend)) + case R_X86_64_32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend)) + } + } + + return nil +} + +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the required ones, and the debug/dwarf package + // does not use the others, so don't bother loading them. + // r: added line. + var names = [...]string{"abbrev", "frame", "info", "line", "str"} + var dat [len(names)][]byte + for i, name := range names { + name = ".debug_" + name + s := f.Section(name) + if s == nil { + continue + } + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + + // If there's a relocation table for .debug_info, we have to process it + // now otherwise the data in .debug_info is invalid for x86-64 objects. + rela := f.Section(".rela.debug_info") + if rela != nil && rela.Type == SHT_RELA && f.Machine == EM_X86_64 { + data, err := rela.Data() + if err != nil { + return nil, err + } + err = f.applyRelocations(dat[2], data) + if err != nil { + return nil, err + } + } + + abbrev, frame, info, line, str := dat[0], dat[1], dat[2], dat[3], dat[4] + d, err := dwarf.New(abbrev, nil, frame, info, line, nil, nil, str) + if err != nil { + return nil, err + } + + // Look for DWARF4 .debug_types sections. + for i, s := range f.Sections { + if s.Name == ".debug_types" { + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + + for _, r := range f.Sections { + if r.Type != SHT_RELA && r.Type != SHT_REL { + continue + } + if int(r.Info) != i { + continue + } + rd, err := r.Data() + if err != nil { + return nil, err + } + err = f.applyRelocations(b, rd) + if err != nil { + return nil, err + } + } + + err = d.AddTypes(fmt.Sprintf("types-%d", i), b) + if err != nil { + return nil, err + } + } + } + + return d, nil +} + +// Symbols returns the symbol table for f. +// +// For compatibility with Go 1.0, Symbols omits the null symbol at index 0. +// After retrieving the symbols as symtab, an externally supplied index x +// corresponds to symtab[x-1], not symtab[x]. +func (f *File) Symbols() ([]Symbol, error) { + sym, _, err := f.getSymbols(SHT_SYMTAB) + return sym, err +} + +type ImportedSymbol struct { + Name string + Version string + Library string +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +// It does not return weak symbols. +func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { + sym, str, err := f.getSymbols(SHT_DYNSYM) + if err != nil { + return nil, err + } + f.gnuVersionInit(str) + var all []ImportedSymbol + for i, s := range sym { + if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF { + all = append(all, ImportedSymbol{Name: s.Name}) + f.gnuVersion(i, &all[len(all)-1]) + } + } + return all, nil +} + +type verneed struct { + File string + Name string +} + +// gnuVersionInit parses the GNU version tables +// for use by calls to gnuVersion. +func (f *File) gnuVersionInit(str []byte) { + // Accumulate verneed information. + vn := f.SectionByType(SHT_GNU_VERNEED) + if vn == nil { + return + } + d, _ := vn.Data() + + var need []verneed + i := 0 + for { + if i+16 > len(d) { + break + } + vers := f.ByteOrder.Uint16(d[i : i+2]) + if vers != 1 { + break + } + cnt := f.ByteOrder.Uint16(d[i+2 : i+4]) + fileoff := f.ByteOrder.Uint32(d[i+4 : i+8]) + aux := f.ByteOrder.Uint32(d[i+8 : i+12]) + next := f.ByteOrder.Uint32(d[i+12 : i+16]) + file, _ := getString(str, int(fileoff)) + + var name string + j := i + int(aux) + for c := 0; c < int(cnt); c++ { + if j+16 > len(d) { + break + } + // hash := f.ByteOrder.Uint32(d[j:j+4]) + // flags := f.ByteOrder.Uint16(d[j+4:j+6]) + other := f.ByteOrder.Uint16(d[j+6 : j+8]) + nameoff := f.ByteOrder.Uint32(d[j+8 : j+12]) + next := f.ByteOrder.Uint32(d[j+12 : j+16]) + name, _ = getString(str, int(nameoff)) + ndx := int(other) + if ndx >= len(need) { + a := make([]verneed, 2*(ndx+1)) + copy(a, need) + need = a + } + + need[ndx] = verneed{file, name} + if next == 0 { + break + } + j += int(next) + } + + if next == 0 { + break + } + i += int(next) + } + + // Versym parallels symbol table, indexing into verneed. + vs := f.SectionByType(SHT_GNU_VERSYM) + if vs == nil { + return + } + d, _ = vs.Data() + + f.gnuNeed = need + f.gnuVersym = d +} + +// gnuVersion adds Library and Version information to sym, +// which came from offset i of the symbol table. +func (f *File) gnuVersion(i int, sym *ImportedSymbol) { + // Each entry is two bytes. + i = (i + 1) * 2 + if i >= len(f.gnuVersym) { + return + } + j := int(f.ByteOrder.Uint16(f.gnuVersym[i:])) + if j < 2 || j >= len(f.gnuNeed) { + return + } + n := &f.gnuNeed[j] + sym.Library = n.File + sym.Version = n.Name +} + +// ImportedLibraries returns the names of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + return f.DynString(DT_NEEDED) +} + +// DynString returns the strings listed for the given tag in the file's dynamic +// section. +// +// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or +// DT_RUNPATH. +func (f *File) DynString(tag DynTag) ([]string, error) { + switch tag { + case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH: + default: + return nil, fmt.Errorf("non-string-valued tag %v", tag) + } + ds := f.SectionByType(SHT_DYNAMIC) + if ds == nil { + // not dynamic, so no libraries + return nil, nil + } + d, err := ds.Data() + if err != nil { + return nil, err + } + str, err := f.stringTable(ds.Link) + if err != nil { + return nil, err + } + var all []string + for len(d) > 0 { + var t DynTag + var v uint64 + switch f.Class { + case ELFCLASS32: + t = DynTag(f.ByteOrder.Uint32(d[0:4])) + v = uint64(f.ByteOrder.Uint32(d[4:8])) + d = d[8:] + case ELFCLASS64: + t = DynTag(f.ByteOrder.Uint64(d[0:8])) + v = f.ByteOrder.Uint64(d[8:16]) + d = d[16:] + } + if t == tag { + s, ok := getString(str, int(v)) + if ok { + all = append(all, s) + } + } + } + return all, nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/file_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/file_test.go new file mode 100644 index 0000000000..69018e0d4e --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/file_test.go @@ -0,0 +1,344 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 elf + +import ( + "bytes" + "compress/gzip" + "encoding/binary" + "io" + "net" + "os" + "path" + "reflect" + "runtime" + "testing" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +type fileTest struct { + file string + hdr FileHeader + sections []SectionHeader + progs []ProgHeader + needed []string +} + +var fileTests = []fileTest{ + { + "testdata/gcc-386-freebsd-exec", + FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc}, + []SectionHeader{ + {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0}, + {".hash", SHT_HASH, SHF_ALLOC, 0x80480ec, 0xec, 0x90, 0x3, 0x0, 0x4, 0x4}, + {".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x804817c, 0x17c, 0x110, 0x4, 0x1, 0x4, 0x10}, + {".dynstr", SHT_STRTAB, SHF_ALLOC, 0x804828c, 0x28c, 0xbb, 0x0, 0x0, 0x1, 0x0}, + {".rel.plt", SHT_REL, SHF_ALLOC, 0x8048348, 0x348, 0x20, 0x3, 0x7, 0x4, 0x8}, + {".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x8048368, 0x368, 0x11, 0x0, 0x0, 0x4, 0x0}, + {".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804837c, 0x37c, 0x50, 0x0, 0x0, 0x4, 0x4}, + {".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x80483cc, 0x3cc, 0x180, 0x0, 0x0, 0x4, 0x0}, + {".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804854c, 0x54c, 0xc, 0x0, 0x0, 0x4, 0x0}, + {".rodata", SHT_PROGBITS, SHF_ALLOC, 0x8048558, 0x558, 0xa3, 0x0, 0x0, 0x1, 0x0}, + {".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80495fc, 0x5fc, 0xc, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x8049608, 0x608, 0x4, 0x0, 0x0, 0x4, 0x0}, + {".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x804960c, 0x60c, 0x98, 0x4, 0x0, 0x4, 0x8}, + {".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496a4, 0x6a4, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496ac, 0x6ac, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b4, 0x6b4, 0x4, 0x0, 0x0, 0x4, 0x0}, + {".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b8, 0x6b8, 0x1c, 0x0, 0x0, 0x4, 0x4}, + {".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x80496d4, 0x6d4, 0x20, 0x0, 0x0, 0x4, 0x0}, + {".comment", SHT_PROGBITS, 0x0, 0x0, 0x6d4, 0x12d, 0x0, 0x0, 0x1, 0x0}, + {".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x801, 0x20, 0x0, 0x0, 0x1, 0x0}, + {".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0x821, 0x1b, 0x0, 0x0, 0x1, 0x0}, + {".debug_info", SHT_PROGBITS, 0x0, 0x0, 0x83c, 0x11d, 0x0, 0x0, 0x1, 0x0}, + {".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0x959, 0x41, 0x0, 0x0, 0x1, 0x0}, + {".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x99a, 0x35, 0x0, 0x0, 0x1, 0x0}, + {".debug_frame", SHT_PROGBITS, 0x0, 0x0, 0x9d0, 0x30, 0x0, 0x0, 0x4, 0x0}, + {".debug_str", SHT_PROGBITS, 0x0, 0x0, 0xa00, 0xd, 0x0, 0x0, 0x1, 0x0}, + {".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xa0d, 0xf8, 0x0, 0x0, 0x1, 0x0}, + {".symtab", SHT_SYMTAB, 0x0, 0x0, 0xfb8, 0x4b0, 0x1d, 0x38, 0x4, 0x10}, + {".strtab", SHT_STRTAB, 0x0, 0x0, 0x1468, 0x206, 0x0, 0x0, 0x1, 0x0}, + }, + []ProgHeader{ + {PT_PHDR, PF_R + PF_X, 0x34, 0x8048034, 0x8048034, 0xa0, 0xa0, 0x4}, + {PT_INTERP, PF_R, 0xd4, 0x80480d4, 0x80480d4, 0x15, 0x15, 0x1}, + {PT_LOAD, PF_R + PF_X, 0x0, 0x8048000, 0x8048000, 0x5fb, 0x5fb, 0x1000}, + {PT_LOAD, PF_R + PF_W, 0x5fc, 0x80495fc, 0x80495fc, 0xd8, 0xf8, 0x1000}, + {PT_DYNAMIC, PF_R + PF_W, 0x60c, 0x804960c, 0x804960c, 0x98, 0x98, 0x4}, + }, + []string{"libc.so.6"}, + }, + { + "testdata/gcc-amd64-linux-exec", + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0}, + []SectionHeader{ + {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0}, + {".note.ABI-tag", SHT_NOTE, SHF_ALLOC, 0x40021c, 0x21c, 0x20, 0x0, 0x0, 0x4, 0x0}, + {".hash", SHT_HASH, SHF_ALLOC, 0x400240, 0x240, 0x24, 0x5, 0x0, 0x8, 0x4}, + {".gnu.hash", SHT_LOOS + 268435446, SHF_ALLOC, 0x400268, 0x268, 0x1c, 0x5, 0x0, 0x8, 0x0}, + {".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x400288, 0x288, 0x60, 0x6, 0x1, 0x8, 0x18}, + {".dynstr", SHT_STRTAB, SHF_ALLOC, 0x4002e8, 0x2e8, 0x3d, 0x0, 0x0, 0x1, 0x0}, + {".gnu.version", SHT_HIOS, SHF_ALLOC, 0x400326, 0x326, 0x8, 0x5, 0x0, 0x2, 0x2}, + {".gnu.version_r", SHT_LOOS + 268435454, SHF_ALLOC, 0x400330, 0x330, 0x20, 0x6, 0x1, 0x8, 0x0}, + {".rela.dyn", SHT_RELA, SHF_ALLOC, 0x400350, 0x350, 0x18, 0x5, 0x0, 0x8, 0x18}, + {".rela.plt", SHT_RELA, SHF_ALLOC, 0x400368, 0x368, 0x30, 0x5, 0xc, 0x8, 0x18}, + {".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400398, 0x398, 0x18, 0x0, 0x0, 0x4, 0x0}, + {".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003b0, 0x3b0, 0x30, 0x0, 0x0, 0x4, 0x10}, + {".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003e0, 0x3e0, 0x1b4, 0x0, 0x0, 0x10, 0x0}, + {".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400594, 0x594, 0xe, 0x0, 0x0, 0x4, 0x0}, + {".rodata", SHT_PROGBITS, SHF_ALLOC, 0x4005a4, 0x5a4, 0x11, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 0x4005b8, 0x5b8, 0x24, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x4005e0, 0x5e0, 0xa4, 0x0, 0x0, 0x8, 0x0}, + {".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600688, 0x688, 0x10, 0x0, 0x0, 0x8, 0x0}, + {".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600698, 0x698, 0x10, 0x0, 0x0, 0x8, 0x0}, + {".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x6006a8, 0x6a8, 0x8, 0x0, 0x0, 0x8, 0x0}, + {".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x6006b0, 0x6b0, 0x1a0, 0x6, 0x0, 0x8, 0x10}, + {".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600850, 0x850, 0x8, 0x0, 0x0, 0x8, 0x8}, + {".got.plt", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600858, 0x858, 0x28, 0x0, 0x0, 0x8, 0x8}, + {".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600880, 0x880, 0x18, 0x0, 0x0, 0x8, 0x0}, + {".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x600898, 0x898, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".comment", SHT_PROGBITS, 0x0, 0x0, 0x898, 0x126, 0x0, 0x0, 0x1, 0x0}, + {".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x9c0, 0x90, 0x0, 0x0, 0x10, 0x0}, + {".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0xa50, 0x25, 0x0, 0x0, 0x1, 0x0}, + {".debug_info", SHT_PROGBITS, 0x0, 0x0, 0xa75, 0x1a7, 0x0, 0x0, 0x1, 0x0}, + {".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xc1c, 0x6f, 0x0, 0x0, 0x1, 0x0}, + {".debug_line", SHT_PROGBITS, 0x0, 0x0, 0xc8b, 0x13f, 0x0, 0x0, 0x1, 0x0}, + {".debug_str", SHT_PROGBITS, SHF_MERGE + SHF_STRINGS, 0x0, 0xdca, 0xb1, 0x0, 0x0, 0x1, 0x1}, + {".debug_ranges", SHT_PROGBITS, 0x0, 0x0, 0xe80, 0x90, 0x0, 0x0, 0x10, 0x0}, + {".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xf10, 0x149, 0x0, 0x0, 0x1, 0x0}, + {".symtab", SHT_SYMTAB, 0x0, 0x0, 0x19a0, 0x6f0, 0x24, 0x39, 0x8, 0x18}, + {".strtab", SHT_STRTAB, 0x0, 0x0, 0x2090, 0x1fc, 0x0, 0x0, 0x1, 0x0}, + }, + []ProgHeader{ + {PT_PHDR, PF_R + PF_X, 0x40, 0x400040, 0x400040, 0x1c0, 0x1c0, 0x8}, + {PT_INTERP, PF_R, 0x200, 0x400200, 0x400200, 0x1c, 0x1c, 1}, + {PT_LOAD, PF_R + PF_X, 0x0, 0x400000, 0x400000, 0x684, 0x684, 0x200000}, + {PT_LOAD, PF_R + PF_W, 0x688, 0x600688, 0x600688, 0x210, 0x218, 0x200000}, + {PT_DYNAMIC, PF_R + PF_W, 0x6b0, 0x6006b0, 0x6006b0, 0x1a0, 0x1a0, 0x8}, + {PT_NOTE, PF_R, 0x21c, 0x40021c, 0x40021c, 0x20, 0x20, 0x4}, + {PT_LOOS + 0x474E550, PF_R, 0x5b8, 0x4005b8, 0x4005b8, 0x24, 0x24, 0x4}, + {PT_LOOS + 0x474E551, PF_R + PF_W, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, + }, + []string{"libc.so.6"}, + }, + { + "testdata/hello-world-core.gz", + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0}, + []SectionHeader{}, + []ProgHeader{ + {Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + }, + nil, + }, +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i] + + var f *File + var err error + if path.Ext(tt.file) == ".gz" { + var r io.ReaderAt + if r, err = decompress(tt.file); err == nil { + f, err = NewFile(r) + } + } else { + f, err = Open(tt.file) + } + if err != nil { + t.Errorf("cannot open file %s: %v", tt.file, err) + continue + } + defer f.Close() + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) + continue + } + for i, s := range f.Sections { + if i >= len(tt.sections) { + break + } + sh := &tt.sections[i] + if !reflect.DeepEqual(&s.SectionHeader, sh) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, &s.SectionHeader, sh) + } + } + for i, p := range f.Progs { + if i >= len(tt.progs) { + break + } + ph := &tt.progs[i] + if !reflect.DeepEqual(&p.ProgHeader, ph) { + t.Errorf("open %s, program %d:\n\thave %#v\n\twant %#v\n", tt.file, i, &p.ProgHeader, ph) + } + } + tn := len(tt.sections) + fn := len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + tn = len(tt.progs) + fn = len(f.Progs) + if tn != fn { + t.Errorf("open %s: len(Progs) = %d, want %d", tt.file, fn, tn) + } + tl := tt.needed + fl, err := f.ImportedLibraries() + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(tl, fl) { + t.Errorf("open %s: DT_NEEDED = %v, want %v", tt.file, tl, fl) + } + } +} + +// elf.NewFile requires io.ReaderAt, which compress/gzip cannot +// provide. Decompress the file to a bytes.Reader. +func decompress(gz string) (io.ReaderAt, error) { + in, err := os.Open(gz) + if err != nil { + return nil, err + } + defer in.Close() + r, err := gzip.NewReader(in) + if err != nil { + return nil, err + } + var out bytes.Buffer + _, err = io.Copy(&out, r) + return bytes.NewReader(out.Bytes()), err +} + +type relocationTestEntry struct { + entryNumber int + entry *dwarf.Entry +} + +type relocationTest struct { + file string + entries []relocationTestEntry +} + +var relocationTests = []relocationTest{ + { + "testdata/go-relocation-test-gcc441-x86-64.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc441-x86.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc424-x86-64.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/gcc-amd64-openbsd-debug-with-rela.obj", + []relocationTestEntry{ + {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}}, + {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}}, + }, + }, +} + +func TestDWARFRelocations(t *testing.T) { + for i, test := range relocationTests { + f, err := Open(test.file) + if err != nil { + t.Error(err) + continue + } + dwarf, err := f.DWARF() + if err != nil { + t.Error(err) + continue + } + for _, testEntry := range test.entries { + reader := dwarf.Reader() + for j := 0; j < testEntry.entryNumber; j++ { + entry, err := reader.Next() + if entry == nil || err != nil { + t.Errorf("Failed to skip to entry %d: %v", testEntry.entryNumber, err) + continue + } + } + entry, err := reader.Next() + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(testEntry.entry, entry) { + t.Errorf("#%d/%d: mismatch: got:%#v want:%#v", i, testEntry.entryNumber, entry, testEntry.entry) + continue + } + } + } +} + +func TestNoSectionOverlaps(t *testing.T) { + // Ensure 6l outputs sections without overlaps. + if runtime.GOOS != "linux" && runtime.GOOS != "freebsd" { + return // not ELF + } + _ = net.ResolveIPAddr // force dynamic linkage + f, err := Open(os.Args[0]) + if err != nil { + t.Error(err) + return + } + for i, si := range f.Sections { + sih := si.SectionHeader + if sih.Type == SHT_NOBITS { + continue + } + for j, sj := range f.Sections { + sjh := sj.SectionHeader + if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.Size == 0 { + continue + } + if sih.Offset >= sjh.Offset && sih.Offset < sjh.Offset+sjh.Size { + t.Errorf("ld produced ELF with section %s within %s: 0x%x <= 0x%x..0x%x < 0x%x", + sih.Name, sjh.Name, sjh.Offset, sih.Offset, sih.Offset+sih.Size, sjh.Offset+sjh.Size) + } + } + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-386-freebsd-exec b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-386-freebsd-exec new file mode 100755 index 0000000000..7af9c58ca7 Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-386-freebsd-exec differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-amd64-linux-exec b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-amd64-linux-exec new file mode 100755 index 0000000000..c6cb1de28c Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-amd64-linux-exec differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj new file mode 100644 index 0000000000..f62b1ea1ca Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc424-x86-64.obj b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc424-x86-64.obj new file mode 100644 index 0000000000..a7c6d6e562 Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc424-x86-64.obj differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc441-x86-64.obj b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc441-x86-64.obj new file mode 100644 index 0000000000..2d37ab6e6e Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc441-x86-64.obj differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc441-x86.obj b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc441-x86.obj new file mode 100644 index 0000000000..0d59fe303b Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/go-relocation-test-gcc441-x86.obj differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/hello-world-core.gz b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/hello-world-core.gz new file mode 100644 index 0000000000..806af6edbc Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/hello-world-core.gz differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/hello.c b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/hello.c new file mode 100644 index 0000000000..34d9ee7923 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf/testdata/hello.c @@ -0,0 +1,7 @@ +#include + +void +main(int argc, char *argv[]) +{ + printf("hello, world\n"); +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.asm b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.asm new file mode 100644 index 0000000000..b9ee9c0a50 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.asm @@ -0,0 +1,58 @@ +TEXT linefrompc(SB),4,$0 // Each byte stores its line delta +BYTE $2; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" +BYTE $2; +#include "pclinetest.h" +BYTE $2; +BYTE $255; + +TEXT pcfromline(SB),4,$0 // Each record stores its line delta, then n, then n more bytes +BYTE $32; BYTE $0; +BYTE $1; BYTE $1; BYTE $0; +BYTE $1; BYTE $0; + +BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0; + + +#include "pclinetest.h" +BYTE $4; BYTE $0; + + +BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" + + +BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0; +BYTE $255; + +// Keep the linker happy +TEXT main·main(SB),4,$0 + RET + +TEXT main·init(SB),4,$0 + // Prevent GC of our test symbols + CALL linefrompc(SB) + CALL pcfromline(SB) + RET diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h new file mode 100644 index 0000000000..156c0b87b0 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h @@ -0,0 +1,9 @@ +// +build ignore + +// Empty include file to generate z symbols + + + + + +// EOF diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go new file mode 100644 index 0000000000..dcd7015bb7 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go @@ -0,0 +1,472 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +/* + * Line tables + */ + +package gosym + +import ( + "encoding/binary" + "sync" +) + +// A LineTable is a data structure mapping program counters to line numbers. +// +// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable, +// and the line number corresponded to a numbering of all source lines in the +// program, across all files. That absolute line number would then have to be +// converted separately to a file name and line number within the file. +// +// In Go 1.2, the format of the data changed so that there is a single LineTable +// for the entire program, shared by all Funcs, and there are no absolute line +// numbers, just line numbers within specific files. +// +// For the most part, LineTable's methods should be treated as an internal +// detail of the package; callers should use the methods on Table instead. +type LineTable struct { + Data []byte + PC uint64 + Line int + + // Go 1.2 state + mu sync.Mutex + go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes + binary binary.ByteOrder + quantum uint32 + ptrsize uint32 + functab []byte + nfunctab uint32 + filetab []byte + nfiletab uint32 + fileMap map[string]uint32 +} + +// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, +// but we have no idea whether we're using arm or not. This only +// matters in the old (pre-Go 1.2) symbol table format, so it's not worth +// fixing. +const oldQuantum = 1 + +func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) { + // The PC/line table can be thought of as a sequence of + // * + // batches. Each update batch results in a (pc, line) pair, + // where line applies to every PC from pc up to but not + // including the pc of the next pair. + // + // Here we process each update individually, which simplifies + // the code, but makes the corner cases more confusing. + b, pc, line = t.Data, t.PC, t.Line + for pc <= targetPC && line != targetLine && len(b) > 0 { + code := b[0] + b = b[1:] + switch { + case code == 0: + if len(b) < 4 { + b = b[0:0] + break + } + val := binary.BigEndian.Uint32(b) + b = b[4:] + line += int(val) + case code <= 64: + line += int(code) + case code <= 128: + line -= int(code - 64) + default: + pc += oldQuantum * uint64(code-128) + continue + } + pc += oldQuantum + } + return b, pc, line +} + +func (t *LineTable) slice(pc uint64) *LineTable { + data, pc, line := t.parse(pc, -1) + return &LineTable{Data: data, PC: pc, Line: line} +} + +// PCToLine returns the line number for the given program counter. +// Callers should use Table's PCToLine method instead. +func (t *LineTable) PCToLine(pc uint64) int { + if t.isGo12() { + return t.go12PCToLine(pc) + } + _, _, line := t.parse(pc, -1) + return line +} + +// LineToPC returns the program counter for the given line number, +// considering only program counters before maxpc. +// Callers should use Table's LineToPC method instead. +func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 { + if t.isGo12() { + return 0 + } + _, pc, line1 := t.parse(maxpc, line) + if line1 != line { + return 0 + } + // Subtract quantum from PC to account for post-line increment + return pc - oldQuantum +} + +// NewLineTable returns a new PC/line table +// corresponding to the encoded data. +// Text must be the start address of the +// corresponding text segment. +func NewLineTable(data []byte, text uint64) *LineTable { + return &LineTable{Data: data, PC: text, Line: 0} +} + +// Go 1.2 symbol table format. +// See golang.org/s/go12symtab. +// +// A general note about the methods here: rather than try to avoid +// index out of bounds errors, we trust Go to detect them, and then +// we recover from the panics and treat them as indicative of a malformed +// or incomplete table. +// +// The methods called by symtab.go, which begin with "go12" prefixes, +// are expected to have that recovery logic. + +// isGo12 reports whether this is a Go 1.2 (or later) symbol table. +func (t *LineTable) isGo12() bool { + t.go12Init() + return t.go12 == 1 +} + +const go12magic = 0xfffffffb + +// uintptr returns the pointer-sized value encoded at b. +// The pointer size is dictated by the table being read. +func (t *LineTable) uintptr(b []byte) uint64 { + if t.ptrsize == 4 { + return uint64(t.binary.Uint32(b)) + } + return t.binary.Uint64(b) +} + +// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table. +func (t *LineTable) go12Init() { + t.mu.Lock() + defer t.mu.Unlock() + if t.go12 != 0 { + return + } + + defer func() { + // If we panic parsing, assume it's not a Go 1.2 symbol table. + recover() + }() + + // Check header: 4-byte magic, two zeros, pc quantum, pointer size. + t.go12 = -1 // not Go 1.2 until proven otherwise + if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || + (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum + (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size + return + } + + switch uint32(go12magic) { + case binary.LittleEndian.Uint32(t.Data): + t.binary = binary.LittleEndian + case binary.BigEndian.Uint32(t.Data): + t.binary = binary.BigEndian + default: + return + } + + t.quantum = uint32(t.Data[6]) + t.ptrsize = uint32(t.Data[7]) + + t.nfunctab = uint32(t.uintptr(t.Data[8:])) + t.functab = t.Data[8+t.ptrsize:] + functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize + fileoff := t.binary.Uint32(t.functab[functabsize:]) + t.functab = t.functab[:functabsize] + t.filetab = t.Data[fileoff:] + t.nfiletab = t.binary.Uint32(t.filetab) + t.filetab = t.filetab[:t.nfiletab*4] + + t.go12 = 1 // so far so good +} + +// go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table. +func (t *LineTable) go12Funcs() []Func { + // Assume it is malformed and return nil on error. + defer func() { + recover() + }() + + n := len(t.functab) / int(t.ptrsize) / 2 + funcs := make([]Func, n) + for i := range funcs { + f := &funcs[i] + f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):])) + f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])) + info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):] + f.LineTable = t + f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:])) + f.Sym = &Sym{ + Value: f.Entry, + Type: 'T', + Name: t.string(t.binary.Uint32(info[t.ptrsize:])), + GoType: 0, + Func: f, + } + } + return funcs +} + +// findFunc returns the func corresponding to the given program counter. +func (t *LineTable) findFunc(pc uint64) []byte { + if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) { + return nil + } + + // The function table is a list of 2*nfunctab+1 uintptrs, + // alternating program counters and offsets to func structures. + f := t.functab + nf := t.nfunctab + for nf > 0 { + m := nf / 2 + fm := f[2*t.ptrsize*m:] + if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) { + return t.Data[t.uintptr(fm[t.ptrsize:]):] + } else if pc < t.uintptr(fm) { + nf = m + } else { + f = f[(m+1)*2*t.ptrsize:] + nf -= m + 1 + } + } + return nil +} + +// readvarint reads, removes, and returns a varint from *pp. +func (t *LineTable) readvarint(pp *[]byte) uint32 { + var v, shift uint32 + p := *pp + for shift = 0; ; shift += 7 { + b := p[0] + p = p[1:] + v |= (uint32(b) & 0x7F) << shift + if b&0x80 == 0 { + break + } + } + *pp = p + return v +} + +// string returns a Go string found at off. +func (t *LineTable) string(off uint32) string { + for i := off; ; i++ { + if t.Data[i] == 0 { + return string(t.Data[off:i]) + } + } +} + +// step advances to the next pc, value pair in the encoded table. +func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { + uvdelta := t.readvarint(p) + if uvdelta == 0 && !first { + return false + } + if uvdelta&1 != 0 { + uvdelta = ^(uvdelta >> 1) + } else { + uvdelta >>= 1 + } + vdelta := int32(uvdelta) + pcdelta := t.readvarint(p) * t.quantum + *pc += uint64(pcdelta) + *val += vdelta + return true +} + +// pcvalue reports the value associated with the target pc. +// off is the offset to the beginning of the pc-value table, +// and entry is the start PC for the corresponding function. +func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 { + if off == 0 { + return -1 + } + p := t.Data[off:] + + val := int32(-1) + pc := entry + for t.step(&p, &pc, &val, pc == entry) { + if targetpc < pc { + return val + } + } + return -1 +} + +// findFileLine scans one function in the binary looking for a +// program counter in the given file on the given line. +// It does so by running the pc-value tables mapping program counter +// to file number. Since most functions come from a single file, these +// are usually short and quick to scan. If a file match is found, then the +// code goes to the expense of looking for a simultaneous line number match. +func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 { + if filetab == 0 || linetab == 0 { + return 0 + } + + fp := t.Data[filetab:] + fl := t.Data[linetab:] + fileVal := int32(-1) + filePC := entry + lineVal := int32(-1) + linePC := entry + fileStartPC := filePC + for t.step(&fp, &filePC, &fileVal, filePC == entry) { + if fileVal == filenum && fileStartPC < filePC { + // fileVal is in effect starting at fileStartPC up to + // but not including filePC, and it's the file we want. + // Run the PC table looking for a matching line number + // or until we reach filePC. + lineStartPC := linePC + for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) { + // lineVal is in effect until linePC, and lineStartPC < filePC. + if lineVal == line { + if fileStartPC <= lineStartPC { + return lineStartPC + } + if fileStartPC < linePC { + return fileStartPC + } + } + lineStartPC = linePC + } + } + fileStartPC = filePC + } + return 0 +} + +// go12PCToLine maps program counter to line number for the Go 1.2 pcln table. +func (t *LineTable) go12PCToLine(pc uint64) (line int) { + return t.go12PCToVal(pc, t.ptrsize+5*4) +} + +// go12PCToSPAdj maps program counter to Stack Pointer adjustment for the Go 1.2 pcln table. +func (t *LineTable) go12PCToSPAdj(pc uint64) (spadj int) { + return t.go12PCToVal(pc, t.ptrsize+3*4) +} + +func (t *LineTable) go12PCToVal(pc uint64, fOffset uint32) (val int) { + defer func() { + if recover() != nil { + val = -1 + } + }() + + f := t.findFunc(pc) + if f == nil { + return -1 + } + entry := t.uintptr(f) + linetab := t.binary.Uint32(f[fOffset:]) + return int(t.pcvalue(linetab, entry, pc)) +} + +// go12PCToFile maps program counter to file name for the Go 1.2 pcln table. +func (t *LineTable) go12PCToFile(pc uint64) (file string) { + defer func() { + if recover() != nil { + file = "" + } + }() + + f := t.findFunc(pc) + if f == nil { + return "" + } + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + fno := t.pcvalue(filetab, entry, pc) + if fno <= 0 { + return "" + } + return t.string(t.binary.Uint32(t.filetab[4*fno:])) +} + +// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. +func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { + defer func() { + if recover() != nil { + pc = 0 + } + }() + + t.initFileMap() + filenum := t.fileMap[file] + if filenum == 0 { + return 0 + } + + // Scan all functions. + // If this turns out to be a bottleneck, we could build a map[int32][]int32 + // mapping file number to a list of functions with code from that file. + for i := uint32(0); i < t.nfunctab; i++ { + f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line)) + if pc != 0 { + return pc + } + } + return 0 +} + +// initFileMap initializes the map from file name to file number. +func (t *LineTable) initFileMap() { + t.mu.Lock() + defer t.mu.Unlock() + + if t.fileMap != nil { + return + } + m := make(map[string]uint32) + + for i := uint32(1); i < t.nfiletab; i++ { + s := t.string(t.binary.Uint32(t.filetab[4*i:])) + m[s] = i + } + t.fileMap = m +} + +// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable. +// Every key maps to obj. That's not a very interesting map, but it provides +// a way for callers to obtain the list of files in the program. +func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) { + defer func() { + recover() + }() + + t.initFileMap() + for file := range t.fileMap { + m[file] = obj + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab_test.go new file mode 100644 index 0000000000..4bd3b0972f --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab_test.go @@ -0,0 +1,287 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 gosym + +import ( + "debug/elf" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var ( + pclineTempDir string + pclinetestBinary string +) + +func dotest(self bool) bool { + // For now, only works on amd64 platforms. + if runtime.GOARCH != "amd64" { + return false + } + // Self test reads test binary; only works on Linux. + if self && runtime.GOOS != "linux" { + return false + } + // Command below expects "sh", so Unix. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + return false + } + if pclinetestBinary != "" { + return true + } + var err error + pclineTempDir, err = ioutil.TempDir("", "pclinetest") + if err != nil { + panic(err) + } + if strings.Contains(pclineTempDir, " ") { + panic("unexpected space in tempdir") + } + // This command builds pclinetest from pclinetest.asm; + // the resulting binary looks like it was built from pclinetest.s, + // but we have renamed it to keep it away from the go tool. + pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest") + command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6", + pclinetestBinary, pclinetestBinary, pclinetestBinary) + cmd := exec.Command("sh", "-c", command) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + return true +} + +func endtest() { + if pclineTempDir != "" { + os.RemoveAll(pclineTempDir) + pclineTempDir = "" + pclinetestBinary = "" + } +} + +func getTable(t *testing.T) *Table { + f, tab := crack(os.Args[0], t) + f.Close() + return tab +} + +func crack(file string, t *testing.T) (*elf.File, *Table) { + // Open self + f, err := elf.Open(file) + if err != nil { + t.Fatal(err) + } + return parse(file, f, t) +} + +func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) { + symdat, err := f.Section(".gosymtab").Data() + if err != nil { + f.Close() + t.Fatalf("reading %s gosymtab: %v", file, err) + } + pclndat, err := f.Section(".gopclntab").Data() + if err != nil { + f.Close() + t.Fatalf("reading %s gopclntab: %v", file, err) + } + + pcln := NewLineTable(pclndat, f.Section(".text").Addr) + tab, err := NewTable(symdat, pcln) + if err != nil { + f.Close() + t.Fatalf("parsing %s gosymtab: %v", file, err) + } + + return f, tab +} + +var goarch = os.Getenv("O") + +func TestLineFromAline(t *testing.T) { + t.Skip("wants to use go tool 6a which hasn't existed for who knows how long") + if !dotest(true) { + return + } + defer endtest() + + tab := getTable(t) + if tab.go12line != nil { + // aline's don't exist in the Go 1.2 table. + t.Skip("not relevant to Go 1.2 symbol table") + } + + // Find the sym package + pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj + if pkg == nil { + t.Fatalf("nil pkg") + } + + // Walk every absolute line and ensure that we hit every + // source line monotonically + lastline := make(map[string]int) + final := -1 + for i := 0; i < 10000; i++ { + path, line := pkg.lineFromAline(i) + // Check for end of object + if path == "" { + if final == -1 { + final = i - 1 + } + continue + } else if final != -1 { + t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line) + } + // It's okay to see files multiple times (e.g., sys.a) + if line == 1 { + lastline[path] = 1 + continue + } + // Check that the is the next line in path + ll, ok := lastline[path] + if !ok { + t.Errorf("file %s starts on line %d", path, line) + } else if line != ll+1 { + t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line) + } + lastline[path] = line + } + if final == -1 { + t.Errorf("never reached end of object") + } +} + +func TestLineAline(t *testing.T) { + t.Skip("wants to use go tool 6a which hasn't existed for who knows how long") + if !dotest(true) { + return + } + defer endtest() + + tab := getTable(t) + if tab.go12line != nil { + // aline's don't exist in the Go 1.2 table. + t.Skip("not relevant to Go 1.2 symbol table") + } + + for _, o := range tab.Files { + // A source file can appear multiple times in a + // object. alineFromLine will always return alines in + // the first file, so track which lines we've seen. + found := make(map[string]int) + for i := 0; i < 1000; i++ { + path, line := o.lineFromAline(i) + if path == "" { + break + } + + // cgo files are full of 'Z' symbols, which we don't handle + if len(path) > 4 && path[len(path)-4:] == ".cgo" { + continue + } + + if minline, ok := found[path]; path != "" && ok { + if minline >= line { + // We've already covered this file + continue + } + } + found[path] = line + + a, err := o.alineFromLine(path, line) + if err != nil { + t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err) + } else if a != i { + t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a) + } + } + } +} + +func TestPCLine(t *testing.T) { + t.Skip("wants to use go tool 6a which hasn't existed for who knows how long") + if !dotest(false) { + return + } + defer endtest() + + f, tab := crack(pclinetestBinary, t) + text := f.Section(".text") + textdat, err := text.Data() + if err != nil { + t.Fatalf("reading .text: %v", err) + } + + // Test PCToLine + sym := tab.LookupFunc("linefrompc") + wantLine := 0 + for pc := sym.Entry; pc < sym.End; pc++ { + off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g + if textdat[off] == 255 { + break + } + wantLine += int(textdat[off]) + t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc) + file, line, fn := tab.PCToLine(pc) + if fn == nil { + t.Errorf("failed to get line of PC %#x", pc) + } else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym { + t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name) + } + } + + // Test LineToPC + sym = tab.LookupFunc("pcfromline") + lookupline := -1 + wantLine = 0 + off := uint64(0) // TODO(rsc): should not need off; bug in 8g + for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) { + file, line, fn := tab.PCToLine(pc) + off = pc - text.Addr + if textdat[off] == 255 { + break + } + wantLine += int(textdat[off]) + if line != wantLine { + t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line) + off = pc + 1 - text.Addr + continue + } + if lookupline == -1 { + lookupline = line + } + for ; lookupline <= line; lookupline++ { + pc2, fn2, err := tab.LineToPC(file, lookupline) + if lookupline != line { + // Should be nothing on this line + if err == nil { + t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name) + } + } else if err != nil { + t.Errorf("failed to get PC of line %d: %s", lookupline, err) + } else if pc != pc2 { + t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name) + } + } + off = pc + 1 - text.Addr + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go new file mode 100644 index 0000000000..2c83b841cf --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go @@ -0,0 +1,731 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 gosym implements access to the Go symbol +// and line number tables embedded in Go binaries generated +// by the gc compilers. +package gosym + +// The table format is a variant of the format used in Plan 9's a.out +// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out. +// The best reference for the differences between the Plan 9 format +// and the Go format is the runtime source, specifically ../../runtime/symtab.c. + +import ( + "bytes" + "encoding/binary" + "fmt" + "strconv" + "strings" +) + +/* + * Symbols + */ + +// A Sym represents a single symbol table entry. +type Sym struct { + Value uint64 + Type byte + Name string + GoType uint64 + // If this symbol if a function symbol, the corresponding Func + Func *Func +} + +// Static reports whether this symbol is static (not visible outside its file). +func (s *Sym) Static() bool { return s.Type >= 'a' } + +// PackageName returns the package part of the symbol name, +// or the empty string if there is none. +func (s *Sym) PackageName() string { + if i := strings.Index(s.Name, "."); i != -1 { + return s.Name[0:i] + } + return "" +} + +// ReceiverName returns the receiver type name of this symbol, +// or the empty string if there is none. +func (s *Sym) ReceiverName() string { + l := strings.Index(s.Name, ".") + r := strings.LastIndex(s.Name, ".") + if l == -1 || r == -1 || l == r { + return "" + } + return s.Name[l+1 : r] +} + +// BaseName returns the symbol name without the package or receiver name. +func (s *Sym) BaseName() string { + if i := strings.LastIndex(s.Name, "."); i != -1 { + return s.Name[i+1:] + } + return s.Name +} + +// A Func collects information about a single function. +type Func struct { + Entry uint64 + *Sym + End uint64 + Params []*Sym + Locals []*Sym + FrameSize int + LineTable *LineTable + Obj *Obj +} + +// An Obj represents a collection of functions in a symbol table. +// +// The exact method of division of a binary into separate Objs is an internal detail +// of the symbol table format. +// +// In early versions of Go each source file became a different Obj. +// +// In Go 1 and Go 1.1, each package produced one Obj for all Go sources +// and one Obj per C source file. +// +// In Go 1.2, there is a single Obj for the entire program. +type Obj struct { + // Funcs is a list of functions in the Obj. + Funcs []Func + + // In Go 1.1 and earlier, Paths is a list of symbols corresponding + // to the source file names that produced the Obj. + // In Go 1.2, Paths is nil. + // Use the keys of Table.Files to obtain a list of source files. + Paths []Sym // meta +} + +/* + * Symbol tables + */ + +// Table represents a Go symbol table. It stores all of the +// symbols decoded from the program and provides methods to translate +// between symbols, names, and addresses. +type Table struct { + Syms []Sym + Funcs []Func + Files map[string]*Obj // nil for Go 1.2 and later binaries + Objs []Obj // nil for Go 1.2 and later binaries + + go12line *LineTable // Go 1.2 line number table +} + +type sym struct { + value uint64 + gotype uint64 + typ byte + name []byte +} + +var ( + littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} + bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} +) + +func walksymtab(data []byte, fn func(sym) error) error { + if len(data) == 0 { // missing symtab is okay + return nil + } + var order binary.ByteOrder = binary.BigEndian + newTable := false + switch { + case bytes.HasPrefix(data, oldLittleEndianSymtab): + // Same as Go 1.0, but little endian. + // Format was used during interim development between Go 1.0 and Go 1.1. + // Should not be widespread, but easy to support. + data = data[6:] + order = binary.LittleEndian + case bytes.HasPrefix(data, bigEndianSymtab): + newTable = true + case bytes.HasPrefix(data, littleEndianSymtab): + newTable = true + order = binary.LittleEndian + } + var ptrsz int + if newTable { + if len(data) < 8 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + ptrsz = int(data[7]) + if ptrsz != 4 && ptrsz != 8 { + return &DecodingError{7, "invalid pointer size", ptrsz} + } + data = data[8:] + } + var s sym + p := data + for len(p) >= 4 { + var typ byte + if newTable { + // Symbol type, value, Go type. + typ = p[0] & 0x3F + wideValue := p[0]&0x40 != 0 + goType := p[0]&0x80 != 0 + if typ < 26 { + typ += 'A' + } else { + typ += 'a' - 26 + } + s.typ = typ + p = p[1:] + if wideValue { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } else { + // varint value + s.value = 0 + shift := uint(0) + for len(p) > 0 && p[0]&0x80 != 0 { + s.value |= uint64(p[0]&0x7F) << shift + shift += 7 + p = p[1:] + } + if len(p) == 0 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.value |= uint64(p[0]) << shift + p = p[1:] + } + if goType { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width go type + if ptrsz == 8 { + s.gotype = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.gotype = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } + } else { + // Value, symbol type. + s.value = uint64(order.Uint32(p[0:4])) + if len(p) < 5 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + typ = p[4] + if typ&0x80 == 0 { + return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + } + typ &^= 0x80 + s.typ = typ + p = p[5:] + } + + // Name. + var i int + var nnul int + for i = 0; i < len(p); i++ { + if p[i] == 0 { + nnul = 1 + break + } + } + switch typ { + case 'z', 'Z': + p = p[i+nnul:] + for i = 0; i+2 <= len(p); i += 2 { + if p[i] == 0 && p[i+1] == 0 { + nnul = 2 + break + } + } + } + if len(p) < i+nnul { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.name = p[0:i] + i += nnul + p = p[i:] + + if !newTable { + if len(p) < 4 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // Go type. + s.gotype = uint64(order.Uint32(p[:4])) + p = p[4:] + } + fn(s) + } + return nil +} + +// NewTable decodes the Go symbol table in data, +// returning an in-memory representation. +func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { + var n int + err := walksymtab(symtab, func(s sym) error { + n++ + return nil + }) + if err != nil { + return nil, err + } + + var t Table + if pcln.isGo12() { + t.go12line = pcln + } + fname := make(map[uint16]string) + t.Syms = make([]Sym, 0, n) + nf := 0 + nz := 0 + lasttyp := uint8(0) + err = walksymtab(symtab, func(s sym) error { + n := len(t.Syms) + t.Syms = t.Syms[0 : n+1] + ts := &t.Syms[n] + ts.Type = s.typ + ts.Value = uint64(s.value) + ts.GoType = uint64(s.gotype) + switch s.typ { + default: + // rewrite name to use . instead of · (c2 b7) + w := 0 + b := s.name + for i := 0; i < len(b); i++ { + if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 { + i++ + b[i] = '.' + } + b[w] = b[i] + w++ + } + ts.Name = string(s.name[0:w]) + case 'z', 'Z': + if lasttyp != 'z' && lasttyp != 'Z' { + nz++ + } + for i := 0; i < len(s.name); i += 2 { + eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) + elt, ok := fname[eltIdx] + if !ok { + return &DecodingError{-1, "bad filename code", eltIdx} + } + if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { + ts.Name += "/" + } + ts.Name += elt + } + } + switch s.typ { + case 'T', 't', 'L', 'l': + nf++ + case 'f': + fname[uint16(s.value)] = ts.Name + } + lasttyp = s.typ + return nil + }) + if err != nil { + return nil, err + } + + t.Funcs = make([]Func, 0, nf) + t.Files = make(map[string]*Obj) + + var obj *Obj + if t.go12line != nil { + // Put all functions into one Obj. + t.Objs = make([]Obj, 1) + obj = &t.Objs[0] + t.go12line.go12MapFiles(t.Files, obj) + } else { + t.Objs = make([]Obj, 0, nz) + } + + // Count text symbols and attach frame sizes, parameters, and + // locals to them. Also, find object file boundaries. + lastf := 0 + for i := 0; i < len(t.Syms); i++ { + sym := &t.Syms[i] + switch sym.Type { + case 'Z', 'z': // path symbol + if t.go12line != nil { + // Go 1.2 binaries have the file information elsewhere. Ignore. + break + } + // Finish the current object + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + lastf = len(t.Funcs) + + // Start new object + n := len(t.Objs) + t.Objs = t.Objs[0 : n+1] + obj = &t.Objs[n] + + // Count & copy path symbols + var end int + for end = i + 1; end < len(t.Syms); end++ { + if c := t.Syms[end].Type; c != 'Z' && c != 'z' { + break + } + } + obj.Paths = t.Syms[i:end] + i = end - 1 // loop will i++ + + // Record file names + depth := 0 + for j := range obj.Paths { + s := &obj.Paths[j] + if s.Name == "" { + depth-- + } else { + if depth == 0 { + t.Files[s.Name] = obj + } + depth++ + } + } + + case 'T', 't', 'L', 'l': // text symbol + if n := len(t.Funcs); n > 0 { + t.Funcs[n-1].End = sym.Value + } + if sym.Name == "etext" { + continue + } + + // Count parameter and local (auto) syms + var np, na int + var end int + countloop: + for end = i + 1; end < len(t.Syms); end++ { + switch t.Syms[end].Type { + case 'T', 't', 'L', 'l', 'Z', 'z': + break countloop + case 'p': + np++ + case 'a': + na++ + } + } + + // Fill in the function symbol + n := len(t.Funcs) + t.Funcs = t.Funcs[0 : n+1] + fn := &t.Funcs[n] + sym.Func = fn + fn.Params = make([]*Sym, 0, np) + fn.Locals = make([]*Sym, 0, na) + fn.Sym = sym + fn.Entry = sym.Value + fn.Obj = obj + if t.go12line != nil { + // All functions share the same line table. + // It knows how to narrow down to a specific + // function quickly. + fn.LineTable = t.go12line + } else if pcln != nil { + fn.LineTable = pcln.slice(fn.Entry) + pcln = fn.LineTable + } + for j := i; j < end; j++ { + s := &t.Syms[j] + switch s.Type { + case 'm': + fn.FrameSize = int(s.Value) + case 'p': + n := len(fn.Params) + fn.Params = fn.Params[0 : n+1] + fn.Params[n] = s + case 'a': + n := len(fn.Locals) + fn.Locals = fn.Locals[0 : n+1] + fn.Locals[n] = s + } + } + i = end - 1 // loop will i++ + } + } + + if t.go12line != nil && nf == 0 { + t.Funcs = t.go12line.go12Funcs() + } + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + return &t, nil +} + +// PCToFunc returns the function containing the program counter pc, +// or nil if there is no such function. +func (t *Table) PCToFunc(pc uint64) *Func { + funcs := t.Funcs + for len(funcs) > 0 { + m := len(funcs) / 2 + fn := &funcs[m] + switch { + case pc < fn.Entry: + funcs = funcs[0:m] + case fn.Entry <= pc && pc < fn.End: + return fn + default: + funcs = funcs[m+1:] + } + } + return nil +} + +// PCToLine looks up line number information for a program counter. +// If there is no information, it returns fn == nil. +func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) { + if fn = t.PCToFunc(pc); fn == nil { + return + } + if t.go12line != nil { + file = t.go12line.go12PCToFile(pc) + line = t.go12line.go12PCToLine(pc) + } else { + file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc)) + } + return +} + +// PCToSPAdj returns the stack pointer adjustment for a program counter. +func (t *Table) PCToSPAdj(pc uint64) (spadj int) { + if fn := t.PCToFunc(pc); fn == nil { + return 0 + } + if t.go12line != nil { + return t.go12line.go12PCToSPAdj(pc) + } + return 0 +} + +// LineToPC looks up the first program counter on the given line in +// the named file. It returns UnknownPathError or UnknownLineError if +// there is an error looking up this line. +func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) { + obj, ok := t.Files[file] + if !ok { + return 0, nil, UnknownFileError(file) + } + + if t.go12line != nil { + pc := t.go12line.go12LineToPC(file, line) + if pc == 0 { + return 0, nil, &UnknownLineError{file, line} + } + return pc, t.PCToFunc(pc), nil + } + + abs, err := obj.alineFromLine(file, line) + if err != nil { + return + } + for i := range obj.Funcs { + f := &obj.Funcs[i] + pc := f.LineTable.LineToPC(abs, f.End) + if pc != 0 { + return pc, f, nil + } + } + return 0, nil, &UnknownLineError{file, line} +} + +// LookupSym returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupSym(name string) *Sym { + // TODO(austin) Maybe make a map + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Name == name { + return s + } + } + } + return nil +} + +// LookupFunc returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupFunc(name string) *Func { + for i := range t.Funcs { + f := &t.Funcs[i] + if f.Sym.Name == name { + return f + } + } + return nil +} + +// SymByAddr returns the text, data, or bss symbol starting at the given address. +func (t *Table) SymByAddr(addr uint64) *Sym { + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Value == addr { + return s + } + } + } + return nil +} + +/* + * Object files + */ + +// This is legacy code for Go 1.1 and earlier, which used the +// Plan 9 format for pc-line tables. This code was never quite +// correct. It's probably very close, and it's usually correct, but +// we never quite found all the corner cases. +// +// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab. + +func (o *Obj) lineFromAline(aline int) (string, int) { + type stackEnt struct { + path string + start int + offset int + prev *stackEnt + } + + noPath := &stackEnt{"", 0, 0, nil} + tos := noPath + +pathloop: + for _, s := range o.Paths { + val := int(s.Value) + switch { + case val > aline: + break pathloop + + case val == 1: + // Start a new stack + tos = &stackEnt{s.Name, val, 0, noPath} + + case s.Name == "": + // Pop + if tos == noPath { + return "", 0 + } + tos.prev.offset += val - tos.start + tos = tos.prev + + default: + // Push + tos = &stackEnt{s.Name, val, 0, tos} + } + } + + if tos == noPath { + return "", 0 + } + return tos.path, aline - tos.start - tos.offset + 1 +} + +func (o *Obj) alineFromLine(path string, line int) (int, error) { + if line < 1 { + return 0, &UnknownLineError{path, line} + } + + for i, s := range o.Paths { + // Find this path + if s.Name != path { + continue + } + + // Find this line at this stack level + depth := 0 + var incstart int + line += int(s.Value) + pathloop: + for _, s := range o.Paths[i:] { + val := int(s.Value) + switch { + case depth == 1 && val >= line: + return line - 1, nil + + case s.Name == "": + depth-- + if depth == 0 { + break pathloop + } else if depth == 1 { + line += val - incstart + } + + default: + if depth == 1 { + incstart = val + } + depth++ + } + } + return 0, &UnknownLineError{path, line} + } + return 0, UnknownFileError(path) +} + +/* + * Errors + */ + +// UnknownFileError represents a failure to find the specific file in +// the symbol table. +type UnknownFileError string + +func (e UnknownFileError) Error() string { return "unknown file: " + string(e) } + +// UnknownLineError represents a failure to map a line to a program +// counter, either because the line is beyond the bounds of the file +// or because there is no code on the given line. +type UnknownLineError struct { + File string + Line int +} + +func (e *UnknownLineError) Error() string { + return "no code at " + e.File + ":" + strconv.Itoa(e.Line) +} + +// DecodingError represents an error during the decoding of +// the symbol table. +type DecodingError struct { + off int + msg string + val interface{} +} + +func (e *DecodingError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" at byte %#x", e.off) + return msg +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/local/local.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/local/local.go new file mode 100644 index 0000000000..b75004bfd5 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/local/local.go @@ -0,0 +1,205 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 + +// Package local provides access to a local program. +package local + +import ( + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/protocol" +) + +var _ debug.Program = (*Program)(nil) +var _ debug.File = (*File)(nil) + +// Program implements the debug.Program interface. +// Through that interface it provides access to a program being debugged. +type Program struct { + s *server.Server +} + +// New creates a new program from the specified file. +// The program can then be started by the Run method. +func New(textFile string) (*Program, error) { + s, err := server.New(textFile) + return &Program{s: s}, err +} + +func (p *Program) Open(name string, mode string) (debug.File, error) { + req := protocol.OpenRequest{ + Name: name, + Mode: mode, + } + var resp protocol.OpenResponse + err := p.s.Open(&req, &resp) + if err != nil { + return nil, err + } + f := &File{ + prog: p, + fd: resp.FD, + } + return f, nil +} + +func (p *Program) Run(args ...string) (debug.Status, error) { + req := protocol.RunRequest{args} + var resp protocol.RunResponse + err := p.s.Run(&req, &resp) + if err != nil { + return debug.Status{}, err + } + return resp.Status, nil +} + +func (p *Program) Stop() (debug.Status, error) { + panic("unimplemented") +} + +func (p *Program) Resume() (debug.Status, error) { + req := protocol.ResumeRequest{} + var resp protocol.ResumeResponse + err := p.s.Resume(&req, &resp) + if err != nil { + return debug.Status{}, err + } + return resp.Status, nil +} + +func (p *Program) Kill() (debug.Status, error) { + panic("unimplemented") +} + +func (p *Program) Breakpoint(address uint64) ([]uint64, error) { + req := protocol.BreakpointRequest{ + Address: address, + } + var resp protocol.BreakpointResponse + err := p.s.Breakpoint(&req, &resp) + return resp.PCs, err +} + +func (p *Program) BreakpointAtFunction(name string) ([]uint64, error) { + req := protocol.BreakpointAtFunctionRequest{ + Function: name, + } + var resp protocol.BreakpointResponse + err := p.s.BreakpointAtFunction(&req, &resp) + return resp.PCs, err +} + +func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) { + req := protocol.BreakpointAtLineRequest{ + File: file, + Line: line, + } + var resp protocol.BreakpointResponse + err := p.s.BreakpointAtLine(&req, &resp) + return resp.PCs, err +} + +func (p *Program) DeleteBreakpoints(pcs []uint64) error { + req := protocol.DeleteBreakpointsRequest{PCs: pcs} + var resp protocol.DeleteBreakpointsResponse + return p.s.DeleteBreakpoints(&req, &resp) +} + +func (p *Program) Eval(expr string) ([]string, error) { + req := protocol.EvalRequest{ + Expr: expr, + } + var resp protocol.EvalResponse + err := p.s.Eval(&req, &resp) + return resp.Result, err +} + +func (p *Program) Evaluate(e string) (debug.Value, error) { + req := protocol.EvaluateRequest{ + Expression: e, + } + var resp protocol.EvaluateResponse + err := p.s.Evaluate(&req, &resp) + return resp.Result, err +} + +func (p *Program) Frames(count int) ([]debug.Frame, error) { + req := protocol.FramesRequest{ + Count: count, + } + var resp protocol.FramesResponse + err := p.s.Frames(&req, &resp) + return resp.Frames, err +} + +func (p *Program) Goroutines() ([]*debug.Goroutine, error) { + req := protocol.GoroutinesRequest{} + var resp protocol.GoroutinesResponse + err := p.s.Goroutines(&req, &resp) + return resp.Goroutines, err +} + +func (p *Program) VarByName(name string) (debug.Var, error) { + req := protocol.VarByNameRequest{Name: name} + var resp protocol.VarByNameResponse + err := p.s.VarByName(&req, &resp) + return resp.Var, err +} + +func (p *Program) Value(v debug.Var) (debug.Value, error) { + req := protocol.ValueRequest{Var: v} + var resp protocol.ValueResponse + err := p.s.Value(&req, &resp) + return resp.Value, err +} + +func (p *Program) MapElement(m debug.Map, index uint64) (debug.Var, debug.Var, error) { + req := protocol.MapElementRequest{Map: m, Index: index} + var resp protocol.MapElementResponse + err := p.s.MapElement(&req, &resp) + return resp.Key, resp.Value, err +} + +// File implements the debug.File interface, providing access +// to file-like resources associated with the target program. +type File struct { + prog *Program // The Program associated with the file. + fd int // File descriptor. +} + +func (f *File) ReadAt(p []byte, offset int64) (int, error) { + req := protocol.ReadAtRequest{ + FD: f.fd, + Len: len(p), + Offset: offset, + } + var resp protocol.ReadAtResponse + err := f.prog.s.ReadAt(&req, &resp) + return copy(p, resp.Data), err +} + +func (f *File) WriteAt(p []byte, offset int64) (int, error) { + panic("unimplemented") +} + +func (f *File) Close() error { + req := protocol.CloseRequest{ + FD: f.fd, + } + var resp protocol.CloseResponse + err := f.prog.s.Close(&req, &resp) + return err +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/fat.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/fat.go new file mode 100644 index 0000000000..c3110002df --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/fat.go @@ -0,0 +1,156 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 macho + +import ( + "encoding/binary" + "fmt" + "io" + "os" +) + +// A FatFile is a Mach-O universal binary that contains at least one architecture. +type FatFile struct { + Magic uint32 + Arches []FatArch + closer io.Closer +} + +// A FatArchHeader represents a fat header for a specific image architecture. +type FatArchHeader struct { + Cpu Cpu + SubCpu uint32 + Offset uint32 + Size uint32 + Align uint32 +} + +const fatArchHeaderSize = 5 * 4 + +// A FatArch is a Mach-O File inside a FatFile. +type FatArch struct { + FatArchHeader + *File +} + +// ErrNotFat is returned from NewFatFile or OpenFat when the file is not a +// universal binary but may be a thin binary, based on its magic number. +var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil} + +// NewFatFile creates a new FatFile for accessing all the Mach-O images in a +// universal binary. The Mach-O binary is expected to start at position 0 in +// the ReaderAt. +func NewFatFile(r io.ReaderAt) (*FatFile, error) { + var ff FatFile + sr := io.NewSectionReader(r, 0, 1<<63-1) + + // Read the fat_header struct, which is always in big endian. + // Start with the magic number. + err := binary.Read(sr, binary.BigEndian, &ff.Magic) + if err != nil { + return nil, &FormatError{0, "error reading magic number", nil} + } else if ff.Magic != MagicFat { + // See if this is a Mach-O file via its magic number. The magic + // must be converted to little endian first though. + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], ff.Magic) + leMagic := binary.LittleEndian.Uint32(buf[:]) + if leMagic == Magic32 || leMagic == Magic64 { + return nil, ErrNotFat + } else { + return nil, &FormatError{0, "invalid magic number", nil} + } + } + offset := int64(4) + + // Read the number of FatArchHeaders that come after the fat_header. + var narch uint32 + err = binary.Read(sr, binary.BigEndian, &narch) + if err != nil { + return nil, &FormatError{offset, "invalid fat_header", nil} + } + offset += 4 + + if narch < 1 { + return nil, &FormatError{offset, "file contains no images", nil} + } + + // Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure + // there are not duplicate architectures. + seenArches := make(map[uint64]bool, narch) + // Make sure that all images are for the same MH_ type. + var machoType Type + + // Following the fat_header comes narch fat_arch structs that index + // Mach-O images further in the file. + ff.Arches = make([]FatArch, narch) + for i := uint32(0); i < narch; i++ { + fa := &ff.Arches[i] + err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader) + if err != nil { + return nil, &FormatError{offset, "invalid fat_arch header", nil} + } + offset += fatArchHeaderSize + + fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size)) + fa.File, err = NewFile(fr) + if err != nil { + return nil, err + } + + // Make sure the architecture for this image is not duplicate. + seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu) + if o, k := seenArches[seenArch]; o || k { + return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil} + } + seenArches[seenArch] = true + + // Make sure the Mach-O type matches that of the first image. + if i == 0 { + machoType = fa.Type + } else { + if fa.Type != machoType { + return nil, &FormatError{offset, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType), nil} + } + } + } + + return &ff, nil +} + +// OpenFat opens the named file using os.Open and prepares it for use as a Mach-O +// universal binary. +func OpenFat(name string) (ff *FatFile, err error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err = NewFatFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return +} + +func (ff *FatFile) Close() error { + var err error + if ff.closer != nil { + err = ff.closer.Close() + ff.closer = nil + } + return err +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/file.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/file.go new file mode 100644 index 0000000000..17df335714 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/file.go @@ -0,0 +1,535 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 macho implements access to Mach-O object files. +package macho + +// High level access to low level data structures. + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "os" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +// A File represents an open Mach-O file. +type File struct { + FileHeader + ByteOrder binary.ByteOrder + Loads []Load + Sections []*Section + + Symtab *Symtab + Dysymtab *Dysymtab + + closer io.Closer +} + +// A Load represents any Mach-O load command. +type Load interface { + Raw() []byte +} + +// A LoadBytes is the uninterpreted bytes of a Mach-O load command. +type LoadBytes []byte + +func (b LoadBytes) Raw() []byte { return b } + +// A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command. +type SegmentHeader struct { + Cmd LoadCmd + Len uint32 + Name string + Addr uint64 + Memsz uint64 + Offset uint64 + Filesz uint64 + Maxprot uint32 + Prot uint32 + Nsect uint32 + Flag uint32 +} + +// A Segment represents a Mach-O 32-bit or 64-bit load segment command. +type Segment struct { + LoadBytes + SegmentHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the segment. +func (s *Segment) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the segment. +func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +type SectionHeader struct { + Name string + Seg string + Addr uint64 + Size uint64 + Offset uint32 + Align uint32 + Reloff uint32 + Nreloc uint32 + Flags uint32 +} + +type Section struct { + SectionHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the Mach-O section. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the Mach-O section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +// A Dylib represents a Mach-O load dynamic library command. +type Dylib struct { + LoadBytes + Name string + Time uint32 + CurrentVersion uint32 + CompatVersion uint32 +} + +// A Symtab represents a Mach-O symbol table command. +type Symtab struct { + LoadBytes + SymtabCmd + Syms []Symbol +} + +// A Dysymtab represents a Mach-O dynamic symbol table command. +type Dysymtab struct { + LoadBytes + DysymtabCmd + IndirectSyms []uint32 // indices into Symtab.Syms +} + +/* + * Mach-O reader + */ + +// FormatError is returned by some operations if the data does +// not have the correct format for an object file. +type FormatError struct { + off int64 + msg string + val interface{} +} + +func (e *FormatError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" in record at byte %#x", e.off) + return msg +} + +// Open opens the named file using os.Open and prepares it for use as a Mach-O binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// NewFile creates a new File for accessing a Mach-O binary in an underlying reader. +// The Mach-O binary is expected to start at position 0 in the ReaderAt. +func NewFile(r io.ReaderAt) (*File, error) { + f := new(File) + sr := io.NewSectionReader(r, 0, 1<<63-1) + + // Read and decode Mach magic to determine byte order, size. + // Magic32 and Magic64 differ only in the bottom bit. + var ident [4]byte + if _, err := r.ReadAt(ident[0:], 0); err != nil { + return nil, err + } + be := binary.BigEndian.Uint32(ident[0:]) + le := binary.LittleEndian.Uint32(ident[0:]) + switch Magic32 &^ 1 { + case be &^ 1: + f.ByteOrder = binary.BigEndian + f.Magic = be + case le &^ 1: + f.ByteOrder = binary.LittleEndian + f.Magic = le + default: + return nil, &FormatError{0, "invalid magic number", nil} + } + + // Read entire file header. + if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil { + return nil, err + } + + // Then load commands. + offset := int64(fileHeaderSize32) + if f.Magic == Magic64 { + offset = fileHeaderSize64 + } + dat := make([]byte, f.Cmdsz) + if _, err := r.ReadAt(dat, offset); err != nil { + return nil, err + } + f.Loads = make([]Load, f.Ncmd) + bo := f.ByteOrder + for i := range f.Loads { + // Each load command begins with uint32 command and length. + if len(dat) < 8 { + return nil, &FormatError{offset, "command block too small", nil} + } + cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8]) + if siz < 8 || siz > uint32(len(dat)) { + return nil, &FormatError{offset, "invalid command block size", nil} + } + var cmddat []byte + cmddat, dat = dat[0:siz], dat[siz:] + offset += int64(siz) + var s *Segment + switch cmd { + default: + f.Loads[i] = LoadBytes(cmddat) + + case LoadCmdDylib: + var hdr DylibCmd + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + l := new(Dylib) + if hdr.Name >= uint32(len(cmddat)) { + return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name} + } + l.Name = cstring(cmddat[hdr.Name:]) + l.Time = hdr.Time + l.CurrentVersion = hdr.CurrentVersion + l.CompatVersion = hdr.CompatVersion + l.LoadBytes = LoadBytes(cmddat) + f.Loads[i] = l + + case LoadCmdSymtab: + var hdr SymtabCmd + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + strtab := make([]byte, hdr.Strsize) + if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil { + return nil, err + } + var symsz int + if f.Magic == Magic64 { + symsz = 16 + } else { + symsz = 12 + } + symdat := make([]byte, int(hdr.Nsyms)*symsz) + if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { + return nil, err + } + st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) + if err != nil { + return nil, err + } + f.Loads[i] = st + f.Symtab = st + + case LoadCmdDysymtab: + var hdr DysymtabCmd + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + dat := make([]byte, hdr.Nindirectsyms*4) + if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { + return nil, err + } + x := make([]uint32, hdr.Nindirectsyms) + if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil { + return nil, err + } + st := new(Dysymtab) + st.LoadBytes = LoadBytes(cmddat) + st.DysymtabCmd = hdr + st.IndirectSyms = x + f.Loads[i] = st + f.Dysymtab = st + + case LoadCmdSegment: + var seg32 Segment32 + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &seg32); err != nil { + return nil, err + } + s = new(Segment) + s.LoadBytes = cmddat + s.Cmd = cmd + s.Len = siz + s.Name = cstring(seg32.Name[0:]) + s.Addr = uint64(seg32.Addr) + s.Memsz = uint64(seg32.Memsz) + s.Offset = uint64(seg32.Offset) + s.Filesz = uint64(seg32.Filesz) + s.Maxprot = seg32.Maxprot + s.Prot = seg32.Prot + s.Nsect = seg32.Nsect + s.Flag = seg32.Flag + f.Loads[i] = s + for i := 0; i < int(s.Nsect); i++ { + var sh32 Section32 + if err := binary.Read(b, bo, &sh32); err != nil { + return nil, err + } + sh := new(Section) + sh.Name = cstring(sh32.Name[0:]) + sh.Seg = cstring(sh32.Seg[0:]) + sh.Addr = uint64(sh32.Addr) + sh.Size = uint64(sh32.Size) + sh.Offset = sh32.Offset + sh.Align = sh32.Align + sh.Reloff = sh32.Reloff + sh.Nreloc = sh32.Nreloc + sh.Flags = sh32.Flags + f.pushSection(sh, r) + } + + case LoadCmdSegment64: + var seg64 Segment64 + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &seg64); err != nil { + return nil, err + } + s = new(Segment) + s.LoadBytes = cmddat + s.Cmd = cmd + s.Len = siz + s.Name = cstring(seg64.Name[0:]) + s.Addr = seg64.Addr + s.Memsz = seg64.Memsz + s.Offset = seg64.Offset + s.Filesz = seg64.Filesz + s.Maxprot = seg64.Maxprot + s.Prot = seg64.Prot + s.Nsect = seg64.Nsect + s.Flag = seg64.Flag + f.Loads[i] = s + for i := 0; i < int(s.Nsect); i++ { + var sh64 Section64 + if err := binary.Read(b, bo, &sh64); err != nil { + return nil, err + } + sh := new(Section) + sh.Name = cstring(sh64.Name[0:]) + sh.Seg = cstring(sh64.Seg[0:]) + sh.Addr = sh64.Addr + sh.Size = sh64.Size + sh.Offset = sh64.Offset + sh.Align = sh64.Align + sh.Reloff = sh64.Reloff + sh.Nreloc = sh64.Nreloc + sh.Flags = sh64.Flags + f.pushSection(sh, r) + } + } + if s != nil { + s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz)) + s.ReaderAt = s.sr + } + } + return f, nil +} + +func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { + bo := f.ByteOrder + symtab := make([]Symbol, hdr.Nsyms) + b := bytes.NewReader(symdat) + for i := range symtab { + var n Nlist64 + if f.Magic == Magic64 { + if err := binary.Read(b, bo, &n); err != nil { + return nil, err + } + } else { + var n32 Nlist32 + if err := binary.Read(b, bo, &n32); err != nil { + return nil, err + } + n.Name = n32.Name + n.Type = n32.Type + n.Sect = n32.Sect + n.Desc = n32.Desc + n.Value = uint64(n32.Value) + } + sym := &symtab[i] + if n.Name >= uint32(len(strtab)) { + return nil, &FormatError{offset, "invalid name in symbol table", n.Name} + } + sym.Name = cstring(strtab[n.Name:]) + sym.Type = n.Type + sym.Sect = n.Sect + sym.Desc = n.Desc + sym.Value = n.Value + } + st := new(Symtab) + st.LoadBytes = LoadBytes(cmddat) + st.Syms = symtab + return st, nil +} + +func (f *File) pushSection(sh *Section, r io.ReaderAt) { + f.Sections = append(f.Sections, sh) + sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) + sh.ReaderAt = sh.sr +} + +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[0:i]) +} + +// Segment returns the first Segment with the given name, or nil if no such segment exists. +func (f *File) Segment(name string) *Segment { + for _, l := range f.Loads { + if s, ok := l.(*Segment); ok && s.Name == name { + return s + } + } + return nil +} + +// Section returns the first section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s + } + } + return nil +} + +// DWARF returns the DWARF debug information for the Mach-O file. +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the required ones, and the debug/dwarf package + // does not use the others, so don't bother loading them. + var names = [...]string{"abbrev", "frame", "info", "line", "str"} + var dat [len(names)][]byte + for i, name := range names { + name = "__debug_" + name + s := f.Section(name) + if s == nil { + continue + } + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + + abbrev, frame, info, line, str := dat[0], dat[1], dat[2], dat[3], dat[4] + return dwarf.New(abbrev, nil, frame, info, line, nil, nil, str) +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +func (f *File) ImportedSymbols() ([]string, error) { + if f.Dysymtab == nil || f.Symtab == nil { + return nil, &FormatError{0, "missing symbol table", nil} + } + + st := f.Symtab + dt := f.Dysymtab + var all []string + for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { + all = append(all, s.Name) + } + return all, nil +} + +// ImportedLibraries returns the paths of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + var all []string + for _, l := range f.Loads { + if lib, ok := l.(*Dylib); ok { + all = append(all, lib.Name) + } + } + return all, nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/file_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/file_test.go new file mode 100644 index 0000000000..852a5ef242 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/file_test.go @@ -0,0 +1,220 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 macho + +import ( + "reflect" + "testing" +) + +type fileTest struct { + file string + hdr FileHeader + segments []*SegmentHeader + sections []*SectionHeader +} + +var fileTests = []fileTest{ + { + "testdata/gcc-386-darwin-exec", + FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0xc, 0x3c0, 0x85}, + []*SegmentHeader{ + {LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0}, + {LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0}, + {LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0}, + {LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0}, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + []*SectionHeader{ + {"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400}, + {"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2}, + {"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0}, + {"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008}, + }, + }, + { + "testdata/gcc-amd64-darwin-exec", + FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85}, + []*SegmentHeader{ + {LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0}, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + []*SectionHeader{ + {"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7}, + }, + }, + { + "testdata/gcc-amd64-darwin-exec-debug", + FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0}, + []*SegmentHeader{ + nil, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0}, + }, + []*SectionHeader{ + {"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7}, + {"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0}, + {"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0}, + {"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0}, + {"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0}, + {"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0}, + {"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0}, + {"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0}, + }, + }, +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i] + + f, err := Open(tt.file) + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) + continue + } + for i, l := range f.Loads { + if i >= len(tt.segments) { + break + } + sh := tt.segments[i] + s, ok := l.(*Segment) + if sh == nil { + if ok { + t.Errorf("open %s, section %d: skipping %#v\n", tt.file, i, &s.SegmentHeader) + } + continue + } + if !ok { + t.Errorf("open %s, section %d: not *Segment\n", tt.file, i) + continue + } + have := &s.SegmentHeader + want := sh + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, segment %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn := len(tt.segments) + fn := len(f.Loads) + if tn != fn { + t.Errorf("open %s: len(Loads) = %d, want %d", tt.file, fn, tn) + } + + for i, sh := range f.Sections { + if i >= len(tt.sections) { + break + } + have := &sh.SectionHeader + want := tt.sections[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn = len(tt.sections) + fn = len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + + } +} + +func TestOpenFailure(t *testing.T) { + filename := "file.go" // not a Mach-O file + _, err := Open(filename) // don't crash + if err == nil { + t.Errorf("open %s: succeeded unexpectedly", filename) + } +} + +func TestOpenFat(t *testing.T) { + ff, err := OpenFat("testdata/fat-gcc-386-amd64-darwin-exec") + if err != nil { + t.Fatal(err) + } + + if ff.Magic != MagicFat { + t.Errorf("OpenFat: got magic number %#x, want %#x", ff.Magic, MagicFat) + } + if len(ff.Arches) != 2 { + t.Errorf("OpenFat: got %d architectures, want 2", len(ff.Arches)) + } + + for i := range ff.Arches { + arch := &ff.Arches[i] + ftArch := &fileTests[i] + + if arch.Cpu != ftArch.hdr.Cpu || arch.SubCpu != ftArch.hdr.SubCpu { + t.Errorf("OpenFat: architecture #%d got cpu=%#x subtype=%#x, expected cpu=%#x, subtype=%#x", i, arch.Cpu, arch.SubCpu, ftArch.hdr.Cpu, ftArch.hdr.SubCpu) + } + + if !reflect.DeepEqual(arch.FileHeader, ftArch.hdr) { + t.Errorf("OpenFat header:\n\tgot %#v\n\twant %#v\n", arch.FileHeader, ftArch.hdr) + } + } +} + +func TestOpenFatFailure(t *testing.T) { + filename := "file.go" // not a Mach-O file + if _, err := OpenFat(filename); err == nil { + t.Errorf("OpenFat %s: succeeded unexpectedly", filename) + } + + filename = "testdata/gcc-386-darwin-exec" // not a fat Mach-O + ff, err := OpenFat(filename) + if err != ErrNotFat { + t.Errorf("OpenFat %s: got %v, want ErrNotFat", filename, err) + } + if ff != nil { + t.Errorf("OpenFat %s: got %v, want nil", filename, ff) + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/macho.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/macho.go new file mode 100644 index 0000000000..06b640e302 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/macho.go @@ -0,0 +1,326 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// Mach-O header data structures +// http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +package macho // import "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho" + +import "strconv" + +// A FileHeader represents a Mach-O file header. +type FileHeader struct { + Magic uint32 + Cpu Cpu + SubCpu uint32 + Type Type + Ncmd uint32 + Cmdsz uint32 + Flags uint32 +} + +const ( + fileHeaderSize32 = 7 * 4 + fileHeaderSize64 = 8 * 4 +) + +const ( + Magic32 uint32 = 0xfeedface + Magic64 uint32 = 0xfeedfacf + MagicFat uint32 = 0xcafebabe +) + +// A Type is the Mach-O file type, e.g. an object file, executable, or dynamic library. +type Type uint32 + +const ( + TypeObj Type = 1 + TypeExec Type = 2 + TypeDylib Type = 6 + TypeBundle Type = 8 +) + +// A Cpu is a Mach-O cpu type. +type Cpu uint32 + +const cpuArch64 = 0x01000000 + +const ( + Cpu386 Cpu = 7 + CpuAmd64 Cpu = Cpu386 | cpuArch64 + CpuArm Cpu = 12 + CpuPpc Cpu = 18 + CpuPpc64 Cpu = CpuPpc | cpuArch64 +) + +var cpuStrings = []intName{ + {uint32(Cpu386), "Cpu386"}, + {uint32(CpuAmd64), "CpuAmd64"}, + {uint32(CpuArm), "CpuArm"}, + {uint32(CpuPpc), "CpuPpc"}, + {uint32(CpuPpc64), "CpuPpc64"}, +} + +func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) } +func (i Cpu) GoString() string { return stringName(uint32(i), cpuStrings, true) } + +// A LoadCmd is a Mach-O load command. +type LoadCmd uint32 + +const ( + LoadCmdSegment LoadCmd = 1 + LoadCmdSymtab LoadCmd = 2 + LoadCmdThread LoadCmd = 4 + LoadCmdUnixThread LoadCmd = 5 // thread+stack + LoadCmdDysymtab LoadCmd = 11 + LoadCmdDylib LoadCmd = 12 + LoadCmdDylinker LoadCmd = 15 + LoadCmdSegment64 LoadCmd = 25 +) + +var cmdStrings = []intName{ + {uint32(LoadCmdSegment), "LoadCmdSegment"}, + {uint32(LoadCmdThread), "LoadCmdThread"}, + {uint32(LoadCmdUnixThread), "LoadCmdUnixThread"}, + {uint32(LoadCmdDylib), "LoadCmdDylib"}, + {uint32(LoadCmdSegment64), "LoadCmdSegment64"}, +} + +func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) } +func (i LoadCmd) GoString() string { return stringName(uint32(i), cmdStrings, true) } + +// A Segment64 is a 64-bit Mach-O segment load command. +type Segment64 struct { + Cmd LoadCmd + Len uint32 + Name [16]byte + Addr uint64 + Memsz uint64 + Offset uint64 + Filesz uint64 + Maxprot uint32 + Prot uint32 + Nsect uint32 + Flag uint32 +} + +// A Segment32 is a 32-bit Mach-O segment load command. +type Segment32 struct { + Cmd LoadCmd + Len uint32 + Name [16]byte + Addr uint32 + Memsz uint32 + Offset uint32 + Filesz uint32 + Maxprot uint32 + Prot uint32 + Nsect uint32 + Flag uint32 +} + +// A DylibCmd is a Mach-O load dynamic library command. +type DylibCmd struct { + Cmd LoadCmd + Len uint32 + Name uint32 + Time uint32 + CurrentVersion uint32 + CompatVersion uint32 +} + +// A Section32 is a 32-bit Mach-O section header. +type Section32 struct { + Name [16]byte + Seg [16]byte + Addr uint32 + Size uint32 + Offset uint32 + Align uint32 + Reloff uint32 + Nreloc uint32 + Flags uint32 + Reserve1 uint32 + Reserve2 uint32 +} + +// A Section32 is a 64-bit Mach-O section header. +type Section64 struct { + Name [16]byte + Seg [16]byte + Addr uint64 + Size uint64 + Offset uint32 + Align uint32 + Reloff uint32 + Nreloc uint32 + Flags uint32 + Reserve1 uint32 + Reserve2 uint32 + Reserve3 uint32 +} + +// A SymtabCmd is a Mach-O symbol table command. +type SymtabCmd struct { + Cmd LoadCmd + Len uint32 + Symoff uint32 + Nsyms uint32 + Stroff uint32 + Strsize uint32 +} + +// A DysymtabCmd is a Mach-O dynamic symbol table command. +type DysymtabCmd struct { + Cmd LoadCmd + Len uint32 + Ilocalsym uint32 + Nlocalsym uint32 + Iextdefsym uint32 + Nextdefsym uint32 + Iundefsym uint32 + Nundefsym uint32 + Tocoffset uint32 + Ntoc uint32 + Modtaboff uint32 + Nmodtab uint32 + Extrefsymoff uint32 + Nextrefsyms uint32 + Indirectsymoff uint32 + Nindirectsyms uint32 + Extreloff uint32 + Nextrel uint32 + Locreloff uint32 + Nlocrel uint32 +} + +// An Nlist32 is a Mach-O 32-bit symbol table entry. +type Nlist32 struct { + Name uint32 + Type uint8 + Sect uint8 + Desc uint16 + Value uint32 +} + +// An Nlist64 is a Mach-O 64-bit symbol table entry. +type Nlist64 struct { + Name uint32 + Type uint8 + Sect uint8 + Desc uint16 + Value uint64 +} + +// A Symbol is a Mach-O 32-bit or 64-bit symbol table entry. +type Symbol struct { + Name string + Type uint8 + Sect uint8 + Desc uint16 + Value uint64 +} + +// A Thread is a Mach-O thread state command. +type Thread struct { + Cmd LoadCmd + Len uint32 + Type uint32 + Data []uint32 +} + +// Regs386 is the Mach-O 386 register structure. +type Regs386 struct { + AX uint32 + BX uint32 + CX uint32 + DX uint32 + DI uint32 + SI uint32 + BP uint32 + SP uint32 + SS uint32 + FLAGS uint32 + IP uint32 + CS uint32 + DS uint32 + ES uint32 + FS uint32 + GS uint32 +} + +// RegsAMD64 is the Mach-O AMD64 register structure. +type RegsAMD64 struct { + AX uint64 + BX uint64 + CX uint64 + DX uint64 + DI uint64 + SI uint64 + BP uint64 + SP uint64 + R8 uint64 + R9 uint64 + R10 uint64 + R11 uint64 + R12 uint64 + R13 uint64 + R14 uint64 + R15 uint64 + IP uint64 + FLAGS uint64 + CS uint64 + FS uint64 + GS uint64 +} + +type intName struct { + i uint32 + s string +} + +func stringName(i uint32, names []intName, goSyntax bool) string { + for _, n := range names { + if n.i == i { + if goSyntax { + return "macho." + n.s + } + return n.s + } + } + return strconv.FormatUint(uint64(i), 10) +} + +func flagName(i uint32, names []intName, goSyntax bool) string { + s := "" + for _, n := range names { + if n.i&i == n.i { + if len(s) > 0 { + s += "+" + } + if goSyntax { + s += "macho." + } + s += n.s + i -= n.i + } + } + if len(s) == 0 { + return "0x" + strconv.FormatUint(uint64(i), 16) + } + if i != 0 { + s += "+0x" + strconv.FormatUint(uint64(i), 16) + } + return s +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/fat-gcc-386-amd64-darwin-exec b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/fat-gcc-386-amd64-darwin-exec new file mode 100644 index 0000000000..7efd19300b Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/fat-gcc-386-amd64-darwin-exec differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-386-darwin-exec b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-386-darwin-exec new file mode 100755 index 0000000000..03ba1bafac Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-386-darwin-exec differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-amd64-darwin-exec b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-amd64-darwin-exec new file mode 100755 index 0000000000..5155a5a26f Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-amd64-darwin-exec differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-amd64-darwin-exec-debug b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-amd64-darwin-exec-debug new file mode 100644 index 0000000000..a47d3aef78 Binary files /dev/null and b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/gcc-amd64-darwin-exec-debug differ diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/hello.c b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/hello.c new file mode 100644 index 0000000000..a689d3644e --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho/testdata/hello.c @@ -0,0 +1,8 @@ +#include + +int +main(void) +{ + printf("hello, world\n"); + return 0; +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/program.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/program.go new file mode 100644 index 0000000000..8cbf53db3e --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/program.go @@ -0,0 +1,327 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 debug provides the portable interface to a program being debugged. +package debug + +import ( + "fmt" + "io" + "strings" +) + +// Program is the interface to a (possibly remote) program being debugged. +// The process (if any) and text file associated with it may change during +// the session, but many resources are associated with the Program rather +// than process or text file so they persist across debuggging runs. +type Program interface { + // Open opens a virtual file associated with the process. + // Names are things like "text", "mem", "fd/2". + // Mode is one of "r", "w", "rw". + // Return values are open File and error. + // When the target binary is re-run, open files are + // automatically updated to refer to the corresponding + // file in the new process. + Open(name string, mode string) (File, error) + + // Run abandons the current running process, if any, + // and execs a new instance of the target binary file + // (which may have changed underfoot). + // Breakpoints and open files are re-established. + // The call hangs until the program stops executing, + // at which point it returns the program status. + // args contains the command-line arguments for the process. + Run(args ...string) (Status, error) + + // Stop stops execution of the current process but + // does not kill it. + Stop() (Status, error) + + // Resume resumes execution of a stopped process. + // The call hangs until the program stops executing, + // at which point it returns the program status. + Resume() (Status, error) + + // TODO: Step(). Where does the granularity happen, + // on the proxy end or the debugging control end? + + // Kill kills the current process. + Kill() (Status, error) + + // Breakpoint sets a breakpoint at the specified address. + Breakpoint(address uint64) (PCs []uint64, err error) + + // BreakpointAtFunction sets a breakpoint at the start of the specified function. + BreakpointAtFunction(name string) (PCs []uint64, err error) + + // BreakpointAtLine sets a breakpoint at the specified source line. + BreakpointAtLine(file string, line uint64) (PCs []uint64, err error) + + // DeleteBreakpoints removes the breakpoints at the specified addresses. + // Addresses where no breakpoint is set are ignored. + DeleteBreakpoints(pcs []uint64) error + + // Eval evaluates the expression (typically an address) and returns + // its string representation(s). Multivalued expressions such as + // matches for regular expressions return multiple values. + // TODO: change this to multiple functions with more specific names. + // Syntax: + // re:regexp + // Returns a list of symbol names that match the expression + // addr:symbol + // Returns a one-element list holding the hexadecimal + // ("0x1234") value of the address of the symbol + // val:symbol + // Returns a one-element list holding the formatted + // value of the symbol + // 0x1234, 01234, 467 + // Returns a one-element list holding the name of the + // symbol ("main.foo") at that address (hex, octal, decimal). + Eval(expr string) ([]string, error) + + // Evaluate evaluates an expression. Accepts a subset of Go expression syntax: + // basic literals, identifiers, parenthesized expressions, and most operators. + // Only the len function call is available. + // + // The expression can refer to local variables and function parameters of the + // function where the program is stopped. + // + // On success, the type of the value returned will be one of: + // int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, + // complex64, complex128, bool, Pointer, Array, Slice, String, Map, Struct, + // Channel, Func, or Interface. + Evaluate(e string) (Value, error) + + // Frames returns up to count stack frames from where the program + // is currently stopped. + Frames(count int) ([]Frame, error) + + // VarByName returns a Var referring to a global variable with the given name. + // TODO: local variables + VarByName(name string) (Var, error) + + // Value gets the value of a variable by reading the program's memory. + Value(v Var) (Value, error) + + // MapElement returns Vars for the key and value of a map element specified by + // a 0-based index. + MapElement(m Map, index uint64) (Var, Var, error) + + // Goroutines gets the current goroutines. + Goroutines() ([]*Goroutine, error) +} + +type Goroutine struct { + ID int64 + Status GoroutineStatus + StatusString string // A human-readable string explaining the status in more detail. + Function string // Name of the goroutine function. + Caller string // Name of the function that created this goroutine. + StackFrames []Frame +} + +type GoroutineStatus byte + +const ( + Running GoroutineStatus = iota + Queued + Blocked +) + +func (g GoroutineStatus) String() string { + switch g { + case Running: + return "running" + case Queued: + return "queued" + case Blocked: + return "blocked" + } + return "invalid status" +} + +func (g *Goroutine) String() string { + return fmt.Sprintf("goroutine %d [%s] %s -> %s", g.ID, g.StatusString, g.Caller, g.Function) +} + +// A reference to a variable in a program. +// TODO: handle variables stored in registers +type Var struct { + TypeID uint64 // A type identifier, opaque to the user. + Address uint64 // The address of the variable. +} + +// A value read from a remote program. +type Value interface{} + +// Pointer is a Value representing a pointer. +// Note that the TypeID field will be the type of the variable being pointed to, +// not the type of this pointer. +type Pointer struct { + TypeID uint64 // A type identifier, opaque to the user. + Address uint64 // The address of the variable. +} + +// Array is a Value representing an array. +type Array struct { + ElementTypeID uint64 + Address uint64 + Length uint64 // Number of elements in the array + StrideBits uint64 // Number of bits between array entries +} + +// Len returns the number of elements in the array. +func (a Array) Len() uint64 { + return a.Length +} + +// Element returns a Var referring to the given element of the array. +func (a Array) Element(index uint64) Var { + return Var{ + TypeID: a.ElementTypeID, + Address: a.Address + index*(a.StrideBits/8), + } +} + +// Slice is a Value representing a slice. +type Slice struct { + Array + Capacity uint64 +} + +// String is a Value representing a string. +// TODO: a method to access more of a truncated string. +type String struct { + // Length contains the length of the remote string, in bytes. + Length uint64 + // String contains the string itself; it may be truncated to fewer bytes than the value of the Length field. + String string +} + +// Map is a Value representing a map. +type Map struct { + TypeID uint64 + Address uint64 + Length uint64 // Number of elements in the map. +} + +// Struct is a Value representing a struct. +type Struct struct { + Fields []StructField +} + +// StructField represents a field in a struct object. +type StructField struct { + Name string + Var Var +} + +// Channel is a Value representing a channel. +type Channel struct { + ElementTypeID uint64 + Address uint64 // Location of the channel struct in memory. + Buffer uint64 // Location of the buffer; zero for nil channels. + Length uint64 // Number of elements stored in the channel buffer. + Capacity uint64 // Capacity of the buffer; zero for unbuffered channels. + Stride uint64 // Number of bytes between buffer entries. + BufferStart uint64 // Index in the buffer of the element at the head of the queue. +} + +// Element returns a Var referring to the given element of the channel's queue. +// If the channel is unbuffered, nil, or if the index is too large, returns a Var with Address == 0. +func (m Channel) Element(index uint64) Var { + if index >= m.Length { + return Var{ + TypeID: m.ElementTypeID, + Address: 0, + } + } + if index < m.Capacity-m.BufferStart { + // The element is in the part of the queue that occurs later in the buffer + // than the head of the queue. + return Var{ + TypeID: m.ElementTypeID, + Address: m.Buffer + (m.BufferStart+index)*m.Stride, + } + } + // The element is in the part of the queue that has wrapped around to the + // start of the buffer. + return Var{ + TypeID: m.ElementTypeID, + Address: m.Buffer + (m.BufferStart+index-m.Capacity)*m.Stride, + } +} + +// Func is a Value representing a func. +type Func struct { + Address uint64 +} + +// Interface is a Value representing an interface. +type Interface struct{} + +// The File interface provides access to file-like resources in the program. +// It implements only ReaderAt and WriterAt, not Reader and Writer, because +// random access is a far more common pattern for things like symbol tables, +// and because enormous address space of virtual memory makes routines +// like io.Copy dangerous. +type File interface { + io.ReaderAt + io.WriterAt + io.Closer +} + +type Status struct { + PC, SP uint64 +} + +type Frame struct { + // PC is the hardware program counter. + PC uint64 + // SP is the hardware stack pointer. + SP uint64 + // File and Line are the source code location of the PC. + File string + Line uint64 + // Function is the name of this frame's function. + Function string + // FunctionStart is the starting PC of the function. + FunctionStart uint64 + // Params contains the function's parameters. + Params []Param + // Vars contains the function's local variables. + Vars []LocalVar +} + +func (f Frame) String() string { + params := make([]string, len(f.Params)) + for i, p := range f.Params { + params[i] = p.Name // TODO: more information + } + p := strings.Join(params, ", ") + off := f.PC - f.FunctionStart + return fmt.Sprintf("%s(%s)\n\t%s:%d +0x%x", f.Function, p, f.File, f.Line, off) +} + +// Param is a parameter of a function. +type Param struct { + Name string + Var Var +} + +// LocalVar is a local variable of a function. +type LocalVar struct { + Name string + Var Var +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/remote/remote.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/remote/remote.go new file mode 100644 index 0000000000..0790623d65 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/remote/remote.go @@ -0,0 +1,305 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 remote provides remote access to a debugproxy server. +package remote + +import ( + "fmt" + "io" + "net/rpc" + "os" + "os/exec" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/protocol" +) + +var _ debug.Program = (*Program)(nil) +var _ debug.File = (*File)(nil) + +// Program implements the debug.Program interface. +// Through that interface it provides access to a program being +// debugged on a possibly remote machine by communicating +// with a debugproxy adjacent to the target program. +type Program struct { + client *rpc.Client +} + +// DebugproxyCmd is the path to the debugproxy command. It is a variable in case +// the default value, "debugproxy", is not in the $PATH. +var DebugproxyCmd = "debugproxy" + +// New connects to the specified host using SSH, starts DebugproxyCmd +// there, and creates a new program from the specified file. +// The program can then be started by the Run method. +func New(host string, textFile string) (*Program, error) { + // TODO: add args. + cmdStrs := []string{"/usr/bin/ssh", host, DebugproxyCmd, "-text", textFile} + if host == "localhost" { + cmdStrs = cmdStrs[2:] + } + cmd := exec.Command(cmdStrs[0], cmdStrs[1:]...) + stdin, toStdin, err := os.Pipe() + if err != nil { + return nil, err + } + fromStdout, stdout, err := os.Pipe() + if err != nil { + return nil, err + } + cmd.Stdin = stdin + cmd.Stdout = stdout + cmd.Stderr = os.Stderr // Stderr from proxy appears on our stderr. + err = cmd.Start() + if err != nil { + return nil, err + } + stdout.Close() + if msg, err := readLine(fromStdout); err != nil { + return nil, err + } else if msg != "OK" { + // Communication error. + return nil, fmt.Errorf("unrecognized message %q", msg) + } + program := &Program{ + client: rpc.NewClient(&rwc{ + ssh: cmd, + r: fromStdout, + w: toStdin, + }), + } + return program, nil +} + +// readLine reads one line of text from the reader. It does no buffering. +// The trailing newline is read but not returned. +func readLine(r io.Reader) (string, error) { + b := make([]byte, 0, 10) + var c [1]byte + for { + _, err := io.ReadFull(r, c[:]) + if err != nil { + return "", err + } + if c[0] == '\n' { + break + } + b = append(b, c[0]) + } + return string(b), nil +} + +// rwc creates a single io.ReadWriteCloser from a read side and a write side. +// It also holds the command object so we can wait for SSH to complete. +// It allows us to do RPC over an SSH connection. +type rwc struct { + ssh *exec.Cmd + r *os.File + w *os.File +} + +func (rwc *rwc) Read(p []byte) (int, error) { + return rwc.r.Read(p) +} + +func (rwc *rwc) Write(p []byte) (int, error) { + return rwc.w.Write(p) +} + +func (rwc *rwc) Close() error { + rerr := rwc.r.Close() + werr := rwc.w.Close() + cerr := rwc.ssh.Wait() + if cerr != nil { + // Wait exit status is most important. + return cerr + } + if rerr != nil { + return rerr + } + return werr +} + +func (p *Program) Open(name string, mode string) (debug.File, error) { + req := protocol.OpenRequest{ + Name: name, + Mode: mode, + } + var resp protocol.OpenResponse + err := p.client.Call("Server.Open", &req, &resp) + if err != nil { + return nil, err + } + f := &File{ + prog: p, + fd: resp.FD, + } + return f, nil +} + +func (p *Program) Run(args ...string) (debug.Status, error) { + req := protocol.RunRequest{args} + var resp protocol.RunResponse + err := p.client.Call("Server.Run", &req, &resp) + if err != nil { + return debug.Status{}, err + } + return resp.Status, nil +} + +func (p *Program) Stop() (debug.Status, error) { + panic("unimplemented") +} + +func (p *Program) Resume() (debug.Status, error) { + req := protocol.ResumeRequest{} + var resp protocol.ResumeResponse + err := p.client.Call("Server.Resume", &req, &resp) + if err != nil { + return debug.Status{}, err + } + return resp.Status, nil +} + +func (p *Program) Kill() (debug.Status, error) { + panic("unimplemented") +} + +func (p *Program) Breakpoint(address uint64) ([]uint64, error) { + req := protocol.BreakpointRequest{ + Address: address, + } + var resp protocol.BreakpointResponse + err := p.client.Call("Server.Breakpoint", &req, &resp) + return resp.PCs, err +} + +func (p *Program) BreakpointAtFunction(name string) ([]uint64, error) { + req := protocol.BreakpointAtFunctionRequest{ + Function: name, + } + var resp protocol.BreakpointResponse + err := p.client.Call("Server.BreakpointAtFunction", &req, &resp) + return resp.PCs, err +} + +func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) { + req := protocol.BreakpointAtLineRequest{ + File: file, + Line: line, + } + var resp protocol.BreakpointResponse + err := p.client.Call("Server.BreakpointAtLine", &req, &resp) + return resp.PCs, err +} + +func (p *Program) DeleteBreakpoints(pcs []uint64) error { + req := protocol.DeleteBreakpointsRequest{PCs: pcs} + var resp protocol.DeleteBreakpointsResponse + return p.client.Call("Server.DeleteBreakpoints", &req, &resp) +} + +func (p *Program) Eval(expr string) ([]string, error) { + req := protocol.EvalRequest{ + Expr: expr, + } + var resp protocol.EvalResponse + err := p.client.Call("Server.Eval", &req, &resp) + return resp.Result, err +} + +func (p *Program) Evaluate(e string) (debug.Value, error) { + req := protocol.EvaluateRequest{ + Expression: e, + } + var resp protocol.EvaluateResponse + err := p.client.Call("Server.Evaluate", &req, &resp) + return resp.Result, err +} + +func (p *Program) Frames(count int) ([]debug.Frame, error) { + req := protocol.FramesRequest{ + Count: count, + } + var resp protocol.FramesResponse + err := p.client.Call("Server.Frames", &req, &resp) + return resp.Frames, err +} + +func (p *Program) Goroutines() ([]*debug.Goroutine, error) { + req := protocol.GoroutinesRequest{} + var resp protocol.GoroutinesResponse + err := p.client.Call("Server.Goroutines", &req, &resp) + return resp.Goroutines, err +} + +func (p *Program) VarByName(name string) (debug.Var, error) { + req := protocol.VarByNameRequest{Name: name} + var resp protocol.VarByNameResponse + err := p.client.Call("Server.VarByName", &req, &resp) + return resp.Var, err +} + +func (p *Program) Value(v debug.Var) (debug.Value, error) { + req := protocol.ValueRequest{Var: v} + var resp protocol.ValueResponse + err := p.client.Call("Server.Value", &req, &resp) + return resp.Value, err +} + +func (p *Program) MapElement(m debug.Map, index uint64) (debug.Var, debug.Var, error) { + req := protocol.MapElementRequest{Map: m, Index: index} + var resp protocol.MapElementResponse + err := p.client.Call("Server.MapElement", &req, &resp) + return resp.Key, resp.Value, err +} + +// File implements the debug.File interface, providing access +// to file-like resources associated with the target program. +type File struct { + prog *Program // The Program associated with the file. + fd int // File descriptor. +} + +func (f *File) ReadAt(p []byte, offset int64) (int, error) { + req := protocol.ReadAtRequest{ + FD: f.fd, + Len: len(p), + Offset: offset, + } + var resp protocol.ReadAtResponse + err := f.prog.client.Call("Server.ReadAt", &req, &resp) + return copy(p, resp.Data), err +} + +func (f *File) WriteAt(p []byte, offset int64) (int, error) { + req := protocol.WriteAtRequest{ + FD: f.fd, + Data: p, + Offset: offset, + } + var resp protocol.WriteAtResponse + err := f.prog.client.Call("Server.WriteAt", &req, &resp) + return resp.Len, err +} + +func (f *File) Close() error { + req := protocol.CloseRequest{ + FD: f.fd, + } + var resp protocol.CloseResponse + err := f.prog.client.Call("Server.Close", &req, &resp) + return err +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/dwarf.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/dwarf.go new file mode 100644 index 0000000000..c0ae9c108b --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/dwarf.go @@ -0,0 +1,112 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 + +package server + +import ( + "errors" + "fmt" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +func (s *Server) functionStartAddress(name string) (uint64, error) { + entry, err := s.dwarfData.LookupFunction(name) + if err != nil { + return 0, err + } + addrAttr := entry.Val(dwarf.AttrLowpc) + if addrAttr == nil { + return 0, fmt.Errorf("symbol %q has no LowPC attribute", name) + } + addr, ok := addrAttr.(uint64) + if !ok { + return 0, fmt.Errorf("symbol %q has non-uint64 LowPC attribute", name) + } + return addr, nil +} + +// evalLocation parses a DWARF location description encoded in v. It works for +// cases where the variable is stored at an offset from the Canonical Frame +// Address. The return value is this offset. +// TODO: a more general location-description-parsing function. +func evalLocation(v []uint8) (int64, error) { + // Some DWARF constants. + const ( + opConsts = 0x11 + opPlus = 0x22 + opCallFrameCFA = 0x9C + ) + if len(v) == 0 { + return 0, errors.New("empty location specifier") + } + if v[0] != opCallFrameCFA { + return 0, errors.New("unsupported location specifier") + } + if len(v) == 1 { + // The location description was just DW_OP_call_frame_cfa, so the location is exactly the CFA. + return 0, nil + } + if v[1] != opConsts { + return 0, errors.New("unsupported location specifier") + } + offset, v, err := sleb128(v[2:]) + if err != nil { + return 0, err + } + if len(v) == 1 && v[0] == opPlus { + // The location description was DW_OP_call_frame_cfa, DW_OP_consts , DW_OP_plus. + // So return the offset. + return offset, nil + } + return 0, errors.New("unsupported location specifier") +} + +func uleb128(v []uint8) (u uint64) { + var shift uint + for _, x := range v { + u |= (uint64(x) & 0x7F) << shift + shift += 7 + if x&0x80 == 0 { + break + } + } + return u +} + +// sleb128 parses a signed integer encoded with sleb128 at the start of v, and +// returns the integer and the remainder of v. +func sleb128(v []uint8) (s int64, rest []uint8, err error) { + var shift uint + var sign int64 = -1 + var i int + var x uint8 + for i, x = range v { + s |= (int64(x) & 0x7F) << shift + shift += 7 + sign <<= 7 + if x&0x80 == 0 { + if x&0x40 != 0 { + s |= sign + } + break + } + } + if i == len(v) { + return 0, nil, errors.New("truncated sleb128") + } + return s, v[i+1:], nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/eval.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/eval.go new file mode 100644 index 0000000000..6a6493a413 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/eval.go @@ -0,0 +1,2115 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// Evaluates Go expressions, using the current values of variables in a program +// being debugged. +// +// TODOs: +// More overflow checking. +// Stricter type checking. +// More expression types. + +// +build linux + +package server + +import ( + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "math" + "math/big" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +const prec = 256 // precision for untyped float and complex constants. + +var ( + // Some big.Ints to use in overflow checks. + bigIntMaxInt32 = big.NewInt(math.MaxInt32) + bigIntMinInt32 = big.NewInt(math.MinInt32) + bigIntMaxInt64 = big.NewInt(math.MaxInt64) + bigIntMinInt64 = big.NewInt(math.MinInt64) + bigIntMaxUint64 = new(big.Int).SetUint64(math.MaxUint64) +) + +// result stores an intermediate value produced during evaluation of an expression. +// +// d contains the DWARF type of the value. For untyped values, d will be nil. +// +// v contains the value itself. For numeric and bool types, v will have the +// corresponding predeclared Go type. +// For untyped integer, rune, float, complex, string, and bool constants, v will +// have type untInt, untRune, untFloat, untComplex, untString, or bool, +// respectively. +// For values of type int, uint and uintptr, v will be an int32, int64, uint32 +// or uint64 as appropriate. +// For address operations, v will have type pointerToValue. +// For the operands of address operations, v will have type addressableValue. +// Other types are represented using the corresponding implementation of +// debug.Value in program.go. +// +// If an evaluation results in an error, the zero value of result is used. +type result struct { + d dwarf.Type + v interface{} +} + +// untInt is an untyped integer constant +type untInt struct { + *big.Int +} + +// untRune is an untyped rune constant +type untRune struct { + *big.Int +} + +// untFloat is an untyped floating-point constant +type untFloat struct { + *big.Float +} + +// untComplex is an untyped complex constant +type untComplex struct { + r *big.Float + i *big.Float +} + +// untString is an untyped string constant +type untString string + +// pointerToValue is a pointer to a value in memory. +// The evaluator constructs these as the result of address operations like "&x". +// Unlike debug.Pointer, the DWARF type stored alongside values of this type +// is the type of the variable, not the type of the pointer. +type pointerToValue struct { + a uint64 +} + +// addressableValue is the memory location of a value. +// The evaluator constructs these while evaluating the operands of address +// operations like "&x", instead of computing the value of x itself. +type addressableValue struct { + a uint64 +} + +// A sliceOf is a slice created by slicing an array. +// Unlike debug.Slice, the DWARF type stored alongside a value of this type is +// the type of the slice's elements, not the type of the slice. +type sliceOf debug.Slice + +// ident is a value for representing a special identifier. +type ident string + +// identLookup is a built-in function of the expression evaluator which gets the +// value of a global symbol. +var identLookup ident = "lookup" + +// evalExpression evaluates a Go expression. +// If the program counter and stack pointer are nonzero, they are used to determine +// what local variables are available and where in memory they are. +func (s *Server) evalExpression(expression string, pc, sp uint64) (debug.Value, error) { + e := evaluator{server: s, expression: expression, pc: pc, sp: sp} + node, err := parser.ParseExpr(expression) + if err != nil { + return nil, err + } + val := e.evalNode(node, false) + if e.evalError != nil { + return nil, e.evalError + } + + // Convert untyped constants to their default types. + switch v := val.v.(type) { + case untInt: + return e.intFromInteger(v) + case untRune: + if v.Cmp(bigIntMaxInt32) == +1 { + return nil, errors.New("constant overflows rune") + } + if v.Cmp(bigIntMinInt32) == -1 { + return nil, errors.New("constant overflows rune") + } + return int32(v.Int64()), nil + case untFloat: + f, _ := v.Float64() + if math.IsInf(f, 0) { + return nil, errors.New("constant overflows float64") + } + if math.IsNaN(f) { + return nil, errors.New("constant is NaN") + } + return f, nil + case untComplex: + r, _ := v.r.Float64() + i, _ := v.i.Float64() + if math.IsInf(r, 0) || math.IsInf(i, 0) { + return nil, errors.New("constant overflows complex128") + } + if math.IsNaN(r) || math.IsNaN(i) { + return nil, errors.New("constant is NaN") + } + return complex(r, i), nil + case untString: + return debug.String{Length: uint64(len(v)), String: string(v)}, nil + case pointerToValue: + return debug.Pointer{TypeID: uint64(val.d.Common().Offset), Address: v.a}, nil + case sliceOf: + return debug.Slice(v), nil + case nil, addressableValue: + // This case should not be reachable. + return nil, errors.New("unknown error") + } + return val.v, nil +} + +type evaluator struct { + // expression is the expression being evaluated. + expression string + // server interacts with the program being debugged. + server *Server + // curNode is the current parse tree node. This is set so that error messages + // can quote the part of the expression that caused an error. + curNode ast.Node + // evalError is the first error that occurred while evaluating the expression, + // or nil if no error has occurred. + evalError error + // pc and sp are the current program counter and stack pointer, used for + // finding local variables. If either are zero, the expression is evaluated + // without using local variables. + pc uint64 + sp uint64 +} + +// setNode sets curNode, and returns curNode's previous value. +func (e *evaluator) setNode(node ast.Node) (old ast.Node) { + old, e.curNode = e.curNode, node + return old +} + +// err saves an error that occurred during evaluation. +// It returns a zero result, so that functions can exit and set an error with +// return e.err(...) +func (e *evaluator) err(s string) result { + if e.evalError != nil { + return result{} + } + // Append the substring of the expression that corresponds to the current AST node. + start := int(e.curNode.Pos() - 1) + end := int(e.curNode.End() - 1) + if start < 0 { + start = 0 + } + if end > len(e.expression) { + end = len(e.expression) + } + if start > end { + start, end = 0, 0 + } + e.evalError = errors.New(s + `: "` + e.expression[start:end] + `"`) + return result{} +} + +// evalNode computes the value of a node in the expression tree. +// If getAddress is true, the node is the argument of an & operator, so evalNode +// will return a result with a value of type addressableValue if possible. +func (e *evaluator) evalNode(node ast.Node, getAddress bool) result { + // Set the current node in the evaluator, so that error messages can refer to + // it. Defer a function call that changes it back. + defer e.setNode(e.setNode(node)) + + switch n := node.(type) { + case *ast.Ident: + if e.pc != 0 && e.sp != 0 { + a, t := e.server.findLocalVar(n.Name, e.pc, e.sp) + if t != nil { + return e.resultFrom(a, t, getAddress) + } + } + a, t := e.server.findGlobalVar(n.Name) + if t != nil { + return e.resultFrom(a, t, getAddress) + } + switch n.Name { + // Note: these could have been redefined as constants in the code, but we + // don't have a way to detect that. + case "true": + return result{nil, true} + case "false": + return result{nil, false} + case "lookup": + return result{nil, identLookup} + } + return e.err("unknown identifier") + + case *ast.BasicLit: + switch n.Kind { + case token.INT: + i := new(big.Int) + if _, ok := i.SetString(n.Value, 0); !ok { + return e.err("invalid integer constant") + } + return result{nil, untInt{i}} + case token.FLOAT: + r, _, err := big.ParseFloat(n.Value, 10, prec, big.ToNearestEven) + if err != nil { + return e.err(err.Error()) + } + return result{nil, untFloat{r}} + case token.IMAG: + if len(n.Value) <= 1 || n.Value[len(n.Value)-1] != 'i' { + return e.err("invalid imaginary constant") + } + r, _, err := big.ParseFloat(n.Value[:len(n.Value)-1], 10, prec, big.ToNearestEven) + if err != nil { + return e.err(err.Error()) + } + return result{nil, untComplex{new(big.Float), r}} + case token.CHAR: + // TODO: unescaping + return result{nil, untRune{new(big.Int).SetInt64(int64(n.Value[1]))}} + case token.STRING: + // TODO: unescaping + if len(n.Value) <= 1 { + return e.err("invalid string constant") + } + return result{nil, untString(n.Value[1 : len(n.Value)-1])} + } + + case *ast.ParenExpr: + return e.evalNode(n.X, getAddress) + + case *ast.StarExpr: + x := e.evalNode(n.X, false) + switch v := x.v.(type) { + case debug.Pointer: + // x.d may be a typedef pointing to a pointer type (or a typedef pointing + // to a typedef pointing to a pointer type, etc.), so remove typedefs + // until we get the underlying pointer type. + t := followTypedefs(x.d) + if pt, ok := t.(*dwarf.PtrType); ok { + return e.resultFrom(v.Address, pt.Type, getAddress) + } else { + return e.err("invalid DWARF type for pointer") + } + case pointerToValue: + return e.resultFrom(v.a, x.d, getAddress) + case nil: + return x + } + return e.err("invalid indirect") + + case *ast.SelectorExpr: + x := e.evalNode(n.X, false) + sel := n.Sel.Name + switch v := x.v.(type) { + case debug.Struct: + for _, f := range v.Fields { + if f.Name == sel { + t, err := e.server.dwarfData.Type(dwarf.Offset(f.Var.TypeID)) + if err != nil { + return e.err(err.Error()) + } + return e.resultFrom(f.Var.Address, t, getAddress) + } + } + return e.err("struct field not found") + case debug.Pointer: + pt, ok := followTypedefs(x.d).(*dwarf.PtrType) // x.d should be a pointer to struct. + if !ok { + return e.err("invalid DWARF information for pointer") + } + st, ok := followTypedefs(pt.Type).(*dwarf.StructType) + if !ok { + break + } + for _, f := range st.Field { + if f.Name == sel { + return e.resultFrom(v.Address+uint64(f.ByteOffset), f.Type, getAddress) + } + } + return e.err("struct field not found") + case pointerToValue: + st, ok := followTypedefs(x.d).(*dwarf.StructType) // x.d should be a struct. + if !ok { + break + } + for _, f := range st.Field { + if f.Name == sel { + return e.resultFrom(v.a+uint64(f.ByteOffset), f.Type, getAddress) + } + } + return e.err("struct field not found") + } + return e.err("invalid selector expression") + + case *ast.IndexExpr: + x, index := e.evalNode(n.X, false), e.evalNode(n.Index, false) + if x.v == nil || index.v == nil { + return result{} + } + // The expression is x[index] + if m, ok := x.v.(debug.Map); ok { + if getAddress { + return e.err("can't take address of map value") + } + mt, ok := followTypedefs(x.d).(*dwarf.MapType) + if !ok { + return e.err("invalid DWARF type for map") + } + var ( + found bool // true if the key was found + value result // the map value for the key + abort bool // true if an error occurred while searching + // fn is a function that checks if one (key, value) pair corresponds + // to the index in the expression. + fn = func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool { + key := e.resultFrom(keyAddr, keyType, false) + if key.v == nil { + abort = true + return false // stop searching map + } + equal, ok := e.evalBinaryOp(token.EQL, index, key).v.(bool) + if !ok { + abort = true + return false // stop searching map + } + if equal { + found = true + value = e.resultFrom(valAddr, valType, false) + return false // stop searching map + } + return true // continue searching map + } + ) + if err := e.server.peekMapValues(mt, m.Address, fn); err != nil { + return e.err(err.Error()) + } + if abort { + // Some operation on individual map keys failed. + return result{} + } + if found { + return value + } + // The key wasn't in the map; return the zero value. + return e.zero(mt.ElemType) + } + + // The index should be a non-negative integer for the remaining cases. + u, err := uint64FromResult(index) + if err != nil { + return e.err("invalid index: " + err.Error()) + } + switch v := x.v.(type) { + case debug.Array: + if u >= v.Length { + return e.err("array index out of bounds") + } + elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID)) + if err != nil { + return e.err(err.Error()) + } + return e.resultFrom(v.Element(u).Address, elemType, getAddress) + case debug.Slice: + if u >= v.Length { + return e.err("slice index out of bounds") + } + elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID)) + if err != nil { + return e.err(err.Error()) + } + return e.resultFrom(v.Element(u).Address, elemType, getAddress) + case sliceOf: + if u >= v.Length { + return e.err("slice index out of bounds") + } + return e.resultFrom(v.Element(u).Address, x.d, getAddress) + case debug.String: + if getAddress { + return e.err("can't take address of string element") + } + if u >= v.Length { + return e.err("string index out of bounds") + } + if u >= uint64(len(v.String)) { + return e.err("string element unavailable") + } + return e.uint8Result(v.String[u]) + case untString: + if getAddress { + return e.err("can't take address of string element") + } + if u >= uint64(len(v)) { + return e.err("string index out of bounds") + } + return e.uint8Result(v[u]) + } + return e.err("invalid index expression") + + case *ast.SliceExpr: + if n.Slice3 && n.High == nil { + return e.err("middle index required in full slice") + } + if n.Slice3 && n.Max == nil { + return e.err("final index required in full slice") + } + var ( + low, high, max uint64 + err error + ) + if n.Low != nil { + low, err = uint64FromResult(e.evalNode(n.Low, false)) + if err != nil { + return e.err("invalid slice lower bound: " + err.Error()) + } + } + if n.High != nil { + high, err = uint64FromResult(e.evalNode(n.High, false)) + if err != nil { + return e.err("invalid slice upper bound: " + err.Error()) + } + } + if n.Max != nil { + max, err = uint64FromResult(e.evalNode(n.Max, false)) + if err != nil { + return e.err("invalid slice capacity: " + err.Error()) + } + } + x := e.evalNode(n.X, false) + switch v := x.v.(type) { + case debug.Array, debug.Pointer, pointerToValue: + // This case handles the slicing of arrays and pointers to arrays. + var arr debug.Array + switch v := x.v.(type) { + case debug.Array: + arr = v + case debug.Pointer: + pt, ok := followTypedefs(x.d).(*dwarf.PtrType) + if !ok { + return e.err("invalid DWARF type for pointer") + } + a := e.resultFrom(v.Address, pt.Type, false) + arr, ok = a.v.(debug.Array) + if !ok { + // v is a pointer to something other than an array. + return e.err("cannot slice pointer") + } + case pointerToValue: + a := e.resultFrom(v.a, x.d, false) + var ok bool + arr, ok = a.v.(debug.Array) + if !ok { + // v is a pointer to something other than an array. + return e.err("cannot slice pointer") + } + } + elemType, err := e.server.dwarfData.Type(dwarf.Offset(arr.ElementTypeID)) + if err != nil { + return e.err(err.Error()) + } + if n.High == nil { + high = arr.Length + } else if high > arr.Length { + return e.err("slice upper bound is too large") + } + if n.Max == nil { + max = arr.Length + } else if max > arr.Length { + return e.err("slice capacity is too large") + } + if low > high || high > max { + return e.err("invalid slice index") + } + return result{ + d: elemType, + v: sliceOf{ + Array: debug.Array{ + ElementTypeID: arr.ElementTypeID, + Address: arr.Element(low).Address, + Length: high - low, + StrideBits: uint64(elemType.Common().ByteSize) * 8, + }, + Capacity: max - low, + }, + } + case debug.Slice: + if n.High == nil { + high = v.Length + } else if high > v.Capacity { + return e.err("slice upper bound is too large") + } + if n.Max == nil { + max = v.Capacity + } else if max > v.Capacity { + return e.err("slice capacity is too large") + } + if low > high || high > max { + return e.err("invalid slice index") + } + v.Address += low * (v.StrideBits / 8) + v.Length = high - low + v.Capacity = max - low + return result{x.d, v} + case sliceOf: + if n.High == nil { + high = v.Length + } else if high > v.Capacity { + return e.err("slice upper bound is too large") + } + if n.Max == nil { + max = v.Capacity + } else if max > v.Capacity { + return e.err("slice capacity is too large") + } + if low > high || high > max { + return e.err("invalid slice index") + } + v.Address += low * (v.StrideBits / 8) + v.Length = high - low + v.Capacity = max - low + return result{x.d, v} + case debug.String: + if n.Max != nil { + return e.err("full slice of string") + } + if n.High == nil { + high = v.Length + } + if low > high || high > v.Length { + return e.err("invalid slice index") + } + v.Length = high - low + if low > uint64(len(v.String)) { + // v.String was truncated before the point where this slice starts. + v.String = "" + } else { + if high > uint64(len(v.String)) { + // v.String was truncated before the point where this slice ends. + high = uint64(len(v.String)) + } + v.String = v.String[low:high] + } + return result{x.d, v} + case untString: + if n.Max != nil { + return e.err("full slice of string") + } + if n.High == nil { + high = uint64(len(v)) + } + if low > high { + return e.err("invalid slice expression") + } + if high > uint64(len(v)) { + return e.err("slice upper bound is too large") + } + return e.stringResult(string(v[low:high])) + default: + return e.err("invalid slice expression") + } + + case *ast.CallExpr: + // Only supports lookup("x"), which gets the value of a global symbol x. + fun := e.evalNode(n.Fun, false) + var args []result + for _, a := range n.Args { + args = append(args, e.evalNode(a, false)) + } + if fun.v == identLookup { + if len(args) != 1 { + return e.err("lookup should have one argument") + } + ident, ok := args[0].v.(untString) + if !ok { + return e.err("argument for lookup should be a string constant") + } + if a, t := e.server.findGlobalVar(string(ident)); t == nil { + return e.err("symbol not found") + } else { + return e.resultFrom(a, t, getAddress) + } + } + return e.err("function calls not implemented") + + case *ast.UnaryExpr: + if n.Op == token.AND { + x := e.evalNode(n.X, true) + switch v := x.v.(type) { + case addressableValue: + return result{x.d, pointerToValue{v.a}} + case nil: + return x + } + return e.err("can't take address") + } + + x := e.evalNode(n.X, false) + if x.v == nil { + return x + } + switch v := x.v.(type) { + + case int8: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case int16: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case int32: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case int64: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case uint8: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case uint16: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case uint32: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case uint64: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case float32: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case float64: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case complex64: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case complex128: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case untInt: + switch n.Op { + case token.ADD: + case token.SUB: + v.Int.Neg(v.Int) + case token.XOR: + v.Int.Not(v.Int) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case untRune: + switch n.Op { + case token.ADD: + case token.SUB: + v.Int.Neg(v.Int) + case token.XOR: + v.Int.Not(v.Int) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case untFloat: + switch n.Op { + case token.ADD: + case token.SUB: + v.Float.Neg(v.Float) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case untComplex: + switch n.Op { + case token.ADD: + case token.SUB: + v.r.Neg(v.r) + v.i.Neg(v.i) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case bool: + switch n.Op { + case token.NOT: + v = !v + default: + return e.err("invalid operation") + } + return result{x.d, v} + } + + case *ast.BinaryExpr: + x := e.evalNode(n.X, false) + if x.v == nil { + return x + } + y := e.evalNode(n.Y, false) + if y.v == nil { + return y + } + return e.evalBinaryOp(n.Op, x, y) + } + return e.err("invalid expression") +} + +// evalBinaryOp evaluates a binary operator op applied to x and y. +func (e *evaluator) evalBinaryOp(op token.Token, x, y result) result { + if op == token.NEQ { + tmp := e.evalBinaryOp(token.EQL, x, y) + b, ok := tmp.v.(bool) + if !ok { + return tmp + } + return result{nil, !b} + } + if op == token.GTR { + return e.evalBinaryOp(token.LSS, y, x) + } + if op == token.GEQ { + return e.evalBinaryOp(token.LEQ, x, y) + } + + x = convertUntyped(x, y) + y = convertUntyped(y, x) + + switch a := x.v.(type) { + + case int8: + b, ok := y.v.(int8) + if !ok { + return e.err("type mismatch") + } + var c int8 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case int16: + b, ok := y.v.(int16) + if !ok { + return e.err("type mismatch") + } + var c int16 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case int32: + b, ok := y.v.(int32) + if !ok { + return e.err("type mismatch") + } + var c int32 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case int64: + b, ok := y.v.(int64) + if !ok { + return e.err("type mismatch") + } + var c int64 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case uint8: + b, ok := y.v.(uint8) + if !ok { + return e.err("type mismatch") + } + var c uint8 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case uint16: + b, ok := y.v.(uint16) + if !ok { + return e.err("type mismatch") + } + var c uint16 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case uint32: + b, ok := y.v.(uint32) + if !ok { + return e.err("type mismatch") + } + var c uint32 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case uint64: + b, ok := y.v.(uint64) + if !ok { + return e.err("type mismatch") + } + var c uint64 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case float32: + b, ok := y.v.(float32) + if !ok { + return e.err("type mismatch") + } + var c float32 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.MUL: + c = a * b + case token.QUO: + c = a / b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case float64: + b, ok := y.v.(float64) + if !ok { + return e.err("type mismatch") + } + var c float64 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.MUL: + c = a * b + case token.QUO: + c = a / b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case complex64: + b, ok := y.v.(complex64) + if !ok { + return e.err("type mismatch") + } + var c complex64 + switch op { + case token.EQL: + return result{nil, a == b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.MUL: + c = a * b + case token.QUO: + c = a / b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case complex128: + b, ok := y.v.(complex128) + if !ok { + return e.err("type mismatch") + } + var c complex128 + switch op { + case token.EQL: + return result{nil, a == b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.MUL: + c = a * b + case token.QUO: + c = a / b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case bool: + b, ok := y.v.(bool) + if !ok { + return e.err("type mismatch") + } + var c bool + switch op { + case token.LOR: + c = a || b + case token.LAND: + c = a && b + case token.EQL: + c = a == b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case debug.String: + b, ok := y.v.(debug.String) + if !ok { + return e.err("type mismatch") + } + var c debug.String + switch op { + // TODO: these comparison operators only use the part of the string that + // was read. Very large strings do not have their entire contents read by + // server.value. + case token.EQL: + return result{nil, a.Length == b.Length && a.String == b.String} + case token.LSS: + return result{nil, a.String < b.String} + case token.LEQ: + return result{nil, a.String <= b.String} + case token.ADD: + c.Length = a.Length + b.Length + if a.Length == uint64(len(a.String)) { + c.String = a.String + b.String + } else { + // The first string was truncated at a.Length characters, so the sum + // must be truncated there too. + c.String = a.String + } + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case untString: + b, ok := y.v.(untString) + if !ok { + return e.err("type mismatch") + } + var c untString + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case untInt: + i := a.Int + b, ok := y.v.(untInt) + if !ok { + return e.err("type mismatch") + } + switch op { + case token.EQL: + return result{nil, i.Cmp(b.Int) == 0} + case token.LSS: + return result{nil, i.Cmp(b.Int) < 0} + case token.LEQ: + return result{nil, i.Cmp(b.Int) <= 0} + } + c := new(big.Int) + switch op { + case token.ADD: + c.Add(i, b.Int) + case token.SUB: + c.Sub(i, b.Int) + case token.OR: + c.Or(i, b.Int) + case token.XOR: + c.Xor(i, b.Int) + case token.MUL: + c.Mul(i, b.Int) + case token.QUO: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Quo(i, b.Int) + case token.REM: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Mod(i, b.Int) + case token.AND: + c.And(i, b.Int) + case token.AND_NOT: + c.AndNot(i, b.Int) + default: + return e.err("invalid operation") + } + return result{nil, untInt{c}} + + case untRune: + i := a.Int + b, ok := y.v.(untRune) + if !ok { + return e.err("type mismatch") + } + switch op { + case token.EQL: + return result{nil, i.Cmp(b.Int) == 0} + case token.LSS: + return result{nil, i.Cmp(b.Int) < 0} + case token.LEQ: + return result{nil, i.Cmp(b.Int) <= 0} + } + c := new(big.Int) + switch op { + case token.ADD: + c.Add(i, b.Int) + case token.SUB: + c.Sub(i, b.Int) + case token.OR: + c.Or(i, b.Int) + case token.XOR: + c.Xor(i, b.Int) + case token.MUL: + c.Mul(i, b.Int) + case token.QUO: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Quo(i, b.Int) + case token.REM: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Mod(i, b.Int) + case token.AND: + c.And(i, b.Int) + case token.AND_NOT: + c.AndNot(i, b.Int) + default: + return e.err("invalid operation") + } + return result{nil, untRune{c}} + + case untFloat: + r := a.Float + b, ok := y.v.(untFloat) + if !ok { + return e.err("type mismatch") + } + switch op { + case token.EQL: + return result{nil, r.Cmp(b.Float) == 0} + case token.LSS: + return result{nil, r.Cmp(b.Float) < 0} + case token.LEQ: + return result{nil, r.Cmp(b.Float) <= 0} + } + c := new(big.Float) + switch op { + case token.ADD: + c.Add(r, b.Float) + case token.SUB: + c.Sub(r, b.Float) + case token.MUL: + c.Mul(r, b.Float) + case token.QUO: + if b.Sign() == 0 { + return e.err("divide by zero") + } + c.Quo(r, b.Float) + default: + return e.err("invalid operation") + } + return result{nil, untFloat{c}} + + case untComplex: + b, ok := y.v.(untComplex) + if !ok { + return e.err("type mismatch") + } + var ( + ar = a.r + br = b.r + ai = a.i + bi = b.i + ) + if op == token.EQL { + return result{nil, ar.Cmp(br) == 0 && ai.Cmp(bi) == 0} + } + var ( + cr = new(big.Float) + ci = new(big.Float) + ) + switch op { + case token.ADD: + cr.Add(ar, br) + ci.Add(ai, bi) + case token.SUB: + cr.Sub(ar, br) + ci.Sub(ai, bi) + case token.MUL: + var t0, t1 big.Float + t0.Mul(ar, br) + t1.Mul(ai, bi) + cr.Sub(&t0, &t1) + t0.Mul(ar, bi) + t1.Mul(ai, br) + ci.Add(&t0, &t1) + case token.QUO: + // a/b = a*conj(b)/|b|^2 + var t0, t1 big.Float + cr.Mul(ar, br) + t0.Mul(ai, bi) + cr.Add(cr, &t0) // cr = Re(a*conj(b)) + ci.Mul(ai, br) + t0.Mul(ar, bi) + ci.Sub(ci, &t0) // ci = Im(a*conj(b)) + t0.Mul(br, br) + t1.Mul(bi, bi) + t0.Add(&t0, &t1) // t0 = |b|^2 + if t0.Sign() == 0 { + return e.err("divide by zero") + } + cr.Quo(cr, &t0) // cr = Re(a*conj(b))/|b|^2 = Re(a/b) + ci.Quo(ci, &t0) // ci = Im(a*conj(b))/|b|^2 = Im(a/b) + } + return result{nil, untComplex{cr, ci}} + } + + return e.err("invalid operation") +} + +// findLocalVar finds a local variable (or function parameter) by name, and +// returns its address and DWARF type. It returns a nil type on failure. +// The PC and SP are used to determine the current function and stack frame. +func (s *Server) findLocalVar(name string, pc, sp uint64) (uint64, dwarf.Type) { + // Find the DWARF entry for the function at pc. + funcEntry, _, err := s.dwarfData.PCToFunction(uint64(pc)) + if err != nil { + return 0, nil + } + + // Compute the stack frame pointer. + fpOffset, err := s.dwarfData.PCToSPOffset(uint64(pc)) + if err != nil { + return 0, nil + } + framePointer := sp + uint64(fpOffset) + + // Check each child of the function's DWARF entry to see if it is a parameter + // or local variable with the right name. If so, return its address and type. + r := s.dwarfData.Reader() + r.Seek(funcEntry.Offset) + for { + varEntry, err := r.Next() + if err != nil { + break + } + if varEntry.Tag == 0 { + // This tag marks the end of the function's DWARF entry's children. + break + } + + // Check this entry corresponds to a local variable or function parameter, + // that it has the correct name, and that we can get its type and location. + // If so, return them. + if varEntry.Tag != dwarf.TagFormalParameter && varEntry.Tag != dwarf.TagVariable { + continue + } + varName, ok := varEntry.Val(dwarf.AttrName).(string) + if !ok { + continue + } + if varName != name { + continue + } + varTypeOffset, ok := varEntry.Val(dwarf.AttrType).(dwarf.Offset) + if !ok { + continue + } + varType, err := s.dwarfData.Type(varTypeOffset) + if err != nil { + continue + } + locationAttribute := varEntry.Val(dwarf.AttrLocation) + if locationAttribute == nil { + continue + } + locationDescription, ok := locationAttribute.([]uint8) + if !ok { + continue + } + frameOffset, err := evalLocation(locationDescription) + if err != nil { + continue + } + return framePointer + uint64(frameOffset), varType + } + + return 0, nil +} + +// findGlobalVar finds a global variable by name, and returns its address and +// DWARF type. It returns a nil type on failure. +func (s *Server) findGlobalVar(name string) (uint64, dwarf.Type) { + entry, err := s.dwarfData.LookupVariable(name) + if err != nil { + return 0, nil + } + loc, err := s.dwarfData.EntryLocation(entry) + if err != nil { + return 0, nil + } + ofs, err := s.dwarfData.EntryTypeOffset(entry) + if err != nil { + return 0, nil + } + typ, err := s.dwarfData.Type(ofs) + if err != nil { + return 0, nil + } + return loc, typ +} + +// intFromInteger converts an untyped integer constant to an int32 or int64, +// depending on the int size of the debugged program. +// It returns an error on overflow, or if it can't determine the int size. +func (e *evaluator) intFromInteger(v untInt) (interface{}, error) { + t, ok := e.getBaseType("int") + if !ok { + return nil, errors.New("couldn't get int size from DWARF info") + } + switch t.Common().ByteSize { + case 4: + if v.Cmp(bigIntMaxInt32) == +1 || v.Cmp(bigIntMinInt32) == -1 { + return nil, errors.New("constant overflows int") + } + return int32(v.Int64()), nil + case 8: + if v.Cmp(bigIntMaxInt64) == +1 || v.Cmp(bigIntMinInt64) == -1 { + return nil, errors.New("constant overflows int") + } + return v.Int64(), nil + } + return nil, errors.New("invalid int size in DWARF info") +} + +// uint8Result constructs a result for a uint8 value. +func (e *evaluator) uint8Result(v uint8) result { + t, ok := e.getBaseType("uint8") + if !ok { + e.err("couldn't construct uint8") + } + return result{t, uint8(v)} +} + +// stringResult constructs a result for a string value. +func (e *evaluator) stringResult(s string) result { + t, ok := e.getBaseType("string") + if !ok { + e.err("couldn't construct string") + } + return result{t, debug.String{Length: uint64(len(s)), String: s}} +} + +// getBaseType returns the *dwarf.Type with a given name. +// TODO: cache this. +func (e *evaluator) getBaseType(name string) (dwarf.Type, bool) { + entry, err := e.server.dwarfData.LookupEntry(name) + if err != nil { + return nil, false + } + t, err := e.server.dwarfData.Type(entry.Offset) + if err != nil { + return nil, false + } + return t, true +} + +// resultFrom constructs a result corresponding to a value in the program with +// the given address and DWARF type. +// If getAddress is true, the result will be the operand of an address expression, +// so resultFrom returns a result containing a value of type addressableValue. +func (e *evaluator) resultFrom(a uint64, t dwarf.Type, getAddress bool) result { + if a == 0 { + return e.err("nil pointer dereference") + } + if getAddress { + return result{t, addressableValue{a}} + } + v, err := e.server.value(t, a) + if err != nil { + return e.err(err.Error()) + } + return result{t, v} +} + +// zero returns the zero value of type t. +// TODO: implement for array and struct. +func (e *evaluator) zero(t dwarf.Type) result { + var v interface{} + switch typ := followTypedefs(t).(type) { + case *dwarf.CharType, *dwarf.IntType, *dwarf.EnumType: + switch typ.Common().ByteSize { + case 1: + v = int8(0) + case 2: + v = int16(0) + case 4: + v = int32(0) + case 8: + v = int64(0) + default: + return e.err("invalid integer size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.UcharType, *dwarf.UintType: + switch typ.Common().ByteSize { + case 1: + v = uint8(0) + case 2: + v = uint16(0) + case 4: + v = uint32(0) + case 8: + v = uint64(0) + default: + return e.err("invalid unsigned integer size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.FloatType: + switch typ.Common().ByteSize { + case 4: + v = float32(0) + case 8: + v = float64(0) + default: + return e.err("invalid float size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.ComplexType: + switch typ.Common().ByteSize { + case 8: + v = complex64(0) + case 16: + v = complex128(0) + default: + return e.err("invalid complex size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.BoolType: + v = false + case *dwarf.PtrType: + v = debug.Pointer{TypeID: uint64(t.Common().Offset)} + case *dwarf.SliceType: + v = debug.Slice{ + Array: debug.Array{ + ElementTypeID: uint64(typ.ElemType.Common().Offset), + StrideBits: uint64(typ.ElemType.Common().ByteSize) * 8, + }, + } + case *dwarf.StringType: + v = debug.String{} + case *dwarf.InterfaceType: + v = debug.Interface{} + case *dwarf.FuncType: + v = debug.Func{} + case *dwarf.MapType: + v = debug.Map{TypeID: uint64(t.Common().Offset)} + case *dwarf.ChanType: + v = debug.Channel{ + ElementTypeID: uint64(typ.ElemType.Common().Offset), + Stride: uint64(typ.ElemType.Common().ByteSize), + } + default: + return e.err("can't get zero value of this type") + } + return result{t, v} +} + +// convertUntyped converts x to be the same type as y, if x is untyped and the +// conversion is possible. +// +// An untyped bool can be converted to a boolean type. +// An untyped string can be converted to a string type. +// An untyped integer, rune, float or complex value can be converted to a +// numeric type, or to an untyped value later in that list. +// +// x is returned unchanged if none of these cases apply. +func convertUntyped(x, y result) result { + switch a := x.v.(type) { + case untInt: + i := a.Int + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + case float32: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, f} + case float64: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, f} + case complex64: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, complex(f, 0)} + case complex128: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, complex(f, 0)} + case untRune: + return result{nil, untRune{i}} + case untFloat: + return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}} + case untComplex: + return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}} + } + case untRune: + i := a.Int + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + case float32: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, f} + case float64: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, f} + case complex64: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, complex(f, 0)} + case complex128: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, complex(f, 0)} + case untRune: + return result{nil, untRune{i}} + case untFloat: + return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}} + case untComplex: + return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}} + } + case untFloat: + if a.IsInt() { + i, _ := a.Int(nil) + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + } + } + switch y.v.(type) { + case float32: + f, _ := a.Float32() + return result{y.d, float32(f)} + case float64: + f, _ := a.Float64() + return result{y.d, float64(f)} + case complex64: + f, _ := a.Float32() + return result{y.d, complex(f, 0)} + case complex128: + f, _ := a.Float64() + return result{y.d, complex(f, 0)} + case untComplex: + return result{nil, untComplex{a.Float, new(big.Float)}} + } + case untComplex: + if a.i.Sign() == 0 { + // a is a real number. + if a.r.IsInt() { + // a is an integer. + i, _ := a.r.Int(nil) + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + } + } + switch y.v.(type) { + case float32: + f, _ := a.r.Float32() + return result{y.d, float32(f)} + case float64: + f, _ := a.r.Float64() + return result{y.d, float64(f)} + } + } + switch y.v.(type) { + case complex64: + r, _ := a.r.Float32() + i, _ := a.i.Float32() + return result{y.d, complex(r, i)} + case complex128: + r, _ := a.r.Float64() + i, _ := a.i.Float64() + return result{y.d, complex(r, i)} + } + case bool: + if x.d != nil { + // x is a typed bool, not an untyped bool. + break + } + switch y.v.(type) { + case bool: + return result{y.d, bool(a)} + } + case untString: + switch y.v.(type) { + case debug.String: + return result{y.d, debug.String{Length: uint64(len(a)), String: string(a)}} + } + } + return x +} + +// uint64FromResult converts a result into a uint64 for slice or index expressions. +// It returns an error if the conversion cannot be done. +func uint64FromResult(x result) (uint64, error) { + switch v := x.v.(type) { + case int8: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case int16: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case int32: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case int64: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case uint8: + return uint64(v), nil + case uint16: + return uint64(v), nil + case uint32: + return uint64(v), nil + case uint64: + return v, nil + case untInt: + if v.Int.Sign() == -1 { + return 0, errors.New("value is negative") + } + if v.Int.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return v.Int.Uint64(), nil + case untRune: + if v.Sign() == -1 { + return 0, errors.New("value is negative") + } + if v.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return v.Uint64(), nil + case untFloat: + if !v.IsInt() { + return 0, errors.New("value is not an integer") + } + if v.Sign() == -1 { + return 0, errors.New("value is negative") + } + i, _ := v.Int(nil) + if i.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return i.Uint64(), nil + case untComplex: + if v.i.Sign() != 0 { + return 0, errors.New("value is complex") + } + if !v.r.IsInt() { + return 0, errors.New("value is not an integer") + } + if v.r.Sign() == -1 { + return 0, errors.New("value is negative") + } + i, _ := v.r.Int(nil) + if i.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return i.Uint64(), nil + } + return 0, fmt.Errorf("cannot convert to unsigned integer") +} + +// followTypedefs returns the underlying type of t, removing any typedefs. +// If t leads to a cycle of typedefs, followTypedefs returns nil. +func followTypedefs(t dwarf.Type) dwarf.Type { + // If t is a *dwarf.TypedefType, next returns t.Type, otherwise it returns t. + // The bool returned is true when the argument was a typedef. + next := func(t dwarf.Type) (dwarf.Type, bool) { + tt, ok := t.(*dwarf.TypedefType) + if !ok { + return t, false + } + return tt.Type, true + } + // Advance two pointers, one at twice the speed, so we can detect if we get + // stuck in a cycle. + slow, fast := t, t + for { + var wasTypedef bool + fast, wasTypedef = next(fast) + if !wasTypedef { + return fast + } + fast, wasTypedef = next(fast) + if !wasTypedef { + return fast + } + slow, _ = next(slow) + if slow == fast { + return nil + } + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/eval.m4 b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/eval.m4 new file mode 100644 index 0000000000..a191b3644e --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/eval.m4 @@ -0,0 +1,1721 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. +//m4_changequote(`@',`@') +// Evaluates Go expressions, using the current values of variables in a program +// being debugged. +// +// TODOs: +// More overflow checking. +// Stricter type checking. +// More expression types. + +package server + +import ( + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "math" + "math/big" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +const prec = 256 // precision for untyped float and complex constants. + +var ( + // Some big.Ints to use in overflow checks. + bigIntMaxInt32 = big.NewInt(math.MaxInt32) + bigIntMinInt32 = big.NewInt(math.MinInt32) + bigIntMaxInt64 = big.NewInt(math.MaxInt64) + bigIntMinInt64 = big.NewInt(math.MinInt64) + bigIntMaxUint64 = new(big.Int).SetUint64(math.MaxUint64) +) + +// result stores an intermediate value produced during evaluation of an expression. +// +// d contains the DWARF type of the value. For untyped values, d will be nil. +// +// v contains the value itself. For numeric and bool types, v will have the +// corresponding predeclared Go type. +// For untyped integer, rune, float, complex, string, and bool constants, v will +// have type untInt, untRune, untFloat, untComplex, untString, or bool, +// respectively. +// For values of type int, uint and uintptr, v will be an int32, int64, uint32 +// or uint64 as appropriate. +// For address operations, v will have type pointerToValue. +// For the operands of address operations, v will have type addressableValue. +// Other types are represented using the corresponding implementation of +// debug.Value in program.go. +// +// If an evaluation results in an error, the zero value of result is used. +type result struct { + d dwarf.Type + v interface{} +} + +// untInt is an untyped integer constant +type untInt struct { + *big.Int +} + +// untRune is an untyped rune constant +type untRune struct { + *big.Int +} + +// untFloat is an untyped floating-point constant +type untFloat struct { + *big.Float +} + +// untComplex is an untyped complex constant +type untComplex struct { + r *big.Float + i *big.Float +} + +// untString is an untyped string constant +type untString string + +// pointerToValue is a pointer to a value in memory. +// The evaluator constructs these as the result of address operations like "&x". +// Unlike debug.Pointer, the DWARF type stored alongside values of this type +// is the type of the variable, not the type of the pointer. +type pointerToValue struct { + a uint64 +} + +// addressableValue is the memory location of a value. +// The evaluator constructs these while evaluating the operands of address +// operations like "&x", instead of computing the value of x itself. +type addressableValue struct { + a uint64 +} + +// A sliceOf is a slice created by slicing an array. +// Unlike debug.Slice, the DWARF type stored alongside a value of this type is +// the type of the slice's elements, not the type of the slice. +type sliceOf debug.Slice + +// ident is a value for representing a special identifier. +type ident string + +// identLookup is a built-in function of the expression evaluator which gets the +// value of a global symbol. +var identLookup ident = "lookup" + +// evalExpression evaluates a Go expression. +// If the program counter and stack pointer are nonzero, they are used to determine +// what local variables are available and where in memory they are. +func (s *Server) evalExpression(expression string, pc, sp uint64) (debug.Value, error) { + e := evaluator{server: s, expression: expression, pc: pc, sp: sp} + node, err := parser.ParseExpr(expression) + if err != nil { + return nil, err + } + val := e.evalNode(node, false) + if e.evalError != nil { + return nil, e.evalError + } + + // Convert untyped constants to their default types. + switch v := val.v.(type) { + case untInt: + return e.intFromInteger(v) + case untRune: + if v.Cmp(bigIntMaxInt32) == +1 { + return nil, errors.New("constant overflows rune") + } + if v.Cmp(bigIntMinInt32) == -1 { + return nil, errors.New("constant overflows rune") + } + return int32(v.Int64()), nil + case untFloat: + f, _ := v.Float64() + if math.IsInf(f, 0) { + return nil, errors.New("constant overflows float64") + } + if math.IsNaN(f) { + return nil, errors.New("constant is NaN") + } + return f, nil + case untComplex: + r, _ := v.r.Float64() + i, _ := v.i.Float64() + if math.IsInf(r, 0) || math.IsInf(i, 0) { + return nil, errors.New("constant overflows complex128") + } + if math.IsNaN(r) || math.IsNaN(i) { + return nil, errors.New("constant is NaN") + } + return complex(r, i), nil + case untString: + return debug.String{Length: uint64(len(v)), String: string(v)}, nil + case pointerToValue: + return debug.Pointer{TypeID: uint64(val.d.Common().Offset), Address: v.a}, nil + case sliceOf: + return debug.Slice(v), nil + case nil, addressableValue: + // This case should not be reachable. + return nil, errors.New("unknown error") + } + return val.v, nil +} + +type evaluator struct { + // expression is the expression being evaluated. + expression string + // server interacts with the program being debugged. + server *Server + // curNode is the current parse tree node. This is set so that error messages + // can quote the part of the expression that caused an error. + curNode ast.Node + // evalError is the first error that occurred while evaluating the expression, + // or nil if no error has occurred. + evalError error + // pc and sp are the current program counter and stack pointer, used for + // finding local variables. If either are zero, the expression is evaluated + // without using local variables. + pc uint64 + sp uint64 +} + +// setNode sets curNode, and returns curNode's previous value. +func (e *evaluator) setNode(node ast.Node) (old ast.Node) { + old, e.curNode = e.curNode, node + return old +} + +// err saves an error that occurred during evaluation. +// It returns a zero result, so that functions can exit and set an error with +// return e.err(...) +func (e *evaluator) err(s string) result { + if e.evalError != nil { + return result{} + } + // Append the substring of the expression that corresponds to the current AST node. + start := int(e.curNode.Pos() - 1) + end := int(e.curNode.End() - 1) + if start < 0 { + start = 0 + } + if end > len(e.expression) { + end = len(e.expression) + } + if start > end { + start, end = 0, 0 + } + e.evalError = errors.New(s + `: "` + e.expression[start:end] + `"`) + return result{} +} + +// evalNode computes the value of a node in the expression tree. +// If getAddress is true, the node is the argument of an & operator, so evalNode +// will return a result with a value of type addressableValue if possible. +func (e *evaluator) evalNode(node ast.Node, getAddress bool) result { + // Set the current node in the evaluator, so that error messages can refer to + // it. Defer a function call that changes it back. + defer e.setNode(e.setNode(node)) + + switch n := node.(type) { + case *ast.Ident: + if e.pc != 0 && e.sp != 0 { + a, t := e.server.findLocalVar(n.Name, e.pc, e.sp) + if t != nil { + return e.resultFrom(a, t, getAddress) + } + } + a, t := e.server.findGlobalVar(n.Name) + if t != nil { + return e.resultFrom(a, t, getAddress) + } + switch n.Name { + // Note: these could have been redefined as constants in the code, but we + // don't have a way to detect that. + case "true": + return result{nil, true} + case "false": + return result{nil, false} + case "lookup": + return result{nil, identLookup} + } + return e.err("unknown identifier") + + case *ast.BasicLit: + switch n.Kind { + case token.INT: + i := new(big.Int) + if _, ok := i.SetString(n.Value, 0); !ok { + return e.err("invalid integer constant") + } + return result{nil, untInt{i}} + case token.FLOAT: + r, _, err := big.ParseFloat(n.Value, 10, prec, big.ToNearestEven) + if err != nil { + return e.err(err.Error()) + } + return result{nil, untFloat{r}} + case token.IMAG: + if len(n.Value) <= 1 || n.Value[len(n.Value)-1] != 'i' { + return e.err("invalid imaginary constant") + } + r, _, err := big.ParseFloat(n.Value[:len(n.Value)-1], 10, prec, big.ToNearestEven) + if err != nil { + return e.err(err.Error()) + } + return result{nil, untComplex{new(big.Float), r}} + case token.CHAR: + // TODO: unescaping + return result{nil, untRune{new(big.Int).SetInt64(int64(n.Value[1]))}} + case token.STRING: + // TODO: unescaping + if len(n.Value) <= 1 { + return e.err("invalid string constant") + } + return result{nil, untString(n.Value[1 : len(n.Value)-1])} + } + + case *ast.ParenExpr: + return e.evalNode(n.X, getAddress) + + case *ast.StarExpr: + x := e.evalNode(n.X, false) + switch v := x.v.(type) { + case debug.Pointer: + // x.d may be a typedef pointing to a pointer type (or a typedef pointing + // to a typedef pointing to a pointer type, etc.), so remove typedefs + // until we get the underlying pointer type. + t := followTypedefs(x.d) + if pt, ok := t.(*dwarf.PtrType); ok { + return e.resultFrom(v.Address, pt.Type, getAddress) + } else { + return e.err("invalid DWARF type for pointer") + } + case pointerToValue: + return e.resultFrom(v.a, x.d, getAddress) + case nil: + return x + } + return e.err("invalid indirect") + + case *ast.SelectorExpr: + x := e.evalNode(n.X, false) + sel := n.Sel.Name + switch v := x.v.(type) { + case debug.Struct: + for _, f := range v.Fields { + if f.Name == sel { + t, err := e.server.dwarfData.Type(dwarf.Offset(f.Var.TypeID)) + if err != nil { + return e.err(err.Error()) + } + return e.resultFrom(f.Var.Address, t, getAddress) + } + } + return e.err("struct field not found") + case debug.Pointer: + pt, ok := followTypedefs(x.d).(*dwarf.PtrType) // x.d should be a pointer to struct. + if !ok { + return e.err("invalid DWARF information for pointer") + } + st, ok := followTypedefs(pt.Type).(*dwarf.StructType) + if !ok { + break + } + for _, f := range st.Field { + if f.Name == sel { + return e.resultFrom(v.Address+uint64(f.ByteOffset), f.Type, getAddress) + } + } + return e.err("struct field not found") + case pointerToValue: + st, ok := followTypedefs(x.d).(*dwarf.StructType) // x.d should be a struct. + if !ok { + break + } + for _, f := range st.Field { + if f.Name == sel { + return e.resultFrom(v.a+uint64(f.ByteOffset), f.Type, getAddress) + } + } + return e.err("struct field not found") + } + return e.err("invalid selector expression") + + case *ast.IndexExpr: + x, index := e.evalNode(n.X, false), e.evalNode(n.Index, false) + if x.v == nil || index.v == nil { + return result{} + } + // The expression is x[index] + if m, ok := x.v.(debug.Map); ok { + if getAddress { + return e.err("can't take address of map value") + } + mt, ok := followTypedefs(x.d).(*dwarf.MapType) + if !ok { + return e.err("invalid DWARF type for map") + } + var ( + found bool // true if the key was found + value result // the map value for the key + abort bool // true if an error occurred while searching + // fn is a function that checks if one (key, value) pair corresponds + // to the index in the expression. + fn = func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool { + key := e.resultFrom(keyAddr, keyType, false) + if key.v == nil { + abort = true + return false // stop searching map + } + equal, ok := e.evalBinaryOp(token.EQL, index, key).v.(bool) + if !ok { + abort = true + return false // stop searching map + } + if equal { + found = true + value = e.resultFrom(valAddr, valType, false) + return false // stop searching map + } + return true // continue searching map + } + ) + if err := e.server.peekMapValues(mt, m.Address, fn); err != nil { + return e.err(err.Error()) + } + if abort { + // Some operation on individual map keys failed. + return result{} + } + if found { + return value + } + // The key wasn't in the map; return the zero value. + return e.zero(mt.ElemType) + } + + // The index should be a non-negative integer for the remaining cases. + u, err := uint64FromResult(index) + if err != nil { + return e.err("invalid index: " + err.Error()) + } + switch v := x.v.(type) { + case debug.Array: + if u >= v.Length { + return e.err("array index out of bounds") + } + elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID)) + if err != nil { + return e.err(err.Error()) + } + return e.resultFrom(v.Element(u).Address, elemType, getAddress) + case debug.Slice: + if u >= v.Length { + return e.err("slice index out of bounds") + } + elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID)) + if err != nil { + return e.err(err.Error()) + } + return e.resultFrom(v.Element(u).Address, elemType, getAddress) + case sliceOf: + if u >= v.Length { + return e.err("slice index out of bounds") + } + return e.resultFrom(v.Element(u).Address, x.d, getAddress) + case debug.String: + if getAddress { + return e.err("can't take address of string element") + } + if u >= v.Length { + return e.err("string index out of bounds") + } + if u >= uint64(len(v.String)) { + return e.err("string element unavailable") + } + return e.uint8Result(v.String[u]) + case untString: + if getAddress { + return e.err("can't take address of string element") + } + if u >= uint64(len(v)) { + return e.err("string index out of bounds") + } + return e.uint8Result(v[u]) + } + return e.err("invalid index expression") + + case *ast.SliceExpr: + if n.Slice3 && n.High == nil { + return e.err("middle index required in full slice") + } + if n.Slice3 && n.Max == nil { + return e.err("final index required in full slice") + } + var ( + low, high, max uint64 + err error + ) + if n.Low != nil { + low, err = uint64FromResult(e.evalNode(n.Low, false)) + if err != nil { + return e.err("invalid slice lower bound: " + err.Error()) + } + } + if n.High != nil { + high, err = uint64FromResult(e.evalNode(n.High, false)) + if err != nil { + return e.err("invalid slice upper bound: " + err.Error()) + } + } + if n.Max != nil { + max, err = uint64FromResult(e.evalNode(n.Max, false)) + if err != nil { + return e.err("invalid slice capacity: " + err.Error()) + } + } + x := e.evalNode(n.X, false) + switch v := x.v.(type) { + case debug.Array, debug.Pointer, pointerToValue: + // This case handles the slicing of arrays and pointers to arrays. + var arr debug.Array + switch v := x.v.(type) { + case debug.Array: + arr = v + case debug.Pointer: + pt, ok := followTypedefs(x.d).(*dwarf.PtrType) + if !ok { + return e.err("invalid DWARF type for pointer") + } + a := e.resultFrom(v.Address, pt.Type, false) + arr, ok = a.v.(debug.Array) + if !ok { + // v is a pointer to something other than an array. + return e.err("cannot slice pointer") + } + case pointerToValue: + a := e.resultFrom(v.a, x.d, false) + var ok bool + arr, ok = a.v.(debug.Array) + if !ok { + // v is a pointer to something other than an array. + return e.err("cannot slice pointer") + } + } + elemType, err := e.server.dwarfData.Type(dwarf.Offset(arr.ElementTypeID)) + if err != nil { + return e.err(err.Error()) + } + if n.High == nil { + high = arr.Length + } else if high > arr.Length { + return e.err("slice upper bound is too large") + } + if n.Max == nil { + max = arr.Length + } else if max > arr.Length { + return e.err("slice capacity is too large") + } + if low > high || high > max { + return e.err("invalid slice index") + } + return result{ + d: elemType, + v: sliceOf{ + Array: debug.Array{ + ElementTypeID: arr.ElementTypeID, + Address: arr.Element(low).Address, + Length: high - low, + StrideBits: uint64(elemType.Common().ByteSize) * 8, + }, + Capacity: max - low, + }, + } + case debug.Slice: + if n.High == nil { + high = v.Length + } else if high > v.Capacity { + return e.err("slice upper bound is too large") + } + if n.Max == nil { + max = v.Capacity + } else if max > v.Capacity { + return e.err("slice capacity is too large") + } + if low > high || high > max { + return e.err("invalid slice index") + } + v.Address += low * (v.StrideBits / 8) + v.Length = high - low + v.Capacity = max - low + return result{x.d, v} + case sliceOf: + if n.High == nil { + high = v.Length + } else if high > v.Capacity { + return e.err("slice upper bound is too large") + } + if n.Max == nil { + max = v.Capacity + } else if max > v.Capacity { + return e.err("slice capacity is too large") + } + if low > high || high > max { + return e.err("invalid slice index") + } + v.Address += low * (v.StrideBits / 8) + v.Length = high - low + v.Capacity = max - low + return result{x.d, v} + case debug.String: + if n.Max != nil { + return e.err("full slice of string") + } + if n.High == nil { + high = v.Length + } + if low > high || high > v.Length { + return e.err("invalid slice index") + } + v.Length = high - low + if low > uint64(len(v.String)) { + // v.String was truncated before the point where this slice starts. + v.String = "" + } else { + if high > uint64(len(v.String)) { + // v.String was truncated before the point where this slice ends. + high = uint64(len(v.String)) + } + v.String = v.String[low:high] + } + return result{x.d, v} + case untString: + if n.Max != nil { + return e.err("full slice of string") + } + if n.High == nil { + high = uint64(len(v)) + } + if low > high { + return e.err("invalid slice expression") + } + if high > uint64(len(v)) { + return e.err("slice upper bound is too large") + } + return e.stringResult(string(v[low:high])) + default: + return e.err("invalid slice expression") + } + + case *ast.CallExpr: + // Only supports lookup("x"), which gets the value of a global symbol x. + fun := e.evalNode(n.Fun, false) + var args []result + for _, a := range n.Args { + args = append(args, e.evalNode(a, false)) + } + if fun.v == identLookup { + if len(args) != 1 { + return e.err("lookup should have one argument") + } + ident, ok := args[0].v.(untString) + if !ok { + return e.err("argument for lookup should be a string constant") + } + if a, t := e.server.findGlobalVar(string(ident)); t == nil { + return e.err("symbol not found") + } else { + return e.resultFrom(a, t, getAddress) + } + } + return e.err("function calls not implemented") + + case *ast.UnaryExpr: + if n.Op == token.AND { + x := e.evalNode(n.X, true) + switch v := x.v.(type) { + case addressableValue: + return result{x.d, pointerToValue{v.a}} + case nil: + return x + } + return e.err("can't take address") + } + + x := e.evalNode(n.X, false) + if x.v == nil { + return x + } + switch v := x.v.(type) { +m4_define(UNARY_INT_OPS, @case $1: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + case token.XOR: + v = ^v + default: + return e.err("invalid operation") + } + return result{x.d, v} +@) +m4_define(UNARY_FLOAT_OPS, @case $1: + switch n.Op { + case token.ADD: + case token.SUB: + v = -v + default: + return e.err("invalid operation") + } + return result{x.d, v} +@) + UNARY_INT_OPS(int8) + UNARY_INT_OPS(int16) + UNARY_INT_OPS(int32) + UNARY_INT_OPS(int64) + UNARY_INT_OPS(uint8) + UNARY_INT_OPS(uint16) + UNARY_INT_OPS(uint32) + UNARY_INT_OPS(uint64) + UNARY_FLOAT_OPS(float32) + UNARY_FLOAT_OPS(float64) + UNARY_FLOAT_OPS(complex64) + UNARY_FLOAT_OPS(complex128) + case untInt: + switch n.Op { + case token.ADD: + case token.SUB: + v.Int.Neg(v.Int) + case token.XOR: + v.Int.Not(v.Int) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case untRune: + switch n.Op { + case token.ADD: + case token.SUB: + v.Int.Neg(v.Int) + case token.XOR: + v.Int.Not(v.Int) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case untFloat: + switch n.Op { + case token.ADD: + case token.SUB: + v.Float.Neg(v.Float) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case untComplex: + switch n.Op { + case token.ADD: + case token.SUB: + v.r.Neg(v.r) + v.i.Neg(v.i) + default: + return e.err("invalid operation") + } + return result{x.d, v} + + case bool: + switch n.Op { + case token.NOT: + v = !v + default: + return e.err("invalid operation") + } + return result{x.d, v} + } + + case *ast.BinaryExpr: + x := e.evalNode(n.X, false) + if x.v == nil { + return x + } + y := e.evalNode(n.Y, false) + if y.v == nil { + return y + } + return e.evalBinaryOp(n.Op, x, y) + } + return e.err("invalid expression") +} + +// evalBinaryOp evaluates a binary operator op applied to x and y. +func (e *evaluator) evalBinaryOp(op token.Token, x, y result) result { + if op == token.NEQ { + tmp := e.evalBinaryOp(token.EQL, x, y) + b, ok := tmp.v.(bool) + if !ok { + return tmp + } + return result{nil, !b} + } + if op == token.GTR { + return e.evalBinaryOp(token.LSS, y, x) + } + if op == token.GEQ { + return e.evalBinaryOp(token.LEQ, x, y) + } + + x = convertUntyped(x, y) + y = convertUntyped(y, x) + + switch a := x.v.(type) { +m4_define(INT_OPS, @case $1: + b, ok := y.v.($1) + if !ok { + return e.err("type mismatch") + } + var c $1 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} +@) +m4_define(UINT_OPS, @case $1: + b, ok := y.v.($1) + if !ok { + return e.err("type mismatch") + } + var c $1 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.OR: + c = a | b + case token.XOR: + c = a ^ b + case token.MUL: + c = a * b + case token.QUO: + if b == 0 { + return e.err("integer divide by zero") + } + c = a / b + case token.REM: + if b == 0 { + return e.err("integer divide by zero") + } + c = a % b + case token.AND: + c = a & b + case token.AND_NOT: + c = a &^ b + default: + return e.err("invalid operation") + } + return result{x.d, c} +@) +m4_define(FLOAT_OPS, @case $1: + b, ok := y.v.($1) + if !ok { + return e.err("type mismatch") + } + var c $1 + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.MUL: + c = a * b + case token.QUO: + c = a / b + default: + return e.err("invalid operation") + } + return result{x.d, c} +@) +m4_define(COMPLEX_OPS, @case $1: + b, ok := y.v.($1) + if !ok { + return e.err("type mismatch") + } + var c $1 + switch op { + case token.EQL: + return result{nil, a == b} + case token.ADD: + c = a + b + case token.SUB: + c = a - b + case token.MUL: + c = a * b + case token.QUO: + c = a / b + default: + return e.err("invalid operation") + } + return result{x.d, c} +@) + INT_OPS(int8) + INT_OPS(int16) + INT_OPS(int32) + INT_OPS(int64) + UINT_OPS(uint8) + UINT_OPS(uint16) + UINT_OPS(uint32) + UINT_OPS(uint64) + FLOAT_OPS(float32) + FLOAT_OPS(float64) + COMPLEX_OPS(complex64) + COMPLEX_OPS(complex128) + case bool: + b, ok := y.v.(bool) + if !ok { + return e.err("type mismatch") + } + var c bool + switch op { + case token.LOR: + c = a || b + case token.LAND: + c = a && b + case token.EQL: + c = a == b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case debug.String: + b, ok := y.v.(debug.String) + if !ok { + return e.err("type mismatch") + } + var c debug.String + switch op { + // TODO: these comparison operators only use the part of the string that + // was read. Very large strings do not have their entire contents read by + // server.value. + case token.EQL: + return result{nil, a.Length == b.Length && a.String == b.String} + case token.LSS: + return result{nil, a.String < b.String} + case token.LEQ: + return result{nil, a.String <= b.String} + case token.ADD: + c.Length = a.Length + b.Length + if a.Length == uint64(len(a.String)) { + c.String = a.String + b.String + } else { + // The first string was truncated at a.Length characters, so the sum + // must be truncated there too. + c.String = a.String + } + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case untString: + b, ok := y.v.(untString) + if !ok { + return e.err("type mismatch") + } + var c untString + switch op { + case token.EQL: + return result{nil, a == b} + case token.LSS: + return result{nil, a < b} + case token.LEQ: + return result{nil, a <= b} + case token.ADD: + c = a + b + default: + return e.err("invalid operation") + } + return result{x.d, c} + + case untInt: + i := a.Int + b, ok := y.v.(untInt) + if !ok { + return e.err("type mismatch") + } + switch op { + case token.EQL: + return result{nil, i.Cmp(b.Int) == 0} + case token.LSS: + return result{nil, i.Cmp(b.Int) < 0} + case token.LEQ: + return result{nil, i.Cmp(b.Int) <= 0} + } + c := new(big.Int) + switch op { + case token.ADD: + c.Add(i, b.Int) + case token.SUB: + c.Sub(i, b.Int) + case token.OR: + c.Or(i, b.Int) + case token.XOR: + c.Xor(i, b.Int) + case token.MUL: + c.Mul(i, b.Int) + case token.QUO: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Quo(i, b.Int) + case token.REM: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Mod(i, b.Int) + case token.AND: + c.And(i, b.Int) + case token.AND_NOT: + c.AndNot(i, b.Int) + default: + return e.err("invalid operation") + } + return result{nil, untInt{c}} + + case untRune: + i := a.Int + b, ok := y.v.(untRune) + if !ok { + return e.err("type mismatch") + } + switch op { + case token.EQL: + return result{nil, i.Cmp(b.Int) == 0} + case token.LSS: + return result{nil, i.Cmp(b.Int) < 0} + case token.LEQ: + return result{nil, i.Cmp(b.Int) <= 0} + } + c := new(big.Int) + switch op { + case token.ADD: + c.Add(i, b.Int) + case token.SUB: + c.Sub(i, b.Int) + case token.OR: + c.Or(i, b.Int) + case token.XOR: + c.Xor(i, b.Int) + case token.MUL: + c.Mul(i, b.Int) + case token.QUO: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Quo(i, b.Int) + case token.REM: + if b.Sign() == 0 { + return e.err("integer divide by zero") + } + c.Mod(i, b.Int) + case token.AND: + c.And(i, b.Int) + case token.AND_NOT: + c.AndNot(i, b.Int) + default: + return e.err("invalid operation") + } + return result{nil, untRune{c}} + + case untFloat: + r := a.Float + b, ok := y.v.(untFloat) + if !ok { + return e.err("type mismatch") + } + switch op { + case token.EQL: + return result{nil, r.Cmp(b.Float) == 0} + case token.LSS: + return result{nil, r.Cmp(b.Float) < 0} + case token.LEQ: + return result{nil, r.Cmp(b.Float) <= 0} + } + c := new(big.Float) + switch op { + case token.ADD: + c.Add(r, b.Float) + case token.SUB: + c.Sub(r, b.Float) + case token.MUL: + c.Mul(r, b.Float) + case token.QUO: + if b.Sign() == 0 { + return e.err("divide by zero") + } + c.Quo(r, b.Float) + default: + return e.err("invalid operation") + } + return result{nil, untFloat{c}} + + case untComplex: + b, ok := y.v.(untComplex) + if !ok { + return e.err("type mismatch") + } + var ( + ar = a.r + br = b.r + ai = a.i + bi = b.i + ) + if op == token.EQL { + return result{nil, ar.Cmp(br) == 0 && ai.Cmp(bi) == 0} + } + var ( + cr = new(big.Float) + ci = new(big.Float) + ) + switch op { + case token.ADD: + cr.Add(ar, br) + ci.Add(ai, bi) + case token.SUB: + cr.Sub(ar, br) + ci.Sub(ai, bi) + case token.MUL: + var t0, t1 big.Float + t0.Mul(ar, br) + t1.Mul(ai, bi) + cr.Sub(&t0, &t1) + t0.Mul(ar, bi) + t1.Mul(ai, br) + ci.Add(&t0, &t1) + case token.QUO: + // a/b = a*conj(b)/|b|^2 + var t0, t1 big.Float + cr.Mul(ar, br) + t0.Mul(ai, bi) + cr.Add(cr, &t0) // cr = Re(a*conj(b)) + ci.Mul(ai, br) + t0.Mul(ar, bi) + ci.Sub(ci, &t0) // ci = Im(a*conj(b)) + t0.Mul(br, br) + t1.Mul(bi, bi) + t0.Add(&t0, &t1) // t0 = |b|^2 + if t0.Sign() == 0 { + return e.err("divide by zero") + } + cr.Quo(cr, &t0) // cr = Re(a*conj(b))/|b|^2 = Re(a/b) + ci.Quo(ci, &t0) // ci = Im(a*conj(b))/|b|^2 = Im(a/b) + } + return result{nil, untComplex{cr, ci}} + } + + return e.err("invalid operation") +} + +// findLocalVar finds a local variable (or function parameter) by name, and +// returns its address and DWARF type. It returns a nil type on failure. +// The PC and SP are used to determine the current function and stack frame. +func (s *Server) findLocalVar(name string, pc, sp uint64) (uint64, dwarf.Type) { + // Find the DWARF entry for the function at pc. + funcEntry, _, err := s.dwarfData.PCToFunction(uint64(pc)) + if err != nil { + return 0, nil + } + + // Compute the stack frame pointer. + fpOffset, err := s.dwarfData.PCToSPOffset(uint64(pc)) + if err != nil { + return 0, nil + } + framePointer := sp + uint64(fpOffset) + + // Check each child of the function's DWARF entry to see if it is a parameter + // or local variable with the right name. If so, return its address and type. + r := s.dwarfData.Reader() + r.Seek(funcEntry.Offset) + for { + varEntry, err := r.Next() + if err != nil { + break + } + if varEntry.Tag == 0 { + // This tag marks the end of the function's DWARF entry's children. + break + } + + // Check this entry corresponds to a local variable or function parameter, + // that it has the correct name, and that we can get its type and location. + // If so, return them. + if varEntry.Tag != dwarf.TagFormalParameter && varEntry.Tag != dwarf.TagVariable { + continue + } + varName, ok := varEntry.Val(dwarf.AttrName).(string) + if !ok { + continue + } + if varName != name { + continue + } + varTypeOffset, ok := varEntry.Val(dwarf.AttrType).(dwarf.Offset) + if !ok { + continue + } + varType, err := s.dwarfData.Type(varTypeOffset) + if err != nil { + continue + } + locationAttribute := varEntry.Val(dwarf.AttrLocation) + if locationAttribute == nil { + continue + } + locationDescription, ok := locationAttribute.([]uint8) + if !ok { + continue + } + frameOffset, err := evalLocation(locationDescription) + if err != nil { + continue + } + return framePointer + uint64(frameOffset), varType + } + + return 0, nil +} + +// findGlobalVar finds a global variable by name, and returns its address and +// DWARF type. It returns a nil type on failure. +func (s *Server) findGlobalVar(name string) (uint64, dwarf.Type) { + entry, err := s.dwarfData.LookupVariable(name) + if err != nil { + return 0, nil + } + loc, err := s.dwarfData.EntryLocation(entry) + if err != nil { + return 0, nil + } + ofs, err := s.dwarfData.EntryTypeOffset(entry) + if err != nil { + return 0, nil + } + typ, err := s.dwarfData.Type(ofs) + if err != nil { + return 0, nil + } + return loc, typ +} + +// intFromInteger converts an untyped integer constant to an int32 or int64, +// depending on the int size of the debugged program. +// It returns an error on overflow, or if it can't determine the int size. +func (e *evaluator) intFromInteger(v untInt) (interface{}, error) { + t, ok := e.getBaseType("int") + if !ok { + return nil, errors.New("couldn't get int size from DWARF info") + } + switch t.Common().ByteSize { + case 4: + if v.Cmp(bigIntMaxInt32) == +1 || v.Cmp(bigIntMinInt32) == -1 { + return nil, errors.New("constant overflows int") + } + return int32(v.Int64()), nil + case 8: + if v.Cmp(bigIntMaxInt64) == +1 || v.Cmp(bigIntMinInt64) == -1 { + return nil, errors.New("constant overflows int") + } + return v.Int64(), nil + } + return nil, errors.New("invalid int size in DWARF info") +} + +// uint8Result constructs a result for a uint8 value. +func (e *evaluator) uint8Result(v uint8) result { + t, ok := e.getBaseType("uint8") + if !ok { + e.err("couldn't construct uint8") + } + return result{t, uint8(v)} +} + +// stringResult constructs a result for a string value. +func (e *evaluator) stringResult(s string) result { + t, ok := e.getBaseType("string") + if !ok { + e.err("couldn't construct string") + } + return result{t, debug.String{Length: uint64(len(s)), String: s}} +} + +// getBaseType returns the *dwarf.Type with a given name. +// TODO: cache this. +func (e *evaluator) getBaseType(name string) (dwarf.Type, bool) { + entry, err := e.server.dwarfData.LookupEntry(name) + if err != nil { + return nil, false + } + t, err := e.server.dwarfData.Type(entry.Offset) + if err != nil { + return nil, false + } + return t, true +} + +// resultFrom constructs a result corresponding to a value in the program with +// the given address and DWARF type. +// If getAddress is true, the result will be the operand of an address expression, +// so resultFrom returns a result containing a value of type addressableValue. +func (e *evaluator) resultFrom(a uint64, t dwarf.Type, getAddress bool) result { + if a == 0 { + return e.err("nil pointer dereference") + } + if getAddress { + return result{t, addressableValue{a}} + } + v, err := e.server.value(t, a) + if err != nil { + return e.err(err.Error()) + } + return result{t, v} +} + +// zero returns the zero value of type t. +// TODO: implement for array and struct. +func (e *evaluator) zero(t dwarf.Type) result { + var v interface{} + switch typ := followTypedefs(t).(type) { + case *dwarf.CharType, *dwarf.IntType, *dwarf.EnumType: + switch typ.Common().ByteSize { + case 1: + v = int8(0) + case 2: + v = int16(0) + case 4: + v = int32(0) + case 8: + v = int64(0) + default: + return e.err("invalid integer size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.UcharType, *dwarf.UintType: + switch typ.Common().ByteSize { + case 1: + v = uint8(0) + case 2: + v = uint16(0) + case 4: + v = uint32(0) + case 8: + v = uint64(0) + default: + return e.err("invalid unsigned integer size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.FloatType: + switch typ.Common().ByteSize { + case 4: + v = float32(0) + case 8: + v = float64(0) + default: + return e.err("invalid float size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.ComplexType: + switch typ.Common().ByteSize { + case 8: + v = complex64(0) + case 16: + v = complex128(0) + default: + return e.err("invalid complex size " + fmt.Sprint(typ.Common().ByteSize)) + } + case *dwarf.BoolType: + v = false + case *dwarf.PtrType: + v = debug.Pointer{TypeID: uint64(t.Common().Offset)} + case *dwarf.SliceType: + v = debug.Slice{ + Array: debug.Array{ + ElementTypeID: uint64(typ.ElemType.Common().Offset), + StrideBits: uint64(typ.ElemType.Common().ByteSize) * 8, + }, + } + case *dwarf.StringType: + v = debug.String{} + case *dwarf.InterfaceType: + v = debug.Interface{} + case *dwarf.FuncType: + v = debug.Func{} + case *dwarf.MapType: + v = debug.Map{TypeID: uint64(t.Common().Offset)} + case *dwarf.ChanType: + v = debug.Channel{ + ElementTypeID: uint64(typ.ElemType.Common().Offset), + Stride: uint64(typ.ElemType.Common().ByteSize), + } + default: + return e.err("can't get zero value of this type") + } + return result{t, v} +} + +// convertUntyped converts x to be the same type as y, if x is untyped and the +// conversion is possible. +// +// An untyped bool can be converted to a boolean type. +// An untyped string can be converted to a string type. +// An untyped integer, rune, float or complex value can be converted to a +// numeric type, or to an untyped value later in that list. +// +// x is returned unchanged if none of these cases apply. +func convertUntyped(x, y result) result { + switch a := x.v.(type) { + case untInt: + i := a.Int + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + case float32: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, f} + case float64: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, f} + case complex64: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, complex(f, 0)} + case complex128: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, complex(f, 0)} + case untRune: + return result{nil, untRune{i}} + case untFloat: + return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}} + case untComplex: + return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}} + } + case untRune: + i := a.Int + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + case float32: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, f} + case float64: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, f} + case complex64: + f, _ := new(big.Float).SetInt(i).Float32() + return result{y.d, complex(f, 0)} + case complex128: + f, _ := new(big.Float).SetInt(i).Float64() + return result{y.d, complex(f, 0)} + case untRune: + return result{nil, untRune{i}} + case untFloat: + return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}} + case untComplex: + return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}} + } + case untFloat: + if a.IsInt() { + i, _ := a.Int(nil) + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + } + } + switch y.v.(type) { + case float32: + f, _ := a.Float32() + return result{y.d, float32(f)} + case float64: + f, _ := a.Float64() + return result{y.d, float64(f)} + case complex64: + f, _ := a.Float32() + return result{y.d, complex(f, 0)} + case complex128: + f, _ := a.Float64() + return result{y.d, complex(f, 0)} + case untComplex: + return result{nil, untComplex{a.Float, new(big.Float)}} + } + case untComplex: + if a.i.Sign() == 0 { + // a is a real number. + if a.r.IsInt() { + // a is an integer. + i, _ := a.r.Int(nil) + switch y.v.(type) { + case int8: + return result{y.d, int8(i.Int64())} + case int16: + return result{y.d, int16(i.Int64())} + case int32: + return result{y.d, int32(i.Int64())} + case int64: + return result{y.d, int64(i.Int64())} + case uint8: + return result{y.d, uint8(i.Uint64())} + case uint16: + return result{y.d, uint16(i.Uint64())} + case uint32: + return result{y.d, uint32(i.Uint64())} + case uint64: + return result{y.d, uint64(i.Uint64())} + } + } + switch y.v.(type) { + case float32: + f, _ := a.r.Float32() + return result{y.d, float32(f)} + case float64: + f, _ := a.r.Float64() + return result{y.d, float64(f)} + } + } + switch y.v.(type) { + case complex64: + r, _ := a.r.Float32() + i, _ := a.i.Float32() + return result{y.d, complex(r, i)} + case complex128: + r, _ := a.r.Float64() + i, _ := a.i.Float64() + return result{y.d, complex(r, i)} + } + case bool: + if x.d != nil { + // x is a typed bool, not an untyped bool. + break + } + switch y.v.(type) { + case bool: + return result{y.d, bool(a)} + } + case untString: + switch y.v.(type) { + case debug.String: + return result{y.d, debug.String{Length: uint64(len(a)), String: string(a)}} + } + } + return x +} + +// uint64FromResult converts a result into a uint64 for slice or index expressions. +// It returns an error if the conversion cannot be done. +func uint64FromResult(x result) (uint64, error) { + switch v := x.v.(type) { + case int8: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case int16: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case int32: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case int64: + if v < 0 { + return 0, errors.New("value is negative") + } + return uint64(v), nil + case uint8: + return uint64(v), nil + case uint16: + return uint64(v), nil + case uint32: + return uint64(v), nil + case uint64: + return v, nil + case untInt: + if v.Int.Sign() == -1 { + return 0, errors.New("value is negative") + } + if v.Int.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return v.Int.Uint64(), nil + case untRune: + if v.Sign() == -1 { + return 0, errors.New("value is negative") + } + if v.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return v.Uint64(), nil + case untFloat: + if !v.IsInt() { + return 0, errors.New("value is not an integer") + } + if v.Sign() == -1 { + return 0, errors.New("value is negative") + } + i, _ := v.Int(nil) + if i.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return i.Uint64(), nil + case untComplex: + if v.i.Sign() != 0 { + return 0, errors.New("value is complex") + } + if !v.r.IsInt() { + return 0, errors.New("value is not an integer") + } + if v.r.Sign() == -1 { + return 0, errors.New("value is negative") + } + i, _ := v.r.Int(nil) + if i.Cmp(bigIntMaxUint64) == +1 { + return 0, errors.New("value is too large") + } + return i.Uint64(), nil + } + return 0, fmt.Errorf("cannot convert to unsigned integer") +} + +// followTypedefs returns the underlying type of t, removing any typedefs. +// If t leads to a cycle of typedefs, followTypedefs returns nil. +func followTypedefs(t dwarf.Type) dwarf.Type { + // If t is a *dwarf.TypedefType, next returns t.Type, otherwise it returns t. + // The bool returned is true when the argument was a typedef. + next := func(t dwarf.Type) (dwarf.Type, bool) { + tt, ok := t.(*dwarf.TypedefType) + if !ok { + return t, false + } + return tt.Type, true + } + // Advance two pointers, one at twice the speed, so we can detect if we get + // stuck in a cycle. + slow, fast := t, t + for { + var wasTypedef bool + fast, wasTypedef = next(fast) + if !wasTypedef { + return fast + } + fast, wasTypedef = next(fast) + if !wasTypedef { + return fast + } + slow, _ = next(slow) + if slow == fast { + return nil + } + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/peek.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/peek.go new file mode 100644 index 0000000000..8b0e11bab1 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/peek.go @@ -0,0 +1,378 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// Functions for reading values of various types from a program's memory. + +// +build linux + +package server + +import ( + "errors" + "fmt" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +// peekBytes reads len(buf) bytes at addr. +func (s *Server) peekBytes(addr uint64, buf []byte) error { + return s.ptracePeek(s.stoppedPid, uintptr(addr), buf) +} + +// peekPtr reads a pointer at addr. +func (s *Server) peekPtr(addr uint64) (uint64, error) { + buf := make([]byte, s.arch.PointerSize) + if err := s.peekBytes(addr, buf); err != nil { + return 0, err + } + return s.arch.Uintptr(buf), nil +} + +// peekUint8 reads a single byte at addr. +func (s *Server) peekUint8(addr uint64) (byte, error) { + buf := make([]byte, 1) + if err := s.peekBytes(addr, buf); err != nil { + return 0, err + } + return uint8(s.arch.UintN(buf)), nil +} + +// peekInt reads an int of size n bytes at addr. +func (s *Server) peekInt(addr uint64, n int64) (int64, error) { + buf := make([]byte, n) + if err := s.peekBytes(addr, buf); err != nil { + return 0, err + } + return s.arch.IntN(buf), nil +} + +// peekUint reads a uint of size n bytes at addr. +func (s *Server) peekUint(addr uint64, n int64) (uint64, error) { + buf := make([]byte, n) + if err := s.peekBytes(addr, buf); err != nil { + return 0, err + } + return s.arch.UintN(buf), nil +} + +// peekSlice reads the header of a slice with the given type and address. +func (s *Server) peekSlice(t *dwarf.SliceType, addr uint64) (debug.Slice, error) { + ptr, err := s.peekPtrStructField(&t.StructType, addr, "array") + if err != nil { + return debug.Slice{}, fmt.Errorf("reading slice location: %s", err) + } + length, err := s.peekUintOrIntStructField(&t.StructType, addr, "len") + if err != nil { + return debug.Slice{}, fmt.Errorf("reading slice length: %s", err) + } + capacity, err := s.peekUintOrIntStructField(&t.StructType, addr, "cap") + if err != nil { + return debug.Slice{}, fmt.Errorf("reading slice capacity: %s", err) + } + if capacity < length { + return debug.Slice{}, fmt.Errorf("slice's capacity %d is less than its length %d", capacity, length) + } + + return debug.Slice{ + debug.Array{ + ElementTypeID: uint64(t.ElemType.Common().Offset), + Address: uint64(ptr), + Length: length, + StrideBits: uint64(t.ElemType.Common().ByteSize) * 8, + }, + capacity, + }, nil +} + +// peekString reads a string of the given type at the given address. +// At most byteLimit bytes will be read. If the string is longer, "..." is appended. +func (s *Server) peekString(typ *dwarf.StringType, a uint64, byteLimit uint64) (string, error) { + ptr, err := s.peekPtrStructField(&typ.StructType, a, "str") + if err != nil { + return "", err + } + length, err := s.peekUintOrIntStructField(&typ.StructType, a, "len") + if err != nil { + return "", err + } + if length > byteLimit { + buf := make([]byte, byteLimit, byteLimit+3) + if err := s.peekBytes(ptr, buf); err != nil { + return "", err + } else { + buf = append(buf, '.', '.', '.') + return string(buf), nil + } + } else { + buf := make([]byte, length) + if err := s.peekBytes(ptr, buf); err != nil { + return "", err + } else { + return string(buf), nil + } + } +} + +// peekCString reads a NUL-terminated string at the given address. +// At most byteLimit bytes will be read. If the string is longer, "..." is appended. +// peekCString never returns errors; if an error occurs, the string will be truncated in some way. +func (s *Server) peekCString(a uint64, byteLimit uint64) string { + buf := make([]byte, byteLimit, byteLimit+3) + s.peekBytes(a, buf) + for i, c := range buf { + if c == 0 { + return string(buf[0:i]) + } + } + buf = append(buf, '.', '.', '.') + return string(buf) +} + +// peekPtrStructField reads a pointer in the field fieldName of the struct +// of type t at addr. +func (s *Server) peekPtrStructField(t *dwarf.StructType, addr uint64, fieldName string) (uint64, error) { + f, err := getField(t, fieldName) + if err != nil { + return 0, fmt.Errorf("reading field %s: %s", fieldName, err) + } + if _, ok := f.Type.(*dwarf.PtrType); !ok { + return 0, fmt.Errorf("field %s is not a pointer", fieldName) + } + return s.peekPtr(addr + uint64(f.ByteOffset)) +} + +// peekUintOrIntStructField reads a signed or unsigned integer in the field fieldName +// of the struct of type t at addr. If the value is negative, it returns an error. +// This function is used when the value should be non-negative, but the DWARF +// type of the field may be signed or unsigned. +func (s *Server) peekUintOrIntStructField(t *dwarf.StructType, addr uint64, fieldName string) (uint64, error) { + f, err := getField(t, fieldName) + if err != nil { + return 0, fmt.Errorf("reading field %s: %s", fieldName, err) + } + ut, ok := f.Type.(*dwarf.UintType) + if ok { + return s.peekUint(addr+uint64(f.ByteOffset), ut.ByteSize) + } + it, ok := f.Type.(*dwarf.IntType) + if !ok { + return 0, fmt.Errorf("field %s is not an integer", fieldName) + } + i, err := s.peekInt(addr+uint64(f.ByteOffset), it.ByteSize) + if err != nil { + return 0, err + } + if i < 0 { + return 0, fmt.Errorf("field %s is negative", fieldName) + } + return uint64(i), nil +} + +// peekUintStructField reads a uint in the field fieldName of the struct +// of type t at addr. The size of the uint is determined by the field. +func (s *Server) peekUintStructField(t *dwarf.StructType, addr uint64, fieldName string) (uint64, error) { + f, err := getField(t, fieldName) + if err != nil { + return 0, fmt.Errorf("reading field %s: %s", fieldName, err) + } + ut, ok := f.Type.(*dwarf.UintType) + if !ok { + return 0, fmt.Errorf("field %s is not an unsigned integer", fieldName) + } + return s.peekUint(addr+uint64(f.ByteOffset), ut.ByteSize) +} + +// peekIntStructField reads an int in the field fieldName of the struct +// of type t at addr. The size of the int is determined by the field. +func (s *Server) peekIntStructField(t *dwarf.StructType, addr uint64, fieldName string) (int64, error) { + f, err := getField(t, fieldName) + if err != nil { + return 0, fmt.Errorf("reading field %s: %s", fieldName, err) + } + it, ok := f.Type.(*dwarf.IntType) + if !ok { + return 0, fmt.Errorf("field %s is not a signed integer", fieldName) + } + return s.peekInt(addr+uint64(f.ByteOffset), it.ByteSize) +} + +// peekStringStructField reads a string field from the struct of the given type +// at the given address. +// At most byteLimit bytes will be read. If the string is longer, "..." is appended. +func (s *Server) peekStringStructField(t *dwarf.StructType, addr uint64, fieldName string, byteLimit uint64) (string, error) { + f, err := getField(t, fieldName) + if err != nil { + return "", fmt.Errorf("reading field %s: %s", fieldName, err) + } + st, ok := followTypedefs(f.Type).(*dwarf.StringType) + if !ok { + return "", fmt.Errorf("field %s is not a string", fieldName) + } + return s.peekString(st, addr+uint64(f.ByteOffset), byteLimit) +} + +// peekMapLocationAndType returns the address and DWARF type of the underlying +// struct of a map variable. +func (s *Server) peekMapLocationAndType(t *dwarf.MapType, a uint64) (uint64, *dwarf.StructType, error) { + // Maps are pointers to structs. + pt, ok := t.Type.(*dwarf.PtrType) + if !ok { + return 0, nil, errors.New("bad map type: not a pointer") + } + st, ok := pt.Type.(*dwarf.StructType) + if !ok { + return 0, nil, errors.New("bad map type: not a pointer to a struct") + } + // a is the address of a pointer to a struct. Get the pointer's value. + a, err := s.peekPtr(a) + if err != nil { + return 0, nil, fmt.Errorf("reading map pointer: %s", err) + } + return a, st, nil +} + +// peekMapValues reads a map at the given address and calls fn with the addresses for each (key, value) pair. +// If fn returns false, peekMapValues stops. +func (s *Server) peekMapValues(t *dwarf.MapType, a uint64, fn func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool) error { + a, st, err := s.peekMapLocationAndType(t, a) + if err != nil { + return err + } + if a == 0 { + // The pointer was nil, so the map is empty. + return nil + } + // Gather information about the struct type and the map bucket type. + b, err := s.peekUintStructField(st, a, "B") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + buckets, err := s.peekPtrStructField(st, a, "buckets") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + oldbuckets, err := s.peekPtrStructField(st, a, "oldbuckets") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + bf, err := getField(st, "buckets") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + bucketPtrType, ok := bf.Type.(*dwarf.PtrType) + if !ok { + return errors.New("bad map bucket type: not a pointer") + } + bt, ok := bucketPtrType.Type.(*dwarf.StructType) + if !ok { + return errors.New("bad map bucket type: not a pointer to a struct") + } + bucketSize := uint64(bucketPtrType.Type.Size()) + tophashField, err := getField(bt, "tophash") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + bucketCnt := uint64(tophashField.Type.Size()) + tophashFieldOffset := uint64(tophashField.ByteOffset) + keysField, err := getField(bt, "keys") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + keysType, ok := keysField.Type.(*dwarf.ArrayType) + if !ok { + return errors.New(`bad map bucket type: "keys" is not an array`) + } + keyType := keysType.Type + keysStride := uint64(keysType.StrideBitSize / 8) + keysFieldOffset := uint64(keysField.ByteOffset) + valuesField, err := getField(bt, "values") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + valuesType, ok := valuesField.Type.(*dwarf.ArrayType) + if !ok { + return errors.New(`bad map bucket type: "values" is not an array`) + } + valueType := valuesType.Type + valuesStride := uint64(valuesType.StrideBitSize / 8) + valuesFieldOffset := uint64(valuesField.ByteOffset) + overflowField, err := getField(bt, "overflow") + if err != nil { + return fmt.Errorf("reading map: %s", err) + } + overflowFieldOffset := uint64(overflowField.ByteOffset) + + // Iterate through the two arrays of buckets. + bucketArrays := [2]struct { + addr uint64 + size uint64 + }{ + {buckets, 1 << b}, + {oldbuckets, 1 << (b - 1)}, + } + for _, bucketArray := range bucketArrays { + if bucketArray.addr == 0 { + continue + } + for i := uint64(0); i < bucketArray.size; i++ { + bucketAddr := bucketArray.addr + i*bucketSize + // Iterate through the linked list of buckets. + // TODO: check for repeated bucket pointers. + for bucketAddr != 0 { + // Iterate through each entry in the bucket. + for j := uint64(0); j < bucketCnt; j++ { + tophash, err := s.peekUint8(bucketAddr + tophashFieldOffset + j) + if err != nil { + return errors.New("reading map: " + err.Error()) + } + // From runtime/hashmap.go + const minTopHash = 4 + if tophash < minTopHash { + continue + } + keyAddr := bucketAddr + keysFieldOffset + j*keysStride + valAddr := bucketAddr + valuesFieldOffset + j*valuesStride + if !fn(keyAddr, valAddr, keyType, valueType) { + return nil + } + } + var err error + bucketAddr, err = s.peekPtr(bucketAddr + overflowFieldOffset) + if err != nil { + return errors.New("reading map: " + err.Error()) + } + } + } + } + + return nil +} + +// peekMapLength returns the number of elements in a map at the given address. +func (s *Server) peekMapLength(t *dwarf.MapType, a uint64) (uint64, error) { + a, st, err := s.peekMapLocationAndType(t, a) + if err != nil { + return 0, err + } + if a == 0 { + // The pointer was nil, so the map is empty. + return 0, nil + } + length, err := s.peekUintOrIntStructField(st, a, "count") + if err != nil { + return 0, fmt.Errorf("reading map: %s", err) + } + return uint64(length), nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/print.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/print.go new file mode 100644 index 0000000000..7a004e5a2c --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/print.go @@ -0,0 +1,548 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 + +package server + +import ( + "bytes" + "fmt" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/arch" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +// typeAndAddress associates an address in the target with a DWARF type. +type typeAndAddress struct { + Type dwarf.Type + Address uint64 +} + +// Routines to print a value using DWARF type descriptions. +// TODO: Does this deserve its own package? It has no dependencies on Server. + +// A Printer pretty-prints values in the target address space. +// It can be reused after each printing operation to avoid unnecessary +// allocations. However, it is not safe for concurrent access. +type Printer struct { + err error // Sticky error value. + server *Server + dwarf *dwarf.Data + arch *arch.Architecture + printBuf bytes.Buffer // Accumulates the output. + visited map[typeAndAddress]bool // Prevents looping on cyclic data. +} + +// printf prints to printBuf. +func (p *Printer) printf(format string, args ...interface{}) { + fmt.Fprintf(&p.printBuf, format, args...) +} + +// errorf prints the error to printBuf, then sets the sticky error for the +// printer, if not already set. +func (p *Printer) errorf(format string, args ...interface{}) { + fmt.Fprintf(&p.printBuf, "<"+format+">", args...) + if p.err != nil { + return + } + p.err = fmt.Errorf(format, args...) +} + +// NewPrinter returns a printer that can use the Server to access and print +// values of the specified architecture described by the provided DWARF data. +func NewPrinter(arch *arch.Architecture, dwarf *dwarf.Data, server *Server) *Printer { + return &Printer{ + server: server, + arch: arch, + dwarf: dwarf, + visited: make(map[typeAndAddress]bool), + } +} + +// reset resets the Printer. It must be called before starting a new +// printing operation. +func (p *Printer) reset() { + p.err = nil + p.printBuf.Reset() + // Just wipe the map rather than reallocating. It's almost always tiny. + for k := range p.visited { + delete(p.visited, k) + } +} + +// Sprint returns the pretty-printed value of the item with the given name, such as "main.global". +func (p *Printer) Sprint(name string) (string, error) { + entry, err := p.dwarf.LookupEntry(name) + if err != nil { + return "", err + } + p.reset() + switch entry.Tag { + case dwarf.TagVariable: // TODO: What other entries have global location attributes? + var a uint64 + iface := entry.Val(dwarf.AttrLocation) + if iface != nil { + a = p.decodeLocation(iface.([]byte)) + } + p.printEntryValueAt(entry, a) + default: + p.errorf("unrecognized entry type %s", entry.Tag) + } + return p.printBuf.String(), p.err +} + +// Figure 24 of DWARF v4. +const ( + locationAddr = 0x03 +) + +// decodeLocation decodes the dwarf data describing an address. +func (p *Printer) decodeLocation(data []byte) uint64 { + switch data[0] { + case locationAddr: + return p.arch.Uintptr(data[1:]) + default: + p.errorf("unimplemented location type %#x", data[0]) + } + return 0 +} + +// SprintEntry returns the pretty-printed value of the item with the specified DWARF Entry and address. +func (p *Printer) SprintEntry(entry *dwarf.Entry, a uint64) (string, error) { + p.reset() + p.printEntryValueAt(entry, a) + return p.printBuf.String(), p.err +} + +// printEntryValueAt pretty-prints the data at the specified address. +// using the type information in the Entry. +func (p *Printer) printEntryValueAt(entry *dwarf.Entry, a uint64) { + if a == 0 { + p.printf("") + return + } + switch entry.Tag { + case dwarf.TagVariable, dwarf.TagFormalParameter: + // OK + default: + p.errorf("unrecognized entry type %s", entry.Tag) + return + } + iface := entry.Val(dwarf.AttrType) + if iface == nil { + p.errorf("no type") + return + } + typ, err := p.dwarf.Type(iface.(dwarf.Offset)) + if err != nil { + p.errorf("type lookup: %v", err) + return + } + p.printValueAt(typ, a) +} + +// printValueAt pretty-prints the data at the specified address. +// using the provided type information. +func (p *Printer) printValueAt(typ dwarf.Type, a uint64) { + if a != 0 { + // Check if we are repeating the same type and address. + ta := typeAndAddress{typ, a} + if p.visited[ta] { + p.printf("(%v %#x)", typ, a) + return + } + p.visited[ta] = true + } + switch typ := typ.(type) { + case *dwarf.BoolType: + if typ.ByteSize != 1 { + p.errorf("unrecognized bool size %d", typ.ByteSize) + return + } + if b, err := p.server.peekUint8(a); err != nil { + p.errorf("reading bool: %s", err) + } else { + p.printf("%t", b != 0) + } + case *dwarf.PtrType: + if ptr, err := p.server.peekPtr(a); err != nil { + p.errorf("reading pointer: %s", err) + } else { + p.printf("%#x", ptr) + } + case *dwarf.IntType: + // Sad we can't tell a rune from an int32. + if i, err := p.server.peekInt(a, typ.ByteSize); err != nil { + p.errorf("reading integer: %s", err) + } else { + p.printf("%d", i) + } + case *dwarf.UintType: + if u, err := p.server.peekUint(a, typ.ByteSize); err != nil { + p.errorf("reading unsigned integer: %s", err) + } else { + p.printf("%d", u) + } + case *dwarf.FloatType: + buf := make([]byte, typ.ByteSize) + if err := p.server.peekBytes(a, buf); err != nil { + p.errorf("reading float: %s", err) + return + } + switch typ.ByteSize { + case 4: + p.printf("%g", p.arch.Float32(buf)) + case 8: + p.printf("%g", p.arch.Float64(buf)) + default: + p.errorf("unrecognized float size %d", typ.ByteSize) + } + case *dwarf.ComplexType: + buf := make([]byte, typ.ByteSize) + if err := p.server.peekBytes(a, buf); err != nil { + p.errorf("reading complex: %s", err) + return + } + switch typ.ByteSize { + case 8: + p.printf("%g", p.arch.Complex64(buf)) + case 16: + p.printf("%g", p.arch.Complex128(buf)) + default: + p.errorf("unrecognized complex size %d", typ.ByteSize) + } + case *dwarf.StructType: + if typ.Kind != "struct" { + // Could be "class" or "union". + p.errorf("can't handle struct type %s", typ.Kind) + return + } + p.printf("%s {", typ.String()) + for i, field := range typ.Field { + if i != 0 { + p.printf(", ") + } + p.printValueAt(field.Type, a+uint64(field.ByteOffset)) + } + p.printf("}") + case *dwarf.ArrayType: + p.printArrayAt(typ, a) + case *dwarf.InterfaceType: + p.printInterfaceAt(typ, a) + case *dwarf.MapType: + p.printMapAt(typ, a) + case *dwarf.ChanType: + p.printChannelAt(typ, a) + case *dwarf.SliceType: + p.printSliceAt(typ, a) + case *dwarf.StringType: + p.printStringAt(typ, a) + case *dwarf.TypedefType: + p.printValueAt(typ.Type, a) + case *dwarf.FuncType: + p.printf("%v @%#x ", typ, a) + case *dwarf.VoidType: + p.printf("void") + default: + p.errorf("unimplemented type %v", typ) + } +} + +func (p *Printer) printArrayAt(typ *dwarf.ArrayType, a uint64) { + elemType := typ.Type + length := typ.Count + stride, ok := p.arrayStride(typ) + if !ok { + p.errorf("can't determine element size") + } + p.printf("%s{", typ) + n := length + if n > 100 { + n = 100 // TODO: Have a way to control this? + } + for i := int64(0); i < n; i++ { + if i != 0 { + p.printf(", ") + } + p.printValueAt(elemType, a) + a += stride // TODO: Alignment and padding - not given by Type + } + if n < length { + p.printf(", ...") + } + p.printf("}") +} + +func (p *Printer) printInterfaceAt(t *dwarf.InterfaceType, a uint64) { + // t embeds a TypedefType, which may point to another typedef. + // The underlying type should be a struct. + st, ok := followTypedefs(&t.TypedefType).(*dwarf.StructType) + if !ok { + p.errorf("bad interface type: not a struct") + return + } + p.printf("(") + tab, err := p.server.peekPtrStructField(st, a, "tab") + if err != nil { + p.errorf("reading interface type: %s", err) + } else { + f, err := getField(st, "tab") + if err != nil { + p.errorf("%s", err) + } else { + p.printTypeOfInterface(f.Type, tab) + } + } + p.printf(", ") + data, err := p.server.peekPtrStructField(st, a, "data") + if err != nil { + p.errorf("reading interface value: %s", err) + } else if data == 0 { + p.printf("") + } else { + p.printf("%#x", data) + } + p.printf(")") +} + +// printTypeOfInterface prints the type of the given tab pointer. +func (p *Printer) printTypeOfInterface(t dwarf.Type, a uint64) { + if a == 0 { + p.printf("") + return + } + // t should be a pointer to a struct containing _type, which is a pointer to a + // struct containing _string, which is the name of the type. + // Depending on the compiler version, some of these types can be typedefs, and + // _string may be a string or a *string. + t1, ok := followTypedefs(t).(*dwarf.PtrType) + if !ok { + p.errorf("interface's tab is not a pointer") + return + } + t2, ok := followTypedefs(t1.Type).(*dwarf.StructType) + if !ok { + p.errorf("interface's tab is not a pointer to struct") + return + } + typeField, err := getField(t2, "_type") + if err != nil { + p.errorf("%s", err) + return + } + t3, ok := followTypedefs(typeField.Type).(*dwarf.PtrType) + if !ok { + p.errorf("interface's _type is not a pointer") + return + } + t4, ok := followTypedefs(t3.Type).(*dwarf.StructType) + if !ok { + p.errorf("interface's _type is not a pointer to struct") + return + } + stringField, err := getField(t4, "_string") + if err != nil { + p.errorf("%s", err) + return + } + if t5, ok := stringField.Type.(*dwarf.PtrType); ok { + stringType, ok := t5.Type.(*dwarf.StringType) + if !ok { + p.errorf("interface _string is a pointer to %T, want string or *string", t5.Type) + return + } + typeAddr, err := p.server.peekPtrStructField(t2, a, "_type") + if err != nil { + p.errorf("reading interface type: %s", err) + return + } + stringAddr, err := p.server.peekPtrStructField(t4, typeAddr, "_string") + if err != nil { + p.errorf("reading interface type: %s", err) + return + } + p.printStringAt(stringType, stringAddr) + } else { + stringType, ok := stringField.Type.(*dwarf.StringType) + if !ok { + p.errorf("interface _string is a %T, want string or *string", stringField.Type) + return + } + typeAddr, err := p.server.peekPtrStructField(t2, a, "_type") + if err != nil { + p.errorf("reading interface type: %s", err) + return + } + stringAddr := typeAddr + uint64(stringField.ByteOffset) + p.printStringAt(stringType, stringAddr) + } +} + +// maxMapValuesToPrint values are printed for each map; any remaining values are +// truncated to "...". +const maxMapValuesToPrint = 8 + +func (p *Printer) printMapAt(typ *dwarf.MapType, a uint64) { + count := 0 + fn := func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) (stop bool) { + count++ + if count > maxMapValuesToPrint { + return false + } + if count > 1 { + p.printf(" ") + } + p.printValueAt(keyType, keyAddr) + p.printf(":") + p.printValueAt(valType, valAddr) + return true + } + p.printf("map[") + if err := p.server.peekMapValues(typ, a, fn); err != nil { + p.errorf("reading map values: %s", err) + } + if count > maxMapValuesToPrint { + p.printf(" ...") + } + p.printf("]") +} + +func (p *Printer) printChannelAt(ct *dwarf.ChanType, a uint64) { + p.printf("(chan %s ", ct.ElemType) + defer p.printf(")") + + a, err := p.server.peekPtr(a) + if err != nil { + p.errorf("reading channel: %s", err) + return + } + if a == 0 { + p.printf("") + return + } + p.printf("%#x", a) + + // ct is a typedef for a pointer to a struct. + pt, ok := ct.TypedefType.Type.(*dwarf.PtrType) + if !ok { + p.errorf("bad channel type: not a pointer") + return + } + st, ok := pt.Type.(*dwarf.StructType) + if !ok { + p.errorf("bad channel type: not a pointer to a struct") + return + } + + // Print the channel buffer's length (qcount) and capacity (dataqsiz), + // if not 0/0. + qcount, err := p.server.peekUintOrIntStructField(st, a, "qcount") + if err != nil { + p.errorf("reading channel: %s", err) + return + } + dataqsiz, err := p.server.peekUintOrIntStructField(st, a, "dataqsiz") + if err != nil { + p.errorf("reading channel: %s", err) + return + } + if qcount != 0 || dataqsiz != 0 { + p.printf(" [%d/%d]", qcount, dataqsiz) + } +} + +func (p *Printer) printSliceAt(typ *dwarf.SliceType, a uint64) { + // Slices look like a struct with fields array *elemtype, len uint32/64, cap uint32/64. + // BUG: Slice header appears to have fields with ByteSize == 0 + ptr, err := p.server.peekPtrStructField(&typ.StructType, a, "array") + if err != nil { + p.errorf("reading slice: %s", err) + return + } + length, err := p.server.peekUintOrIntStructField(&typ.StructType, a, "len") + if err != nil { + p.errorf("reading slice: %s", err) + return + } + // Capacity is not used yet. + _, err = p.server.peekUintOrIntStructField(&typ.StructType, a, "cap") + if err != nil { + p.errorf("reading slice: %s", err) + return + } + elemType := typ.ElemType + size, ok := p.sizeof(typ.ElemType) + if !ok { + p.errorf("can't determine element size") + } + p.printf("%s{", typ) + for i := uint64(0); i < length; i++ { + if i != 0 { + p.printf(", ") + } + p.printValueAt(elemType, ptr) + ptr += size // TODO: Alignment and padding - not given by Type + } + p.printf("}") +} + +func (p *Printer) printStringAt(typ *dwarf.StringType, a uint64) { + const maxStringSize = 100 + if s, err := p.server.peekString(typ, a, maxStringSize); err != nil { + p.errorf("reading string: %s", err) + } else { + p.printf("%q", s) + } +} + +// sizeof returns the byte size of the type. +func (p *Printer) sizeof(typ dwarf.Type) (uint64, bool) { + size := typ.Size() // Will be -1 if ByteSize is not set. + if size >= 0 { + return uint64(size), true + } + switch typ.(type) { + case *dwarf.PtrType: + // This is the only one we know of, but more may arise. + return uint64(p.arch.PointerSize), true + } + return 0, false +} + +// arrayStride returns the stride of a dwarf.ArrayType in bytes. +func (p *Printer) arrayStride(t *dwarf.ArrayType) (uint64, bool) { + stride := t.StrideBitSize + if stride > 0 { + return uint64(stride / 8), true + } + return p.sizeof(t.Type) +} + +// getField finds the *dwarf.StructField in a dwarf.StructType with name fieldName. +func getField(t *dwarf.StructType, fieldName string) (*dwarf.StructField, error) { + var r *dwarf.StructField + for _, f := range t.Field { + if f.Name == fieldName { + if r != nil { + return nil, fmt.Errorf("struct definition repeats field %s", fieldName) + } + r = f + } + } + if r == nil { + return nil, fmt.Errorf("struct field %s missing", fieldName) + } + return r, nil +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/protocol/protocol.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/protocol/protocol.go new file mode 100644 index 0000000000..31c852c0d9 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/protocol/protocol.go @@ -0,0 +1,174 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 protocol defines the types used to represent calls to the debug server. +package protocol + +import ( + "encoding/gob" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" +) + +func init() { + // Register implementations of debug.Value with gob. + gob.Register(debug.Pointer{}) + gob.Register(debug.Array{}) + gob.Register(debug.Struct{}) + gob.Register(debug.Slice{}) + gob.Register(debug.Map{}) + gob.Register(debug.String{}) + gob.Register(debug.Channel{}) + gob.Register(debug.Func{}) + gob.Register(debug.Interface{}) +} + +// For regularity, each method has a unique Request and a Response type even +// when not strictly necessary. + +// File I/O, at the top because they're simple. + +type ReadAtRequest struct { + FD int + Len int + Offset int64 +} + +type ReadAtResponse struct { + Data []byte +} + +type WriteAtRequest struct { + FD int + Data []byte + Offset int64 +} + +type WriteAtResponse struct { + Len int +} + +type CloseRequest struct { + FD int +} + +type CloseResponse struct { +} + +// Program methods. + +type OpenRequest struct { + Name string + Mode string +} + +type OpenResponse struct { + FD int +} + +type RunRequest struct { + Args []string +} + +type RunResponse struct { + Status debug.Status +} + +type ResumeRequest struct { +} + +type ResumeResponse struct { + Status debug.Status +} + +type BreakpointRequest struct { + Address uint64 +} + +type BreakpointAtFunctionRequest struct { + Function string +} + +type BreakpointAtLineRequest struct { + File string + Line uint64 +} + +type BreakpointResponse struct { + PCs []uint64 +} + +type DeleteBreakpointsRequest struct { + PCs []uint64 +} + +type DeleteBreakpointsResponse struct { +} + +type EvalRequest struct { + Expr string +} + +type EvalResponse struct { + Result []string +} + +type EvaluateRequest struct { + Expression string +} + +type EvaluateResponse struct { + Result debug.Value +} + +type FramesRequest struct { + Count int +} + +type FramesResponse struct { + Frames []debug.Frame +} + +type VarByNameRequest struct { + Name string +} + +type VarByNameResponse struct { + Var debug.Var +} + +type ValueRequest struct { + Var debug.Var +} + +type ValueResponse struct { + Value debug.Value +} + +type MapElementRequest struct { + Map debug.Map + Index uint64 +} + +type MapElementResponse struct { + Key debug.Var + Value debug.Var +} + +type GoroutinesRequest struct { +} + +type GoroutinesResponse struct { + Goroutines []*debug.Goroutine +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/ptrace.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/ptrace.go new file mode 100644 index 0000000000..dba82454b0 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/ptrace.go @@ -0,0 +1,157 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 + +package server + +import ( + "fmt" + "os" + "runtime" + "syscall" + "time" +) + +// ptraceRun runs all the closures from fc on a dedicated OS thread. Errors +// are returned on ec. Both channels must be unbuffered, to ensure that the +// resultant error is sent back to the same goroutine that sent the closure. +func ptraceRun(fc chan func() error, ec chan error) { + if cap(fc) != 0 || cap(ec) != 0 { + panic("ptraceRun was given buffered channels") + } + runtime.LockOSThread() + for f := range fc { + ec <- f() + } +} + +func (s *Server) startProcess(name string, argv []string, attr *os.ProcAttr) (proc *os.Process, err error) { + s.fc <- func() error { + var err1 error + proc, err1 = os.StartProcess(name, argv, attr) + return err1 + } + err = <-s.ec + return +} + +func (s *Server) ptraceCont(pid int, signal int) (err error) { + s.fc <- func() error { + return syscall.PtraceCont(pid, signal) + } + return <-s.ec +} + +func (s *Server) ptraceGetRegs(pid int, regsout *syscall.PtraceRegs) (err error) { + s.fc <- func() error { + return syscall.PtraceGetRegs(pid, regsout) + } + return <-s.ec +} + +func (s *Server) ptracePeek(pid int, addr uintptr, out []byte) (err error) { + s.fc <- func() error { + n, err := syscall.PtracePeekText(pid, addr, out) + if err != nil { + return err + } + if n != len(out) { + return fmt.Errorf("ptracePeek: peeked %d bytes, want %d", n, len(out)) + } + return nil + } + return <-s.ec +} + +func (s *Server) ptracePoke(pid int, addr uintptr, data []byte) (err error) { + s.fc <- func() error { + n, err := syscall.PtracePokeText(pid, addr, data) + if err != nil { + return err + } + if n != len(data) { + return fmt.Errorf("ptracePoke: poked %d bytes, want %d", n, len(data)) + } + return nil + } + return <-s.ec +} + +func (s *Server) ptraceSetOptions(pid int, options int) (err error) { + s.fc <- func() error { + return syscall.PtraceSetOptions(pid, options) + } + return <-s.ec +} + +func (s *Server) ptraceSetRegs(pid int, regs *syscall.PtraceRegs) (err error) { + s.fc <- func() error { + return syscall.PtraceSetRegs(pid, regs) + } + return <-s.ec +} + +func (s *Server) ptraceSingleStep(pid int) (err error) { + s.fc <- func() error { + return syscall.PtraceSingleStep(pid) + } + return <-s.ec +} + +type breakpointsChangedError struct { + call call +} + +func (*breakpointsChangedError) Error() string { + return "breakpoints changed" +} + +func (s *Server) wait(pid int, allowBreakpointsChange bool) (wpid int, status syscall.WaitStatus, err error) { + // We poll syscall.Wait4 with WNOHANG, sleeping in between, as a poor man's + // waitpid-with-timeout. This allows adding and removing breakpoints + // concurrently with waiting to hit an existing breakpoint. + f := func() error { + var err1 error + wpid, err1 = syscall.Wait4(pid, &status, syscall.WALL|syscall.WNOHANG, nil) + return err1 + } + + const ( + minSleep = 1 * time.Microsecond + maxSleep = 100 * time.Millisecond + ) + for sleep := minSleep; ; { + s.fc <- f + err = <-s.ec + + // wpid == 0 means that wait found nothing (and returned due to WNOHANG). + if wpid != 0 { + return + } + + if allowBreakpointsChange { + select { + case c := <-s.breakpointc: + return 0, 0, &breakpointsChangedError{c} + default: + } + } + + time.Sleep(sleep) + if sleep < maxSleep { + sleep *= 10 + } + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/server.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/server.go new file mode 100644 index 0000000000..dabde3c133 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/server.go @@ -0,0 +1,1157 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 + +// Package server provides RPC access to a local program being debugged. +// It is the remote end of the client implementation of the Program interface. +package server + +//go:generate sh -c "m4 -P eval.m4 > eval.go" + +import ( + "bytes" + "errors" + "fmt" + "os" + "regexp" + "strconv" + "strings" + "sync" + "syscall" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/arch" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/macho" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/protocol" +) + +type breakpoint struct { + pc uint64 + origInstr [arch.MaxBreakpointSize]byte +} + +type call struct { + req, resp interface{} + errc chan error +} + +type Server struct { + arch arch.Architecture + executable string // Name of executable. + dwarfData *dwarf.Data + + breakpointc chan call + otherc chan call + + fc chan func() error + ec chan error + + proc *os.Process + procIsUp bool + stoppedPid int + stoppedRegs syscall.PtraceRegs + topOfStackAddrs []uint64 + breakpoints map[uint64]breakpoint + files []*file // Index == file descriptor. + printer *Printer + + // goroutineStack reads the stack of a (non-running) goroutine. + goroutineStack func(uint64) ([]debug.Frame, error) + goroutineStackOnce sync.Once +} + +// peek implements the Peeker interface required by the printer. +func (s *Server) peek(offset uintptr, buf []byte) error { + return s.ptracePeek(s.stoppedPid, offset, buf) +} + +// New parses the executable and builds local data structures for answering requests. +// It returns a Server ready to serve requests about the executable. +func New(executable string) (*Server, error) { + fd, err := os.Open(executable) + if err != nil { + return nil, err + } + defer fd.Close() + architecture, dwarfData, err := loadExecutable(fd) + if err != nil { + return nil, err + } + srv := &Server{ + arch: *architecture, + executable: executable, + dwarfData: dwarfData, + breakpointc: make(chan call), + otherc: make(chan call), + fc: make(chan func() error), + ec: make(chan error), + breakpoints: make(map[uint64]breakpoint), + } + srv.printer = NewPrinter(architecture, dwarfData, srv) + go ptraceRun(srv.fc, srv.ec) + go srv.loop() + return srv, nil +} + +func loadExecutable(f *os.File) (*arch.Architecture, *dwarf.Data, error) { + // TODO: How do we detect NaCl? + if obj, err := elf.NewFile(f); err == nil { + dwarfData, err := obj.DWARF() + if err != nil { + return nil, nil, err + } + + switch obj.Machine { + case elf.EM_ARM: + return &arch.ARM, dwarfData, nil + case elf.EM_386: + switch obj.Class { + case elf.ELFCLASS32: + return &arch.X86, dwarfData, nil + case elf.ELFCLASS64: + return &arch.AMD64, dwarfData, nil + } + case elf.EM_X86_64: + return &arch.AMD64, dwarfData, nil + } + return nil, nil, fmt.Errorf("unrecognized ELF architecture") + } + if obj, err := macho.NewFile(f); err == nil { + dwarfData, err := obj.DWARF() + if err != nil { + return nil, nil, err + } + + /* TODO + table, err := parseMachO(obj) + if err != nil { + return nil, nil, err + } + */ + switch obj.Cpu { + case macho.Cpu386: + return &arch.X86, dwarfData, nil + case macho.CpuAmd64: + return &arch.AMD64, dwarfData, nil + } + return nil, nil, fmt.Errorf("unrecognized Mach-O architecture") + } + return nil, nil, fmt.Errorf("unrecognized binary format") +} + +func (s *Server) loop() { + for { + var c call + select { + case c = <-s.breakpointc: + case c = <-s.otherc: + } + s.dispatch(c) + } +} + +func (s *Server) dispatch(c call) { + switch req := c.req.(type) { + case *protocol.BreakpointRequest: + c.errc <- s.handleBreakpoint(req, c.resp.(*protocol.BreakpointResponse)) + case *protocol.BreakpointAtFunctionRequest: + c.errc <- s.handleBreakpointAtFunction(req, c.resp.(*protocol.BreakpointResponse)) + case *protocol.BreakpointAtLineRequest: + c.errc <- s.handleBreakpointAtLine(req, c.resp.(*protocol.BreakpointResponse)) + case *protocol.DeleteBreakpointsRequest: + c.errc <- s.handleDeleteBreakpoints(req, c.resp.(*protocol.DeleteBreakpointsResponse)) + case *protocol.CloseRequest: + c.errc <- s.handleClose(req, c.resp.(*protocol.CloseResponse)) + case *protocol.EvalRequest: + c.errc <- s.handleEval(req, c.resp.(*protocol.EvalResponse)) + case *protocol.EvaluateRequest: + c.errc <- s.handleEvaluate(req, c.resp.(*protocol.EvaluateResponse)) + case *protocol.FramesRequest: + c.errc <- s.handleFrames(req, c.resp.(*protocol.FramesResponse)) + case *protocol.OpenRequest: + c.errc <- s.handleOpen(req, c.resp.(*protocol.OpenResponse)) + case *protocol.ReadAtRequest: + c.errc <- s.handleReadAt(req, c.resp.(*protocol.ReadAtResponse)) + case *protocol.ResumeRequest: + c.errc <- s.handleResume(req, c.resp.(*protocol.ResumeResponse)) + case *protocol.RunRequest: + c.errc <- s.handleRun(req, c.resp.(*protocol.RunResponse)) + case *protocol.VarByNameRequest: + c.errc <- s.handleVarByName(req, c.resp.(*protocol.VarByNameResponse)) + case *protocol.ValueRequest: + c.errc <- s.handleValue(req, c.resp.(*protocol.ValueResponse)) + case *protocol.MapElementRequest: + c.errc <- s.handleMapElement(req, c.resp.(*protocol.MapElementResponse)) + case *protocol.GoroutinesRequest: + c.errc <- s.handleGoroutines(req, c.resp.(*protocol.GoroutinesResponse)) + default: + panic(fmt.Sprintf("unexpected call request type %T", c.req)) + } +} + +func (s *Server) call(c chan call, req, resp interface{}) error { + errc := make(chan error) + c <- call{req, resp, errc} + return <-errc +} + +type file struct { + mode string + index int + f debug.File +} + +func (s *Server) Open(req *protocol.OpenRequest, resp *protocol.OpenResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleOpen(req *protocol.OpenRequest, resp *protocol.OpenResponse) error { + // TODO: Better simulation. For now we just open the named OS file. + var flag int + switch req.Mode { + case "r": + flag = os.O_RDONLY + case "w": + flag = os.O_WRONLY + case "rw": + flag = os.O_RDWR + default: + return fmt.Errorf("Open: bad open mode %q", req.Mode) + } + osFile, err := os.OpenFile(req.Name, flag, 0) + if err != nil { + return err + } + // Find a file descriptor (index) slot. + index := 0 + for ; index < len(s.files) && s.files[index] != nil; index++ { + } + f := &file{ + mode: req.Mode, + index: index, + f: osFile, + } + if index == len(s.files) { + s.files = append(s.files, f) + } else { + s.files[index] = f + } + return nil +} + +func (s *Server) ReadAt(req *protocol.ReadAtRequest, resp *protocol.ReadAtResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleReadAt(req *protocol.ReadAtRequest, resp *protocol.ReadAtResponse) error { + fd := req.FD + if fd < 0 || len(s.files) <= fd || s.files[fd] == nil { + return fmt.Errorf("ReadAt: bad file descriptor %d", fd) + } + f := s.files[fd] + buf := make([]byte, req.Len) // TODO: Don't allocate every time + n, err := f.f.ReadAt(buf, req.Offset) + resp.Data = buf[:n] + return err +} + +func (s *Server) Close(req *protocol.CloseRequest, resp *protocol.CloseResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleClose(req *protocol.CloseRequest, resp *protocol.CloseResponse) error { + fd := req.FD + if fd < 0 || fd >= len(s.files) || s.files[fd] == nil { + return fmt.Errorf("Close: bad file descriptor %d", fd) + } + err := s.files[fd].f.Close() + // Remove it regardless + s.files[fd] = nil + return err +} + +func (s *Server) Run(req *protocol.RunRequest, resp *protocol.RunResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleRun(req *protocol.RunRequest, resp *protocol.RunResponse) error { + if s.proc != nil { + s.proc.Kill() + s.proc = nil + s.procIsUp = false + s.stoppedPid = 0 + s.stoppedRegs = syscall.PtraceRegs{} + s.topOfStackAddrs = nil + } + argv := append([]string{s.executable}, req.Args...) + p, err := s.startProcess(s.executable, argv, &os.ProcAttr{ + Files: []*os.File{ + nil, // TODO: be able to feed the target's stdin. + os.Stderr, // TODO: be able to capture the target's stdout. + os.Stderr, + }, + Sys: &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGKILL, + Ptrace: true, + }, + }) + if err != nil { + return err + } + s.proc = p + s.stoppedPid = p.Pid + return nil +} + +func (s *Server) Resume(req *protocol.ResumeRequest, resp *protocol.ResumeResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleResume(req *protocol.ResumeRequest, resp *protocol.ResumeResponse) error { + if s.proc == nil { + return fmt.Errorf("Resume: Run did not successfully start a process") + } + + if !s.procIsUp { + s.procIsUp = true + if _, err := s.waitForTrap(s.stoppedPid, false); err != nil { + return err + } + if err := s.ptraceSetOptions(s.stoppedPid, syscall.PTRACE_O_TRACECLONE); err != nil { + return fmt.Errorf("ptraceSetOptions: %v", err) + } + } else if _, ok := s.breakpoints[s.stoppedRegs.Rip]; ok { + if err := s.ptraceSingleStep(s.stoppedPid); err != nil { + return fmt.Errorf("ptraceSingleStep: %v", err) + } + if _, err := s.waitForTrap(s.stoppedPid, false); err != nil { + return err + } + } + + for { + if err := s.setBreakpoints(); err != nil { + return err + } + if err := s.ptraceCont(s.stoppedPid, 0); err != nil { + return fmt.Errorf("ptraceCont: %v", err) + } + + wpid, err := s.waitForTrap(-1, true) + if err == nil { + s.stoppedPid = wpid + break + } + bce, ok := err.(*breakpointsChangedError) + if !ok { + return err + } + + if err := syscall.Kill(s.stoppedPid, syscall.SIGSTOP); err != nil { + return fmt.Errorf("kill(SIGSTOP): %v", err) + } + _, status, err := s.wait(s.stoppedPid, false) + if err != nil { + return fmt.Errorf("wait (after SIGSTOP): %v", err) + } + if !status.Stopped() || status.StopSignal() != syscall.SIGSTOP { + return fmt.Errorf("wait (after SIGSTOP): unexpected wait status 0x%x", status) + } + + if err := s.liftBreakpoints(); err != nil { + return err + } + + loop: + for c := bce.call; ; { + s.dispatch(c) + select { + case c = <-s.breakpointc: + default: + break loop + } + } + } + if err := s.liftBreakpoints(); err != nil { + return err + } + + if err := s.ptraceGetRegs(s.stoppedPid, &s.stoppedRegs); err != nil { + return fmt.Errorf("ptraceGetRegs: %v", err) + } + + s.stoppedRegs.Rip -= uint64(s.arch.BreakpointSize) + + if err := s.ptraceSetRegs(s.stoppedPid, &s.stoppedRegs); err != nil { + return fmt.Errorf("ptraceSetRegs: %v", err) + } + + resp.Status.PC = s.stoppedRegs.Rip + resp.Status.SP = s.stoppedRegs.Rsp + return nil +} + +func (s *Server) waitForTrap(pid int, allowBreakpointsChange bool) (wpid int, err error) { + for { + wpid, status, err := s.wait(pid, allowBreakpointsChange) + if err != nil { + if _, ok := err.(*breakpointsChangedError); !ok { + err = fmt.Errorf("wait: %v", err) + } + return 0, err + } + if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != syscall.PTRACE_EVENT_CLONE { + return wpid, nil + } + if status.StopSignal() == syscall.SIGPROF { + err = s.ptraceCont(wpid, int(syscall.SIGPROF)) + } else { + err = s.ptraceCont(wpid, 0) // TODO: non-zero when wait catches other signals? + } + if err != nil { + return 0, fmt.Errorf("ptraceCont: %v", err) + } + } +} + +func (s *Server) Breakpoint(req *protocol.BreakpointRequest, resp *protocol.BreakpointResponse) error { + return s.call(s.breakpointc, req, resp) +} + +func (s *Server) handleBreakpoint(req *protocol.BreakpointRequest, resp *protocol.BreakpointResponse) error { + return s.addBreakpoints([]uint64{req.Address}, resp) +} + +func (s *Server) BreakpointAtFunction(req *protocol.BreakpointAtFunctionRequest, resp *protocol.BreakpointResponse) error { + return s.call(s.breakpointc, req, resp) +} + +func (s *Server) handleBreakpointAtFunction(req *protocol.BreakpointAtFunctionRequest, resp *protocol.BreakpointResponse) error { + pc, err := s.functionStartAddress(req.Function) + if err != nil { + return err + } + return s.addBreakpoints([]uint64{pc}, resp) +} + +func (s *Server) BreakpointAtLine(req *protocol.BreakpointAtLineRequest, resp *protocol.BreakpointResponse) error { + return s.call(s.breakpointc, req, resp) +} + +func (s *Server) handleBreakpointAtLine(req *protocol.BreakpointAtLineRequest, resp *protocol.BreakpointResponse) error { + if s.dwarfData == nil { + return fmt.Errorf("no DWARF data") + } + if pcs, err := s.dwarfData.LineToBreakpointPCs(req.File, req.Line); err != nil { + return err + } else { + return s.addBreakpoints(pcs, resp) + } +} + +// addBreakpoints adds breakpoints at the addresses in pcs, then stores pcs in the response. +func (s *Server) addBreakpoints(pcs []uint64, resp *protocol.BreakpointResponse) error { + // Get the original code at each address with ptracePeek. + bps := make([]breakpoint, 0, len(pcs)) + for _, pc := range pcs { + if _, alreadySet := s.breakpoints[pc]; alreadySet { + continue + } + var bp breakpoint + if err := s.ptracePeek(s.stoppedPid, uintptr(pc), bp.origInstr[:s.arch.BreakpointSize]); err != nil { + return fmt.Errorf("ptracePeek: %v", err) + } + bp.pc = pc + bps = append(bps, bp) + } + // If all the peeks succeeded, update the list of breakpoints. + for _, bp := range bps { + s.breakpoints[bp.pc] = bp + } + resp.PCs = pcs + return nil +} + +func (s *Server) DeleteBreakpoints(req *protocol.DeleteBreakpointsRequest, resp *protocol.DeleteBreakpointsResponse) error { + return s.call(s.breakpointc, req, resp) +} + +func (s *Server) handleDeleteBreakpoints(req *protocol.DeleteBreakpointsRequest, resp *protocol.DeleteBreakpointsResponse) error { + for _, pc := range req.PCs { + delete(s.breakpoints, pc) + } + return nil +} + +func (s *Server) setBreakpoints() error { + for pc := range s.breakpoints { + err := s.ptracePoke(s.stoppedPid, uintptr(pc), s.arch.BreakpointInstr[:s.arch.BreakpointSize]) + if err != nil { + return fmt.Errorf("setBreakpoints: %v", err) + } + } + return nil +} + +func (s *Server) liftBreakpoints() error { + for pc, breakpoint := range s.breakpoints { + err := s.ptracePoke(s.stoppedPid, uintptr(pc), breakpoint.origInstr[:s.arch.BreakpointSize]) + if err != nil { + return fmt.Errorf("liftBreakpoints: %v", err) + } + } + return nil +} + +func (s *Server) Eval(req *protocol.EvalRequest, resp *protocol.EvalResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleEval(req *protocol.EvalRequest, resp *protocol.EvalResponse) (err error) { + resp.Result, err = s.eval(req.Expr) + return err +} + +// eval evaluates an expression. +// TODO: very weak. +func (s *Server) eval(expr string) ([]string, error) { + switch { + case strings.HasPrefix(expr, "re:"): + // Regular expression. Return list of symbols. + re, err := regexp.Compile(expr[3:]) + if err != nil { + return nil, err + } + return s.dwarfData.LookupMatchingSymbols(re) + + case strings.HasPrefix(expr, "addr:"): + // Symbol lookup. Return address. + addr, err := s.functionStartAddress(expr[5:]) + if err != nil { + return nil, err + } + return []string{fmt.Sprintf("%#x", addr)}, nil + + case strings.HasPrefix(expr, "val:"): + // Symbol lookup. Return formatted value. + value, err := s.printer.Sprint(expr[4:]) + if err != nil { + return nil, err + } + return []string{value}, nil + + case strings.HasPrefix(expr, "src:"): + // Numerical address. Return file.go:123. + addr, err := strconv.ParseUint(expr[4:], 0, 0) + if err != nil { + return nil, err + } + file, line, err := s.lookupSource(addr) + if err != nil { + return nil, err + } + return []string{fmt.Sprintf("%s:%d", file, line)}, nil + + case len(expr) > 0 && '0' <= expr[0] && expr[0] <= '9': + // Numerical address. Return symbol. + addr, err := strconv.ParseUint(expr, 0, 0) + if err != nil { + return nil, err + } + entry, _, err := s.dwarfData.PCToFunction(addr) + if err != nil { + return nil, err + } + name, ok := entry.Val(dwarf.AttrName).(string) + if !ok { + return nil, fmt.Errorf("function at 0x%x has no name", addr) + } + return []string{name}, nil + } + + return nil, fmt.Errorf("bad expression syntax: %q", expr) +} + +func (s *Server) Evaluate(req *protocol.EvaluateRequest, resp *protocol.EvaluateResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleEvaluate(req *protocol.EvaluateRequest, resp *protocol.EvaluateResponse) (err error) { + resp.Result, err = s.evalExpression(req.Expression, s.stoppedRegs.Rip, s.stoppedRegs.Rsp) + return err +} + +func (s *Server) lookupSource(pc uint64) (file string, line uint64, err error) { + if s.dwarfData == nil { + return + } + // TODO: The gosym equivalent also returns the relevant Func. Do that when + // DWARF has the same facility. + return s.dwarfData.PCToLine(pc) +} + +func (s *Server) Frames(req *protocol.FramesRequest, resp *protocol.FramesResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleFrames(req *protocol.FramesRequest, resp *protocol.FramesResponse) error { + // TODO: verify that we're stopped. + if s.topOfStackAddrs == nil { + if err := s.evaluateTopOfStackAddrs(); err != nil { + return err + } + } + + regs := syscall.PtraceRegs{} + err := s.ptraceGetRegs(s.stoppedPid, ®s) + if err != nil { + return err + } + resp.Frames, err = s.walkStack(regs.Rip, regs.Rsp, req.Count) + return err +} + +// walkStack returns up to the requested number of stack frames. +func (s *Server) walkStack(pc, sp uint64, count int) ([]debug.Frame, error) { + var frames []debug.Frame + + var buf [8]byte + b := new(bytes.Buffer) + r := s.dwarfData.Reader() + + // TODO: handle walking over a split stack. + for i := 0; i < count; i++ { + b.Reset() + file, line, err := s.dwarfData.PCToLine(pc) + if err != nil { + return frames, err + } + fpOffset, err := s.dwarfData.PCToSPOffset(pc) + if err != nil { + return frames, err + } + fp := sp + uint64(fpOffset) + entry, funcEntry, err := s.dwarfData.PCToFunction(pc) + if err != nil { + return frames, err + } + frame := debug.Frame{ + PC: pc, + SP: sp, + File: file, + Line: line, + FunctionStart: funcEntry, + } + frame.Function, _ = entry.Val(dwarf.AttrName).(string) + r.Seek(entry.Offset) + for { + entry, err := r.Next() + if err != nil { + return frames, err + } + if entry.Tag == 0 { + break + } + // TODO: report variables we couldn't parse? + if entry.Tag == dwarf.TagFormalParameter { + if v, err := s.parseParameterOrLocal(entry, fp); err == nil { + frame.Params = append(frame.Params, debug.Param(v)) + } + } + if entry.Tag == dwarf.TagVariable { + if v, err := s.parseParameterOrLocal(entry, fp); err == nil { + frame.Vars = append(frame.Vars, v) + } + } + } + frames = append(frames, frame) + + // Walk to the caller's PC and SP. + if s.topOfStack(funcEntry) { + break + } + err = s.ptracePeek(s.stoppedPid, uintptr(fp-uint64(s.arch.PointerSize)), buf[:s.arch.PointerSize]) + if err != nil { + return frames, fmt.Errorf("ptracePeek: %v", err) + } + pc, sp = s.arch.Uintptr(buf[:s.arch.PointerSize]), fp + } + return frames, nil +} + +// parseParameterOrLocal parses the entry for a function parameter or local +// variable, which are both specified the same way. fp contains the frame +// pointer, which is used to calculate the variable location. +func (s *Server) parseParameterOrLocal(entry *dwarf.Entry, fp uint64) (debug.LocalVar, error) { + var v debug.LocalVar + v.Name, _ = entry.Val(dwarf.AttrName).(string) + if off, err := s.dwarfData.EntryTypeOffset(entry); err != nil { + return v, err + } else { + v.Var.TypeID = uint64(off) + } + if i := entry.Val(dwarf.AttrLocation); i == nil { + return v, fmt.Errorf("missing location description") + } else if locationDescription, ok := i.([]uint8); !ok { + return v, fmt.Errorf("unsupported location description") + } else if offset, err := evalLocation(locationDescription); err != nil { + return v, err + } else { + v.Var.Address = fp + uint64(offset) + } + return v, nil +} + +func (s *Server) evaluateTopOfStackAddrs() error { + var ( + lookup func(name string) (uint64, error) + indirect bool + names []string + ) + if _, err := s.dwarfData.LookupVariable("runtime.rt0_goPC"); err != nil { + // Look for a Go 1.3 binary (or earlier version). + lookup, indirect, names = s.functionStartAddress, false, []string{ + "runtime.goexit", + "runtime.mstart", + "runtime.mcall", + "runtime.morestack", + "runtime.lessstack", + "_rt0_go", + } + } else { + // Look for a Go 1.4 binary (or later version). + lookup = func(name string) (uint64, error) { + entry, err := s.dwarfData.LookupVariable(name) + if err != nil { + return 0, err + } + return s.dwarfData.EntryLocation(entry) + } + indirect, names = true, []string{ + "runtime.goexitPC", + "runtime.mstartPC", + "runtime.mcallPC", + "runtime.morestackPC", + "runtime.rt0_goPC", + } + } + // TODO: also look for runtime.externalthreadhandlerp, on Windows. + + addrs := make([]uint64, 0, len(names)) + for _, name := range names { + addr, err := lookup(name) + if err != nil { + return err + } + addrs = append(addrs, addr) + } + + if indirect { + buf := make([]byte, s.arch.PointerSize) + for i, addr := range addrs { + if err := s.ptracePeek(s.stoppedPid, uintptr(addr), buf); err != nil { + return fmt.Errorf("ptracePeek: %v", err) + } + addrs[i] = s.arch.Uintptr(buf) + } + } + + s.topOfStackAddrs = addrs + return nil +} + +// topOfStack is the out-of-process equivalent of runtime·topofstack. +func (s *Server) topOfStack(funcEntry uint64) bool { + for _, addr := range s.topOfStackAddrs { + if addr == funcEntry { + return true + } + } + return false +} + +func (s *Server) VarByName(req *protocol.VarByNameRequest, resp *protocol.VarByNameResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleVarByName(req *protocol.VarByNameRequest, resp *protocol.VarByNameResponse) error { + entry, err := s.dwarfData.LookupVariable(req.Name) + if err != nil { + return fmt.Errorf("variable %s: %s", req.Name, err) + } + + loc, err := s.dwarfData.EntryLocation(entry) + if err != nil { + return fmt.Errorf("variable %s: %s", req.Name, err) + } + + off, err := s.dwarfData.EntryTypeOffset(entry) + if err != nil { + return fmt.Errorf("variable %s: %s", req.Name, err) + } + + resp.Var.TypeID = uint64(off) + resp.Var.Address = loc + return nil +} + +func (s *Server) Value(req *protocol.ValueRequest, resp *protocol.ValueResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleValue(req *protocol.ValueRequest, resp *protocol.ValueResponse) error { + t, err := s.dwarfData.Type(dwarf.Offset(req.Var.TypeID)) + if err != nil { + return err + } + resp.Value, err = s.value(t, req.Var.Address) + return err +} + +func (s *Server) MapElement(req *protocol.MapElementRequest, resp *protocol.MapElementResponse) error { + return s.call(s.otherc, req, resp) +} + +func (s *Server) handleMapElement(req *protocol.MapElementRequest, resp *protocol.MapElementResponse) error { + t, err := s.dwarfData.Type(dwarf.Offset(req.Map.TypeID)) + if err != nil { + return err + } + m, ok := t.(*dwarf.MapType) + if !ok { + return fmt.Errorf("variable is not a map") + } + var count uint64 + // fn will be called for each element of the map. + // When we reach the requested element, we fill in *resp and stop. + // TODO: cache locations of elements. + fn := func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool { + count++ + if count == req.Index+1 { + resp.Key = debug.Var{TypeID: uint64(keyType.Common().Offset), Address: keyAddr} + resp.Value = debug.Var{TypeID: uint64(valType.Common().Offset), Address: valAddr} + return false + } + return true + } + if err := s.peekMapValues(m, req.Map.Address, fn); err != nil { + return err + } + if count <= req.Index { + // There weren't enough elements. + return fmt.Errorf("map has no element %d", req.Index) + } + return nil +} + +func (s *Server) Goroutines(req *protocol.GoroutinesRequest, resp *protocol.GoroutinesResponse) error { + return s.call(s.otherc, req, resp) +} + +const invalidStatus debug.GoroutineStatus = 99 + +var ( + gStatus = [...]debug.GoroutineStatus{ + 0: debug.Queued, // _Gidle + 1: debug.Queued, // _Grunnable + 2: debug.Running, // _Grunning + 3: debug.Blocked, // _Gsyscall + 4: debug.Blocked, // _Gwaiting + 5: invalidStatus, // _Gmoribund_unused + 6: invalidStatus, // _Gdead + 7: invalidStatus, // _Genqueue + 8: debug.Running, // _Gcopystack + } + gScanStatus = [...]debug.GoroutineStatus{ + 0: invalidStatus, // _Gscan + _Gidle + 1: debug.Queued, // _Gscanrunnable + 2: debug.Running, // _Gscanrunning + 3: debug.Blocked, // _Gscansyscall + 4: debug.Blocked, // _Gscanwaiting + 5: invalidStatus, // _Gscan + _Gmoribund_unused + 6: invalidStatus, // _Gscan + _Gdead + 7: debug.Queued, // _Gscanenqueue + } + gStatusString = [...]string{ + 0: "idle", + 1: "runnable", + 2: "running", + 3: "syscall", + 4: "waiting", + 8: "copystack", + } + gScanStatusString = [...]string{ + 1: "scanrunnable", + 2: "scanrunning", + 3: "scansyscall", + 4: "scanwaiting", + 7: "scanenqueue", + } +) + +func (s *Server) handleGoroutines(req *protocol.GoroutinesRequest, resp *protocol.GoroutinesResponse) error { + // Get DWARF type information for runtime.g. + ge, err := s.dwarfData.LookupEntry("runtime.g") + if err != nil { + return err + } + t, err := s.dwarfData.Type(ge.Offset) + if err != nil { + return err + } + gType, ok := followTypedefs(t).(*dwarf.StructType) + if !ok { + return errors.New("runtime.g is not a struct") + } + + var ( + allgPtr, allgLen uint64 + allgPtrOk bool + ) + for { + // Try to read the slice runtime.allgs. + allgsEntry, err := s.dwarfData.LookupVariable("runtime.allgs") + if err != nil { + break + } + allgsAddr, err := s.dwarfData.EntryLocation(allgsEntry) + if err != nil { + break + } + off, err := s.dwarfData.EntryTypeOffset(allgsEntry) + if err != nil { + break + } + t, err := s.dwarfData.Type(off) + if err != nil { + break + } + allgsType, ok := followTypedefs(t).(*dwarf.SliceType) + if !ok { + break + } + allgs, err := s.peekSlice(allgsType, allgsAddr) + if err != nil { + break + } + + allgPtr, allgLen, allgPtrOk = allgs.Address, allgs.Length, true + break + } + if !allgPtrOk { + // Read runtime.allg. + allgEntry, err := s.dwarfData.LookupVariable("runtime.allg") + if err != nil { + return err + } + allgAddr, err := s.dwarfData.EntryLocation(allgEntry) + if err != nil { + return err + } + allgPtr, err = s.peekPtr(allgAddr) + if err != nil { + return fmt.Errorf("reading allg: %v", err) + } + + // Read runtime.allglen. + allglenEntry, err := s.dwarfData.LookupVariable("runtime.allglen") + if err != nil { + return err + } + off, err := s.dwarfData.EntryTypeOffset(allglenEntry) + if err != nil { + return err + } + allglenType, err := s.dwarfData.Type(off) + if err != nil { + return err + } + allglenAddr, err := s.dwarfData.EntryLocation(allglenEntry) + if err != nil { + return err + } + switch followTypedefs(allglenType).(type) { + case *dwarf.UintType, *dwarf.IntType: + allgLen, err = s.peekUint(allglenAddr, allglenType.Common().ByteSize) + if err != nil { + return fmt.Errorf("reading allglen: %v", err) + } + default: + // Some runtimes don't specify the type for allglen. Assume it's uint32. + allgLen, err = s.peekUint(allglenAddr, 4) + if err != nil { + return fmt.Errorf("reading allglen: %v", err) + } + if allgLen != 0 { + break + } + // Zero? Let's try uint64. + allgLen, err = s.peekUint(allglenAddr, 8) + if err != nil { + return fmt.Errorf("reading allglen: %v", err) + } + } + } + + // Initialize s.goroutineStack. + s.goroutineStackOnce.Do(func() { s.goroutineStackInit(gType) }) + + for i := uint64(0); i < allgLen; i++ { + // allg is an array of pointers to g structs. Read allg[i]. + g, err := s.peekPtr(allgPtr + i*uint64(s.arch.PointerSize)) + if err != nil { + return err + } + gr := debug.Goroutine{} + + // Read status from the field named "atomicstatus" or "status". + status, err := s.peekUintStructField(gType, g, "atomicstatus") + if err != nil { + status, err = s.peekUintOrIntStructField(gType, g, "status") + } + if err != nil { + return err + } + if status == 6 { + // _Gdead. + continue + } + gr.Status = invalidStatus + if status < uint64(len(gStatus)) { + gr.Status = gStatus[status] + gr.StatusString = gStatusString[status] + } else if status^0x1000 < uint64(len(gScanStatus)) { + gr.Status = gScanStatus[status^0x1000] + gr.StatusString = gScanStatusString[status^0x1000] + } + if gr.Status == invalidStatus { + return fmt.Errorf("unexpected goroutine status 0x%x", status) + } + if status == 4 || status == 0x1004 { + // _Gwaiting or _Gscanwaiting. + // Try reading waitreason to get a better value for StatusString. + // Depending on the runtime, waitreason may be a Go string or a C string. + if waitreason, err := s.peekStringStructField(gType, g, "waitreason", 80); err == nil { + if waitreason != "" { + gr.StatusString = waitreason + } + } else if ptr, err := s.peekPtrStructField(gType, g, "waitreason"); err == nil { + waitreason := s.peekCString(ptr, 80) + if waitreason != "" { + gr.StatusString = waitreason + } + } + } + + gr.ID, err = s.peekIntStructField(gType, g, "goid") + if err != nil { + return err + } + + // Best-effort attempt to get the names of the goroutine function and the + // function that created the goroutine. They aren't always available. + functionName := func(pc uint64) string { + entry, _, err := s.dwarfData.PCToFunction(pc) + if err != nil { + return "" + } + name, _ := entry.Val(dwarf.AttrName).(string) + return name + } + if startpc, err := s.peekUintStructField(gType, g, "startpc"); err == nil { + gr.Function = functionName(startpc) + } + if gopc, err := s.peekUintStructField(gType, g, "gopc"); err == nil { + gr.Caller = functionName(gopc) + } + if gr.Status != debug.Running { + // TODO: running goroutines too. + gr.StackFrames, _ = s.goroutineStack(g) + } + + resp.Goroutines = append(resp.Goroutines, &gr) + } + + return nil +} + +// TODO: let users specify how many frames they want. 10 will be enough to +// determine the reason a goroutine is blocked. +const goroutineStackFrameCount = 10 + +// goroutineStackInit initializes s.goroutineStack. +func (s *Server) goroutineStackInit(gType *dwarf.StructType) { + // If we fail to read the DWARF data needed for s.goroutineStack, calling it + // will always return the error that occurred during initialization. + var err error // err is captured by the func below. + s.goroutineStack = func(gAddr uint64) ([]debug.Frame, error) { + return nil, err + } + + // Get g field "sched", which contains fields pc and sp. + schedField, err := getField(gType, "sched") + if err != nil { + return + } + schedOffset := uint64(schedField.ByteOffset) + schedType, ok := followTypedefs(schedField.Type).(*dwarf.StructType) + if !ok { + err = errors.New(`g field "sched" has the wrong type`) + return + } + + // Get the size of the pc and sp fields and their offsets inside the g struct, + // so we can quickly peek those values for each goroutine later. + var ( + schedPCOffset, schedSPOffset uint64 + schedPCByteSize, schedSPByteSize int64 + ) + for _, x := range []struct { + field string + offset *uint64 + bytesize *int64 + }{ + {"pc", &schedPCOffset, &schedPCByteSize}, + {"sp", &schedSPOffset, &schedSPByteSize}, + } { + var f *dwarf.StructField + f, err = getField(schedType, x.field) + if err != nil { + return + } + *x.offset = schedOffset + uint64(f.ByteOffset) + switch t := followTypedefs(f.Type).(type) { + case *dwarf.UintType, *dwarf.IntType: + *x.bytesize = t.Common().ByteSize + default: + err = fmt.Errorf("gobuf field %q has the wrong type", x.field) + return + } + } + + s.goroutineStack = func(gAddr uint64) ([]debug.Frame, error) { + schedPC, err := s.peekUint(gAddr+schedPCOffset, schedPCByteSize) + if err != nil { + return nil, err + } + schedSP, err := s.peekUint(gAddr+schedSPOffset, schedSPByteSize) + if err != nil { + return nil, err + } + return s.walkStack(schedPC, schedSP, goroutineStackFrameCount) + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/value.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/value.go new file mode 100644 index 0000000000..324b244356 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/value.go @@ -0,0 +1,260 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 + +package server + +import ( + "fmt" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" +) + +// value peeks the program's memory at the given address, parsing it as a value of type t. +func (s *Server) value(t dwarf.Type, addr uint64) (debug.Value, error) { + // readBasic reads the memory for a basic type of size n bytes. + readBasic := func(n int64) ([]byte, error) { + switch n { + case 1, 2, 4, 8, 16: + default: + return nil, fmt.Errorf("invalid size: %d", n) + } + buf := make([]byte, n) + if err := s.peek(uintptr(addr), buf); err != nil { + return nil, err + } + return buf, nil + } + + switch t := t.(type) { + case *dwarf.CharType, *dwarf.IntType: + bs := t.Common().ByteSize + buf, err := readBasic(bs) + if err != nil { + return nil, fmt.Errorf("reading integer: %s", err) + } + x := s.arch.IntN(buf) + switch bs { + case 1: + return int8(x), nil + case 2: + return int16(x), nil + case 4: + return int32(x), nil + case 8: + return int64(x), nil + default: + return nil, fmt.Errorf("invalid integer size: %d", bs) + } + case *dwarf.UcharType, *dwarf.UintType, *dwarf.AddrType: + bs := t.Common().ByteSize + buf, err := readBasic(bs) + if err != nil { + return nil, fmt.Errorf("reading unsigned integer: %s", err) + } + x := s.arch.UintN(buf) + switch bs { + case 1: + return uint8(x), nil + case 2: + return uint16(x), nil + case 4: + return uint32(x), nil + case 8: + return uint64(x), nil + default: + return nil, fmt.Errorf("invalid unsigned integer size: %d", bs) + } + case *dwarf.BoolType: + bs := t.Common().ByteSize + buf, err := readBasic(bs) + if err != nil { + return nil, fmt.Errorf("reading boolean: %s", err) + } + for _, b := range buf { + if b != 0 { + return true, nil + } + } + return false, nil + case *dwarf.FloatType: + bs := t.Common().ByteSize + buf, err := readBasic(bs) + if err != nil { + return nil, fmt.Errorf("reading float: %s", err) + } + switch bs { + case 4: + return s.arch.Float32(buf), nil + case 8: + return s.arch.Float64(buf), nil + default: + return nil, fmt.Errorf("invalid float size: %d", bs) + } + case *dwarf.ComplexType: + bs := t.Common().ByteSize + buf, err := readBasic(bs) + if err != nil { + return nil, fmt.Errorf("reading complex: %s", err) + } + switch bs { + case 8: + return s.arch.Complex64(buf), nil + case 16: + return s.arch.Complex128(buf), nil + default: + return nil, fmt.Errorf("invalid complex size: %d", bs) + } + case *dwarf.PtrType: + bs := t.Common().ByteSize + if bs != int64(s.arch.PointerSize) { + return nil, fmt.Errorf("invalid pointer size: %d", bs) + } + buf, err := readBasic(bs) + if err != nil { + return nil, fmt.Errorf("reading pointer: %s", err) + } + return debug.Pointer{ + TypeID: uint64(t.Type.Common().Offset), + Address: uint64(s.arch.Uintptr(buf)), + }, nil + case *dwarf.SliceType: + if s, err := s.peekSlice(t, addr); err != nil { + return nil, err + } else { + return s, nil + } + case *dwarf.ArrayType: + length := t.Count + stride := t.StrideBitSize + if stride%8 != 0 { + return nil, fmt.Errorf("array is not byte-aligned") + } + return debug.Array{ + ElementTypeID: uint64(t.Type.Common().Offset), + Address: uint64(addr), + Length: uint64(length), + StrideBits: uint64(stride), + }, nil + case *dwarf.StructType: + fields := make([]debug.StructField, len(t.Field)) + for i, field := range t.Field { + fields[i] = debug.StructField{ + Name: field.Name, + Var: debug.Var{ + TypeID: uint64(field.Type.Common().Offset), + Address: uint64(addr) + uint64(field.ByteOffset), + }, + } + } + return debug.Struct{fields}, nil + case *dwarf.TypedefType: + return s.value(t.Type, addr) + case *dwarf.MapType: + length, err := s.peekMapLength(t, addr) + if err != nil { + return nil, err + } + return debug.Map{ + TypeID: uint64(t.Common().Offset), + Address: addr, + Length: length, + }, nil + case *dwarf.StringType: + ptr, err := s.peekPtrStructField(&t.StructType, addr, "str") + if err != nil { + return nil, fmt.Errorf("reading string location: %s", err) + } + length, err := s.peekUintOrIntStructField(&t.StructType, addr, "len") + if err != nil { + return nil, fmt.Errorf("reading string length: %s", err) + } + + const maxStringSize = 256 + + n := length + if n > maxStringSize { + n = maxStringSize + } + tmp := make([]byte, n) + if err := s.peekBytes(ptr, tmp); err != nil { + return nil, fmt.Errorf("reading string contents: %s", err) + } + return debug.String{Length: length, String: string(tmp)}, nil + case *dwarf.ChanType: + pt, ok := t.TypedefType.Type.(*dwarf.PtrType) + if !ok { + return nil, fmt.Errorf("reading channel: type is not a pointer") + } + st, ok := pt.Type.(*dwarf.StructType) + if !ok { + return nil, fmt.Errorf("reading channel: type is not a pointer to struct") + } + + a, err := s.peekPtr(addr) + if err != nil { + return nil, fmt.Errorf("reading channel pointer: %s", err) + } + if a == 0 { + // This channel is nil. + return debug.Channel{ + ElementTypeID: uint64(t.ElemType.Common().Offset), + Address: 0, + Buffer: 0, + Length: 0, + Capacity: 0, + Stride: uint64(t.ElemType.Common().ByteSize), + BufferStart: 0, + }, nil + } + + buf, err := s.peekPtrStructField(st, a, "buf") + if err != nil { + return nil, fmt.Errorf("reading channel buffer location: %s", err) + } + qcount, err := s.peekUintOrIntStructField(st, a, "qcount") + if err != nil { + return nil, fmt.Errorf("reading channel length: %s", err) + } + capacity, err := s.peekUintOrIntStructField(st, a, "dataqsiz") + if err != nil { + return nil, fmt.Errorf("reading channel capacity: %s", err) + } + recvx, err := s.peekUintOrIntStructField(st, a, "recvx") + if err != nil { + return nil, fmt.Errorf("reading channel buffer index: %s", err) + } + return debug.Channel{ + ElementTypeID: uint64(t.ElemType.Common().Offset), + Address: a, + Buffer: buf, + Length: qcount, + Capacity: capacity, + Stride: uint64(t.ElemType.Common().ByteSize), + BufferStart: recvx, + }, nil + case *dwarf.FuncType: + a, err := s.peekPtr(addr) + if err != nil { + return nil, fmt.Errorf("reading func: %s", err) + } + return debug.Func{Address: a}, nil + case *dwarf.InterfaceType: + return debug.Interface{}, nil + // TODO: more types + } + return nil, fmt.Errorf("Unsupported type %T", t) +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/doc.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/doc.go new file mode 100644 index 0000000000..4257227654 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/doc.go @@ -0,0 +1,15 @@ +// 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. + +package peek diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/peek_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/peek_test.go new file mode 100644 index 0000000000..e3daecb280 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/peek_test.go @@ -0,0 +1,1074 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 + +package peek_test + +import ( + "flag" + "fmt" + "log" + "os" + "os/exec" + "reflect" + "regexp" + "sync" + "testing" + + "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/debug/remote" +) + +var expectedVarValues = map[string]interface{}{ + `main.Z_bool_false`: false, + `main.Z_bool_true`: true, + `main.Z_complex128`: complex128(1.987654321 - 2.987654321i), + `main.Z_complex64`: complex64(1.54321 + 2.54321i), + `main.Z_float32`: float32(1.54321), + `main.Z_float64`: float64(1.987654321), + `main.Z_int16`: int16(-32321), + `main.Z_int32`: int32(-1987654321), + `main.Z_int64`: int64(-9012345678987654321), + `main.Z_int8`: int8(-121), + `main.Z_uint16`: uint16(54321), + `main.Z_uint32`: uint32(3217654321), + `main.Z_uint64`: uint64(12345678900987654321), + `main.Z_uint8`: uint8(231), +} + +// TODO: the string forms of some types we're testing aren't stable +var expectedVars = map[string]string{ + `main.Z_array`: `[5]int8{-121, 121, 3, 2, 1}`, + `main.Z_array_empty`: `[0]int8{}`, + `main.Z_bool_false`: `false`, + `main.Z_bool_true`: `true`, + `main.Z_channel`: `(chan int16 0xX)`, + `main.Z_channel_2`: `(chan int16 0xX)`, + `main.Z_channel_buffered`: `(chan int16 0xX [6/10])`, + `main.Z_channel_nil`: `(chan int16 )`, + `main.Z_array_of_empties`: `[2]{}{{} {}, ({} 0xX)}`, + `main.Z_complex128`: `(1.987654321-2.987654321i)`, + `main.Z_complex64`: `(1.54321+2.54321i)`, + `main.Z_float32`: `1.54321`, + `main.Z_float64`: `1.987654321`, + `main.Z_func_int8_r_int8`: `func(int8, *int8) void @0xX `, + `main.Z_func_int8_r_pint8`: `func(int8, **int8) void @0xX `, + `main.Z_func_bar`: `func(*main.FooStruct) void @0xX `, + `main.Z_func_nil`: `func(int8, *int8) void @0xX `, + `main.Z_int`: `-21`, + `main.Z_int16`: `-32321`, + `main.Z_int32`: `-1987654321`, + `main.Z_int64`: `-9012345678987654321`, + `main.Z_int8`: `-121`, + `main.Z_int_typedef`: `88`, + `main.Z_interface`: `("*main.FooStruct", 0xX)`, + `main.Z_interface_nil`: `(, )`, + `main.Z_interface_typed_nil`: `("*main.FooStruct", )`, + `main.Z_map`: `map[-21:3.54321]`, + `main.Z_map_2`: `map[1024:1]`, + `main.Z_map_3`: `map[1024:1 512:-1]`, + `main.Z_map_empty`: `map[]`, + `main.Z_map_nil`: `map[]`, + `main.Z_pointer`: `0xX`, + `main.Z_pointer_nil`: `0x0`, + `main.Z_slice`: `[]uint8{115, 108, 105, 99, 101}`, + `main.Z_slice_2`: `[]int8{-121, 121}`, + `main.Z_slice_nil`: `[]uint8{}`, + `main.Z_string`: `"I'm a string"`, + `main.Z_struct`: `main.FooStruct {21, "hi"}`, + `main.Z_uint`: `21`, + `main.Z_uint16`: `54321`, + `main.Z_uint32`: `3217654321`, + `main.Z_uint64`: `12345678900987654321`, + `main.Z_uint8`: `231`, + `main.Z_uintptr`: `21`, + `main.Z_unsafe_pointer`: `0xX`, + `main.Z_unsafe_pointer_nil`: `0x0`, +} + +// expectedEvaluate contains expected results of the debug.Evaluate function. +// A nil value indicates that an error is expected. +var expectedEvaluate = map[string]debug.Value{ + `x`: int16(42), + `local_array`: debug.Array{42, 42, 5, 8}, + `local_channel`: debug.Channel{42, 42, 42, 0, 0, 2, 0}, + `local_channel_buffered`: debug.Channel{42, 42, 42, 6, 10, 2, 8}, + `local_map`: debug.Map{42, 42, 1}, + `local_map_2`: debug.Map{42, 42, 1}, + `local_map_3`: debug.Map{42, 42, 2}, + `local_map_empty`: debug.Map{42, 42, 0}, + `x + 5`: int16(47), + `x - 5`: int16(37), + `x / 5`: int16(8), + `x % 5`: int16(2), + `x & 2`: int16(2), + `x | 1`: int16(43), + `x ^ 3`: int16(41), + `5 + x`: int16(47), + `5 - x`: int16(-37), + `100 / x`: int16(2), + `100 % x`: int16(16), + `2 & x`: int16(2), + `1 | x`: int16(43), + `3 ^ x`: int16(41), + `12`: 12, + `+42`: 42, + `23i`: 23i, + `34.0`: 34.0, + `34.5`: 34.5, + `1e5`: 100000.0, + `0x42`: 66, + `'c'`: 'c', + `"de"`: debug.String{2, `de`}, + "`ef`": debug.String{2, `ef`}, + `"de" + "fg"`: debug.String{4, `defg`}, + `/* comment */ -5`: -5, + `false`: false, + `true`: true, + `!false`: true, + `!true`: false, + `5 + 5`: 10, + `true || false`: true, + `false || false`: false, + `true && false`: false, + `true && true`: true, + `!(5 > 8)`: true, + `10 + 'a'`: 'k', + `10 + 10.5`: 20.5, + `10 + 10.5i`: 10 + 10.5i, + `'a' + 10.5`: 107.5, + `'a' + 10.5i`: 97 + 10.5i, + `10.5 + 20.5i`: 10.5 + 20.5i, + `10 * 20`: 200, + `10.0 - 20.5`: -10.5, + `(6 + 8i) * 4`: 24 + 32i, + `(6 + 8i) * (1 + 1i)`: -2 + 14i, + `(6 + 8i) * (6 - 8i)`: complex128(100), + `(6 + 8i) / (3 + 4i)`: complex128(2), + `local_array[2]`: int8(3), + `&local_array[1]`: debug.Pointer{42, 42}, + `local_map[-21]`: float32(3.54321), + `local_map[+21]`: float32(0), + `local_map_3[1024]`: int8(1), + `local_map_3[512]`: int8(-1), + `local_map_empty[21]`: float32(0), + `"hello"[2]`: uint8('l'), + `local_array[1:3][1]`: int8(3), + `local_array[0:4][2:3][0]`: int8(3), + `local_array[:]`: debug.Slice{debug.Array{42, 42, 5, 8}, 5}, + `local_array[:2]`: debug.Slice{debug.Array{42, 42, 2, 8}, 5}, + `local_array[2:]`: debug.Slice{debug.Array{42, 42, 3, 8}, 3}, + `local_array[1:3]`: debug.Slice{debug.Array{42, 42, 2, 8}, 4}, + `local_array[:3:4]`: debug.Slice{debug.Array{42, 42, 3, 8}, 4}, + `local_array[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, + `local_array[1:][1:][1:]`: debug.Slice{debug.Array{42, 42, 2, 8}, 2}, + `(&local_array)[:]`: debug.Slice{debug.Array{42, 42, 5, 8}, 5}, + `(&local_array)[:2]`: debug.Slice{debug.Array{42, 42, 2, 8}, 5}, + `(&local_array)[2:]`: debug.Slice{debug.Array{42, 42, 3, 8}, 3}, + `(&local_array)[1:3]`: debug.Slice{debug.Array{42, 42, 2, 8}, 4}, + `(&local_array)[:3:4]`: debug.Slice{debug.Array{42, 42, 3, 8}, 4}, + `(&local_array)[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, + `lookup("main.Z_array")`: debug.Array{42, 42, 5, 8}, + `lookup("main.Z_array_empty")`: debug.Array{42, 42, 0, 8}, + `lookup("main.Z_bool_false")`: false, + `lookup("main.Z_bool_true")`: true, + `lookup("main.Z_channel")`: debug.Channel{42, 42, 42, 0, 0, 2, 0}, + `lookup("main.Z_channel_buffered")`: debug.Channel{42, 42, 42, 6, 10, 2, 8}, + `lookup("main.Z_channel_nil")`: debug.Channel{42, 0, 0, 0, 0, 2, 0}, + `lookup("main.Z_array_of_empties")`: debug.Array{42, 42, 2, 0}, + `lookup("main.Z_complex128")`: complex128(1.987654321 - 2.987654321i), + `lookup("main.Z_complex64")`: complex64(1.54321 + 2.54321i), + `lookup("main.Z_float32")`: float32(1.54321), + `lookup("main.Z_float64")`: float64(1.987654321), + `lookup("main.Z_func_int8_r_int8")`: debug.Func{42}, + `lookup("main.Z_func_int8_r_pint8")`: debug.Func{42}, + `lookup("main.Z_func_bar")`: debug.Func{42}, + `lookup("main.Z_func_nil")`: debug.Func{0}, + `lookup("main.Z_int")`: -21, + `lookup("main.Z_int16")`: int16(-32321), + `lookup("main.Z_int32")`: int32(-1987654321), + `lookup("main.Z_int64")`: int64(-9012345678987654321), + `lookup("main.Z_int8")`: int8(-121), + `lookup("main.Z_int_typedef")`: int16(88), + `lookup("main.Z_interface")`: debug.Interface{}, + `lookup("main.Z_interface_nil")`: debug.Interface{}, + `lookup("main.Z_interface_typed_nil")`: debug.Interface{}, + `lookup("main.Z_map")`: debug.Map{42, 42, 1}, + `lookup("main.Z_map_2")`: debug.Map{42, 42, 1}, + `lookup("main.Z_map_3")`: debug.Map{42, 42, 2}, + `lookup("main.Z_map_empty")`: debug.Map{42, 42, 0}, + `lookup("main.Z_map_nil")`: debug.Map{42, 42, 0}, + `lookup("main.Z_pointer")`: debug.Pointer{42, 42}, + `lookup("main.Z_pointer_nil")`: debug.Pointer{42, 0}, + `lookup("main.Z_slice")`: debug.Slice{debug.Array{42, 42, 5, 8}, 5}, + `lookup("main.Z_slice_2")`: debug.Slice{debug.Array{42, 42, 2, 8}, 5}, + `lookup("main.Z_slice_nil")`: debug.Slice{debug.Array{42, 0, 0, 8}, 0}, + `lookup("main.Z_string")`: debug.String{12, `I'm a string`}, + `lookup("main.Z_struct")`: debug.Struct{[]debug.StructField{{"a", debug.Var{}}, {"b", debug.Var{}}}}, + `lookup("main.Z_uint")`: uint(21), + `lookup("main.Z_uint16")`: uint16(54321), + `lookup("main.Z_uint32")`: uint32(3217654321), + `lookup("main.Z_uint64")`: uint64(12345678900987654321), + `lookup("main.Z_uint8")`: uint8(231), + `lookup("main.Z_uintptr")`: uint(21), + `lookup("main.Z_unsafe_pointer")`: debug.Pointer{0, 42}, + `lookup("main.Z_unsafe_pointer_nil")`: debug.Pointer{0, 0}, + `lookup("main.Z_int") + lookup("main.Z_int")`: -42, + `lookup("main.Z_int16") < 0`: true, + `lookup("main.Z_uint32") + lookup("main.Z_uint32")`: uint32(2140341346), + `lookup("main.Z_bool_true") || lookup("main.Z_bool_false")`: true, + `lookup("main.Z_bool_true") && lookup("main.Z_bool_false")`: false, + `lookup("main.Z_bool_false") || lookup("main.Z_bool_false")`: false, + `!lookup("main.Z_bool_true")`: false, + `!lookup("main.Z_bool_false")`: true, + `lookup("main.Z_array")[2]`: int8(3), + `lookup("main.Z_array")[1:3][1]`: int8(3), + `lookup("main.Z_array")[0:4][2:3][0]`: int8(3), + `lookup("main.Z_array_of_empties")[0]`: debug.Struct{}, + `lookup("main.Z_complex128") * 10.0`: complex128(19.87654321 - 29.87654321i), + `lookup("main.Z_complex64") * 0.1`: complex64(0.154321 + 0.254321i), + `lookup("main.Z_float32") * 10.0`: float32(15.4321), + `lookup("main.Z_float64") * 0.1`: float64(0.1987654321), + `lookup("main.Z_int") + 1`: int(-20), + `lookup("main.Z_int16") - 10`: int16(-32331), + `lookup("main.Z_int32") / 10`: int32(-198765432), + `lookup("main.Z_int64") / 10`: int64(-901234567898765432), + `lookup("main.Z_int8") + 10`: int8(-111), + `lookup("main.Z_map")[-21]`: float32(3.54321), + `lookup("main.Z_map")[+21]`: float32(0), + `lookup("main.Z_map_empty")[21]`: float32(0), + `lookup("main.Z_slice")[1]`: uint8(108), + `lookup("main.Z_slice_2")[1]`: int8(121), + `lookup("main.Z_slice")[1:5][0:3][1]`: uint8('i'), + `lookup("main.Z_array")[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, + `(&lookup("main.Z_array"))[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, + `lookup("main.Z_string") + "!"`: debug.String{13, `I'm a string!`}, + `lookup("main.Z_struct").a`: 21, + `(&lookup("main.Z_struct")).a`: 21, + `lookup("main.Z_uint")/10`: uint(2), + `lookup("main.Z_uint16")/10`: uint16(5432), + `lookup("main.Z_uint32")/10`: uint32(321765432), + `lookup("main.Z_uint64")/10`: uint64(1234567890098765432), + `lookup("main.Z_uint8")/10`: uint8(23), + `lookup("main.Z_pointer").a`: 21, + `(*lookup("main.Z_pointer")).a`: 21, + `(&*lookup("main.Z_pointer")).a`: 21, + `lookup("main.Z_pointer").b`: debug.String{2, `hi`}, + `(*lookup("main.Z_pointer")).b`: debug.String{2, `hi`}, + `(&*lookup("main.Z_pointer")).b`: debug.String{2, `hi`}, + `lookup("main.Z_map_nil")[32]`: float32(0), + `&lookup("main.Z_int16")`: debug.Pointer{42, 42}, + `&lookup("main.Z_array")[1]`: debug.Pointer{42, 42}, + `&lookup("main.Z_slice")[1]`: debug.Pointer{42, 42}, + `*&lookup("main.Z_int16")`: int16(-32321), + `*&*&*&*&lookup("main.Z_int16")`: int16(-32321), + `lookup("time.Local")`: debug.Pointer{42, 42}, + `5 + false`: nil, + ``: nil, + `x + ""`: nil, + `x / 0`: nil, + `0 / 0`: nil, + `'a' / ('a'-'a')`: nil, + `0.0 / 0.0`: nil, + `3i / 0.0`: nil, + `x % 0`: nil, + `0 % 0`: nil, + `'a' % ('a'-'a')`: nil, + `local_array[-2] + 1`: nil, + `local_array[22] + 1`: nil, + `local_slice[-2] + 1`: nil, + `local_slice[22] + 1`: nil, + `local_string[-2]`: nil, + `local_string[22]`: nil, + `"hello"[-2]`: nil, + `"hello"[22]`: nil, + `local_pointer_nil.a`: nil, + `(local_struct).c`: nil, + `(&local_struct).c`: nil, + `(*local_pointer).c`: nil, + `lookup("not a real symbol")`: nil, + `lookup("x")`: nil, + `lookup(x)`: nil, + `lookup(42)`: nil, +} + +func isHex(r uint8) bool { + switch { + case '0' <= r && r <= '9': + return true + case 'a' <= r && r <= 'f': + return true + case 'A' <= r && r <= 'F': + return true + default: + return false + } +} + +// structRE is used by matches to remove 'struct ' from type names, which is not +// output by every version of the compiler. +var structRE = regexp.MustCompile("struct *") + +// Check s matches the pattern in p. +// An 'X' in p greedily matches one or more hex characters in s. +func matches(p, s string) bool { + // Remove 'struct' and following spaces from s. + s = structRE.ReplaceAllString(s, "") + j := 0 + for i := 0; i < len(p); i++ { + if j == len(s) { + return false + } + c := p[i] + if c == 'X' { + if !isHex(s[j]) { + return false + } + for j < len(s) && isHex(s[j]) { + j++ + } + continue + } + if c != s[j] { + return false + } + j++ + } + return j == len(s) +} + +const ( + proxySrc = "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/cmd/debugproxy" + traceeSrc = "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/testdata" +) + +var ( + // Locations of the proxy and tracee executables. + proxyBinary = "./debugproxy.out" + traceeBinary = "./tracee.out" + // Onces that ensure initProxy and initTracee are called at most once. + proxyOnce sync.Once + traceeOnce sync.Once + // Flags for setting the location of the proxy and tracee, so they don't need to be built. + proxyFlag = flag.String("proxy", "", "Location of debugproxy. If empty, proxy will be built.") + traceeFlag = flag.String("target", "", "Location of target. If empty, target will be built.") + // Executables this test has built, which will be removed on completion of the tests. + filesToRemove []string +) + +func TestMain(m *testing.M) { + flag.Parse() + x := m.Run() + for _, f := range filesToRemove { + os.Remove(f) + } + os.Exit(x) +} + +func run(name string, args ...string) error { + cmd := exec.Command(name, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +func initProxy() { + if *proxyFlag != "" { + proxyBinary = *proxyFlag + remote.DebugproxyCmd = proxyBinary + return + } + if err := run("go", "build", "-o", proxyBinary, proxySrc); err != nil { + log.Fatalf("couldn't build proxy: %v", err) + } + filesToRemove = append(filesToRemove, proxyBinary) + remote.DebugproxyCmd = proxyBinary +} + +func initTracee() { + if *traceeFlag != "" { + traceeBinary = *traceeFlag + return + } + if err := run("go", "build", "-o", traceeBinary, traceeSrc); err != nil { + log.Fatalf("couldn't build target: %v", err) + } + filesToRemove = append(filesToRemove, traceeBinary) +} + +func TestLocalProgram(t *testing.T) { + t.Skip("Fails looking for runtime.lessstack for some reason") + traceeOnce.Do(initTracee) + prog, err := local.New(traceeBinary) + if err != nil { + t.Fatal("local.New:", err) + } + testProgram(t, prog) +} + +func TestRemoteProgram(t *testing.T) { + t.Skip("Fails looking for runtime.lessstack for some reason") + traceeOnce.Do(initTracee) + proxyOnce.Do(initProxy) + prog, err := remote.New("localhost", traceeBinary) + if err != nil { + t.Fatal("remote.New:", err) + } + testProgram(t, prog) +} + +func testProgram(t *testing.T, prog debug.Program) { + _, err := prog.Run("some", "arguments") + if err != nil { + log.Fatalf("Run: %v", err) + } + + pcs, err := prog.BreakpointAtFunction("main.foo") + if err != nil { + log.Fatalf("BreakpointAtFunction: %v", err) + } + fmt.Printf("breakpoints set at %x\n", pcs) + + _, err = prog.Resume() + if err != nil { + log.Fatalf("Resume: %v", err) + } + + gs, err := prog.Goroutines() + if err != nil { + t.Fatalf("Goroutines(): got error %s", err) + } + for _, g := range gs { + fmt.Println(g) + for _, f := range g.StackFrames { + fmt.Println(f) + } + } + + frames, err := prog.Frames(100) + if err != nil { + log.Fatalf("prog.Frames error: %v", err) + } + fmt.Printf("%#v\n", frames) + if len(frames) == 0 { + t.Fatalf("no stack frames returned") + } + if frames[0].Function != "main.foo" { + t.Errorf("function name: got %s expected main.foo", frames[0].Function) + } + if len(frames[0].Params) != 2 { + t.Errorf("got %d parameters, expected 2", len(frames[0].Params)) + } else { + x := frames[0].Params[0] + y := frames[0].Params[1] + if x.Name != "x" { + x, y = y, x + } + if x.Name != "x" { + t.Errorf("parameter name: got %s expected x", x.Name) + } + if y.Name != "y" { + t.Errorf("parameter name: got %s expected y", y.Name) + } + if val, err := prog.Value(x.Var); err != nil { + t.Errorf("value of x: %s", err) + } else if val != int16(42) { + t.Errorf("value of x: got %T(%v) expected int16(42)", val, val) + } + if val, err := prog.Value(y.Var); err != nil { + t.Errorf("value of y: %s", err) + } else if val != float32(1.5) { + t.Errorf("value of y: got %T(%v) expected float32(1.5)", val, val) + } + } + + varnames, err := prog.Eval(`re:main\.Z_.*`) + if err != nil { + log.Fatalf("prog.Eval error: %v", err) + } + + // Evaluate each of the variables found above, and check they match + // expectedVars. + seen := make(map[string]bool) + for _, v := range varnames { + val, err := prog.Eval("val:" + v) + if err != nil { + log.Fatalf("prog.Eval error for %s: %v", v, err) + } else { + fmt.Printf("%s = %v\n", v, val) + if seen[v] { + log.Fatalf("repeated variable %s\n", v) + } + seen[v] = true + if len(val) != 1 { + log.Fatalf("should be one value for %s\n", v) + } + expected, ok := expectedVars[v] + if !ok { + log.Fatalf("unexpected variable %s\n", v) + } else { + if !matches(expected, val[0]) { + log.Fatalf("expected %s = %s\n", v, expected) + } + } + } + } + for v, e := range expectedVars { + if !seen[v] { + log.Fatalf("didn't get %s = %s\n", v, e) + } + } + + // Remove the breakpoint at main.foo. + err = prog.DeleteBreakpoints(pcs) + if err != nil { + log.Fatalf("DeleteBreakpoints: %v", err) + } + + // Set a breakpoint at line 125, resume, and check we stopped there. + pcsLine125, err := prog.BreakpointAtLine("testdata/main.go", 125) + if err != nil { + t.Fatal("BreakpointAtLine:", err) + } + status, err := prog.Resume() + if err != nil { + log.Fatalf("Resume: %v", err) + } + stoppedAt := func(pcs []uint64) bool { + for _, pc := range pcs { + if status.PC == pc { + return true + } + } + return false + } + if !stoppedAt(pcsLine125) { + t.Errorf("stopped at %X; expected one of %X.", status.PC, pcsLine125) + } + + for k, v := range expectedEvaluate { + val, err := prog.Evaluate(k) + if v == nil { + if err == nil { + t.Errorf("got Evaluate(%s) = %v, expected error", k, val) + } + continue + } + if err != nil { + t.Errorf("Evaluate(%s): got error %s, expected %v", k, err, v) + continue + } + typ := reflect.TypeOf(v) + if typ != reflect.TypeOf(val) && typ != reflect.TypeOf(int(0)) && typ != reflect.TypeOf(uint(0)) { + t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) + continue + } + + // For types with fields like Address, TypeID, etc., we can't know the exact + // value, so we only test whether those fields are zero or not. + switch v := v.(type) { + default: + if v != val { + t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) + } + case debug.Array: + val := val.(debug.Array) + if v.ElementTypeID == 0 && val.ElementTypeID != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val) + } + if v.ElementTypeID != 0 && val.ElementTypeID == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val) + } + if v.Address == 0 && val.Address != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) + } + if v.Address != 0 && val.Address == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) + } + case debug.Slice: + val := val.(debug.Slice) + if v.ElementTypeID == 0 && val.ElementTypeID != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val) + } + if v.ElementTypeID != 0 && val.ElementTypeID == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val) + } + if v.Address == 0 && val.Address != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) + } + if v.Address != 0 && val.Address == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) + } + case debug.Map: + val := val.(debug.Map) + if v.TypeID == 0 && val.TypeID != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero TypeID", k, val) + } + if v.TypeID != 0 && val.TypeID == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero TypeID", k, val) + } + if v.Address == 0 && val.Address != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) + } + if v.Address != 0 && val.Address == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) + } + case debug.Pointer: + val := val.(debug.Pointer) + if v.TypeID == 0 && val.TypeID != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero TypeID", k, val) + } + if v.TypeID != 0 && val.TypeID == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero TypeID", k, val) + } + if v.Address == 0 && val.Address != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) + } + if v.Address != 0 && val.Address == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) + } + case debug.Channel: + val := val.(debug.Channel) + if v.ElementTypeID == 0 && val.ElementTypeID != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val) + } + if v.ElementTypeID != 0 && val.ElementTypeID == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val) + } + if v.Address == 0 && val.Address != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) + } + if v.Address != 0 && val.Address == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) + } + if v.Buffer == 0 && val.Buffer != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero Buffer", k, val) + } + if v.Buffer != 0 && val.Buffer == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero Buffer", k, val) + } + case debug.Struct: + val := val.(debug.Struct) + if len(v.Fields) != len(val.Fields) { + t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) + break + } + for i := range v.Fields { + a := v.Fields[i].Name + b := val.Fields[i].Name + if a != b { + t.Errorf("Evaluate(%s): field name mismatch: %s vs %s", k, a, b) + break + } + } + case debug.Func: + val := val.(debug.Func) + if v.Address == 0 && val.Address != 0 { + t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) + } + if v.Address != 0 && val.Address == 0 { + t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) + } + case int: + // ints in a remote program can be returned as int32 or int64 + switch val := val.(type) { + case int32: + if val != int32(v) { + t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) + } + case int64: + if val != int64(v) { + t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) + } + default: + t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) + } + case uint: + // uints in a remote program can be returned as uint32 or uint64 + switch val := val.(type) { + case uint32: + if val != uint32(v) { + t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) + } + case uint64: + if val != uint64(v) { + t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) + } + default: + t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) + } + } + } + + // Evaluate a struct. + v := `lookup("main.Z_struct")` + val, err := prog.Evaluate(v) + if err != nil { + t.Fatalf("Evaluate: %s", err) + } + s, ok := val.(debug.Struct) + if !ok { + t.Fatalf("got Evaluate(%q) = %T(%v), expected debug.Struct", v, val, val) + } + // Check the values of its fields. + if len(s.Fields) != 2 { + t.Fatalf("got Evaluate(%q) = %+v, expected 2 fields", v, s) + } + if v0, err := prog.Value(s.Fields[0].Var); err != nil { + t.Errorf("Value: %s", err) + } else if v0 != int32(21) && v0 != int64(21) { + t.Errorf("Value: got %T(%v), expected 21", v0, v0) + } + if v1, err := prog.Value(s.Fields[1].Var); err != nil { + t.Errorf("Value: %s", err) + } else if v1 != (debug.String{2, "hi"}) { + t.Errorf("Value: got %T(%v), expected `hi`", v1, v1) + } + + // Remove the breakpoint at line 125, set a breakpoint at main.f1 and main.f2, + // then delete the breakpoint at main.f1. Resume, then check we stopped at + // main.f2. + err = prog.DeleteBreakpoints(pcsLine125) + if err != nil { + log.Fatalf("DeleteBreakpoints: %v", err) + } + pcs1, err := prog.BreakpointAtFunction("main.f1") + if err != nil { + log.Fatalf("BreakpointAtFunction: %v", err) + } + pcs2, err := prog.BreakpointAtFunction("main.f2") + if err != nil { + log.Fatalf("BreakpointAtFunction: %v", err) + } + err = prog.DeleteBreakpoints(pcs1) + if err != nil { + log.Fatalf("DeleteBreakpoints: %v", err) + } + status, err = prog.Resume() + if err != nil { + log.Fatalf("Resume: %v", err) + } + if !stoppedAt(pcs2) { + t.Errorf("stopped at %X; expected one of %X.", status.PC, pcs2) + } + + // Check we get the expected results calling VarByName then Value + // for the variables in expectedVarValues. + for name, exp := range expectedVarValues { + if v, err := prog.VarByName(name); err != nil { + t.Errorf("VarByName(%s): %s", name, err) + } else if val, err := prog.Value(v); err != nil { + t.Errorf("value of %s: %s", name, err) + } else if val != exp { + t.Errorf("value of %s: got %T(%v) want %T(%v)", name, val, val, exp, exp) + } + } + + // Check some error cases for VarByName and Value. + if _, err = prog.VarByName("not a real name"); err == nil { + t.Error("VarByName for invalid name: expected error") + } + if _, err = prog.Value(debug.Var{}); err == nil { + t.Error("value of invalid var: expected error") + } + if v, err := prog.VarByName("main.Z_int16"); err != nil { + t.Error("VarByName(main.Z_int16) error:", err) + } else { + v.Address = 0 + // v now has a valid type but a bad address. + _, err = prog.Value(v) + if err == nil { + t.Error("value of invalid location: expected error") + } + } + + // checkValue tests that we can get a Var for a variable with the given name, + // that we can then get the value of that Var, and that calling fn for that + // value succeeds. + checkValue := func(name string, fn func(val debug.Value) error) { + if v, err := prog.VarByName(name); err != nil { + t.Errorf("VarByName(%s): %s", name, err) + } else if val, err := prog.Value(v); err != nil { + t.Errorf("value of %s: %s", name, err) + } else if err := fn(val); err != nil { + t.Errorf("value of %s: %s", name, err) + } + } + + checkValue("main.Z_uintptr", func(val debug.Value) error { + if val != uint32(21) && val != uint64(21) { + // Z_uintptr should be an unsigned integer with size equal to the debugged + // program's address size. + return fmt.Errorf("got %T(%v) want 21", val, val) + } + return nil + }) + + checkValue("main.Z_int", func(val debug.Value) error { + if val != int32(-21) && val != int64(-21) { + return fmt.Errorf("got %T(%v) want -21", val, val) + } + return nil + }) + + checkValue("main.Z_uint", func(val debug.Value) error { + if val != uint32(21) && val != uint64(21) { + return fmt.Errorf("got %T(%v) want 21", val, val) + } + return nil + }) + + checkValue("main.Z_pointer", func(val debug.Value) error { + if _, ok := val.(debug.Pointer); !ok { + return fmt.Errorf("got %T(%v) expected Pointer", val, val) + } + return nil + }) + + checkValue("main.Z_pointer_nil", func(val debug.Value) error { + if p, ok := val.(debug.Pointer); !ok { + return fmt.Errorf("got %T(%v) expected Pointer", val, val) + } else if p.Address != 0 { + return fmt.Errorf("got %T(%v) expected nil pointer", val, val) + } + return nil + }) + + checkValue("main.Z_array", func(val debug.Value) error { + a, ok := val.(debug.Array) + if !ok { + return fmt.Errorf("got %T(%v) expected Array", val, val) + } + if a.Len() != 5 { + return fmt.Errorf("got array length %d expected 5", a.Len()) + } + expected := [5]int8{-121, 121, 3, 2, 1} + for i := uint64(0); i < 5; i++ { + if v, err := prog.Value(a.Element(i)); err != nil { + return fmt.Errorf("reading element %d: %s", i, err) + } else if v != expected[i] { + return fmt.Errorf("element %d: got %T(%v) want %T(%d)", i, v, v, expected[i], expected[i]) + } + } + return nil + }) + + checkValue("main.Z_slice", func(val debug.Value) error { + s, ok := val.(debug.Slice) + if !ok { + return fmt.Errorf("got %T(%v) expected Slice", val, val) + } + if s.Len() != 5 { + return fmt.Errorf("got slice length %d expected 5", s.Len()) + } + expected := []uint8{115, 108, 105, 99, 101} + for i := uint64(0); i < 5; i++ { + if v, err := prog.Value(s.Element(i)); err != nil { + return fmt.Errorf("reading element %d: %s", i, err) + } else if v != expected[i] { + return fmt.Errorf("element %d: got %T(%v) want %T(%d)", i, v, v, expected[i], expected[i]) + } + } + return nil + }) + + checkValue("main.Z_map_empty", func(val debug.Value) error { + m, ok := val.(debug.Map) + if !ok { + return fmt.Errorf("got %T(%v) expected Map", val, val) + } + if m.Length != 0 { + return fmt.Errorf("got map length %d expected 0", m.Length) + } + return nil + }) + + checkValue("main.Z_map_nil", func(val debug.Value) error { + m, ok := val.(debug.Map) + if !ok { + return fmt.Errorf("got %T(%v) expected Map", val, val) + } + if m.Length != 0 { + return fmt.Errorf("got map length %d expected 0", m.Length) + } + return nil + }) + + checkValue("main.Z_map_3", func(val debug.Value) error { + m, ok := val.(debug.Map) + if !ok { + return fmt.Errorf("got %T(%v) expected Map", val, val) + } + if m.Length != 2 { + return fmt.Errorf("got map length %d expected 2", m.Length) + } + keyVar0, valVar0, err := prog.MapElement(m, 0) + if err != nil { + return err + } + keyVar1, valVar1, err := prog.MapElement(m, 1) + if err != nil { + return err + } + key0, err := prog.Value(keyVar0) + if err != nil { + return err + } + key1, err := prog.Value(keyVar1) + if err != nil { + return err + } + val0, err := prog.Value(valVar0) + if err != nil { + return err + } + val1, err := prog.Value(valVar1) + if err != nil { + return err + } + // The map should contain 1024,1 and 512,-1 in some order. + ok1 := key0 == int16(1024) && val0 == int8(1) && key1 == int16(512) && val1 == int8(-1) + ok2 := key1 == int16(1024) && val1 == int8(1) && key0 == int16(512) && val0 == int8(-1) + if !ok1 && !ok2 { + return fmt.Errorf("got values (%d,%d) and (%d,%d), expected (1024,1) and (512,-1) in some order", key0, val0, key1, val1) + } + _, _, err = prog.MapElement(m, 2) + if err == nil { + return fmt.Errorf("MapElement: reading at a bad index succeeded, expected error") + } + return nil + }) + + checkValue("main.Z_string", func(val debug.Value) error { + s, ok := val.(debug.String) + if !ok { + return fmt.Errorf("got %T(%v) expected String", val, val) + } + if s.Length != 12 { + return fmt.Errorf("got string length %d expected 12", s.Length) + } + expected := "I'm a string" + if s.String != expected { + return fmt.Errorf("got %s expected %s", s.String, expected) + } + return nil + }) + + checkValue("main.Z_channel", func(val debug.Value) error { + c, ok := val.(debug.Channel) + if !ok { + return fmt.Errorf("got %T(%v) expected Channel", val, val) + } + if c.Buffer == 0 { + return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer) + } + if c.Length != 0 { + return fmt.Errorf("got length %d expected 0", c.Length) + } + if c.Capacity != 0 { + return fmt.Errorf("got capacity %d expected 0", c.Capacity) + } + return nil + }) + + checkValue("main.Z_channel_2", func(val debug.Value) error { + c, ok := val.(debug.Channel) + if !ok { + return fmt.Errorf("got %T(%v) expected Channel", val, val) + } + if c.Buffer == 0 { + return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer) + } + if c.Length != 0 { + return fmt.Errorf("got length %d expected 0", c.Length) + } + if c.Capacity != 0 { + return fmt.Errorf("got capacity %d expected 0", c.Capacity) + } + return nil + }) + + checkValue("main.Z_channel_nil", func(val debug.Value) error { + c, ok := val.(debug.Channel) + if !ok { + return fmt.Errorf("got %T(%v) expected Channel", val, val) + } + if c.Buffer != 0 { + return fmt.Errorf("got buffer address %d expected 0", c.Buffer) + } + if c.Length != 0 { + return fmt.Errorf("got length %d expected 0", c.Length) + } + if c.Capacity != 0 { + return fmt.Errorf("got capacity %d expected 0", c.Capacity) + } + return nil + }) + + checkValue("main.Z_channel_buffered", func(val debug.Value) error { + c, ok := val.(debug.Channel) + if !ok { + return fmt.Errorf("got %T(%v) expected Channel", val, val) + } + if c.Buffer == 0 { + return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer) + } + if c.Length != 6 { + return fmt.Errorf("got length %d expected 6", c.Length) + } + if c.Capacity != 10 { + return fmt.Errorf("got capacity %d expected 10", c.Capacity) + } + if c.Stride != 2 { + return fmt.Errorf("got stride %d expected 2", c.Stride) + } + expected := []int16{8, 9, 10, 11, 12, 13} + for i := uint64(0); i < 6; i++ { + if v, err := prog.Value(c.Element(i)); err != nil { + return fmt.Errorf("reading element %d: %s", i, err) + } else if v != expected[i] { + return fmt.Errorf("element %d: got %T(%v) want %T(%d)", i, v, v, expected[i], expected[i]) + } + } + v := c.Element(6) + if v.Address != 0 { + return fmt.Errorf("invalid element returned Var with address %d, expected 0", v.Address) + } + return nil + }) + + checkValue("main.Z_func_bar", func(val debug.Value) error { + f, ok := val.(debug.Func) + if !ok { + return fmt.Errorf("got %T(%v) expected Func", val, val) + } + if f.Address == 0 { + return fmt.Errorf("got func address %d expected nonzero", f.Address) + } + return nil + }) + + checkValue("main.Z_func_nil", func(val debug.Value) error { + f, ok := val.(debug.Func) + if !ok { + return fmt.Errorf("got %T(%v) expected Func", val, val) + } + if f.Address != 0 { + return fmt.Errorf("got func address %d expected zero", f.Address) + } + return nil + }) +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/testdata/main.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/testdata/main.go new file mode 100644 index 0000000000..41d0f41c01 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/tests/peek/testdata/main.go @@ -0,0 +1,203 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 main + +import ( + "fmt" + "log" + "os" + "time" + "unsafe" +) + +type FooInterface interface { + Bar() +} + +type FooStruct struct { + a int + b string +} + +func (f *FooStruct) Bar() {} + +type myInt int16 + +var ( + Z_bool_false bool = false + Z_bool_true bool = true + Z_int int = -21 + Z_int8 int8 = -121 + Z_int16 int16 = -32321 + Z_int32 int32 = -1987654321 + Z_int64 int64 = -9012345678987654321 + Z_int_typedef myInt = 88 + Z_uint uint = 21 + Z_uint8 uint8 = 231 + Z_uint16 uint16 = 54321 + Z_uint32 uint32 = 3217654321 + Z_uint64 uint64 = 12345678900987654321 + Z_uintptr uintptr = 21 + Z_float32 float32 = 1.54321 + Z_float64 float64 = 1.987654321 + Z_complex64 complex64 = 1.54321 + 2.54321i + Z_complex128 complex128 = 1.987654321 - 2.987654321i + Z_array [5]int8 = [5]int8{-121, 121, 3, 2, 1} + Z_array_empty [0]int8 = [0]int8{} + Z_array_of_empties [2]struct{} = [2]struct{}{struct{}{}, struct{}{}} + Z_channel chan int16 = make(chan int16) + Z_channel_2 chan int16 = make(chan int16) + Z_channel_buffered chan int16 = make(chan int16, 10) + Z_channel_nil chan int16 + Z_func_bar = (*FooStruct).Bar + Z_func_int8_r_int8 = func(x int8) int8 { return x + 1 } + Z_func_int8_r_pint8 = func(x int8) *int8 { y := x + 1; return &y } + Z_func_nil func(x int8) int8 = nil + Z_interface FooInterface = &Z_struct + Z_interface_typed_nil FooInterface = Z_pointer_nil + Z_interface_nil FooInterface + Z_map map[int8]float32 = map[int8]float32{-21: 3.54321} + Z_map_2 map[int16]int8 = map[int16]int8{1024: 1} + Z_map_3 map[int16]int8 = map[int16]int8{1024: 1, 512: -1} + Z_map_empty map[int8]float32 = map[int8]float32{} + Z_map_nil map[int8]float32 + Z_pointer *FooStruct = &Z_struct + Z_pointer_nil *FooStruct + Z_slice []byte = []byte{'s', 'l', 'i', 'c', 'e'} + Z_slice_2 []int8 = Z_array[0:2] + Z_slice_nil []byte + Z_string string = "I'm a string" + Z_struct FooStruct = FooStruct{a: 21, b: "hi"} + Z_unsafe_pointer unsafe.Pointer = unsafe.Pointer(&Z_uint) + Z_unsafe_pointer_nil unsafe.Pointer +) + +func foo(x int16, y float32) { + var ( + local_array [5]int8 = [5]int8{-121, 121, 3, 2, 1} + local_bool_false bool = false + local_bool_true bool = true + local_channel chan int16 = Z_channel + local_channel_buffered chan int16 = Z_channel_buffered + local_channel_nil chan int16 + local_complex128 complex128 = 1.987654321 - 2.987654321i + local_complex64 complex64 = 1.54321 + 2.54321i + local_float32 float32 = 1.54321 + local_float64 float64 = 1.987654321 + local_func_bar = (*FooStruct).Bar + local_func_int8_r_int8 = func(x int8) int8 { return x + 1 } + local_func_int8_r_pint8 = func(x int8) *int8 { y := x + 1; return &y } + local_func_nil func(x int8) int8 = nil + local_int int = -21 + local_int16 int16 = -32321 + local_int32 int32 = -1987654321 + local_int64 int64 = -9012345678987654321 + local_int8 int8 = -121 + local_int_typedef myInt = 88 + local_interface FooInterface = &Z_struct + local_interface_nil FooInterface + local_interface_typed_nil FooInterface = Z_pointer_nil + local_map map[int8]float32 = map[int8]float32{-21: 3.54321} + local_map_2 map[int16]int8 = map[int16]int8{1024: 1} + local_map_3 map[int16]int8 = map[int16]int8{1024: 1, 512: -1} + local_map_empty map[int8]float32 = map[int8]float32{} + local_map_nil map[int8]float32 + local_pointer *FooStruct = &Z_struct + local_pointer_nil *FooStruct + local_slice []byte = []byte{'s', 'l', 'i', 'c', 'e'} + local_slice_2 []int8 = Z_array[0:2] + local_slice_nil []byte + local_string string = "I'm a string" + local_struct FooStruct = FooStruct{a: 21, b: "hi"} + local_uint uint = 21 + local_uint16 uint16 = 54321 + local_uint32 uint32 = 3217654321 + local_uint64 uint64 = 12345678900987654321 + local_uint8 uint8 = 231 + local_uintptr uintptr = 21 + local_unsafe_pointer unsafe.Pointer = unsafe.Pointer(&Z_uint) + local_unsafe_pointer_nil unsafe.Pointer + ) + fmt.Println(Z_bool_false, Z_bool_true) + fmt.Println(Z_int, Z_int8, Z_int16, Z_int32, Z_int64, Z_int_typedef) + fmt.Println(Z_uint, Z_uint8, Z_uint16, Z_uint32, Z_uint64, Z_uintptr) + fmt.Println(Z_float32, Z_float64, Z_complex64, Z_complex128) + fmt.Println(Z_array, Z_array_empty, Z_array_of_empties) + fmt.Println(Z_channel, Z_channel_buffered, Z_channel_nil) + fmt.Println(Z_func_bar, Z_func_int8_r_int8, Z_func_int8_r_pint8, Z_func_nil) + fmt.Println(Z_interface, Z_interface_nil, Z_interface_typed_nil) + fmt.Println(Z_map, Z_map_2, Z_map_3, Z_map_empty, Z_map_nil) + fmt.Println(Z_pointer, Z_pointer_nil) + fmt.Println(Z_slice, Z_slice_2, Z_slice_nil) + fmt.Println(Z_string, Z_struct) + fmt.Println(Z_unsafe_pointer, Z_unsafe_pointer_nil) + fmt.Println(local_bool_false, local_bool_true) + fmt.Println(local_int, local_int8, local_int16, local_int32, local_int64, local_int_typedef) + fmt.Println(local_uint, local_uint8, local_uint16, local_uint32, local_uint64, local_uintptr) + fmt.Println(local_float32, local_float64, local_complex64, local_complex128, local_array) + fmt.Println(local_channel, local_channel_buffered, local_channel_nil) + fmt.Println(local_func_bar, local_func_int8_r_int8, local_func_int8_r_pint8, local_func_nil) + fmt.Println(local_interface, local_interface_nil, local_interface_typed_nil) + fmt.Println(local_map, local_map_2, local_map_3, local_map_empty, local_map_nil) + fmt.Println(local_pointer, local_pointer_nil) + fmt.Println(local_slice, local_slice_2, local_slice_nil) + fmt.Println(local_string, local_struct) + fmt.Println(local_unsafe_pointer, local_unsafe_pointer_nil) + f1() + f2() +} + +func f1() { + fmt.Println() +} + +func f2() { + fmt.Println() +} + +func bar() { + foo(42, 1.5) + fmt.Print() +} + +func populateChannels() { + go func() { + Z_channel_2 <- 8 + }() + go func() { + for i := int16(0); i < 14; i++ { + Z_channel_buffered <- i + } + }() + go func() { + for i := 0; i < 8; i++ { + <-Z_channel_buffered + } + }() + time.Sleep(time.Second / 20) +} + +func main() { + args := os.Args[1:] + expected := []string{"some", "arguments"} + if len(args) != 2 || args[0] != expected[0] || args[1] != expected[1] { + log.Fatalf("got command-line args %v, expected %v", args, expected) + } + populateChannels() + for ; ; time.Sleep(2 * time.Second) { + bar() + } + select {} +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector.go new file mode 100644 index 0000000000..5467df9702 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector.go @@ -0,0 +1,460 @@ +// 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 valuecollector is used to collect the values of variables in a program. +package valuecollector + +import ( + "bytes" + "fmt" + "strconv" + "strings" + + "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug" + cd "google.golang.org/api/clouddebugger/v2" +) + +const ( + maxArrayLength = 50 + maxMapLength = 20 +) + +// Collector is given references to variables from a program being debugged +// using AddVariable. Then when ReadValues is called, the Collector will fetch +// the values of those variables. Any variables referred to by those values +// will also be fetched; e.g. the targets of pointers, members of structs, +// elements of slices, etc. This continues iteratively, building a graph of +// values, until all the reachable values are fetched, or a size limit is +// reached. +// +// Variables are passed to the Collector as debug.Var, which is used by x/debug +// to represent references to variables. Values are returned as cd.Variable, +// which is used by the Debuglet Controller to represent the graph of values. +// +// For example, if the program has a struct variable: +// +// foo := SomeStruct{a:42, b:"xyz"} +// +// and we call AddVariable with a reference to foo, we will get back a result +// like: +// +// cd.Variable{Name:"foo", VarTableIndex:10} +// +// which denotes a variable named "foo" which will have its value stored in +// element 10 of the table that will later be returned by ReadValues. That +// element might be: +// +// out[10] = &cd.Variable{Members:{{Name:"a", VarTableIndex:11},{Name:"b", VarTableIndex:12}}} +// +// which denotes a struct with two members a and b, whose values are in elements +// 11 and 12 of the output table: +// +// out[11] = &cd.Variable{Value:"42"} +// out[12] = &cd.Variable{Value:"xyz"} +type Collector struct { + // prog is the program being debugged. + prog debug.Program + // limit is the maximum size of the output slice of values. + limit int + // index is a map from references (variables and map elements) to their + // locations in the table. + index map[reference]int + // table contains the references, including those given to the + // Collector directly and those the Collector itself found. + // If VarTableIndex is set to 0 in a cd.Variable, it is ignored, so the first entry + // of table can't be used. On initialization we put a dummy value there. + table []reference +} + +// reference represents a value which is in the queue to be read by the +// collector. It is either a debug.Var, or a mapElement. +type reference interface{} + +// mapElement represents an element of a map in the debugged program's memory. +type mapElement struct { + debug.Map + index uint64 +} + +// NewCollector returns a Collector for the given program and size limit. +// The limit is the maximum size of the slice of values returned by ReadValues. +func NewCollector(prog debug.Program, limit int) *Collector { + return &Collector{ + prog: prog, + limit: limit, + index: make(map[reference]int), + table: []reference{debug.Var{}}, + } +} + +// AddVariable adds another variable to be collected. +// The Collector doesn't get the value immediately; it returns a cd.Variable +// that contains an index into the table which will later be returned by +// ReadValues. +func (c *Collector) AddVariable(lv debug.LocalVar) *cd.Variable { + ret := &cd.Variable{Name: lv.Name} + if index, ok := c.add(lv.Var); !ok { + // If the add call failed, it's because we reached the size limit. + // The Debuglet Controller's convention is to pass it a "Not Captured" error + // in this case. + ret.Status = statusMessage(messageNotCaptured, true, refersToVariableName) + } else { + ret.VarTableIndex = int64(index) + } + return ret +} + +// add adds a reference to the set of values to be read from the +// program. It returns the index in the output table that will contain the +// corresponding value. It fails if the table has reached the size limit. +// It deduplicates references, so the index may be the same as one that was +// returned from an earlier add call. +func (c *Collector) add(r reference) (outputIndex int, ok bool) { + if i, ok := c.index[r]; ok { + return i, true + } + i := len(c.table) + if i >= c.limit { + return 0, false + } + c.index[r] = i + c.table = append(c.table, r) + return i, true +} + +func addMember(v *cd.Variable, name string) *cd.Variable { + v2 := &cd.Variable{Name: name} + v.Members = append(v.Members, v2) + return v2 +} + +// ReadValues fetches values of the variables that were passed to the Collector +// with AddVariable. The values of any new variables found are also fetched, +// e.g. the targets of pointers or the members of structs, until we reach the +// size limit or we run out of values to fetch. +// The results are output as a []*cd.Variable, which is the type we need to send +// to the Debuglet Controller after we trigger a breakpoint. +func (c *Collector) ReadValues() (out []*cd.Variable) { + for i := 0; i < len(c.table); i++ { + // Create a new cd.Variable for this value, and append it to the output. + dcv := new(cd.Variable) + out = append(out, dcv) + if i == 0 { + // The first element is unused. + continue + } + switch x := c.table[i].(type) { + case mapElement: + key, value, err := c.prog.MapElement(x.Map, x.index) + if err != nil { + dcv.Status = statusMessage(err.Error(), true, refersToVariableValue) + continue + } + // Add a member for the key. + member := addMember(dcv, "key") + if index, ok := c.add(key); !ok { + // The table is full. + member.Status = statusMessage(messageNotCaptured, true, refersToVariableName) + continue + } else { + member.VarTableIndex = int64(index) + } + // Add a member for the value. + member = addMember(dcv, "value") + if index, ok := c.add(value); !ok { + // The table is full. + member.Status = statusMessage(messageNotCaptured, true, refersToVariableName) + } else { + member.VarTableIndex = int64(index) + } + case debug.Var: + if v, err := c.prog.Value(x); err != nil { + dcv.Status = statusMessage(err.Error(), true, refersToVariableValue) + } else { + c.FillValue(v, dcv) + } + } + } + return out +} + +// indexable is an interface for arrays, slices and channels. +type indexable interface { + Len() uint64 + Element(uint64) debug.Var +} + +// channel implements indexable. +type channel struct { + debug.Channel +} + +func (c channel) Len() uint64 { + return c.Length +} + +var ( + _ indexable = debug.Array{} + _ indexable = debug.Slice{} + _ indexable = channel{} +) + +// FillValue copies a value into a cd.Variable. Any variables referred to by +// that value, e.g. struct members and pointer targets, are added to the +// collector's queue, to be fetched later by ReadValues. +func (c *Collector) FillValue(v debug.Value, dcv *cd.Variable) { + if c, ok := v.(debug.Channel); ok { + // Convert to channel, which implements indexable. + v = channel{c} + } + // Fill in dcv in a manner depending on the type of the value we got. + switch val := v.(type) { + case int8, int16, int32, int64, bool, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128: + // For simple types, we just print the value to dcv.Value. + dcv.Value = fmt.Sprint(val) + case string: + // Put double quotes around strings. + dcv.Value = strconv.Quote(val) + case debug.String: + if uint64(len(val.String)) < val.Length { + // This string value was truncated. + dcv.Value = strconv.Quote(val.String + "...") + } else { + dcv.Value = strconv.Quote(val.String) + } + case debug.Struct: + // For structs, we add an entry to dcv.Members for each field in the + // struct. + // Each member will contain the name of the field, and the index in the + // output table which will contain the value of that field. + for _, f := range val.Fields { + member := addMember(dcv, f.Name) + if index, ok := c.add(f.Var); !ok { + // The table is full. + member.Status = statusMessage(messageNotCaptured, true, refersToVariableName) + } else { + member.VarTableIndex = int64(index) + } + } + case debug.Map: + dcv.Value = fmt.Sprintf("len = %d", val.Length) + for i := uint64(0); i < val.Length; i++ { + field := addMember(dcv, `⚫`) + if i == maxMapLength { + field.Name = "..." + field.Status = statusMessage(messageTruncated, true, refersToVariableName) + break + } + if index, ok := c.add(mapElement{val, i}); !ok { + // The value table is full; add a member to contain the error message. + field.Name = "..." + field.Status = statusMessage(messageNotCaptured, true, refersToVariableName) + break + } else { + field.VarTableIndex = int64(index) + } + } + case debug.Pointer: + if val.Address == 0 { + dcv.Value = "" + } else if val.TypeID == 0 { + // We don't know the type of the pointer, so just output the address as + // the value. + dcv.Value = fmt.Sprintf("0x%X", val.Address) + dcv.Status = statusMessage(messageUnknownPointerType, false, refersToVariableName) + } else { + // Adds the pointed-to variable to the table, and links this value to + // that table entry through VarTableIndex. + dcv.Value = fmt.Sprintf("0x%X", val.Address) + target := addMember(dcv, "") + if index, ok := c.add(debug.Var(val)); !ok { + target.Status = statusMessage(messageNotCaptured, true, refersToVariableName) + } else { + target.VarTableIndex = int64(index) + } + } + case indexable: + // Arrays, slices and channels. + dcv.Value = "len = " + fmt.Sprint(val.Len()) + for j := uint64(0); j < val.Len(); j++ { + field := addMember(dcv, fmt.Sprint(`[`, j, `]`)) + if j == maxArrayLength { + field.Name = "..." + field.Status = statusMessage(messageTruncated, true, refersToVariableName) + break + } + vr := val.Element(j) + if index, ok := c.add(vr); !ok { + // The value table is full; add a member to contain the error message. + field.Name = "..." + field.Status = statusMessage(messageNotCaptured, true, refersToVariableName) + break + } else { + // Add a member with the index as the name. + field.VarTableIndex = int64(index) + } + } + default: + dcv.Status = statusMessage(messageUnknownType, false, refersToVariableName) + } +} + +// statusMessage returns a *cd.StatusMessage with the given message, IsError +// field and refersTo field. +func statusMessage(msg string, isError bool, refersTo int) *cd.StatusMessage { + return &cd.StatusMessage{ + Description: &cd.FormatMessage{Format: "$0", Parameters: []string{msg}}, + IsError: isError, + RefersTo: refersToString[refersTo], + } +} + +// LogString produces a string for a logpoint, substituting in variable values +// using evaluatedExpressions and varTable. +func LogString(s string, evaluatedExpressions []*cd.Variable, varTable []*cd.Variable) string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "LOGPOINT: ") + seen := make(map[*cd.Variable]bool) + for i := 0; i < len(s); { + if s[i] == '$' { + i++ + if num, n, ok := parseToken(s[i:], len(evaluatedExpressions)-1); ok { + // This token is one of $0, $1, etc. Write the corresponding expression. + writeExpression(&buf, evaluatedExpressions[num], false, varTable, seen) + i += n + } else { + // Something else, like $$. + buf.WriteByte(s[i]) + i++ + } + } else { + buf.WriteByte(s[i]) + i++ + } + } + return buf.String() +} + +func parseToken(s string, max int) (num int, bytesRead int, ok bool) { + var i int + for i < len(s) && s[i] >= '0' && s[i] <= '9' { + i++ + } + num, err := strconv.Atoi(s[:i]) + return num, i, err == nil && num <= max +} + +// writeExpression recursively writes variables to buf, in a format suitable +// for logging. If printName is true, writes the name of the variable. +func writeExpression(buf *bytes.Buffer, v *cd.Variable, printName bool, varTable []*cd.Variable, seen map[*cd.Variable]bool) { + if v == nil { + // Shouldn't happen. + return + } + name, value, status, members := v.Name, v.Value, v.Status, v.Members + + // If v.VarTableIndex is not zero, it refers to an element of varTable. + // We merge its fields with the fields we got from v. + var other *cd.Variable + if idx := int(v.VarTableIndex); idx > 0 && idx < len(varTable) { + other = varTable[idx] + } + if other != nil { + if name == "" { + name = other.Name + } + if value == "" { + value = other.Value + } + if status == nil { + status = other.Status + } + if len(members) == 0 { + members = other.Members + } + } + if printName && name != "" { + buf.WriteString(name) + buf.WriteByte(':') + } + + // If we have seen this value before, write "..." rather than repeating it. + if seen[v] { + buf.WriteString("...") + return + } + seen[v] = true + if other != nil { + if seen[other] { + buf.WriteString("...") + return + } + seen[other] = true + } + + if value != "" && !strings.HasPrefix(value, "len = ") { + // A plain value. + buf.WriteString(value) + } else if status != nil && status.Description != nil { + // An error. + for _, p := range status.Description.Parameters { + buf.WriteByte('(') + buf.WriteString(p) + buf.WriteByte(')') + } + } else if name == `⚫` { + // A map element. + first := true + for _, member := range members { + if first { + first = false + } else { + buf.WriteByte(':') + } + writeExpression(buf, member, false, varTable, seen) + } + } else { + // A map, array, slice, channel, or struct. + isStruct := value == "" + first := true + buf.WriteByte('{') + for _, member := range members { + if first { + first = false + } else { + buf.WriteString(", ") + } + writeExpression(buf, member, isStruct, varTable, seen) + } + buf.WriteByte('}') + } +} + +const ( + // Error messages for cd.StatusMessage + messageNotCaptured = "Not captured" + messageTruncated = "Truncated" + messageUnknownPointerType = "Unknown pointer type" + messageUnknownType = "Unknown type" + // RefersTo values for cd.StatusMessage. + refersToVariableName = iota + refersToVariableValue +) + +// refersToString contains the strings for each refersTo value. +// See the definition of StatusMessage in the v2/clouddebugger package. +var refersToString = map[int]string{ + refersToVariableName: "VARIABLE_NAME", + refersToVariableValue: "VARIABLE_VALUE", +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector_test.go new file mode 100644 index 0000000000..3bf0be3725 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector_test.go @@ -0,0 +1,418 @@ +// 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 valuecollector + +import ( + "fmt" + "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" +) + +const ( + // Some arbitrary type IDs for the test, for use in debug.Var's TypeID field. + // A TypeID of 0 means the type is unknown, so we start at 1. + int16Type = iota + 1 + stringType + structType + pointerType + arrayType + int32Type + debugStringType + mapType + channelType + sliceType +) + +func TestValueCollector(t *testing.T) { + // Construct the collector. + c := NewCollector(&Program{}, 26) + // Add some variables of various types, whose values we want the collector to read. + variablesToAdd := []debug.LocalVar{ + {Name: "a", Var: debug.Var{TypeID: int16Type, Address: 0x1}}, + {Name: "b", Var: debug.Var{TypeID: stringType, Address: 0x2}}, + {Name: "c", Var: debug.Var{TypeID: structType, Address: 0x3}}, + {Name: "d", Var: debug.Var{TypeID: pointerType, Address: 0x4}}, + {Name: "e", Var: debug.Var{TypeID: arrayType, Address: 0x5}}, + {Name: "f", Var: debug.Var{TypeID: debugStringType, Address: 0x6}}, + {Name: "g", Var: debug.Var{TypeID: mapType, Address: 0x7}}, + {Name: "h", Var: debug.Var{TypeID: channelType, Address: 0x8}}, + {Name: "i", Var: debug.Var{TypeID: sliceType, Address: 0x9}}, + } + expectedResults := []*cd.Variable{ + &cd.Variable{Name: "a", VarTableIndex: 1}, + &cd.Variable{Name: "b", VarTableIndex: 2}, + &cd.Variable{Name: "c", VarTableIndex: 3}, + &cd.Variable{Name: "d", VarTableIndex: 4}, + &cd.Variable{Name: "e", VarTableIndex: 5}, + &cd.Variable{Name: "f", VarTableIndex: 6}, + &cd.Variable{Name: "g", VarTableIndex: 7}, + &cd.Variable{Name: "h", VarTableIndex: 8}, + &cd.Variable{Name: "i", VarTableIndex: 9}, + } + for i, v := range variablesToAdd { + added := c.AddVariable(v) + if !testutil.Equal(added, expectedResults[i]) { + t.Errorf("AddVariable: got %+v want %+v", *added, *expectedResults[i]) + } + } + // Read the values, compare the output to what we expect. + v := c.ReadValues() + expectedValues := []*cd.Variable{ + &cd.Variable{}, + &cd.Variable{Value: "1"}, + &cd.Variable{Value: `"hello"`}, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "x", VarTableIndex: 1}, + &cd.Variable{Name: "y", VarTableIndex: 2}, + }, + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{VarTableIndex: 1}, + }, + Value: "0x1", + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "[0]", VarTableIndex: 10}, + &cd.Variable{Name: "[1]", VarTableIndex: 11}, + &cd.Variable{Name: "[2]", VarTableIndex: 12}, + &cd.Variable{Name: "[3]", VarTableIndex: 13}, + }, + Value: "len = 4", + }, + &cd.Variable{Value: `"world"`}, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "⚫", VarTableIndex: 14}, + &cd.Variable{Name: "⚫", VarTableIndex: 15}, + &cd.Variable{Name: "⚫", VarTableIndex: 16}, + }, + Value: "len = 3", + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "[0]", VarTableIndex: 17}, + &cd.Variable{Name: "[1]", VarTableIndex: 18}, + }, + Value: "len = 2", + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "[0]", VarTableIndex: 19}, + &cd.Variable{Name: "[1]", VarTableIndex: 20}, + }, + Value: "len = 2", + }, + &cd.Variable{Value: "100"}, + &cd.Variable{Value: "104"}, + &cd.Variable{Value: "108"}, + &cd.Variable{Value: "112"}, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "key", VarTableIndex: 21}, + &cd.Variable{Name: "value", VarTableIndex: 22}, + }, + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "key", VarTableIndex: 23}, + &cd.Variable{Name: "value", VarTableIndex: 24}, + }, + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "key", VarTableIndex: 25}, + &cd.Variable{ + Name: "value", + Status: &cd.StatusMessage{ + Description: &cd.FormatMessage{ + Format: "$0", + Parameters: []string{"Not captured"}, + }, + IsError: true, + RefersTo: "VARIABLE_NAME", + }, + }, + }, + }, + &cd.Variable{Value: "246"}, + &cd.Variable{Value: "210"}, + &cd.Variable{Value: "300"}, + &cd.Variable{Value: "304"}, + &cd.Variable{Value: "400"}, + &cd.Variable{Value: "404"}, + &cd.Variable{Value: "1400"}, + &cd.Variable{Value: "1404"}, + &cd.Variable{Value: "2400"}, + } + if !testutil.Equal(v, expectedValues) { + t.Errorf("ReadValues: got %v want %v", v, expectedValues) + // Do element-by-element comparisons, for more useful error messages. + for i := range v { + if i < len(expectedValues) && !testutil.Equal(v[i], expectedValues[i]) { + t.Errorf("element %d: got %+v want %+v", i, *v[i], *expectedValues[i]) + } + } + } +} + +// Program implements the similarly-named interface in x/debug. +// ValueCollector should only call its Value and MapElement methods. +type Program struct { + debug.Program +} + +func (p *Program) Value(v debug.Var) (debug.Value, error) { + // We determine what to return using v.TypeID. + switch v.TypeID { + case int16Type: + // We use the address as the value, so that we're testing whether the right + // address was calculated. + return int16(v.Address), nil + case stringType: + // A string. + return "hello", nil + case structType: + // A struct with two elements. + return debug.Struct{ + Fields: []debug.StructField{ + { + Name: "x", + Var: debug.Var{TypeID: int16Type, Address: 0x1}, + }, + { + Name: "y", + Var: debug.Var{TypeID: stringType, Address: 0x2}, + }, + }, + }, nil + case pointerType: + // A pointer to the first variable above. + return debug.Pointer{TypeID: int16Type, Address: 0x1}, nil + case arrayType: + // An array of 4 32-bit-wide elements. + return debug.Array{ + ElementTypeID: int32Type, + Address: 0x64, + Length: 4, + StrideBits: 32, + }, nil + case debugStringType: + return debug.String{ + Length: 5, + String: "world", + }, nil + case mapType: + return debug.Map{ + TypeID: 99, + Address: 0x100, + Length: 3, + }, nil + case channelType: + return debug.Channel{ + ElementTypeID: int32Type, + Address: 200, + Buffer: 210, + Length: 2, + Capacity: 10, + Stride: 4, + BufferStart: 9, + }, nil + case sliceType: + // A slice of 2 32-bit-wide elements. + return debug.Slice{ + Array: debug.Array{ + ElementTypeID: int32Type, + Address: 300, + Length: 2, + StrideBits: 32, + }, + Capacity: 50, + }, nil + case int32Type: + // We use the address as the value, so that we're testing whether the right + // address was calculated. + return int32(v.Address), nil + } + return nil, fmt.Errorf("unexpected Value request") +} + +func (p *Program) MapElement(m debug.Map, index uint64) (debug.Var, debug.Var, error) { + return debug.Var{TypeID: int16Type, Address: 1000*index + 400}, + debug.Var{TypeID: int32Type, Address: 1000*index + 404}, + nil +} + +func TestLogString(t *testing.T) { + bp := cd.Breakpoint{ + Action: "LOG", + LogMessageFormat: "$0 hello, $$7world! $1 $2 $3 $4 $5$6 $7 $8", + EvaluatedExpressions: []*cd.Variable{ + &cd.Variable{Name: "a", VarTableIndex: 1}, + &cd.Variable{Name: "b", VarTableIndex: 2}, + &cd.Variable{Name: "c", VarTableIndex: 3}, + &cd.Variable{Name: "d", VarTableIndex: 4}, + &cd.Variable{Name: "e", VarTableIndex: 5}, + &cd.Variable{Name: "f", VarTableIndex: 6}, + &cd.Variable{Name: "g", VarTableIndex: 7}, + &cd.Variable{Name: "h", VarTableIndex: 8}, + &cd.Variable{Name: "i", VarTableIndex: 9}, + }, + } + varTable := []*cd.Variable{ + &cd.Variable{}, + &cd.Variable{Value: "1"}, + &cd.Variable{Value: `"hello"`}, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "x", Value: "1"}, + &cd.Variable{Name: "y", Value: `"hello"`}, + &cd.Variable{Name: "z", VarTableIndex: 3}, + }, + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{VarTableIndex: 1}, + }, + Value: "0x1", + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "[0]", VarTableIndex: 10}, + &cd.Variable{Name: "[1]", VarTableIndex: 11}, + &cd.Variable{Name: "[2]", VarTableIndex: 12}, + &cd.Variable{Name: "[3]", VarTableIndex: 13}, + }, + Value: "len = 4", + }, + &cd.Variable{Value: `"world"`}, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "⚫", VarTableIndex: 14}, + &cd.Variable{Name: "⚫", VarTableIndex: 15}, + &cd.Variable{Name: "⚫", VarTableIndex: 16}, + }, + Value: "len = 3", + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "[0]", VarTableIndex: 17}, + &cd.Variable{Name: "[1]", VarTableIndex: 18}, + }, + Value: "len = 2", + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "[0]", VarTableIndex: 19}, + &cd.Variable{Name: "[1]", VarTableIndex: 20}, + }, + Value: "len = 2", + }, + &cd.Variable{Value: "100"}, + &cd.Variable{Value: "104"}, + &cd.Variable{Value: "108"}, + &cd.Variable{Value: "112"}, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "key", VarTableIndex: 21}, + &cd.Variable{Name: "value", VarTableIndex: 22}, + }, + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "key", VarTableIndex: 23}, + &cd.Variable{Name: "value", VarTableIndex: 24}, + }, + }, + &cd.Variable{ + Members: []*cd.Variable{ + &cd.Variable{Name: "key", VarTableIndex: 25}, + &cd.Variable{ + Name: "value", + Status: &cd.StatusMessage{ + Description: &cd.FormatMessage{ + Format: "$0", + Parameters: []string{"Not captured"}, + }, + IsError: true, + RefersTo: "VARIABLE_NAME", + }, + }, + }, + }, + &cd.Variable{Value: "246"}, + &cd.Variable{Value: "210"}, + &cd.Variable{Value: "300"}, + &cd.Variable{Value: "304"}, + &cd.Variable{Value: "400"}, + &cd.Variable{Value: "404"}, + &cd.Variable{Value: "1400"}, + &cd.Variable{Value: "1404"}, + &cd.Variable{Value: "2400"}, + } + s := LogString(bp.LogMessageFormat, bp.EvaluatedExpressions, varTable) + expected := `LOGPOINT: 1 hello, $7world! "hello" {x:1, y:"hello", z:...} ` + + `0x1 {100, 104, 108, 112} "world"{400:404, 1400:1404, 2400:(Not captured)} ` + + `{246, 210} {300, 304}` + if s != expected { + t.Errorf("LogString: got %q want %q", s, expected) + } +} + +func TestParseToken(t *testing.T) { + for _, c := range []struct { + s string + max int + num int + n int + ok bool + }{ + {"", 0, 0, 0, false}, + {".", 0, 0, 0, false}, + {"0", 0, 0, 1, true}, + {"0", 1, 0, 1, true}, + {"00", 0, 0, 2, true}, + {"1.", 1, 1, 1, true}, + {"1.", 0, 0, 0, false}, + {"10", 10, 10, 2, true}, + {"10..", 10, 10, 2, true}, + {"10", 11, 10, 2, true}, + {"10..", 11, 10, 2, true}, + {"10", 9, 0, 0, false}, + {"10..", 9, 0, 0, false}, + {" 10", 10, 0, 0, false}, + {"010", 10, 10, 3, true}, + {"123456789", 123456789, 123456789, 9, true}, + {"123456789", 123456788, 0, 0, false}, + {"123456789123456789123456789", 999999999, 0, 0, false}, + } { + num, n, ok := parseToken(c.s, c.max) + if ok != c.ok { + t.Errorf("parseToken(%q, %d): got ok=%t want ok=%t", c.s, c.max, ok, c.ok) + continue + } + if !ok { + continue + } + if num != c.num || n != c.n { + t.Errorf("parseToken(%q, %d): got %d,%d,%t want %d,%d,%t", c.s, c.max, num, n, ok, c.num, c.n, c.ok) + } + } +} diff --git a/vendor/cloud.google.com/go/compute/metadata/examples_test.go b/vendor/cloud.google.com/go/compute/metadata/examples_test.go new file mode 100644 index 0000000000..6c546a63c9 --- /dev/null +++ b/vendor/cloud.google.com/go/compute/metadata/examples_test.go @@ -0,0 +1,46 @@ +// 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 metadata_test + +import ( + "net/http" + + "cloud.google.com/go/compute/metadata" +) + +// This example demonstrates how to use your own transport when using this package. +func ExampleNewClient() { + c := metadata.NewClient(&http.Client{Transport: userAgentTransport{ + userAgent: "my-user-agent", + base: http.DefaultTransport, + }}) + p, err := c.ProjectID() + if err != nil { + // TODO: Handle error. + } + _ = p // TODO: Use p. +} + +// userAgentTransport sets the User-Agent header before calling base. +type userAgentTransport struct { + userAgent string + base http.RoundTripper +} + +// RoundTrip implements the http.RoundTripper interface. +func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", t.userAgent) + return t.base.RoundTrip(req) +} diff --git a/vendor/cloud.google.com/go/compute/metadata/metadata.go b/vendor/cloud.google.com/go/compute/metadata/metadata.go new file mode 100644 index 0000000000..9d0660be47 --- /dev/null +++ b/vendor/cloud.google.com/go/compute/metadata/metadata.go @@ -0,0 +1,503 @@ +// 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 metadata provides access to Google Compute Engine (GCE) +// metadata and API service accounts. +// +// This package is a wrapper around the GCE metadata service, +// as documented at https://developers.google.com/compute/docs/metadata. +package metadata // import "cloud.google.com/go/compute/metadata" + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "runtime" + "strings" + "sync" + "time" + + "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" +) + +const ( + // metadataIP is the documented metadata server IP address. + metadataIP = "169.254.169.254" + + // metadataHostEnv is the environment variable specifying the + // GCE metadata hostname. If empty, the default value of + // metadataIP ("169.254.169.254") is used instead. + // This is variable name is not defined by any spec, as far as + // I know; it was made up for the Go package. + metadataHostEnv = "GCE_METADATA_HOST" + + userAgent = "gcloud-golang/0.1" +) + +type cachedValue struct { + k string + trim bool + mu sync.Mutex + v string +} + +var ( + projID = &cachedValue{k: "project/project-id", trim: true} + projNum = &cachedValue{k: "project/numeric-project-id", trim: true} + instID = &cachedValue{k: "instance/id", trim: true} +) + +var ( + defaultClient = &Client{hc: &http.Client{ + Transport: &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 2 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + ResponseHeaderTimeout: 2 * time.Second, + }, + }} + subscribeClient = &Client{hc: &http.Client{ + Transport: &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 2 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + }, + }} +) + +// NotDefinedError is returned when requested metadata is not defined. +// +// The underlying string is the suffix after "/computeMetadata/v1/". +// +// This error is not returned if the value is defined to be the empty +// string. +type NotDefinedError string + +func (suffix NotDefinedError) Error() string { + return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix)) +} + +func (c *cachedValue) get(cl *Client) (v string, err error) { + defer c.mu.Unlock() + c.mu.Lock() + if c.v != "" { + return c.v, nil + } + if c.trim { + v, err = cl.getTrimmed(c.k) + } else { + v, err = cl.Get(c.k) + } + if err == nil { + c.v = v + } + return +} + +var ( + onGCEOnce sync.Once + onGCE bool +) + +// OnGCE reports whether this process is running on Google Compute Engine. +func OnGCE() bool { + onGCEOnce.Do(initOnGCE) + return onGCE +} + +func initOnGCE() { + onGCE = testOnGCE() +} + +func testOnGCE() bool { + // The user explicitly said they're on GCE, so trust them. + if os.Getenv(metadataHostEnv) != "" { + return true + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + resc := make(chan bool, 2) + + // Try two strategies in parallel. + // See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194 + go func() { + req, _ := http.NewRequest("GET", "http://"+metadataIP, nil) + req.Header.Set("User-Agent", userAgent) + res, err := ctxhttp.Do(ctx, defaultClient.hc, req) + if err != nil { + resc <- false + return + } + defer res.Body.Close() + resc <- res.Header.Get("Metadata-Flavor") == "Google" + }() + + go func() { + addrs, err := net.LookupHost("metadata.google.internal") + if err != nil || len(addrs) == 0 { + resc <- false + return + } + resc <- strsContains(addrs, metadataIP) + }() + + tryHarder := systemInfoSuggestsGCE() + if tryHarder { + res := <-resc + if res { + // The first strategy succeeded, so let's use it. + return true + } + // Wait for either the DNS or metadata server probe to + // contradict the other one and say we are running on + // GCE. Give it a lot of time to do so, since the system + // info already suggests we're running on a GCE BIOS. + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + select { + case res = <-resc: + return res + case <-timer.C: + // Too slow. Who knows what this system is. + return false + } + } + + // There's no hint from the system info that we're running on + // GCE, so use the first probe's result as truth, whether it's + // true or false. The goal here is to optimize for speed for + // users who are NOT running on GCE. We can't assume that + // either a DNS lookup or an HTTP request to a blackholed IP + // address is fast. Worst case this should return when the + // metaClient's Transport.ResponseHeaderTimeout or + // Transport.Dial.Timeout fires (in two seconds). + return <-resc +} + +// systemInfoSuggestsGCE reports whether the local system (without +// doing network requests) suggests that we're running on GCE. If this +// returns true, testOnGCE tries a bit harder to reach its metadata +// server. +func systemInfoSuggestsGCE() bool { + if runtime.GOOS != "linux" { + // We don't have any non-Linux clues available, at least yet. + return false + } + slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name") + name := strings.TrimSpace(string(slurp)) + return name == "Google" || name == "Google Compute Engine" +} + +// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no +// ResponseHeaderTimeout). +func Subscribe(suffix string, fn func(v string, ok bool) error) error { + return subscribeClient.Subscribe(suffix, fn) +} + +// Get calls Client.Get on the default client. +func Get(suffix string) (string, error) { return defaultClient.Get(suffix) } + +// ProjectID returns the current instance's project ID string. +func ProjectID() (string, error) { return defaultClient.ProjectID() } + +// NumericProjectID returns the current instance's numeric project ID. +func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() } + +// InternalIP returns the instance's primary internal IP address. +func InternalIP() (string, error) { return defaultClient.InternalIP() } + +// ExternalIP returns the instance's primary external (public) IP address. +func ExternalIP() (string, error) { return defaultClient.ExternalIP() } + +// Hostname returns the instance's hostname. This will be of the form +// ".c..internal". +func Hostname() (string, error) { return defaultClient.Hostname() } + +// InstanceTags returns the list of user-defined instance tags, +// assigned when initially creating a GCE instance. +func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() } + +// InstanceID returns the current VM's numeric instance ID. +func InstanceID() (string, error) { return defaultClient.InstanceID() } + +// InstanceName returns the current VM's instance ID string. +func InstanceName() (string, error) { return defaultClient.InstanceName() } + +// Zone returns the current VM's zone, such as "us-central1-b". +func Zone() (string, error) { return defaultClient.Zone() } + +// InstanceAttributes calls Client.InstanceAttributes on the default client. +func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() } + +// ProjectAttributes calls Client.ProjectAttributes on the default client. +func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() } + +// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client. +func InstanceAttributeValue(attr string) (string, error) { + return defaultClient.InstanceAttributeValue(attr) +} + +// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client. +func ProjectAttributeValue(attr string) (string, error) { + return defaultClient.ProjectAttributeValue(attr) +} + +// Scopes calls Client.Scopes on the default client. +func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) } + +func strsContains(ss []string, s string) bool { + for _, v := range ss { + if v == s { + return true + } + } + return false +} + +// A Client provides metadata. +type Client struct { + hc *http.Client +} + +// NewClient returns a Client that can be used to fetch metadata. All HTTP requests +// will use the given http.Client instead of the default client. +func NewClient(c *http.Client) *Client { + return &Client{hc: c} +} + +// getETag returns a value from the metadata service as well as the associated ETag. +// This func is otherwise equivalent to Get. +func (c *Client) getETag(suffix string) (value, etag string, err error) { + // Using a fixed IP makes it very difficult to spoof the metadata service in + // a container, which is an important use-case for local testing of cloud + // deployments. To enable spoofing of the metadata service, the environment + // variable GCE_METADATA_HOST is first inspected to decide where metadata + // requests shall go. + host := os.Getenv(metadataHostEnv) + if host == "" { + // Using 169.254.169.254 instead of "metadata" here because Go + // binaries built with the "netgo" tag and without cgo won't + // know the search suffix for "metadata" is + // ".google.internal", and this IP address is documented as + // being stable anyway. + host = metadataIP + } + url := "http://" + host + "/computeMetadata/v1/" + suffix + req, _ := http.NewRequest("GET", url, nil) + req.Header.Set("Metadata-Flavor", "Google") + req.Header.Set("User-Agent", userAgent) + res, err := c.hc.Do(req) + if err != nil { + return "", "", err + } + defer res.Body.Close() + if res.StatusCode == http.StatusNotFound { + return "", "", NotDefinedError(suffix) + } + if res.StatusCode != 200 { + return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url) + } + all, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", "", err + } + return string(all), res.Header.Get("Etag"), nil +} + +// Get returns a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// +// If the GCE_METADATA_HOST environment variable is not defined, a default of +// 169.254.169.254 will be used instead. +// +// If the requested metadata is not defined, the returned error will +// be of type NotDefinedError. +func (c *Client) Get(suffix string) (string, error) { + val, _, err := c.getETag(suffix) + return val, err +} + +func (c *Client) getTrimmed(suffix string) (s string, err error) { + s, err = c.Get(suffix) + s = strings.TrimSpace(s) + return +} + +func (c *Client) lines(suffix string) ([]string, error) { + j, err := c.Get(suffix) + if err != nil { + return nil, err + } + s := strings.Split(strings.TrimSpace(j), "\n") + for i := range s { + s[i] = strings.TrimSpace(s[i]) + } + return s, nil +} + +// ProjectID returns the current instance's project ID string. +func (c *Client) ProjectID() (string, error) { return projID.get(c) } + +// NumericProjectID returns the current instance's numeric project ID. +func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) } + +// InstanceID returns the current VM's numeric instance ID. +func (c *Client) InstanceID() (string, error) { return instID.get(c) } + +// InternalIP returns the instance's primary internal IP address. +func (c *Client) InternalIP() (string, error) { + return c.getTrimmed("instance/network-interfaces/0/ip") +} + +// ExternalIP returns the instance's primary external (public) IP address. +func (c *Client) ExternalIP() (string, error) { + return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip") +} + +// Hostname returns the instance's hostname. This will be of the form +// ".c..internal". +func (c *Client) Hostname() (string, error) { + return c.getTrimmed("instance/hostname") +} + +// InstanceTags returns the list of user-defined instance tags, +// assigned when initially creating a GCE instance. +func (c *Client) InstanceTags() ([]string, error) { + var s []string + j, err := c.Get("instance/tags") + if err != nil { + return nil, err + } + if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil { + return nil, err + } + return s, nil +} + +// InstanceName returns the current VM's instance ID string. +func (c *Client) InstanceName() (string, error) { + host, err := c.Hostname() + if err != nil { + return "", err + } + return strings.Split(host, ".")[0], nil +} + +// Zone returns the current VM's zone, such as "us-central1-b". +func (c *Client) Zone() (string, error) { + zone, err := c.getTrimmed("instance/zone") + // zone is of the form "projects//zones/". + if err != nil { + return "", err + } + return zone[strings.LastIndex(zone, "/")+1:], nil +} + +// InstanceAttributes returns the list of user-defined attributes, +// assigned when initially creating a GCE VM instance. The value of an +// attribute can be obtained with InstanceAttributeValue. +func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") } + +// ProjectAttributes returns the list of user-defined attributes +// applying to the project as a whole, not just this VM. The value of +// an attribute can be obtained with ProjectAttributeValue. +func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") } + +// InstanceAttributeValue returns the value of the provided VM +// instance attribute. +// +// If the requested attribute is not defined, the returned error will +// be of type NotDefinedError. +// +// InstanceAttributeValue may return ("", nil) if the attribute was +// defined to be the empty string. +func (c *Client) InstanceAttributeValue(attr string) (string, error) { + return c.Get("instance/attributes/" + attr) +} + +// ProjectAttributeValue returns the value of the provided +// project attribute. +// +// If the requested attribute is not defined, the returned error will +// be of type NotDefinedError. +// +// ProjectAttributeValue may return ("", nil) if the attribute was +// defined to be the empty string. +func (c *Client) ProjectAttributeValue(attr string) (string, error) { + return c.Get("project/attributes/" + attr) +} + +// Scopes returns the service account scopes for the given account. +// The account may be empty or the string "default" to use the instance's +// main account. +func (c *Client) Scopes(serviceAccount string) ([]string, error) { + if serviceAccount == "" { + serviceAccount = "default" + } + return c.lines("instance/service-accounts/" + serviceAccount + "/scopes") +} + +// Subscribe subscribes to a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// The suffix may contain query parameters. +// +// Subscribe calls fn with the latest metadata value indicated by the provided +// suffix. If the metadata value is deleted, fn is called with the empty string +// and ok false. Subscribe blocks until fn returns a non-nil error or the value +// is deleted. Subscribe returns the error value returned from the last call to +// fn, which may be nil when ok == false. +func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error { + const failedSubscribeSleep = time.Second * 5 + + // First check to see if the metadata value exists at all. + val, lastETag, err := c.getETag(suffix) + if err != nil { + return err + } + + if err := fn(val, true); err != nil { + return err + } + + ok := true + if strings.ContainsRune(suffix, '?') { + suffix += "&wait_for_change=true&last_etag=" + } else { + suffix += "?wait_for_change=true&last_etag=" + } + for { + val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag)) + if err != nil { + if _, deleted := err.(NotDefinedError); !deleted { + time.Sleep(failedSubscribeSleep) + continue // Retry on other errors. + } + ok = false + } + lastETag = etag + + if err := fn(val, ok); err != nil || !ok { + return err + } + } +} diff --git a/vendor/cloud.google.com/go/compute/metadata/metadata_test.go b/vendor/cloud.google.com/go/compute/metadata/metadata_test.go new file mode 100644 index 0000000000..83142b26bc --- /dev/null +++ b/vendor/cloud.google.com/go/compute/metadata/metadata_test.go @@ -0,0 +1,80 @@ +// 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 metadata + +import ( + "bytes" + "io/ioutil" + "net/http" + "os" + "sync" + "testing" +) + +func TestOnGCE_Stress(t *testing.T) { + if testing.Short() { + t.Skip("skipping in -short mode") + } + var last bool + for i := 0; i < 100; i++ { + onGCEOnce = sync.Once{} + + now := OnGCE() + if i > 0 && now != last { + t.Errorf("%d. changed from %v to %v", i, last, now) + } + last = now + } + t.Logf("OnGCE() = %v", last) +} + +func TestOnGCE_Force(t *testing.T) { + onGCEOnce = sync.Once{} + old := os.Getenv(metadataHostEnv) + defer os.Setenv(metadataHostEnv, old) + os.Setenv(metadataHostEnv, "127.0.0.1") + if !OnGCE() { + t.Error("OnGCE() = false; want true") + } +} + +func TestOverrideUserAgent(t *testing.T) { + const userAgent = "my-user-agent" + rt := &rrt{} + c := NewClient(&http.Client{Transport: userAgentTransport{userAgent, rt}}) + c.Get("foo") + if got, want := rt.gotUserAgent, userAgent; got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +type userAgentTransport struct { + userAgent string + base http.RoundTripper +} + +func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", t.userAgent) + return t.base.RoundTrip(req) +} + +type rrt struct { + gotUserAgent string +} + +func (r *rrt) RoundTrip(req *http.Request) (*http.Response, error) { + r.gotUserAgent = req.Header.Get("User-Agent") + return &http.Response{Body: ioutil.NopCloser(bytes.NewReader(nil))}, nil +} diff --git a/vendor/cloud.google.com/go/container/apiv1/ListClusters_smoke_test.go b/vendor/cloud.google.com/go/container/apiv1/ListClusters_smoke_test.go new file mode 100644 index 0000000000..a8e1af97ce --- /dev/null +++ b/vendor/cloud.google.com/go/container/apiv1/ListClusters_smoke_test.go @@ -0,0 +1,68 @@ +// 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 container + +import ( + containerpb "google.golang.org/genproto/googleapis/container/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestClusterManagerSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClusterManagerClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var projectId2 string = projectId + var zone string = "us-central1-a" + var request = &containerpb.ListClustersRequest{ + ProjectId: projectId2, + Zone: zone, + } + + if _, err := c.ListClusters(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/container/apiv1/cluster_manager_client.go b/vendor/cloud.google.com/go/container/apiv1/cluster_manager_client.go new file mode 100644 index 0000000000..335cbe114b --- /dev/null +++ b/vendor/cloud.google.com/go/container/apiv1/cluster_manager_client.go @@ -0,0 +1,676 @@ +// 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 container + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + containerpb "google.golang.org/genproto/googleapis/container/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ClusterManagerCallOptions contains the retry settings for each method of ClusterManagerClient. +type ClusterManagerCallOptions struct { + ListClusters []gax.CallOption + GetCluster []gax.CallOption + CreateCluster []gax.CallOption + UpdateCluster []gax.CallOption + UpdateNodePool []gax.CallOption + SetNodePoolAutoscaling []gax.CallOption + SetLoggingService []gax.CallOption + SetMonitoringService []gax.CallOption + SetAddonsConfig []gax.CallOption + SetLocations []gax.CallOption + UpdateMaster []gax.CallOption + SetMasterAuth []gax.CallOption + DeleteCluster []gax.CallOption + ListOperations []gax.CallOption + GetOperation []gax.CallOption + CancelOperation []gax.CallOption + GetServerConfig []gax.CallOption + ListNodePools []gax.CallOption + GetNodePool []gax.CallOption + CreateNodePool []gax.CallOption + DeleteNodePool []gax.CallOption + RollbackNodePoolUpgrade []gax.CallOption + SetNodePoolManagement []gax.CallOption + SetLabels []gax.CallOption + SetLegacyAbac []gax.CallOption + StartIPRotation []gax.CallOption + CompleteIPRotation []gax.CallOption + SetNodePoolSize []gax.CallOption + SetNetworkPolicy []gax.CallOption + SetMaintenancePolicy []gax.CallOption +} + +func defaultClusterManagerClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("container.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultClusterManagerCallOptions() *ClusterManagerCallOptions { + 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 &ClusterManagerCallOptions{ + ListClusters: retry[[2]string{"default", "idempotent"}], + GetCluster: retry[[2]string{"default", "idempotent"}], + CreateCluster: retry[[2]string{"default", "non_idempotent"}], + UpdateCluster: retry[[2]string{"default", "non_idempotent"}], + UpdateNodePool: retry[[2]string{"default", "non_idempotent"}], + SetNodePoolAutoscaling: retry[[2]string{"default", "non_idempotent"}], + SetLoggingService: retry[[2]string{"default", "non_idempotent"}], + SetMonitoringService: retry[[2]string{"default", "non_idempotent"}], + SetAddonsConfig: retry[[2]string{"default", "non_idempotent"}], + SetLocations: retry[[2]string{"default", "non_idempotent"}], + UpdateMaster: retry[[2]string{"default", "non_idempotent"}], + SetMasterAuth: retry[[2]string{"default", "non_idempotent"}], + DeleteCluster: retry[[2]string{"default", "idempotent"}], + ListOperations: retry[[2]string{"default", "idempotent"}], + GetOperation: retry[[2]string{"default", "idempotent"}], + CancelOperation: retry[[2]string{"default", "non_idempotent"}], + GetServerConfig: retry[[2]string{"default", "idempotent"}], + ListNodePools: retry[[2]string{"default", "idempotent"}], + GetNodePool: retry[[2]string{"default", "idempotent"}], + CreateNodePool: retry[[2]string{"default", "non_idempotent"}], + DeleteNodePool: retry[[2]string{"default", "idempotent"}], + RollbackNodePoolUpgrade: retry[[2]string{"default", "non_idempotent"}], + SetNodePoolManagement: retry[[2]string{"default", "non_idempotent"}], + SetLabels: retry[[2]string{"default", "non_idempotent"}], + SetLegacyAbac: retry[[2]string{"default", "non_idempotent"}], + StartIPRotation: retry[[2]string{"default", "non_idempotent"}], + CompleteIPRotation: retry[[2]string{"default", "non_idempotent"}], + SetNodePoolSize: retry[[2]string{"default", "non_idempotent"}], + SetNetworkPolicy: retry[[2]string{"default", "non_idempotent"}], + SetMaintenancePolicy: retry[[2]string{"default", "non_idempotent"}], + } +} + +// ClusterManagerClient is a client for interacting with Google Container Engine API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ClusterManagerClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + clusterManagerClient containerpb.ClusterManagerClient + + // The call options for this service. + CallOptions *ClusterManagerCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewClusterManagerClient creates a new cluster manager client. +// +// Google Container Engine Cluster Manager v1 +func NewClusterManagerClient(ctx context.Context, opts ...option.ClientOption) (*ClusterManagerClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultClusterManagerClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ClusterManagerClient{ + conn: conn, + CallOptions: defaultClusterManagerCallOptions(), + + clusterManagerClient: containerpb.NewClusterManagerClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ClusterManagerClient) 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 *ClusterManagerClient) 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 *ClusterManagerClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListClusters lists all clusters owned by a project in either the specified zone or all +// zones. +func (c *ClusterManagerClient) ListClusters(ctx context.Context, req *containerpb.ListClustersRequest, opts ...gax.CallOption) (*containerpb.ListClustersResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListClusters[0:len(c.CallOptions.ListClusters):len(c.CallOptions.ListClusters)], opts...) + var resp *containerpb.ListClustersResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.ListClusters(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetCluster gets the details of a specific cluster. +func (c *ClusterManagerClient) GetCluster(ctx context.Context, req *containerpb.GetClusterRequest, opts ...gax.CallOption) (*containerpb.Cluster, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetCluster[0:len(c.CallOptions.GetCluster):len(c.CallOptions.GetCluster)], opts...) + var resp *containerpb.Cluster + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.GetCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateCluster creates a cluster, consisting of the specified number and type of Google +// Compute Engine instances. +// +// By default, the cluster is created in the project's +// default network (at /compute/docs/networks-and-firewalls#networks). +// +// One firewall is added for the cluster. After cluster creation, +// the cluster creates routes for each node to allow the containers +// on that node to communicate with all other instances in the +// cluster. +// +// Finally, an entry is added to the project's global metadata indicating +// which CIDR range is being used by the cluster. +func (c *ClusterManagerClient) CreateCluster(ctx context.Context, req *containerpb.CreateClusterRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateCluster[0:len(c.CallOptions.CreateCluster):len(c.CallOptions.CreateCluster)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.CreateCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateCluster updates the settings of a specific cluster. +func (c *ClusterManagerClient) UpdateCluster(ctx context.Context, req *containerpb.UpdateClusterRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateCluster[0:len(c.CallOptions.UpdateCluster):len(c.CallOptions.UpdateCluster)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.UpdateCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateNodePool updates the version and/or image type of a specific node pool. +func (c *ClusterManagerClient) UpdateNodePool(ctx context.Context, req *containerpb.UpdateNodePoolRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateNodePool[0:len(c.CallOptions.UpdateNodePool):len(c.CallOptions.UpdateNodePool)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.UpdateNodePool(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetNodePoolAutoscaling sets the autoscaling settings of a specific node pool. +func (c *ClusterManagerClient) SetNodePoolAutoscaling(ctx context.Context, req *containerpb.SetNodePoolAutoscalingRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetNodePoolAutoscaling[0:len(c.CallOptions.SetNodePoolAutoscaling):len(c.CallOptions.SetNodePoolAutoscaling)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetNodePoolAutoscaling(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetLoggingService sets the logging service of a specific cluster. +func (c *ClusterManagerClient) SetLoggingService(ctx context.Context, req *containerpb.SetLoggingServiceRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetLoggingService[0:len(c.CallOptions.SetLoggingService):len(c.CallOptions.SetLoggingService)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetLoggingService(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetMonitoringService sets the monitoring service of a specific cluster. +func (c *ClusterManagerClient) SetMonitoringService(ctx context.Context, req *containerpb.SetMonitoringServiceRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetMonitoringService[0:len(c.CallOptions.SetMonitoringService):len(c.CallOptions.SetMonitoringService)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetMonitoringService(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetAddonsConfig sets the addons of a specific cluster. +func (c *ClusterManagerClient) SetAddonsConfig(ctx context.Context, req *containerpb.SetAddonsConfigRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetAddonsConfig[0:len(c.CallOptions.SetAddonsConfig):len(c.CallOptions.SetAddonsConfig)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetAddonsConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetLocations sets the locations of a specific cluster. +func (c *ClusterManagerClient) SetLocations(ctx context.Context, req *containerpb.SetLocationsRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetLocations[0:len(c.CallOptions.SetLocations):len(c.CallOptions.SetLocations)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetLocations(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateMaster updates the master of a specific cluster. +func (c *ClusterManagerClient) UpdateMaster(ctx context.Context, req *containerpb.UpdateMasterRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateMaster[0:len(c.CallOptions.UpdateMaster):len(c.CallOptions.UpdateMaster)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.UpdateMaster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetMasterAuth used to set master auth materials. Currently supports :- +// Changing the admin password of a specific cluster. +// This can be either via password generation or explicitly set the password. +func (c *ClusterManagerClient) SetMasterAuth(ctx context.Context, req *containerpb.SetMasterAuthRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetMasterAuth[0:len(c.CallOptions.SetMasterAuth):len(c.CallOptions.SetMasterAuth)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetMasterAuth(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteCluster deletes the cluster, including the Kubernetes endpoint and all worker +// nodes. +// +// Firewalls and routes that were configured during cluster creation +// are also deleted. +// +// Other Google Compute Engine resources that might be in use by the cluster +// (e.g. load balancer resources) will not be deleted if they weren't present +// at the initial create time. +func (c *ClusterManagerClient) DeleteCluster(ctx context.Context, req *containerpb.DeleteClusterRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteCluster[0:len(c.CallOptions.DeleteCluster):len(c.CallOptions.DeleteCluster)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.DeleteCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListOperations lists all operations in a project in a specific zone or all zones. +func (c *ClusterManagerClient) ListOperations(ctx context.Context, req *containerpb.ListOperationsRequest, opts ...gax.CallOption) (*containerpb.ListOperationsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListOperations[0:len(c.CallOptions.ListOperations):len(c.CallOptions.ListOperations)], opts...) + var resp *containerpb.ListOperationsResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.ListOperations(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetOperation gets the specified operation. +func (c *ClusterManagerClient) GetOperation(ctx context.Context, req *containerpb.GetOperationRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetOperation[0:len(c.CallOptions.GetOperation):len(c.CallOptions.GetOperation)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.GetOperation(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CancelOperation cancels the specified operation. +func (c *ClusterManagerClient) CancelOperation(ctx context.Context, req *containerpb.CancelOperationRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CancelOperation[0:len(c.CallOptions.CancelOperation):len(c.CallOptions.CancelOperation)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.clusterManagerClient.CancelOperation(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// GetServerConfig returns configuration info about the Container Engine service. +func (c *ClusterManagerClient) GetServerConfig(ctx context.Context, req *containerpb.GetServerConfigRequest, opts ...gax.CallOption) (*containerpb.ServerConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetServerConfig[0:len(c.CallOptions.GetServerConfig):len(c.CallOptions.GetServerConfig)], opts...) + var resp *containerpb.ServerConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.GetServerConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListNodePools lists the node pools for a cluster. +func (c *ClusterManagerClient) ListNodePools(ctx context.Context, req *containerpb.ListNodePoolsRequest, opts ...gax.CallOption) (*containerpb.ListNodePoolsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListNodePools[0:len(c.CallOptions.ListNodePools):len(c.CallOptions.ListNodePools)], opts...) + var resp *containerpb.ListNodePoolsResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.ListNodePools(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetNodePool retrieves the node pool requested. +func (c *ClusterManagerClient) GetNodePool(ctx context.Context, req *containerpb.GetNodePoolRequest, opts ...gax.CallOption) (*containerpb.NodePool, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetNodePool[0:len(c.CallOptions.GetNodePool):len(c.CallOptions.GetNodePool)], opts...) + var resp *containerpb.NodePool + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.GetNodePool(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateNodePool creates a node pool for a cluster. +func (c *ClusterManagerClient) CreateNodePool(ctx context.Context, req *containerpb.CreateNodePoolRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateNodePool[0:len(c.CallOptions.CreateNodePool):len(c.CallOptions.CreateNodePool)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.CreateNodePool(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteNodePool deletes a node pool from a cluster. +func (c *ClusterManagerClient) DeleteNodePool(ctx context.Context, req *containerpb.DeleteNodePoolRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteNodePool[0:len(c.CallOptions.DeleteNodePool):len(c.CallOptions.DeleteNodePool)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.DeleteNodePool(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// RollbackNodePoolUpgrade roll back the previously Aborted or Failed NodePool upgrade. +// This will be an no-op if the last upgrade successfully completed. +func (c *ClusterManagerClient) RollbackNodePoolUpgrade(ctx context.Context, req *containerpb.RollbackNodePoolUpgradeRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.RollbackNodePoolUpgrade[0:len(c.CallOptions.RollbackNodePoolUpgrade):len(c.CallOptions.RollbackNodePoolUpgrade)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.RollbackNodePoolUpgrade(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetNodePoolManagement sets the NodeManagement options for a node pool. +func (c *ClusterManagerClient) SetNodePoolManagement(ctx context.Context, req *containerpb.SetNodePoolManagementRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetNodePoolManagement[0:len(c.CallOptions.SetNodePoolManagement):len(c.CallOptions.SetNodePoolManagement)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetNodePoolManagement(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetLabels sets labels on a cluster. +func (c *ClusterManagerClient) SetLabels(ctx context.Context, req *containerpb.SetLabelsRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetLabels[0:len(c.CallOptions.SetLabels):len(c.CallOptions.SetLabels)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetLabels(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetLegacyAbac enables or disables the ABAC authorization mechanism on a cluster. +func (c *ClusterManagerClient) SetLegacyAbac(ctx context.Context, req *containerpb.SetLegacyAbacRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetLegacyAbac[0:len(c.CallOptions.SetLegacyAbac):len(c.CallOptions.SetLegacyAbac)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetLegacyAbac(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// StartIPRotation start master IP rotation. +func (c *ClusterManagerClient) StartIPRotation(ctx context.Context, req *containerpb.StartIPRotationRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.StartIPRotation[0:len(c.CallOptions.StartIPRotation):len(c.CallOptions.StartIPRotation)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.StartIPRotation(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CompleteIPRotation completes master IP rotation. +func (c *ClusterManagerClient) CompleteIPRotation(ctx context.Context, req *containerpb.CompleteIPRotationRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CompleteIPRotation[0:len(c.CallOptions.CompleteIPRotation):len(c.CallOptions.CompleteIPRotation)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.CompleteIPRotation(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetNodePoolSize sets the size of a specific node pool. +func (c *ClusterManagerClient) SetNodePoolSize(ctx context.Context, req *containerpb.SetNodePoolSizeRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetNodePoolSize[0:len(c.CallOptions.SetNodePoolSize):len(c.CallOptions.SetNodePoolSize)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetNodePoolSize(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetNetworkPolicy enables/Disables Network Policy for a cluster. +func (c *ClusterManagerClient) SetNetworkPolicy(ctx context.Context, req *containerpb.SetNetworkPolicyRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetNetworkPolicy[0:len(c.CallOptions.SetNetworkPolicy):len(c.CallOptions.SetNetworkPolicy)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetNetworkPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetMaintenancePolicy sets the maintenance policy for a cluster. +func (c *ClusterManagerClient) SetMaintenancePolicy(ctx context.Context, req *containerpb.SetMaintenancePolicyRequest, opts ...gax.CallOption) (*containerpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetMaintenancePolicy[0:len(c.CallOptions.SetMaintenancePolicy):len(c.CallOptions.SetMaintenancePolicy)], opts...) + var resp *containerpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterManagerClient.SetMaintenancePolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/container/apiv1/cluster_manager_client_example_test.go b/vendor/cloud.google.com/go/container/apiv1/cluster_manager_client_example_test.go new file mode 100644 index 0000000000..e76586a7f2 --- /dev/null +++ b/vendor/cloud.google.com/go/container/apiv1/cluster_manager_client_example_test.go @@ -0,0 +1,571 @@ +// 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 container_test + +import ( + "cloud.google.com/go/container/apiv1" + "golang.org/x/net/context" + containerpb "google.golang.org/genproto/googleapis/container/v1" +) + +func ExampleNewClusterManagerClient() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClusterManagerClient_ListClusters() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.ListClustersRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListClusters(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_GetCluster() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.GetClusterRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_CreateCluster() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.CreateClusterRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_UpdateCluster() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.UpdateClusterRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_UpdateNodePool() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.UpdateNodePoolRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateNodePool(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetNodePoolAutoscaling() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetNodePoolAutoscalingRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetNodePoolAutoscaling(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetLoggingService() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetLoggingServiceRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetLoggingService(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetMonitoringService() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetMonitoringServiceRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetMonitoringService(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetAddonsConfig() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetAddonsConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetAddonsConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetLocations() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetLocationsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetLocations(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_UpdateMaster() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.UpdateMasterRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateMaster(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetMasterAuth() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetMasterAuthRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetMasterAuth(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_DeleteCluster() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.DeleteClusterRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.DeleteCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_ListOperations() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.ListOperationsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListOperations(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_GetOperation() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.GetOperationRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetOperation(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_CancelOperation() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.CancelOperationRequest{ + // TODO: Fill request struct fields. + } + err = c.CancelOperation(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClusterManagerClient_GetServerConfig() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.GetServerConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetServerConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_ListNodePools() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.ListNodePoolsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListNodePools(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_GetNodePool() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.GetNodePoolRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetNodePool(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_CreateNodePool() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.CreateNodePoolRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateNodePool(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_DeleteNodePool() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.DeleteNodePoolRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.DeleteNodePool(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_RollbackNodePoolUpgrade() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.RollbackNodePoolUpgradeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.RollbackNodePoolUpgrade(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetNodePoolManagement() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetNodePoolManagementRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetNodePoolManagement(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetLabels() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetLabelsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetLabels(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetLegacyAbac() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetLegacyAbacRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetLegacyAbac(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_StartIPRotation() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.StartIPRotationRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.StartIPRotation(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_CompleteIPRotation() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.CompleteIPRotationRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CompleteIPRotation(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetNodePoolSize() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetNodePoolSizeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetNodePoolSize(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetNetworkPolicy() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetNetworkPolicyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetNetworkPolicy(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterManagerClient_SetMaintenancePolicy() { + ctx := context.Background() + c, err := container.NewClusterManagerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &containerpb.SetMaintenancePolicyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetMaintenancePolicy(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/container/apiv1/doc.go b/vendor/cloud.google.com/go/container/apiv1/doc.go new file mode 100644 index 0000000000..a96384251d --- /dev/null +++ b/vendor/cloud.google.com/go/container/apiv1/doc.go @@ -0,0 +1,48 @@ +// 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 container is an auto-generated package for the +// Google Container Engine API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// The Google Kubernetes Engine API is used for building and managing +// container +// based applications, powered by the open source Kubernetes technology. +package container // import "cloud.google.com/go/container/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/container/apiv1/mock_test.go b/vendor/cloud.google.com/go/container/apiv1/mock_test.go new file mode 100644 index 0000000000..002f7557f8 --- /dev/null +++ b/vendor/cloud.google.com/go/container/apiv1/mock_test.go @@ -0,0 +1,2912 @@ +// 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 container + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + containerpb "google.golang.org/genproto/googleapis/container/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockClusterManagerServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + containerpb.ClusterManagerServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockClusterManagerServer) ListClusters(ctx context.Context, req *containerpb.ListClustersRequest) (*containerpb.ListClustersResponse, 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].(*containerpb.ListClustersResponse), nil +} + +func (s *mockClusterManagerServer) GetCluster(ctx context.Context, req *containerpb.GetClusterRequest) (*containerpb.Cluster, 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].(*containerpb.Cluster), nil +} + +func (s *mockClusterManagerServer) CreateCluster(ctx context.Context, req *containerpb.CreateClusterRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) UpdateCluster(ctx context.Context, req *containerpb.UpdateClusterRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) UpdateNodePool(ctx context.Context, req *containerpb.UpdateNodePoolRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetNodePoolAutoscaling(ctx context.Context, req *containerpb.SetNodePoolAutoscalingRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetLoggingService(ctx context.Context, req *containerpb.SetLoggingServiceRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetMonitoringService(ctx context.Context, req *containerpb.SetMonitoringServiceRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetAddonsConfig(ctx context.Context, req *containerpb.SetAddonsConfigRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetLocations(ctx context.Context, req *containerpb.SetLocationsRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) UpdateMaster(ctx context.Context, req *containerpb.UpdateMasterRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetMasterAuth(ctx context.Context, req *containerpb.SetMasterAuthRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) DeleteCluster(ctx context.Context, req *containerpb.DeleteClusterRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) ListOperations(ctx context.Context, req *containerpb.ListOperationsRequest) (*containerpb.ListOperationsResponse, 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].(*containerpb.ListOperationsResponse), nil +} + +func (s *mockClusterManagerServer) GetOperation(ctx context.Context, req *containerpb.GetOperationRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) CancelOperation(ctx context.Context, req *containerpb.CancelOperationRequest) (*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 *mockClusterManagerServer) GetServerConfig(ctx context.Context, req *containerpb.GetServerConfigRequest) (*containerpb.ServerConfig, 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].(*containerpb.ServerConfig), nil +} + +func (s *mockClusterManagerServer) ListNodePools(ctx context.Context, req *containerpb.ListNodePoolsRequest) (*containerpb.ListNodePoolsResponse, 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].(*containerpb.ListNodePoolsResponse), nil +} + +func (s *mockClusterManagerServer) GetNodePool(ctx context.Context, req *containerpb.GetNodePoolRequest) (*containerpb.NodePool, 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].(*containerpb.NodePool), nil +} + +func (s *mockClusterManagerServer) CreateNodePool(ctx context.Context, req *containerpb.CreateNodePoolRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) DeleteNodePool(ctx context.Context, req *containerpb.DeleteNodePoolRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) RollbackNodePoolUpgrade(ctx context.Context, req *containerpb.RollbackNodePoolUpgradeRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetNodePoolManagement(ctx context.Context, req *containerpb.SetNodePoolManagementRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetLabels(ctx context.Context, req *containerpb.SetLabelsRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetLegacyAbac(ctx context.Context, req *containerpb.SetLegacyAbacRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) StartIPRotation(ctx context.Context, req *containerpb.StartIPRotationRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) CompleteIPRotation(ctx context.Context, req *containerpb.CompleteIPRotationRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetNodePoolSize(ctx context.Context, req *containerpb.SetNodePoolSizeRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetNetworkPolicy(ctx context.Context, req *containerpb.SetNetworkPolicyRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +func (s *mockClusterManagerServer) SetMaintenancePolicy(ctx context.Context, req *containerpb.SetMaintenancePolicyRequest) (*containerpb.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].(*containerpb.Operation), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockClusterManager mockClusterManagerServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + containerpb.RegisterClusterManagerServer(serv, &mockClusterManager) + + 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 TestClusterManagerListClusters(t *testing.T) { + var expectedResponse *containerpb.ListClustersResponse = &containerpb.ListClustersResponse{} + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var request = &containerpb.ListClustersRequest{ + ProjectId: projectId, + Zone: zone, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListClusters(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerListClustersError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var request = &containerpb.ListClustersRequest{ + ProjectId: projectId, + Zone: zone, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListClusters(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 TestClusterManagerGetCluster(t *testing.T) { + var name string = "name3373707" + var description string = "description-1724546052" + var initialNodeCount int32 = 1682564205 + var loggingService string = "loggingService-1700501035" + var monitoringService string = "monitoringService1469270462" + var network string = "network1843485230" + var clusterIpv4Cidr string = "clusterIpv4Cidr-141875831" + var subnetwork string = "subnetwork-1302785042" + var enableKubernetesAlpha bool = false + var labelFingerprint string = "labelFingerprint714995737" + var selfLink string = "selfLink-1691268851" + var zone2 string = "zone2-696322977" + var endpoint string = "endpoint1741102485" + var initialClusterVersion string = "initialClusterVersion-276373352" + var currentMasterVersion string = "currentMasterVersion-920953983" + var currentNodeVersion string = "currentNodeVersion-407476063" + var createTime string = "createTime-493574096" + var statusMessage string = "statusMessage-239442758" + var nodeIpv4CidrSize int32 = 1181176815 + var servicesIpv4Cidr string = "servicesIpv4Cidr1966438125" + var currentNodeCount int32 = 178977560 + var expireTime string = "expireTime-96179731" + var expectedResponse = &containerpb.Cluster{ + Name: name, + Description: description, + InitialNodeCount: initialNodeCount, + LoggingService: loggingService, + MonitoringService: monitoringService, + Network: network, + ClusterIpv4Cidr: clusterIpv4Cidr, + Subnetwork: subnetwork, + EnableKubernetesAlpha: enableKubernetesAlpha, + LabelFingerprint: labelFingerprint, + SelfLink: selfLink, + Zone: zone2, + Endpoint: endpoint, + InitialClusterVersion: initialClusterVersion, + CurrentMasterVersion: currentMasterVersion, + CurrentNodeVersion: currentNodeVersion, + CreateTime: createTime, + StatusMessage: statusMessage, + NodeIpv4CidrSize: nodeIpv4CidrSize, + ServicesIpv4Cidr: servicesIpv4Cidr, + CurrentNodeCount: currentNodeCount, + ExpireTime: expireTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.GetClusterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCluster(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerGetClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.GetClusterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCluster(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 TestClusterManagerCreateCluster(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var cluster *containerpb.Cluster = &containerpb.Cluster{} + var request = &containerpb.CreateClusterRequest{ + ProjectId: projectId, + Zone: zone, + Cluster: cluster, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateCluster(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerCreateClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var cluster *containerpb.Cluster = &containerpb.Cluster{} + var request = &containerpb.CreateClusterRequest{ + ProjectId: projectId, + Zone: zone, + Cluster: cluster, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateCluster(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 TestClusterManagerUpdateCluster(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var update *containerpb.ClusterUpdate = &containerpb.ClusterUpdate{} + var request = &containerpb.UpdateClusterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Update: update, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCluster(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerUpdateClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var update *containerpb.ClusterUpdate = &containerpb.ClusterUpdate{} + var request = &containerpb.UpdateClusterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Update: update, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCluster(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 TestClusterManagerUpdateNodePool(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var nodeVersion string = "nodeVersion1790136219" + var imageType string = "imageType-1442758754" + var request = &containerpb.UpdateNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + NodeVersion: nodeVersion, + ImageType: imageType, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateNodePool(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerUpdateNodePoolError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var nodeVersion string = "nodeVersion1790136219" + var imageType string = "imageType-1442758754" + var request = &containerpb.UpdateNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + NodeVersion: nodeVersion, + ImageType: imageType, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateNodePool(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 TestClusterManagerSetNodePoolAutoscaling(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var autoscaling *containerpb.NodePoolAutoscaling = &containerpb.NodePoolAutoscaling{} + var request = &containerpb.SetNodePoolAutoscalingRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + Autoscaling: autoscaling, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNodePoolAutoscaling(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetNodePoolAutoscalingError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var autoscaling *containerpb.NodePoolAutoscaling = &containerpb.NodePoolAutoscaling{} + var request = &containerpb.SetNodePoolAutoscalingRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + Autoscaling: autoscaling, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNodePoolAutoscaling(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 TestClusterManagerSetLoggingService(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var loggingService string = "loggingService-1700501035" + var request = &containerpb.SetLoggingServiceRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + LoggingService: loggingService, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLoggingService(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetLoggingServiceError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var loggingService string = "loggingService-1700501035" + var request = &containerpb.SetLoggingServiceRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + LoggingService: loggingService, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLoggingService(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 TestClusterManagerSetMonitoringService(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var monitoringService string = "monitoringService1469270462" + var request = &containerpb.SetMonitoringServiceRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + MonitoringService: monitoringService, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetMonitoringService(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetMonitoringServiceError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var monitoringService string = "monitoringService1469270462" + var request = &containerpb.SetMonitoringServiceRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + MonitoringService: monitoringService, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetMonitoringService(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 TestClusterManagerSetAddonsConfig(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var addonsConfig *containerpb.AddonsConfig = &containerpb.AddonsConfig{} + var request = &containerpb.SetAddonsConfigRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + AddonsConfig: addonsConfig, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetAddonsConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetAddonsConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var addonsConfig *containerpb.AddonsConfig = &containerpb.AddonsConfig{} + var request = &containerpb.SetAddonsConfigRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + AddonsConfig: addonsConfig, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetAddonsConfig(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 TestClusterManagerSetLocations(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var locations []string = nil + var request = &containerpb.SetLocationsRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Locations: locations, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLocations(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetLocationsError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var locations []string = nil + var request = &containerpb.SetLocationsRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Locations: locations, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLocations(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 TestClusterManagerUpdateMaster(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var masterVersion string = "masterVersion-2139460613" + var request = &containerpb.UpdateMasterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + MasterVersion: masterVersion, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateMaster(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerUpdateMasterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var masterVersion string = "masterVersion-2139460613" + var request = &containerpb.UpdateMasterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + MasterVersion: masterVersion, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateMaster(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 TestClusterManagerSetMasterAuth(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var action containerpb.SetMasterAuthRequest_Action = containerpb.SetMasterAuthRequest_UNKNOWN + var update *containerpb.MasterAuth = &containerpb.MasterAuth{} + var request = &containerpb.SetMasterAuthRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Action: action, + Update: update, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetMasterAuth(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetMasterAuthError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var action containerpb.SetMasterAuthRequest_Action = containerpb.SetMasterAuthRequest_UNKNOWN + var update *containerpb.MasterAuth = &containerpb.MasterAuth{} + var request = &containerpb.SetMasterAuthRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Action: action, + Update: update, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetMasterAuth(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 TestClusterManagerDeleteCluster(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.DeleteClusterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeleteCluster(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerDeleteClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.DeleteClusterRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeleteCluster(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 TestClusterManagerListOperations(t *testing.T) { + var expectedResponse *containerpb.ListOperationsResponse = &containerpb.ListOperationsResponse{} + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var request = &containerpb.ListOperationsRequest{ + ProjectId: projectId, + Zone: zone, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListOperations(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerListOperationsError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var request = &containerpb.ListOperationsRequest{ + ProjectId: projectId, + Zone: zone, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListOperations(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 TestClusterManagerGetOperation(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var operationId string = "operationId-274116877" + var request = &containerpb.GetOperationRequest{ + ProjectId: projectId, + Zone: zone, + OperationId: operationId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetOperation(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerGetOperationError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var operationId string = "operationId-274116877" + var request = &containerpb.GetOperationRequest{ + ProjectId: projectId, + Zone: zone, + OperationId: operationId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetOperation(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 TestClusterManagerCancelOperation(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var operationId string = "operationId-274116877" + var request = &containerpb.CancelOperationRequest{ + ProjectId: projectId, + Zone: zone, + OperationId: operationId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CancelOperation(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestClusterManagerCancelOperationError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var operationId string = "operationId-274116877" + var request = &containerpb.CancelOperationRequest{ + ProjectId: projectId, + Zone: zone, + OperationId: operationId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CancelOperation(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 TestClusterManagerGetServerConfig(t *testing.T) { + var defaultClusterVersion string = "defaultClusterVersion111003029" + var defaultImageType string = "defaultImageType-918225828" + var expectedResponse = &containerpb.ServerConfig{ + DefaultClusterVersion: defaultClusterVersion, + DefaultImageType: defaultImageType, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var request = &containerpb.GetServerConfigRequest{ + ProjectId: projectId, + Zone: zone, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetServerConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerGetServerConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var request = &containerpb.GetServerConfigRequest{ + ProjectId: projectId, + Zone: zone, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetServerConfig(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 TestClusterManagerListNodePools(t *testing.T) { + var expectedResponse *containerpb.ListNodePoolsResponse = &containerpb.ListNodePoolsResponse{} + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.ListNodePoolsRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListNodePools(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerListNodePoolsError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.ListNodePoolsRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListNodePools(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 TestClusterManagerGetNodePool(t *testing.T) { + var name string = "name3373707" + var initialNodeCount int32 = 1682564205 + var selfLink string = "selfLink-1691268851" + var version string = "version351608024" + var statusMessage string = "statusMessage-239442758" + var expectedResponse = &containerpb.NodePool{ + Name: name, + InitialNodeCount: initialNodeCount, + SelfLink: selfLink, + Version: version, + StatusMessage: statusMessage, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var request = &containerpb.GetNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetNodePool(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerGetNodePoolError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var request = &containerpb.GetNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetNodePool(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 TestClusterManagerCreateNodePool(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePool *containerpb.NodePool = &containerpb.NodePool{} + var request = &containerpb.CreateNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePool: nodePool, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateNodePool(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerCreateNodePoolError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePool *containerpb.NodePool = &containerpb.NodePool{} + var request = &containerpb.CreateNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePool: nodePool, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateNodePool(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 TestClusterManagerDeleteNodePool(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var request = &containerpb.DeleteNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeleteNodePool(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerDeleteNodePoolError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var request = &containerpb.DeleteNodePoolRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeleteNodePool(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 TestClusterManagerRollbackNodePoolUpgrade(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var request = &containerpb.RollbackNodePoolUpgradeRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RollbackNodePoolUpgrade(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerRollbackNodePoolUpgradeError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var request = &containerpb.RollbackNodePoolUpgradeRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RollbackNodePoolUpgrade(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 TestClusterManagerSetNodePoolManagement(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var management *containerpb.NodeManagement = &containerpb.NodeManagement{} + var request = &containerpb.SetNodePoolManagementRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + Management: management, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNodePoolManagement(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetNodePoolManagementError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var management *containerpb.NodeManagement = &containerpb.NodeManagement{} + var request = &containerpb.SetNodePoolManagementRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + Management: management, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNodePoolManagement(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 TestClusterManagerSetLabels(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var resourceLabels map[string]string = nil + var labelFingerprint string = "labelFingerprint714995737" + var request = &containerpb.SetLabelsRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + ResourceLabels: resourceLabels, + LabelFingerprint: labelFingerprint, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLabels(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetLabelsError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var resourceLabels map[string]string = nil + var labelFingerprint string = "labelFingerprint714995737" + var request = &containerpb.SetLabelsRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + ResourceLabels: resourceLabels, + LabelFingerprint: labelFingerprint, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLabels(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 TestClusterManagerSetLegacyAbac(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var enabled bool = false + var request = &containerpb.SetLegacyAbacRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Enabled: enabled, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLegacyAbac(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetLegacyAbacError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var enabled bool = false + var request = &containerpb.SetLegacyAbacRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + Enabled: enabled, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetLegacyAbac(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 TestClusterManagerStartIPRotation(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.StartIPRotationRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.StartIPRotation(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerStartIPRotationError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.StartIPRotationRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.StartIPRotation(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 TestClusterManagerCompleteIPRotation(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.CompleteIPRotationRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CompleteIPRotation(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerCompleteIPRotationError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var request = &containerpb.CompleteIPRotationRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CompleteIPRotation(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 TestClusterManagerSetNodePoolSize(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var nodeCount int32 = 1539922066 + var request = &containerpb.SetNodePoolSizeRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + NodeCount: nodeCount, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNodePoolSize(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetNodePoolSizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var nodePoolId string = "nodePoolId1043384033" + var nodeCount int32 = 1539922066 + var request = &containerpb.SetNodePoolSizeRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NodePoolId: nodePoolId, + NodeCount: nodeCount, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNodePoolSize(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 TestClusterManagerSetNetworkPolicy(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var networkPolicy *containerpb.NetworkPolicy = &containerpb.NetworkPolicy{} + var request = &containerpb.SetNetworkPolicyRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NetworkPolicy: networkPolicy, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNetworkPolicy(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetNetworkPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var networkPolicy *containerpb.NetworkPolicy = &containerpb.NetworkPolicy{} + var request = &containerpb.SetNetworkPolicyRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + NetworkPolicy: networkPolicy, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetNetworkPolicy(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 TestClusterManagerSetMaintenancePolicy(t *testing.T) { + var name string = "name3373707" + var zone2 string = "zone2-696322977" + var detail string = "detail-1335224239" + var statusMessage string = "statusMessage-239442758" + var selfLink string = "selfLink-1691268851" + var targetLink string = "targetLink-2084812312" + var startTime string = "startTime-1573145462" + var endTime string = "endTime1725551537" + var expectedResponse = &containerpb.Operation{ + Name: name, + Zone: zone2, + Detail: detail, + StatusMessage: statusMessage, + SelfLink: selfLink, + TargetLink: targetLink, + StartTime: startTime, + EndTime: endTime, + } + + mockClusterManager.err = nil + mockClusterManager.reqs = nil + + mockClusterManager.resps = append(mockClusterManager.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var maintenancePolicy *containerpb.MaintenancePolicy = &containerpb.MaintenancePolicy{} + var request = &containerpb.SetMaintenancePolicyRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + MaintenancePolicy: maintenancePolicy, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetMaintenancePolicy(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterManager.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 TestClusterManagerSetMaintenancePolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterManager.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var zone string = "zone3744684" + var clusterId string = "clusterId240280960" + var maintenancePolicy *containerpb.MaintenancePolicy = &containerpb.MaintenancePolicy{} + var request = &containerpb.SetMaintenancePolicyRequest{ + ProjectId: projectId, + Zone: zone, + ClusterId: clusterId, + MaintenancePolicy: maintenancePolicy, + } + + c, err := NewClusterManagerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetMaintenancePolicy(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/vendor/cloud.google.com/go/container/container.go b/vendor/cloud.google.com/go/container/container.go new file mode 100644 index 0000000000..5301009117 --- /dev/null +++ b/vendor/cloud.google.com/go/container/container.go @@ -0,0 +1,266 @@ +// 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 container contains a deprecated Google Container Engine client. +// +// Deprecated: Use cloud.google.com/go/container/apiv1 instead. +package container // import "cloud.google.com/go/container" + +import ( + "errors" + "fmt" + "time" + + "golang.org/x/net/context" + raw "google.golang.org/api/container/v1" + "google.golang.org/api/option" + htransport "google.golang.org/api/transport/http" +) + +type Type string + +const ( + TypeCreate = Type("createCluster") + TypeDelete = Type("deleteCluster") +) + +type Status string + +const ( + StatusDone = Status("done") + StatusPending = Status("pending") + StatusRunning = Status("running") + StatusError = Status("error") + StatusProvisioning = Status("provisioning") + StatusStopping = Status("stopping") +) + +const prodAddr = "https://container.googleapis.com/" +const userAgent = "gcloud-golang-container/20151008" + +// Client is a Google Container Engine client, which may be used to manage +// clusters with a project. It must be constructed via NewClient. +type Client struct { + projectID string + svc *raw.Service +} + +// NewClient creates a new Google Container Engine client. +func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) { + o := []option.ClientOption{ + option.WithEndpoint(prodAddr), + option.WithScopes(raw.CloudPlatformScope), + option.WithUserAgent(userAgent), + } + o = append(o, opts...) + httpClient, endpoint, err := htransport.NewClient(ctx, o...) + if err != nil { + return nil, fmt.Errorf("dialing: %v", err) + } + + svc, err := raw.New(httpClient) + if err != nil { + return nil, fmt.Errorf("constructing container client: %v", err) + } + svc.BasePath = endpoint + + c := &Client{ + projectID: projectID, + svc: svc, + } + + return c, nil +} + +// Resource is a Google Container Engine cluster resource. +type Resource struct { + // Name is the name of this cluster. The name must be unique + // within this project and zone, and can be up to 40 characters. + Name string + + // Description is the description of the cluster. Optional. + Description string + + // Zone is the Google Compute Engine zone in which the cluster resides. + Zone string + + // Status is the current status of the cluster. It could either be + // StatusError, StatusProvisioning, StatusRunning or StatusStopping. + Status Status + + // Num is the number of the nodes in this cluster resource. + Num int64 + + // APIVersion is the version of the Kubernetes master and kubelets running + // in this cluster. Allowed value is 0.4.2, or leave blank to + // pick up the latest stable release. + APIVersion string + + // Endpoint is the IP address of this cluster's Kubernetes master. + // The endpoint can be accessed at https://username:password@endpoint/. + // See Username and Password fields for the username and password information. + Endpoint string + + // Username is the username to use when accessing the Kubernetes master endpoint. + Username string + + // Password is the password to use when accessing the Kubernetes master endpoint. + Password string + + // ContainerIPv4CIDR is the IP addresses of the container pods in + // this cluster, in CIDR notation (e.g. 1.2.3.4/29). + ContainerIPv4CIDR string + + // ServicesIPv4CIDR is the IP addresses of the Kubernetes services in this + // cluster, in CIDR notation (e.g. 1.2.3.4/29). Service addresses are + // always in the 10.0.0.0/16 range. + ServicesIPv4CIDR string + + // MachineType is a Google Compute Engine machine type (e.g. n1-standard-1). + // If none set, the default type is used while creating a new cluster. + MachineType string + + // This field is ignored. It was removed from the underlying container API in v1. + SourceImage string + + // Created is the creation time of this cluster. + Created time.Time +} + +func resourceFromRaw(c *raw.Cluster) *Resource { + if c == nil { + return nil + } + r := &Resource{ + Name: c.Name, + Description: c.Description, + Zone: c.Zone, + Status: Status(c.Status), + Num: c.CurrentNodeCount, + APIVersion: c.InitialClusterVersion, + Endpoint: c.Endpoint, + Username: c.MasterAuth.Username, + Password: c.MasterAuth.Password, + ContainerIPv4CIDR: c.ClusterIpv4Cidr, + ServicesIPv4CIDR: c.ServicesIpv4Cidr, + MachineType: c.NodeConfig.MachineType, + } + r.Created, _ = time.Parse(time.RFC3339, c.CreateTime) + return r +} + +func resourcesFromRaw(c []*raw.Cluster) []*Resource { + r := make([]*Resource, len(c)) + for i, val := range c { + r[i] = resourceFromRaw(val) + } + return r +} + +// Op represents a Google Container Engine API operation. +type Op struct { + // Name is the name of the operation. + Name string + + // Zone is the Google Compute Engine zone. + Zone string + + // This field is ignored. It was removed from the underlying container API in v1. + TargetURL string + + // Type is the operation type. It could be either be TypeCreate or TypeDelete. + Type Type + + // Status is the current status of this operation. It could be either + // OpDone or OpPending. + Status Status +} + +func opFromRaw(o *raw.Operation) *Op { + if o == nil { + return nil + } + return &Op{ + Name: o.Name, + Zone: o.Zone, + Type: Type(o.OperationType), + Status: Status(o.Status), + } +} + +func opsFromRaw(o []*raw.Operation) []*Op { + ops := make([]*Op, len(o)) + for i, val := range o { + ops[i] = opFromRaw(val) + } + return ops +} + +// Clusters returns a list of cluster resources from the specified zone. +// If no zone is specified, it returns all clusters under the user project. +func (c *Client) Clusters(ctx context.Context, zone string) ([]*Resource, error) { + if zone == "" { + zone = "-" + } + resp, err := c.svc.Projects.Zones.Clusters.List(c.projectID, zone).Do() + if err != nil { + return nil, err + } + return resourcesFromRaw(resp.Clusters), nil +} + +// Cluster returns metadata about the specified cluster. +func (c *Client) Cluster(ctx context.Context, zone, name string) (*Resource, error) { + resp, err := c.svc.Projects.Zones.Clusters.Get(c.projectID, zone, name).Do() + if err != nil { + return nil, err + } + return resourceFromRaw(resp), nil +} + +// DeleteCluster deletes a cluster. +func (c *Client) DeleteCluster(ctx context.Context, zone, name string) error { + _, err := c.svc.Projects.Zones.Clusters.Delete(c.projectID, zone, name).Do() + return err +} + +// Operations returns a list of operations from the specified zone. +// If no zone is specified, it looks up for all of the operations +// that are running under the user's project. +func (c *Client) Operations(ctx context.Context, zone string) ([]*Op, error) { + if zone == "" { + resp, err := c.svc.Projects.Zones.Operations.List(c.projectID, "-").Do() + if err != nil { + return nil, err + } + return opsFromRaw(resp.Operations), nil + } + resp, err := c.svc.Projects.Zones.Operations.List(c.projectID, zone).Do() + if err != nil { + return nil, err + } + return opsFromRaw(resp.Operations), nil +} + +// Operation returns an operation. +func (c *Client) Operation(ctx context.Context, zone, name string) (*Op, error) { + resp, err := c.svc.Projects.Zones.Operations.Get(c.projectID, zone, name).Do() + if err != nil { + return nil, err + } + if resp.StatusMessage != "" { + return nil, errors.New(resp.StatusMessage) + } + return opFromRaw(resp), nil +} diff --git a/vendor/cloud.google.com/go/dataproc/apiv1/ListClusters_smoke_test.go b/vendor/cloud.google.com/go/dataproc/apiv1/ListClusters_smoke_test.go new file mode 100644 index 0000000000..732c4b0b8a --- /dev/null +++ b/vendor/cloud.google.com/go/dataproc/apiv1/ListClusters_smoke_test.go @@ -0,0 +1,69 @@ +// 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 dataproc + +import ( + dataprocpb "google.golang.org/genproto/googleapis/cloud/dataproc/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestClusterControllerSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClusterControllerClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var projectId2 string = projectId + var region string = "global" + var request = &dataprocpb.ListClustersRequest{ + ProjectId: projectId2, + Region: region, + } + + iter := c.ListClusters(ctx, request) + if _, err := iter.Next(); err != nil && err != iterator.Done { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/dataproc/apiv1/cluster_controller_client.go b/vendor/cloud.google.com/go/dataproc/apiv1/cluster_controller_client.go new file mode 100644 index 0000000000..2b209cc89c --- /dev/null +++ b/vendor/cloud.google.com/go/dataproc/apiv1/cluster_controller_client.go @@ -0,0 +1,598 @@ +// 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 dataproc + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dataprocpb "google.golang.org/genproto/googleapis/cloud/dataproc/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ClusterControllerCallOptions contains the retry settings for each method of ClusterControllerClient. +type ClusterControllerCallOptions struct { + CreateCluster []gax.CallOption + UpdateCluster []gax.CallOption + DeleteCluster []gax.CallOption + GetCluster []gax.CallOption + ListClusters []gax.CallOption + DiagnoseCluster []gax.CallOption +} + +func defaultClusterControllerClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dataproc.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultClusterControllerCallOptions() *ClusterControllerCallOptions { + 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 &ClusterControllerCallOptions{ + CreateCluster: retry[[2]string{"default", "non_idempotent"}], + UpdateCluster: retry[[2]string{"default", "non_idempotent"}], + DeleteCluster: retry[[2]string{"default", "idempotent"}], + GetCluster: retry[[2]string{"default", "idempotent"}], + ListClusters: retry[[2]string{"default", "idempotent"}], + DiagnoseCluster: retry[[2]string{"default", "non_idempotent"}], + } +} + +// ClusterControllerClient is a client for interacting with Google Cloud Dataproc API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ClusterControllerClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + clusterControllerClient dataprocpb.ClusterControllerClient + + // 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 *ClusterControllerCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewClusterControllerClient creates a new cluster controller client. +// +// The ClusterControllerService provides methods to manage clusters +// of Google Compute Engine instances. +func NewClusterControllerClient(ctx context.Context, opts ...option.ClientOption) (*ClusterControllerClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultClusterControllerClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ClusterControllerClient{ + conn: conn, + CallOptions: defaultClusterControllerCallOptions(), + + clusterControllerClient: dataprocpb.NewClusterControllerClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ClusterControllerClient) 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 *ClusterControllerClient) 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 *ClusterControllerClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// CreateCluster creates a cluster in a project. +func (c *ClusterControllerClient) CreateCluster(ctx context.Context, req *dataprocpb.CreateClusterRequest, opts ...gax.CallOption) (*CreateClusterOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateCluster[0:len(c.CallOptions.CreateCluster):len(c.CallOptions.CreateCluster)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterControllerClient.CreateCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &CreateClusterOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// UpdateCluster updates a cluster in a project. +func (c *ClusterControllerClient) UpdateCluster(ctx context.Context, req *dataprocpb.UpdateClusterRequest, opts ...gax.CallOption) (*UpdateClusterOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateCluster[0:len(c.CallOptions.UpdateCluster):len(c.CallOptions.UpdateCluster)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterControllerClient.UpdateCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &UpdateClusterOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// DeleteCluster deletes a cluster in a project. +func (c *ClusterControllerClient) DeleteCluster(ctx context.Context, req *dataprocpb.DeleteClusterRequest, opts ...gax.CallOption) (*DeleteClusterOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteCluster[0:len(c.CallOptions.DeleteCluster):len(c.CallOptions.DeleteCluster)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterControllerClient.DeleteCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &DeleteClusterOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// GetCluster gets the resource representation for a cluster in a project. +func (c *ClusterControllerClient) GetCluster(ctx context.Context, req *dataprocpb.GetClusterRequest, opts ...gax.CallOption) (*dataprocpb.Cluster, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetCluster[0:len(c.CallOptions.GetCluster):len(c.CallOptions.GetCluster)], opts...) + var resp *dataprocpb.Cluster + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterControllerClient.GetCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListClusters lists all regions/{region}/clusters in a project. +func (c *ClusterControllerClient) ListClusters(ctx context.Context, req *dataprocpb.ListClustersRequest, opts ...gax.CallOption) *ClusterIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListClusters[0:len(c.CallOptions.ListClusters):len(c.CallOptions.ListClusters)], opts...) + it := &ClusterIterator{} + req = proto.Clone(req).(*dataprocpb.ListClustersRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dataprocpb.Cluster, string, error) { + var resp *dataprocpb.ListClustersResponse + 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.clusterControllerClient.ListClusters(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Clusters, 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 +} + +// DiagnoseCluster gets cluster diagnostic information. +// After the operation completes, the Operation.response field +// contains DiagnoseClusterOutputLocation. +func (c *ClusterControllerClient) DiagnoseCluster(ctx context.Context, req *dataprocpb.DiagnoseClusterRequest, opts ...gax.CallOption) (*DiagnoseClusterOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DiagnoseCluster[0:len(c.CallOptions.DiagnoseCluster):len(c.CallOptions.DiagnoseCluster)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.clusterControllerClient.DiagnoseCluster(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &DiagnoseClusterOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// ClusterIterator manages a stream of *dataprocpb.Cluster. +type ClusterIterator struct { + items []*dataprocpb.Cluster + 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 []*dataprocpb.Cluster, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *ClusterIterator) 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 *ClusterIterator) Next() (*dataprocpb.Cluster, error) { + var item *dataprocpb.Cluster + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *ClusterIterator) bufLen() int { + return len(it.items) +} + +func (it *ClusterIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// CreateClusterOperation manages a long-running operation from CreateCluster. +type CreateClusterOperation struct { + lro *longrunning.Operation +} + +// CreateClusterOperation returns a new CreateClusterOperation from a given name. +// The name must be that of a previously created CreateClusterOperation, possibly from a different process. +func (c *ClusterControllerClient) CreateClusterOperation(name string) *CreateClusterOperation { + return &CreateClusterOperation{ + 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 *CreateClusterOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*dataprocpb.Cluster, error) { + var resp dataprocpb.Cluster + if err := op.lro.WaitWithInterval(ctx, &resp, 10000*time.Millisecond, 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 *CreateClusterOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*dataprocpb.Cluster, error) { + var resp dataprocpb.Cluster + 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 *CreateClusterOperation) Metadata() (*dataprocpb.ClusterOperationMetadata, error) { + var meta dataprocpb.ClusterOperationMetadata + 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 *CreateClusterOperation) 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 *CreateClusterOperation) Name() string { + return op.lro.Name() +} + +// Delete deletes a long-running operation. +// This method indicates that the client is no longer interested in the operation result. +// It does not cancel the operation. +func (op *CreateClusterOperation) Delete(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Delete(ctx, opts...) +} + +// DeleteClusterOperation manages a long-running operation from DeleteCluster. +type DeleteClusterOperation struct { + lro *longrunning.Operation +} + +// DeleteClusterOperation returns a new DeleteClusterOperation from a given name. +// The name must be that of a previously created DeleteClusterOperation, possibly from a different process. +func (c *ClusterControllerClient) DeleteClusterOperation(name string) *DeleteClusterOperation { + return &DeleteClusterOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *DeleteClusterOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 10000*time.Millisecond, opts...) +} + +// 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. +func (op *DeleteClusterOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *DeleteClusterOperation) Metadata() (*dataprocpb.ClusterOperationMetadata, error) { + var meta dataprocpb.ClusterOperationMetadata + 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 *DeleteClusterOperation) 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 *DeleteClusterOperation) Name() string { + return op.lro.Name() +} + +// Delete deletes a long-running operation. +// This method indicates that the client is no longer interested in the operation result. +// It does not cancel the operation. +func (op *DeleteClusterOperation) Delete(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Delete(ctx, opts...) +} + +// DiagnoseClusterOperation manages a long-running operation from DiagnoseCluster. +type DiagnoseClusterOperation struct { + lro *longrunning.Operation +} + +// DiagnoseClusterOperation returns a new DiagnoseClusterOperation from a given name. +// The name must be that of a previously created DiagnoseClusterOperation, possibly from a different process. +func (c *ClusterControllerClient) DiagnoseClusterOperation(name string) *DiagnoseClusterOperation { + return &DiagnoseClusterOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *DiagnoseClusterOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 10000*time.Millisecond, opts...) +} + +// 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. +func (op *DiagnoseClusterOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *DiagnoseClusterOperation) Metadata() (*dataprocpb.DiagnoseClusterResults, error) { + var meta dataprocpb.DiagnoseClusterResults + 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 *DiagnoseClusterOperation) 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 *DiagnoseClusterOperation) Name() string { + return op.lro.Name() +} + +// Delete deletes a long-running operation. +// This method indicates that the client is no longer interested in the operation result. +// It does not cancel the operation. +func (op *DiagnoseClusterOperation) Delete(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Delete(ctx, opts...) +} + +// UpdateClusterOperation manages a long-running operation from UpdateCluster. +type UpdateClusterOperation struct { + lro *longrunning.Operation +} + +// UpdateClusterOperation returns a new UpdateClusterOperation from a given name. +// The name must be that of a previously created UpdateClusterOperation, possibly from a different process. +func (c *ClusterControllerClient) UpdateClusterOperation(name string) *UpdateClusterOperation { + return &UpdateClusterOperation{ + 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 *UpdateClusterOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*dataprocpb.Cluster, error) { + var resp dataprocpb.Cluster + if err := op.lro.WaitWithInterval(ctx, &resp, 10000*time.Millisecond, 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 *UpdateClusterOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*dataprocpb.Cluster, error) { + var resp dataprocpb.Cluster + 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 *UpdateClusterOperation) Metadata() (*dataprocpb.ClusterOperationMetadata, error) { + var meta dataprocpb.ClusterOperationMetadata + 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 *UpdateClusterOperation) 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 *UpdateClusterOperation) Name() string { + return op.lro.Name() +} + +// Delete deletes a long-running operation. +// This method indicates that the client is no longer interested in the operation result. +// It does not cancel the operation. +func (op *UpdateClusterOperation) Delete(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Delete(ctx, opts...) +} diff --git a/vendor/cloud.google.com/go/dataproc/apiv1/cluster_controller_client_example_test.go b/vendor/cloud.google.com/go/dataproc/apiv1/cluster_controller_client_example_test.go new file mode 100644 index 0000000000..7a99093602 --- /dev/null +++ b/vendor/cloud.google.com/go/dataproc/apiv1/cluster_controller_client_example_test.go @@ -0,0 +1,160 @@ +// 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 dataproc_test + +import ( + "cloud.google.com/go/dataproc/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dataprocpb "google.golang.org/genproto/googleapis/cloud/dataproc/v1" +) + +func ExampleNewClusterControllerClient() { + ctx := context.Background() + c, err := dataproc.NewClusterControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClusterControllerClient_CreateCluster() { + ctx := context.Background() + c, err := dataproc.NewClusterControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.CreateClusterRequest{ + // TODO: Fill request struct fields. + } + op, err := c.CreateCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterControllerClient_UpdateCluster() { + ctx := context.Background() + c, err := dataproc.NewClusterControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.UpdateClusterRequest{ + // TODO: Fill request struct fields. + } + op, err := c.UpdateCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterControllerClient_DeleteCluster() { + ctx := context.Background() + c, err := dataproc.NewClusterControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.DeleteClusterRequest{ + // TODO: Fill request struct fields. + } + op, err := c.DeleteCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} + +func ExampleClusterControllerClient_GetCluster() { + ctx := context.Background() + c, err := dataproc.NewClusterControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.GetClusterRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClusterControllerClient_ListClusters() { + ctx := context.Background() + c, err := dataproc.NewClusterControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.ListClustersRequest{ + // TODO: Fill request struct fields. + } + it := c.ListClusters(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClusterControllerClient_DiagnoseCluster() { + ctx := context.Background() + c, err := dataproc.NewClusterControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.DiagnoseClusterRequest{ + // TODO: Fill request struct fields. + } + op, err := c.DiagnoseCluster(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} diff --git a/vendor/cloud.google.com/go/dataproc/apiv1/doc.go b/vendor/cloud.google.com/go/dataproc/apiv1/doc.go new file mode 100644 index 0000000000..80c61eebb5 --- /dev/null +++ b/vendor/cloud.google.com/go/dataproc/apiv1/doc.go @@ -0,0 +1,46 @@ +// 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 dataproc is an auto-generated package for the +// Google Cloud Dataproc API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Manages Hadoop-based clusters and jobs on Google Cloud Platform. +package dataproc // import "cloud.google.com/go/dataproc/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/dataproc/apiv1/job_controller_client.go b/vendor/cloud.google.com/go/dataproc/apiv1/job_controller_client.go new file mode 100644 index 0000000000..4450677400 --- /dev/null +++ b/vendor/cloud.google.com/go/dataproc/apiv1/job_controller_client.go @@ -0,0 +1,290 @@ +// 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 dataproc + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dataprocpb "google.golang.org/genproto/googleapis/cloud/dataproc/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// JobControllerCallOptions contains the retry settings for each method of JobControllerClient. +type JobControllerCallOptions struct { + SubmitJob []gax.CallOption + GetJob []gax.CallOption + ListJobs []gax.CallOption + UpdateJob []gax.CallOption + CancelJob []gax.CallOption + DeleteJob []gax.CallOption +} + +func defaultJobControllerClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dataproc.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultJobControllerCallOptions() *JobControllerCallOptions { + 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 &JobControllerCallOptions{ + SubmitJob: retry[[2]string{"default", "non_idempotent"}], + GetJob: retry[[2]string{"default", "idempotent"}], + ListJobs: retry[[2]string{"default", "idempotent"}], + UpdateJob: retry[[2]string{"default", "non_idempotent"}], + CancelJob: retry[[2]string{"default", "non_idempotent"}], + DeleteJob: retry[[2]string{"default", "idempotent"}], + } +} + +// JobControllerClient is a client for interacting with Google Cloud Dataproc API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type JobControllerClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + jobControllerClient dataprocpb.JobControllerClient + + // The call options for this service. + CallOptions *JobControllerCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewJobControllerClient creates a new job controller client. +// +// The JobController provides methods to manage jobs. +func NewJobControllerClient(ctx context.Context, opts ...option.ClientOption) (*JobControllerClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultJobControllerClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &JobControllerClient{ + conn: conn, + CallOptions: defaultJobControllerCallOptions(), + + jobControllerClient: dataprocpb.NewJobControllerClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *JobControllerClient) 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 *JobControllerClient) 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 *JobControllerClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// SubmitJob submits a job to a cluster. +func (c *JobControllerClient) SubmitJob(ctx context.Context, req *dataprocpb.SubmitJobRequest, opts ...gax.CallOption) (*dataprocpb.Job, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SubmitJob[0:len(c.CallOptions.SubmitJob):len(c.CallOptions.SubmitJob)], opts...) + var resp *dataprocpb.Job + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.jobControllerClient.SubmitJob(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetJob gets the resource representation for a job in a project. +func (c *JobControllerClient) GetJob(ctx context.Context, req *dataprocpb.GetJobRequest, opts ...gax.CallOption) (*dataprocpb.Job, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetJob[0:len(c.CallOptions.GetJob):len(c.CallOptions.GetJob)], opts...) + var resp *dataprocpb.Job + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.jobControllerClient.GetJob(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListJobs lists regions/{region}/jobs in a project. +func (c *JobControllerClient) ListJobs(ctx context.Context, req *dataprocpb.ListJobsRequest, opts ...gax.CallOption) *JobIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListJobs[0:len(c.CallOptions.ListJobs):len(c.CallOptions.ListJobs)], opts...) + it := &JobIterator{} + req = proto.Clone(req).(*dataprocpb.ListJobsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dataprocpb.Job, string, error) { + var resp *dataprocpb.ListJobsResponse + 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.jobControllerClient.ListJobs(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Jobs, 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 +} + +// UpdateJob updates a job in a project. +func (c *JobControllerClient) UpdateJob(ctx context.Context, req *dataprocpb.UpdateJobRequest, opts ...gax.CallOption) (*dataprocpb.Job, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateJob[0:len(c.CallOptions.UpdateJob):len(c.CallOptions.UpdateJob)], opts...) + var resp *dataprocpb.Job + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.jobControllerClient.UpdateJob(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CancelJob starts a job cancellation request. To access the job resource +// after cancellation, call +// regions/{region}/jobs.list (at /dataproc/docs/reference/rest/v1/projects.regions.jobs/list) or +// regions/{region}/jobs.get (at /dataproc/docs/reference/rest/v1/projects.regions.jobs/get). +func (c *JobControllerClient) CancelJob(ctx context.Context, req *dataprocpb.CancelJobRequest, opts ...gax.CallOption) (*dataprocpb.Job, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CancelJob[0:len(c.CallOptions.CancelJob):len(c.CallOptions.CancelJob)], opts...) + var resp *dataprocpb.Job + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.jobControllerClient.CancelJob(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteJob deletes the job from the project. If the job is active, the delete fails, +// and the response returns FAILED_PRECONDITION. +func (c *JobControllerClient) DeleteJob(ctx context.Context, req *dataprocpb.DeleteJobRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteJob[0:len(c.CallOptions.DeleteJob):len(c.CallOptions.DeleteJob)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.jobControllerClient.DeleteJob(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// JobIterator manages a stream of *dataprocpb.Job. +type JobIterator struct { + items []*dataprocpb.Job + 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 []*dataprocpb.Job, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *JobIterator) 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 *JobIterator) Next() (*dataprocpb.Job, error) { + var item *dataprocpb.Job + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *JobIterator) bufLen() int { + return len(it.items) +} + +func (it *JobIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/dataproc/apiv1/job_controller_client_example_test.go b/vendor/cloud.google.com/go/dataproc/apiv1/job_controller_client_example_test.go new file mode 100644 index 0000000000..50c1f26566 --- /dev/null +++ b/vendor/cloud.google.com/go/dataproc/apiv1/job_controller_client_example_test.go @@ -0,0 +1,146 @@ +// 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 dataproc_test + +import ( + "cloud.google.com/go/dataproc/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dataprocpb "google.golang.org/genproto/googleapis/cloud/dataproc/v1" +) + +func ExampleNewJobControllerClient() { + ctx := context.Background() + c, err := dataproc.NewJobControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleJobControllerClient_SubmitJob() { + ctx := context.Background() + c, err := dataproc.NewJobControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.SubmitJobRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SubmitJob(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleJobControllerClient_GetJob() { + ctx := context.Background() + c, err := dataproc.NewJobControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.GetJobRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetJob(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleJobControllerClient_ListJobs() { + ctx := context.Background() + c, err := dataproc.NewJobControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.ListJobsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListJobs(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleJobControllerClient_UpdateJob() { + ctx := context.Background() + c, err := dataproc.NewJobControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.UpdateJobRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateJob(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleJobControllerClient_CancelJob() { + ctx := context.Background() + c, err := dataproc.NewJobControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.CancelJobRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CancelJob(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleJobControllerClient_DeleteJob() { + ctx := context.Background() + c, err := dataproc.NewJobControllerClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dataprocpb.DeleteJobRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteJob(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/dataproc/apiv1/mock_test.go b/vendor/cloud.google.com/go/dataproc/apiv1/mock_test.go new file mode 100644 index 0000000000..1f4669faf4 --- /dev/null +++ b/vendor/cloud.google.com/go/dataproc/apiv1/mock_test.go @@ -0,0 +1,1196 @@ +// 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 dataproc + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + dataprocpb "google.golang.org/genproto/googleapis/cloud/dataproc/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + field_maskpb "google.golang.org/genproto/protobuf/field_mask" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockClusterControllerServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dataprocpb.ClusterControllerServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockClusterControllerServer) CreateCluster(ctx context.Context, req *dataprocpb.CreateClusterRequest) (*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 *mockClusterControllerServer) UpdateCluster(ctx context.Context, req *dataprocpb.UpdateClusterRequest) (*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 *mockClusterControllerServer) DeleteCluster(ctx context.Context, req *dataprocpb.DeleteClusterRequest) (*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 *mockClusterControllerServer) GetCluster(ctx context.Context, req *dataprocpb.GetClusterRequest) (*dataprocpb.Cluster, 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].(*dataprocpb.Cluster), nil +} + +func (s *mockClusterControllerServer) ListClusters(ctx context.Context, req *dataprocpb.ListClustersRequest) (*dataprocpb.ListClustersResponse, 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].(*dataprocpb.ListClustersResponse), nil +} + +func (s *mockClusterControllerServer) DiagnoseCluster(ctx context.Context, req *dataprocpb.DiagnoseClusterRequest) (*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 +} + +type mockJobControllerServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dataprocpb.JobControllerServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockJobControllerServer) SubmitJob(ctx context.Context, req *dataprocpb.SubmitJobRequest) (*dataprocpb.Job, 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].(*dataprocpb.Job), nil +} + +func (s *mockJobControllerServer) GetJob(ctx context.Context, req *dataprocpb.GetJobRequest) (*dataprocpb.Job, 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].(*dataprocpb.Job), nil +} + +func (s *mockJobControllerServer) ListJobs(ctx context.Context, req *dataprocpb.ListJobsRequest) (*dataprocpb.ListJobsResponse, 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].(*dataprocpb.ListJobsResponse), nil +} + +func (s *mockJobControllerServer) UpdateJob(ctx context.Context, req *dataprocpb.UpdateJobRequest) (*dataprocpb.Job, 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].(*dataprocpb.Job), nil +} + +func (s *mockJobControllerServer) CancelJob(ctx context.Context, req *dataprocpb.CancelJobRequest) (*dataprocpb.Job, 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].(*dataprocpb.Job), nil +} + +func (s *mockJobControllerServer) DeleteJob(ctx context.Context, req *dataprocpb.DeleteJobRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockClusterController mockClusterControllerServer + mockJobController mockJobControllerServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + dataprocpb.RegisterClusterControllerServer(serv, &mockClusterController) + dataprocpb.RegisterJobControllerServer(serv, &mockJobController) + + 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 TestClusterControllerCreateCluster(t *testing.T) { + var projectId2 string = "projectId2939242356" + var clusterName string = "clusterName-1018081872" + var clusterUuid string = "clusterUuid-1017854240" + var expectedResponse = &dataprocpb.Cluster{ + ProjectId: projectId2, + ClusterName: clusterName, + ClusterUuid: clusterUuid, + } + + mockClusterController.err = nil + mockClusterController.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var cluster *dataprocpb.Cluster = &dataprocpb.Cluster{} + var request = &dataprocpb.CreateClusterRequest{ + ProjectId: projectId, + Region: region, + Cluster: cluster, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateCluster(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, mockClusterController.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 TestClusterControllerCreateClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterController.err = nil + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var cluster *dataprocpb.Cluster = &dataprocpb.Cluster{} + var request = &dataprocpb.CreateClusterRequest{ + ProjectId: projectId, + Region: region, + Cluster: cluster, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateCluster(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 TestClusterControllerUpdateCluster(t *testing.T) { + var projectId2 string = "projectId2939242356" + var clusterName2 string = "clusterName2875867491" + var clusterUuid string = "clusterUuid-1017854240" + var expectedResponse = &dataprocpb.Cluster{ + ProjectId: projectId2, + ClusterName: clusterName2, + ClusterUuid: clusterUuid, + } + + mockClusterController.err = nil + mockClusterController.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var cluster *dataprocpb.Cluster = &dataprocpb.Cluster{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &dataprocpb.UpdateClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + Cluster: cluster, + UpdateMask: updateMask, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateCluster(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, mockClusterController.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 TestClusterControllerUpdateClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterController.err = nil + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var cluster *dataprocpb.Cluster = &dataprocpb.Cluster{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &dataprocpb.UpdateClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + Cluster: cluster, + UpdateMask: updateMask, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateCluster(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 TestClusterControllerDeleteCluster(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockClusterController.err = nil + mockClusterController.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var request = &dataprocpb.DeleteClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.DeleteCluster(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterController.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestClusterControllerDeleteClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterController.err = nil + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var request = &dataprocpb.DeleteClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.DeleteCluster(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestClusterControllerGetCluster(t *testing.T) { + var projectId2 string = "projectId2939242356" + var clusterName2 string = "clusterName2875867491" + var clusterUuid string = "clusterUuid-1017854240" + var expectedResponse = &dataprocpb.Cluster{ + ProjectId: projectId2, + ClusterName: clusterName2, + ClusterUuid: clusterUuid, + } + + mockClusterController.err = nil + mockClusterController.reqs = nil + + mockClusterController.resps = append(mockClusterController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var request = &dataprocpb.GetClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCluster(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterController.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 TestClusterControllerGetClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var request = &dataprocpb.GetClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCluster(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 TestClusterControllerListClusters(t *testing.T) { + var nextPageToken string = "" + var clustersElement *dataprocpb.Cluster = &dataprocpb.Cluster{} + var clusters = []*dataprocpb.Cluster{clustersElement} + var expectedResponse = &dataprocpb.ListClustersResponse{ + NextPageToken: nextPageToken, + Clusters: clusters, + } + + mockClusterController.err = nil + mockClusterController.reqs = nil + + mockClusterController.resps = append(mockClusterController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var request = &dataprocpb.ListClustersRequest{ + ProjectId: projectId, + Region: region, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListClusters(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterController.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Clusters[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 TestClusterControllerListClustersError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var request = &dataprocpb.ListClustersRequest{ + ProjectId: projectId, + Region: region, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListClusters(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 TestClusterControllerDiagnoseCluster(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockClusterController.err = nil + mockClusterController.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var request = &dataprocpb.DiagnoseClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.DiagnoseCluster(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockClusterController.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestClusterControllerDiagnoseClusterError(t *testing.T) { + errCode := codes.PermissionDenied + mockClusterController.err = nil + mockClusterController.resps = append(mockClusterController.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var clusterName string = "clusterName-1018081872" + var request = &dataprocpb.DiagnoseClusterRequest{ + ProjectId: projectId, + Region: region, + ClusterName: clusterName, + } + + c, err := NewClusterControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.DiagnoseCluster(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestJobControllerSubmitJob(t *testing.T) { + var driverOutputResourceUri string = "driverOutputResourceUri-542229086" + var driverControlFilesUri string = "driverControlFilesUri207057643" + var expectedResponse = &dataprocpb.Job{ + DriverOutputResourceUri: driverOutputResourceUri, + DriverControlFilesUri: driverControlFilesUri, + } + + mockJobController.err = nil + mockJobController.reqs = nil + + mockJobController.resps = append(mockJobController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var job *dataprocpb.Job = &dataprocpb.Job{} + var request = &dataprocpb.SubmitJobRequest{ + ProjectId: projectId, + Region: region, + Job: job, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SubmitJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockJobController.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 TestJobControllerSubmitJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockJobController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var job *dataprocpb.Job = &dataprocpb.Job{} + var request = &dataprocpb.SubmitJobRequest{ + ProjectId: projectId, + Region: region, + Job: job, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SubmitJob(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 TestJobControllerGetJob(t *testing.T) { + var driverOutputResourceUri string = "driverOutputResourceUri-542229086" + var driverControlFilesUri string = "driverControlFilesUri207057643" + var expectedResponse = &dataprocpb.Job{ + DriverOutputResourceUri: driverOutputResourceUri, + DriverControlFilesUri: driverControlFilesUri, + } + + mockJobController.err = nil + mockJobController.reqs = nil + + mockJobController.resps = append(mockJobController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var request = &dataprocpb.GetJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockJobController.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 TestJobControllerGetJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockJobController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var request = &dataprocpb.GetJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetJob(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 TestJobControllerListJobs(t *testing.T) { + var nextPageToken string = "" + var jobsElement *dataprocpb.Job = &dataprocpb.Job{} + var jobs = []*dataprocpb.Job{jobsElement} + var expectedResponse = &dataprocpb.ListJobsResponse{ + NextPageToken: nextPageToken, + Jobs: jobs, + } + + mockJobController.err = nil + mockJobController.reqs = nil + + mockJobController.resps = append(mockJobController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var request = &dataprocpb.ListJobsRequest{ + ProjectId: projectId, + Region: region, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListJobs(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockJobController.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Jobs[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 TestJobControllerListJobsError(t *testing.T) { + errCode := codes.PermissionDenied + mockJobController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var request = &dataprocpb.ListJobsRequest{ + ProjectId: projectId, + Region: region, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListJobs(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 TestJobControllerUpdateJob(t *testing.T) { + var driverOutputResourceUri string = "driverOutputResourceUri-542229086" + var driverControlFilesUri string = "driverControlFilesUri207057643" + var expectedResponse = &dataprocpb.Job{ + DriverOutputResourceUri: driverOutputResourceUri, + DriverControlFilesUri: driverControlFilesUri, + } + + mockJobController.err = nil + mockJobController.reqs = nil + + mockJobController.resps = append(mockJobController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var job *dataprocpb.Job = &dataprocpb.Job{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &dataprocpb.UpdateJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + Job: job, + UpdateMask: updateMask, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockJobController.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 TestJobControllerUpdateJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockJobController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var job *dataprocpb.Job = &dataprocpb.Job{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &dataprocpb.UpdateJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + Job: job, + UpdateMask: updateMask, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateJob(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 TestJobControllerCancelJob(t *testing.T) { + var driverOutputResourceUri string = "driverOutputResourceUri-542229086" + var driverControlFilesUri string = "driverControlFilesUri207057643" + var expectedResponse = &dataprocpb.Job{ + DriverOutputResourceUri: driverOutputResourceUri, + DriverControlFilesUri: driverControlFilesUri, + } + + mockJobController.err = nil + mockJobController.reqs = nil + + mockJobController.resps = append(mockJobController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var request = &dataprocpb.CancelJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CancelJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockJobController.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 TestJobControllerCancelJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockJobController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var request = &dataprocpb.CancelJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CancelJob(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 TestJobControllerDeleteJob(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockJobController.err = nil + mockJobController.reqs = nil + + mockJobController.resps = append(mockJobController.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var request = &dataprocpb.DeleteJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockJobController.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestJobControllerDeleteJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockJobController.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var region string = "region-934795532" + var jobId string = "jobId-1154752291" + var request = &dataprocpb.DeleteJobRequest{ + ProjectId: projectId, + Region: region, + JobId: jobId, + } + + c, err := NewJobControllerClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteJob(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) + } +} diff --git a/vendor/cloud.google.com/go/datastore/client.go b/vendor/cloud.google.com/go/datastore/client.go new file mode 100644 index 0000000000..9596b4f172 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/client.go @@ -0,0 +1,118 @@ +// Copyright 2017 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 datastore + +import ( + "fmt" + + gax "github.com/googleapis/gax-go" + + "cloud.google.com/go/internal" + "cloud.google.com/go/internal/version" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/datastore/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// datastoreClient is a wrapper for the pb.DatastoreClient that includes gRPC +// metadata to be sent in each request for server-side traffic management. +type datastoreClient struct { + // Embed so we still implement the DatastoreClient interface, + // if the interface adds more methods. + pb.DatastoreClient + + c pb.DatastoreClient + md metadata.MD +} + +func newDatastoreClient(conn *grpc.ClientConn, projectID string) pb.DatastoreClient { + return &datastoreClient{ + c: pb.NewDatastoreClient(conn), + md: metadata.Pairs( + resourcePrefixHeader, "projects/"+projectID, + "x-goog-api-client", fmt.Sprintf("gl-go/%s gccl/%s grpc/", version.Go(), version.Repo)), + } +} + +func (dc *datastoreClient) Lookup(ctx context.Context, in *pb.LookupRequest, opts ...grpc.CallOption) (res *pb.LookupResponse, err error) { + err = dc.invoke(ctx, func(ctx context.Context) error { + res, err = dc.c.Lookup(ctx, in, opts...) + return err + }) + return res, err +} + +func (dc *datastoreClient) RunQuery(ctx context.Context, in *pb.RunQueryRequest, opts ...grpc.CallOption) (res *pb.RunQueryResponse, err error) { + err = dc.invoke(ctx, func(ctx context.Context) error { + res, err = dc.c.RunQuery(ctx, in, opts...) + return err + }) + return res, err +} + +func (dc *datastoreClient) BeginTransaction(ctx context.Context, in *pb.BeginTransactionRequest, opts ...grpc.CallOption) (res *pb.BeginTransactionResponse, err error) { + err = dc.invoke(ctx, func(ctx context.Context) error { + res, err = dc.c.BeginTransaction(ctx, in, opts...) + return err + }) + return res, err +} + +func (dc *datastoreClient) Commit(ctx context.Context, in *pb.CommitRequest, opts ...grpc.CallOption) (res *pb.CommitResponse, err error) { + err = dc.invoke(ctx, func(ctx context.Context) error { + res, err = dc.c.Commit(ctx, in, opts...) + return err + }) + return res, err +} + +func (dc *datastoreClient) Rollback(ctx context.Context, in *pb.RollbackRequest, opts ...grpc.CallOption) (res *pb.RollbackResponse, err error) { + err = dc.invoke(ctx, func(ctx context.Context) error { + res, err = dc.c.Rollback(ctx, in, opts...) + return err + }) + return res, err +} + +func (dc *datastoreClient) AllocateIds(ctx context.Context, in *pb.AllocateIdsRequest, opts ...grpc.CallOption) (res *pb.AllocateIdsResponse, err error) { + err = dc.invoke(ctx, func(ctx context.Context) error { + res, err = dc.c.AllocateIds(ctx, in, opts...) + return err + }) + return res, err +} + +func (dc *datastoreClient) invoke(ctx context.Context, f func(ctx context.Context) error) error { + ctx = metadata.NewOutgoingContext(ctx, dc.md) + return internal.Retry(ctx, gax.Backoff{}, func() (stop bool, err error) { + err = f(ctx) + return !shouldRetry(err), err + }) +} + +func shouldRetry(err error) bool { + if err == nil { + return false + } + s, ok := status.FromError(err) + if !ok { + return false + } + // See https://cloud.google.com/datastore/docs/concepts/errors. + return s.Code() == codes.Unavailable || s.Code() == codes.DeadlineExceeded +} diff --git a/vendor/cloud.google.com/go/datastore/datastore.go b/vendor/cloud.google.com/go/datastore/datastore.go new file mode 100644 index 0000000000..77091bca3a --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/datastore.go @@ -0,0 +1,627 @@ +// 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 datastore + +import ( + "errors" + "fmt" + "log" + "os" + "reflect" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + "google.golang.org/api/option" + gtransport "google.golang.org/api/transport/grpc" + pb "google.golang.org/genproto/googleapis/datastore/v1" + "google.golang.org/grpc" +) + +const ( + prodAddr = "datastore.googleapis.com:443" + userAgent = "gcloud-golang-datastore/20160401" +) + +// ScopeDatastore grants permissions to view and/or manage datastore entities +const ScopeDatastore = "https://www.googleapis.com/auth/datastore" + +// resourcePrefixHeader is the name of the metadata header used to indicate +// the resource being operated on. +const resourcePrefixHeader = "google-cloud-resource-prefix" + +// Client is a client for reading and writing data in a datastore dataset. +type Client struct { + conn *grpc.ClientConn + client pb.DatastoreClient + endpoint string + dataset string // Called dataset by the datastore API, synonym for project ID. +} + +// NewClient creates a new Client for a given dataset. +// If the project ID is empty, it is derived from the DATASTORE_PROJECT_ID environment variable. +// If the DATASTORE_EMULATOR_HOST environment variable is set, client will use its value +// to connect to a locally-running datastore emulator. +func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) { + var o []option.ClientOption + // Environment variables for gcd emulator: + // https://cloud.google.com/datastore/docs/tools/datastore-emulator + // If the emulator is available, dial it without passing any credentials. + if addr := os.Getenv("DATASTORE_EMULATOR_HOST"); addr != "" { + o = []option.ClientOption{ + option.WithEndpoint(addr), + option.WithoutAuthentication(), + option.WithGRPCDialOption(grpc.WithInsecure()), + } + } else { + o = []option.ClientOption{ + option.WithEndpoint(prodAddr), + option.WithScopes(ScopeDatastore), + option.WithUserAgent(userAgent), + } + } + // Warn if we see the legacy emulator environment variables. + if os.Getenv("DATASTORE_HOST") != "" && os.Getenv("DATASTORE_EMULATOR_HOST") == "" { + log.Print("WARNING: legacy environment variable DATASTORE_HOST is ignored. Use DATASTORE_EMULATOR_HOST instead.") + } + if os.Getenv("DATASTORE_DATASET") != "" && os.Getenv("DATASTORE_PROJECT_ID") == "" { + log.Print("WARNING: legacy environment variable DATASTORE_DATASET is ignored. Use DATASTORE_PROJECT_ID instead.") + } + if projectID == "" { + projectID = os.Getenv("DATASTORE_PROJECT_ID") + } + if projectID == "" { + return nil, errors.New("datastore: missing project/dataset id") + } + o = append(o, opts...) + conn, err := gtransport.Dial(ctx, o...) + if err != nil { + return nil, fmt.Errorf("dialing: %v", err) + } + return &Client{ + conn: conn, + client: newDatastoreClient(conn, projectID), + dataset: projectID, + }, nil + +} + +var ( + // ErrInvalidEntityType is returned when functions like Get or Next are + // passed a dst or src argument of invalid type. + ErrInvalidEntityType = errors.New("datastore: invalid entity type") + // ErrInvalidKey is returned when an invalid key is presented. + ErrInvalidKey = errors.New("datastore: invalid key") + // ErrNoSuchEntity is returned when no entity was found for a given key. + ErrNoSuchEntity = errors.New("datastore: no such entity") +) + +type multiArgType int + +const ( + multiArgTypeInvalid multiArgType = iota + multiArgTypePropertyLoadSaver + multiArgTypeStruct + multiArgTypeStructPtr + multiArgTypeInterface +) + +// ErrFieldMismatch is returned when a field is to be loaded into a different +// type than the one it was stored from, or when a field is missing or +// unexported in the destination struct. +// StructType is the type of the struct pointed to by the destination argument +// passed to Get or to Iterator.Next. +type ErrFieldMismatch struct { + StructType reflect.Type + FieldName string + Reason string +} + +func (e *ErrFieldMismatch) Error() string { + return fmt.Sprintf("datastore: cannot load field %q into a %q: %s", + e.FieldName, e.StructType, e.Reason) +} + +// GeoPoint represents a location as latitude/longitude in degrees. +type GeoPoint struct { + Lat, Lng float64 +} + +// Valid returns whether a GeoPoint is within [-90, 90] latitude and [-180, 180] longitude. +func (g GeoPoint) Valid() bool { + return -90 <= g.Lat && g.Lat <= 90 && -180 <= g.Lng && g.Lng <= 180 +} + +func keyToProto(k *Key) *pb.Key { + if k == nil { + return nil + } + + var path []*pb.Key_PathElement + for { + el := &pb.Key_PathElement{Kind: k.Kind} + if k.ID != 0 { + el.IdType = &pb.Key_PathElement_Id{Id: k.ID} + } else if k.Name != "" { + el.IdType = &pb.Key_PathElement_Name{Name: k.Name} + } + path = append(path, el) + if k.Parent == nil { + break + } + k = k.Parent + } + + // The path should be in order [grandparent, parent, child] + // We did it backward above, so reverse back. + for i := 0; i < len(path)/2; i++ { + path[i], path[len(path)-i-1] = path[len(path)-i-1], path[i] + } + + key := &pb.Key{Path: path} + if k.Namespace != "" { + key.PartitionId = &pb.PartitionId{ + NamespaceId: k.Namespace, + } + } + return key +} + +// protoToKey decodes a protocol buffer representation of a key into an +// equivalent *Key object. If the key is invalid, protoToKey will return the +// invalid key along with ErrInvalidKey. +func protoToKey(p *pb.Key) (*Key, error) { + var key *Key + var namespace string + if partition := p.PartitionId; partition != nil { + namespace = partition.NamespaceId + } + for _, el := range p.Path { + key = &Key{ + Namespace: namespace, + Kind: el.Kind, + ID: el.GetId(), + Name: el.GetName(), + Parent: key, + } + } + if !key.valid() { // Also detects key == nil. + return key, ErrInvalidKey + } + return key, nil +} + +// multiKeyToProto is a batch version of keyToProto. +func multiKeyToProto(keys []*Key) []*pb.Key { + ret := make([]*pb.Key, len(keys)) + for i, k := range keys { + ret[i] = keyToProto(k) + } + return ret +} + +// multiKeyToProto is a batch version of keyToProto. +func multiProtoToKey(keys []*pb.Key) ([]*Key, error) { + hasErr := false + ret := make([]*Key, len(keys)) + err := make(MultiError, len(keys)) + for i, k := range keys { + ret[i], err[i] = protoToKey(k) + if err[i] != nil { + hasErr = true + } + } + if hasErr { + return nil, err + } + return ret, nil +} + +// multiValid is a batch version of Key.valid. It returns an error, not a +// []bool. +func multiValid(key []*Key) error { + invalid := false + for _, k := range key { + if !k.valid() { + invalid = true + break + } + } + if !invalid { + return nil + } + err := make(MultiError, len(key)) + for i, k := range key { + if !k.valid() { + err[i] = ErrInvalidKey + } + } + return err +} + +// checkMultiArg checks that v has type []S, []*S, []I, or []P, for some struct +// type S, for some interface type I, or some non-interface non-pointer type P +// such that P or *P implements PropertyLoadSaver. +// +// It returns what category the slice's elements are, and the reflect.Type +// that represents S, I or P. +// +// As a special case, PropertyList is an invalid type for v. +// +// TODO(djd): multiArg is very confusing. Fold this logic into the +// relevant Put/Get methods to make the logic less opaque. +func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) { + if v.Kind() != reflect.Slice { + return multiArgTypeInvalid, nil + } + if v.Type() == typeOfPropertyList { + return multiArgTypeInvalid, nil + } + elemType = v.Type().Elem() + if reflect.PtrTo(elemType).Implements(typeOfPropertyLoadSaver) { + return multiArgTypePropertyLoadSaver, elemType + } + switch elemType.Kind() { + case reflect.Struct: + return multiArgTypeStruct, elemType + case reflect.Interface: + return multiArgTypeInterface, elemType + case reflect.Ptr: + elemType = elemType.Elem() + if elemType.Kind() == reflect.Struct { + return multiArgTypeStructPtr, elemType + } + } + return multiArgTypeInvalid, nil +} + +// Close closes the Client. +func (c *Client) Close() error { + return c.conn.Close() +} + +// Get loads the entity stored for key into dst, which must be a struct pointer +// or implement PropertyLoadSaver. If there is no such entity for the key, Get +// returns ErrNoSuchEntity. +// +// The values of dst's unmatched struct fields are not modified, and matching +// slice-typed fields are not reset before appending to them. In particular, it +// is recommended to pass a pointer to a zero valued struct on each Get call. +// +// ErrFieldMismatch is returned when a field is to be loaded into a different +// type than the one it was stored from, or when a field is missing or +// unexported in the destination struct. ErrFieldMismatch is only returned if +// dst is a struct pointer. +func (c *Client) Get(ctx context.Context, key *Key, dst interface{}) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Get") + defer func() { trace.EndSpan(ctx, err) }() + + if dst == nil { // get catches nil interfaces; we need to catch nil ptr here + return ErrInvalidEntityType + } + err = c.get(ctx, []*Key{key}, []interface{}{dst}, nil) + if me, ok := err.(MultiError); ok { + return me[0] + } + return err +} + +// GetMulti is a batch version of Get. +// +// dst must be a []S, []*S, []I or []P, for some struct type S, some interface +// type I, or some non-interface non-pointer type P such that P or *P +// implements PropertyLoadSaver. If an []I, each element must be a valid dst +// for Get: it must be a struct pointer or implement PropertyLoadSaver. +// +// As a special case, PropertyList is an invalid type for dst, even though a +// PropertyList is a slice of structs. It is treated as invalid to avoid being +// mistakenly passed when []PropertyList was intended. +func (c *Client) GetMulti(ctx context.Context, keys []*Key, dst interface{}) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.GetMulti") + defer func() { trace.EndSpan(ctx, err) }() + + return c.get(ctx, keys, dst, nil) +} + +func (c *Client) get(ctx context.Context, keys []*Key, dst interface{}, opts *pb.ReadOptions) error { + v := reflect.ValueOf(dst) + multiArgType, _ := checkMultiArg(v) + + // Sanity checks + if multiArgType == multiArgTypeInvalid { + return errors.New("datastore: dst has invalid type") + } + if len(keys) != v.Len() { + return errors.New("datastore: keys and dst slices have different length") + } + if len(keys) == 0 { + return nil + } + + // Go through keys, validate them, serialize then, and create a dict mapping them to their indices. + // Equal keys are deduped. + multiErr, any := make(MultiError, len(keys)), false + keyMap := make(map[string][]int, len(keys)) + pbKeys := make([]*pb.Key, 0, len(keys)) + for i, k := range keys { + if !k.valid() { + multiErr[i] = ErrInvalidKey + any = true + } else { + ks := k.String() + if _, ok := keyMap[ks]; !ok { + pbKeys = append(pbKeys, keyToProto(k)) + } + keyMap[ks] = append(keyMap[ks], i) + } + } + if any { + return multiErr + } + req := &pb.LookupRequest{ + ProjectId: c.dataset, + Keys: pbKeys, + ReadOptions: opts, + } + resp, err := c.client.Lookup(ctx, req) + if err != nil { + return err + } + found := resp.Found + missing := resp.Missing + // Upper bound 100 iterations to prevent infinite loop. + // We choose 100 iterations somewhat logically: + // Max number of Entities you can request from Datastore is 1,000. + // Max size for a Datastore Entity is 1 MiB. + // Max request size is 10 MiB, so we assume max response size is also 10 MiB. + // 1,000 / 10 = 100. + // Note that if ctx has a deadline, the deadline will probably + // be hit before we reach 100 iterations. + for i := 0; len(resp.Deferred) > 0 && i < 100; i++ { + req.Keys = resp.Deferred + resp, err = c.client.Lookup(ctx, req) + if err != nil { + return err + } + found = append(found, resp.Found...) + missing = append(missing, resp.Missing...) + } + + filled := 0 + for _, e := range found { + k, err := protoToKey(e.Entity.Key) + if err != nil { + return errors.New("datastore: internal error: server returned an invalid key") + } + filled += len(keyMap[k.String()]) + for _, index := range keyMap[k.String()] { + elem := v.Index(index) + if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct { + elem = elem.Addr() + } + if multiArgType == multiArgTypeStructPtr && elem.IsNil() { + elem.Set(reflect.New(elem.Type().Elem())) + } + if err := loadEntityProto(elem.Interface(), e.Entity); err != nil { + multiErr[index] = err + any = true + } + } + } + for _, e := range missing { + k, err := protoToKey(e.Entity.Key) + if err != nil { + return errors.New("datastore: internal error: server returned an invalid key") + } + filled += len(keyMap[k.String()]) + for _, index := range keyMap[k.String()] { + multiErr[index] = ErrNoSuchEntity + } + any = true + } + + if filled != len(keys) { + return errors.New("datastore: internal error: server returned the wrong number of entities") + } + + if any { + return multiErr + } + return nil +} + +// Put saves the entity src into the datastore with key k. src must be a struct +// pointer or implement PropertyLoadSaver; if a struct pointer then any +// unexported fields of that struct will be skipped. If k is an incomplete key, +// the returned key will be a unique key generated by the datastore. +func (c *Client) Put(ctx context.Context, key *Key, src interface{}) (*Key, error) { + k, err := c.PutMulti(ctx, []*Key{key}, []interface{}{src}) + if err != nil { + if me, ok := err.(MultiError); ok { + return nil, me[0] + } + return nil, err + } + return k[0], nil +} + +// PutMulti is a batch version of Put. +// +// src must satisfy the same conditions as the dst argument to GetMulti. +// TODO(jba): rewrite in terms of Mutate. +func (c *Client) PutMulti(ctx context.Context, keys []*Key, src interface{}) (ret []*Key, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.PutMulti") + defer func() { trace.EndSpan(ctx, err) }() + + mutations, err := putMutations(keys, src) + if err != nil { + return nil, err + } + + // Make the request. + req := &pb.CommitRequest{ + ProjectId: c.dataset, + Mutations: mutations, + Mode: pb.CommitRequest_NON_TRANSACTIONAL, + } + resp, err := c.client.Commit(ctx, req) + if err != nil { + return nil, err + } + + // Copy any newly minted keys into the returned keys. + ret = make([]*Key, len(keys)) + for i, key := range keys { + if key.Incomplete() { + // This key is in the mutation results. + ret[i], err = protoToKey(resp.MutationResults[i].Key) + if err != nil { + return nil, errors.New("datastore: internal error: server returned an invalid key") + } + } else { + ret[i] = key + } + } + return ret, nil +} + +func putMutations(keys []*Key, src interface{}) ([]*pb.Mutation, error) { + v := reflect.ValueOf(src) + multiArgType, _ := checkMultiArg(v) + if multiArgType == multiArgTypeInvalid { + return nil, errors.New("datastore: src has invalid type") + } + if len(keys) != v.Len() { + return nil, errors.New("datastore: key and src slices have different length") + } + if len(keys) == 0 { + return nil, nil + } + if err := multiValid(keys); err != nil { + return nil, err + } + mutations := make([]*pb.Mutation, 0, len(keys)) + multiErr := make(MultiError, len(keys)) + hasErr := false + for i, k := range keys { + elem := v.Index(i) + // Two cases where we need to take the address: + // 1) multiArgTypePropertyLoadSaver => &elem implements PLS + // 2) multiArgTypeStruct => saveEntity needs *struct + if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct { + elem = elem.Addr() + } + p, err := saveEntity(k, elem.Interface()) + if err != nil { + multiErr[i] = err + hasErr = true + } + var mut *pb.Mutation + if k.Incomplete() { + mut = &pb.Mutation{Operation: &pb.Mutation_Insert{Insert: p}} + } else { + mut = &pb.Mutation{Operation: &pb.Mutation_Upsert{Upsert: p}} + } + mutations = append(mutations, mut) + } + if hasErr { + return nil, multiErr + } + return mutations, nil +} + +// Delete deletes the entity for the given key. +func (c *Client) Delete(ctx context.Context, key *Key) error { + err := c.DeleteMulti(ctx, []*Key{key}) + if me, ok := err.(MultiError); ok { + return me[0] + } + return err +} + +// DeleteMulti is a batch version of Delete. +// TODO(jba): rewrite in terms of Mutate. +func (c *Client) DeleteMulti(ctx context.Context, keys []*Key) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.DeleteMulti") + defer func() { trace.EndSpan(ctx, err) }() + + mutations, err := deleteMutations(keys) + if err != nil { + return err + } + + req := &pb.CommitRequest{ + ProjectId: c.dataset, + Mutations: mutations, + Mode: pb.CommitRequest_NON_TRANSACTIONAL, + } + _, err = c.client.Commit(ctx, req) + return err +} + +func deleteMutations(keys []*Key) ([]*pb.Mutation, error) { + mutations := make([]*pb.Mutation, 0, len(keys)) + set := make(map[string]bool, len(keys)) + for _, k := range keys { + if k.Incomplete() { + return nil, fmt.Errorf("datastore: can't delete the incomplete key: %v", k) + } + ks := k.String() + if !set[ks] { + mutations = append(mutations, &pb.Mutation{ + Operation: &pb.Mutation_Delete{Delete: keyToProto(k)}, + }) + } + set[ks] = true + } + return mutations, nil +} + +// Mutate applies one or more mutations atomically. +// It returns the keys of the argument Mutations, in the same order. +// +// If any of the mutations are invalid, Mutate returns a MultiError with the errors. +// Mutate returns a MultiError in this case even if there is only one Mutation. +func (c *Client) Mutate(ctx context.Context, muts ...*Mutation) (ret []*Key, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Mutate") + defer func() { trace.EndSpan(ctx, err) }() + + pmuts, err := mutationProtos(muts) + if err != nil { + return nil, err + } + req := &pb.CommitRequest{ + ProjectId: c.dataset, + Mutations: pmuts, + Mode: pb.CommitRequest_NON_TRANSACTIONAL, + } + resp, err := c.client.Commit(ctx, req) + if err != nil { + return nil, err + } + // Copy any newly minted keys into the returned keys. + ret = make([]*Key, len(muts)) + for i, mut := range muts { + if mut.key.Incomplete() { + // This key is in the mutation results. + ret[i], err = protoToKey(resp.MutationResults[i].Key) + if err != nil { + return nil, errors.New("datastore: internal error: server returned an invalid key") + } + } else { + ret[i] = mut.key + } + } + return ret, nil +} diff --git a/vendor/cloud.google.com/go/datastore/datastore_test.go b/vendor/cloud.google.com/go/datastore/datastore_test.go new file mode 100644 index 0000000000..074e5e8a2a --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/datastore_test.go @@ -0,0 +1,3495 @@ +// 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 datastore + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "sort" + "strings" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + + "github.com/golang/protobuf/proto" + "github.com/google/go-cmp/cmp" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/datastore/v1" + "google.golang.org/grpc" +) + +type ( + myBlob []byte + myByte byte + myString string +) + +func makeMyByteSlice(n int) []myByte { + b := make([]myByte, n) + for i := range b { + b[i] = myByte(i) + } + return b +} + +func makeInt8Slice(n int) []int8 { + b := make([]int8, n) + for i := range b { + b[i] = int8(i) + } + return b +} + +func makeUint8Slice(n int) []uint8 { + b := make([]uint8, n) + for i := range b { + b[i] = uint8(i) + } + return b +} + +func newKey(stringID string, parent *Key) *Key { + return NameKey("kind", stringID, parent) +} + +var ( + testKey0 = newKey("name0", nil) + testKey1a = newKey("name1", nil) + testKey1b = newKey("name1", nil) + testKey2a = newKey("name2", testKey0) + testKey2b = newKey("name2", testKey0) + testGeoPt0 = GeoPoint{Lat: 1.2, Lng: 3.4} + testGeoPt1 = GeoPoint{Lat: 5, Lng: 10} + testBadGeoPt = GeoPoint{Lat: 1000, Lng: 34} + + ts = time.Unix(1e9, 0).UTC() +) + +type B0 struct { + B []byte `datastore:",noindex"` +} + +type B1 struct { + B []int8 +} + +type B2 struct { + B myBlob `datastore:",noindex"` +} + +type B3 struct { + B []myByte `datastore:",noindex"` +} + +type B4 struct { + B [][]byte +} + +type C0 struct { + I int + C chan int +} + +type C1 struct { + I int + C *chan int +} + +type C2 struct { + I int + C []chan int +} + +type C3 struct { + C string +} + +type c4 struct { + C string +} + +type E struct{} + +type G0 struct { + G GeoPoint +} + +type G1 struct { + G []GeoPoint +} + +type K0 struct { + K *Key +} + +type K1 struct { + K []*Key +} + +type S struct { + St string +} + +type NoOmit struct { + A string + B int `datastore:"Bb"` + C bool `datastore:",noindex"` +} + +type OmitAll struct { + A string `datastore:",omitempty"` + B int `datastore:"Bb,omitempty"` + C bool `datastore:",omitempty,noindex"` + D time.Time `datastore:",omitempty"` + F []int `datastore:",omitempty"` +} + +type Omit struct { + A string `datastore:",omitempty"` + B int `datastore:"Bb,omitempty"` + C bool `datastore:",omitempty,noindex"` + D time.Time `datastore:",omitempty"` + F []int `datastore:",omitempty"` + S `datastore:",omitempty"` +} + +type NoOmits struct { + No []NoOmit `datastore:",omitempty"` + S `datastore:",omitempty"` + Ss S `datastore:",omitempty"` +} + +type N0 struct { + X0 + Nonymous X0 + Ignore string `datastore:"-"` + Other string +} + +type N1 struct { + X0 + Nonymous []X0 + Ignore string `datastore:"-"` + Other string +} + +type N2 struct { + N1 `datastore:"red"` + Green N1 `datastore:"green"` + Blue N1 + White N1 `datastore:"-"` +} + +type N3 struct { + C3 `datastore:"red"` +} + +type N4 struct { + c4 +} + +type N5 struct { + c4 `datastore:"red"` +} + +type O0 struct { + I int64 +} + +type O1 struct { + I int32 +} + +type U0 struct { + U uint +} + +type U1 struct { + U string +} + +type T struct { + T time.Time +} + +type X0 struct { + S string + I int + i int +} + +type X1 struct { + S myString + I int32 + J int64 +} + +type X2 struct { + Z string + i int +} + +type X3 struct { + S bool + I int +} + +type Y0 struct { + B bool + F []float64 + G []float64 +} + +type Y1 struct { + B bool + F float64 +} + +type Y2 struct { + B bool + F []int64 +} + +type Pointers struct { + Pi *int + Ps *string + Pb *bool + Pf *float64 + Pg *GeoPoint + Pt *time.Time +} + +type PointersOmitEmpty struct { + Pi *int `datastore:",omitempty"` + Ps *string `datastore:",omitempty"` + Pb *bool `datastore:",omitempty"` + Pf *float64 `datastore:",omitempty"` + Pg *GeoPoint `datastore:",omitempty"` + Pt *time.Time `datastore:",omitempty"` +} + +func populatedPointers() *Pointers { + var ( + i int + s string + b bool + f float64 + g GeoPoint + t time.Time + ) + return &Pointers{ + Pi: &i, + Ps: &s, + Pb: &b, + Pf: &f, + Pg: &g, + Pt: &t, + } +} + +type Tagged struct { + A int `datastore:"a,noindex"` + B []int `datastore:"b"` + C int `datastore:",noindex"` + D int `datastore:""` + E int + I int `datastore:"-"` + J int `datastore:",noindex" json:"j"` + + Y0 `datastore:"-"` + Z chan int `datastore:"-"` +} + +type InvalidTagged1 struct { + I int `datastore:"\t"` +} + +type InvalidTagged2 struct { + I int + J int `datastore:"I"` +} + +type InvalidTagged3 struct { + X string `datastore:"-,noindex"` +} + +type InvalidTagged4 struct { + X string `datastore:",garbage"` +} + +type Inner1 struct { + W int32 + X string +} + +type Inner2 struct { + Y float64 +} + +type Inner3 struct { + Z bool +} + +type Inner5 struct { + WW int +} + +type Inner4 struct { + X Inner5 +} + +type Outer struct { + A int16 + I []Inner1 + J Inner2 + Inner3 +} + +type OuterFlatten struct { + A int16 + I []Inner1 `datastore:",flatten"` + J Inner2 `datastore:",flatten,noindex"` + Inner3 `datastore:",flatten"` + K Inner4 `datastore:",flatten"` +} + +type OuterEquivalent struct { + A int16 + IDotW []int32 `datastore:"I.W"` + IDotX []string `datastore:"I.X"` + JDotY float64 `datastore:"J.Y"` + Z bool +} + +type Dotted struct { + A DottedA `datastore:"A0.A1.A2"` +} + +type DottedA struct { + B DottedB `datastore:"B3"` +} + +type DottedB struct { + C int `datastore:"C4.C5"` +} + +type SliceOfSlices struct { + I int + S []struct { + J int + F []float64 + } `datastore:",flatten"` +} + +type Recursive struct { + I int + R []Recursive +} + +type MutuallyRecursive0 struct { + I int + R []MutuallyRecursive1 +} + +type MutuallyRecursive1 struct { + I int + R []MutuallyRecursive0 +} + +type EntityWithKey struct { + I int + S string + K *Key `datastore:"__key__"` +} + +type EntityWithKey2 EntityWithKey + +type WithNestedEntityWithKey struct { + N EntityWithKey +} + +type WithNonKeyField struct { + I int + K string `datastore:"__key__"` +} + +type NestedWithNonKeyField struct { + N WithNonKeyField +} + +type Basic struct { + A string +} + +type PtrToStructField struct { + B *Basic + C *Basic `datastore:"c,noindex"` + *Basic + D []*Basic +} + +var two int = 2 + +type EmbeddedTime struct { + time.Time +} + +type SpecialTime struct { + MyTime EmbeddedTime +} + +type Doubler struct { + S string + I int64 + B bool +} + +type Repeat struct { + Key string + Value []byte +} + +type Repeated struct { + Repeats []Repeat +} + +func (d *Doubler) Load(props []Property) error { + return LoadStruct(d, props) +} + +func (d *Doubler) Save() ([]Property, error) { + // Save the default Property slice to an in-memory buffer (a PropertyList). + props, err := SaveStruct(d) + if err != nil { + return nil, err + } + var list PropertyList + if err := list.Load(props); err != nil { + return nil, err + } + + // Edit that PropertyList, and send it on. + for i := range list { + switch v := list[i].Value.(type) { + case string: + // + means string concatenation. + list[i].Value = v + v + case int64: + // + means integer addition. + list[i].Value = v + v + } + } + return list.Save() +} + +var _ PropertyLoadSaver = (*Doubler)(nil) + +type Deriver struct { + S, Derived, Ignored string +} + +func (e *Deriver) Load(props []Property) error { + for _, p := range props { + if p.Name != "S" { + continue + } + e.S = p.Value.(string) + e.Derived = "derived+" + e.S + } + return nil +} + +func (e *Deriver) Save() ([]Property, error) { + return []Property{ + { + Name: "S", + Value: e.S, + }, + }, nil +} + +var _ PropertyLoadSaver = (*Deriver)(nil) + +type BadMultiPropEntity struct{} + +func (e *BadMultiPropEntity) Load(props []Property) error { + return errors.New("unimplemented") +} + +func (e *BadMultiPropEntity) Save() ([]Property, error) { + // Write multiple properties with the same name "I". + var props []Property + for i := 0; i < 3; i++ { + props = append(props, Property{ + Name: "I", + Value: int64(i), + }) + } + return props, nil +} + +var _ PropertyLoadSaver = (*BadMultiPropEntity)(nil) + +type testCase struct { + desc string + src interface{} + want interface{} + putErr string + getErr string +} + +var testCases = []testCase{ + { + "chan save fails", + &C0{I: -1}, + &E{}, + "unsupported struct field", + "", + }, + { + "*chan save fails", + &C1{I: -1}, + &E{}, + "unsupported struct field", + "", + }, + { + "[]chan save fails", + &C2{I: -1, C: make([]chan int, 8)}, + &E{}, + "unsupported struct field", + "", + }, + { + "chan load fails", + &C3{C: "not a chan"}, + &C0{}, + "", + "type mismatch", + }, + { + "*chan load fails", + &C3{C: "not a *chan"}, + &C1{}, + "", + "type mismatch", + }, + { + "[]chan load fails", + &C3{C: "not a []chan"}, + &C2{}, + "", + "type mismatch", + }, + { + "empty struct", + &E{}, + &E{}, + "", + "", + }, + { + "geopoint", + &G0{G: testGeoPt0}, + &G0{G: testGeoPt0}, + "", + "", + }, + { + "geopoint invalid", + &G0{G: testBadGeoPt}, + &G0{}, + "invalid GeoPoint value", + "", + }, + { + "geopoint as props", + &G0{G: testGeoPt0}, + &PropertyList{ + Property{Name: "G", Value: testGeoPt0, NoIndex: false}, + }, + "", + "", + }, + { + "geopoint slice", + &G1{G: []GeoPoint{testGeoPt0, testGeoPt1}}, + &G1{G: []GeoPoint{testGeoPt0, testGeoPt1}}, + "", + "", + }, + { + "omit empty, all", + &OmitAll{}, + new(PropertyList), + "", + "", + }, + { + "omit empty", + &Omit{}, + &PropertyList{ + Property{Name: "St", Value: "", NoIndex: false}, + }, + "", + "", + }, + { + "omit empty, fields populated", + &Omit{ + A: "a", + B: 10, + C: true, + F: []int{11}, + }, + &PropertyList{ + Property{Name: "A", Value: "a", NoIndex: false}, + Property{Name: "Bb", Value: int64(10), NoIndex: false}, + Property{Name: "C", Value: true, NoIndex: true}, + Property{Name: "F", Value: []interface{}{int64(11)}, NoIndex: false}, + Property{Name: "St", Value: "", NoIndex: false}, + }, + "", + "", + }, + { + "omit empty, fields populated", + &Omit{ + A: "a", + B: 10, + C: true, + F: []int{11}, + S: S{St: "string"}, + }, + &PropertyList{ + Property{Name: "A", Value: "a", NoIndex: false}, + Property{Name: "Bb", Value: int64(10), NoIndex: false}, + Property{Name: "C", Value: true, NoIndex: true}, + Property{Name: "F", Value: []interface{}{int64(11)}, NoIndex: false}, + Property{Name: "St", Value: "string", NoIndex: false}, + }, + "", + "", + }, + { + "omit empty does not propagate", + &NoOmits{ + No: []NoOmit{ + {}, + }, + S: S{}, + Ss: S{}, + }, + &PropertyList{ + Property{Name: "No", Value: []interface{}{ + &Entity{ + Properties: []Property{ + {Name: "A", Value: "", NoIndex: false}, + {Name: "Bb", Value: int64(0), NoIndex: false}, + {Name: "C", Value: false, NoIndex: true}, + }, + }, + }, NoIndex: false}, + Property{Name: "Ss", Value: &Entity{ + Properties: []Property{ + {Name: "St", Value: "", NoIndex: false}, + }, + }, NoIndex: false}, + Property{Name: "St", Value: "", NoIndex: false}, + }, + "", + "", + }, + { + "key", + &K0{K: testKey1a}, + &K0{K: testKey1b}, + "", + "", + }, + { + "key with parent", + &K0{K: testKey2a}, + &K0{K: testKey2b}, + "", + "", + }, + { + "nil key", + &K0{}, + &K0{}, + "", + "", + }, + { + "all nil keys in slice", + &K1{[]*Key{nil, nil}}, + &K1{[]*Key{nil, nil}}, + "", + "", + }, + { + "some nil keys in slice", + &K1{[]*Key{testKey1a, nil, testKey2a}}, + &K1{[]*Key{testKey1b, nil, testKey2b}}, + "", + "", + }, + { + "overflow", + &O0{I: 1 << 48}, + &O1{}, + "", + "overflow", + }, + { + "time", + &T{T: time.Unix(1e9, 0)}, + &T{T: time.Unix(1e9, 0)}, + "", + "", + }, + { + "time as props", + &T{T: time.Unix(1e9, 0)}, + &PropertyList{ + Property{Name: "T", Value: time.Unix(1e9, 0), NoIndex: false}, + }, + "", + "", + }, + { + "uint save", + &U0{U: 1}, + &U0{}, + "unsupported struct field", + "", + }, + { + "uint load", + &U1{U: "not a uint"}, + &U0{}, + "", + "type mismatch", + }, + { + "zero", + &X0{}, + &X0{}, + "", + "", + }, + { + "basic", + &X0{S: "one", I: 2, i: 3}, + &X0{S: "one", I: 2}, + "", + "", + }, + { + "save string/int load myString/int32", + &X0{S: "one", I: 2, i: 3}, + &X1{S: "one", I: 2}, + "", + "", + }, + { + "missing fields", + &X0{S: "one", I: 2, i: 3}, + &X2{}, + "", + "no such struct field", + }, + { + "save string load bool", + &X0{S: "one", I: 2, i: 3}, + &X3{I: 2}, + "", + "type mismatch", + }, + { + "basic slice", + &Y0{B: true, F: []float64{7, 8, 9}}, + &Y0{B: true, F: []float64{7, 8, 9}}, + "", + "", + }, + { + "save []float64 load float64", + &Y0{B: true, F: []float64{7, 8, 9}}, + &Y1{B: true}, + "", + "requires a slice", + }, + { + "save []float64 load []int64", + &Y0{B: true, F: []float64{7, 8, 9}}, + &Y2{B: true}, + "", + "type mismatch", + }, + { + "single slice is too long", + &Y0{F: make([]float64, maxIndexedProperties+1)}, + &Y0{}, + "too many indexed properties", + "", + }, + { + "two slices are too long", + &Y0{F: make([]float64, maxIndexedProperties), G: make([]float64, maxIndexedProperties)}, + &Y0{}, + "too many indexed properties", + "", + }, + { + "one slice and one scalar are too long", + &Y0{F: make([]float64, maxIndexedProperties), B: true}, + &Y0{}, + "too many indexed properties", + "", + }, + { + "slice of slices of bytes", + &Repeated{ + Repeats: []Repeat{ + { + Key: "key 1", + Value: []byte("value 1"), + }, + { + Key: "key 2", + Value: []byte("value 2"), + }, + }, + }, + &Repeated{ + Repeats: []Repeat{ + { + Key: "key 1", + Value: []byte("value 1"), + }, + { + Key: "key 2", + Value: []byte("value 2"), + }, + }, + }, + "", + "", + }, + { + "long blob", + &B0{B: makeUint8Slice(maxIndexedProperties + 1)}, + &B0{B: makeUint8Slice(maxIndexedProperties + 1)}, + "", + "", + }, + { + "long []int8 is too long", + &B1{B: makeInt8Slice(maxIndexedProperties + 1)}, + &B1{}, + "too many indexed properties", + "", + }, + { + "short []int8", + &B1{B: makeInt8Slice(3)}, + &B1{B: makeInt8Slice(3)}, + "", + "", + }, + { + "long myBlob", + &B2{B: makeUint8Slice(maxIndexedProperties + 1)}, + &B2{B: makeUint8Slice(maxIndexedProperties + 1)}, + "", + "", + }, + { + "short myBlob", + &B2{B: makeUint8Slice(3)}, + &B2{B: makeUint8Slice(3)}, + "", + "", + }, + { + "long []myByte", + &B3{B: makeMyByteSlice(maxIndexedProperties + 1)}, + &B3{B: makeMyByteSlice(maxIndexedProperties + 1)}, + "", + "", + }, + { + "short []myByte", + &B3{B: makeMyByteSlice(3)}, + &B3{B: makeMyByteSlice(3)}, + "", + "", + }, + { + "slice of blobs", + &B4{B: [][]byte{ + makeUint8Slice(3), + makeUint8Slice(4), + makeUint8Slice(5), + }}, + &B4{B: [][]byte{ + makeUint8Slice(3), + makeUint8Slice(4), + makeUint8Slice(5), + }}, + "", + "", + }, + { + "[]byte must be noindex", + &PropertyList{ + Property{Name: "B", Value: makeUint8Slice(1501), NoIndex: false}, + }, + nil, + "[]byte property too long to index", + "", + }, + { + "string must be noindex", + &PropertyList{ + Property{Name: "B", Value: strings.Repeat("x", 1501), NoIndex: false}, + }, + nil, + "string property too long to index", + "", + }, + { + "slice of []byte must be noindex", + &PropertyList{ + Property{Name: "B", Value: []interface{}{ + []byte("short"), + makeUint8Slice(1501), + }, NoIndex: false}, + }, + nil, + "[]byte property too long to index", + "", + }, + { + "slice of string must be noindex", + &PropertyList{ + Property{Name: "B", Value: []interface{}{ + "short", + strings.Repeat("x", 1501), + }, NoIndex: false}, + }, + nil, + "string property too long to index", + "", + }, + { + "save tagged load props", + &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6, J: 7}, + &PropertyList{ + // A and B are renamed to a and b; A and C are noindex, I is ignored. + // Order is sorted as per byName. + Property{Name: "C", Value: int64(3), NoIndex: true}, + Property{Name: "D", Value: int64(4), NoIndex: false}, + Property{Name: "E", Value: int64(5), NoIndex: false}, + Property{Name: "J", Value: int64(7), NoIndex: true}, + Property{Name: "a", Value: int64(1), NoIndex: true}, + Property{Name: "b", Value: []interface{}{int64(21), int64(22), int64(23)}, NoIndex: false}, + }, + "", + "", + }, + { + "save tagged load tagged", + &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6, J: 7}, + &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, J: 7}, + "", + "", + }, + { + "invalid tagged1", + &InvalidTagged1{I: 1}, + &InvalidTagged1{}, + "struct tag has invalid property name", + "", + }, + { + "invalid tagged2", + &InvalidTagged2{I: 1, J: 2}, + &InvalidTagged2{J: 2}, + "", + "", + }, + { + "invalid tagged3", + &InvalidTagged3{X: "hello"}, + &InvalidTagged3{}, + "struct tag has invalid property name: \"-\"", + "", + }, + { + "invalid tagged4", + &InvalidTagged4{X: "hello"}, + &InvalidTagged4{}, + "struct tag has invalid option: \"garbage\"", + "", + }, + { + "doubler", + &Doubler{S: "s", I: 1, B: true}, + &Doubler{S: "ss", I: 2, B: true}, + "", + "", + }, + { + "save struct load props", + &X0{S: "s", I: 1}, + &PropertyList{ + Property{Name: "I", Value: int64(1), NoIndex: false}, + Property{Name: "S", Value: "s", NoIndex: false}, + }, + "", + "", + }, + { + "save props load struct", + &PropertyList{ + Property{Name: "I", Value: int64(1), NoIndex: false}, + Property{Name: "S", Value: "s", NoIndex: false}, + }, + &X0{S: "s", I: 1}, + "", + "", + }, + { + "nil-value props", + &PropertyList{ + Property{Name: "I", Value: nil, NoIndex: false}, + Property{Name: "B", Value: nil, NoIndex: false}, + Property{Name: "S", Value: nil, NoIndex: false}, + Property{Name: "F", Value: nil, NoIndex: false}, + Property{Name: "K", Value: nil, NoIndex: false}, + Property{Name: "T", Value: nil, NoIndex: false}, + Property{Name: "J", Value: []interface{}{nil, int64(7), nil}, NoIndex: false}, + }, + &struct { + I int64 + B bool + S string + F float64 + K *Key + T time.Time + J []int64 + }{ + J: []int64{0, 7, 0}, + }, + "", + "", + }, + { + "save outer load props flatten", + &OuterFlatten{ + A: 1, + I: []Inner1{ + {10, "ten"}, + {20, "twenty"}, + {30, "thirty"}, + }, + J: Inner2{ + Y: 3.14, + }, + Inner3: Inner3{ + Z: true, + }, + K: Inner4{ + X: Inner5{ + WW: 12, + }, + }, + }, + &PropertyList{ + Property{Name: "A", Value: int64(1), NoIndex: false}, + Property{Name: "I.W", Value: []interface{}{int64(10), int64(20), int64(30)}, NoIndex: false}, + Property{Name: "I.X", Value: []interface{}{"ten", "twenty", "thirty"}, NoIndex: false}, + Property{Name: "J.Y", Value: float64(3.14), NoIndex: true}, + Property{Name: "K.X.WW", Value: int64(12), NoIndex: false}, + Property{Name: "Z", Value: true, NoIndex: false}, + }, + "", + "", + }, + { + "load outer props flatten", + &PropertyList{ + Property{Name: "A", Value: int64(1), NoIndex: false}, + Property{Name: "I.W", Value: []interface{}{int64(10), int64(20), int64(30)}, NoIndex: false}, + Property{Name: "I.X", Value: []interface{}{"ten", "twenty", "thirty"}, NoIndex: false}, + Property{Name: "J.Y", Value: float64(3.14), NoIndex: true}, + Property{Name: "Z", Value: true, NoIndex: false}, + }, + &OuterFlatten{ + A: 1, + I: []Inner1{ + {10, "ten"}, + {20, "twenty"}, + {30, "thirty"}, + }, + J: Inner2{ + Y: 3.14, + }, + Inner3: Inner3{ + Z: true, + }, + }, + "", + "", + }, + { + "save outer load props", + &Outer{ + A: 1, + I: []Inner1{ + {10, "ten"}, + {20, "twenty"}, + {30, "thirty"}, + }, + J: Inner2{ + Y: 3.14, + }, + Inner3: Inner3{ + Z: true, + }, + }, + &PropertyList{ + Property{Name: "A", Value: int64(1), NoIndex: false}, + Property{Name: "I", Value: []interface{}{ + &Entity{ + Properties: []Property{ + {Name: "W", Value: int64(10), NoIndex: false}, + {Name: "X", Value: "ten", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "W", Value: int64(20), NoIndex: false}, + {Name: "X", Value: "twenty", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "W", Value: int64(30), NoIndex: false}, + {Name: "X", Value: "thirty", NoIndex: false}, + }, + }, + }, NoIndex: false}, + Property{Name: "J", Value: &Entity{ + Properties: []Property{ + {Name: "Y", Value: float64(3.14), NoIndex: false}, + }, + }, NoIndex: false}, + Property{Name: "Z", Value: true, NoIndex: false}, + }, + "", + "", + }, + { + "save props load outer-equivalent", + &PropertyList{ + Property{Name: "A", Value: int64(1), NoIndex: false}, + Property{Name: "I.W", Value: []interface{}{int64(10), int64(20), int64(30)}, NoIndex: false}, + Property{Name: "I.X", Value: []interface{}{"ten", "twenty", "thirty"}, NoIndex: false}, + Property{Name: "J.Y", Value: float64(3.14), NoIndex: false}, + Property{Name: "Z", Value: true, NoIndex: false}, + }, + &OuterEquivalent{ + A: 1, + IDotW: []int32{10, 20, 30}, + IDotX: []string{"ten", "twenty", "thirty"}, + JDotY: 3.14, + Z: true, + }, + "", + "", + }, + { + "dotted names save", + &Dotted{A: DottedA{B: DottedB{C: 88}}}, + &PropertyList{ + Property{Name: "A0.A1.A2", Value: &Entity{ + Properties: []Property{ + {Name: "B3", Value: &Entity{ + Properties: []Property{ + {Name: "C4.C5", Value: int64(88), NoIndex: false}, + }, + }, NoIndex: false}, + }, + }, NoIndex: false}, + }, + "", + "", + }, + { + "dotted names load", + &PropertyList{ + Property{Name: "A0.A1.A2", Value: &Entity{ + Properties: []Property{ + {Name: "B3", Value: &Entity{ + Properties: []Property{ + {Name: "C4.C5", Value: 99, NoIndex: false}, + }, + }, NoIndex: false}, + }, + }, NoIndex: false}, + }, + &Dotted{A: DottedA{B: DottedB{C: 99}}}, + "", + "", + }, + { + "save struct load deriver", + &X0{S: "s", I: 1}, + &Deriver{S: "s", Derived: "derived+s"}, + "", + "", + }, + { + "save deriver load struct", + &Deriver{S: "s", Derived: "derived+s", Ignored: "ignored"}, + &X0{S: "s"}, + "", + "", + }, + { + "zero time.Time", + &T{T: time.Time{}}, + &T{T: time.Time{}}, + "", + "", + }, + { + "time.Time near Unix zero time", + &T{T: time.Unix(0, 4e3)}, + &T{T: time.Unix(0, 4e3)}, + "", + "", + }, + { + "time.Time, far in the future", + &T{T: time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC)}, + &T{T: time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC)}, + "", + "", + }, + { + "time.Time, very far in the past", + &T{T: time.Date(-300000, 1, 1, 0, 0, 0, 0, time.UTC)}, + &T{}, + "time value out of range", + "", + }, + { + "time.Time, very far in the future", + &T{T: time.Date(294248, 1, 1, 0, 0, 0, 0, time.UTC)}, + &T{}, + "time value out of range", + "", + }, + { + "structs", + &N0{ + X0: X0{S: "one", I: 2, i: 3}, + Nonymous: X0{S: "four", I: 5, i: 6}, + Ignore: "ignore", + Other: "other", + }, + &N0{ + X0: X0{S: "one", I: 2}, + Nonymous: X0{S: "four", I: 5}, + Other: "other", + }, + "", + "", + }, + { + "slice of structs", + &N1{ + X0: X0{S: "one", I: 2, i: 3}, + Nonymous: []X0{ + {S: "four", I: 5, i: 6}, + {S: "seven", I: 8, i: 9}, + {S: "ten", I: 11, i: 12}, + {S: "thirteen", I: 14, i: 15}, + }, + Ignore: "ignore", + Other: "other", + }, + &N1{ + X0: X0{S: "one", I: 2}, + Nonymous: []X0{ + {S: "four", I: 5}, + {S: "seven", I: 8}, + {S: "ten", I: 11}, + {S: "thirteen", I: 14}, + }, + Other: "other", + }, + "", + "", + }, + { + "structs with slices of structs", + &N2{ + N1: N1{ + X0: X0{S: "rouge"}, + Nonymous: []X0{ + {S: "rosso0"}, + {S: "rosso1"}, + }, + }, + Green: N1{ + X0: X0{S: "vert"}, + Nonymous: []X0{ + {S: "verde0"}, + {S: "verde1"}, + {S: "verde2"}, + }, + }, + Blue: N1{ + X0: X0{S: "bleu"}, + Nonymous: []X0{ + {S: "blu0"}, + {S: "blu1"}, + {S: "blu2"}, + {S: "blu3"}, + }, + }, + }, + &N2{ + N1: N1{ + X0: X0{S: "rouge"}, + Nonymous: []X0{ + {S: "rosso0"}, + {S: "rosso1"}, + }, + }, + Green: N1{ + X0: X0{S: "vert"}, + Nonymous: []X0{ + {S: "verde0"}, + {S: "verde1"}, + {S: "verde2"}, + }, + }, + Blue: N1{ + X0: X0{S: "bleu"}, + Nonymous: []X0{ + {S: "blu0"}, + {S: "blu1"}, + {S: "blu2"}, + {S: "blu3"}, + }, + }, + }, + "", + "", + }, + { + "save structs load props", + &N2{ + N1: N1{ + X0: X0{S: "rouge"}, + Nonymous: []X0{ + {S: "rosso0"}, + {S: "rosso1"}, + }, + }, + Green: N1{ + X0: X0{S: "vert"}, + Nonymous: []X0{ + {S: "verde0"}, + {S: "verde1"}, + {S: "verde2"}, + }, + }, + Blue: N1{ + X0: X0{S: "bleu"}, + Nonymous: []X0{ + {S: "blu0"}, + {S: "blu1"}, + {S: "blu2"}, + {S: "blu3"}, + }, + }, + }, + &PropertyList{ + Property{Name: "Blue", Value: &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "Nonymous", Value: []interface{}{ + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "blu0", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "blu1", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "blu2", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "blu3", NoIndex: false}, + }, + }, + }, NoIndex: false}, + {Name: "Other", Value: "", NoIndex: false}, + {Name: "S", Value: "bleu", NoIndex: false}, + }, + }, NoIndex: false}, + Property{Name: "green", Value: &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "Nonymous", Value: []interface{}{ + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "verde0", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "verde1", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "verde2", NoIndex: false}, + }, + }, + }, NoIndex: false}, + {Name: "Other", Value: "", NoIndex: false}, + {Name: "S", Value: "vert", NoIndex: false}, + }, + }, NoIndex: false}, + Property{Name: "red", Value: &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "Nonymous", Value: []interface{}{ + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "rosso0", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "I", Value: int64(0), NoIndex: false}, + {Name: "S", Value: "rosso1", NoIndex: false}, + }, + }, + }, NoIndex: false}, + {Name: "Other", Value: "", NoIndex: false}, + {Name: "S", Value: "rouge", NoIndex: false}, + }, + }, NoIndex: false}, + }, + "", + "", + }, + { + "nested entity with key", + &WithNestedEntityWithKey{ + N: EntityWithKey{ + I: 12, + S: "abcd", + K: testKey0, + }, + }, + &WithNestedEntityWithKey{ + N: EntityWithKey{ + I: 12, + S: "abcd", + K: testKey0, + }, + }, + "", + "", + }, + { + "entity with key at top level", + &EntityWithKey{ + I: 12, + S: "abc", + K: testKey0, + }, + &EntityWithKey{ + I: 12, + S: "abc", + K: testKey0, + }, + "", + "", + }, + { + "entity with key at top level (key is populated on load)", + &EntityWithKey{ + I: 12, + S: "abc", + }, + &EntityWithKey{ + I: 12, + S: "abc", + K: testKey0, + }, + "", + "", + }, + { + "__key__ field not a *Key", + &NestedWithNonKeyField{ + N: WithNonKeyField{ + I: 12, + K: "abcd", + }, + }, + &NestedWithNonKeyField{ + N: WithNonKeyField{ + I: 12, + K: "abcd", + }, + }, + "datastore: __key__ field on struct datastore.WithNonKeyField is not a *datastore.Key", + "", + }, + { + "save struct with ptr to struct fields", + &PtrToStructField{ + &Basic{ + A: "b", + }, + &Basic{ + A: "c", + }, + &Basic{ + A: "anon", + }, + []*Basic{ + { + A: "slice0", + }, + { + A: "slice1", + }, + }, + }, + &PropertyList{ + Property{Name: "A", Value: "anon", NoIndex: false}, + Property{Name: "B", Value: &Entity{ + Properties: []Property{ + {Name: "A", Value: "b", NoIndex: false}, + }, + }}, + Property{Name: "D", Value: []interface{}{ + &Entity{ + Properties: []Property{ + {Name: "A", Value: "slice0", NoIndex: false}, + }, + }, + &Entity{ + Properties: []Property{ + {Name: "A", Value: "slice1", NoIndex: false}, + }, + }, + }, NoIndex: false}, + Property{Name: "c", Value: &Entity{ + Properties: []Property{ + {Name: "A", Value: "c", NoIndex: true}, + }, + }, NoIndex: true}, + }, + "", + "", + }, + { + "save and load struct with ptr to struct fields", + &PtrToStructField{ + &Basic{ + A: "b", + }, + &Basic{ + A: "c", + }, + &Basic{ + A: "anon", + }, + []*Basic{ + { + A: "slice0", + }, + { + A: "slice1", + }, + }, + }, + &PtrToStructField{ + &Basic{ + A: "b", + }, + &Basic{ + A: "c", + }, + &Basic{ + A: "anon", + }, + []*Basic{ + { + A: "slice0", + }, + { + A: "slice1", + }, + }, + }, + "", + "", + }, + { + "struct with nil ptr to struct fields", + &PtrToStructField{ + nil, + nil, + nil, + nil, + }, + new(PropertyList), + "", + "", + }, + { + "nested load entity with key", + &WithNestedEntityWithKey{ + N: EntityWithKey{ + I: 12, + S: "abcd", + K: testKey0, + }, + }, + &PropertyList{ + Property{Name: "N", Value: &Entity{ + Key: testKey0, + Properties: []Property{ + {Name: "I", Value: int64(12), NoIndex: false}, + {Name: "S", Value: "abcd", NoIndex: false}, + }, + }, + NoIndex: false}, + }, + "", + "", + }, + { + "nested save entity with key", + &PropertyList{ + Property{Name: "N", Value: &Entity{ + Key: testKey0, + Properties: []Property{ + {Name: "I", Value: int64(12), NoIndex: false}, + {Name: "S", Value: "abcd", NoIndex: false}, + }, + }, NoIndex: false}, + }, + + &WithNestedEntityWithKey{ + N: EntityWithKey{ + I: 12, + S: "abcd", + K: testKey0, + }, + }, + "", + "", + }, + { + "anonymous field with tag", + &N3{ + C3: C3{C: "s"}, + }, + &PropertyList{ + Property{Name: "red", Value: &Entity{ + Properties: []Property{ + {Name: "C", Value: "s", NoIndex: false}, + }, + }, NoIndex: false}, + }, + "", + "", + }, + { + "unexported anonymous field", + &N4{ + c4: c4{C: "s"}, + }, + &PropertyList{ + Property{Name: "C", Value: "s", NoIndex: false}, + }, + "", + "", + }, + { + "unexported anonymous field with tag", + &N5{ + c4: c4{C: "s"}, + }, + new(PropertyList), + "", + "", + }, + { + "save props load structs with ragged fields", + &PropertyList{ + Property{Name: "red.S", Value: "rot", NoIndex: false}, + Property{Name: "green.Nonymous.I", Value: []interface{}{int64(10), int64(11), int64(12), int64(13)}, NoIndex: false}, + Property{Name: "Blue.Nonymous.I", Value: []interface{}{int64(20), int64(21)}, NoIndex: false}, + Property{Name: "Blue.Nonymous.S", Value: []interface{}{"blau0", "blau1", "blau2"}, NoIndex: false}, + }, + &N2{ + N1: N1{ + X0: X0{S: "rot"}, + }, + Green: N1{ + Nonymous: []X0{ + {I: 10}, + {I: 11}, + {I: 12}, + {I: 13}, + }, + }, + Blue: N1{ + Nonymous: []X0{ + {S: "blau0", I: 20}, + {S: "blau1", I: 21}, + {S: "blau2"}, + }, + }, + }, + "", + "", + }, + { + "save structs with noindex tags", + &struct { + A struct { + X string `datastore:",noindex"` + Y string + } `datastore:",noindex"` + B struct { + X string `datastore:",noindex"` + Y string + } + }{}, + &PropertyList{ + Property{Name: "A", Value: &Entity{ + Properties: []Property{ + {Name: "X", Value: "", NoIndex: true}, + {Name: "Y", Value: "", NoIndex: true}, + }, + }, NoIndex: true}, + Property{Name: "B", Value: &Entity{ + Properties: []Property{ + {Name: "X", Value: "", NoIndex: true}, + {Name: "Y", Value: "", NoIndex: false}, + }, + }, NoIndex: false}, + }, + "", + "", + }, + { + "embedded struct with name override", + &struct { + Inner1 `datastore:"foo"` + }{}, + &PropertyList{ + Property{Name: "foo", Value: &Entity{ + Properties: []Property{ + {Name: "W", Value: int64(0), NoIndex: false}, + {Name: "X", Value: "", NoIndex: false}, + }, + }, NoIndex: false}, + }, + "", + "", + }, + { + "slice of slices", + &SliceOfSlices{}, + nil, + "flattening nested structs leads to a slice of slices", + "", + }, + { + "recursive struct", + &Recursive{}, + &Recursive{}, + "", + "", + }, + { + "mutually recursive struct", + &MutuallyRecursive0{}, + &MutuallyRecursive0{}, + "", + "", + }, + { + "non-exported struct fields", + &struct { + i, J int64 + }{i: 1, J: 2}, + &PropertyList{ + Property{Name: "J", Value: int64(2), NoIndex: false}, + }, + "", + "", + }, + { + "json.RawMessage", + &struct { + J json.RawMessage + }{ + J: json.RawMessage("rawr"), + }, + &PropertyList{ + Property{Name: "J", Value: []byte("rawr"), NoIndex: false}, + }, + "", + "", + }, + { + "json.RawMessage to myBlob", + &struct { + B json.RawMessage + }{ + B: json.RawMessage("rawr"), + }, + &B2{B: myBlob("rawr")}, + "", + "", + }, + { + "repeated property names", + &PropertyList{ + Property{Name: "A", Value: ""}, + Property{Name: "A", Value: ""}, + }, + nil, + "duplicate Property", + "", + }, + { + "embedded time field", + &SpecialTime{MyTime: EmbeddedTime{ts}}, + &SpecialTime{MyTime: EmbeddedTime{ts}}, + "", + "", + }, + { + "embedded time load", + &PropertyList{ + Property{Name: "MyTime.Time", Value: ts}, + }, + &SpecialTime{MyTime: EmbeddedTime{ts}}, + "", + "", + }, + { + "pointer fields: nil", + &Pointers{}, + &Pointers{}, + "", + "", + }, + { + "pointer fields: populated with zeroes", + populatedPointers(), + populatedPointers(), + "", + "", + }, +} + +// checkErr returns the empty string if either both want and err are zero, +// or if want is a non-empty substring of err's string representation. +func checkErr(want string, err error) string { + if err != nil { + got := err.Error() + if want == "" || strings.Index(got, want) == -1 { + return got + } + } else if want != "" { + return fmt.Sprintf("want error %q", want) + } + return "" +} + +func TestRoundTrip(t *testing.T) { + for _, tc := range testCases { + p, err := saveEntity(testKey0, tc.src) + if s := checkErr(tc.putErr, err); s != "" { + t.Errorf("%s: save: %s", tc.desc, s) + continue + } + if p == nil { + continue + } + var got interface{} + if _, ok := tc.want.(*PropertyList); ok { + got = new(PropertyList) + } else { + got = reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() + } + err = loadEntityProto(got, p) + if s := checkErr(tc.getErr, err); s != "" { + t.Errorf("%s: load: %s", tc.desc, s) + continue + } + if pl, ok := got.(*PropertyList); ok { + // Sort by name to make sure we have a deterministic order. + sortPL(*pl) + } + + if !testutil.Equal(got, tc.want, cmp.AllowUnexported(X0{}, X2{})) { + t.Errorf("%s: compare:\ngot: %+#v\nwant: %+#v", tc.desc, got, tc.want) + continue + } + } +} + +type aPtrPLS struct { + Count int +} + +func (pls *aPtrPLS) Load([]Property) error { + pls.Count += 1 + return nil +} + +func (pls *aPtrPLS) Save() ([]Property, error) { + return []Property{{Name: "Count", Value: 4}}, nil +} + +type aValuePLS struct { + Count int +} + +func (pls aValuePLS) Load([]Property) error { + pls.Count += 2 + return nil +} + +func (pls aValuePLS) Save() ([]Property, error) { + return []Property{{Name: "Count", Value: 8}}, nil +} + +type aValuePtrPLS struct { + Count int +} + +func (pls *aValuePtrPLS) Load([]Property) error { + pls.Count = 11 + return nil +} + +func (pls *aValuePtrPLS) Save() ([]Property, error) { + return []Property{{Name: "Count", Value: 12}}, nil +} + +type aNotPLS struct { + Count int +} + +type plsString string + +func (s *plsString) Load([]Property) error { + *s = "LOADED" + return nil +} + +func (s *plsString) Save() ([]Property, error) { + return []Property{{Name: "SS", Value: "SAVED"}}, nil +} + +func ptrToplsString(s string) *plsString { + plsStr := plsString(s) + return &plsStr +} + +type aSubPLS struct { + Foo string + Bar *aPtrPLS + Baz aValuePtrPLS + S plsString +} + +type aSubNotPLS struct { + Foo string + Bar *aNotPLS +} + +type aSubPLSErr struct { + Foo string + Bar aValuePLS +} + +type aSubPLSNoErr struct { + Foo string + Bar aPtrPLS +} + +type GrandparentFlatten struct { + Parent Parent `datastore:",flatten"` +} + +type GrandparentOfPtrFlatten struct { + Parent ParentOfPtr `datastore:",flatten"` +} + +type GrandparentOfSlice struct { + Parent ParentOfSlice +} + +type GrandparentOfSlicePtrs struct { + Parent ParentOfSlicePtrs +} + +type GrandparentOfSliceFlatten struct { + Parent ParentOfSlice `datastore:",flatten"` +} + +type GrandparentOfSlicePtrsFlatten struct { + Parent ParentOfSlicePtrs `datastore:",flatten"` +} + +type Grandparent struct { + Parent Parent +} + +type Parent struct { + Child Child + String plsString +} + +type ParentOfPtr struct { + Child *Child + String *plsString +} + +type ParentOfSlice struct { + Children []Child + Strings []plsString +} + +type ParentOfSlicePtrs struct { + Children []*Child + Strings []*plsString +} + +type Child struct { + I int + Grandchild Grandchild +} + +type Grandchild struct { + S string +} + +func (c *Child) Load(props []Property) error { + for _, p := range props { + if p.Name == "I" { + c.I += 1 + } else if p.Name == "Grandchild.S" { + c.Grandchild.S = "grandchild loaded" + } + } + + return nil +} + +func (c *Child) Save() ([]Property, error) { + v := c.I + 1 + return []Property{ + {Name: "I", Value: v}, + {Name: "Grandchild.S", Value: fmt.Sprintf("grandchild saved %d", v)}, + }, nil +} + +func TestLoadSavePLS(t *testing.T) { + type testCase struct { + desc string + src interface{} + wantSave *pb.Entity + wantLoad interface{} + saveErr string + loadErr string + } + + testCases := []testCase{ + { + desc: "non-struct implements PLS (top-level)", + src: ptrToplsString("hello"), + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + wantLoad: ptrToplsString("LOADED"), + }, + { + desc: "substructs do implement PLS", + src: &aSubPLS{Foo: "foo", Bar: &aPtrPLS{Count: 2}, Baz: aValuePtrPLS{Count: 15}, S: "something"}, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Foo": {ValueType: &pb.Value_StringValue{StringValue: "foo"}}, + "Bar": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Count": {ValueType: &pb.Value_IntegerValue{IntegerValue: 4}}, + }, + }, + }}, + "Baz": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Count": {ValueType: &pb.Value_IntegerValue{IntegerValue: 12}}, + }, + }, + }}, + "S": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + }, + }, + wantLoad: &aSubPLS{Foo: "foo", Bar: &aPtrPLS{Count: 1}, Baz: aValuePtrPLS{Count: 11}, S: "LOADED"}, + }, + { + desc: "substruct (ptr) does implement PLS, nil valued substruct", + src: &aSubPLS{Foo: "foo", S: "something"}, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Foo": {ValueType: &pb.Value_StringValue{StringValue: "foo"}}, + "Baz": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Count": {ValueType: &pb.Value_IntegerValue{IntegerValue: 12}}, + }, + }, + }}, + "S": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + }, + }, + wantLoad: &aSubPLS{Foo: "foo", Baz: aValuePtrPLS{Count: 11}, S: "LOADED"}, + }, + { + desc: "substruct (ptr) does not implement PLS", + src: &aSubNotPLS{Foo: "foo", Bar: &aNotPLS{Count: 2}}, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Foo": {ValueType: &pb.Value_StringValue{StringValue: "foo"}}, + "Bar": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Count": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + }}, + }, + }, + wantLoad: &aSubNotPLS{Foo: "foo", Bar: &aNotPLS{Count: 2}}, + }, + { + desc: "substruct (value) does implement PLS, error on save", + src: &aSubPLSErr{Foo: "foo", Bar: aValuePLS{Count: 2}}, + wantSave: (*pb.Entity)(nil), + wantLoad: &aSubPLSErr{}, + saveErr: "PropertyLoadSaver methods must be implemented on a pointer", + }, + { + desc: "substruct (value) does implement PLS, error on load", + src: &aSubPLSNoErr{Foo: "foo", Bar: aPtrPLS{Count: 2}}, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Foo": {ValueType: &pb.Value_StringValue{StringValue: "foo"}}, + "Bar": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Count": {ValueType: &pb.Value_IntegerValue{IntegerValue: 4}}, + }, + }, + }}, + }, + }, + wantLoad: &aSubPLSErr{}, + loadErr: "PropertyLoadSaver methods must be implemented on a pointer", + }, + + { + desc: "parent does not have flatten option, child impl PLS", + src: &Grandparent{ + Parent: Parent{ + Child: Child{ + I: 9, + Grandchild: Grandchild{ + S: "BAD", + }, + }, + String: plsString("something"), + }, + }, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Parent": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Child": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}}, + "Grandchild.S": {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 10"}}, + }, + }, + }}, + "String": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + }, + }, + }}, + }, + }, + wantLoad: &Grandparent{ + Parent: Parent{ + Child: Child{ + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + String: "LOADED", + }, + }, + }, + { + desc: "parent has flatten option enabled, child impl PLS", + src: &GrandparentFlatten{ + Parent: Parent{ + Child: Child{ + I: 7, + Grandchild: Grandchild{ + S: "BAD", + }, + }, + String: plsString("something"), + }, + }, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Parent.Child.I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 8}}, + "Parent.Child.Grandchild.S": {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 8"}}, + "Parent.String.SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + wantLoad: &GrandparentFlatten{ + Parent: Parent{ + Child: Child{ + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + String: "LOADED", + }, + }, + }, + + { + desc: "parent has flatten option enabled, child (ptr to) impl PLS", + src: &GrandparentOfPtrFlatten{ + Parent: ParentOfPtr{ + Child: &Child{ + I: 7, + Grandchild: Grandchild{ + S: "BAD", + }, + }, + String: ptrToplsString("something"), + }, + }, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Parent.Child.I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 8}}, + "Parent.Child.Grandchild.S": {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 8"}}, + "Parent.String.SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + wantLoad: &GrandparentOfPtrFlatten{ + Parent: ParentOfPtr{ + Child: &Child{ + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + String: ptrToplsString("LOADED"), + }, + }, + }, + { + desc: "children (slice of) impl PLS", + src: &GrandparentOfSlice{ + Parent: ParentOfSlice{ + Children: []Child{ + { + I: 7, + Grandchild: Grandchild{ + S: "BAD", + }, + }, + { + I: 9, + Grandchild: Grandchild{ + S: "BAD2", + }, + }, + }, + Strings: []plsString{ + "something1", + "something2", + }, + }, + }, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Parent": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Children": {ValueType: &pb.Value_ArrayValue{ + ArrayValue: &pb.ArrayValue{Values: []*pb.Value{ + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 8}}, + "Grandchild.S": {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 8"}}, + }, + }, + }}, + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}}, + "Grandchild.S": {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 10"}}, + }, + }, + }}, + }}, + }}, + "Strings": {ValueType: &pb.Value_ArrayValue{ + ArrayValue: &pb.ArrayValue{Values: []*pb.Value{ + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + }}, + }}, + }, + }, + }}, + }, + }, + wantLoad: &GrandparentOfSlice{ + Parent: ParentOfSlice{ + Children: []Child{ + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + }, + Strings: []plsString{ + "LOADED", + "LOADED", + }, + }, + }, + }, + { + desc: "children (slice of ptrs) impl PLS", + src: &GrandparentOfSlicePtrs{ + Parent: ParentOfSlicePtrs{ + Children: []*Child{ + { + I: 7, + Grandchild: Grandchild{ + S: "BAD", + }, + }, + { + I: 9, + Grandchild: Grandchild{ + S: "BAD2", + }, + }, + }, + Strings: []*plsString{ + ptrToplsString("something1"), + ptrToplsString("something2"), + }, + }, + }, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Parent": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "Children": {ValueType: &pb.Value_ArrayValue{ + ArrayValue: &pb.ArrayValue{Values: []*pb.Value{ + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 8}}, + "Grandchild.S": {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 8"}}, + }, + }, + }}, + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}}, + "Grandchild.S": {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 10"}}, + }, + }, + }}, + }}, + }}, + "Strings": {ValueType: &pb.Value_ArrayValue{ + ArrayValue: &pb.ArrayValue{Values: []*pb.Value{ + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + }}, + }}, + }, + }, + }}, + }, + }, + wantLoad: &GrandparentOfSlicePtrs{ + Parent: ParentOfSlicePtrs{ + Children: []*Child{ + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + }, + Strings: []*plsString{ + ptrToplsString("LOADED"), + ptrToplsString("LOADED"), + }, + }, + }, + }, + { + desc: "parent has flatten option, children (slice of) impl PLS", + src: &GrandparentOfSliceFlatten{ + Parent: ParentOfSlice{ + Children: []Child{ + { + I: 7, + Grandchild: Grandchild{ + S: "BAD", + }, + }, + { + I: 9, + Grandchild: Grandchild{ + S: "BAD2", + }, + }, + }, + Strings: []plsString{ + "something1", + "something2", + }, + }, + }, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Parent.Children.I": {ValueType: &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_IntegerValue{IntegerValue: 8}}, + {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}}, + }, + }, + }}, + "Parent.Children.Grandchild.S": {ValueType: &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 8"}}, + {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 10"}}, + }, + }, + }}, + "Parent.Strings.SS": {ValueType: &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + }, + }, + wantLoad: &GrandparentOfSliceFlatten{ + Parent: ParentOfSlice{ + Children: []Child{ + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + }, + Strings: []plsString{ + "LOADED", + "LOADED", + }, + }, + }, + }, + { + desc: "parent has flatten option, children (slice of ptrs) impl PLS", + src: &GrandparentOfSlicePtrsFlatten{ + Parent: ParentOfSlicePtrs{ + Children: []*Child{ + { + I: 7, + Grandchild: Grandchild{ + S: "BAD", + }, + }, + { + I: 9, + Grandchild: Grandchild{ + S: "BAD2", + }, + }, + }, + Strings: []*plsString{ + ptrToplsString("something1"), + ptrToplsString("something1"), + }, + }, + }, + wantSave: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Parent.Children.I": {ValueType: &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_IntegerValue{IntegerValue: 8}}, + {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}}, + }, + }, + }}, + "Parent.Children.Grandchild.S": {ValueType: &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 8"}}, + {ValueType: &pb.Value_StringValue{StringValue: "grandchild saved 10"}}, + }, + }, + }}, + "Parent.Strings.SS": {ValueType: &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + {ValueType: &pb.Value_StringValue{StringValue: "SAVED"}}, + }, + }, + }}, + }, + }, + wantLoad: &GrandparentOfSlicePtrsFlatten{ + Parent: ParentOfSlicePtrs{ + Children: []*Child{ + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + { + I: 1, + Grandchild: Grandchild{ + S: "grandchild loaded", + }, + }, + }, + Strings: []*plsString{ + ptrToplsString("LOADED"), + ptrToplsString("LOADED"), + }, + }, + }, + }, + } + + for _, tc := range testCases { + e, err := saveEntity(testKey0, tc.src) + if tc.saveErr == "" { // Want no error. + if err != nil { + t.Errorf("%s: save: %v", tc.desc, err) + continue + } + if !testutil.Equal(e, tc.wantSave) { + t.Errorf("%s: save: \ngot: %+v\nwant: %+v", tc.desc, e, tc.wantSave) + continue + } + } else { // Want error. + if err == nil { + t.Errorf("%s: save: want err", tc.desc) + continue + } + if !strings.Contains(err.Error(), tc.saveErr) { + t.Errorf("%s: save: \ngot err '%s'\nwant err '%s'", tc.desc, err.Error(), tc.saveErr) + } + continue + } + + gota := reflect.New(reflect.TypeOf(tc.wantLoad).Elem()).Interface() + err = loadEntityProto(gota, e) + if tc.loadErr == "" { // Want no error. + if err != nil { + t.Errorf("%s: load: %v", tc.desc, err) + continue + } + if !testutil.Equal(gota, tc.wantLoad) { + t.Errorf("%s: load: \ngot: %+v\nwant: %+v", tc.desc, gota, tc.wantLoad) + continue + } + } else { // Want error. + if err == nil { + t.Errorf("%s: load: want err", tc.desc) + continue + } + if !strings.Contains(err.Error(), tc.loadErr) { + t.Errorf("%s: load: \ngot err '%s'\nwant err '%s'", tc.desc, err.Error(), tc.loadErr) + } + } + } +} + +func TestQueryConstruction(t *testing.T) { + tests := []struct { + q, exp *Query + err string + }{ + { + q: NewQuery("Foo"), + exp: &Query{ + kind: "Foo", + limit: -1, + }, + }, + { + // Regular filtered query with standard spacing. + q: NewQuery("Foo").Filter("foo >", 7), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: greaterThan, + Value: 7, + }, + }, + limit: -1, + }, + }, + { + // Filtered query with no spacing. + q: NewQuery("Foo").Filter("foo=", 6), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: equal, + Value: 6, + }, + }, + limit: -1, + }, + }, + { + // Filtered query with funky spacing. + q: NewQuery("Foo").Filter(" foo< ", 8), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: lessThan, + Value: 8, + }, + }, + limit: -1, + }, + }, + { + // Filtered query with multicharacter op. + q: NewQuery("Foo").Filter("foo >=", 9), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: greaterEq, + Value: 9, + }, + }, + limit: -1, + }, + }, + { + // Query with ordering. + q: NewQuery("Foo").Order("bar"), + exp: &Query{ + kind: "Foo", + order: []order{ + { + FieldName: "bar", + Direction: ascending, + }, + }, + limit: -1, + }, + }, + { + // Query with reverse ordering, and funky spacing. + q: NewQuery("Foo").Order(" - bar"), + exp: &Query{ + kind: "Foo", + order: []order{ + { + FieldName: "bar", + Direction: descending, + }, + }, + limit: -1, + }, + }, + { + // Query with an empty ordering. + q: NewQuery("Foo").Order(""), + err: "empty order", + }, + { + // Query with a + ordering. + q: NewQuery("Foo").Order("+bar"), + err: "invalid order", + }, + } + for i, test := range tests { + if test.q.err != nil { + got := test.q.err.Error() + if !strings.Contains(got, test.err) { + t.Errorf("%d: error mismatch: got %q want something containing %q", i, got, test.err) + } + continue + } + if !testutil.Equal(test.q, test.exp, cmp.AllowUnexported(Query{})) { + t.Errorf("%d: mismatch: got %v want %v", i, test.q, test.exp) + } + } +} + +func TestPutMultiTypes(t *testing.T) { + ctx := context.Background() + type S struct { + A int + B string + } + + testCases := []struct { + desc string + src interface{} + wantErr bool + }{ + // Test cases to check each of the valid input types for src. + // Each case has the same elements. + { + desc: "type []struct", + src: []S{ + {1, "one"}, {2, "two"}, + }, + }, + { + desc: "type []*struct", + src: []*S{ + {1, "one"}, {2, "two"}, + }, + }, + { + desc: "type []interface{} with PLS elems", + src: []interface{}{ + &PropertyList{Property{Name: "A", Value: 1}, Property{Name: "B", Value: "one"}}, + &PropertyList{Property{Name: "A", Value: 2}, Property{Name: "B", Value: "two"}}, + }, + }, + { + desc: "type []interface{} with struct ptr elems", + src: []interface{}{ + &S{1, "one"}, &S{2, "two"}, + }, + }, + { + desc: "type []PropertyLoadSaver{}", + src: []PropertyLoadSaver{ + &PropertyList{Property{Name: "A", Value: 1}, Property{Name: "B", Value: "one"}}, + &PropertyList{Property{Name: "A", Value: 2}, Property{Name: "B", Value: "two"}}, + }, + }, + { + desc: "type []P (non-pointer, *P implements PropertyLoadSaver)", + src: []PropertyList{ + {Property{Name: "A", Value: 1}, Property{Name: "B", Value: "one"}}, + {Property{Name: "A", Value: 2}, Property{Name: "B", Value: "two"}}, + }, + }, + // Test some invalid cases. + { + desc: "type []interface{} with struct elems", + src: []interface{}{ + S{1, "one"}, S{2, "two"}, + }, + wantErr: true, + }, + { + desc: "PropertyList", + src: PropertyList{ + Property{Name: "A", Value: 1}, + Property{Name: "B", Value: "one"}, + }, + wantErr: true, + }, + { + desc: "type []int", + src: []int{1, 2}, + wantErr: true, + }, + { + desc: "not a slice", + src: S{1, "one"}, + wantErr: true, + }, + } + + // Use the same keys and expected entities for all tests. + keys := []*Key{ + NameKey("testKind", "first", nil), + NameKey("testKind", "second", nil), + } + want := []*pb.Mutation{ + {Operation: &pb.Mutation_Upsert{ + Upsert: &pb.Entity{ + Key: keyToProto(keys[0]), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}}, + "B": {ValueType: &pb.Value_StringValue{StringValue: "one"}}, + }, + }}}, + {Operation: &pb.Mutation_Upsert{ + Upsert: &pb.Entity{ + Key: keyToProto(keys[1]), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + "B": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + }, + }}}, + } + + for _, tt := range testCases { + // Set up a fake client which captures upserts. + var got []*pb.Mutation + client := &Client{ + client: &fakeClient{ + commitFn: func(req *pb.CommitRequest) (*pb.CommitResponse, error) { + got = req.Mutations + return &pb.CommitResponse{}, nil + }, + }, + } + + _, err := client.PutMulti(ctx, keys, tt.src) + if err != nil { + if !tt.wantErr { + t.Errorf("%s: error %v", tt.desc, err) + } + continue + } + if tt.wantErr { + t.Errorf("%s: wanted error, but none returned", tt.desc) + continue + } + if len(got) != len(want) { + t.Errorf("%s: got %d entities, want %d", tt.desc, len(got), len(want)) + continue + } + for i, e := range got { + if !proto.Equal(e, want[i]) { + t.Logf("%s: entity %d doesn't match\ngot: %v\nwant: %v", tt.desc, i, e, want[i]) + } + } + } +} + +func TestNoIndexOnSliceProperties(t *testing.T) { + // Check that ExcludeFromIndexes is set on the inner elements, + // rather than the top-level ArrayValue value. + pl := PropertyList{ + Property{ + Name: "repeated", + Value: []interface{}{ + 123, + false, + "short", + strings.Repeat("a", 1503), + }, + NoIndex: true, + }, + } + key := NameKey("dummy", "dummy", nil) + + entity, err := saveEntity(key, &pl) + if err != nil { + t.Fatalf("saveEntity: %v", err) + } + + want := &pb.Value{ + ValueType: &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{Values: []*pb.Value{ + {ValueType: &pb.Value_IntegerValue{IntegerValue: 123}, ExcludeFromIndexes: true}, + {ValueType: &pb.Value_BooleanValue{BooleanValue: false}, ExcludeFromIndexes: true}, + {ValueType: &pb.Value_StringValue{StringValue: "short"}, ExcludeFromIndexes: true}, + {ValueType: &pb.Value_StringValue{StringValue: strings.Repeat("a", 1503)}, ExcludeFromIndexes: true}, + }}}, + } + if got := entity.Properties["repeated"]; !proto.Equal(got, want) { + t.Errorf("Entity proto differs\ngot: %v\nwant: %v", got, want) + } +} + +type byName PropertyList + +func (s byName) Len() int { return len(s) } +func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name } +func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// sortPL sorts the property list by property name, and +// recursively sorts any nested property lists, or nested slices of +// property lists. +func sortPL(pl PropertyList) { + sort.Stable(byName(pl)) + for _, p := range pl { + switch p.Value.(type) { + case *Entity: + sortPL(p.Value.(*Entity).Properties) + case []interface{}: + for _, p2 := range p.Value.([]interface{}) { + if nent, ok := p2.(*Entity); ok { + sortPL(nent.Properties) + } + } + } + } +} + +func TestValidGeoPoint(t *testing.T) { + testCases := []struct { + desc string + pt GeoPoint + want bool + }{ + { + "valid", + GeoPoint{67.21, 13.37}, + true, + }, + { + "high lat", + GeoPoint{-90.01, 13.37}, + false, + }, + { + "low lat", + GeoPoint{90.01, 13.37}, + false, + }, + { + "high lng", + GeoPoint{67.21, 182}, + false, + }, + { + "low lng", + GeoPoint{67.21, -181}, + false, + }, + } + + for _, tc := range testCases { + if got := tc.pt.Valid(); got != tc.want { + t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want) + } + } +} + +func TestPutInvalidEntity(t *testing.T) { + // Test that trying to put an invalid entity always returns the correct error + // type. + + // Fake client that can pretend to start a transaction. + fakeClient := &fakeDatastoreClient{ + beginTransaction: func(*pb.BeginTransactionRequest) (*pb.BeginTransactionResponse, error) { + return &pb.BeginTransactionResponse{ + Transaction: []byte("deadbeef"), + }, nil + }, + } + client := &Client{ + client: fakeClient, + } + + ctx := context.Background() + key := IncompleteKey("kind", nil) + + _, err := client.Put(ctx, key, "invalid entity") + if err != ErrInvalidEntityType { + t.Errorf("client.Put returned err %v, want %v", err, ErrInvalidEntityType) + } + + _, err = client.PutMulti(ctx, []*Key{key}, []interface{}{"invalid entity"}) + if me, ok := err.(MultiError); !ok { + t.Errorf("client.PutMulti returned err %v, want MultiError type", err) + } else if len(me) != 1 || me[0] != ErrInvalidEntityType { + t.Errorf("client.PutMulti returned err %v, want MulitError{ErrInvalidEntityType}", err) + } + + client.RunInTransaction(ctx, func(tx *Transaction) error { + _, err := tx.Put(key, "invalid entity") + if err != ErrInvalidEntityType { + t.Errorf("tx.Put returned err %v, want %v", err, ErrInvalidEntityType) + } + + _, err = tx.PutMulti([]*Key{key}, []interface{}{"invalid entity"}) + if me, ok := err.(MultiError); !ok { + t.Errorf("tx.PutMulti returned err %v, want MultiError type", err) + } else if len(me) != 1 || me[0] != ErrInvalidEntityType { + t.Errorf("tx.PutMulti returned err %v, want MulitError{ErrInvalidEntityType}", err) + } + + return errors.New("bang!") // Return error: we don't actually want to commit. + }) +} + +func TestDeferred(t *testing.T) { + type Ent struct { + A int + B string + } + + keys := []*Key{ + NameKey("testKind", "first", nil), + NameKey("testKind", "second", nil), + } + + entity1 := &pb.Entity{ + Key: keyToProto(keys[0]), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}}, + "B": {ValueType: &pb.Value_StringValue{StringValue: "one"}}, + }, + } + entity2 := &pb.Entity{ + Key: keyToProto(keys[1]), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + "B": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + }, + } + + // count keeps track of the number of times fakeClient.lookup has been + // called. + var count int + // Fake client that will return Deferred keys in resp on the first call. + fakeClient := &fakeDatastoreClient{ + lookup: func(*pb.LookupRequest) (*pb.LookupResponse, error) { + count++ + // On the first call, we return deferred keys. + if count == 1 { + return &pb.LookupResponse{ + Found: []*pb.EntityResult{ + { + Entity: entity1, + Version: 1, + }, + }, + Deferred: []*pb.Key{ + keyToProto(keys[1]), + }, + }, nil + } + + // On the second call, we do not return any more deferred keys. + return &pb.LookupResponse{ + Found: []*pb.EntityResult{ + { + Entity: entity2, + Version: 1, + }, + }, + }, nil + }, + } + client := &Client{ + client: fakeClient, + } + + ctx := context.Background() + + dst := make([]Ent, len(keys)) + err := client.GetMulti(ctx, keys, dst) + if err != nil { + t.Fatalf("client.Get: %v", err) + } + + if count != 2 { + t.Fatalf("expected client.lookup to be called 2 times. Got %d", count) + } + + if len(dst) != 2 { + t.Fatalf("expected 2 entities returned, got %d", len(dst)) + } + + for _, e := range dst { + if e.A == 1 { + if e.B != "one" { + t.Fatalf("unexpected entity %+v", e) + } + } else if e.A == 2 { + if e.B != "two" { + t.Fatalf("unexpected entity %+v", e) + } + } else { + t.Fatalf("unexpected entity %+v", e) + } + } + +} + +type KeyLoaderEnt struct { + A int + K *Key +} + +func (e *KeyLoaderEnt) Load(p []Property) error { + e.A = 2 + return nil +} + +func (e *KeyLoaderEnt) LoadKey(k *Key) error { + e.K = k + return nil +} + +func (e *KeyLoaderEnt) Save() ([]Property, error) { + return []Property{{Name: "A", Value: int64(3)}}, nil +} + +func TestKeyLoaderEndToEnd(t *testing.T) { + keys := []*Key{ + NameKey("testKind", "first", nil), + NameKey("testKind", "second", nil), + } + + entity1 := &pb.Entity{ + Key: keyToProto(keys[0]), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}}, + "B": {ValueType: &pb.Value_StringValue{StringValue: "one"}}, + }, + } + entity2 := &pb.Entity{ + Key: keyToProto(keys[1]), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + "B": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + }, + } + + fakeClient := &fakeDatastoreClient{ + lookup: func(*pb.LookupRequest) (*pb.LookupResponse, error) { + return &pb.LookupResponse{ + Found: []*pb.EntityResult{ + { + Entity: entity1, + Version: 1, + }, + { + Entity: entity2, + Version: 1, + }, + }, + }, nil + }, + } + client := &Client{ + client: fakeClient, + } + + ctx := context.Background() + + dst := make([]*KeyLoaderEnt, len(keys)) + err := client.GetMulti(ctx, keys, dst) + if err != nil { + t.Fatalf("client.Get: %v", err) + } + + for i := range dst { + if !testutil.Equal(dst[i].K, keys[i]) { + t.Fatalf("unexpected entity %d to have key %+v, got %+v", i, keys[i], dst[i].K) + } + } +} + +func TestDeferredMissing(t *testing.T) { + type Ent struct { + A int + B string + } + + keys := []*Key{ + NameKey("testKind", "first", nil), + NameKey("testKind", "second", nil), + } + + entity1 := &pb.Entity{ + Key: keyToProto(keys[0]), + } + entity2 := &pb.Entity{ + Key: keyToProto(keys[1]), + } + + var count int + fakeClient := &fakeDatastoreClient{ + lookup: func(*pb.LookupRequest) (*pb.LookupResponse, error) { + count++ + + if count == 1 { + return &pb.LookupResponse{ + Missing: []*pb.EntityResult{ + { + Entity: entity1, + Version: 1, + }, + }, + Deferred: []*pb.Key{ + keyToProto(keys[1]), + }, + }, nil + } + + return &pb.LookupResponse{ + Missing: []*pb.EntityResult{ + { + Entity: entity2, + Version: 1, + }, + }, + }, nil + }, + } + client := &Client{ + client: fakeClient, + } + + ctx := context.Background() + + dst := make([]Ent, len(keys)) + err := client.GetMulti(ctx, keys, dst) + errs, ok := err.(MultiError) + if !ok { + t.Fatalf("expected error returns to be MultiError; got %v", err) + } + if len(errs) != 2 { + t.Fatalf("expected 2 errors returns, got %d", len(errs)) + } + if errs[0] != ErrNoSuchEntity { + t.Fatalf("expected error to be ErrNoSuchEntity; got %v", errs[0]) + } + if errs[1] != ErrNoSuchEntity { + t.Fatalf("expected error to be ErrNoSuchEntity; got %v", errs[1]) + } + + if count != 2 { + t.Fatalf("expected client.lookup to be called 2 times. Got %d", count) + } + + if len(dst) != 2 { + t.Fatalf("expected 2 entities returned, got %d", len(dst)) + } + + for _, e := range dst { + if e.A != 0 || e.B != "" { + t.Fatalf("unexpected entity %+v", e) + } + } +} + +type fakeDatastoreClient struct { + pb.DatastoreClient + + // Optional handlers for the datastore methods. + // Any handlers left undefined will return an error. + lookup func(*pb.LookupRequest) (*pb.LookupResponse, error) + runQuery func(*pb.RunQueryRequest) (*pb.RunQueryResponse, error) + beginTransaction func(*pb.BeginTransactionRequest) (*pb.BeginTransactionResponse, error) + commit func(*pb.CommitRequest) (*pb.CommitResponse, error) + rollback func(*pb.RollbackRequest) (*pb.RollbackResponse, error) + allocateIds func(*pb.AllocateIdsRequest) (*pb.AllocateIdsResponse, error) +} + +func (c *fakeDatastoreClient) Lookup(ctx context.Context, in *pb.LookupRequest, opts ...grpc.CallOption) (*pb.LookupResponse, error) { + if c.lookup == nil { + return nil, errors.New("no lookup handler defined") + } + return c.lookup(in) +} +func (c *fakeDatastoreClient) RunQuery(ctx context.Context, in *pb.RunQueryRequest, opts ...grpc.CallOption) (*pb.RunQueryResponse, error) { + if c.runQuery == nil { + return nil, errors.New("no runQuery handler defined") + } + return c.runQuery(in) +} +func (c *fakeDatastoreClient) BeginTransaction(ctx context.Context, in *pb.BeginTransactionRequest, opts ...grpc.CallOption) (*pb.BeginTransactionResponse, error) { + if c.beginTransaction == nil { + return nil, errors.New("no beginTransaction handler defined") + } + return c.beginTransaction(in) +} +func (c *fakeDatastoreClient) Commit(ctx context.Context, in *pb.CommitRequest, opts ...grpc.CallOption) (*pb.CommitResponse, error) { + if c.commit == nil { + return nil, errors.New("no commit handler defined") + } + return c.commit(in) +} +func (c *fakeDatastoreClient) Rollback(ctx context.Context, in *pb.RollbackRequest, opts ...grpc.CallOption) (*pb.RollbackResponse, error) { + if c.rollback == nil { + return nil, errors.New("no rollback handler defined") + } + return c.rollback(in) +} +func (c *fakeDatastoreClient) AllocateIds(ctx context.Context, in *pb.AllocateIdsRequest, opts ...grpc.CallOption) (*pb.AllocateIdsResponse, error) { + if c.allocateIds == nil { + return nil, errors.New("no allocateIds handler defined") + } + return c.allocateIds(in) +} diff --git a/vendor/cloud.google.com/go/datastore/doc.go b/vendor/cloud.google.com/go/datastore/doc.go new file mode 100644 index 0000000000..a153869850 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/doc.go @@ -0,0 +1,488 @@ +// 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 datastore provides a client for Google Cloud Datastore. + +See https://godoc.org/cloud.google.com/go for authentication, timeouts, +connection pooling and similar aspects of this package. + + +Basic Operations + +Entities are the unit of storage and are associated with a key. A key +consists of an optional parent key, a string application ID, a string kind +(also known as an entity type), and either a StringID or an IntID. A +StringID is also known as an entity name or key name. + +It is valid to create a key with a zero StringID and a zero IntID; this is +called an incomplete key, and does not refer to any saved entity. Putting an +entity into the datastore under an incomplete key will cause a unique key +to be generated for that entity, with a non-zero IntID. + +An entity's contents are a mapping from case-sensitive field names to values. +Valid value types are: + - signed integers (int, int8, int16, int32 and int64), + - bool, + - string, + - float32 and float64, + - []byte (up to 1 megabyte in length), + - any type whose underlying type is one of the above predeclared types, + - *Key, + - GeoPoint, + - time.Time (stored with microsecond precision), + - structs whose fields are all valid value types, + - pointers to structs whose fields are all valid value types, + - slices of any of the above, + - pointers to a signed integer, bool, string, float32, or float64. + +Slices of structs are valid, as are structs that contain slices. + +The Get and Put functions load and save an entity's contents. An entity's +contents are typically represented by a struct pointer. + +Example code: + + type Entity struct { + Value string + } + + func main() { + ctx := context.Background() + + // Create a datastore client. In a typical application, you would create + // a single client which is reused for every datastore operation. + dsClient, err := datastore.NewClient(ctx, "my-project") + if err != nil { + // Handle error. + } + + k := datastore.NameKey("Entity", "stringID", nil) + e := new(Entity) + if err := dsClient.Get(ctx, k, e); err != nil { + // Handle error. + } + + old := e.Value + e.Value = "Hello World!" + + if _, err := dsClient.Put(ctx, k, e); err != nil { + // Handle error. + } + + fmt.Printf("Updated value from %q to %q\n", old, e.Value) + } + +GetMulti, PutMulti and DeleteMulti are batch versions of the Get, Put and +Delete functions. They take a []*Key instead of a *Key, and may return a +datastore.MultiError when encountering partial failure. + +Mutate generalizes PutMulti and DeleteMulti to a sequence of any Datastore mutations. +It takes a series of mutations created with NewInsert, NewUpdate, NewUpsert and +NewDelete and applies them atomically. + + +Properties + +An entity's contents can be represented by a variety of types. These are +typically struct pointers, but can also be any type that implements the +PropertyLoadSaver interface. If using a struct pointer, you do not have to +explicitly implement the PropertyLoadSaver interface; the datastore will +automatically convert via reflection. If a struct pointer does implement that +interface then those methods will be used in preference to the default +behavior for struct pointers. Struct pointers are more strongly typed and are +easier to use; PropertyLoadSavers are more flexible. + +The actual types passed do not have to match between Get and Put calls or even +across different calls to datastore. It is valid to put a *PropertyList and +get that same entity as a *myStruct, or put a *myStruct0 and get a *myStruct1. +Conceptually, any entity is saved as a sequence of properties, and is loaded +into the destination value on a property-by-property basis. When loading into +a struct pointer, an entity that cannot be completely represented (such as a +missing field) will result in an ErrFieldMismatch error but it is up to the +caller whether this error is fatal, recoverable or ignorable. + +By default, for struct pointers, all properties are potentially indexed, and +the property name is the same as the field name (and hence must start with an +upper case letter). + +Fields may have a `datastore:"name,options"` tag. The tag name is the +property name, which must be one or more valid Go identifiers joined by ".", +but may start with a lower case letter. An empty tag name means to just use the +field name. A "-" tag name means that the datastore will ignore that field. + +The only valid options are "omitempty", "noindex" and "flatten". + +If the options include "omitempty" and the value of the field is empty, then the +field will be omitted on Save. The empty values are false, 0, any nil pointer or +interface value, and any array, slice, map, or string of length zero. Struct field +values will never be empty, except for nil pointers. + +If options include "noindex" then the field will not be indexed. All fields are indexed +by default. Strings or byte slices longer than 1500 bytes cannot be indexed; +fields used to store long strings and byte slices must be tagged with "noindex" +or they will cause Put operations to fail. + +For a nested struct field, the options may also include "flatten". This indicates +that the immediate fields and any nested substruct fields of the nested struct should be +flattened. See below for examples. + +To use multiple options together, separate them by a comma. +The order does not matter. + +If the options is "" then the comma may be omitted. + +Example code: + + // A and B are renamed to a and b. + // A, C and J are not indexed. + // D's tag is equivalent to having no tag at all (E). + // I is ignored entirely by the datastore. + // J has tag information for both the datastore and json packages. + type TaggedStruct struct { + A int `datastore:"a,noindex"` + B int `datastore:"b"` + C int `datastore:",noindex"` + D int `datastore:""` + E int + I int `datastore:"-"` + J int `datastore:",noindex" json:"j"` + } + + +Slice Fields + +A field of slice type corresponds to a Datastore array property, except for []byte, which corresponds +to a Datastore blob. + +Zero-length slice fields are not saved. Slice fields of length 1 or greater are saved +as Datastore arrays. When a zero-length Datastore array is loaded into a slice field, +the slice field remains unchanged. + +If a non-array value is loaded into a slice field, the result will be a slice with +one element, containing the value. + +Loading Nulls + +Loading a Datastore Null into a basic type (int, float, etc.) results in a zero value. +Loading a Null into a slice of basic type results in a slice of size 1 containing the zero value. +Loading a Null into a pointer field results in nil. +Loading a Null into a field of struct type is an error. + +Pointer Fields + +A struct field can be a pointer to a signed integer, floating-point number, string or +bool. Putting a non-nil pointer will store its dereferenced value. Putting a nil +pointer will store a Datastore Null property, unless the field is marked omitempty, +in which case no property will be stored. + +Loading a Null into a pointer field sets the pointer to nil. Loading any other value +allocates new storage with the value, and sets the field to point to it. + + +Key Field + +If the struct contains a *datastore.Key field tagged with the name "__key__", +its value will be ignored on Put. When reading the Entity back into the Go struct, +the field will be populated with the *datastore.Key value used to query for +the Entity. + +Example code: + + type MyEntity struct { + A int + K *datastore.Key `datastore:"__key__"` + } + + k := datastore.NameKey("Entity", "stringID", nil) + e := MyEntity{A: 12} + k, err = dsClient.Put(ctx, k, e) + if err != nil { + // Handle error. + } + + var entities []MyEntity + q := datastore.NewQuery("Entity").Filter("A =", 12).Limit(1) + _, err := dsClient.GetAll(ctx, q, &entities) + if err != nil { + // Handle error + } + + log.Println(entities[0]) + // Prints {12 /Entity,stringID} + + + +Structured Properties + +If the struct pointed to contains other structs, then the nested or embedded +structs are themselves saved as Entity values. For example, given these definitions: + + type Inner struct { + W int32 + X string + } + + type Outer struct { + I Inner + } + +then an Outer would have one property, Inner, encoded as an Entity value. + +If an outer struct is tagged "noindex" then all of its implicit flattened +fields are effectively "noindex". + +If the Inner struct contains a *Key field with the name "__key__", like so: + + type Inner struct { + W int32 + X string + K *datastore.Key `datastore:"__key__"` + } + + type Outer struct { + I Inner + } + +then the value of K will be used as the Key for Inner, represented +as an Entity value in datastore. + +If any nested struct fields should be flattened, instead of encoded as +Entity values, the nested struct field should be tagged with the "flatten" +option. For example, given the following: + + type Inner1 struct { + W int32 + X string + } + + type Inner2 struct { + Y float64 + } + + type Inner3 struct { + Z bool + } + + type Inner4 struct { + WW int + } + + type Inner5 struct { + X Inner4 + } + + type Outer struct { + A int16 + I []Inner1 `datastore:",flatten"` + J Inner2 `datastore:",flatten"` + K Inner5 `datastore:",flatten"` + Inner3 `datastore:",flatten"` + } + +an Outer's properties would be equivalent to those of: + + type OuterEquivalent struct { + A int16 + IDotW []int32 `datastore:"I.W"` + IDotX []string `datastore:"I.X"` + JDotY float64 `datastore:"J.Y"` + KDotXDotWW int `datastore:"K.X.WW"` + Z bool + } + +Note that the "flatten" option cannot be used for Entity value fields. +The server will reject any dotted field names for an Entity value. + + +The PropertyLoadSaver Interface + +An entity's contents can also be represented by any type that implements the +PropertyLoadSaver interface. This type may be a struct pointer, but it does +not have to be. The datastore package will call Load when getting the entity's +contents, and Save when putting the entity's contents. +Possible uses include deriving non-stored fields, verifying fields, or indexing +a field only if its value is positive. + +Example code: + + type CustomPropsExample struct { + I, J int + // Sum is not stored, but should always be equal to I + J. + Sum int `datastore:"-"` + } + + func (x *CustomPropsExample) Load(ps []datastore.Property) error { + // Load I and J as usual. + if err := datastore.LoadStruct(x, ps); err != nil { + return err + } + // Derive the Sum field. + x.Sum = x.I + x.J + return nil + } + + func (x *CustomPropsExample) Save() ([]datastore.Property, error) { + // Validate the Sum field. + if x.Sum != x.I + x.J { + return nil, errors.New("CustomPropsExample has inconsistent sum") + } + // Save I and J as usual. The code below is equivalent to calling + // "return datastore.SaveStruct(x)", but is done manually for + // demonstration purposes. + return []datastore.Property{ + { + Name: "I", + Value: int64(x.I), + }, + { + Name: "J", + Value: int64(x.J), + }, + }, nil + } + +The *PropertyList type implements PropertyLoadSaver, and can therefore hold an +arbitrary entity's contents. + +The KeyLoader Interface + +If a type implements the PropertyLoadSaver interface, it may +also want to implement the KeyLoader interface. +The KeyLoader interface exists to allow implementations of PropertyLoadSaver +to also load an Entity's Key into the Go type. This type may be a struct +pointer, but it does not have to be. The datastore package will call LoadKey +when getting the entity's contents, after calling Load. + +Example code: + + type WithKeyExample struct { + I int + Key *datastore.Key + } + + func (x *WithKeyExample) LoadKey(k *datastore.Key) error { + x.Key = k + return nil + } + + func (x *WithKeyExample) Load(ps []datastore.Property) error { + // Load I as usual. + return datastore.LoadStruct(x, ps) + } + + func (x *WithKeyExample) Save() ([]datastore.Property, error) { + // Save I as usual. + return datastore.SaveStruct(x) + } + +To load a Key into a struct which does not implement the PropertyLoadSaver +interface, see the "Key Field" section above. + + +Queries + +Queries retrieve entities based on their properties or key's ancestry. Running +a query yields an iterator of results: either keys or (key, entity) pairs. +Queries are re-usable and it is safe to call Query.Run from concurrent +goroutines. Iterators are not safe for concurrent use. + +Queries are immutable, and are either created by calling NewQuery, or derived +from an existing query by calling a method like Filter or Order that returns a +new query value. A query is typically constructed by calling NewQuery followed +by a chain of zero or more such methods. These methods are: + - Ancestor and Filter constrain the entities returned by running a query. + - Order affects the order in which they are returned. + - Project constrains the fields returned. + - Distinct de-duplicates projected entities. + - KeysOnly makes the iterator return only keys, not (key, entity) pairs. + - Start, End, Offset and Limit define which sub-sequence of matching entities + to return. Start and End take cursors, Offset and Limit take integers. Start + and Offset affect the first result, End and Limit affect the last result. + If both Start and Offset are set, then the offset is relative to Start. + If both End and Limit are set, then the earliest constraint wins. Limit is + relative to Start+Offset, not relative to End. As a special case, a + negative limit means unlimited. + +Example code: + + type Widget struct { + Description string + Price int + } + + func printWidgets(ctx context.Context, client *datastore.Client) { + q := datastore.NewQuery("Widget"). + Filter("Price <", 1000). + Order("-Price") + for t := client.Run(ctx, q); ; { + var x Widget + key, err := t.Next(&x) + if err == iterator.Done { + break + } + if err != nil { + // Handle error. + } + fmt.Printf("Key=%v\nWidget=%#v\n\n", key, x) + } + } + + +Transactions + +Client.RunInTransaction runs a function in a transaction. + +Example code: + + type Counter struct { + Count int + } + + func incCount(ctx context.Context, client *datastore.Client) { + var count int + key := datastore.NameKey("Counter", "singleton", nil) + _, err := client.RunInTransaction(ctx, func(tx *datastore.Transaction) error { + var x Counter + if err := tx.Get(key, &x); err != nil && err != datastore.ErrNoSuchEntity { + return err + } + x.Count++ + if _, err := tx.Put(key, &x); err != nil { + return err + } + count = x.Count + return nil + }) + if err != nil { + // Handle error. + } + // The value of count is only valid once the transaction is successful + // (RunInTransaction has returned nil). + fmt.Printf("Count=%d\n", count) + } + +Pass the ReadOnly option to RunInTransaction if your transaction is used only for Get, +GetMulti or queries. Read-only transactions are more efficient. + +Google Cloud Datastore Emulator + +This package supports the Cloud Datastore emulator, which is useful for testing and +development. Environment variables are used to indicate that datastore traffic should be +directed to the emulator instead of the production Datastore service. + +To install and set up the emulator and its environment variables, see the documentation +at https://cloud.google.com/datastore/docs/tools/datastore-emulator. +*/ +package datastore // import "cloud.google.com/go/datastore" diff --git a/vendor/cloud.google.com/go/datastore/errors.go b/vendor/cloud.google.com/go/datastore/errors.go new file mode 100644 index 0000000000..ee1f002214 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/errors.go @@ -0,0 +1,47 @@ +// 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. + +// This file provides error functions for common API failure modes. + +package datastore + +import ( + "fmt" +) + +// MultiError is returned by batch operations when there are errors with +// particular elements. Errors will be in a one-to-one correspondence with +// the input elements; successful elements will have a nil entry. +type MultiError []error + +func (m MultiError) Error() string { + s, n := "", 0 + for _, e := range m { + if e != nil { + if n == 0 { + s = e.Error() + } + n++ + } + } + switch n { + case 0: + return "(0 errors)" + case 1: + return s + case 2: + return s + " (and 1 other error)" + } + return fmt.Sprintf("%s (and %d other errors)", s, n-1) +} diff --git a/vendor/cloud.google.com/go/datastore/example_test.go b/vendor/cloud.google.com/go/datastore/example_test.go new file mode 100644 index 0000000000..a1f12c8ad6 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/example_test.go @@ -0,0 +1,567 @@ +// 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 datastore_test + +import ( + "fmt" + "log" + "time" + + "cloud.google.com/go/datastore" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleNewClient() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + _ = client // TODO: Use client. +} + +func ExampleClient_Get() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + type Article struct { + Title string + Description string + Body string `datastore:",noindex"` + Author *datastore.Key + PublishedAt time.Time + } + key := datastore.NameKey("Article", "articled1", nil) + article := &Article{} + if err := client.Get(ctx, key, article); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_Put() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + type Article struct { + Title string + Description string + Body string `datastore:",noindex"` + Author *datastore.Key + PublishedAt time.Time + } + newKey := datastore.IncompleteKey("Article", nil) + _, err = client.Put(ctx, newKey, &Article{ + Title: "The title of the article", + Description: "The description of the article...", + Body: "...", + Author: datastore.NameKey("Author", "jbd", nil), + PublishedAt: time.Now(), + }) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_Put_flatten() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + log.Fatal(err) + } + + type Animal struct { + Name string + Type string + Breed string + } + + type Human struct { + Name string + Height int + Pet Animal `datastore:",flatten"` + } + + newKey := datastore.IncompleteKey("Human", nil) + _, err = client.Put(ctx, newKey, &Human{ + Name: "Susan", + Height: 67, + Pet: Animal{ + Name: "Fluffy", + Type: "Cat", + Breed: "Sphynx", + }, + }) + if err != nil { + log.Fatal(err) + } +} + +func ExampleClient_Delete() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + key := datastore.NameKey("Article", "articled1", nil) + if err := client.Delete(ctx, key); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_DeleteMulti() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + var keys []*datastore.Key + for i := 1; i <= 10; i++ { + keys = append(keys, datastore.IDKey("Article", int64(i), nil)) + } + if err := client.DeleteMulti(ctx, keys); err != nil { + // TODO: Handle error. + } +} + +type Post struct { + Title string + PublishedAt time.Time + Comments int +} + +func ExampleClient_GetMulti() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + keys := []*datastore.Key{ + datastore.NameKey("Post", "post1", nil), + datastore.NameKey("Post", "post2", nil), + datastore.NameKey("Post", "post3", nil), + } + posts := make([]Post, 3) + if err := client.GetMulti(ctx, keys, posts); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_PutMulti_slice() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + keys := []*datastore.Key{ + datastore.NameKey("Post", "post1", nil), + datastore.NameKey("Post", "post2", nil), + } + + // PutMulti with a Post slice. + posts := []*Post{ + {Title: "Post 1", PublishedAt: time.Now()}, + {Title: "Post 2", PublishedAt: time.Now()}, + } + if _, err := client.PutMulti(ctx, keys, posts); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_PutMulti_interfaceSlice() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + keys := []*datastore.Key{ + datastore.NameKey("Post", "post1", nil), + datastore.NameKey("Post", "post2", nil), + } + + // PutMulti with an empty interface slice. + posts := []interface{}{ + &Post{Title: "Post 1", PublishedAt: time.Now()}, + &Post{Title: "Post 2", PublishedAt: time.Now()}, + } + if _, err := client.PutMulti(ctx, keys, posts); err != nil { + // TODO: Handle error. + } +} + +func ExampleNewQuery() { + // Query for Post entities. + q := datastore.NewQuery("Post") + _ = q // TODO: Use the query with Client.Run. +} + +func ExampleNewQuery_options() { + // Query to order the posts by the number of comments they have received. + q := datastore.NewQuery("Post").Order("-Comments") + // Start listing from an offset and limit the results. + q = q.Offset(20).Limit(10) + _ = q // TODO: Use the query. +} + +func ExampleClient_Count() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // Count the number of the post entities. + q := datastore.NewQuery("Post") + n, err := client.Count(ctx, q) + if err != nil { + // TODO: Handle error. + } + fmt.Printf("There are %d posts.", n) +} + +func ExampleClient_Run() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // List the posts published since yesterday. + yesterday := time.Now().Add(-24 * time.Hour) + q := datastore.NewQuery("Post").Filter("PublishedAt >", yesterday) + it := client.Run(ctx, q) + _ = it // TODO: iterate using Next. +} + +func ExampleClient_NewTransaction() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + const retries = 3 + + // Increment a counter. + // See https://cloud.google.com/appengine/articles/sharding_counters for + // a more scalable solution. + type Counter struct { + Count int + } + + key := datastore.NameKey("counter", "CounterA", nil) + var tx *datastore.Transaction + for i := 0; i < retries; i++ { + tx, err = client.NewTransaction(ctx) + if err != nil { + break + } + + var c Counter + if err = tx.Get(key, &c); err != nil && err != datastore.ErrNoSuchEntity { + break + } + c.Count++ + if _, err = tx.Put(key, &c); err != nil { + break + } + + // Attempt to commit the transaction. If there's a conflict, try again. + if _, err = tx.Commit(); err != datastore.ErrConcurrentTransaction { + break + } + } + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_RunInTransaction() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + // Increment a counter. + // See https://cloud.google.com/appengine/articles/sharding_counters for + // a more scalable solution. + type Counter struct { + Count int + } + + var count int + key := datastore.NameKey("Counter", "singleton", nil) + _, err = client.RunInTransaction(ctx, func(tx *datastore.Transaction) error { + var x Counter + if err := tx.Get(key, &x); err != nil && err != datastore.ErrNoSuchEntity { + return err + } + x.Count++ + if _, err := tx.Put(key, &x); err != nil { + return err + } + count = x.Count + return nil + }) + if err != nil { + // TODO: Handle error. + } + // The value of count is only valid once the transaction is successful + // (RunInTransaction has returned nil). + fmt.Printf("Count=%d\n", count) +} + +func ExampleClient_AllocateIDs() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + var keys []*datastore.Key + for i := 0; i < 10; i++ { + keys = append(keys, datastore.IncompleteKey("Article", nil)) + } + keys, err = client.AllocateIDs(ctx, keys) + if err != nil { + // TODO: Handle error. + } + _ = keys // TODO: Use keys. +} + +func ExampleKey_Encode() { + key := datastore.IDKey("Article", 1, nil) + encoded := key.Encode() + fmt.Println(encoded) + // Output: EgsKB0FydGljbGUQAQ +} + +func ExampleDecodeKey() { + const encoded = "EgsKB0FydGljbGUQAQ" + key, err := datastore.DecodeKey(encoded) + if err != nil { + // TODO: Handle error. + } + fmt.Println(key) + // Output: /Article,1 +} + +func ExampleIDKey() { + // Key with numeric ID. + k := datastore.IDKey("Article", 1, nil) + _ = k // TODO: Use key. +} + +func ExampleNameKey() { + // Key with string ID. + k := datastore.NameKey("Article", "article8", nil) + _ = k // TODO: Use key. +} + +func ExampleIncompleteKey() { + k := datastore.IncompleteKey("Article", nil) + _ = k // TODO: Use incomplete key. +} + +func ExampleClient_GetAll() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + var posts []*Post + keys, err := client.GetAll(ctx, datastore.NewQuery("Post"), &posts) + for i, key := range keys { + fmt.Println(key) + fmt.Println(posts[i]) + } +} + +func ExampleClient_Mutate() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + key1 := datastore.NameKey("Post", "post1", nil) + key2 := datastore.NameKey("Post", "post2", nil) + key3 := datastore.NameKey("Post", "post3", nil) + key4 := datastore.NameKey("Post", "post4", nil) + + _, err = client.Mutate(ctx, + datastore.NewInsert(key1, Post{Title: "Post 1"}), + datastore.NewUpsert(key2, Post{Title: "Post 2"}), + datastore.NewUpdate(key3, Post{Title: "Post 3"}), + datastore.NewDelete(key4)) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleCommit_Key() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "") + if err != nil { + // TODO: Handle error. + } + var pk1, pk2 *datastore.PendingKey + // Create two posts in a single transaction. + commit, err := client.RunInTransaction(ctx, func(tx *datastore.Transaction) error { + var err error + pk1, err = tx.Put(datastore.IncompleteKey("Post", nil), &Post{Title: "Post 1", PublishedAt: time.Now()}) + if err != nil { + return err + } + pk2, err = tx.Put(datastore.IncompleteKey("Post", nil), &Post{Title: "Post 2", PublishedAt: time.Now()}) + if err != nil { + return err + } + return nil + }) + if err != nil { + // TODO: Handle error. + } + // Now pk1, pk2 are valid PendingKeys. Let's convert them into real keys + // using the Commit object. + k1 := commit.Key(pk1) + k2 := commit.Key(pk2) + fmt.Println(k1, k2) +} + +func ExampleIterator_Next() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Run(ctx, datastore.NewQuery("Post")) + for { + var p Post + key, err := it.Next(&p) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(key, p) + } +} + +func ExampleIterator_Cursor() { + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Run(ctx, datastore.NewQuery("Post")) + for { + var p Post + _, err := it.Next(&p) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(p) + cursor, err := it.Cursor() + if err != nil { + // TODO: Handle error. + } + // When printed, a cursor will display as a string that can be passed + // to datastore.NewCursor. + fmt.Printf("to resume with this post, use cursor %s\n", cursor) + } +} + +func ExampleDecodeCursor() { + // See Query.Start for a fuller example of DecodeCursor. + // getCursor represents a function that returns a cursor from a previous + // iteration in string form. + cursorString := getCursor() + cursor, err := datastore.DecodeCursor(cursorString) + if err != nil { + // TODO: Handle error. + } + _ = cursor // TODO: Use the cursor with Query.Start or Query.End. +} + +func getCursor() string { return "" } + +func ExampleQuery_Start() { + // This example demonstrates how to use cursors and Query.Start + // to resume an iteration. + ctx := context.Background() + client, err := datastore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // getCursor represents a function that returns a cursor from a previous + // iteration in string form. + cursorString := getCursor() + cursor, err := datastore.DecodeCursor(cursorString) + if err != nil { + // TODO: Handle error. + } + it := client.Run(ctx, datastore.NewQuery("Post").Start(cursor)) + _ = it // TODO: Use iterator. +} + +func ExampleLoadStruct() { + type Player struct { + User string + Score int + } + // Normally LoadStruct would only be used inside a custom implementation of + // PropertyLoadSaver; this is for illustrative purposes only. + props := []datastore.Property{ + {Name: "User", Value: "Alice"}, + {Name: "Score", Value: int64(97)}, + } + + var p Player + if err := datastore.LoadStruct(&p, props); err != nil { + // TODO: Handle error. + } + fmt.Println(p) + // Output: {Alice 97} +} + +func ExampleSaveStruct() { + type Player struct { + User string + Score int + } + + p := &Player{ + User: "Alice", + Score: 97, + } + props, err := datastore.SaveStruct(p) + if err != nil { + // TODO: Handle error. + } + fmt.Println(props) + // TODO(jba): make this output stable: Output: [{User Alice false} {Score 97 false}] +} diff --git a/vendor/cloud.google.com/go/datastore/integration_test.go b/vendor/cloud.google.com/go/datastore/integration_test.go new file mode 100644 index 0000000000..af836ed5d8 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/integration_test.go @@ -0,0 +1,1277 @@ +// 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 datastore + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "log" + "net" + "os" + "reflect" + "sort" + "strings" + "sync" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/rpcreplay" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// TODO(djd): Make test entity clean up more robust: some test entities may +// be left behind if tests are aborted, the transport fails, etc. + +var timeNow = time.Now() + +// suffix is a timestamp-based suffix which is appended to key names, +// particularly for the root keys of entity groups. This reduces flakiness +// when the tests are run in parallel. +var suffix string + +const replayFilename = "datastore.replay" + +type replayInfo struct { + ProjectID string + Time time.Time +} + +var ( + record = flag.Bool("record", false, "record RPCs") + + newTestClient = func(ctx context.Context, t *testing.T) *Client { + return newClient(ctx, t, nil) + } +) + +func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + flag.Parse() + if testing.Short() { + if *record { + log.Fatal("cannot combine -short and -record") + } + if testutil.CanReplay(replayFilename) { + initReplay() + } + } else if *record { + if testutil.ProjID() == "" { + log.Fatal("must record with a project ID") + } + b, err := json.Marshal(replayInfo{ + ProjectID: testutil.ProjID(), + Time: timeNow, + }) + if err != nil { + log.Fatal(err) + } + rec, err := rpcreplay.NewRecorder(replayFilename, b) + if err != nil { + log.Fatal(err) + } + defer func() { + if err := rec.Close(); err != nil { + log.Fatalf("closing recorder: %v", err) + } + }() + newTestClient = func(ctx context.Context, t *testing.T) *Client { + return newClient(ctx, t, rec.DialOptions()) + } + log.Printf("recording to %s", replayFilename) + } + suffix = fmt.Sprintf("-t%d", timeNow.UnixNano()) + return m.Run() +} + +func initReplay() { + rep, err := rpcreplay.NewReplayer(replayFilename) + if err != nil { + log.Fatal(err) + } + defer rep.Close() + + var ri replayInfo + if err := json.Unmarshal(rep.Initial(), &ri); err != nil { + log.Fatalf("unmarshaling initial replay info: %v", err) + } + timeNow = ri.Time.In(time.Local) + + conn, err := replayConn(rep) + if err != nil { + log.Fatal(err) + } + newTestClient = func(ctx context.Context, t *testing.T) *Client { + client, err := NewClient(ctx, ri.ProjectID, option.WithGRPCConn(conn)) + if err != nil { + t.Fatalf("NewClient: %v", err) + } + return client + } + log.Printf("replaying from %s", replayFilename) +} + +func replayConn(rep *rpcreplay.Replayer) (*grpc.ClientConn, error) { + // If we make a real connection we need creds from somewhere, and they + // might not be available, for instance on Travis. + // Replaying doesn't require a connection live at all, but we need + // something to attach gRPC interceptors to. + // So we start a local listener and connect to it, then close them down. + // TODO(jba): build something like this into the replayer? + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + conn, err := grpc.Dial(l.Addr().String(), + append([]grpc.DialOption{grpc.WithInsecure()}, rep.DialOptions()...)...) + if err != nil { + return nil, err + } + conn.Close() + l.Close() + return conn, nil +} + +func newClient(ctx context.Context, t *testing.T, dialOpts []grpc.DialOption) *Client { + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + ts := testutil.TokenSource(ctx, ScopeDatastore) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + opts := []option.ClientOption{option.WithTokenSource(ts)} + for _, opt := range dialOpts { + opts = append(opts, option.WithGRPCDialOption(opt)) + } + client, err := NewClient(ctx, testutil.ProjID(), opts...) + if err != nil { + t.Fatalf("NewClient: %v", err) + } + return client +} + +func TestBasics(t *testing.T) { + ctx, _ := context.WithTimeout(context.Background(), time.Second*20) + client := newTestClient(ctx, t) + defer client.Close() + + type X struct { + I int + S string + T time.Time + } + + x0 := X{66, "99", timeNow.Truncate(time.Millisecond)} + k, err := client.Put(ctx, IncompleteKey("BasicsX", nil), &x0) + if err != nil { + t.Fatalf("client.Put: %v", err) + } + x1 := X{} + err = client.Get(ctx, k, &x1) + if err != nil { + t.Errorf("client.Get: %v", err) + } + err = client.Delete(ctx, k) + if err != nil { + t.Errorf("client.Delete: %v", err) + } + if !testutil.Equal(x0, x1) { + t.Errorf("compare: x0=%v, x1=%v", x0, x1) + } +} + +func TestTopLevelKeyLoaded(t *testing.T) { + ctx, _ := context.WithTimeout(context.Background(), time.Second*20) + client := newTestClient(ctx, t) + defer client.Close() + + completeKey := NameKey("EntityWithKey", "myent", nil) + + type EntityWithKey struct { + I int + S string + K *Key `datastore:"__key__"` + } + + in := &EntityWithKey{ + I: 12, + S: "abcd", + } + + k, err := client.Put(ctx, completeKey, in) + if err != nil { + t.Fatalf("client.Put: %v", err) + } + + var e EntityWithKey + err = client.Get(ctx, k, &e) + if err != nil { + t.Fatalf("client.Get: %v", err) + } + + // The two keys should be absolutely identical. + if !testutil.Equal(e.K, k) { + t.Fatalf("e.K not equal to k; got %#v, want %#v", e.K, k) + } + +} + +func TestListValues(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + p0 := PropertyList{ + {Name: "L", Value: []interface{}{int64(12), "string", true}}, + } + k, err := client.Put(ctx, IncompleteKey("ListValue", nil), &p0) + if err != nil { + t.Fatalf("client.Put: %v", err) + } + var p1 PropertyList + if err := client.Get(ctx, k, &p1); err != nil { + t.Errorf("client.Get: %v", err) + } + if !testutil.Equal(p0, p1) { + t.Errorf("compare:\np0=%v\np1=%#v", p0, p1) + } + if err = client.Delete(ctx, k); err != nil { + t.Errorf("client.Delete: %v", err) + } +} + +func TestGetMulti(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + type X struct { + I int + } + p := NameKey("X", "x"+suffix, nil) + + cases := []struct { + key *Key + put bool + }{ + {key: NameKey("X", "item1", p), put: true}, + {key: NameKey("X", "item2", p), put: false}, + {key: NameKey("X", "item3", p), put: false}, + {key: NameKey("X", "item3", p), put: false}, + {key: NameKey("X", "item4", p), put: true}, + } + + var src, dst []*X + var srcKeys, dstKeys []*Key + for _, c := range cases { + dst = append(dst, &X{}) + dstKeys = append(dstKeys, c.key) + if c.put { + src = append(src, &X{}) + srcKeys = append(srcKeys, c.key) + } + } + if _, err := client.PutMulti(ctx, srcKeys, src); err != nil { + t.Error(err) + } + err := client.GetMulti(ctx, dstKeys, dst) + if err == nil { + t.Errorf("client.GetMulti got %v, expected error", err) + } + e, ok := err.(MultiError) + if !ok { + t.Errorf("client.GetMulti got %T, expected MultiError", err) + } + for i, err := range e { + got, want := err, (error)(nil) + if !cases[i].put { + got, want = err, ErrNoSuchEntity + } + if got != want { + t.Errorf("MultiError[%d] == %v, want %v", i, got, want) + } + } +} + +type Z struct { + S string + T string `datastore:",noindex"` + P []byte + K []byte `datastore:",noindex"` +} + +func (z Z) String() string { + var lens []string + v := reflect.ValueOf(z) + for i := 0; i < v.NumField(); i++ { + if l := v.Field(i).Len(); l > 0 { + lens = append(lens, fmt.Sprintf("len(%s)=%d", v.Type().Field(i).Name, l)) + } + } + return fmt.Sprintf("Z{ %s }", strings.Join(lens, ",")) +} + +func TestUnindexableValues(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + x1500 := strings.Repeat("x", 1500) + x1501 := strings.Repeat("x", 1501) + testCases := []struct { + in Z + wantErr bool + }{ + {in: Z{S: x1500}, wantErr: false}, + {in: Z{S: x1501}, wantErr: true}, + {in: Z{T: x1500}, wantErr: false}, + {in: Z{T: x1501}, wantErr: false}, + {in: Z{P: []byte(x1500)}, wantErr: false}, + {in: Z{P: []byte(x1501)}, wantErr: true}, + {in: Z{K: []byte(x1500)}, wantErr: false}, + {in: Z{K: []byte(x1501)}, wantErr: false}, + } + for _, tt := range testCases { + _, err := client.Put(ctx, IncompleteKey("BasicsZ", nil), &tt.in) + if (err != nil) != tt.wantErr { + t.Errorf("client.Put %s got err %v, want err %t", tt.in, err, tt.wantErr) + } + } +} + +func TestNilKey(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + testCases := []struct { + in K0 + wantErr bool + }{ + {in: K0{K: testKey0}, wantErr: false}, + {in: K0{}, wantErr: false}, + } + for _, tt := range testCases { + _, err := client.Put(ctx, IncompleteKey("NilKey", nil), &tt.in) + if (err != nil) != tt.wantErr { + t.Errorf("client.Put %s got err %v, want err %t", tt.in, err, tt.wantErr) + } + } +} + +type SQChild struct { + I, J int + T, U int64 +} + +type SQTestCase struct { + desc string + q *Query + wantCount int + wantSum int +} + +func testSmallQueries(t *testing.T, ctx context.Context, client *Client, parent *Key, children []*SQChild, + testCases []SQTestCase, extraTests ...func()) { + keys := make([]*Key, len(children)) + for i := range keys { + keys[i] = IncompleteKey("SQChild", parent) + } + keys, err := client.PutMulti(ctx, keys, children) + if err != nil { + t.Fatalf("client.PutMulti: %v", err) + } + defer func() { + err := client.DeleteMulti(ctx, keys) + if err != nil { + t.Errorf("client.DeleteMulti: %v", err) + } + }() + + for _, tc := range testCases { + count, err := client.Count(ctx, tc.q) + if err != nil { + t.Errorf("Count %q: %v", tc.desc, err) + continue + } + if count != tc.wantCount { + t.Errorf("Count %q: got %d want %d", tc.desc, count, tc.wantCount) + continue + } + } + + for _, tc := range testCases { + var got []SQChild + _, err := client.GetAll(ctx, tc.q, &got) + if err != nil { + t.Errorf("client.GetAll %q: %v", tc.desc, err) + continue + } + sum := 0 + for _, c := range got { + sum += c.I + c.J + } + if sum != tc.wantSum { + t.Errorf("sum %q: got %d want %d", tc.desc, sum, tc.wantSum) + continue + } + } + for _, x := range extraTests { + x() + } +} + +func TestFilters(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + parent := NameKey("SQParent", "TestFilters"+suffix, nil) + now := timeNow.Truncate(time.Millisecond).Unix() + children := []*SQChild{ + {I: 0, T: now, U: now}, + {I: 1, T: now, U: now}, + {I: 2, T: now, U: now}, + {I: 3, T: now, U: now}, + {I: 4, T: now, U: now}, + {I: 5, T: now, U: now}, + {I: 6, T: now, U: now}, + {I: 7, T: now, U: now}, + } + baseQuery := NewQuery("SQChild").Ancestor(parent).Filter("T=", now) + testSmallQueries(t, ctx, client, parent, children, []SQTestCase{ + { + "I>1", + baseQuery.Filter("I>", 1), + 6, + 2 + 3 + 4 + 5 + 6 + 7, + }, + { + "I>2 AND I<=5", + baseQuery.Filter("I>", 2).Filter("I<=", 5), + 3, + 3 + 4 + 5, + }, + { + "I>=3 AND I<3", + baseQuery.Filter("I>=", 3).Filter("I<", 3), + 0, + 0, + }, + { + "I=4", + baseQuery.Filter("I=", 4), + 1, + 4, + }, + }, func() { + got := []*SQChild{} + want := []*SQChild{ + {I: 0, T: now, U: now}, + {I: 1, T: now, U: now}, + {I: 2, T: now, U: now}, + {I: 3, T: now, U: now}, + {I: 4, T: now, U: now}, + {I: 5, T: now, U: now}, + {I: 6, T: now, U: now}, + {I: 7, T: now, U: now}, + } + _, err := client.GetAll(ctx, baseQuery.Order("I"), &got) + if err != nil { + t.Errorf("client.GetAll: %v", err) + } + if !testutil.Equal(got, want) { + t.Errorf("compare: got=%v, want=%v", got, want) + } + }, func() { + got := []*SQChild{} + want := []*SQChild{ + {I: 7, T: now, U: now}, + {I: 6, T: now, U: now}, + {I: 5, T: now, U: now}, + {I: 4, T: now, U: now}, + {I: 3, T: now, U: now}, + {I: 2, T: now, U: now}, + {I: 1, T: now, U: now}, + {I: 0, T: now, U: now}, + } + _, err := client.GetAll(ctx, baseQuery.Order("-I"), &got) + if err != nil { + t.Errorf("client.GetAll: %v", err) + } + if !testutil.Equal(got, want) { + t.Errorf("compare: got=%v, want=%v", got, want) + } + }) +} + +type ckey struct{} + +func TestLargeQuery(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + parent := NameKey("LQParent", "TestFilters"+suffix, nil) + now := timeNow.Truncate(time.Millisecond).Unix() + + // Make a large number of children entities. + const n = 800 + children := make([]*SQChild, 0, n) + keys := make([]*Key, 0, n) + for i := 0; i < n; i++ { + children = append(children, &SQChild{I: i, T: now, U: now}) + keys = append(keys, IncompleteKey("SQChild", parent)) + } + + // Store using PutMulti in batches. + const batchSize = 500 + for i := 0; i < n; i = i + 500 { + j := i + batchSize + if j > n { + j = n + } + fullKeys, err := client.PutMulti(ctx, keys[i:j], children[i:j]) + if err != nil { + t.Fatalf("PutMulti(%d, %d): %v", i, j, err) + } + defer func() { + err := client.DeleteMulti(ctx, fullKeys) + if err != nil { + t.Errorf("client.DeleteMulti: %v", err) + } + }() + } + + q := NewQuery("SQChild").Ancestor(parent).Filter("T=", now).Order("I") + + // Wait group to allow us to run query tests in parallel below. + var wg sync.WaitGroup + + // Check we get the expected count and results for various limits/offsets. + queryTests := []struct { + limit, offset, want int + }{ + // Just limit. + {limit: 0, want: 0}, + {limit: 100, want: 100}, + {limit: 501, want: 501}, + {limit: n, want: n}, + {limit: n * 2, want: n}, + {limit: -1, want: n}, + // Just offset. + {limit: -1, offset: 100, want: n - 100}, + {limit: -1, offset: 500, want: n - 500}, + {limit: -1, offset: n, want: 0}, + // Limit and offset. + {limit: 100, offset: 100, want: 100}, + {limit: 1000, offset: 100, want: n - 100}, + {limit: 500, offset: 500, want: n - 500}, + } + for _, tt := range queryTests { + q := q.Limit(tt.limit).Offset(tt.offset) + wg.Add(1) + + go func(limit, offset, want int) { + defer wg.Done() + // Check Count returns the expected number of results. + count, err := client.Count(ctx, q) + if err != nil { + t.Errorf("client.Count(limit=%d offset=%d): %v", limit, offset, err) + return + } + if count != want { + t.Errorf("Count(limit=%d offset=%d) returned %d, want %d", limit, offset, count, want) + } + + var got []SQChild + _, err = client.GetAll(ctx, q, &got) + if err != nil { + t.Errorf("client.GetAll(limit=%d offset=%d): %v", limit, offset, err) + return + } + if len(got) != want { + t.Errorf("GetAll(limit=%d offset=%d) returned %d, want %d", limit, offset, len(got), want) + } + for i, child := range got { + if got, want := child.I, i+offset; got != want { + t.Errorf("GetAll(limit=%d offset=%d) got[%d].I == %d; want %d", limit, offset, i, got, want) + break + } + } + }(tt.limit, tt.offset, tt.want) + } + + // Also check iterator cursor behaviour. + cursorTests := []struct { + limit, offset int // Query limit and offset. + count int // The number of times to call "next" + want int // The I value of the desired element, -1 for "Done". + }{ + // No limits. + {count: 0, limit: -1, want: 0}, + {count: 5, limit: -1, want: 5}, + {count: 500, limit: -1, want: 500}, + {count: 1000, limit: -1, want: -1}, // No more results. + // Limits. + {count: 5, limit: 5, want: 5}, + {count: 500, limit: 5, want: 5}, + {count: 1000, limit: 1000, want: -1}, // No more results. + // Offsets. + {count: 0, offset: 5, limit: -1, want: 5}, + {count: 5, offset: 5, limit: -1, want: 10}, + {count: 200, offset: 500, limit: -1, want: 700}, + {count: 200, offset: 1000, limit: -1, want: -1}, // No more results. + } + for _, tt := range cursorTests { + wg.Add(1) + + go func(count, limit, offset, want int) { + defer wg.Done() + + ctx := context.WithValue(ctx, ckey{}, fmt.Sprintf("c=%d,l=%d,o=%d", count, limit, offset)) + // Run iterator through count calls to Next. + it := client.Run(ctx, q.Limit(limit).Offset(offset).KeysOnly()) + for i := 0; i < count; i++ { + _, err := it.Next(nil) + if err == iterator.Done { + break + } + if err != nil { + t.Errorf("count=%d, limit=%d, offset=%d: it.Next failed at i=%d", count, limit, offset, i) + return + } + } + + // Grab the cursor. + cursor, err := it.Cursor() + if err != nil { + t.Errorf("count=%d, limit=%d, offset=%d: it.Cursor: %v", count, limit, offset, err) + return + } + + // Make a request for the next element. + it = client.Run(ctx, q.Limit(1).Start(cursor)) + var entity SQChild + _, err = it.Next(&entity) + switch { + case want == -1: + if err != iterator.Done { + t.Errorf("count=%d, limit=%d, offset=%d: it.Next from cursor %v, want Done", count, limit, offset, err) + } + case err != nil: + t.Errorf("count=%d, limit=%d, offset=%d: it.Next from cursor: %v, want nil", count, limit, offset, err) + case entity.I != want: + t.Errorf("count=%d, limit=%d, offset=%d: got.I = %d, want %d", count, limit, offset, entity.I, want) + } + }(tt.count, tt.limit, tt.offset, tt.want) + } + wg.Wait() +} + +func TestEventualConsistency(t *testing.T) { + // TODO(jba): either make this actually test eventual consistency, or + // delete it. Currently it behaves the same with or without the + // EventualConsistency call. + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + parent := NameKey("SQParent", "TestEventualConsistency"+suffix, nil) + now := timeNow.Truncate(time.Millisecond).Unix() + children := []*SQChild{ + {I: 0, T: now, U: now}, + {I: 1, T: now, U: now}, + {I: 2, T: now, U: now}, + } + query := NewQuery("SQChild").Ancestor(parent).Filter("T =", now).EventualConsistency() + testSmallQueries(t, ctx, client, parent, children, nil, func() { + got, err := client.Count(ctx, query) + if err != nil { + t.Fatalf("Count: %v", err) + } + if got < 0 || 3 < got { + t.Errorf("Count: got %d, want [0,3]", got) + } + }) +} + +func TestProjection(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + parent := NameKey("SQParent", "TestProjection"+suffix, nil) + now := timeNow.Truncate(time.Millisecond).Unix() + children := []*SQChild{ + {I: 1 << 0, J: 100, T: now, U: now}, + {I: 1 << 1, J: 100, T: now, U: now}, + {I: 1 << 2, J: 200, T: now, U: now}, + {I: 1 << 3, J: 300, T: now, U: now}, + {I: 1 << 4, J: 300, T: now, U: now}, + } + baseQuery := NewQuery("SQChild").Ancestor(parent).Filter("T=", now).Filter("J>", 150) + testSmallQueries(t, ctx, client, parent, children, []SQTestCase{ + { + "project", + baseQuery.Project("J"), + 3, + 200 + 300 + 300, + }, + { + "distinct", + baseQuery.Project("J").Distinct(), + 2, + 200 + 300, + }, + { + "distinct on", + baseQuery.Project("J").DistinctOn("J"), + 2, + 200 + 300, + }, + { + "project on meaningful (GD_WHEN) field", + baseQuery.Project("U"), + 3, + 0, + }, + }) +} + +func TestAllocateIDs(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + keys := make([]*Key, 5) + for i := range keys { + keys[i] = IncompleteKey("AllocID", nil) + } + keys, err := client.AllocateIDs(ctx, keys) + if err != nil { + t.Errorf("AllocID #0 failed: %v", err) + } + if want := len(keys); want != 5 { + t.Errorf("Expected to allocate 5 keys, %d keys are found", want) + } + for _, k := range keys { + if k.Incomplete() { + t.Errorf("Unexpeceted incomplete key found: %v", k) + } + } +} + +func TestGetAllWithFieldMismatch(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + type Fat struct { + X, Y int + } + type Thin struct { + X int + } + + // Ancestor queries (those within an entity group) are strongly consistent + // by default, which prevents a test from being flaky. + // See https://cloud.google.com/appengine/docs/go/datastore/queries#Go_Data_consistency + // for more information. + parent := NameKey("SQParent", "TestGetAllWithFieldMismatch"+suffix, nil) + putKeys := make([]*Key, 3) + for i := range putKeys { + putKeys[i] = IDKey("GetAllThing", int64(10+i), parent) + _, err := client.Put(ctx, putKeys[i], &Fat{X: 20 + i, Y: 30 + i}) + if err != nil { + t.Fatalf("client.Put: %v", err) + } + } + + var got []Thin + want := []Thin{ + {X: 20}, + {X: 21}, + {X: 22}, + } + getKeys, err := client.GetAll(ctx, NewQuery("GetAllThing").Ancestor(parent), &got) + if len(getKeys) != 3 && !testutil.Equal(getKeys, putKeys) { + t.Errorf("client.GetAll: keys differ\ngetKeys=%v\nputKeys=%v", getKeys, putKeys) + } + if !testutil.Equal(got, want) { + t.Errorf("client.GetAll: entities differ\ngot =%v\nwant=%v", got, want) + } + if _, ok := err.(*ErrFieldMismatch); !ok { + t.Errorf("client.GetAll: got err=%v, want ErrFieldMismatch", err) + } +} + +func TestKindlessQueries(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + type Dee struct { + I int + Why string + } + type Dum struct { + I int + Pling string + } + + parent := NameKey("Tweedle", "tweedle"+suffix, nil) + + keys := []*Key{ + NameKey("Dee", "dee0", parent), + NameKey("Dum", "dum1", parent), + NameKey("Dum", "dum2", parent), + NameKey("Dum", "dum3", parent), + } + src := []interface{}{ + &Dee{1, "binary0001"}, + &Dum{2, "binary0010"}, + &Dum{4, "binary0100"}, + &Dum{8, "binary1000"}, + } + keys, err := client.PutMulti(ctx, keys, src) + if err != nil { + t.Fatalf("put: %v", err) + } + + testCases := []struct { + desc string + query *Query + want []int + wantErr string + }{ + { + desc: "Dee", + query: NewQuery("Dee"), + want: []int{1}, + }, + { + desc: "Doh", + query: NewQuery("Doh"), + want: nil}, + { + desc: "Dum", + query: NewQuery("Dum"), + want: []int{2, 4, 8}, + }, + { + desc: "", + query: NewQuery(""), + want: []int{1, 2, 4, 8}, + }, + { + desc: "Kindless filter", + query: NewQuery("").Filter("__key__ =", keys[2]), + want: []int{4}, + }, + { + desc: "Kindless order", + query: NewQuery("").Order("__key__"), + want: []int{1, 2, 4, 8}, + }, + { + desc: "Kindless bad filter", + query: NewQuery("").Filter("I =", 4), + wantErr: "kind is required", + }, + { + desc: "Kindless bad order", + query: NewQuery("").Order("-__key__"), + wantErr: "kind is required for all orders except __key__ ascending", + }, + } +loop: + for _, tc := range testCases { + q := tc.query.Ancestor(parent) + gotCount, err := client.Count(ctx, q) + if err != nil { + if tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr) { + t.Errorf("count %q: err %v, want err %q", tc.desc, err, tc.wantErr) + } + continue + } + if tc.wantErr != "" { + t.Errorf("count %q: want err %q", tc.desc, tc.wantErr) + continue + } + if gotCount != len(tc.want) { + t.Errorf("count %q: got %d want %d", tc.desc, gotCount, len(tc.want)) + continue + } + var got []int + for iter := client.Run(ctx, q); ; { + var dst struct { + I int + Why, Pling string + } + _, err := iter.Next(&dst) + if err == iterator.Done { + break + } + if err != nil { + t.Errorf("iter.Next %q: %v", tc.desc, err) + continue loop + } + got = append(got, dst.I) + } + sort.Ints(got) + if !testutil.Equal(got, tc.want) { + t.Errorf("elems %q: got %+v want %+v", tc.desc, got, tc.want) + continue + } + } +} + +func TestTransaction(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + type Counter struct { + N int + T time.Time + } + + bangErr := errors.New("bang") + tests := []struct { + desc string + causeConflict []bool + retErr []error + want int + wantErr error + }{ + { + desc: "3 attempts, no conflicts", + causeConflict: []bool{false}, + retErr: []error{nil}, + want: 11, + }, + { + desc: "1 attempt, user error", + causeConflict: []bool{false}, + retErr: []error{bangErr}, + wantErr: bangErr, + }, + { + desc: "2 attempts, 1 conflict", + causeConflict: []bool{true, false}, + retErr: []error{nil, nil}, + want: 13, // Each conflict increments by 2. + }, + { + desc: "3 attempts, 3 conflicts", + causeConflict: []bool{true, true, true}, + retErr: []error{nil, nil, nil}, + wantErr: ErrConcurrentTransaction, + }, + } + + for i, tt := range tests { + // Put a new counter. + c := &Counter{N: 10, T: timeNow} + key, err := client.Put(ctx, IncompleteKey("TransCounter", nil), c) + if err != nil { + t.Errorf("%s: client.Put: %v", tt.desc, err) + continue + } + defer client.Delete(ctx, key) + + // Increment the counter in a transaction. + // The test case can manually cause a conflict or return an + // error at each attempt. + var attempts int + _, err = client.RunInTransaction(ctx, func(tx *Transaction) error { + attempts++ + if attempts > len(tt.causeConflict) { + return fmt.Errorf("too many attempts. Got %d, max %d", attempts, len(tt.causeConflict)) + } + + var c Counter + if err := tx.Get(key, &c); err != nil { + return err + } + c.N++ + if _, err := tx.Put(key, &c); err != nil { + return err + } + + if tt.causeConflict[attempts-1] { + c.N += 1 + if _, err := client.Put(ctx, key, &c); err != nil { + return err + } + } + + return tt.retErr[attempts-1] + }, MaxAttempts(i)) + + // Check the error returned by RunInTransaction. + if err != tt.wantErr { + t.Errorf("%s: got err %v, want %v", tt.desc, err, tt.wantErr) + continue + } + if err != nil { + continue + } + + // Check the final value of the counter. + if err := client.Get(ctx, key, c); err != nil { + t.Errorf("%s: client.Get: %v", tt.desc, err) + continue + } + if c.N != tt.want { + t.Errorf("%s: counter N=%d, want N=%d", tt.desc, c.N, tt.want) + } + } +} + +func TestReadOnlyTransaction(t *testing.T) { + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + ctx := context.Background() + client := newClient(ctx, t, nil) + defer client.Close() + + type value struct{ N int } + + // Put a value. + const n = 5 + v := &value{N: n} + key, err := client.Put(ctx, IncompleteKey("roTxn", nil), v) + if err != nil { + t.Fatal(err) + } + defer client.Delete(ctx, key) + + // Read it from a read-only transaction. + _, err = client.RunInTransaction(ctx, func(tx *Transaction) error { + if err := tx.Get(key, v); err != nil { + return err + } + return nil + }, ReadOnly) + if err != nil { + t.Fatal(err) + } + if v.N != n { + t.Fatalf("got %d, want %d", v.N, n) + } + + // Attempting to write from a read-only transaction is an error. + _, err = client.RunInTransaction(ctx, func(tx *Transaction) error { + if _, err := tx.Put(key, v); err != nil { + return err + } + return nil + }, ReadOnly) + if err == nil { + t.Fatal("got nil, want error") + } +} + +func TestNilPointers(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + type X struct { + S string + } + + src := []*X{{"zero"}, {"one"}} + keys := []*Key{IncompleteKey("NilX", nil), IncompleteKey("NilX", nil)} + keys, err := client.PutMulti(ctx, keys, src) + if err != nil { + t.Fatalf("PutMulti: %v", err) + } + + // It's okay to store into a slice of nil *X. + xs := make([]*X, 2) + if err := client.GetMulti(ctx, keys, xs); err != nil { + t.Errorf("GetMulti: %v", err) + } else if !testutil.Equal(xs, src) { + t.Errorf("GetMulti fetched %v, want %v", xs, src) + } + + // It isn't okay to store into a single nil *X. + var x0 *X + if err, want := client.Get(ctx, keys[0], x0), ErrInvalidEntityType; err != want { + t.Errorf("Get: err %v; want %v", err, want) + } + + // Test that deleting with duplicate keys work. + keys = append(keys, keys...) + if err := client.DeleteMulti(ctx, keys); err != nil { + t.Errorf("Delete: %v", err) + } +} + +func TestNestedRepeatedElementNoIndex(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + type Inner struct { + Name string + Value string `datastore:",noindex"` + } + type Outer struct { + Config []Inner + } + m := &Outer{ + Config: []Inner{ + {Name: "short", Value: "a"}, + {Name: "long", Value: strings.Repeat("a", 2000)}, + }, + } + + key := NameKey("Nested", "Nested"+suffix, nil) + if _, err := client.Put(ctx, key, m); err != nil { + t.Fatalf("client.Put: %v", err) + } + if err := client.Delete(ctx, key); err != nil { + t.Fatalf("client.Delete: %v", err) + } +} + +func TestPointerFields(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + want := populatedPointers() + key, err := client.Put(ctx, IncompleteKey("pointers", nil), want) + if err != nil { + t.Fatal(err) + } + var got Pointers + if err := client.Get(ctx, key, &got); err != nil { + t.Fatal(err) + } + if got.Pi == nil || *got.Pi != *want.Pi { + t.Errorf("Pi: got %v, want %v", got.Pi, *want.Pi) + } + if got.Ps == nil || *got.Ps != *want.Ps { + t.Errorf("Ps: got %v, want %v", got.Ps, *want.Ps) + } + if got.Pb == nil || *got.Pb != *want.Pb { + t.Errorf("Pb: got %v, want %v", got.Pb, *want.Pb) + } + if got.Pf == nil || *got.Pf != *want.Pf { + t.Errorf("Pf: got %v, want %v", got.Pf, *want.Pf) + } + if got.Pg == nil || *got.Pg != *want.Pg { + t.Errorf("Pg: got %v, want %v", got.Pg, *want.Pg) + } + if got.Pt == nil || !got.Pt.Equal(*want.Pt) { + t.Errorf("Pt: got %v, want %v", got.Pt, *want.Pt) + } +} + +func TestMutate(t *testing.T) { + // test Client.Mutate + testMutate(t, func(ctx context.Context, client *Client, muts ...*Mutation) ([]*Key, error) { + return client.Mutate(ctx, muts...) + }) + // test Transaction.Mutate + testMutate(t, func(ctx context.Context, client *Client, muts ...*Mutation) ([]*Key, error) { + var pkeys []*PendingKey + commit, err := client.RunInTransaction(ctx, func(tx *Transaction) error { + var err error + pkeys, err = tx.Mutate(muts...) + return err + }) + if err != nil { + return nil, err + } + var keys []*Key + for _, pk := range pkeys { + keys = append(keys, commit.Key(pk)) + } + return keys, nil + }) +} + +func testMutate(t *testing.T, mutate func(ctx context.Context, client *Client, muts ...*Mutation) ([]*Key, error)) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + type T struct{ I int } + + check := func(k *Key, want interface{}) { + var x T + err := client.Get(ctx, k, &x) + switch want := want.(type) { + case error: + if err != want { + t.Errorf("key %s: got error %v, want %v", k, err, want) + } + case int: + if err != nil { + t.Fatalf("key %s: %v", k, err) + } + if x.I != want { + t.Errorf("key %s: got %d, want %d", k, x.I, want) + } + default: + panic("check: bad arg") + } + } + + keys, err := mutate(ctx, client, + NewInsert(IncompleteKey("t", nil), &T{1}), + NewUpsert(IncompleteKey("t", nil), &T{2}), + ) + if err != nil { + t.Fatal(err) + } + check(keys[0], 1) + check(keys[1], 2) + + _, err = mutate(ctx, client, + NewUpdate(keys[0], &T{3}), + NewDelete(keys[1]), + ) + check(keys[0], 3) + check(keys[1], ErrNoSuchEntity) + + _, err = mutate(ctx, client, NewInsert(keys[0], &T{4})) + if got, want := status.Code(err), codes.AlreadyExists; got != want { + t.Errorf("Insert existing key: got %s, want %s", got, want) + } + + _, err = mutate(ctx, client, NewUpdate(keys[1], &T{4})) + if got, want := status.Code(err), codes.NotFound; got != want { + t.Errorf("Update non-existing key: got %s, want %s", got, want) + } +} diff --git a/vendor/cloud.google.com/go/datastore/key.go b/vendor/cloud.google.com/go/datastore/key.go new file mode 100644 index 0000000000..fdcee83b49 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/key.go @@ -0,0 +1,280 @@ +// 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 datastore + +import ( + "bytes" + "encoding/base64" + "encoding/gob" + "errors" + "strconv" + "strings" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +// Key represents the datastore key for a stored entity. +type Key struct { + // Kind cannot be empty. + Kind string + // Either ID or Name must be zero for the Key to be valid. + // If both are zero, the Key is incomplete. + ID int64 + Name string + // Parent must either be a complete Key or nil. + Parent *Key + + // Namespace provides the ability to partition your data for multiple + // tenants. In most cases, it is not necessary to specify a namespace. + // See docs on datastore multitenancy for details: + // https://cloud.google.com/datastore/docs/concepts/multitenancy + Namespace string +} + +// Incomplete reports whether the key does not refer to a stored entity. +func (k *Key) Incomplete() bool { + return k.Name == "" && k.ID == 0 +} + +// valid returns whether the key is valid. +func (k *Key) valid() bool { + if k == nil { + return false + } + for ; k != nil; k = k.Parent { + if k.Kind == "" { + return false + } + if k.Name != "" && k.ID != 0 { + return false + } + if k.Parent != nil { + if k.Parent.Incomplete() { + return false + } + if k.Parent.Namespace != k.Namespace { + return false + } + } + } + return true +} + +// Equal reports whether two keys are equal. Two keys are equal if they are +// both nil, or if their kinds, IDs, names, namespaces and parents are equal. +func (k *Key) Equal(o *Key) bool { + for { + if k == nil || o == nil { + return k == o // if either is nil, both must be nil + } + if k.Namespace != o.Namespace || k.Name != o.Name || k.ID != o.ID || k.Kind != o.Kind { + return false + } + if k.Parent == nil && o.Parent == nil { + return true + } + k = k.Parent + o = o.Parent + } +} + +// marshal marshals the key's string representation to the buffer. +func (k *Key) marshal(b *bytes.Buffer) { + if k.Parent != nil { + k.Parent.marshal(b) + } + b.WriteByte('/') + b.WriteString(k.Kind) + b.WriteByte(',') + if k.Name != "" { + b.WriteString(k.Name) + } else { + b.WriteString(strconv.FormatInt(k.ID, 10)) + } +} + +// String returns a string representation of the key. +func (k *Key) String() string { + if k == nil { + return "" + } + b := bytes.NewBuffer(make([]byte, 0, 512)) + k.marshal(b) + return b.String() +} + +// Note: Fields not renamed compared to appengine gobKey struct +// This ensures gobs created by appengine can be read here, and vice/versa +type gobKey struct { + Kind string + StringID string + IntID int64 + Parent *gobKey + AppID string + Namespace string +} + +func keyToGobKey(k *Key) *gobKey { + if k == nil { + return nil + } + return &gobKey{ + Kind: k.Kind, + StringID: k.Name, + IntID: k.ID, + Parent: keyToGobKey(k.Parent), + Namespace: k.Namespace, + } +} + +func gobKeyToKey(gk *gobKey) *Key { + if gk == nil { + return nil + } + return &Key{ + Kind: gk.Kind, + Name: gk.StringID, + ID: gk.IntID, + Parent: gobKeyToKey(gk.Parent), + Namespace: gk.Namespace, + } +} + +// GobEncode marshals the key into a sequence of bytes +// using an encoding/gob.Encoder. +func (k *Key) GobEncode() ([]byte, error) { + buf := new(bytes.Buffer) + if err := gob.NewEncoder(buf).Encode(keyToGobKey(k)); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// GobDecode unmarshals a sequence of bytes using an encoding/gob.Decoder. +func (k *Key) GobDecode(buf []byte) error { + gk := new(gobKey) + if err := gob.NewDecoder(bytes.NewBuffer(buf)).Decode(gk); err != nil { + return err + } + *k = *gobKeyToKey(gk) + return nil +} + +// MarshalJSON marshals the key into JSON. +func (k *Key) MarshalJSON() ([]byte, error) { + return []byte(`"` + k.Encode() + `"`), nil +} + +// UnmarshalJSON unmarshals a key JSON object into a Key. +func (k *Key) UnmarshalJSON(buf []byte) error { + if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' { + return errors.New("datastore: bad JSON key") + } + k2, err := DecodeKey(string(buf[1 : len(buf)-1])) + if err != nil { + return err + } + *k = *k2 + return nil +} + +// Encode returns an opaque representation of the key +// suitable for use in HTML and URLs. +// This is compatible with the Python and Java runtimes. +func (k *Key) Encode() string { + pKey := keyToProto(k) + + b, err := proto.Marshal(pKey) + if err != nil { + panic(err) + } + + // Trailing padding is stripped. + return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") +} + +// DecodeKey decodes a key from the opaque representation returned by Encode. +func DecodeKey(encoded string) (*Key, error) { + // Re-add padding. + if m := len(encoded) % 4; m != 0 { + encoded += strings.Repeat("=", 4-m) + } + + b, err := base64.URLEncoding.DecodeString(encoded) + if err != nil { + return nil, err + } + + pKey := new(pb.Key) + if err := proto.Unmarshal(b, pKey); err != nil { + return nil, err + } + return protoToKey(pKey) +} + +// AllocateIDs accepts a slice of incomplete keys and returns a +// slice of complete keys that are guaranteed to be valid in the datastore. +func (c *Client) AllocateIDs(ctx context.Context, keys []*Key) ([]*Key, error) { + if keys == nil { + return nil, nil + } + + req := &pb.AllocateIdsRequest{ + ProjectId: c.dataset, + Keys: multiKeyToProto(keys), + } + resp, err := c.client.AllocateIds(ctx, req) + if err != nil { + return nil, err + } + + return multiProtoToKey(resp.Keys) +} + +// IncompleteKey creates a new incomplete key. +// The supplied kind cannot be empty. +// The namespace of the new key is empty. +func IncompleteKey(kind string, parent *Key) *Key { + return &Key{ + Kind: kind, + Parent: parent, + } +} + +// NameKey creates a new key with a name. +// The supplied kind cannot be empty. +// The supplied parent must either be a complete key or nil. +// The namespace of the new key is empty. +func NameKey(kind, name string, parent *Key) *Key { + return &Key{ + Kind: kind, + Name: name, + Parent: parent, + } +} + +// IDKey creates a new key with an ID. +// The supplied kind cannot be empty. +// The supplied parent must either be a complete key or nil. +// The namespace of the new key is empty. +func IDKey(kind string, id int64, parent *Key) *Key { + return &Key{ + Kind: kind, + ID: id, + Parent: parent, + } +} diff --git a/vendor/cloud.google.com/go/datastore/key_test.go b/vendor/cloud.google.com/go/datastore/key_test.go new file mode 100644 index 0000000000..eaa81cf8fa --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/key_test.go @@ -0,0 +1,210 @@ +// 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 datastore + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "testing" +) + +func TestEqual(t *testing.T) { + testCases := []struct { + x, y *Key + equal bool + }{ + { + x: nil, + y: nil, + equal: true, + }, + { + x: &Key{Kind: "kindA"}, + y: &Key{Kind: "kindA"}, + equal: true, + }, + { + x: &Key{Kind: "kindA", Name: "nameA"}, + y: &Key{Kind: "kindA", Name: "nameA"}, + equal: true, + }, + { + x: &Key{Kind: "kindA", Name: "nameA", Namespace: "gopherspace"}, + y: &Key{Kind: "kindA", Name: "nameA", Namespace: "gopherspace"}, + equal: true, + }, + { + x: &Key{Kind: "kindA", ID: 1337, Parent: &Key{Kind: "kindX", Name: "nameX"}}, + y: &Key{Kind: "kindA", ID: 1337, Parent: &Key{Kind: "kindX", Name: "nameX"}}, + equal: true, + }, + { + x: &Key{Kind: "kindA", Name: "nameA"}, + y: &Key{Kind: "kindB", Name: "nameA"}, + equal: false, + }, + { + x: &Key{Kind: "kindA", Name: "nameA"}, + y: &Key{Kind: "kindA", Name: "nameB"}, + equal: false, + }, + { + x: &Key{Kind: "kindA", Name: "nameA"}, + y: &Key{Kind: "kindA", ID: 1337}, + equal: false, + }, + { + x: &Key{Kind: "kindA", Name: "nameA"}, + y: &Key{Kind: "kindA", Name: "nameA", Namespace: "gopherspace"}, + equal: false, + }, + { + x: &Key{Kind: "kindA", ID: 1337, Parent: &Key{Kind: "kindX", Name: "nameX"}}, + y: &Key{Kind: "kindA", ID: 1337, Parent: &Key{Kind: "kindY", Name: "nameX"}}, + equal: false, + }, + { + x: &Key{Kind: "kindA", ID: 1337, Parent: &Key{Kind: "kindX", Name: "nameX"}}, + y: &Key{Kind: "kindA", ID: 1337}, + equal: false, + }, + } + + for _, tt := range testCases { + if got := tt.x.Equal(tt.y); got != tt.equal { + t.Errorf("Equal(%v, %v) = %t; want %t", tt.x, tt.y, got, tt.equal) + } + if got := tt.y.Equal(tt.x); got != tt.equal { + t.Errorf("Equal(%v, %v) = %t; want %t", tt.y, tt.x, got, tt.equal) + } + } +} + +func TestEncoding(t *testing.T) { + testCases := []struct { + k *Key + valid bool + }{ + { + k: nil, + valid: false, + }, + { + k: &Key{}, + valid: false, + }, + { + k: &Key{Kind: "kindA"}, + valid: true, + }, + { + k: &Key{Kind: "kindA", Namespace: "gopherspace"}, + valid: true, + }, + { + k: &Key{Kind: "kindA", Name: "nameA"}, + valid: true, + }, + { + k: &Key{Kind: "kindA", ID: 1337}, + valid: true, + }, + { + k: &Key{Kind: "kindA", Name: "nameA", ID: 1337}, + valid: false, + }, + { + k: &Key{Kind: "kindA", Parent: &Key{Kind: "kindB", Name: "nameB"}}, + valid: true, + }, + { + k: &Key{Kind: "kindA", Parent: &Key{Kind: "kindB"}}, + valid: false, + }, + { + k: &Key{Kind: "kindA", Parent: &Key{Kind: "kindB", Name: "nameB", Namespace: "gopherspace"}}, + valid: false, + }, + } + + for _, tt := range testCases { + if got := tt.k.valid(); got != tt.valid { + t.Errorf("valid(%v) = %t; want %t", tt.k, got, tt.valid) + } + + // Check encoding/decoding for valid keys. + if !tt.valid { + continue + } + enc := tt.k.Encode() + dec, err := DecodeKey(enc) + if err != nil { + t.Errorf("DecodeKey(%q) from %v: %v", enc, tt.k, err) + continue + } + if !tt.k.Equal(dec) { + t.Logf("Proto: %s", keyToProto(tt.k)) + t.Errorf("Decoded key %v not equal to %v", dec, tt.k) + } + + b, err := json.Marshal(tt.k) + if err != nil { + t.Errorf("json.Marshal(%v): %v", tt.k, err) + continue + } + key := &Key{} + if err := json.Unmarshal(b, key); err != nil { + t.Errorf("json.Unmarshal(%s) for key %v: %v", b, tt.k, err) + continue + } + if !tt.k.Equal(key) { + t.Errorf("JSON decoded key %v not equal to %v", dec, tt.k) + } + + buf := &bytes.Buffer{} + gobEnc := gob.NewEncoder(buf) + if err := gobEnc.Encode(tt.k); err != nil { + t.Errorf("gobEnc.Encode(%v): %v", tt.k, err) + continue + } + gobDec := gob.NewDecoder(buf) + key = &Key{} + if err := gobDec.Decode(key); err != nil { + t.Errorf("gobDec.Decode() for key %v: %v", tt.k, err) + } + if !tt.k.Equal(key) { + t.Errorf("gob decoded key %v not equal to %v", dec, tt.k) + } + } +} + +func TestInvalidKeyDecode(t *testing.T) { + // Check that decoding an invalid key returns an err and doesn't panic. + enc := NameKey("Kind", "Foo", nil).Encode() + + invalid := []string{ + "", + "Laboratorio", + enc + "Junk", + enc[:len(enc)-4], + } + for _, enc := range invalid { + key, err := DecodeKey(enc) + if err == nil || key != nil { + t.Errorf("DecodeKey(%q) = %v, %v; want nil, error", enc, key, err) + } + } +} diff --git a/vendor/cloud.google.com/go/datastore/load.go b/vendor/cloud.google.com/go/datastore/load.go new file mode 100644 index 0000000000..61aa3ed28a --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/load.go @@ -0,0 +1,509 @@ +// 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 datastore + +import ( + "fmt" + "reflect" + "strings" + "time" + + "cloud.google.com/go/internal/fields" + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +var ( + typeOfByteSlice = reflect.TypeOf([]byte(nil)) + typeOfTime = reflect.TypeOf(time.Time{}) + typeOfGeoPoint = reflect.TypeOf(GeoPoint{}) + typeOfKeyPtr = reflect.TypeOf(&Key{}) + typeOfEntityPtr = reflect.TypeOf(&Entity{}) +) + +// typeMismatchReason returns a string explaining why the property p could not +// be stored in an entity field of type v.Type(). +func typeMismatchReason(p Property, v reflect.Value) string { + entityType := "empty" + switch p.Value.(type) { + case int64: + entityType = "int" + case bool: + entityType = "bool" + case string: + entityType = "string" + case float64: + entityType = "float" + case *Key: + entityType = "*datastore.Key" + case *Entity: + entityType = "*datastore.Entity" + case GeoPoint: + entityType = "GeoPoint" + case time.Time: + entityType = "time.Time" + case []byte: + entityType = "[]byte" + } + + return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type()) +} + +func overflowReason(x interface{}, v reflect.Value) string { + return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type()) +} + +type propertyLoader struct { + // m holds the number of times a substruct field like "Foo.Bar.Baz" has + // been seen so far. The map is constructed lazily. + m map[string]int +} + +func (l *propertyLoader) load(codec fields.List, structValue reflect.Value, p Property, prev map[string]struct{}) string { + sl, ok := p.Value.([]interface{}) + if !ok { + return l.loadOneElement(codec, structValue, p, prev) + } + for _, val := range sl { + p.Value = val + if errStr := l.loadOneElement(codec, structValue, p, prev); errStr != "" { + return errStr + } + } + return "" +} + +// loadOneElement loads the value of Property p into structValue based on the provided +// codec. codec is used to find the field in structValue into which p should be loaded. +// prev is the set of property names already seen for structValue. +func (l *propertyLoader) loadOneElement(codec fields.List, structValue reflect.Value, p Property, prev map[string]struct{}) string { + var sliceOk bool + var sliceIndex int + var v reflect.Value + + name := p.Name + fieldNames := strings.Split(name, ".") + + for len(fieldNames) > 0 { + var field *fields.Field + + // Start by trying to find a field with name. If none found, + // cut off the last field (delimited by ".") and find its parent + // in the codec. + // eg. for name "A.B.C.D", split off "A.B.C" and try to + // find a field in the codec with this name. + // Loop again with "A.B", etc. + for i := len(fieldNames); i > 0; i-- { + parent := strings.Join(fieldNames[:i], ".") + field = codec.Match(parent) + if field != nil { + fieldNames = fieldNames[i:] + break + } + } + + // If we never found a matching field in the codec, return + // error message. + if field == nil { + return "no such struct field" + } + + v = initField(structValue, field.Index) + if !v.IsValid() { + return "no such struct field" + } + if !v.CanSet() { + return "cannot set struct field" + } + + // If field implements PLS, we delegate loading to the PLS's Load early, + // and stop iterating through fields. + ok, err := plsFieldLoad(v, p, fieldNames) + if err != nil { + return err.Error() + } + if ok { + return "" + } + + if field.Type.Kind() == reflect.Struct { + codec, err = structCache.Fields(field.Type) + if err != nil { + return err.Error() + } + structValue = v + } + + // If the element is a slice, we need to accommodate it. + if v.Kind() == reflect.Slice && v.Type() != typeOfByteSlice { + if l.m == nil { + l.m = make(map[string]int) + } + sliceIndex = l.m[p.Name] + l.m[p.Name] = sliceIndex + 1 + for v.Len() <= sliceIndex { + v.Set(reflect.Append(v, reflect.New(v.Type().Elem()).Elem())) + } + structValue = v.Index(sliceIndex) + + // If structValue implements PLS, we delegate loading to the PLS's + // Load early, and stop iterating through fields. + ok, err := plsFieldLoad(structValue, p, fieldNames) + if err != nil { + return err.Error() + } + if ok { + return "" + } + + if structValue.Type().Kind() == reflect.Struct { + codec, err = structCache.Fields(structValue.Type()) + if err != nil { + return err.Error() + } + } + sliceOk = true + } + } + + var slice reflect.Value + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { + slice = v + v = reflect.New(v.Type().Elem()).Elem() + } else if _, ok := prev[p.Name]; ok && !sliceOk { + // Zero the field back out that was set previously, turns out + // it's a slice and we don't know what to do with it + v.Set(reflect.Zero(v.Type())) + return "multiple-valued property requires a slice field type" + } + + prev[p.Name] = struct{}{} + + if errReason := setVal(v, p); errReason != "" { + // Set the slice back to its zero value. + if slice.IsValid() { + slice.Set(reflect.Zero(slice.Type())) + } + return errReason + } + + if slice.IsValid() { + slice.Index(sliceIndex).Set(v) + } + + return "" +} + +// plsFieldLoad first tries to converts v's value to a PLS, then v's addressed +// value to a PLS. If neither succeeds, plsFieldLoad returns false for first return +// value. Otherwise, the first return value will be true. +// If v is successfully converted to a PLS, plsFieldLoad will then try to Load +// the property p into v (by way of the PLS's Load method). +// +// If the field v has been flattened, the Property's name must be altered +// before calling Load to reflect the field v. +// For example, if our original field name was "A.B.C.D", +// and at this point in iteration we had initialized the field +// corresponding to "A" and have moved into the struct, so that now +// v corresponds to the field named "B", then we want to let the +// PLS handle this field (B)'s subfields ("C", "D"), +// so we send the property to the PLS's Load, renamed to "C.D". +// +// If subfields are present, the field v has been flattened. +func plsFieldLoad(v reflect.Value, p Property, subfields []string) (ok bool, err error) { + vpls, err := plsForLoad(v) + if err != nil { + return false, err + } + + if vpls == nil { + return false, nil + } + + // If Entity, load properties as well as key. + if e, ok := p.Value.(*Entity); ok { + err = loadEntity(vpls, e) + return true, err + } + + // If flattened, we must alter the property's name to reflect + // the field v. + if len(subfields) > 0 { + p.Name = strings.Join(subfields, ".") + } + + return true, vpls.Load([]Property{p}) +} + +// setVal sets 'v' to the value of the Property 'p'. +func setVal(v reflect.Value, p Property) (s string) { + pValue := p.Value + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x, ok := pValue.(int64) + if !ok && pValue != nil { + return typeMismatchReason(p, v) + } + if v.OverflowInt(x) { + return overflowReason(x, v) + } + v.SetInt(x) + case reflect.Bool: + x, ok := pValue.(bool) + if !ok && pValue != nil { + return typeMismatchReason(p, v) + } + v.SetBool(x) + case reflect.String: + x, ok := pValue.(string) + if !ok && pValue != nil { + return typeMismatchReason(p, v) + } + v.SetString(x) + case reflect.Float32, reflect.Float64: + x, ok := pValue.(float64) + if !ok && pValue != nil { + return typeMismatchReason(p, v) + } + if v.OverflowFloat(x) { + return overflowReason(x, v) + } + v.SetFloat(x) + case reflect.Ptr: + // v must be a pointer to either a Key, an Entity, or one of the supported basic types. + if v.Type() != typeOfKeyPtr && v.Type().Elem().Kind() != reflect.Struct && !isValidPointerType(v.Type().Elem()) { + return typeMismatchReason(p, v) + } + + if pValue == nil { + // If v is populated already, set it to nil. + if !v.IsNil() { + v.Set(reflect.New(v.Type()).Elem()) + } + return "" + } + + if x, ok := p.Value.(*Key); ok { + if _, ok := v.Interface().(*Key); !ok { + return typeMismatchReason(p, v) + } + v.Set(reflect.ValueOf(x)) + return "" + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + switch x := pValue.(type) { + case *Entity: + err := loadEntity(v.Interface(), x) + if err != nil { + return err.Error() + } + case int64: + if v.Elem().OverflowInt(x) { + return overflowReason(x, v.Elem()) + } + v.Elem().SetInt(x) + case float64: + if v.Elem().OverflowFloat(x) { + return overflowReason(x, v.Elem()) + } + v.Elem().SetFloat(x) + case bool: + v.Elem().SetBool(x) + case string: + v.Elem().SetString(x) + case GeoPoint, time.Time: + v.Elem().Set(reflect.ValueOf(x)) + default: + return typeMismatchReason(p, v) + } + case reflect.Struct: + switch v.Type() { + case typeOfTime: + x, ok := pValue.(time.Time) + if !ok && pValue != nil { + return typeMismatchReason(p, v) + } + v.Set(reflect.ValueOf(x)) + case typeOfGeoPoint: + x, ok := pValue.(GeoPoint) + if !ok && pValue != nil { + return typeMismatchReason(p, v) + } + v.Set(reflect.ValueOf(x)) + default: + ent, ok := pValue.(*Entity) + if !ok { + return typeMismatchReason(p, v) + } + err := loadEntity(v.Addr().Interface(), ent) + if err != nil { + return err.Error() + } + } + case reflect.Slice: + x, ok := pValue.([]byte) + if !ok && pValue != nil { + return typeMismatchReason(p, v) + } + if v.Type().Elem().Kind() != reflect.Uint8 { + return typeMismatchReason(p, v) + } + v.SetBytes(x) + default: + return typeMismatchReason(p, v) + } + return "" +} + +// initField is similar to reflect's Value.FieldByIndex, in that it +// returns the nested struct field corresponding to index, but it +// initialises any nil pointers encountered when traversing the structure. +func initField(val reflect.Value, index []int) reflect.Value { + for _, i := range index[:len(index)-1] { + val = val.Field(i) + if val.Kind() == reflect.Ptr { + if val.IsNil() { + val.Set(reflect.New(val.Type().Elem())) + } + val = val.Elem() + } + } + return val.Field(index[len(index)-1]) +} + +// loadEntityProto loads an EntityProto into PropertyLoadSaver or struct pointer. +func loadEntityProto(dst interface{}, src *pb.Entity) error { + ent, err := protoToEntity(src) + if err != nil { + return err + } + return loadEntity(dst, ent) +} + +func loadEntity(dst interface{}, ent *Entity) error { + if pls, ok := dst.(PropertyLoadSaver); ok { + err := pls.Load(ent.Properties) + if err != nil { + return err + } + if e, ok := dst.(KeyLoader); ok { + err = e.LoadKey(ent.Key) + } + return err + } + return loadEntityToStruct(dst, ent) +} + +func loadEntityToStruct(dst interface{}, ent *Entity) error { + pls, err := newStructPLS(dst) + if err != nil { + return err + } + + // Try and load key. + keyField := pls.codec.Match(keyFieldName) + if keyField != nil && ent.Key != nil { + pls.v.FieldByIndex(keyField.Index).Set(reflect.ValueOf(ent.Key)) + } + + // Load properties. + return pls.Load(ent.Properties) +} + +func (s structPLS) Load(props []Property) error { + var fieldName, errReason string + var l propertyLoader + + prev := make(map[string]struct{}) + for _, p := range props { + if errStr := l.load(s.codec, s.v, p, prev); errStr != "" { + // We don't return early, as we try to load as many properties as possible. + // It is valid to load an entity into a struct that cannot fully represent it. + // That case returns an error, but the caller is free to ignore it. + fieldName, errReason = p.Name, errStr + } + } + if errReason != "" { + return &ErrFieldMismatch{ + StructType: s.v.Type(), + FieldName: fieldName, + Reason: errReason, + } + } + return nil +} + +func protoToEntity(src *pb.Entity) (*Entity, error) { + props := make([]Property, 0, len(src.Properties)) + for name, val := range src.Properties { + v, err := propToValue(val) + if err != nil { + return nil, err + } + props = append(props, Property{ + Name: name, + Value: v, + NoIndex: val.ExcludeFromIndexes, + }) + } + var key *Key + if src.Key != nil { + // Ignore any error, since nested entity values + // are allowed to have an invalid key. + key, _ = protoToKey(src.Key) + } + + return &Entity{key, props}, nil +} + +// propToValue returns a Go value that represents the PropertyValue. For +// example, a TimestampValue becomes a time.Time. +func propToValue(v *pb.Value) (interface{}, error) { + switch v := v.ValueType.(type) { + case *pb.Value_NullValue: + return nil, nil + case *pb.Value_BooleanValue: + return v.BooleanValue, nil + case *pb.Value_IntegerValue: + return v.IntegerValue, nil + case *pb.Value_DoubleValue: + return v.DoubleValue, nil + case *pb.Value_TimestampValue: + return time.Unix(v.TimestampValue.Seconds, int64(v.TimestampValue.Nanos)), nil + case *pb.Value_KeyValue: + return protoToKey(v.KeyValue) + case *pb.Value_StringValue: + return v.StringValue, nil + case *pb.Value_BlobValue: + return []byte(v.BlobValue), nil + case *pb.Value_GeoPointValue: + return GeoPoint{Lat: v.GeoPointValue.Latitude, Lng: v.GeoPointValue.Longitude}, nil + case *pb.Value_EntityValue: + return protoToEntity(v.EntityValue) + case *pb.Value_ArrayValue: + arr := make([]interface{}, 0, len(v.ArrayValue.Values)) + for _, v := range v.ArrayValue.Values { + vv, err := propToValue(v) + if err != nil { + return nil, err + } + arr = append(arr, vv) + } + return arr, nil + default: + return nil, nil + } +} diff --git a/vendor/cloud.google.com/go/datastore/load_test.go b/vendor/cloud.google.com/go/datastore/load_test.go new file mode 100644 index 0000000000..26ef6a8283 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/load_test.go @@ -0,0 +1,910 @@ +// 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 datastore + +import ( + "reflect" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +type Simple struct { + I int64 +} + +type SimpleWithTag struct { + I int64 `datastore:"II"` +} + +type NestedSimpleWithTag struct { + A SimpleWithTag `datastore:"AA"` +} + +type NestedSliceOfSimple struct { + A []Simple +} + +type SimpleTwoFields struct { + S string + SS string +} + +type NestedSimpleAnonymous struct { + Simple + X string +} + +type NestedSimple struct { + A Simple + I int +} + +type NestedSimple1 struct { + A Simple + X string +} + +type NestedSimple2X struct { + AA NestedSimple + A SimpleTwoFields + S string +} + +type BDotB struct { + B string `datastore:"B.B"` +} + +type ABDotB struct { + A BDotB +} + +type MultiAnonymous struct { + Simple + SimpleTwoFields + X string +} + +func TestLoadEntityNestedLegacy(t *testing.T) { + testCases := []struct { + desc string + src *pb.Entity + want interface{} + }{ + { + desc: "nested", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + "A.I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + want: &NestedSimple1{ + A: Simple{I: 2}, + X: "two", + }, + }, + { + desc: "nested with tag", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "AA.II": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + want: &NestedSimpleWithTag{ + A: SimpleWithTag{I: 2}, + }, + }, + { + desc: "nested with anonymous struct field", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + want: &NestedSimpleAnonymous{ + Simple: Simple{I: 2}, + X: "two", + }, + }, + { + desc: "nested with dotted field tag", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "A.B.B": {ValueType: &pb.Value_StringValue{StringValue: "bb"}}, + }, + }, + want: &ABDotB{ + A: BDotB{ + B: "bb", + }, + }, + }, + { + desc: "nested with multiple anonymous fields", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}}, + "S": {ValueType: &pb.Value_StringValue{StringValue: "S"}}, + "SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}}, + "X": {ValueType: &pb.Value_StringValue{StringValue: "s"}}, + }, + }, + want: &MultiAnonymous{ + Simple: Simple{I: 3}, + SimpleTwoFields: SimpleTwoFields{S: "S", SS: "s"}, + X: "s", + }, + }, + } + + for _, tc := range testCases { + dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() + err := loadEntityProto(dst, tc.src) + if err != nil { + t.Errorf("loadEntityProto: %s: %v", tc.desc, err) + continue + } + + if !testutil.Equal(tc.want, dst) { + t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, dst, tc.want) + } + } +} + +type WithKey struct { + X string + I int + K *Key `datastore:"__key__"` +} + +type NestedWithKey struct { + Y string + N WithKey +} + +var ( + incompleteKey = newKey("", nil) + invalidKey = newKey("s", incompleteKey) +) + +func TestLoadEntityNested(t *testing.T) { + testCases := []struct { + desc string + src *pb.Entity + want interface{} + }{ + { + desc: "nested basic", + src: &pb.Entity{ + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}}, + }, + }, + }}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}}, + }, + }, + want: &NestedSimple{ + A: Simple{I: 3}, + I: 10, + }, + }, + { + desc: "nested with struct tags", + src: &pb.Entity{ + Properties: map[string]*pb.Value{ + "AA": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "II": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}}, + }, + }, + }}, + }, + }, + want: &NestedSimpleWithTag{ + A: SimpleWithTag{I: 1}, + }, + }, + { + desc: "nested 2x", + src: &pb.Entity{ + Properties: map[string]*pb.Value{ + "AA": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}}, + }, + }, + }}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}}, + }, + }, + }}, + "A": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "S": {ValueType: &pb.Value_StringValue{StringValue: "S"}}, + "SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}}, + }, + }, + }}, + "S": {ValueType: &pb.Value_StringValue{StringValue: "SS"}}, + }, + }, + want: &NestedSimple2X{ + AA: NestedSimple{ + A: Simple{I: 3}, + I: 1, + }, + A: SimpleTwoFields{S: "S", SS: "s"}, + S: "SS", + }, + }, + { + desc: "nested anonymous", + src: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}}, + "X": {ValueType: &pb.Value_StringValue{StringValue: "SomeX"}}, + }, + }, + want: &NestedSimpleAnonymous{ + Simple: Simple{I: 3}, + X: "SomeX", + }, + }, + { + desc: "nested simple with slice", + src: &pb.Entity{ + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_ArrayValue{ + ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}}, + }, + }, + }}, + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 4}}, + }, + }, + }}, + }, + }, + }}, + }, + }, + + want: &NestedSliceOfSimple{ + A: []Simple{{I: 3}, {I: 4}}, + }, + }, + { + desc: "nested with multiple anonymous fields", + src: &pb.Entity{ + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}}, + "S": {ValueType: &pb.Value_StringValue{StringValue: "S"}}, + "SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}}, + "X": {ValueType: &pb.Value_StringValue{StringValue: "ss"}}, + }, + }, + want: &MultiAnonymous{ + Simple: Simple{I: 3}, + SimpleTwoFields: SimpleTwoFields{S: "S", SS: "s"}, + X: "ss", + }, + }, + { + desc: "nested with dotted field tag", + src: &pb.Entity{ + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "B.B": {ValueType: &pb.Value_StringValue{StringValue: "bb"}}, + }, + }, + }}, + }, + }, + want: &ABDotB{ + A: BDotB{ + B: "bb", + }, + }, + }, + { + desc: "nested entity with key", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}}, + "N": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Key: keyToProto(testKey1a), + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + }}, + }, + }, + want: &NestedWithKey{ + Y: "yyy", + N: WithKey{ + X: "two", + I: 2, + K: testKey1a, + }, + }, + }, + { + desc: "nested entity with invalid key", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}}, + "N": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Key: keyToProto(invalidKey), + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + }}, + }, + }, + want: &NestedWithKey{ + Y: "yyy", + N: WithKey{ + X: "two", + I: 2, + K: invalidKey, + }, + }, + }, + } + + for _, tc := range testCases { + dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() + err := loadEntityProto(dst, tc.src) + if err != nil { + t.Errorf("loadEntityProto: %s: %v", tc.desc, err) + continue + } + + if !testutil.Equal(tc.want, dst) { + t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, dst, tc.want) + } + } +} + +type NestedStructPtrs struct { + *SimpleTwoFields + Nest *SimpleTwoFields + TwiceNest *NestedSimple2 + I int +} + +type NestedSimple2 struct { + A *Simple + I int +} + +func TestAlreadyPopulatedDst(t *testing.T) { + testCases := []struct { + desc string + src *pb.Entity + dst interface{} + want interface{} + }{ + { + desc: "simple already populated, nil properties", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "I": {ValueType: &pb.Value_NullValue{}}, + }, + }, + dst: &Simple{ + I: 12, + }, + want: &Simple{}, + }, + { + desc: "nested structs already populated", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "SS": {ValueType: &pb.Value_StringValue{StringValue: "world"}}, + }, + }, + dst: &SimpleTwoFields{S: "hello" /* SS: "" */}, + want: &SimpleTwoFields{S: "hello", SS: "world"}, + }, + { + desc: "nested structs already populated, pValues nil", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "S": {ValueType: &pb.Value_NullValue{}}, + "SS": {ValueType: &pb.Value_StringValue{StringValue: "ss hello"}}, + "Nest": {ValueType: &pb.Value_NullValue{}}, + "TwiceNest": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_NullValue{}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + }}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 5}}, + }, + }, + dst: &NestedStructPtrs{ + &SimpleTwoFields{S: "hello" /* SS: "" */}, + &SimpleTwoFields{ /* S: "" */ SS: "twice hello"}, + &NestedSimple2{ + A: &Simple{I: 2}, + /* I: 0 */ + }, + 0, + }, + want: &NestedStructPtrs{ + &SimpleTwoFields{ /* S: "" */ SS: "ss hello"}, + nil, + &NestedSimple2{ + /* A: nil, */ + I: 2, + }, + 5, + }, + }, + } + + for _, tc := range testCases { + err := loadEntityProto(tc.dst, tc.src) + if err != nil { + t.Errorf("loadEntityProto: %s: %v", tc.desc, err) + continue + } + + if !testutil.Equal(tc.want, tc.dst) { + t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, tc.dst, tc.want) + } + } +} + +type PLS0 struct { + A string +} + +func (p *PLS0) Load(props []Property) error { + for _, pp := range props { + if pp.Name == "A" { + p.A = pp.Value.(string) + } + } + return nil +} + +func (p *PLS0) Save() (props []Property, err error) { + return []Property{{Name: "A", Value: p.A}}, nil +} + +type KeyLoader1 struct { + A string + K *Key +} + +func (kl *KeyLoader1) Load(props []Property) error { + for _, pp := range props { + if pp.Name == "A" { + kl.A = pp.Value.(string) + } + } + return nil +} + +func (kl *KeyLoader1) Save() (props []Property, err error) { + return []Property{{Name: "A", Value: kl.A}}, nil +} + +func (kl *KeyLoader1) LoadKey(k *Key) error { + kl.K = k + return nil +} + +type KeyLoader2 struct { + B int + Key *Key +} + +func (kl *KeyLoader2) Load(props []Property) error { + for _, pp := range props { + if pp.Name == "B" { + kl.B = int(pp.Value.(int64)) + } + } + return nil +} + +func (kl *KeyLoader2) Save() (props []Property, err error) { + return []Property{{Name: "B", Value: int64(kl.B)}}, nil +} + +func (kl *KeyLoader2) LoadKey(k *Key) error { + kl.Key = k + return nil +} + +type KeyLoader3 struct { + C bool + K *Key +} + +func (kl *KeyLoader3) Load(props []Property) error { + for _, pp := range props { + if pp.Name == "C" { + kl.C = pp.Value.(bool) + } + } + return nil +} + +func (kl *KeyLoader3) Save() (props []Property, err error) { + return []Property{{Name: "C", Value: kl.C}}, nil +} + +func (kl *KeyLoader3) LoadKey(k *Key) error { + kl.K = k + return nil +} + +type KeyLoader4 struct { + PLS0 + K *Key +} + +func (kl *KeyLoader4) LoadKey(k *Key) error { + kl.K = k + return nil +} + +type NotKeyLoader struct { + A string + K *Key +} + +func (p *NotKeyLoader) Load(props []Property) error { + for _, pp := range props { + if pp.Name == "A" { + p.A = pp.Value.(string) + } + } + return nil +} + +func (p *NotKeyLoader) Save() (props []Property, err error) { + return []Property{{Name: "A", Value: p.A}}, nil +} + +type NotPLSKeyLoader struct { + A string + K *Key `datastore:"__key__"` +} + +type NestedKeyLoaders struct { + Two *KeyLoader2 + Three []*KeyLoader3 + Four *KeyLoader4 + PLS *NotKeyLoader +} + +func TestKeyLoader(t *testing.T) { + testCases := []struct { + desc string + src *pb.Entity + dst interface{} + want interface{} + }{ + { + desc: "simple key loader", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}}, + }, + }, + dst: &KeyLoader1{}, + want: &KeyLoader1{ + A: "hello", + K: testKey0, + }, + }, + { + desc: "simple key loader with unmatched properties", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}}, + "B": {ValueType: &pb.Value_StringValue{StringValue: "unmatched"}}, + }, + }, + dst: &NotPLSKeyLoader{}, + want: &NotPLSKeyLoader{ + A: "hello", + K: testKey0, + }, + }, + { + desc: "embedded PLS key loader", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}}, + }, + }, + dst: &KeyLoader4{}, + want: &KeyLoader4{ + PLS0: PLS0{A: "hello"}, + K: testKey0, + }, + }, + { + desc: "nested key loaders", + src: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Two": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "B": {ValueType: &pb.Value_IntegerValue{IntegerValue: 12}}, + }, + Key: keyToProto(testKey1a), + }, + }}, + "Three": {ValueType: &pb.Value_ArrayValue{ + ArrayValue: &pb.ArrayValue{ + Values: []*pb.Value{ + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "C": {ValueType: &pb.Value_BooleanValue{BooleanValue: true}}, + }, + Key: keyToProto(testKey1b), + }, + }}, + {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "C": {ValueType: &pb.Value_BooleanValue{BooleanValue: false}}, + }, + Key: keyToProto(testKey0), + }, + }}, + }, + }, + }}, + "Four": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_StringValue{StringValue: "testing"}}, + }, + Key: keyToProto(testKey2a), + }, + }}, + "PLS": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "A": {ValueType: &pb.Value_StringValue{StringValue: "something"}}, + }, + + Key: keyToProto(testKey1a), + }, + }}, + }, + }, + dst: &NestedKeyLoaders{}, + want: &NestedKeyLoaders{ + Two: &KeyLoader2{B: 12, Key: testKey1a}, + Three: []*KeyLoader3{ + { + C: true, + K: testKey1b, + }, + { + C: false, + K: testKey0, + }, + }, + Four: &KeyLoader4{ + PLS0: PLS0{A: "testing"}, + K: testKey2a, + }, + PLS: &NotKeyLoader{A: "something"}, + }, + }, + } + + for _, tc := range testCases { + err := loadEntityProto(tc.dst, tc.src) + if err != nil { + // While loadEntityProto may return an error, if that error is + // ErrFieldMismatch, then there is still data in tc.dst to compare. + if _, ok := err.(*ErrFieldMismatch); !ok { + t.Errorf("loadEntityProto: %s: %v", tc.desc, err) + continue + } + } + + if !testutil.Equal(tc.want, tc.dst) { + t.Errorf("%s: compare:\ngot: %+v\nwant: %+v", tc.desc, tc.dst, tc.want) + } + } +} + +func TestLoadPointers(t *testing.T) { + for _, test := range []struct { + desc string + in []Property + want Pointers + }{ + { + desc: "nil properties load as nil pointers", + in: []Property{ + {Name: "Pi", Value: nil}, + {Name: "Ps", Value: nil}, + {Name: "Pb", Value: nil}, + {Name: "Pf", Value: nil}, + {Name: "Pg", Value: nil}, + {Name: "Pt", Value: nil}, + }, + want: Pointers{}, + }, + { + desc: "missing properties load as nil pointers", + in: []Property(nil), + want: Pointers{}, + }, + { + desc: "non-nil properties load as the appropriate values", + in: []Property{ + {Name: "Pi", Value: int64(1)}, + {Name: "Ps", Value: "x"}, + {Name: "Pb", Value: true}, + {Name: "Pf", Value: 3.14}, + {Name: "Pg", Value: GeoPoint{Lat: 1, Lng: 2}}, + {Name: "Pt", Value: time.Unix(100, 0)}, + }, + want: func() Pointers { + p := populatedPointers() + *p.Pi = 1 + *p.Ps = "x" + *p.Pb = true + *p.Pf = 3.14 + *p.Pg = GeoPoint{Lat: 1, Lng: 2} + *p.Pt = time.Unix(100, 0) + return *p + }(), + }, + } { + var got Pointers + if err := LoadStruct(&got, test.in); err != nil { + t.Fatalf("%s: %v", test.desc, err) + } + if !testutil.Equal(got, test.want) { + t.Errorf("%s:\ngot %+v\nwant %+v", test.desc, got, test.want) + } + } +} + +func TestLoadNonArrayIntoSlice(t *testing.T) { + // Loading a non-array value into a slice field results in a slice of size 1. + var got struct{ S []string } + if err := LoadStruct(&got, []Property{{Name: "S", Value: "x"}}); err != nil { + t.Fatal(err) + } + if want := []string{"x"}; !testutil.Equal(got.S, want) { + t.Errorf("got %#v, want %#v", got.S, want) + } +} + +func TestLoadEmptyArrayIntoSlice(t *testing.T) { + // Loading an empty array into a slice field is a no-op. + var got = struct{ S []string }{[]string{"x"}} + if err := LoadStruct(&got, []Property{{Name: "S", Value: []interface{}{}}}); err != nil { + t.Fatal(err) + } + if want := []string{"x"}; !testutil.Equal(got.S, want) { + t.Errorf("got %#v, want %#v", got.S, want) + } +} + +func TestLoadNull(t *testing.T) { + // Loading a Datastore Null into a basic type (int, float, etc.) results in a zero value. + // Loading a Null into a slice of basic type results in a slice of size 1 containing the zero value. + // (As expected from the behavior of slices and nulls with basic types.) + type S struct { + I int64 + F float64 + S string + B bool + A []string + } + got := S{ + I: 1, + F: 1.0, + S: "1", + B: true, + A: []string{"X"}, + } + want := S{A: []string{""}} + props := []Property{{Name: "I"}, {Name: "F"}, {Name: "S"}, {Name: "B"}, {Name: "A"}} + if err := LoadStruct(&got, props); err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + // Loading a Null into a pointer to struct field results in a nil field. + got2 := struct{ X *S }{X: &S{}} + if err := LoadStruct(&got2, []Property{{Name: "X"}}); err != nil { + t.Fatal(err) + } + if got2.X != nil { + t.Errorf("got %v, want nil", got2.X) + } + + // Loading a Null into a struct field is an error. + got3 := struct{ X S }{} + err := LoadStruct(&got3, []Property{{Name: "X"}}) + if err == nil { + t.Error("got nil, want error") + } +} + +// var got2 struct{ S []Pet } +// if err := LoadStruct(&got2, []Property{{Name: "S", Value: nil}}); err != nil { +// t.Fatal(err) +// } + +// } diff --git a/vendor/cloud.google.com/go/datastore/mutation.go b/vendor/cloud.google.com/go/datastore/mutation.go new file mode 100644 index 0000000000..f80f964653 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/mutation.go @@ -0,0 +1,129 @@ +// 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 datastore + +import ( + "fmt" + + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +// A Mutation represents a change to a Datastore entity. +type Mutation struct { + key *Key // needed for transaction PendingKeys and to dedup deletions + mut *pb.Mutation + err error +} + +func (m *Mutation) isDelete() bool { + _, ok := m.mut.Operation.(*pb.Mutation_Delete) + return ok +} + +// NewInsert creates a mutation that will save the entity src into the datastore with +// key k, returning an error if k already exists. +// See Client.Put for valid values of src. +func NewInsert(k *Key, src interface{}) *Mutation { + if !k.valid() { + return &Mutation{err: ErrInvalidKey} + } + p, err := saveEntity(k, src) + if err != nil { + return &Mutation{err: err} + } + return &Mutation{ + key: k, + mut: &pb.Mutation{Operation: &pb.Mutation_Insert{Insert: p}}, + } +} + +// NewUpsert creates a mutation that saves the entity src into the datastore with key +// k, whether or not k exists. See Client.Put for valid values of src. +func NewUpsert(k *Key, src interface{}) *Mutation { + if !k.valid() { + return &Mutation{err: ErrInvalidKey} + } + p, err := saveEntity(k, src) + if err != nil { + return &Mutation{err: err} + } + return &Mutation{ + key: k, + mut: &pb.Mutation{Operation: &pb.Mutation_Upsert{Upsert: p}}, + } +} + +// NewUpdate creates a mutation that replaces the entity in the datastore with key k, +// returning an error if k does not exist. See Client.Put for valid values of src. +func NewUpdate(k *Key, src interface{}) *Mutation { + if !k.valid() { + return &Mutation{err: ErrInvalidKey} + } + if k.Incomplete() { + return &Mutation{err: fmt.Errorf("datastore: can't update the incomplete key: %v", k)} + } + p, err := saveEntity(k, src) + if err != nil { + return &Mutation{err: err} + } + return &Mutation{ + key: k, + mut: &pb.Mutation{Operation: &pb.Mutation_Update{Update: p}}, + } +} + +// NewDelete creates a mutation that deletes the entity with key k. +func NewDelete(k *Key) *Mutation { + if !k.valid() { + return &Mutation{err: ErrInvalidKey} + } + if k.Incomplete() { + return &Mutation{err: fmt.Errorf("datastore: can't delete the incomplete key: %v", k)} + } + return &Mutation{ + key: k, + mut: &pb.Mutation{Operation: &pb.Mutation_Delete{Delete: keyToProto(k)}}, + } +} + +func mutationProtos(muts []*Mutation) ([]*pb.Mutation, error) { + // If any of the mutations have errors, collect and return them. + var merr MultiError + for i, m := range muts { + if m.err != nil { + if merr == nil { + merr = make(MultiError, len(muts)) + } + merr[i] = m.err + } + } + if merr != nil { + return nil, merr + } + var protos []*pb.Mutation + // Collect protos. Remove duplicate deletions (see deleteMutations). + seen := map[string]bool{} + for _, m := range muts { + if m.isDelete() { + ks := m.key.String() + if seen[ks] { + continue + } + seen[ks] = true + } + protos = append(protos, m.mut) + } + return protos, nil +} diff --git a/vendor/cloud.google.com/go/datastore/mutation_test.go b/vendor/cloud.google.com/go/datastore/mutation_test.go new file mode 100644 index 0000000000..0d21141ad9 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/mutation_test.go @@ -0,0 +1,150 @@ +// 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 datastore + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +func TestMutationProtos(t *testing.T) { + var keys []*Key + for i := 1; i <= 4; i++ { + k := IDKey("kind", int64(i), nil) + keys = append(keys, k) + } + entity := &PropertyList{{Name: "n", Value: "v"}} + entityForKey := func(k *Key) *pb.Entity { + return &pb.Entity{ + Key: keyToProto(k), + Properties: map[string]*pb.Value{ + "n": {ValueType: &pb.Value_StringValue{StringValue: "v"}}, + }, + } + } + for _, test := range []struct { + desc string + in []*Mutation + want []*pb.Mutation + }{ + { + desc: "nil", + in: nil, + want: nil, + }, + { + desc: "empty", + in: []*Mutation{}, + want: nil, + }, + { + desc: "various", + in: []*Mutation{ + NewInsert(keys[0], entity), + NewUpsert(keys[1], entity), + NewUpdate(keys[2], entity), + NewDelete(keys[3]), + }, + want: []*pb.Mutation{ + {Operation: &pb.Mutation_Insert{Insert: entityForKey(keys[0])}}, + {Operation: &pb.Mutation_Upsert{Upsert: entityForKey(keys[1])}}, + {Operation: &pb.Mutation_Update{Update: entityForKey(keys[2])}}, + {Operation: &pb.Mutation_Delete{Delete: keyToProto(keys[3])}}, + }, + }, + { + desc: "duplicate deletes", + in: []*Mutation{ + NewDelete(keys[0]), + NewInsert(keys[1], entity), + NewDelete(keys[0]), + NewDelete(keys[2]), + NewDelete(keys[0]), + }, + want: []*pb.Mutation{ + {Operation: &pb.Mutation_Delete{Delete: keyToProto(keys[0])}}, + {Operation: &pb.Mutation_Insert{Insert: entityForKey(keys[1])}}, + {Operation: &pb.Mutation_Delete{Delete: keyToProto(keys[2])}}, + }, + }, + } { + got, err := mutationProtos(test.in) + if err != nil { + t.Errorf("%s: %v", test.desc, err) + continue + } + if diff := testutil.Diff(got, test.want); diff != "" { + t.Errorf("%s: %s", test.desc, diff) + } + } +} + +func TestMutationProtosErrors(t *testing.T) { + entity := &PropertyList{{Name: "n", Value: "v"}} + k := IDKey("kind", 1, nil) + ik := IncompleteKey("kind", nil) + for _, test := range []struct { + desc string + in []*Mutation + want []int // non-nil indexes of MultiError + }{ + { + desc: "invalid key", + in: []*Mutation{ + NewInsert(nil, entity), + NewUpdate(nil, entity), + NewUpsert(nil, entity), + NewDelete(nil), + }, + want: []int{0, 1, 2, 3}, + }, + { + desc: "incomplete key", + in: []*Mutation{ + NewInsert(ik, entity), + NewUpdate(ik, entity), + NewUpsert(ik, entity), + NewDelete(ik), + }, + want: []int{1, 3}, + }, + { + desc: "bad entity", + in: []*Mutation{ + NewInsert(k, 1), + NewUpdate(k, 2), + NewUpsert(k, 3), + }, + want: []int{0, 1, 2}, + }, + } { + _, err := mutationProtos(test.in) + if err == nil { + t.Errorf("%s: got nil, want error", test.desc) + continue + } + var got []int + for i, err := range err.(MultiError) { + if err != nil { + got = append(got, i) + } + } + if !testutil.Equal(got, test.want) { + t.Errorf("%s: got errors at %v, want at %v", test.desc, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/datastore/oc_test.go b/vendor/cloud.google.com/go/datastore/oc_test.go new file mode 100644 index 0000000000..6c4ab400f0 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/oc_test.go @@ -0,0 +1,45 @@ +// 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. + +// +build go1.8 + +package datastore + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" +) + +func TestOCTracing(t *testing.T) { + ctx := context.Background() + client := newTestClient(ctx, t) + defer client.Close() + + te := testutil.NewTestExporter() + defer te.Unregister() + + type SomeValue struct { + S string + } + _, err := client.Put(ctx, IncompleteKey("SomeKey", nil), &SomeValue{"foo"}) + if err != nil { + t.Fatalf("client.Put: %v", err) + } + + if len(te.Spans) == 0 { + t.Fatalf("Expected some span to be created, but got %d", 0) + } +} diff --git a/vendor/cloud.google.com/go/datastore/prop.go b/vendor/cloud.google.com/go/datastore/prop.go new file mode 100644 index 0000000000..47898c9c60 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/prop.go @@ -0,0 +1,342 @@ +// 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 datastore + +import ( + "fmt" + "reflect" + "strings" + "unicode" + + "cloud.google.com/go/internal/fields" +) + +// Entities with more than this many indexed properties will not be saved. +const maxIndexedProperties = 20000 + +// []byte fields more than 1 megabyte long will not be loaded or saved. +const maxBlobLen = 1 << 20 + +// Property is a name/value pair plus some metadata. A datastore entity's +// contents are loaded and saved as a sequence of Properties. Each property +// name must be unique within an entity. +type Property struct { + // Name is the property name. + Name string + // Value is the property value. The valid types are: + // - int64 + // - bool + // - string + // - float64 + // - *Key + // - time.Time + // - GeoPoint + // - []byte (up to 1 megabyte in length) + // - *Entity (representing a nested struct) + // Value can also be: + // - []interface{} where each element is one of the above types + // This set is smaller than the set of valid struct field types that the + // datastore can load and save. A Value's type must be explicitly on + // the list above; it is not sufficient for the underlying type to be + // on that list. For example, a Value of "type myInt64 int64" is + // invalid. Smaller-width integers and floats are also invalid. Again, + // this is more restrictive than the set of valid struct field types. + // + // A Value will have an opaque type when loading entities from an index, + // such as via a projection query. Load entities into a struct instead + // of a PropertyLoadSaver when using a projection query. + // + // A Value may also be the nil interface value; this is equivalent to + // Python's None but not directly representable by a Go struct. Loading + // a nil-valued property into a struct will set that field to the zero + // value. + Value interface{} + // NoIndex is whether the datastore cannot index this property. + // If NoIndex is set to false, []byte and string values are limited to + // 1500 bytes. + NoIndex bool +} + +// An Entity is the value type for a nested struct. +// This type is only used for a Property's Value. +type Entity struct { + Key *Key + Properties []Property +} + +// PropertyLoadSaver can be converted from and to a slice of Properties. +type PropertyLoadSaver interface { + Load([]Property) error + Save() ([]Property, error) +} + +// KeyLoader can store a Key. +type KeyLoader interface { + // PropertyLoadSaver is embedded because a KeyLoader + // must also always implement PropertyLoadSaver. + PropertyLoadSaver + LoadKey(k *Key) error +} + +// PropertyList converts a []Property to implement PropertyLoadSaver. +type PropertyList []Property + +var ( + typeOfPropertyLoadSaver = reflect.TypeOf((*PropertyLoadSaver)(nil)).Elem() + typeOfPropertyList = reflect.TypeOf(PropertyList(nil)) +) + +// Load loads all of the provided properties into l. +// It does not first reset *l to an empty slice. +func (l *PropertyList) Load(p []Property) error { + *l = append(*l, p...) + return nil +} + +// Save saves all of l's properties as a slice of Properties. +func (l *PropertyList) Save() ([]Property, error) { + return *l, nil +} + +// validPropertyName returns whether name consists of one or more valid Go +// identifiers joined by ".". +func validPropertyName(name string) bool { + if name == "" { + return false + } + for _, s := range strings.Split(name, ".") { + if s == "" { + return false + } + first := true + for _, c := range s { + if first { + first = false + if c != '_' && !unicode.IsLetter(c) { + return false + } + } else { + if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return false + } + } + } + } + return true +} + +// parseTag interprets datastore struct field tags +func parseTag(t reflect.StructTag) (name string, keep bool, other interface{}, err error) { + s := t.Get("datastore") + parts := strings.Split(s, ",") + if parts[0] == "-" && len(parts) == 1 { + return "", false, nil, nil + } + if parts[0] != "" && !validPropertyName(parts[0]) { + err = fmt.Errorf("datastore: struct tag has invalid property name: %q", parts[0]) + return "", false, nil, err + } + + var opts saveOpts + if len(parts) > 1 { + for _, p := range parts[1:] { + switch p { + case "flatten": + opts.flatten = true + case "omitempty": + opts.omitEmpty = true + case "noindex": + opts.noIndex = true + default: + err = fmt.Errorf("datastore: struct tag has invalid option: %q", p) + return "", false, nil, err + } + } + other = opts + } + return parts[0], true, other, nil +} + +func validateType(t reflect.Type) error { + if t.Kind() != reflect.Struct { + return fmt.Errorf("datastore: validate called with non-struct type %s", t) + } + + return validateChildType(t, "", false, false, map[reflect.Type]bool{}) +} + +// validateChildType is a recursion helper func for validateType +func validateChildType(t reflect.Type, fieldName string, flatten, prevSlice bool, prevTypes map[reflect.Type]bool) error { + if prevTypes[t] { + return nil + } + prevTypes[t] = true + + switch t.Kind() { + case reflect.Slice: + if flatten && prevSlice { + return fmt.Errorf("datastore: flattening nested structs leads to a slice of slices: field %q", fieldName) + } + return validateChildType(t.Elem(), fieldName, flatten, true, prevTypes) + case reflect.Struct: + if t == typeOfTime || t == typeOfGeoPoint { + return nil + } + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + + // If a named field is unexported, ignore it. An anonymous + // unexported field is processed, because it may contain + // exported fields, which are visible. + exported := (f.PkgPath == "") + if !exported && !f.Anonymous { + continue + } + + _, keep, other, err := parseTag(f.Tag) + // Handle error from parseTag now instead of later (in cache.Fields call). + if err != nil { + return err + } + if !keep { + continue + } + if other != nil { + opts := other.(saveOpts) + flatten = flatten || opts.flatten + } + if err := validateChildType(f.Type, f.Name, flatten, prevSlice, prevTypes); err != nil { + return err + } + } + case reflect.Ptr: + if t == typeOfKeyPtr { + return nil + } + return validateChildType(t.Elem(), fieldName, flatten, prevSlice, prevTypes) + } + return nil +} + +// isLeafType determines whether or not a type is a 'leaf type' +// and should not be recursed into, but considered one field. +func isLeafType(t reflect.Type) bool { + return t == typeOfTime || t == typeOfGeoPoint +} + +// structCache collects the structs whose fields have already been calculated. +var structCache = fields.NewCache(parseTag, validateType, isLeafType) + +// structPLS adapts a struct to be a PropertyLoadSaver. +type structPLS struct { + v reflect.Value + codec fields.List +} + +// newStructPLS returns a structPLS, which implements the +// PropertyLoadSaver interface, for the struct pointer p. +func newStructPLS(p interface{}) (*structPLS, error) { + v := reflect.ValueOf(p) + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return nil, ErrInvalidEntityType + } + v = v.Elem() + f, err := structCache.Fields(v.Type()) + if err != nil { + return nil, err + } + return &structPLS{v, f}, nil +} + +// LoadStruct loads the properties from p to dst. +// dst must be a struct pointer. +// +// The values of dst's unmatched struct fields are not modified, +// and matching slice-typed fields are not reset before appending to +// them. In particular, it is recommended to pass a pointer to a zero +// valued struct on each LoadStruct call. +func LoadStruct(dst interface{}, p []Property) error { + x, err := newStructPLS(dst) + if err != nil { + return err + } + return x.Load(p) +} + +// SaveStruct returns the properties from src as a slice of Properties. +// src must be a struct pointer. +func SaveStruct(src interface{}) ([]Property, error) { + x, err := newStructPLS(src) + if err != nil { + return nil, err + } + return x.Save() +} + +// plsForLoad tries to convert v to a PropertyLoadSaver. +// If successful, plsForLoad returns a settable v as a PropertyLoadSaver. +// +// plsForLoad is intended to be used with nested struct fields which +// may implement PropertyLoadSaver. +// +// v must be settable. +func plsForLoad(v reflect.Value) (PropertyLoadSaver, error) { + var nilPtr bool + if v.Kind() == reflect.Ptr && v.IsNil() { + nilPtr = true + v.Set(reflect.New(v.Type().Elem())) + } + + vpls, err := pls(v) + if nilPtr && (vpls == nil || err != nil) { + // unset v + v.Set(reflect.Zero(v.Type())) + } + + return vpls, err +} + +// plsForSave tries to convert v to a PropertyLoadSaver. +// If successful, plsForSave returns v as a PropertyLoadSaver. +// +// plsForSave is intended to be used with nested struct fields which +// may implement PropertyLoadSaver. +// +// v must be settable. +func plsForSave(v reflect.Value) (PropertyLoadSaver, error) { + switch v.Kind() { + case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface, reflect.Chan, reflect.Func: + // If v is nil, return early. v contains no data to save. + if v.IsNil() { + return nil, nil + } + } + + return pls(v) +} + +func pls(v reflect.Value) (PropertyLoadSaver, error) { + if v.Kind() != reflect.Ptr { + if _, ok := v.Interface().(PropertyLoadSaver); ok { + return nil, fmt.Errorf("datastore: PropertyLoadSaver methods must be implemented on a pointer to %T.", v.Interface()) + } + + v = v.Addr() + } + + vpls, _ := v.Interface().(PropertyLoadSaver) + return vpls, nil +} diff --git a/vendor/cloud.google.com/go/datastore/query.go b/vendor/cloud.google.com/go/datastore/query.go new file mode 100644 index 0000000000..04f1c26377 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/query.go @@ -0,0 +1,784 @@ +// 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 datastore + +import ( + "encoding/base64" + "errors" + "fmt" + "math" + "reflect" + "strconv" + "strings" + + "cloud.google.com/go/internal/trace" + wrapperspb "github.com/golang/protobuf/ptypes/wrappers" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +type operator int + +const ( + lessThan operator = iota + 1 + lessEq + equal + greaterEq + greaterThan + + keyFieldName = "__key__" +) + +var operatorToProto = map[operator]pb.PropertyFilter_Operator{ + lessThan: pb.PropertyFilter_LESS_THAN, + lessEq: pb.PropertyFilter_LESS_THAN_OR_EQUAL, + equal: pb.PropertyFilter_EQUAL, + greaterEq: pb.PropertyFilter_GREATER_THAN_OR_EQUAL, + greaterThan: pb.PropertyFilter_GREATER_THAN, +} + +// filter is a conditional filter on query results. +type filter struct { + FieldName string + Op operator + Value interface{} +} + +type sortDirection bool + +const ( + ascending sortDirection = false + descending sortDirection = true +) + +var sortDirectionToProto = map[sortDirection]pb.PropertyOrder_Direction{ + ascending: pb.PropertyOrder_ASCENDING, + descending: pb.PropertyOrder_DESCENDING, +} + +// order is a sort order on query results. +type order struct { + FieldName string + Direction sortDirection +} + +// NewQuery creates a new Query for a specific entity kind. +// +// An empty kind means to return all entities, including entities created and +// managed by other App Engine features, and is called a kindless query. +// Kindless queries cannot include filters or sort orders on property values. +func NewQuery(kind string) *Query { + return &Query{ + kind: kind, + limit: -1, + } +} + +// Query represents a datastore query. +type Query struct { + kind string + ancestor *Key + filter []filter + order []order + projection []string + + distinct bool + distinctOn []string + keysOnly bool + eventual bool + limit int32 + offset int32 + start []byte + end []byte + + namespace string + + trans *Transaction + + err error +} + +func (q *Query) clone() *Query { + x := *q + // Copy the contents of the slice-typed fields to a new backing store. + if len(q.filter) > 0 { + x.filter = make([]filter, len(q.filter)) + copy(x.filter, q.filter) + } + if len(q.order) > 0 { + x.order = make([]order, len(q.order)) + copy(x.order, q.order) + } + return &x +} + +// Ancestor returns a derivative query with an ancestor filter. +// The ancestor should not be nil. +func (q *Query) Ancestor(ancestor *Key) *Query { + q = q.clone() + if ancestor == nil { + q.err = errors.New("datastore: nil query ancestor") + return q + } + q.ancestor = ancestor + return q +} + +// EventualConsistency returns a derivative query that returns eventually +// consistent results. +// It only has an effect on ancestor queries. +func (q *Query) EventualConsistency() *Query { + q = q.clone() + q.eventual = true + return q +} + +// Namespace returns a derivative query that is associated with the given +// namespace. +// +// A namespace may be used to partition data for multi-tenant applications. +// For details, see https://cloud.google.com/datastore/docs/concepts/multitenancy. +func (q *Query) Namespace(ns string) *Query { + q = q.clone() + q.namespace = ns + return q +} + +// Transaction returns a derivative query that is associated with the given +// transaction. +// +// All reads performed as part of the transaction will come from a single +// consistent snapshot. Furthermore, if the transaction is set to a +// serializable isolation level, another transaction cannot concurrently modify +// the data that is read or modified by this transaction. +func (q *Query) Transaction(t *Transaction) *Query { + q = q.clone() + q.trans = t + return q +} + +// Filter returns a derivative query with a field-based filter. +// The filterStr argument must be a field name followed by optional space, +// followed by an operator, one of ">", "<", ">=", "<=", or "=". +// Fields are compared against the provided value using the operator. +// Multiple filters are AND'ed together. +// Field names which contain spaces, quote marks, or operator characters +// should be passed as quoted Go string literals as returned by strconv.Quote +// or the fmt package's %q verb. +func (q *Query) Filter(filterStr string, value interface{}) *Query { + q = q.clone() + filterStr = strings.TrimSpace(filterStr) + if filterStr == "" { + q.err = fmt.Errorf("datastore: invalid filter %q", filterStr) + return q + } + f := filter{ + FieldName: strings.TrimRight(filterStr, " ><=!"), + Value: value, + } + switch op := strings.TrimSpace(filterStr[len(f.FieldName):]); op { + case "<=": + f.Op = lessEq + case ">=": + f.Op = greaterEq + case "<": + f.Op = lessThan + case ">": + f.Op = greaterThan + case "=": + f.Op = equal + default: + q.err = fmt.Errorf("datastore: invalid operator %q in filter %q", op, filterStr) + return q + } + var err error + f.FieldName, err = unquote(f.FieldName) + if err != nil { + q.err = fmt.Errorf("datastore: invalid syntax for quoted field name %q", f.FieldName) + return q + } + q.filter = append(q.filter, f) + return q +} + +// Order returns a derivative query with a field-based sort order. Orders are +// applied in the order they are added. The default order is ascending; to sort +// in descending order prefix the fieldName with a minus sign (-). +// Field names which contain spaces, quote marks, or the minus sign +// should be passed as quoted Go string literals as returned by strconv.Quote +// or the fmt package's %q verb. +func (q *Query) Order(fieldName string) *Query { + q = q.clone() + fieldName, dir := strings.TrimSpace(fieldName), ascending + if strings.HasPrefix(fieldName, "-") { + fieldName, dir = strings.TrimSpace(fieldName[1:]), descending + } else if strings.HasPrefix(fieldName, "+") { + q.err = fmt.Errorf("datastore: invalid order: %q", fieldName) + return q + } + fieldName, err := unquote(fieldName) + if err != nil { + q.err = fmt.Errorf("datastore: invalid syntax for quoted field name %q", fieldName) + return q + } + if fieldName == "" { + q.err = errors.New("datastore: empty order") + return q + } + q.order = append(q.order, order{ + Direction: dir, + FieldName: fieldName, + }) + return q +} + +// unquote optionally interprets s as a double-quoted or backquoted Go +// string literal if it begins with the relevant character. +func unquote(s string) (string, error) { + if s == "" || (s[0] != '`' && s[0] != '"') { + return s, nil + } + return strconv.Unquote(s) +} + +// Project returns a derivative query that yields only the given fields. It +// cannot be used with KeysOnly. +func (q *Query) Project(fieldNames ...string) *Query { + q = q.clone() + q.projection = append([]string(nil), fieldNames...) + return q +} + +// Distinct returns a derivative query that yields de-duplicated entities with +// respect to the set of projected fields. It is only used for projection +// queries. Distinct cannot be used with DistinctOn. +func (q *Query) Distinct() *Query { + q = q.clone() + q.distinct = true + return q +} + +// DistinctOn returns a derivative query that yields de-duplicated entities with +// respect to the set of the specified fields. It is only used for projection +// queries. The field list should be a subset of the projected field list. +// DistinctOn cannot be used with Distinct. +func (q *Query) DistinctOn(fieldNames ...string) *Query { + q = q.clone() + q.distinctOn = fieldNames + return q +} + +// KeysOnly returns a derivative query that yields only keys, not keys and +// entities. It cannot be used with projection queries. +func (q *Query) KeysOnly() *Query { + q = q.clone() + q.keysOnly = true + return q +} + +// Limit returns a derivative query that has a limit on the number of results +// returned. A negative value means unlimited. +func (q *Query) Limit(limit int) *Query { + q = q.clone() + if limit < math.MinInt32 || limit > math.MaxInt32 { + q.err = errors.New("datastore: query limit overflow") + return q + } + q.limit = int32(limit) + return q +} + +// Offset returns a derivative query that has an offset of how many keys to +// skip over before returning results. A negative value is invalid. +func (q *Query) Offset(offset int) *Query { + q = q.clone() + if offset < 0 { + q.err = errors.New("datastore: negative query offset") + return q + } + if offset > math.MaxInt32 { + q.err = errors.New("datastore: query offset overflow") + return q + } + q.offset = int32(offset) + return q +} + +// Start returns a derivative query with the given start point. +func (q *Query) Start(c Cursor) *Query { + q = q.clone() + q.start = c.cc + return q +} + +// End returns a derivative query with the given end point. +func (q *Query) End(c Cursor) *Query { + q = q.clone() + q.end = c.cc + return q +} + +// toProto converts the query to a protocol buffer. +func (q *Query) toProto(req *pb.RunQueryRequest) error { + if len(q.projection) != 0 && q.keysOnly { + return errors.New("datastore: query cannot both project and be keys-only") + } + if len(q.distinctOn) != 0 && q.distinct { + return errors.New("datastore: query cannot be both distinct and distinct-on") + } + dst := &pb.Query{} + if q.kind != "" { + dst.Kind = []*pb.KindExpression{{Name: q.kind}} + } + if q.projection != nil { + for _, propertyName := range q.projection { + dst.Projection = append(dst.Projection, &pb.Projection{Property: &pb.PropertyReference{Name: propertyName}}) + } + + for _, propertyName := range q.distinctOn { + dst.DistinctOn = append(dst.DistinctOn, &pb.PropertyReference{Name: propertyName}) + } + + if q.distinct { + for _, propertyName := range q.projection { + dst.DistinctOn = append(dst.DistinctOn, &pb.PropertyReference{Name: propertyName}) + } + } + } + if q.keysOnly { + dst.Projection = []*pb.Projection{{Property: &pb.PropertyReference{Name: keyFieldName}}} + } + + var filters []*pb.Filter + for _, qf := range q.filter { + if qf.FieldName == "" { + return errors.New("datastore: empty query filter field name") + } + v, err := interfaceToProto(reflect.ValueOf(qf.Value).Interface(), false) + if err != nil { + return fmt.Errorf("datastore: bad query filter value type: %v", err) + } + op, ok := operatorToProto[qf.Op] + if !ok { + return errors.New("datastore: unknown query filter operator") + } + xf := &pb.PropertyFilter{ + Op: op, + Property: &pb.PropertyReference{Name: qf.FieldName}, + Value: v, + } + filters = append(filters, &pb.Filter{ + FilterType: &pb.Filter_PropertyFilter{PropertyFilter: xf}, + }) + } + + if q.ancestor != nil { + filters = append(filters, &pb.Filter{ + FilterType: &pb.Filter_PropertyFilter{PropertyFilter: &pb.PropertyFilter{ + Property: &pb.PropertyReference{Name: keyFieldName}, + Op: pb.PropertyFilter_HAS_ANCESTOR, + Value: &pb.Value{ValueType: &pb.Value_KeyValue{KeyValue: keyToProto(q.ancestor)}}, + }}}) + } + + if len(filters) == 1 { + dst.Filter = filters[0] + } else if len(filters) > 1 { + dst.Filter = &pb.Filter{FilterType: &pb.Filter_CompositeFilter{CompositeFilter: &pb.CompositeFilter{ + Op: pb.CompositeFilter_AND, + Filters: filters, + }}} + } + + for _, qo := range q.order { + if qo.FieldName == "" { + return errors.New("datastore: empty query order field name") + } + xo := &pb.PropertyOrder{ + Property: &pb.PropertyReference{Name: qo.FieldName}, + Direction: sortDirectionToProto[qo.Direction], + } + dst.Order = append(dst.Order, xo) + } + if q.limit >= 0 { + dst.Limit = &wrapperspb.Int32Value{Value: q.limit} + } + dst.Offset = q.offset + dst.StartCursor = q.start + dst.EndCursor = q.end + + if t := q.trans; t != nil { + if t.id == nil { + return errExpiredTransaction + } + if q.eventual { + return errors.New("datastore: cannot use EventualConsistency query in a transaction") + } + req.ReadOptions = &pb.ReadOptions{ + ConsistencyType: &pb.ReadOptions_Transaction{Transaction: t.id}, + } + } + + if q.eventual { + req.ReadOptions = &pb.ReadOptions{ConsistencyType: &pb.ReadOptions_ReadConsistency_{ReadConsistency: pb.ReadOptions_EVENTUAL}} + } + + req.QueryType = &pb.RunQueryRequest_Query{Query: dst} + return nil +} + +// Count returns the number of results for the given query. +// +// The running time and number of API calls made by Count scale linearly with +// the sum of the query's offset and limit. Unless the result count is +// expected to be small, it is best to specify a limit; otherwise Count will +// continue until it finishes counting or the provided context expires. +func (c *Client) Count(ctx context.Context, q *Query) (n int, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Query.Count") + defer func() { trace.EndSpan(ctx, err) }() + + // Check that the query is well-formed. + if q.err != nil { + return 0, q.err + } + + // Create a copy of the query, with keysOnly true (if we're not a projection, + // since the two are incompatible). + newQ := q.clone() + newQ.keysOnly = len(newQ.projection) == 0 + + // Create an iterator and use it to walk through the batches of results + // directly. + it := c.Run(ctx, newQ) + for { + err := it.nextBatch() + if err == iterator.Done { + return n, nil + } + if err != nil { + return 0, err + } + n += len(it.results) + } +} + +// GetAll runs the provided query in the given context and returns all keys +// that match that query, as well as appending the values to dst. +// +// dst must have type *[]S or *[]*S or *[]P, for some struct type S or some non- +// interface, non-pointer type P such that P or *P implements PropertyLoadSaver. +// +// As a special case, *PropertyList is an invalid type for dst, even though a +// PropertyList is a slice of structs. It is treated as invalid to avoid being +// mistakenly passed when *[]PropertyList was intended. +// +// The keys returned by GetAll will be in a 1-1 correspondence with the entities +// added to dst. +// +// If q is a ``keys-only'' query, GetAll ignores dst and only returns the keys. +// +// The running time and number of API calls made by GetAll scale linearly with +// with the sum of the query's offset and limit. Unless the result count is +// expected to be small, it is best to specify a limit; otherwise GetAll will +// continue until it finishes collecting results or the provided context +// expires. +func (c *Client) GetAll(ctx context.Context, q *Query, dst interface{}) (keys []*Key, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Query.GetAll") + defer func() { trace.EndSpan(ctx, err) }() + + var ( + dv reflect.Value + mat multiArgType + elemType reflect.Type + errFieldMismatch error + ) + if !q.keysOnly { + dv = reflect.ValueOf(dst) + if dv.Kind() != reflect.Ptr || dv.IsNil() { + return nil, ErrInvalidEntityType + } + dv = dv.Elem() + mat, elemType = checkMultiArg(dv) + if mat == multiArgTypeInvalid || mat == multiArgTypeInterface { + return nil, ErrInvalidEntityType + } + } + + for t := c.Run(ctx, q); ; { + k, e, err := t.next() + if err == iterator.Done { + break + } + if err != nil { + return keys, err + } + if !q.keysOnly { + ev := reflect.New(elemType) + if elemType.Kind() == reflect.Map { + // This is a special case. The zero values of a map type are + // not immediately useful; they have to be make'd. + // + // Funcs and channels are similar, in that a zero value is not useful, + // but even a freshly make'd channel isn't useful: there's no fixed + // channel buffer size that is always going to be large enough, and + // there's no goroutine to drain the other end. Theoretically, these + // types could be supported, for example by sniffing for a constructor + // method or requiring prior registration, but for now it's not a + // frequent enough concern to be worth it. Programmers can work around + // it by explicitly using Iterator.Next instead of the Query.GetAll + // convenience method. + x := reflect.MakeMap(elemType) + ev.Elem().Set(x) + } + if err = loadEntityProto(ev.Interface(), e); err != nil { + if _, ok := err.(*ErrFieldMismatch); ok { + // We continue loading entities even in the face of field mismatch errors. + // If we encounter any other error, that other error is returned. Otherwise, + // an ErrFieldMismatch is returned. + errFieldMismatch = err + } else { + return keys, err + } + } + if mat != multiArgTypeStructPtr { + ev = ev.Elem() + } + dv.Set(reflect.Append(dv, ev)) + } + keys = append(keys, k) + } + return keys, errFieldMismatch +} + +// Run runs the given query in the given context. +func (c *Client) Run(ctx context.Context, q *Query) *Iterator { + if q.err != nil { + return &Iterator{err: q.err} + } + t := &Iterator{ + ctx: ctx, + client: c, + limit: q.limit, + offset: q.offset, + keysOnly: q.keysOnly, + pageCursor: q.start, + entityCursor: q.start, + req: &pb.RunQueryRequest{ + ProjectId: c.dataset, + }, + } + + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Query.Run") + defer func() { trace.EndSpan(ctx, t.err) }() + if q.namespace != "" { + t.req.PartitionId = &pb.PartitionId{ + NamespaceId: q.namespace, + } + } + + if err := q.toProto(t.req); err != nil { + t.err = err + } + return t +} + +// Iterator is the result of running a query. +type Iterator struct { + ctx context.Context + client *Client + err error + + // results is the list of EntityResults still to be iterated over from the + // most recent API call. It will be nil if no requests have yet been issued. + results []*pb.EntityResult + // req is the request to send. It may be modified and used multiple times. + req *pb.RunQueryRequest + + // limit is the limit on the number of results this iterator should return. + // The zero value is used to prevent further fetches from the server. + // A negative value means unlimited. + limit int32 + // offset is the number of results that still need to be skipped. + offset int32 + // keysOnly records whether the query was keys-only (skip entity loading). + keysOnly bool + + // pageCursor is the compiled cursor for the next batch/page of result. + // TODO(djd): Can we delete this in favour of paging with the last + // entityCursor from each batch? + pageCursor []byte + // entityCursor is the compiled cursor of the next result. + entityCursor []byte +} + +// Next returns the key of the next result. When there are no more results, +// iterator.Done is returned as the error. +// +// If the query is not keys only and dst is non-nil, it also loads the entity +// stored for that key into the struct pointer or PropertyLoadSaver dst, with +// the same semantics and possible errors as for the Get function. +func (t *Iterator) Next(dst interface{}) (k *Key, err error) { + k, e, err := t.next() + if err != nil { + return nil, err + } + if dst != nil && !t.keysOnly { + err = loadEntityProto(dst, e) + } + return k, err +} + +func (t *Iterator) next() (*Key, *pb.Entity, error) { + // Fetch additional batches while there are no more results. + for t.err == nil && len(t.results) == 0 { + t.err = t.nextBatch() + } + if t.err != nil { + return nil, nil, t.err + } + + // Extract the next result, update cursors, and parse the entity's key. + e := t.results[0] + t.results = t.results[1:] + t.entityCursor = e.Cursor + if len(t.results) == 0 { + t.entityCursor = t.pageCursor // At the end of the batch. + } + if e.Entity.Key == nil { + return nil, nil, errors.New("datastore: internal error: server did not return a key") + } + k, err := protoToKey(e.Entity.Key) + if err != nil || k.Incomplete() { + return nil, nil, errors.New("datastore: internal error: server returned an invalid key") + } + + return k, e.Entity, nil +} + +// nextBatch makes a single call to the server for a batch of results. +func (t *Iterator) nextBatch() error { + if t.limit == 0 { + return iterator.Done // Short-circuits the zero-item response. + } + + // Adjust the query with the latest start cursor, limit and offset. + q := t.req.GetQuery() + q.StartCursor = t.pageCursor + q.Offset = t.offset + if t.limit >= 0 { + q.Limit = &wrapperspb.Int32Value{Value: t.limit} + } else { + q.Limit = nil + } + + // Run the query. + resp, err := t.client.client.RunQuery(t.ctx, t.req) + if err != nil { + return err + } + + // Adjust any offset from skipped results. + skip := resp.Batch.SkippedResults + if skip < 0 { + return errors.New("datastore: internal error: negative number of skipped_results") + } + t.offset -= skip + if t.offset < 0 { + return errors.New("datastore: internal error: query skipped too many results") + } + if t.offset > 0 && len(resp.Batch.EntityResults) > 0 { + return errors.New("datastore: internal error: query returned results before requested offset") + } + + // Adjust the limit. + if t.limit >= 0 { + t.limit -= int32(len(resp.Batch.EntityResults)) + if t.limit < 0 { + return errors.New("datastore: internal error: query returned more results than the limit") + } + } + + // If there are no more results available, set limit to zero to prevent + // further fetches. Otherwise, check that there is a next page cursor available. + if resp.Batch.MoreResults != pb.QueryResultBatch_NOT_FINISHED { + t.limit = 0 + } else if resp.Batch.EndCursor == nil { + return errors.New("datastore: internal error: server did not return a cursor") + } + + // Update cursors. + // If any results were skipped, use the SkippedCursor as the next entity cursor. + if skip > 0 { + t.entityCursor = resp.Batch.SkippedCursor + } else { + t.entityCursor = q.StartCursor + } + t.pageCursor = resp.Batch.EndCursor + + t.results = resp.Batch.EntityResults + return nil +} + +// Cursor returns a cursor for the iterator's current location. +func (t *Iterator) Cursor() (c Cursor, err error) { + t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Query.Cursor") + defer func() { trace.EndSpan(t.ctx, err) }() + + // If there is still an offset, we need to the skip those results first. + for t.err == nil && t.offset > 0 { + t.err = t.nextBatch() + } + + if t.err != nil && t.err != iterator.Done { + return Cursor{}, t.err + } + + return Cursor{t.entityCursor}, nil +} + +// Cursor is an iterator's position. It can be converted to and from an opaque +// string. A cursor can be used from different HTTP requests, but only with a +// query with the same kind, ancestor, filter and order constraints. +// +// The zero Cursor can be used to indicate that there is no start and/or end +// constraint for a query. +type Cursor struct { + cc []byte +} + +// String returns a base-64 string representation of a cursor. +func (c Cursor) String() string { + if c.cc == nil { + return "" + } + + return strings.TrimRight(base64.URLEncoding.EncodeToString(c.cc), "=") +} + +// Decode decodes a cursor from its base-64 string representation. +func DecodeCursor(s string) (Cursor, error) { + if s == "" { + return Cursor{}, nil + } + if n := len(s) % 4; n != 0 { + s += strings.Repeat("=", 4-n) + } + b, err := base64.URLEncoding.DecodeString(s) + if err != nil { + return Cursor{}, err + } + return Cursor{b}, nil +} diff --git a/vendor/cloud.google.com/go/datastore/query_test.go b/vendor/cloud.google.com/go/datastore/query_test.go new file mode 100644 index 0000000000..48bd6235ad --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/query_test.go @@ -0,0 +1,548 @@ +// 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 datastore + +import ( + "errors" + "fmt" + "reflect" + "sort" + "testing" + + "cloud.google.com/go/internal/testutil" + + "github.com/golang/protobuf/proto" + "github.com/google/go-cmp/cmp" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/datastore/v1" + "google.golang.org/grpc" +) + +var ( + key1 = &pb.Key{ + Path: []*pb.Key_PathElement{ + { + Kind: "Gopher", + IdType: &pb.Key_PathElement_Id{Id: 6}, + }, + }, + } + key2 = &pb.Key{ + Path: []*pb.Key_PathElement{ + { + Kind: "Gopher", + IdType: &pb.Key_PathElement_Id{Id: 6}, + }, + { + Kind: "Gopher", + IdType: &pb.Key_PathElement_Id{Id: 8}, + }, + }, + } +) + +type fakeClient struct { + pb.DatastoreClient + queryFn func(*pb.RunQueryRequest) (*pb.RunQueryResponse, error) + commitFn func(*pb.CommitRequest) (*pb.CommitResponse, error) +} + +func (c *fakeClient) RunQuery(_ context.Context, req *pb.RunQueryRequest, _ ...grpc.CallOption) (*pb.RunQueryResponse, error) { + return c.queryFn(req) +} + +func (c *fakeClient) Commit(_ context.Context, req *pb.CommitRequest, _ ...grpc.CallOption) (*pb.CommitResponse, error) { + return c.commitFn(req) +} + +func fakeRunQuery(in *pb.RunQueryRequest) (*pb.RunQueryResponse, error) { + expectedIn := &pb.RunQueryRequest{ + QueryType: &pb.RunQueryRequest_Query{Query: &pb.Query{ + Kind: []*pb.KindExpression{{Name: "Gopher"}}, + }}, + } + if !proto.Equal(in, expectedIn) { + return nil, fmt.Errorf("unsupported argument: got %v want %v", in, expectedIn) + } + return &pb.RunQueryResponse{ + Batch: &pb.QueryResultBatch{ + MoreResults: pb.QueryResultBatch_NO_MORE_RESULTS, + EntityResultType: pb.EntityResult_FULL, + EntityResults: []*pb.EntityResult{ + { + Entity: &pb.Entity{ + Key: key1, + Properties: map[string]*pb.Value{ + "Name": {ValueType: &pb.Value_StringValue{StringValue: "George"}}, + "Height": {ValueType: &pb.Value_IntegerValue{IntegerValue: 32}}, + }, + }, + }, + { + Entity: &pb.Entity{ + Key: key2, + Properties: map[string]*pb.Value{ + "Name": {ValueType: &pb.Value_StringValue{StringValue: "Rufus"}}, + // No height for Rufus. + }, + }, + }, + }, + }, + }, nil +} + +type StructThatImplementsPLS struct{} + +func (StructThatImplementsPLS) Load(p []Property) error { return nil } +func (StructThatImplementsPLS) Save() ([]Property, error) { return nil, nil } + +var _ PropertyLoadSaver = StructThatImplementsPLS{} + +type StructPtrThatImplementsPLS struct{} + +func (*StructPtrThatImplementsPLS) Load(p []Property) error { return nil } +func (*StructPtrThatImplementsPLS) Save() ([]Property, error) { return nil, nil } + +var _ PropertyLoadSaver = &StructPtrThatImplementsPLS{} + +type PropertyMap map[string]Property + +func (m PropertyMap) Load(props []Property) error { + for _, p := range props { + m[p.Name] = p + } + return nil +} + +func (m PropertyMap) Save() ([]Property, error) { + props := make([]Property, 0, len(m)) + for _, p := range m { + props = append(props, p) + } + return props, nil +} + +var _ PropertyLoadSaver = PropertyMap{} + +type Gopher struct { + Name string + Height int +} + +// typeOfEmptyInterface is the type of interface{}, but we can't use +// reflect.TypeOf((interface{})(nil)) directly because TypeOf takes an +// interface{}. +var typeOfEmptyInterface = reflect.TypeOf((*interface{})(nil)).Elem() + +func TestCheckMultiArg(t *testing.T) { + testCases := []struct { + v interface{} + mat multiArgType + elemType reflect.Type + }{ + // Invalid cases. + {nil, multiArgTypeInvalid, nil}, + {Gopher{}, multiArgTypeInvalid, nil}, + {&Gopher{}, multiArgTypeInvalid, nil}, + {PropertyList{}, multiArgTypeInvalid, nil}, // This is a special case. + {PropertyMap{}, multiArgTypeInvalid, nil}, + {[]*PropertyList(nil), multiArgTypeInvalid, nil}, + {[]*PropertyMap(nil), multiArgTypeInvalid, nil}, + {[]**Gopher(nil), multiArgTypeInvalid, nil}, + {[]*interface{}(nil), multiArgTypeInvalid, nil}, + // Valid cases. + { + []PropertyList(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(PropertyList{}), + }, + { + []PropertyMap(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(PropertyMap{}), + }, + { + []StructThatImplementsPLS(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(StructThatImplementsPLS{}), + }, + { + []StructPtrThatImplementsPLS(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(StructPtrThatImplementsPLS{}), + }, + { + []Gopher(nil), + multiArgTypeStruct, + reflect.TypeOf(Gopher{}), + }, + { + []*Gopher(nil), + multiArgTypeStructPtr, + reflect.TypeOf(Gopher{}), + }, + { + []interface{}(nil), + multiArgTypeInterface, + typeOfEmptyInterface, + }, + } + for _, tc := range testCases { + mat, elemType := checkMultiArg(reflect.ValueOf(tc.v)) + if mat != tc.mat || elemType != tc.elemType { + t.Errorf("checkMultiArg(%T): got %v, %v want %v, %v", + tc.v, mat, elemType, tc.mat, tc.elemType) + } + } +} + +func TestSimpleQuery(t *testing.T) { + struct1 := Gopher{Name: "George", Height: 32} + struct2 := Gopher{Name: "Rufus"} + pList1 := PropertyList{ + { + Name: "Height", + Value: int64(32), + }, + { + Name: "Name", + Value: "George", + }, + } + pList2 := PropertyList{ + { + Name: "Name", + Value: "Rufus", + }, + } + pMap1 := PropertyMap{ + "Name": Property{ + Name: "Name", + Value: "George", + }, + "Height": Property{ + Name: "Height", + Value: int64(32), + }, + } + pMap2 := PropertyMap{ + "Name": Property{ + Name: "Name", + Value: "Rufus", + }, + } + + testCases := []struct { + dst interface{} + want interface{} + }{ + // The destination must have type *[]P, *[]S or *[]*S, for some non-interface + // type P such that *P implements PropertyLoadSaver, or for some struct type S. + {new([]Gopher), &[]Gopher{struct1, struct2}}, + {new([]*Gopher), &[]*Gopher{&struct1, &struct2}}, + {new([]PropertyList), &[]PropertyList{pList1, pList2}}, + {new([]PropertyMap), &[]PropertyMap{pMap1, pMap2}}, + + // Any other destination type is invalid. + {0, nil}, + {Gopher{}, nil}, + {PropertyList{}, nil}, + {PropertyMap{}, nil}, + {[]int{}, nil}, + {[]Gopher{}, nil}, + {[]PropertyList{}, nil}, + {new(int), nil}, + {new(Gopher), nil}, + {new(PropertyList), nil}, // This is a special case. + {new(PropertyMap), nil}, + {new([]int), nil}, + {new([]map[int]int), nil}, + {new([]map[string]Property), nil}, + {new([]map[string]interface{}), nil}, + {new([]*int), nil}, + {new([]*map[int]int), nil}, + {new([]*map[string]Property), nil}, + {new([]*map[string]interface{}), nil}, + {new([]**Gopher), nil}, + {new([]*PropertyList), nil}, + {new([]*PropertyMap), nil}, + } + for _, tc := range testCases { + nCall := 0 + client := &Client{ + client: &fakeClient{ + queryFn: func(req *pb.RunQueryRequest) (*pb.RunQueryResponse, error) { + nCall++ + return fakeRunQuery(req) + }, + }, + } + ctx := context.Background() + + var ( + expectedErr error + expectedNCall int + ) + if tc.want == nil { + expectedErr = ErrInvalidEntityType + } else { + expectedNCall = 1 + } + keys, err := client.GetAll(ctx, NewQuery("Gopher"), tc.dst) + if err != expectedErr { + t.Errorf("dst type %T: got error %v, want %v", tc.dst, err, expectedErr) + continue + } + if nCall != expectedNCall { + t.Errorf("dst type %T: Context.Call was called an incorrect number of times: got %d want %d", tc.dst, nCall, expectedNCall) + continue + } + if err != nil { + continue + } + + key1 := IDKey("Gopher", 6, nil) + expectedKeys := []*Key{ + key1, + IDKey("Gopher", 8, key1), + } + if l1, l2 := len(keys), len(expectedKeys); l1 != l2 { + t.Errorf("dst type %T: got %d keys, want %d keys", tc.dst, l1, l2) + continue + } + for i, key := range keys { + if !keysEqual(key, expectedKeys[i]) { + t.Errorf("dst type %T: got key #%d %v, want %v", tc.dst, i, key, expectedKeys[i]) + continue + } + } + + // Make sure we sort any PropertyList items (the order is not deterministic). + if pLists, ok := tc.dst.(*[]PropertyList); ok { + for _, p := range *pLists { + sort.Sort(byName(p)) + } + } + + if !testutil.Equal(tc.dst, tc.want) { + t.Errorf("dst type %T: Entities\ngot %+v\nwant %+v", tc.dst, tc.dst, tc.want) + continue + } + } +} + +// keysEqual is like (*Key).Equal, but ignores the App ID. +func keysEqual(a, b *Key) bool { + for a != nil && b != nil { + if a.Kind != b.Kind || a.Name != b.Name || a.ID != b.ID { + return false + } + a, b = a.Parent, b.Parent + } + return a == b +} + +func TestQueriesAreImmutable(t *testing.T) { + // Test that deriving q2 from q1 does not modify q1. + q0 := NewQuery("foo") + q1 := NewQuery("foo") + q2 := q1.Offset(2) + if !testutil.Equal(q0, q1, cmp.AllowUnexported(Query{})) { + t.Errorf("q0 and q1 were not equal") + } + if testutil.Equal(q1, q2, cmp.AllowUnexported(Query{})) { + t.Errorf("q1 and q2 were equal") + } + + // Test that deriving from q4 twice does not conflict, even though + // q4 has a long list of order clauses. This tests that the arrays + // backed by a query's slice of orders are not shared. + f := func() *Query { + q := NewQuery("bar") + // 47 is an ugly number that is unlikely to be near a re-allocation + // point in repeated append calls. For example, it's not near a power + // of 2 or a multiple of 10. + for i := 0; i < 47; i++ { + q = q.Order(fmt.Sprintf("x%d", i)) + } + return q + } + q3 := f().Order("y") + q4 := f() + q5 := q4.Order("y") + q6 := q4.Order("z") + if !testutil.Equal(q3, q5, cmp.AllowUnexported(Query{})) { + t.Errorf("q3 and q5 were not equal") + } + if testutil.Equal(q5, q6, cmp.AllowUnexported(Query{})) { + t.Errorf("q5 and q6 were equal") + } +} + +func TestFilterParser(t *testing.T) { + testCases := []struct { + filterStr string + wantOK bool + wantFieldName string + wantOp operator + }{ + // Supported ops. + {"x<", true, "x", lessThan}, + {"x <", true, "x", lessThan}, + {"x <", true, "x", lessThan}, + {" x < ", true, "x", lessThan}, + {"x <=", true, "x", lessEq}, + {"x =", true, "x", equal}, + {"x >=", true, "x", greaterEq}, + {"x >", true, "x", greaterThan}, + {"in >", true, "in", greaterThan}, + {"in>", true, "in", greaterThan}, + // Valid but (currently) unsupported ops. + {"x!=", false, "", 0}, + {"x !=", false, "", 0}, + {" x != ", false, "", 0}, + {"x IN", false, "", 0}, + {"x in", false, "", 0}, + // Invalid ops. + {"x EQ", false, "", 0}, + {"x lt", false, "", 0}, + {"x <>", false, "", 0}, + {"x >>", false, "", 0}, + {"x ==", false, "", 0}, + {"x =<", false, "", 0}, + {"x =>", false, "", 0}, + {"x !", false, "", 0}, + {"x ", false, "", 0}, + {"x", false, "", 0}, + // Quoted and interesting field names. + {"x > y =", true, "x > y", equal}, + {"` x ` =", true, " x ", equal}, + {`" x " =`, true, " x ", equal}, + {`" \"x " =`, true, ` "x `, equal}, + {`" x =`, false, "", 0}, + {`" x ="`, false, "", 0}, + {"` x \" =", false, "", 0}, + } + for _, tc := range testCases { + q := NewQuery("foo").Filter(tc.filterStr, 42) + if ok := q.err == nil; ok != tc.wantOK { + t.Errorf("%q: ok=%t, want %t", tc.filterStr, ok, tc.wantOK) + continue + } + if !tc.wantOK { + continue + } + if len(q.filter) != 1 { + t.Errorf("%q: len=%d, want %d", tc.filterStr, len(q.filter), 1) + continue + } + got, want := q.filter[0], filter{tc.wantFieldName, tc.wantOp, 42} + if got != want { + t.Errorf("%q: got %v, want %v", tc.filterStr, got, want) + continue + } + } +} + +func TestNamespaceQuery(t *testing.T) { + gotNamespace := make(chan string, 1) + ctx := context.Background() + client := &Client{ + client: &fakeClient{ + queryFn: func(req *pb.RunQueryRequest) (*pb.RunQueryResponse, error) { + if part := req.PartitionId; part != nil { + gotNamespace <- part.NamespaceId + } else { + gotNamespace <- "" + } + return nil, errors.New("not implemented") + }, + }, + } + + var gs []Gopher + + // Ignore errors for the rest of this test. + client.GetAll(ctx, NewQuery("gopher"), &gs) + if got, want := <-gotNamespace, ""; got != want { + t.Errorf("GetAll: got namespace %q, want %q", got, want) + } + client.Count(ctx, NewQuery("gopher")) + if got, want := <-gotNamespace, ""; got != want { + t.Errorf("Count: got namespace %q, want %q", got, want) + } + + const ns = "not_default" + client.GetAll(ctx, NewQuery("gopher").Namespace(ns), &gs) + if got, want := <-gotNamespace, ns; got != want { + t.Errorf("GetAll: got namespace %q, want %q", got, want) + } + client.Count(ctx, NewQuery("gopher").Namespace(ns)) + if got, want := <-gotNamespace, ns; got != want { + t.Errorf("Count: got namespace %q, want %q", got, want) + } +} + +func TestReadOptions(t *testing.T) { + tid := []byte{1} + for _, test := range []struct { + q *Query + want *pb.ReadOptions + }{ + { + q: NewQuery(""), + want: nil, + }, + { + q: NewQuery("").Transaction(nil), + want: nil, + }, + { + q: NewQuery("").Transaction(&Transaction{id: tid}), + want: &pb.ReadOptions{ + ConsistencyType: &pb.ReadOptions_Transaction{ + Transaction: tid, + }, + }, + }, + { + q: NewQuery("").EventualConsistency(), + want: &pb.ReadOptions{ + ConsistencyType: &pb.ReadOptions_ReadConsistency_{ + ReadConsistency: pb.ReadOptions_EVENTUAL, + }, + }, + }, + } { + req := &pb.RunQueryRequest{} + if err := test.q.toProto(req); err != nil { + t.Fatalf("%+v: got %v, want no error", test.q, err) + } + if got := req.ReadOptions; !proto.Equal(got, test.want) { + t.Errorf("%+v:\ngot %+v\nwant %+v", test.q, got, test.want) + } + } + // Test errors. + for _, q := range []*Query{ + NewQuery("").Transaction(&Transaction{id: nil}), + NewQuery("").Transaction(&Transaction{id: tid}).EventualConsistency(), + } { + req := &pb.RunQueryRequest{} + if err := q.toProto(req); err == nil { + t.Errorf("%+v: got nil, wanted error", q) + } + } +} diff --git a/vendor/cloud.google.com/go/datastore/save.go b/vendor/cloud.google.com/go/datastore/save.go new file mode 100644 index 0000000000..35c462c0c9 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/save.go @@ -0,0 +1,466 @@ +// Copyright 4 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 datastore + +import ( + "errors" + "fmt" + "reflect" + "time" + "unicode/utf8" + + timepb "github.com/golang/protobuf/ptypes/timestamp" + pb "google.golang.org/genproto/googleapis/datastore/v1" + llpb "google.golang.org/genproto/googleapis/type/latlng" +) + +type saveOpts struct { + noIndex bool + flatten bool + omitEmpty bool +} + +// saveEntity saves an EntityProto into a PropertyLoadSaver or struct pointer. +func saveEntity(key *Key, src interface{}) (*pb.Entity, error) { + var err error + var props []Property + if e, ok := src.(PropertyLoadSaver); ok { + props, err = e.Save() + } else { + props, err = SaveStruct(src) + } + if err != nil { + return nil, err + } + return propertiesToProto(key, props) +} + +// TODO(djd): Convert this and below to return ([]Property, error). +func saveStructProperty(props *[]Property, name string, opts saveOpts, v reflect.Value) error { + p := Property{ + Name: name, + NoIndex: opts.noIndex, + } + + if opts.omitEmpty && isEmptyValue(v) { + return nil + } + + // First check if field type implements PLS. If so, use PLS to + // save. + ok, err := plsFieldSave(props, p, name, opts, v) + if err != nil { + return err + } + if ok { + return nil + } + + switch x := v.Interface().(type) { + case *Key, time.Time, GeoPoint: + p.Value = x + default: + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p.Value = v.Int() + case reflect.Bool: + p.Value = v.Bool() + case reflect.String: + p.Value = v.String() + case reflect.Float32, reflect.Float64: + p.Value = v.Float() + case reflect.Slice: + if v.Type().Elem().Kind() == reflect.Uint8 { + p.Value = v.Bytes() + } else { + return saveSliceProperty(props, name, opts, v) + } + case reflect.Ptr: + if isValidPointerType(v.Type().Elem()) { + if v.IsNil() { + // Nil pointer becomes a nil property value (unless omitempty, handled above). + p.Value = nil + *props = append(*props, p) + return nil + } + return saveStructProperty(props, name, opts, v.Elem()) + } + if v.Type().Elem().Kind() != reflect.Struct { + return fmt.Errorf("datastore: unsupported struct field type: %s", v.Type()) + } + // Pointer to struct is a special case. + if v.IsNil() { + return nil + } + v = v.Elem() + fallthrough + case reflect.Struct: + if !v.CanAddr() { + return fmt.Errorf("datastore: unsupported struct field: value is unaddressable") + } + vi := v.Addr().Interface() + + sub, err := newStructPLS(vi) + if err != nil { + return fmt.Errorf("datastore: unsupported struct field: %v", err) + } + + if opts.flatten { + return sub.save(props, opts, name+".") + } + + var subProps []Property + err = sub.save(&subProps, opts, "") + if err != nil { + return err + } + subKey, err := sub.key(v) + if err != nil { + return err + } + + p.Value = &Entity{ + Key: subKey, + Properties: subProps, + } + } + } + if p.Value == nil { + return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type()) + } + *props = append(*props, p) + return nil +} + +// plsFieldSave first tries to converts v's value to a PLS, then v's addressed +// value to a PLS. If neither succeeds, plsFieldSave returns false for first return +// value. +// If v is successfully converted to a PLS, plsFieldSave will then add the +// Value to property p by way of the PLS's Save method, and append it to props. +// +// If the flatten option is present in opts, name must be prepended to each property's +// name before it is appended to props. Eg. if name were "A" and a subproperty's name +// were "B", the resultant name of the property to be appended to props would be "A.B". +func plsFieldSave(props *[]Property, p Property, name string, opts saveOpts, v reflect.Value) (ok bool, err error) { + vpls, err := plsForSave(v) + if err != nil { + return false, err + } + + if vpls == nil { + return false, nil + } + + subProps, err := vpls.Save() + if err != nil { + return true, err + } + + if opts.flatten { + for _, subp := range subProps { + subp.Name = name + "." + subp.Name + *props = append(*props, subp) + } + return true, nil + } + + p.Value = &Entity{Properties: subProps} + *props = append(*props, p) + + return true, nil +} + +// key extracts the *Key struct field from struct v based on the structCodec of s. +func (s structPLS) key(v reflect.Value) (*Key, error) { + if v.Kind() != reflect.Struct { + return nil, errors.New("datastore: cannot save key of non-struct type") + } + + keyField := s.codec.Match(keyFieldName) + + if keyField == nil { + return nil, nil + } + + f := v.FieldByIndex(keyField.Index) + k, ok := f.Interface().(*Key) + if !ok { + return nil, fmt.Errorf("datastore: %s field on struct %T is not a *datastore.Key", keyFieldName, v.Interface()) + } + + return k, nil +} + +func saveSliceProperty(props *[]Property, name string, opts saveOpts, v reflect.Value) error { + // Easy case: if the slice is empty, we're done. + if v.Len() == 0 { + return nil + } + // Work out the properties generated by the first element in the slice. This will + // usually be a single property, but will be more if this is a slice of structs. + var headProps []Property + if err := saveStructProperty(&headProps, name, opts, v.Index(0)); err != nil { + return err + } + + // Convert the first element's properties into slice properties, and + // keep track of the values in a map. + values := make(map[string][]interface{}, len(headProps)) + for _, p := range headProps { + values[p.Name] = append(make([]interface{}, 0, v.Len()), p.Value) + } + + // Find the elements for the subsequent elements. + for i := 1; i < v.Len(); i++ { + elemProps := make([]Property, 0, len(headProps)) + if err := saveStructProperty(&elemProps, name, opts, v.Index(i)); err != nil { + return err + } + for _, p := range elemProps { + v, ok := values[p.Name] + if !ok { + return fmt.Errorf("datastore: unexpected property %q in elem %d of slice", p.Name, i) + } + values[p.Name] = append(v, p.Value) + } + } + + // Convert to the final properties. + for _, p := range headProps { + p.Value = values[p.Name] + *props = append(*props, p) + } + return nil +} + +func (s structPLS) Save() ([]Property, error) { + var props []Property + if err := s.save(&props, saveOpts{}, ""); err != nil { + return nil, err + } + return props, nil +} + +func (s structPLS) save(props *[]Property, opts saveOpts, prefix string) error { + for _, f := range s.codec { + name := prefix + f.Name + v := getField(s.v, f.Index) + if !v.IsValid() || !v.CanSet() { + continue + } + + var tagOpts saveOpts + if f.ParsedTag != nil { + tagOpts = f.ParsedTag.(saveOpts) + } + + var opts1 saveOpts + opts1.noIndex = opts.noIndex || tagOpts.noIndex + opts1.flatten = opts.flatten || tagOpts.flatten + opts1.omitEmpty = tagOpts.omitEmpty // don't propagate + if err := saveStructProperty(props, name, opts1, v); err != nil { + return err + } + } + return nil +} + +// getField returns the field from v at the given index path. +// If it encounters a nil-valued field in the path, getField +// stops and returns a zero-valued reflect.Value, preventing the +// panic that would have been caused by reflect's FieldByIndex. +func getField(v reflect.Value, index []int) reflect.Value { + var zero reflect.Value + if v.Type().Kind() != reflect.Struct { + return zero + } + + for _, i := range index { + if v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Struct { + if v.IsNil() { + return zero + } + v = v.Elem() + } + v = v.Field(i) + } + return v +} + +func propertiesToProto(key *Key, props []Property) (*pb.Entity, error) { + e := &pb.Entity{ + Key: keyToProto(key), + Properties: map[string]*pb.Value{}, + } + indexedProps := 0 + for _, p := range props { + // Do not send a Key value a a field to datastore. + if p.Name == keyFieldName { + continue + } + + val, err := interfaceToProto(p.Value, p.NoIndex) + if err != nil { + return nil, fmt.Errorf("datastore: %v for a Property with Name %q", err, p.Name) + } + if !p.NoIndex { + rVal := reflect.ValueOf(p.Value) + if rVal.Kind() == reflect.Slice && rVal.Type().Elem().Kind() != reflect.Uint8 { + indexedProps += rVal.Len() + } else { + indexedProps++ + } + } + if indexedProps > maxIndexedProperties { + return nil, errors.New("datastore: too many indexed properties") + } + + if _, ok := e.Properties[p.Name]; ok { + return nil, fmt.Errorf("datastore: duplicate Property with Name %q", p.Name) + } + e.Properties[p.Name] = val + } + return e, nil +} + +func interfaceToProto(iv interface{}, noIndex bool) (*pb.Value, error) { + val := &pb.Value{ExcludeFromIndexes: noIndex} + switch v := iv.(type) { + case int: + val.ValueType = &pb.Value_IntegerValue{IntegerValue: int64(v)} + case int32: + val.ValueType = &pb.Value_IntegerValue{IntegerValue: int64(v)} + case int64: + val.ValueType = &pb.Value_IntegerValue{IntegerValue: v} + case bool: + val.ValueType = &pb.Value_BooleanValue{BooleanValue: v} + case string: + if len(v) > 1500 && !noIndex { + return nil, errors.New("string property too long to index") + } + if !utf8.ValidString(v) { + return nil, fmt.Errorf("string is not valid utf8: %q", v) + } + val.ValueType = &pb.Value_StringValue{StringValue: v} + case float32: + val.ValueType = &pb.Value_DoubleValue{DoubleValue: float64(v)} + case float64: + val.ValueType = &pb.Value_DoubleValue{DoubleValue: v} + case *Key: + if v == nil { + val.ValueType = &pb.Value_NullValue{} + } else { + val.ValueType = &pb.Value_KeyValue{KeyValue: keyToProto(v)} + } + case GeoPoint: + if !v.Valid() { + return nil, errors.New("invalid GeoPoint value") + } + val.ValueType = &pb.Value_GeoPointValue{GeoPointValue: &llpb.LatLng{ + Latitude: v.Lat, + Longitude: v.Lng, + }} + case time.Time: + if v.Before(minTime) || v.After(maxTime) { + return nil, errors.New("time value out of range") + } + val.ValueType = &pb.Value_TimestampValue{TimestampValue: &timepb.Timestamp{ + Seconds: v.Unix(), + Nanos: int32(v.Nanosecond()), + }} + case []byte: + if len(v) > 1500 && !noIndex { + return nil, errors.New("[]byte property too long to index") + } + val.ValueType = &pb.Value_BlobValue{BlobValue: v} + case *Entity: + e, err := propertiesToProto(v.Key, v.Properties) + if err != nil { + return nil, err + } + val.ValueType = &pb.Value_EntityValue{EntityValue: e} + case []interface{}: + arr := make([]*pb.Value, 0, len(v)) + for i, v := range v { + elem, err := interfaceToProto(v, noIndex) + if err != nil { + return nil, fmt.Errorf("%v at index %d", err, i) + } + arr = append(arr, elem) + } + val.ValueType = &pb.Value_ArrayValue{ArrayValue: &pb.ArrayValue{Values: arr}} + // ArrayValues have ExcludeFromIndexes set on the individual items, rather + // than the top-level value. + val.ExcludeFromIndexes = false + default: + rv := reflect.ValueOf(iv) + if !rv.IsValid() { + val.ValueType = &pb.Value_NullValue{} + } else if rv.Kind() == reflect.Ptr { // non-nil pointer: dereference + if rv.IsNil() { + val.ValueType = &pb.Value_NullValue{} + return val, nil + } + return interfaceToProto(rv.Elem().Interface(), noIndex) + } else { + return nil, fmt.Errorf("invalid Value type %T", iv) + } + } + // TODO(jbd): Support EntityValue. + return val, nil +} + +// isEmptyValue is taken from the encoding/json package in the +// standard library. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Struct: + if t, ok := v.Interface().(time.Time); ok { + return t.IsZero() + } + } + return false +} + +// isValidPointerType reports whether a struct field can be a pointer to type t +// for the purposes of saving and loading. +func isValidPointerType(t reflect.Type) bool { + if t == typeOfTime || t == typeOfGeoPoint { + return true + } + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Bool: + return true + case reflect.String: + return true + case reflect.Float32, reflect.Float64: + return true + } + return false +} diff --git a/vendor/cloud.google.com/go/datastore/save_test.go b/vendor/cloud.google.com/go/datastore/save_test.go new file mode 100644 index 0000000000..3086aac64c --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/save_test.go @@ -0,0 +1,285 @@ +// 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 datastore + +import ( + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +func TestInterfaceToProtoNil(t *testing.T) { + // A nil *Key, or a nil value of any other pointer type, should convert to a NullValue. + for _, in := range []interface{}{ + (*Key)(nil), + (*int)(nil), + (*string)(nil), + (*bool)(nil), + (*float64)(nil), + (*GeoPoint)(nil), + (*time.Time)(nil), + } { + got, err := interfaceToProto(in, false) + if err != nil { + t.Fatalf("%T: %v", in, err) + } + _, ok := got.ValueType.(*pb.Value_NullValue) + if !ok { + t.Errorf("%T: got: %T\nwant: %T", in, got.ValueType, &pb.Value_NullValue{}) + } + } +} + +func TestSaveEntityNested(t *testing.T) { + type WithKey struct { + X string + I int + K *Key `datastore:"__key__"` + } + + type NestedWithKey struct { + Y string + N WithKey + } + + type WithoutKey struct { + X string + I int + } + + type NestedWithoutKey struct { + Y string + N WithoutKey + } + + type a struct { + S string + } + + type UnexpAnonym struct { + a + } + + testCases := []struct { + desc string + src interface{} + key *Key + want *pb.Entity + }{ + { + desc: "nested entity with key", + src: &NestedWithKey{ + Y: "yyy", + N: WithKey{ + X: "two", + I: 2, + K: testKey1a, + }, + }, + key: testKey0, + want: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}}, + "N": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Key: keyToProto(testKey1a), + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + }}, + }, + }, + }, + { + desc: "nested entity with incomplete key", + src: &NestedWithKey{ + Y: "yyy", + N: WithKey{ + X: "two", + I: 2, + K: incompleteKey, + }, + }, + key: testKey0, + want: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}}, + "N": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Key: keyToProto(incompleteKey), + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + }}, + }, + }, + }, + { + desc: "nested entity without key", + src: &NestedWithoutKey{ + Y: "yyy", + N: WithoutKey{ + X: "two", + I: 2, + }, + }, + key: testKey0, + want: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}}, + "N": {ValueType: &pb.Value_EntityValue{ + EntityValue: &pb.Entity{ + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}}, + }, + }, + }}, + }, + }, + }, + { + desc: "key at top level", + src: &WithKey{ + X: "three", + I: 3, + K: testKey0, + }, + key: testKey0, + want: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "X": {ValueType: &pb.Value_StringValue{StringValue: "three"}}, + "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}}, + }, + }, + }, + { + desc: "nested unexported anonymous struct field", + src: &UnexpAnonym{ + a{S: "hello"}, + }, + key: testKey0, + want: &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "S": {ValueType: &pb.Value_StringValue{StringValue: "hello"}}, + }, + }, + }, + } + + for _, tc := range testCases { + got, err := saveEntity(tc.key, tc.src) + if err != nil { + t.Errorf("saveEntity: %s: %v", tc.desc, err) + continue + } + + if !testutil.Equal(tc.want, got) { + t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, got, tc.want) + } + } +} + +func TestSavePointers(t *testing.T) { + for _, test := range []struct { + desc string + in interface{} + want []Property + }{ + { + desc: "nil pointers save as nil-valued properties", + in: &Pointers{}, + want: []Property{ + {Name: "Pi", Value: nil}, + {Name: "Ps", Value: nil}, + {Name: "Pb", Value: nil}, + {Name: "Pf", Value: nil}, + {Name: "Pg", Value: nil}, + {Name: "Pt", Value: nil}, + }, + }, + { + desc: "nil omitempty pointers not saved", + in: &PointersOmitEmpty{}, + want: []Property(nil), + }, + { + desc: "non-nil zero-valued pointers save as zero values", + in: populatedPointers(), + want: []Property{ + {Name: "Pi", Value: int64(0)}, + {Name: "Ps", Value: ""}, + {Name: "Pb", Value: false}, + {Name: "Pf", Value: 0.0}, + {Name: "Pg", Value: GeoPoint{}}, + {Name: "Pt", Value: time.Time{}}, + }, + }, + { + desc: "non-nil non-zero-valued pointers save as the appropriate values", + in: func() *Pointers { + p := populatedPointers() + *p.Pi = 1 + *p.Ps = "x" + *p.Pb = true + *p.Pf = 3.14 + *p.Pg = GeoPoint{Lat: 1, Lng: 2} + *p.Pt = time.Unix(100, 0) + return p + }(), + want: []Property{ + {Name: "Pi", Value: int64(1)}, + {Name: "Ps", Value: "x"}, + {Name: "Pb", Value: true}, + {Name: "Pf", Value: 3.14}, + {Name: "Pg", Value: GeoPoint{Lat: 1, Lng: 2}}, + {Name: "Pt", Value: time.Unix(100, 0)}, + }, + }, + } { + got, err := SaveStruct(test.in) + if err != nil { + t.Fatalf("%s: %v", test.desc, err) + } + if !testutil.Equal(got, test.want) { + t.Errorf("%s\ngot %#v\nwant %#v\n", test.desc, got, test.want) + } + } +} + +func TestSaveEmptySlice(t *testing.T) { + // Zero-length slice fields are not saved. + for _, slice := range [][]string{nil, {}} { + got, err := SaveStruct(&struct{ S []string }{S: slice}) + if err != nil { + t.Fatal(err) + } + if len(got) != 0 { + t.Errorf("%#v: got %d properties, wanted zero", slice, len(got)) + } + } +} diff --git a/vendor/cloud.google.com/go/datastore/testdata/index.yaml b/vendor/cloud.google.com/go/datastore/testdata/index.yaml new file mode 100644 index 0000000000..47bc9de867 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/testdata/index.yaml @@ -0,0 +1,41 @@ +indexes: + +- kind: SQChild + ancestor: yes + properties: + - name: T + - name: I + +- kind: SQChild + ancestor: yes + properties: + - name: T + - name: I + direction: desc + +- kind: SQChild + ancestor: yes + properties: + - name: I + - name: T + - name: U + +- kind: SQChild + ancestor: yes + properties: + - name: I + - name: T + - name: U + +- kind: SQChild + ancestor: yes + properties: + - name: T + - name: J + +- kind: SQChild + ancestor: yes + properties: + - name: T + - name: J + - name: U \ No newline at end of file diff --git a/vendor/cloud.google.com/go/datastore/time.go b/vendor/cloud.google.com/go/datastore/time.go new file mode 100644 index 0000000000..da6c780daa --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/time.go @@ -0,0 +1,36 @@ +// 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 datastore + +import ( + "math" + "time" +) + +var ( + minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6)*1e3) + maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)*1e3) +) + +func toUnixMicro(t time.Time) int64 { + // We cannot use t.UnixNano() / 1e3 because we want to handle times more than + // 2^63 nanoseconds (which is about 292 years) away from 1970, and those cannot + // be represented in the numerator of a single int64 divide. + return t.Unix()*1e6 + int64(t.Nanosecond()/1e3) +} + +func fromUnixMicro(t int64) time.Time { + return time.Unix(t/1e6, (t%1e6)*1e3) +} diff --git a/vendor/cloud.google.com/go/datastore/time_test.go b/vendor/cloud.google.com/go/datastore/time_test.go new file mode 100644 index 0000000000..f82efc7e51 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/time_test.go @@ -0,0 +1,75 @@ +// 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 datastore + +import ( + "testing" + "time" +) + +func TestUnixMicro(t *testing.T) { + // Test that all these time.Time values survive a round trip to unix micros. + testCases := []time.Time{ + {}, + time.Date(2, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(23, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(234, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1000, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1600, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1700, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1800, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), + time.Unix(-1e6, -1000), + time.Unix(-1e6, 0), + time.Unix(-1e6, +1000), + time.Unix(-60, -1000), + time.Unix(-60, 0), + time.Unix(-60, +1000), + time.Unix(-1, -1000), + time.Unix(-1, 0), + time.Unix(-1, +1000), + time.Unix(0, -3000), + time.Unix(0, -2000), + time.Unix(0, -1000), + time.Unix(0, 0), + time.Unix(0, +1000), + time.Unix(0, +2000), + time.Unix(+60, -1000), + time.Unix(+60, 0), + time.Unix(+60, +1000), + time.Unix(+1e6, -1000), + time.Unix(+1e6, 0), + time.Unix(+1e6, +1000), + time.Date(1999, 12, 31, 23, 59, 59, 999000, time.UTC), + time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(2006, 1, 2, 15, 4, 5, 678000, time.UTC), + time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + time.Date(3456, 1, 1, 0, 0, 0, 0, time.UTC), + } + for _, tc := range testCases { + got := fromUnixMicro(toUnixMicro(tc)) + if !got.Equal(tc) { + t.Errorf("got %q, want %q", got, tc) + } + } + + // Test that a time.Time that isn't an integral number of microseconds + // is not perfectly reconstructed after a round trip. + t0 := time.Unix(0, 123) + t1 := fromUnixMicro(toUnixMicro(t0)) + if t1.Nanosecond()%1000 != 0 || t0.Nanosecond()%1000 == 0 { + t.Errorf("quantization to µs: got %q with %d ns, started with %d ns", t1, t1.Nanosecond(), t0.Nanosecond()) + } +} diff --git a/vendor/cloud.google.com/go/datastore/transaction.go b/vendor/cloud.google.com/go/datastore/transaction.go new file mode 100644 index 0000000000..e990d2ae22 --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/transaction.go @@ -0,0 +1,409 @@ +// 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 datastore + +import ( + "errors" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +// ErrConcurrentTransaction is returned when a transaction is rolled back due +// to a conflict with a concurrent transaction. +var ErrConcurrentTransaction = errors.New("datastore: concurrent transaction") + +var errExpiredTransaction = errors.New("datastore: transaction expired") + +type transactionSettings struct { + attempts int + readOnly bool + prevID []byte // ID of the transaction to retry +} + +// newTransactionSettings creates a transactionSettings with a given TransactionOption slice. +// Unconfigured options will be set to default values. +func newTransactionSettings(opts []TransactionOption) *transactionSettings { + s := &transactionSettings{attempts: 3} + for _, o := range opts { + o.apply(s) + } + return s +} + +// TransactionOption configures the way a transaction is executed. +type TransactionOption interface { + apply(*transactionSettings) +} + +// MaxAttempts returns a TransactionOption that overrides the default 3 attempt times. +func MaxAttempts(attempts int) TransactionOption { + return maxAttempts(attempts) +} + +type maxAttempts int + +func (w maxAttempts) apply(s *transactionSettings) { + if w > 0 { + s.attempts = int(w) + } +} + +// ReadOnly is a TransactionOption that marks the transaction as read-only. +var ReadOnly TransactionOption + +func init() { + ReadOnly = readOnly{} +} + +type readOnly struct{} + +func (readOnly) apply(s *transactionSettings) { + s.readOnly = true +} + +// Transaction represents a set of datastore operations to be committed atomically. +// +// Operations are enqueued by calling the Put and Delete methods on Transaction +// (or their Multi-equivalents). These operations are only committed when the +// Commit method is invoked. To ensure consistency, reads must be performed by +// using Transaction's Get method or by using the Transaction method when +// building a query. +// +// A Transaction must be committed or rolled back exactly once. +type Transaction struct { + id []byte + client *Client + ctx context.Context + mutations []*pb.Mutation // The mutations to apply. + pending map[int]*PendingKey // Map from mutation index to incomplete keys pending transaction completion. +} + +// NewTransaction starts a new transaction. +func (c *Client) NewTransaction(ctx context.Context, opts ...TransactionOption) (t *Transaction, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.NewTransaction") + defer func() { trace.EndSpan(ctx, err) }() + + for _, o := range opts { + if _, ok := o.(maxAttempts); ok { + return nil, errors.New("datastore: NewTransaction does not accept MaxAttempts option") + } + } + return c.newTransaction(ctx, newTransactionSettings(opts)) +} + +func (c *Client) newTransaction(ctx context.Context, s *transactionSettings) (*Transaction, error) { + req := &pb.BeginTransactionRequest{ProjectId: c.dataset} + if s.readOnly { + req.TransactionOptions = &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadOnly_{ReadOnly: &pb.TransactionOptions_ReadOnly{}}, + } + } else if s.prevID != nil { + req.TransactionOptions = &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadWrite_{ReadWrite: &pb.TransactionOptions_ReadWrite{ + PreviousTransaction: s.prevID, + }}, + } + } + resp, err := c.client.BeginTransaction(ctx, req) + if err != nil { + return nil, err + } + return &Transaction{ + id: resp.Transaction, + ctx: ctx, + client: c, + mutations: nil, + pending: make(map[int]*PendingKey), + }, nil +} + +// RunInTransaction runs f in a transaction. f is invoked with a Transaction +// that f should use for all the transaction's datastore operations. +// +// f must not call Commit or Rollback on the provided Transaction. +// +// If f returns nil, RunInTransaction commits the transaction, +// returning the Commit and a nil error if it succeeds. If the commit fails due +// to a conflicting transaction, RunInTransaction retries f with a new +// Transaction. It gives up and returns ErrConcurrentTransaction after three +// failed attempts (or as configured with MaxAttempts). +// +// If f returns non-nil, then the transaction will be rolled back and +// RunInTransaction will return the same error. The function f is not retried. +// +// Note that when f returns, the transaction is not committed. Calling code +// must not assume that any of f's changes have been committed until +// RunInTransaction returns nil. +// +// Since f may be called multiple times, f should usually be idempotent – that +// is, it should have the same result when called multiple times. Note that +// Transaction.Get will append when unmarshalling slice fields, so it is not +// necessarily idempotent. +func (c *Client) RunInTransaction(ctx context.Context, f func(tx *Transaction) error, opts ...TransactionOption) (cmt *Commit, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.RunInTransaction") + defer func() { trace.EndSpan(ctx, err) }() + + settings := newTransactionSettings(opts) + for n := 0; n < settings.attempts; n++ { + tx, err := c.newTransaction(ctx, settings) + if err != nil { + return nil, err + } + if err := f(tx); err != nil { + _ = tx.Rollback() + return nil, err + } + if cmt, err := tx.Commit(); err != ErrConcurrentTransaction { + return cmt, err + } + // Pass this transaction's ID to the retry transaction to preserve + // transaction priority. + if !settings.readOnly { + settings.prevID = tx.id + } + } + return nil, ErrConcurrentTransaction +} + +// Commit applies the enqueued operations atomically. +func (t *Transaction) Commit() (c *Commit, err error) { + t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Transaction.Commit") + defer func() { trace.EndSpan(t.ctx, err) }() + + if t.id == nil { + return nil, errExpiredTransaction + } + req := &pb.CommitRequest{ + ProjectId: t.client.dataset, + TransactionSelector: &pb.CommitRequest_Transaction{Transaction: t.id}, + Mutations: t.mutations, + Mode: pb.CommitRequest_TRANSACTIONAL, + } + t.id = nil + resp, err := t.client.client.Commit(t.ctx, req) + if err != nil { + if grpc.Code(err) == codes.Aborted { + return nil, ErrConcurrentTransaction + } + return nil, err + } + + // Copy any newly minted keys into the returned keys. + for i, p := range t.pending { + if i >= len(resp.MutationResults) || resp.MutationResults[i].Key == nil { + return nil, errors.New("datastore: internal error: server returned the wrong mutation results") + } + key, err := protoToKey(resp.MutationResults[i].Key) + if err != nil { + return nil, errors.New("datastore: internal error: server returned an invalid key") + } + p.key = key + p.commit = c + } + + return c, nil +} + +// Rollback abandons a pending transaction. +func (t *Transaction) Rollback() (err error) { + t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Transaction.Rollback") + defer func() { trace.EndSpan(t.ctx, err) }() + + if t.id == nil { + return errExpiredTransaction + } + id := t.id + t.id = nil + _, err = t.client.client.Rollback(t.ctx, &pb.RollbackRequest{ + ProjectId: t.client.dataset, + Transaction: id, + }) + return err +} + +// Get is the transaction-specific version of the package function Get. +// All reads performed during the transaction will come from a single consistent +// snapshot. Furthermore, if the transaction is set to a serializable isolation +// level, another transaction cannot concurrently modify the data that is read +// or modified by this transaction. +func (t *Transaction) Get(key *Key, dst interface{}) (err error) { + t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Transaction.Get") + defer func() { trace.EndSpan(t.ctx, err) }() + + opts := &pb.ReadOptions{ + ConsistencyType: &pb.ReadOptions_Transaction{Transaction: t.id}, + } + err = t.client.get(t.ctx, []*Key{key}, []interface{}{dst}, opts) + if me, ok := err.(MultiError); ok { + return me[0] + } + return err +} + +// GetMulti is a batch version of Get. +func (t *Transaction) GetMulti(keys []*Key, dst interface{}) (err error) { + t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Transaction.GetMulti") + defer func() { trace.EndSpan(t.ctx, err) }() + + if t.id == nil { + return errExpiredTransaction + } + opts := &pb.ReadOptions{ + ConsistencyType: &pb.ReadOptions_Transaction{Transaction: t.id}, + } + return t.client.get(t.ctx, keys, dst, opts) +} + +// Put is the transaction-specific version of the package function Put. +// +// Put returns a PendingKey which can be resolved into a Key using the +// return value from a successful Commit. If key is an incomplete key, the +// returned pending key will resolve to a unique key generated by the +// datastore. +func (t *Transaction) Put(key *Key, src interface{}) (*PendingKey, error) { + h, err := t.PutMulti([]*Key{key}, []interface{}{src}) + if err != nil { + if me, ok := err.(MultiError); ok { + return nil, me[0] + } + return nil, err + } + return h[0], nil +} + +// PutMulti is a batch version of Put. One PendingKey is returned for each +// element of src in the same order. +// TODO(jba): rewrite in terms of Mutate. +func (t *Transaction) PutMulti(keys []*Key, src interface{}) (ret []*PendingKey, err error) { + t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Transaction.PutMulti") + defer func() { trace.EndSpan(t.ctx, err) }() + + if t.id == nil { + return nil, errExpiredTransaction + } + mutations, err := putMutations(keys, src) + if err != nil { + return nil, err + } + origin := len(t.mutations) + t.mutations = append(t.mutations, mutations...) + + // Prepare the returned handles, pre-populating where possible. + ret = make([]*PendingKey, len(keys)) + for i, key := range keys { + p := &PendingKey{} + if key.Incomplete() { + // This key will be in the final commit result. + t.pending[origin+i] = p + } else { + p.key = key + } + ret[i] = p + } + + return ret, nil +} + +// Delete is the transaction-specific version of the package function Delete. +// Delete enqueues the deletion of the entity for the given key, to be +// committed atomically upon calling Commit. +func (t *Transaction) Delete(key *Key) error { + err := t.DeleteMulti([]*Key{key}) + if me, ok := err.(MultiError); ok { + return me[0] + } + return err +} + +// DeleteMulti is a batch version of Delete. +// TODO(jba): rewrite in terms of Mutate. +func (t *Transaction) DeleteMulti(keys []*Key) (err error) { + t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Transaction.DeleteMulti") + defer func() { trace.EndSpan(t.ctx, err) }() + + if t.id == nil { + return errExpiredTransaction + } + mutations, err := deleteMutations(keys) + if err != nil { + return err + } + t.mutations = append(t.mutations, mutations...) + return nil +} + +// Mutate adds the mutations to the transaction. They will all be applied atomically +// upon calling Commit. Mutate returns a PendingKey for each Mutation in the argument +// list, in the same order. PendingKeys for Delete mutations are always nil. +// +// If any of the mutations are invalid, Mutate returns a MultiError with the errors. +// Mutate returns a MultiError in this case even if there is only one Mutation. +// +// For an example, see Client.Mutate. +func (t *Transaction) Mutate(muts ...*Mutation) ([]*PendingKey, error) { + if t.id == nil { + return nil, errExpiredTransaction + } + pmuts, err := mutationProtos(muts) + if err != nil { + return nil, err + } + origin := len(t.mutations) + t.mutations = append(t.mutations, pmuts...) + // Prepare the returned handles, pre-populating where possible. + ret := make([]*PendingKey, len(muts)) + for i, mut := range muts { + if mut.isDelete() { + continue + } + p := &PendingKey{} + if mut.key.Incomplete() { + // This key will be in the final commit result. + t.pending[origin+i] = p + } else { + p.key = mut.key + } + ret[i] = p + } + return ret, nil +} + +// Commit represents the result of a committed transaction. +type Commit struct{} + +// Key resolves a pending key handle into a final key. +func (c *Commit) Key(p *PendingKey) *Key { + if p == nil { // if called on a *PendingKey from a Delete mutation + return nil + } + // If p.commit is nil, the PendingKey did not come from an incomplete key, + // so p.key is valid. + if p.commit != nil && c != p.commit { + panic("PendingKey was not created by corresponding transaction") + } + return p.key +} + +// PendingKey represents the key for newly-inserted entity. It can be +// resolved into a Key by calling the Key method of Commit. +type PendingKey struct { + key *Key + commit *Commit +} diff --git a/vendor/cloud.google.com/go/datastore/transaction_test.go b/vendor/cloud.google.com/go/datastore/transaction_test.go new file mode 100644 index 0000000000..88a7ca233b --- /dev/null +++ b/vendor/cloud.google.com/go/datastore/transaction_test.go @@ -0,0 +1,78 @@ +// Copyright 2017 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 datastore + +import ( + "testing" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + pb "google.golang.org/genproto/googleapis/datastore/v1" +) + +func TestNewTransaction(t *testing.T) { + var got *pb.BeginTransactionRequest + client := &Client{ + dataset: "project", + client: &fakeDatastoreClient{ + beginTransaction: func(req *pb.BeginTransactionRequest) (*pb.BeginTransactionResponse, error) { + got = req + return &pb.BeginTransactionResponse{ + Transaction: []byte("tid"), + }, nil + }, + }, + } + ctx := context.Background() + for _, test := range []struct { + settings *transactionSettings + want *pb.BeginTransactionRequest + }{ + { + &transactionSettings{}, + &pb.BeginTransactionRequest{ProjectId: "project"}, + }, + { + &transactionSettings{readOnly: true}, + &pb.BeginTransactionRequest{ + ProjectId: "project", + TransactionOptions: &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadOnly_{ReadOnly: &pb.TransactionOptions_ReadOnly{}}, + }, + }, + }, + { + &transactionSettings{prevID: []byte("tid")}, + &pb.BeginTransactionRequest{ + ProjectId: "project", + TransactionOptions: &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadWrite_{ReadWrite: &pb.TransactionOptions_ReadWrite{ + PreviousTransaction: []byte("tid"), + }, + }, + }, + }, + }, + } { + _, err := client.newTransaction(ctx, test.settings) + if err != nil { + t.Fatal(err) + } + if !proto.Equal(got, test.want) { + t.Errorf("%+v:\ngot %+v\nwant %+v", test.settings, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/debugger/apiv2/controller2_client.go b/vendor/cloud.google.com/go/debugger/apiv2/controller2_client.go new file mode 100644 index 0000000000..9dfa3bdc46 --- /dev/null +++ b/vendor/cloud.google.com/go/debugger/apiv2/controller2_client.go @@ -0,0 +1,217 @@ +// 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 debugger + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + clouddebuggerpb "google.golang.org/genproto/googleapis/devtools/clouddebugger/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// Controller2CallOptions contains the retry settings for each method of Controller2Client. +type Controller2CallOptions struct { + RegisterDebuggee []gax.CallOption + ListActiveBreakpoints []gax.CallOption + UpdateActiveBreakpoint []gax.CallOption +} + +func defaultController2ClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("clouddebugger.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultController2CallOptions() *Controller2CallOptions { + 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 &Controller2CallOptions{ + RegisterDebuggee: retry[[2]string{"default", "non_idempotent"}], + ListActiveBreakpoints: retry[[2]string{"default", "idempotent"}], + UpdateActiveBreakpoint: retry[[2]string{"default", "idempotent"}], + } +} + +// Controller2Client is a client for interacting with Stackdriver Debugger API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type Controller2Client struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + controller2Client clouddebuggerpb.Controller2Client + + // The call options for this service. + CallOptions *Controller2CallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewController2Client creates a new controller2 client. +// +// The Controller service provides the API for orchestrating a collection of +// debugger agents to perform debugging tasks. These agents are each attached +// to a process of an application which may include one or more replicas. +// +// The debugger agents register with the Controller to identify the application +// being debugged, the Debuggee. All agents that register with the same data, +// represent the same Debuggee, and are assigned the same debuggee_id. +// +// The debugger agents call the Controller to retrieve the list of active +// Breakpoints. Agents with the same debuggee_id get the same breakpoints +// list. An agent that can fulfill the breakpoint request updates the +// Controller with the breakpoint result. The controller selects the first +// result received and discards the rest of the results. +// Agents that poll again for active breakpoints will no longer have +// the completed breakpoint in the list and should remove that breakpoint from +// their attached process. +// +// The Controller service does not provide a way to retrieve the results of +// a completed breakpoint. This functionality is available using the Debugger +// service. +func NewController2Client(ctx context.Context, opts ...option.ClientOption) (*Controller2Client, error) { + conn, err := transport.DialGRPC(ctx, append(defaultController2ClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &Controller2Client{ + conn: conn, + CallOptions: defaultController2CallOptions(), + + controller2Client: clouddebuggerpb.NewController2Client(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *Controller2Client) 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 *Controller2Client) 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 *Controller2Client) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// RegisterDebuggee registers the debuggee with the controller service. +// +// All agents attached to the same application must call this method with +// exactly the same request content to get back the same stable debuggee_id. +// Agents should call this method again whenever google.rpc.Code.NOT_FOUND +// is returned from any controller method. +// +// This protocol allows the controller service to disable debuggees, recover +// from data loss, or change the debuggee_id format. Agents must handle +// debuggee_id value changing upon re-registration. +func (c *Controller2Client) RegisterDebuggee(ctx context.Context, req *clouddebuggerpb.RegisterDebuggeeRequest, opts ...gax.CallOption) (*clouddebuggerpb.RegisterDebuggeeResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.RegisterDebuggee[0:len(c.CallOptions.RegisterDebuggee):len(c.CallOptions.RegisterDebuggee)], opts...) + var resp *clouddebuggerpb.RegisterDebuggeeResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.controller2Client.RegisterDebuggee(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListActiveBreakpoints returns the list of all active breakpoints for the debuggee. +// +// The breakpoint specification (location, condition, and expressions +// fields) is semantically immutable, although the field values may +// change. For example, an agent may update the location line number +// to reflect the actual line where the breakpoint was set, but this +// doesn't change the breakpoint semantics. +// +// This means that an agent does not need to check if a breakpoint has changed +// when it encounters the same breakpoint on a successive call. +// Moreover, an agent should remember the breakpoints that are completed +// until the controller removes them from the active list to avoid +// setting those breakpoints again. +func (c *Controller2Client) ListActiveBreakpoints(ctx context.Context, req *clouddebuggerpb.ListActiveBreakpointsRequest, opts ...gax.CallOption) (*clouddebuggerpb.ListActiveBreakpointsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListActiveBreakpoints[0:len(c.CallOptions.ListActiveBreakpoints):len(c.CallOptions.ListActiveBreakpoints)], opts...) + var resp *clouddebuggerpb.ListActiveBreakpointsResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.controller2Client.ListActiveBreakpoints(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateActiveBreakpoint updates the breakpoint state or mutable fields. +// The entire Breakpoint message must be sent back to the controller service. +// +// Updates to active breakpoint fields are only allowed if the new value +// does not change the breakpoint specification. Updates to the location, +// condition and expressions fields should not alter the breakpoint +// semantics. These may only make changes such as canonicalizing a value +// or snapping the location to the correct line of code. +func (c *Controller2Client) UpdateActiveBreakpoint(ctx context.Context, req *clouddebuggerpb.UpdateActiveBreakpointRequest, opts ...gax.CallOption) (*clouddebuggerpb.UpdateActiveBreakpointResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateActiveBreakpoint[0:len(c.CallOptions.UpdateActiveBreakpoint):len(c.CallOptions.UpdateActiveBreakpoint)], opts...) + var resp *clouddebuggerpb.UpdateActiveBreakpointResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.controller2Client.UpdateActiveBreakpoint(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/debugger/apiv2/controller2_client_example_test.go b/vendor/cloud.google.com/go/debugger/apiv2/controller2_client_example_test.go new file mode 100644 index 0000000000..4ea1860788 --- /dev/null +++ b/vendor/cloud.google.com/go/debugger/apiv2/controller2_client_example_test.go @@ -0,0 +1,87 @@ +// 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 debugger_test + +import ( + "cloud.google.com/go/debugger/apiv2" + "golang.org/x/net/context" + clouddebuggerpb "google.golang.org/genproto/googleapis/devtools/clouddebugger/v2" +) + +func ExampleNewController2Client() { + ctx := context.Background() + c, err := debugger.NewController2Client(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleController2Client_RegisterDebuggee() { + ctx := context.Background() + c, err := debugger.NewController2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.RegisterDebuggeeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.RegisterDebuggee(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleController2Client_ListActiveBreakpoints() { + ctx := context.Background() + c, err := debugger.NewController2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.ListActiveBreakpointsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListActiveBreakpoints(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleController2Client_UpdateActiveBreakpoint() { + ctx := context.Background() + c, err := debugger.NewController2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.UpdateActiveBreakpointRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateActiveBreakpoint(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/debugger/apiv2/debugger2_client.go b/vendor/cloud.google.com/go/debugger/apiv2/debugger2_client.go new file mode 100644 index 0000000000..680aa5ed10 --- /dev/null +++ b/vendor/cloud.google.com/go/debugger/apiv2/debugger2_client.go @@ -0,0 +1,213 @@ +// 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 debugger + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + clouddebuggerpb "google.golang.org/genproto/googleapis/devtools/clouddebugger/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// Debugger2CallOptions contains the retry settings for each method of Debugger2Client. +type Debugger2CallOptions struct { + SetBreakpoint []gax.CallOption + GetBreakpoint []gax.CallOption + DeleteBreakpoint []gax.CallOption + ListBreakpoints []gax.CallOption + ListDebuggees []gax.CallOption +} + +func defaultDebugger2ClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("clouddebugger.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultDebugger2CallOptions() *Debugger2CallOptions { + 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 &Debugger2CallOptions{ + SetBreakpoint: retry[[2]string{"default", "non_idempotent"}], + GetBreakpoint: retry[[2]string{"default", "idempotent"}], + DeleteBreakpoint: retry[[2]string{"default", "idempotent"}], + ListBreakpoints: retry[[2]string{"default", "idempotent"}], + ListDebuggees: retry[[2]string{"default", "idempotent"}], + } +} + +// Debugger2Client is a client for interacting with Stackdriver Debugger API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type Debugger2Client struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + debugger2Client clouddebuggerpb.Debugger2Client + + // The call options for this service. + CallOptions *Debugger2CallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewDebugger2Client creates a new debugger2 client. +// +// The Debugger service provides the API that allows users to collect run-time +// information from a running application, without stopping or slowing it down +// and without modifying its state. An application may include one or +// more replicated processes performing the same work. +// +// A debugged application is represented using the Debuggee concept. The +// Debugger service provides a way to query for available debuggees, but does +// not provide a way to create one. A debuggee is created using the Controller +// service, usually by running a debugger agent with the application. +// +// The Debugger service enables the client to set one or more Breakpoints on a +// Debuggee and collect the results of the set Breakpoints. +func NewDebugger2Client(ctx context.Context, opts ...option.ClientOption) (*Debugger2Client, error) { + conn, err := transport.DialGRPC(ctx, append(defaultDebugger2ClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &Debugger2Client{ + conn: conn, + CallOptions: defaultDebugger2CallOptions(), + + debugger2Client: clouddebuggerpb.NewDebugger2Client(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *Debugger2Client) 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 *Debugger2Client) 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 *Debugger2Client) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// SetBreakpoint sets the breakpoint to the debuggee. +func (c *Debugger2Client) SetBreakpoint(ctx context.Context, req *clouddebuggerpb.SetBreakpointRequest, opts ...gax.CallOption) (*clouddebuggerpb.SetBreakpointResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SetBreakpoint[0:len(c.CallOptions.SetBreakpoint):len(c.CallOptions.SetBreakpoint)], opts...) + var resp *clouddebuggerpb.SetBreakpointResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.debugger2Client.SetBreakpoint(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetBreakpoint gets breakpoint information. +func (c *Debugger2Client) GetBreakpoint(ctx context.Context, req *clouddebuggerpb.GetBreakpointRequest, opts ...gax.CallOption) (*clouddebuggerpb.GetBreakpointResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetBreakpoint[0:len(c.CallOptions.GetBreakpoint):len(c.CallOptions.GetBreakpoint)], opts...) + var resp *clouddebuggerpb.GetBreakpointResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.debugger2Client.GetBreakpoint(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteBreakpoint deletes the breakpoint from the debuggee. +func (c *Debugger2Client) DeleteBreakpoint(ctx context.Context, req *clouddebuggerpb.DeleteBreakpointRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteBreakpoint[0:len(c.CallOptions.DeleteBreakpoint):len(c.CallOptions.DeleteBreakpoint)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.debugger2Client.DeleteBreakpoint(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListBreakpoints lists all breakpoints for the debuggee. +func (c *Debugger2Client) ListBreakpoints(ctx context.Context, req *clouddebuggerpb.ListBreakpointsRequest, opts ...gax.CallOption) (*clouddebuggerpb.ListBreakpointsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListBreakpoints[0:len(c.CallOptions.ListBreakpoints):len(c.CallOptions.ListBreakpoints)], opts...) + var resp *clouddebuggerpb.ListBreakpointsResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.debugger2Client.ListBreakpoints(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListDebuggees lists all the debuggees that the user has access to. +func (c *Debugger2Client) ListDebuggees(ctx context.Context, req *clouddebuggerpb.ListDebuggeesRequest, opts ...gax.CallOption) (*clouddebuggerpb.ListDebuggeesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListDebuggees[0:len(c.CallOptions.ListDebuggees):len(c.CallOptions.ListDebuggees)], opts...) + var resp *clouddebuggerpb.ListDebuggeesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.debugger2Client.ListDebuggees(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/debugger/apiv2/debugger2_client_example_test.go b/vendor/cloud.google.com/go/debugger/apiv2/debugger2_client_example_test.go new file mode 100644 index 0000000000..b7f72e95cf --- /dev/null +++ b/vendor/cloud.google.com/go/debugger/apiv2/debugger2_client_example_test.go @@ -0,0 +1,121 @@ +// 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 debugger_test + +import ( + "cloud.google.com/go/debugger/apiv2" + "golang.org/x/net/context" + clouddebuggerpb "google.golang.org/genproto/googleapis/devtools/clouddebugger/v2" +) + +func ExampleNewDebugger2Client() { + ctx := context.Background() + c, err := debugger.NewDebugger2Client(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleDebugger2Client_SetBreakpoint() { + ctx := context.Background() + c, err := debugger.NewDebugger2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.SetBreakpointRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SetBreakpoint(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleDebugger2Client_GetBreakpoint() { + ctx := context.Background() + c, err := debugger.NewDebugger2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.GetBreakpointRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetBreakpoint(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleDebugger2Client_DeleteBreakpoint() { + ctx := context.Background() + c, err := debugger.NewDebugger2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.DeleteBreakpointRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteBreakpoint(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleDebugger2Client_ListBreakpoints() { + ctx := context.Background() + c, err := debugger.NewDebugger2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.ListBreakpointsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListBreakpoints(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleDebugger2Client_ListDebuggees() { + ctx := context.Background() + c, err := debugger.NewDebugger2Client(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouddebuggerpb.ListDebuggeesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListDebuggees(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/debugger/apiv2/doc.go b/vendor/cloud.google.com/go/debugger/apiv2/doc.go new file mode 100644 index 0000000000..56402afb81 --- /dev/null +++ b/vendor/cloud.google.com/go/debugger/apiv2/doc.go @@ -0,0 +1,50 @@ +// 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 debugger is an auto-generated package for the +// Stackdriver Debugger API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Examines the call stack and variables of a running application +// without stopping or slowing it down. +// +// Use the client at cloud.google.com/go/cmd/go-cloud-debug-agent in preference to this. +package debugger // import "cloud.google.com/go/debugger/apiv2" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/cloud_debugger", + } +} diff --git a/vendor/cloud.google.com/go/debugger/apiv2/mock_test.go b/vendor/cloud.google.com/go/debugger/apiv2/mock_test.go new file mode 100644 index 0000000000..50591543bd --- /dev/null +++ b/vendor/cloud.google.com/go/debugger/apiv2/mock_test.go @@ -0,0 +1,693 @@ +// 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 debugger + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + clouddebuggerpb "google.golang.org/genproto/googleapis/devtools/clouddebugger/v2" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockDebugger2Server struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + clouddebuggerpb.Debugger2Server + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockDebugger2Server) SetBreakpoint(ctx context.Context, req *clouddebuggerpb.SetBreakpointRequest) (*clouddebuggerpb.SetBreakpointResponse, 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].(*clouddebuggerpb.SetBreakpointResponse), nil +} + +func (s *mockDebugger2Server) GetBreakpoint(ctx context.Context, req *clouddebuggerpb.GetBreakpointRequest) (*clouddebuggerpb.GetBreakpointResponse, 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].(*clouddebuggerpb.GetBreakpointResponse), nil +} + +func (s *mockDebugger2Server) DeleteBreakpoint(ctx context.Context, req *clouddebuggerpb.DeleteBreakpointRequest) (*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 *mockDebugger2Server) ListBreakpoints(ctx context.Context, req *clouddebuggerpb.ListBreakpointsRequest) (*clouddebuggerpb.ListBreakpointsResponse, 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].(*clouddebuggerpb.ListBreakpointsResponse), nil +} + +func (s *mockDebugger2Server) ListDebuggees(ctx context.Context, req *clouddebuggerpb.ListDebuggeesRequest) (*clouddebuggerpb.ListDebuggeesResponse, 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].(*clouddebuggerpb.ListDebuggeesResponse), nil +} + +type mockController2Server struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + clouddebuggerpb.Controller2Server + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockController2Server) RegisterDebuggee(ctx context.Context, req *clouddebuggerpb.RegisterDebuggeeRequest) (*clouddebuggerpb.RegisterDebuggeeResponse, 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].(*clouddebuggerpb.RegisterDebuggeeResponse), nil +} + +func (s *mockController2Server) ListActiveBreakpoints(ctx context.Context, req *clouddebuggerpb.ListActiveBreakpointsRequest) (*clouddebuggerpb.ListActiveBreakpointsResponse, 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].(*clouddebuggerpb.ListActiveBreakpointsResponse), nil +} + +func (s *mockController2Server) UpdateActiveBreakpoint(ctx context.Context, req *clouddebuggerpb.UpdateActiveBreakpointRequest) (*clouddebuggerpb.UpdateActiveBreakpointResponse, 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].(*clouddebuggerpb.UpdateActiveBreakpointResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockDebugger2 mockDebugger2Server + mockController2 mockController2Server +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + clouddebuggerpb.RegisterDebugger2Server(serv, &mockDebugger2) + clouddebuggerpb.RegisterController2Server(serv, &mockController2) + + 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 TestDebugger2SetBreakpoint(t *testing.T) { + var expectedResponse *clouddebuggerpb.SetBreakpointResponse = &clouddebuggerpb.SetBreakpointResponse{} + + mockDebugger2.err = nil + mockDebugger2.reqs = nil + + mockDebugger2.resps = append(mockDebugger2.resps[:0], expectedResponse) + + var debuggeeId string = "debuggeeId-997255898" + var breakpoint *clouddebuggerpb.Breakpoint = &clouddebuggerpb.Breakpoint{} + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.SetBreakpointRequest{ + DebuggeeId: debuggeeId, + Breakpoint: breakpoint, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetBreakpoint(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDebugger2.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 TestDebugger2SetBreakpointError(t *testing.T) { + errCode := codes.PermissionDenied + mockDebugger2.err = gstatus.Error(errCode, "test error") + + var debuggeeId string = "debuggeeId-997255898" + var breakpoint *clouddebuggerpb.Breakpoint = &clouddebuggerpb.Breakpoint{} + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.SetBreakpointRequest{ + DebuggeeId: debuggeeId, + Breakpoint: breakpoint, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SetBreakpoint(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 TestDebugger2GetBreakpoint(t *testing.T) { + var expectedResponse *clouddebuggerpb.GetBreakpointResponse = &clouddebuggerpb.GetBreakpointResponse{} + + mockDebugger2.err = nil + mockDebugger2.reqs = nil + + mockDebugger2.resps = append(mockDebugger2.resps[:0], expectedResponse) + + var debuggeeId string = "debuggeeId-997255898" + var breakpointId string = "breakpointId498424873" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.GetBreakpointRequest{ + DebuggeeId: debuggeeId, + BreakpointId: breakpointId, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetBreakpoint(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDebugger2.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 TestDebugger2GetBreakpointError(t *testing.T) { + errCode := codes.PermissionDenied + mockDebugger2.err = gstatus.Error(errCode, "test error") + + var debuggeeId string = "debuggeeId-997255898" + var breakpointId string = "breakpointId498424873" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.GetBreakpointRequest{ + DebuggeeId: debuggeeId, + BreakpointId: breakpointId, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetBreakpoint(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 TestDebugger2DeleteBreakpoint(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDebugger2.err = nil + mockDebugger2.reqs = nil + + mockDebugger2.resps = append(mockDebugger2.resps[:0], expectedResponse) + + var debuggeeId string = "debuggeeId-997255898" + var breakpointId string = "breakpointId498424873" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.DeleteBreakpointRequest{ + DebuggeeId: debuggeeId, + BreakpointId: breakpointId, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteBreakpoint(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDebugger2.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDebugger2DeleteBreakpointError(t *testing.T) { + errCode := codes.PermissionDenied + mockDebugger2.err = gstatus.Error(errCode, "test error") + + var debuggeeId string = "debuggeeId-997255898" + var breakpointId string = "breakpointId498424873" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.DeleteBreakpointRequest{ + DebuggeeId: debuggeeId, + BreakpointId: breakpointId, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteBreakpoint(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 TestDebugger2ListBreakpoints(t *testing.T) { + var nextWaitToken string = "nextWaitToken1006864251" + var expectedResponse = &clouddebuggerpb.ListBreakpointsResponse{ + NextWaitToken: nextWaitToken, + } + + mockDebugger2.err = nil + mockDebugger2.reqs = nil + + mockDebugger2.resps = append(mockDebugger2.resps[:0], expectedResponse) + + var debuggeeId string = "debuggeeId-997255898" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.ListBreakpointsRequest{ + DebuggeeId: debuggeeId, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListBreakpoints(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDebugger2.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 TestDebugger2ListBreakpointsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDebugger2.err = gstatus.Error(errCode, "test error") + + var debuggeeId string = "debuggeeId-997255898" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.ListBreakpointsRequest{ + DebuggeeId: debuggeeId, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListBreakpoints(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 TestDebugger2ListDebuggees(t *testing.T) { + var expectedResponse *clouddebuggerpb.ListDebuggeesResponse = &clouddebuggerpb.ListDebuggeesResponse{} + + mockDebugger2.err = nil + mockDebugger2.reqs = nil + + mockDebugger2.resps = append(mockDebugger2.resps[:0], expectedResponse) + + var project string = "project-309310695" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.ListDebuggeesRequest{ + Project: project, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDebuggees(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDebugger2.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 TestDebugger2ListDebuggeesError(t *testing.T) { + errCode := codes.PermissionDenied + mockDebugger2.err = gstatus.Error(errCode, "test error") + + var project string = "project-309310695" + var clientVersion string = "clientVersion-1506231196" + var request = &clouddebuggerpb.ListDebuggeesRequest{ + Project: project, + ClientVersion: clientVersion, + } + + c, err := NewDebugger2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDebuggees(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 TestController2RegisterDebuggee(t *testing.T) { + var expectedResponse *clouddebuggerpb.RegisterDebuggeeResponse = &clouddebuggerpb.RegisterDebuggeeResponse{} + + mockController2.err = nil + mockController2.reqs = nil + + mockController2.resps = append(mockController2.resps[:0], expectedResponse) + + var debuggee *clouddebuggerpb.Debuggee = &clouddebuggerpb.Debuggee{} + var request = &clouddebuggerpb.RegisterDebuggeeRequest{ + Debuggee: debuggee, + } + + c, err := NewController2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RegisterDebuggee(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockController2.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 TestController2RegisterDebuggeeError(t *testing.T) { + errCode := codes.PermissionDenied + mockController2.err = gstatus.Error(errCode, "test error") + + var debuggee *clouddebuggerpb.Debuggee = &clouddebuggerpb.Debuggee{} + var request = &clouddebuggerpb.RegisterDebuggeeRequest{ + Debuggee: debuggee, + } + + c, err := NewController2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RegisterDebuggee(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 TestController2ListActiveBreakpoints(t *testing.T) { + var nextWaitToken string = "nextWaitToken1006864251" + var waitExpired bool = false + var expectedResponse = &clouddebuggerpb.ListActiveBreakpointsResponse{ + NextWaitToken: nextWaitToken, + WaitExpired: waitExpired, + } + + mockController2.err = nil + mockController2.reqs = nil + + mockController2.resps = append(mockController2.resps[:0], expectedResponse) + + var debuggeeId string = "debuggeeId-997255898" + var request = &clouddebuggerpb.ListActiveBreakpointsRequest{ + DebuggeeId: debuggeeId, + } + + c, err := NewController2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListActiveBreakpoints(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockController2.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 TestController2ListActiveBreakpointsError(t *testing.T) { + errCode := codes.PermissionDenied + mockController2.err = gstatus.Error(errCode, "test error") + + var debuggeeId string = "debuggeeId-997255898" + var request = &clouddebuggerpb.ListActiveBreakpointsRequest{ + DebuggeeId: debuggeeId, + } + + c, err := NewController2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListActiveBreakpoints(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 TestController2UpdateActiveBreakpoint(t *testing.T) { + var expectedResponse *clouddebuggerpb.UpdateActiveBreakpointResponse = &clouddebuggerpb.UpdateActiveBreakpointResponse{} + + mockController2.err = nil + mockController2.reqs = nil + + mockController2.resps = append(mockController2.resps[:0], expectedResponse) + + var debuggeeId string = "debuggeeId-997255898" + var breakpoint *clouddebuggerpb.Breakpoint = &clouddebuggerpb.Breakpoint{} + var request = &clouddebuggerpb.UpdateActiveBreakpointRequest{ + DebuggeeId: debuggeeId, + Breakpoint: breakpoint, + } + + c, err := NewController2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateActiveBreakpoint(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockController2.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 TestController2UpdateActiveBreakpointError(t *testing.T) { + errCode := codes.PermissionDenied + mockController2.err = gstatus.Error(errCode, "test error") + + var debuggeeId string = "debuggeeId-997255898" + var breakpoint *clouddebuggerpb.Breakpoint = &clouddebuggerpb.Breakpoint{} + var request = &clouddebuggerpb.UpdateActiveBreakpointRequest{ + DebuggeeId: debuggeeId, + Breakpoint: breakpoint, + } + + c, err := NewController2Client(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateActiveBreakpoint(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/vendor/cloud.google.com/go/dialogflow/apiv2/agents_client.go b/vendor/cloud.google.com/go/dialogflow/apiv2/agents_client.go new file mode 100644 index 0000000000..3238b45754 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/agents_client.go @@ -0,0 +1,604 @@ +// 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 dialogflow + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/proto" + structpbpb "github.com/golang/protobuf/ptypes/struct" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// AgentsCallOptions contains the retry settings for each method of AgentsClient. +type AgentsCallOptions struct { + GetAgent []gax.CallOption + SearchAgents []gax.CallOption + TrainAgent []gax.CallOption + ExportAgent []gax.CallOption + ImportAgent []gax.CallOption + RestoreAgent []gax.CallOption +} + +func defaultAgentsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dialogflow.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultAgentsCallOptions() *AgentsCallOptions { + 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 &AgentsCallOptions{ + GetAgent: retry[[2]string{"default", "idempotent"}], + SearchAgents: retry[[2]string{"default", "idempotent"}], + TrainAgent: retry[[2]string{"default", "idempotent"}], + ExportAgent: retry[[2]string{"default", "idempotent"}], + ImportAgent: retry[[2]string{"default", "non_idempotent"}], + RestoreAgent: retry[[2]string{"default", "idempotent"}], + } +} + +// AgentsClient is a client for interacting with Dialogflow API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type AgentsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + agentsClient dialogflowpb.AgentsClient + + // 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 *AgentsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewAgentsClient creates a new agents client. +// +// Agents are best described as Natural Language Understanding (NLU) modules +// that transform user requests into actionable data. You can include agents +// in your app, product, or service to determine user intent and respond to the +// user in a natural way. +// +// After you create an agent, you can add [Intents][google.cloud.dialogflow.v2.Intents], [Contexts][google.cloud.dialogflow.v2.Contexts], +// [Entity Types][google.cloud.dialogflow.v2.EntityTypes], [Webhooks][google.cloud.dialogflow.v2.WebhookRequest], and so on to +// manage the flow of a conversation and match user input to predefined intents +// and actions. +// +// You can create an agent using both Dialogflow Standard Edition and +// Dialogflow Enterprise Edition. For details, see +// Dialogflow Editions (at /dialogflow-enterprise/docs/editions). +// +// You can save your agent for backup or versioning by exporting the agent by +// using the [ExportAgent][google.cloud.dialogflow.v2.Agents.ExportAgent] method. You can import a saved +// agent by using the [ImportAgent][google.cloud.dialogflow.v2.Agents.ImportAgent] method. +// +// Dialogflow provides several +// prebuilt agents (at https://dialogflow.com/docs/prebuilt-agents) for common +// conversation scenarios such as determining a date and time, converting +// currency, and so on. +// +// For more information about agents, see the +// Dialogflow documentation (at https://dialogflow.com/docs/agents). +func NewAgentsClient(ctx context.Context, opts ...option.ClientOption) (*AgentsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultAgentsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &AgentsClient{ + conn: conn, + CallOptions: defaultAgentsCallOptions(), + + agentsClient: dialogflowpb.NewAgentsClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *AgentsClient) 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 *AgentsClient) 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 *AgentsClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// GetAgent retrieves the specified agent. +func (c *AgentsClient) GetAgent(ctx context.Context, req *dialogflowpb.GetAgentRequest, opts ...gax.CallOption) (*dialogflowpb.Agent, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetAgent[0:len(c.CallOptions.GetAgent):len(c.CallOptions.GetAgent)], opts...) + var resp *dialogflowpb.Agent + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.agentsClient.GetAgent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SearchAgents returns the list of agents. +// +// Since there is at most one conversational agent per project, this method is +// useful primarily for listing all agents across projects the caller has +// access to. One can achieve that with a wildcard project collection id "-". +// Refer to List +// Sub-Collections (at https://cloud.google.com/apis/design/design_patterns#list_sub-collections). +func (c *AgentsClient) SearchAgents(ctx context.Context, req *dialogflowpb.SearchAgentsRequest, opts ...gax.CallOption) *AgentIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SearchAgents[0:len(c.CallOptions.SearchAgents):len(c.CallOptions.SearchAgents)], opts...) + it := &AgentIterator{} + req = proto.Clone(req).(*dialogflowpb.SearchAgentsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dialogflowpb.Agent, string, error) { + var resp *dialogflowpb.SearchAgentsResponse + 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.agentsClient.SearchAgents(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Agents, 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 +} + +// TrainAgent trains the specified agent. +// +// Operation +func (c *AgentsClient) TrainAgent(ctx context.Context, req *dialogflowpb.TrainAgentRequest, opts ...gax.CallOption) (*TrainAgentOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.TrainAgent[0:len(c.CallOptions.TrainAgent):len(c.CallOptions.TrainAgent)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.agentsClient.TrainAgent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &TrainAgentOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// ExportAgent exports the specified agent to a ZIP file. +// +// Operation +func (c *AgentsClient) ExportAgent(ctx context.Context, req *dialogflowpb.ExportAgentRequest, opts ...gax.CallOption) (*ExportAgentOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ExportAgent[0:len(c.CallOptions.ExportAgent):len(c.CallOptions.ExportAgent)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.agentsClient.ExportAgent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &ExportAgentOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// ImportAgent imports the specified agent from a ZIP file. +// +// Uploads new intents and entity types without deleting the existing ones. +// Intents and entity types with the same name are replaced with the new +// versions from ImportAgentRequest. +// +// Operation +func (c *AgentsClient) ImportAgent(ctx context.Context, req *dialogflowpb.ImportAgentRequest, opts ...gax.CallOption) (*ImportAgentOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ImportAgent[0:len(c.CallOptions.ImportAgent):len(c.CallOptions.ImportAgent)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.agentsClient.ImportAgent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &ImportAgentOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// RestoreAgent restores the specified agent from a ZIP file. +// +// Replaces the current agent version with a new one. All the intents and +// entity types in the older version are deleted. +// +// Operation +func (c *AgentsClient) RestoreAgent(ctx context.Context, req *dialogflowpb.RestoreAgentRequest, opts ...gax.CallOption) (*RestoreAgentOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.RestoreAgent[0:len(c.CallOptions.RestoreAgent):len(c.CallOptions.RestoreAgent)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.agentsClient.RestoreAgent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &RestoreAgentOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// AgentIterator manages a stream of *dialogflowpb.Agent. +type AgentIterator struct { + items []*dialogflowpb.Agent + 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 []*dialogflowpb.Agent, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *AgentIterator) 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 *AgentIterator) Next() (*dialogflowpb.Agent, error) { + var item *dialogflowpb.Agent + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *AgentIterator) bufLen() int { + return len(it.items) +} + +func (it *AgentIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// ExportAgentOperation manages a long-running operation from ExportAgent. +type ExportAgentOperation struct { + lro *longrunning.Operation +} + +// ExportAgentOperation returns a new ExportAgentOperation from a given name. +// The name must be that of a previously created ExportAgentOperation, possibly from a different process. +func (c *AgentsClient) ExportAgentOperation(name string) *ExportAgentOperation { + return &ExportAgentOperation{ + 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 *ExportAgentOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*dialogflowpb.ExportAgentResponse, error) { + var resp dialogflowpb.ExportAgentResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 5000*time.Millisecond, 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 *ExportAgentOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*dialogflowpb.ExportAgentResponse, error) { + var resp dialogflowpb.ExportAgentResponse + 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 *ExportAgentOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *ExportAgentOperation) 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 *ExportAgentOperation) Name() string { + return op.lro.Name() +} + +// ImportAgentOperation manages a long-running operation from ImportAgent. +type ImportAgentOperation struct { + lro *longrunning.Operation +} + +// ImportAgentOperation returns a new ImportAgentOperation from a given name. +// The name must be that of a previously created ImportAgentOperation, possibly from a different process. +func (c *AgentsClient) ImportAgentOperation(name string) *ImportAgentOperation { + return &ImportAgentOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *ImportAgentOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *ImportAgentOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *ImportAgentOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *ImportAgentOperation) 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 *ImportAgentOperation) Name() string { + return op.lro.Name() +} + +// RestoreAgentOperation manages a long-running operation from RestoreAgent. +type RestoreAgentOperation struct { + lro *longrunning.Operation +} + +// RestoreAgentOperation returns a new RestoreAgentOperation from a given name. +// The name must be that of a previously created RestoreAgentOperation, possibly from a different process. +func (c *AgentsClient) RestoreAgentOperation(name string) *RestoreAgentOperation { + return &RestoreAgentOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *RestoreAgentOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *RestoreAgentOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *RestoreAgentOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *RestoreAgentOperation) 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 *RestoreAgentOperation) Name() string { + return op.lro.Name() +} + +// TrainAgentOperation manages a long-running operation from TrainAgent. +type TrainAgentOperation struct { + lro *longrunning.Operation +} + +// TrainAgentOperation returns a new TrainAgentOperation from a given name. +// The name must be that of a previously created TrainAgentOperation, possibly from a different process. +func (c *AgentsClient) TrainAgentOperation(name string) *TrainAgentOperation { + return &TrainAgentOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *TrainAgentOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *TrainAgentOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *TrainAgentOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *TrainAgentOperation) 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 *TrainAgentOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/agents_client_example_test.go b/vendor/cloud.google.com/go/dialogflow/apiv2/agents_client_example_test.go new file mode 100644 index 0000000000..92c18f5527 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/agents_client_example_test.go @@ -0,0 +1,156 @@ +// 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 dialogflow_test + +import ( + "cloud.google.com/go/dialogflow/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" +) + +func ExampleNewAgentsClient() { + ctx := context.Background() + c, err := dialogflow.NewAgentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleAgentsClient_GetAgent() { + ctx := context.Background() + c, err := dialogflow.NewAgentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.GetAgentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetAgent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleAgentsClient_SearchAgents() { + ctx := context.Background() + c, err := dialogflow.NewAgentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.SearchAgentsRequest{ + // TODO: Fill request struct fields. + } + it := c.SearchAgents(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleAgentsClient_TrainAgent() { + ctx := context.Background() + c, err := dialogflow.NewAgentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.TrainAgentRequest{ + // TODO: Fill request struct fields. + } + op, err := c.TrainAgent(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} + +func ExampleAgentsClient_ExportAgent() { + ctx := context.Background() + c, err := dialogflow.NewAgentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.ExportAgentRequest{ + // TODO: Fill request struct fields. + } + op, err := c.ExportAgent(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleAgentsClient_ImportAgent() { + ctx := context.Background() + c, err := dialogflow.NewAgentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.ImportAgentRequest{ + // TODO: Fill request struct fields. + } + op, err := c.ImportAgent(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} + +func ExampleAgentsClient_RestoreAgent() { + ctx := context.Background() + c, err := dialogflow.NewAgentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.RestoreAgentRequest{ + // TODO: Fill request struct fields. + } + op, err := c.RestoreAgent(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/contexts_client.go b/vendor/cloud.google.com/go/dialogflow/apiv2/contexts_client.go new file mode 100644 index 0000000000..7782c60d8d --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/contexts_client.go @@ -0,0 +1,298 @@ +// 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 dialogflow + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ContextsCallOptions contains the retry settings for each method of ContextsClient. +type ContextsCallOptions struct { + ListContexts []gax.CallOption + GetContext []gax.CallOption + CreateContext []gax.CallOption + UpdateContext []gax.CallOption + DeleteContext []gax.CallOption + DeleteAllContexts []gax.CallOption +} + +func defaultContextsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dialogflow.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultContextsCallOptions() *ContextsCallOptions { + 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 &ContextsCallOptions{ + ListContexts: retry[[2]string{"default", "idempotent"}], + GetContext: retry[[2]string{"default", "idempotent"}], + CreateContext: retry[[2]string{"default", "non_idempotent"}], + UpdateContext: retry[[2]string{"default", "non_idempotent"}], + DeleteContext: retry[[2]string{"default", "idempotent"}], + DeleteAllContexts: retry[[2]string{"default", "idempotent"}], + } +} + +// ContextsClient is a client for interacting with Dialogflow API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ContextsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + contextsClient dialogflowpb.ContextsClient + + // The call options for this service. + CallOptions *ContextsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewContextsClient creates a new contexts client. +// +// A context represents additional information included with user input or with +// an intent returned by the Dialogflow API. Contexts are helpful for +// differentiating user input which may be vague or have a different meaning +// depending on additional details from your application such as user setting +// and preferences, previous user input, where the user is in your application, +// geographic location, and so on. +// +// You can include contexts as input parameters of a +// [DetectIntent][google.cloud.dialogflow.v2.Sessions.DetectIntent] (or +// [StreamingDetectIntent][google.cloud.dialogflow.v2.Sessions.StreamingDetectIntent]) request, +// or as output contexts included in the returned intent. +// Contexts expire when an intent is matched, after the number of DetectIntent +// requests specified by the lifespan_count parameter, or after 10 minutes +// if no intents are matched for a DetectIntent request. +// +// For more information about contexts, see the +// Dialogflow documentation (at https://dialogflow.com/docs/contexts). +func NewContextsClient(ctx context.Context, opts ...option.ClientOption) (*ContextsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultContextsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ContextsClient{ + conn: conn, + CallOptions: defaultContextsCallOptions(), + + contextsClient: dialogflowpb.NewContextsClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ContextsClient) 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 *ContextsClient) 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 *ContextsClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListContexts returns the list of all contexts in the specified session. +func (c *ContextsClient) ListContexts(ctx context.Context, req *dialogflowpb.ListContextsRequest, opts ...gax.CallOption) *ContextIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListContexts[0:len(c.CallOptions.ListContexts):len(c.CallOptions.ListContexts)], opts...) + it := &ContextIterator{} + req = proto.Clone(req).(*dialogflowpb.ListContextsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dialogflowpb.Context, string, error) { + var resp *dialogflowpb.ListContextsResponse + 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.contextsClient.ListContexts(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Contexts, 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 +} + +// GetContext retrieves the specified context. +func (c *ContextsClient) GetContext(ctx context.Context, req *dialogflowpb.GetContextRequest, opts ...gax.CallOption) (*dialogflowpb.Context, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetContext[0:len(c.CallOptions.GetContext):len(c.CallOptions.GetContext)], opts...) + var resp *dialogflowpb.Context + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.contextsClient.GetContext(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateContext creates a context. +func (c *ContextsClient) CreateContext(ctx context.Context, req *dialogflowpb.CreateContextRequest, opts ...gax.CallOption) (*dialogflowpb.Context, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateContext[0:len(c.CallOptions.CreateContext):len(c.CallOptions.CreateContext)], opts...) + var resp *dialogflowpb.Context + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.contextsClient.CreateContext(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateContext updates the specified context. +func (c *ContextsClient) UpdateContext(ctx context.Context, req *dialogflowpb.UpdateContextRequest, opts ...gax.CallOption) (*dialogflowpb.Context, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateContext[0:len(c.CallOptions.UpdateContext):len(c.CallOptions.UpdateContext)], opts...) + var resp *dialogflowpb.Context + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.contextsClient.UpdateContext(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteContext deletes the specified context. +func (c *ContextsClient) DeleteContext(ctx context.Context, req *dialogflowpb.DeleteContextRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteContext[0:len(c.CallOptions.DeleteContext):len(c.CallOptions.DeleteContext)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.contextsClient.DeleteContext(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// DeleteAllContexts deletes all active contexts in the specified session. +func (c *ContextsClient) DeleteAllContexts(ctx context.Context, req *dialogflowpb.DeleteAllContextsRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteAllContexts[0:len(c.CallOptions.DeleteAllContexts):len(c.CallOptions.DeleteAllContexts)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.contextsClient.DeleteAllContexts(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ContextIterator manages a stream of *dialogflowpb.Context. +type ContextIterator struct { + items []*dialogflowpb.Context + 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 []*dialogflowpb.Context, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *ContextIterator) 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 *ContextIterator) Next() (*dialogflowpb.Context, error) { + var item *dialogflowpb.Context + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *ContextIterator) bufLen() int { + return len(it.items) +} + +func (it *ContextIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/contexts_client_example_test.go b/vendor/cloud.google.com/go/dialogflow/apiv2/contexts_client_example_test.go new file mode 100644 index 0000000000..75adedb5d9 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/contexts_client_example_test.go @@ -0,0 +1,144 @@ +// 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 dialogflow_test + +import ( + "cloud.google.com/go/dialogflow/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" +) + +func ExampleNewContextsClient() { + ctx := context.Background() + c, err := dialogflow.NewContextsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleContextsClient_ListContexts() { + ctx := context.Background() + c, err := dialogflow.NewContextsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.ListContextsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListContexts(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleContextsClient_GetContext() { + ctx := context.Background() + c, err := dialogflow.NewContextsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.GetContextRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetContext(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleContextsClient_CreateContext() { + ctx := context.Background() + c, err := dialogflow.NewContextsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.CreateContextRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateContext(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleContextsClient_UpdateContext() { + ctx := context.Background() + c, err := dialogflow.NewContextsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.UpdateContextRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateContext(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleContextsClient_DeleteContext() { + ctx := context.Background() + c, err := dialogflow.NewContextsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.DeleteContextRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteContext(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleContextsClient_DeleteAllContexts() { + ctx := context.Background() + c, err := dialogflow.NewContextsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.DeleteAllContextsRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteAllContexts(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/doc.go b/vendor/cloud.google.com/go/dialogflow/apiv2/doc.go new file mode 100644 index 0000000000..0a2c071acf --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/doc.go @@ -0,0 +1,47 @@ +// 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 dialogflow is an auto-generated package for the +// Dialogflow API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// An end-to-end development suite for conversational interfaces (e.g., +// chatbots, voice-powered apps and devices). +package dialogflow // import "cloud.google.com/go/dialogflow/apiv2" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/entity_types_client.go b/vendor/cloud.google.com/go/dialogflow/apiv2/entity_types_client.go new file mode 100644 index 0000000000..f5342be855 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/entity_types_client.go @@ -0,0 +1,723 @@ +// 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 dialogflow + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/proto" + structpbpb "github.com/golang/protobuf/ptypes/struct" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// EntityTypesCallOptions contains the retry settings for each method of EntityTypesClient. +type EntityTypesCallOptions struct { + ListEntityTypes []gax.CallOption + GetEntityType []gax.CallOption + CreateEntityType []gax.CallOption + UpdateEntityType []gax.CallOption + DeleteEntityType []gax.CallOption + BatchUpdateEntityTypes []gax.CallOption + BatchDeleteEntityTypes []gax.CallOption + BatchCreateEntities []gax.CallOption + BatchUpdateEntities []gax.CallOption + BatchDeleteEntities []gax.CallOption +} + +func defaultEntityTypesClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dialogflow.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultEntityTypesCallOptions() *EntityTypesCallOptions { + 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 &EntityTypesCallOptions{ + ListEntityTypes: retry[[2]string{"default", "idempotent"}], + GetEntityType: retry[[2]string{"default", "idempotent"}], + CreateEntityType: retry[[2]string{"default", "non_idempotent"}], + UpdateEntityType: retry[[2]string{"default", "non_idempotent"}], + DeleteEntityType: retry[[2]string{"default", "idempotent"}], + BatchUpdateEntityTypes: retry[[2]string{"default", "non_idempotent"}], + BatchDeleteEntityTypes: retry[[2]string{"default", "idempotent"}], + BatchCreateEntities: retry[[2]string{"default", "non_idempotent"}], + BatchUpdateEntities: retry[[2]string{"default", "non_idempotent"}], + BatchDeleteEntities: retry[[2]string{"default", "idempotent"}], + } +} + +// EntityTypesClient is a client for interacting with Dialogflow API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type EntityTypesClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + entityTypesClient dialogflowpb.EntityTypesClient + + // 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 *EntityTypesCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewEntityTypesClient creates a new entity types client. +// +// Entities are extracted from user input and represent parameters that are +// meaningful to your application. For example, a date range, a proper name +// such as a geographic location or landmark, and so on. Entities represent +// actionable data for your application. +// +// When you define an entity, you can also include synonyms that all map to +// that entity. For example, "soft drink", "soda", "pop", and so on. +// +// There are three types of entities: +// +// * **System** - entities that are defined by the Dialogflow API for common +// data types such as date, time, currency, and so on. A system entity is +// represented by the `EntityType` type. +// +// * **Developer** - entities that are defined by you that represent +// actionable data that is meaningful to your application. For example, +// you could define a `pizza.sauce` entity for red or white pizza sauce, +// a `pizza.cheese` entity for the different types of cheese on a pizza, +// a `pizza.topping` entity for different toppings, and so on. A developer +// entity is represented by the `EntityType` type. +// +// * **User** - entities that are built for an individual user such as +// favorites, preferences, playlists, and so on. A user entity is +// represented by the [SessionEntityType][google.cloud.dialogflow.v2.SessionEntityType] type. +// +// For more information about entity types, see the +// [Dialogflow documentation](https://dialogflow.com/docs/entities). +func NewEntityTypesClient(ctx context.Context, opts ...option.ClientOption) (*EntityTypesClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultEntityTypesClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &EntityTypesClient{ + conn: conn, + CallOptions: defaultEntityTypesCallOptions(), + + entityTypesClient: dialogflowpb.NewEntityTypesClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *EntityTypesClient) 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 *EntityTypesClient) 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 *EntityTypesClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListEntityTypes returns the list of all entity types in the specified agent. +func (c *EntityTypesClient) ListEntityTypes(ctx context.Context, req *dialogflowpb.ListEntityTypesRequest, opts ...gax.CallOption) *EntityTypeIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListEntityTypes[0:len(c.CallOptions.ListEntityTypes):len(c.CallOptions.ListEntityTypes)], opts...) + it := &EntityTypeIterator{} + req = proto.Clone(req).(*dialogflowpb.ListEntityTypesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dialogflowpb.EntityType, string, error) { + var resp *dialogflowpb.ListEntityTypesResponse + 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.entityTypesClient.ListEntityTypes(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.EntityTypes, 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 +} + +// GetEntityType retrieves the specified entity type. +func (c *EntityTypesClient) GetEntityType(ctx context.Context, req *dialogflowpb.GetEntityTypeRequest, opts ...gax.CallOption) (*dialogflowpb.EntityType, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetEntityType[0:len(c.CallOptions.GetEntityType):len(c.CallOptions.GetEntityType)], opts...) + var resp *dialogflowpb.EntityType + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.GetEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateEntityType creates an entity type in the specified agent. +func (c *EntityTypesClient) CreateEntityType(ctx context.Context, req *dialogflowpb.CreateEntityTypeRequest, opts ...gax.CallOption) (*dialogflowpb.EntityType, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateEntityType[0:len(c.CallOptions.CreateEntityType):len(c.CallOptions.CreateEntityType)], opts...) + var resp *dialogflowpb.EntityType + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.CreateEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateEntityType updates the specified entity type. +func (c *EntityTypesClient) UpdateEntityType(ctx context.Context, req *dialogflowpb.UpdateEntityTypeRequest, opts ...gax.CallOption) (*dialogflowpb.EntityType, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateEntityType[0:len(c.CallOptions.UpdateEntityType):len(c.CallOptions.UpdateEntityType)], opts...) + var resp *dialogflowpb.EntityType + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.UpdateEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteEntityType deletes the specified entity type. +func (c *EntityTypesClient) DeleteEntityType(ctx context.Context, req *dialogflowpb.DeleteEntityTypeRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteEntityType[0:len(c.CallOptions.DeleteEntityType):len(c.CallOptions.DeleteEntityType)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.entityTypesClient.DeleteEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// BatchUpdateEntityTypes updates/Creates multiple entity types in the specified agent. +// +// Operation +func (c *EntityTypesClient) BatchUpdateEntityTypes(ctx context.Context, req *dialogflowpb.BatchUpdateEntityTypesRequest, opts ...gax.CallOption) (*BatchUpdateEntityTypesOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchUpdateEntityTypes[0:len(c.CallOptions.BatchUpdateEntityTypes):len(c.CallOptions.BatchUpdateEntityTypes)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.BatchUpdateEntityTypes(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &BatchUpdateEntityTypesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// BatchDeleteEntityTypes deletes entity types in the specified agent. +// +// Operation +func (c *EntityTypesClient) BatchDeleteEntityTypes(ctx context.Context, req *dialogflowpb.BatchDeleteEntityTypesRequest, opts ...gax.CallOption) (*BatchDeleteEntityTypesOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchDeleteEntityTypes[0:len(c.CallOptions.BatchDeleteEntityTypes):len(c.CallOptions.BatchDeleteEntityTypes)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.BatchDeleteEntityTypes(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &BatchDeleteEntityTypesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// BatchCreateEntities creates multiple new entities in the specified entity type (extends the +// existing collection of entries). +// +// Operation +func (c *EntityTypesClient) BatchCreateEntities(ctx context.Context, req *dialogflowpb.BatchCreateEntitiesRequest, opts ...gax.CallOption) (*BatchCreateEntitiesOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchCreateEntities[0:len(c.CallOptions.BatchCreateEntities):len(c.CallOptions.BatchCreateEntities)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.BatchCreateEntities(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &BatchCreateEntitiesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// BatchUpdateEntities updates entities in the specified entity type (replaces the existing +// collection of entries). +// +// Operation +func (c *EntityTypesClient) BatchUpdateEntities(ctx context.Context, req *dialogflowpb.BatchUpdateEntitiesRequest, opts ...gax.CallOption) (*BatchUpdateEntitiesOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchUpdateEntities[0:len(c.CallOptions.BatchUpdateEntities):len(c.CallOptions.BatchUpdateEntities)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.BatchUpdateEntities(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &BatchUpdateEntitiesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// BatchDeleteEntities deletes entities in the specified entity type. +// +// Operation +func (c *EntityTypesClient) BatchDeleteEntities(ctx context.Context, req *dialogflowpb.BatchDeleteEntitiesRequest, opts ...gax.CallOption) (*BatchDeleteEntitiesOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchDeleteEntities[0:len(c.CallOptions.BatchDeleteEntities):len(c.CallOptions.BatchDeleteEntities)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.entityTypesClient.BatchDeleteEntities(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &BatchDeleteEntitiesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// EntityTypeIterator manages a stream of *dialogflowpb.EntityType. +type EntityTypeIterator struct { + items []*dialogflowpb.EntityType + 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 []*dialogflowpb.EntityType, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *EntityTypeIterator) 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 *EntityTypeIterator) Next() (*dialogflowpb.EntityType, error) { + var item *dialogflowpb.EntityType + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *EntityTypeIterator) bufLen() int { + return len(it.items) +} + +func (it *EntityTypeIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// BatchCreateEntitiesOperation manages a long-running operation from BatchCreateEntities. +type BatchCreateEntitiesOperation struct { + lro *longrunning.Operation +} + +// BatchCreateEntitiesOperation returns a new BatchCreateEntitiesOperation from a given name. +// The name must be that of a previously created BatchCreateEntitiesOperation, possibly from a different process. +func (c *EntityTypesClient) BatchCreateEntitiesOperation(name string) *BatchCreateEntitiesOperation { + return &BatchCreateEntitiesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *BatchCreateEntitiesOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *BatchCreateEntitiesOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *BatchCreateEntitiesOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *BatchCreateEntitiesOperation) 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 *BatchCreateEntitiesOperation) Name() string { + return op.lro.Name() +} + +// BatchDeleteEntitiesOperation manages a long-running operation from BatchDeleteEntities. +type BatchDeleteEntitiesOperation struct { + lro *longrunning.Operation +} + +// BatchDeleteEntitiesOperation returns a new BatchDeleteEntitiesOperation from a given name. +// The name must be that of a previously created BatchDeleteEntitiesOperation, possibly from a different process. +func (c *EntityTypesClient) BatchDeleteEntitiesOperation(name string) *BatchDeleteEntitiesOperation { + return &BatchDeleteEntitiesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *BatchDeleteEntitiesOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *BatchDeleteEntitiesOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *BatchDeleteEntitiesOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *BatchDeleteEntitiesOperation) 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 *BatchDeleteEntitiesOperation) Name() string { + return op.lro.Name() +} + +// BatchDeleteEntityTypesOperation manages a long-running operation from BatchDeleteEntityTypes. +type BatchDeleteEntityTypesOperation struct { + lro *longrunning.Operation +} + +// BatchDeleteEntityTypesOperation returns a new BatchDeleteEntityTypesOperation from a given name. +// The name must be that of a previously created BatchDeleteEntityTypesOperation, possibly from a different process. +func (c *EntityTypesClient) BatchDeleteEntityTypesOperation(name string) *BatchDeleteEntityTypesOperation { + return &BatchDeleteEntityTypesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *BatchDeleteEntityTypesOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *BatchDeleteEntityTypesOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *BatchDeleteEntityTypesOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *BatchDeleteEntityTypesOperation) 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 *BatchDeleteEntityTypesOperation) Name() string { + return op.lro.Name() +} + +// BatchUpdateEntitiesOperation manages a long-running operation from BatchUpdateEntities. +type BatchUpdateEntitiesOperation struct { + lro *longrunning.Operation +} + +// BatchUpdateEntitiesOperation returns a new BatchUpdateEntitiesOperation from a given name. +// The name must be that of a previously created BatchUpdateEntitiesOperation, possibly from a different process. +func (c *EntityTypesClient) BatchUpdateEntitiesOperation(name string) *BatchUpdateEntitiesOperation { + return &BatchUpdateEntitiesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *BatchUpdateEntitiesOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *BatchUpdateEntitiesOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *BatchUpdateEntitiesOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *BatchUpdateEntitiesOperation) 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 *BatchUpdateEntitiesOperation) Name() string { + return op.lro.Name() +} + +// BatchUpdateEntityTypesOperation manages a long-running operation from BatchUpdateEntityTypes. +type BatchUpdateEntityTypesOperation struct { + lro *longrunning.Operation +} + +// BatchUpdateEntityTypesOperation returns a new BatchUpdateEntityTypesOperation from a given name. +// The name must be that of a previously created BatchUpdateEntityTypesOperation, possibly from a different process. +func (c *EntityTypesClient) BatchUpdateEntityTypesOperation(name string) *BatchUpdateEntityTypesOperation { + return &BatchUpdateEntityTypesOperation{ + 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 *BatchUpdateEntityTypesOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*dialogflowpb.BatchUpdateEntityTypesResponse, error) { + var resp dialogflowpb.BatchUpdateEntityTypesResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 5000*time.Millisecond, 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 *BatchUpdateEntityTypesOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*dialogflowpb.BatchUpdateEntityTypesResponse, error) { + var resp dialogflowpb.BatchUpdateEntityTypesResponse + 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 *BatchUpdateEntityTypesOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *BatchUpdateEntityTypesOperation) 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 *BatchUpdateEntityTypesOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/entity_types_client_example_test.go b/vendor/cloud.google.com/go/dialogflow/apiv2/entity_types_client_example_test.go new file mode 100644 index 0000000000..e5580d9ce2 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/entity_types_client_example_test.go @@ -0,0 +1,227 @@ +// 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 dialogflow_test + +import ( + "cloud.google.com/go/dialogflow/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" +) + +func ExampleNewEntityTypesClient() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleEntityTypesClient_ListEntityTypes() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.ListEntityTypesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListEntityTypes(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleEntityTypesClient_GetEntityType() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.GetEntityTypeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleEntityTypesClient_CreateEntityType() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.CreateEntityTypeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleEntityTypesClient_UpdateEntityType() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.UpdateEntityTypeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleEntityTypesClient_DeleteEntityType() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.DeleteEntityTypeRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleEntityTypesClient_BatchUpdateEntityTypes() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.BatchUpdateEntityTypesRequest{ + // TODO: Fill request struct fields. + } + op, err := c.BatchUpdateEntityTypes(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleEntityTypesClient_BatchDeleteEntityTypes() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.BatchDeleteEntityTypesRequest{ + // TODO: Fill request struct fields. + } + op, err := c.BatchDeleteEntityTypes(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} + +func ExampleEntityTypesClient_BatchCreateEntities() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.BatchCreateEntitiesRequest{ + // TODO: Fill request struct fields. + } + op, err := c.BatchCreateEntities(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} + +func ExampleEntityTypesClient_BatchUpdateEntities() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.BatchUpdateEntitiesRequest{ + // TODO: Fill request struct fields. + } + op, err := c.BatchUpdateEntities(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} + +func ExampleEntityTypesClient_BatchDeleteEntities() { + ctx := context.Background() + c, err := dialogflow.NewEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.BatchDeleteEntitiesRequest{ + // TODO: Fill request struct fields. + } + op, err := c.BatchDeleteEntities(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/intents_client.go b/vendor/cloud.google.com/go/dialogflow/apiv2/intents_client.go new file mode 100644 index 0000000000..d0b7f16a4c --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/intents_client.go @@ -0,0 +1,487 @@ +// 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 dialogflow + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/proto" + structpbpb "github.com/golang/protobuf/ptypes/struct" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// IntentsCallOptions contains the retry settings for each method of IntentsClient. +type IntentsCallOptions struct { + ListIntents []gax.CallOption + GetIntent []gax.CallOption + CreateIntent []gax.CallOption + UpdateIntent []gax.CallOption + DeleteIntent []gax.CallOption + BatchUpdateIntents []gax.CallOption + BatchDeleteIntents []gax.CallOption +} + +func defaultIntentsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dialogflow.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultIntentsCallOptions() *IntentsCallOptions { + 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 &IntentsCallOptions{ + ListIntents: retry[[2]string{"default", "idempotent"}], + GetIntent: retry[[2]string{"default", "idempotent"}], + CreateIntent: retry[[2]string{"default", "non_idempotent"}], + UpdateIntent: retry[[2]string{"default", "non_idempotent"}], + DeleteIntent: retry[[2]string{"default", "idempotent"}], + BatchUpdateIntents: retry[[2]string{"default", "non_idempotent"}], + BatchDeleteIntents: retry[[2]string{"default", "idempotent"}], + } +} + +// IntentsClient is a client for interacting with Dialogflow API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type IntentsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + intentsClient dialogflowpb.IntentsClient + + // 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 *IntentsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewIntentsClient creates a new intents client. +// +// An intent represents a mapping between input from a user and an action to +// be taken by your application. When you pass user input to the +// [DetectIntent][google.cloud.dialogflow.v2.Sessions.DetectIntent] (or +// [StreamingDetectIntent][google.cloud.dialogflow.v2.Sessions.StreamingDetectIntent]) method, the +// Dialogflow API analyzes the input and searches +// for a matching intent. If no match is found, the Dialogflow API returns a +// fallback intent (`is_fallback` = true). +// +// You can provide additional information for the Dialogflow API to use to +// match user input to an intent by adding the following to your intent. +// +// * **Contexts** - provide additional context for intent analysis. For +// example, if an intent is related to an object in your application that +// plays music, you can provide a context to determine when to match the +// intent if the user input is “turn it off”. You can include a context +// that matches the intent when there is previous user input of +// "play music", and not when there is previous user input of +// "turn on the light". +// +// * **Events** - allow for matching an intent by using an event name +// instead of user input. Your application can provide an event name and +// related parameters to the Dialogflow API to match an intent. For +// example, when your application starts, you can send a welcome event +// with a user name parameter to the Dialogflow API to match an intent with +// a personalized welcome message for the user. +// +// * **Training phrases** - provide examples of user input to train the +// Dialogflow API agent to better match intents. +// +// For more information about intents, see the +// [Dialogflow documentation](https://dialogflow.com/docs/intents). +func NewIntentsClient(ctx context.Context, opts ...option.ClientOption) (*IntentsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultIntentsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &IntentsClient{ + conn: conn, + CallOptions: defaultIntentsCallOptions(), + + intentsClient: dialogflowpb.NewIntentsClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *IntentsClient) 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 *IntentsClient) 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 *IntentsClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListIntents returns the list of all intents in the specified agent. +func (c *IntentsClient) ListIntents(ctx context.Context, req *dialogflowpb.ListIntentsRequest, opts ...gax.CallOption) *IntentIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListIntents[0:len(c.CallOptions.ListIntents):len(c.CallOptions.ListIntents)], opts...) + it := &IntentIterator{} + req = proto.Clone(req).(*dialogflowpb.ListIntentsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dialogflowpb.Intent, string, error) { + var resp *dialogflowpb.ListIntentsResponse + 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.intentsClient.ListIntents(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Intents, 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 +} + +// GetIntent retrieves the specified intent. +func (c *IntentsClient) GetIntent(ctx context.Context, req *dialogflowpb.GetIntentRequest, opts ...gax.CallOption) (*dialogflowpb.Intent, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetIntent[0:len(c.CallOptions.GetIntent):len(c.CallOptions.GetIntent)], opts...) + var resp *dialogflowpb.Intent + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.intentsClient.GetIntent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateIntent creates an intent in the specified agent. +func (c *IntentsClient) CreateIntent(ctx context.Context, req *dialogflowpb.CreateIntentRequest, opts ...gax.CallOption) (*dialogflowpb.Intent, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateIntent[0:len(c.CallOptions.CreateIntent):len(c.CallOptions.CreateIntent)], opts...) + var resp *dialogflowpb.Intent + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.intentsClient.CreateIntent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateIntent updates the specified intent. +func (c *IntentsClient) UpdateIntent(ctx context.Context, req *dialogflowpb.UpdateIntentRequest, opts ...gax.CallOption) (*dialogflowpb.Intent, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateIntent[0:len(c.CallOptions.UpdateIntent):len(c.CallOptions.UpdateIntent)], opts...) + var resp *dialogflowpb.Intent + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.intentsClient.UpdateIntent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteIntent deletes the specified intent. +func (c *IntentsClient) DeleteIntent(ctx context.Context, req *dialogflowpb.DeleteIntentRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteIntent[0:len(c.CallOptions.DeleteIntent):len(c.CallOptions.DeleteIntent)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.intentsClient.DeleteIntent(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// BatchUpdateIntents updates/Creates multiple intents in the specified agent. +// +// Operation +func (c *IntentsClient) BatchUpdateIntents(ctx context.Context, req *dialogflowpb.BatchUpdateIntentsRequest, opts ...gax.CallOption) (*BatchUpdateIntentsOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchUpdateIntents[0:len(c.CallOptions.BatchUpdateIntents):len(c.CallOptions.BatchUpdateIntents)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.intentsClient.BatchUpdateIntents(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &BatchUpdateIntentsOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// BatchDeleteIntents deletes intents in the specified agent. +// +// Operation +func (c *IntentsClient) BatchDeleteIntents(ctx context.Context, req *dialogflowpb.BatchDeleteIntentsRequest, opts ...gax.CallOption) (*BatchDeleteIntentsOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchDeleteIntents[0:len(c.CallOptions.BatchDeleteIntents):len(c.CallOptions.BatchDeleteIntents)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.intentsClient.BatchDeleteIntents(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &BatchDeleteIntentsOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// IntentIterator manages a stream of *dialogflowpb.Intent. +type IntentIterator struct { + items []*dialogflowpb.Intent + 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 []*dialogflowpb.Intent, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *IntentIterator) 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 *IntentIterator) Next() (*dialogflowpb.Intent, error) { + var item *dialogflowpb.Intent + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *IntentIterator) bufLen() int { + return len(it.items) +} + +func (it *IntentIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// BatchDeleteIntentsOperation manages a long-running operation from BatchDeleteIntents. +type BatchDeleteIntentsOperation struct { + lro *longrunning.Operation +} + +// BatchDeleteIntentsOperation returns a new BatchDeleteIntentsOperation from a given name. +// The name must be that of a previously created BatchDeleteIntentsOperation, possibly from a different process. +func (c *IntentsClient) BatchDeleteIntentsOperation(name string) *BatchDeleteIntentsOperation { + return &BatchDeleteIntentsOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *BatchDeleteIntentsOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 5000*time.Millisecond, opts...) +} + +// 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. +func (op *BatchDeleteIntentsOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *BatchDeleteIntentsOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *BatchDeleteIntentsOperation) 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 *BatchDeleteIntentsOperation) Name() string { + return op.lro.Name() +} + +// BatchUpdateIntentsOperation manages a long-running operation from BatchUpdateIntents. +type BatchUpdateIntentsOperation struct { + lro *longrunning.Operation +} + +// BatchUpdateIntentsOperation returns a new BatchUpdateIntentsOperation from a given name. +// The name must be that of a previously created BatchUpdateIntentsOperation, possibly from a different process. +func (c *IntentsClient) BatchUpdateIntentsOperation(name string) *BatchUpdateIntentsOperation { + return &BatchUpdateIntentsOperation{ + 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 *BatchUpdateIntentsOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*dialogflowpb.BatchUpdateIntentsResponse, error) { + var resp dialogflowpb.BatchUpdateIntentsResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 5000*time.Millisecond, 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 *BatchUpdateIntentsOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*dialogflowpb.BatchUpdateIntentsResponse, error) { + var resp dialogflowpb.BatchUpdateIntentsResponse + 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 *BatchUpdateIntentsOperation) Metadata() (*structpbpb.Struct, error) { + var meta structpbpb.Struct + 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 *BatchUpdateIntentsOperation) 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 *BatchUpdateIntentsOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/intents_client_example_test.go b/vendor/cloud.google.com/go/dialogflow/apiv2/intents_client_example_test.go new file mode 100644 index 0000000000..ba5eae6bd3 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/intents_client_example_test.go @@ -0,0 +1,170 @@ +// 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 dialogflow_test + +import ( + "cloud.google.com/go/dialogflow/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" +) + +func ExampleNewIntentsClient() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleIntentsClient_ListIntents() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.ListIntentsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListIntents(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleIntentsClient_GetIntent() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.GetIntentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetIntent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIntentsClient_CreateIntent() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.CreateIntentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateIntent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIntentsClient_UpdateIntent() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.UpdateIntentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateIntent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIntentsClient_DeleteIntent() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.DeleteIntentRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteIntent(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleIntentsClient_BatchUpdateIntents() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.BatchUpdateIntentsRequest{ + // TODO: Fill request struct fields. + } + op, err := c.BatchUpdateIntents(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIntentsClient_BatchDeleteIntents() { + ctx := context.Background() + c, err := dialogflow.NewIntentsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.BatchDeleteIntentsRequest{ + // TODO: Fill request struct fields. + } + op, err := c.BatchDeleteIntents(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/mock_test.go b/vendor/cloud.google.com/go/dialogflow/apiv2/mock_test.go new file mode 100644 index 0000000000..aa7fba2116 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/mock_test.go @@ -0,0 +1,3149 @@ +// 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 dialogflow + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockAgentsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dialogflowpb.AgentsServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockAgentsServer) GetAgent(ctx context.Context, req *dialogflowpb.GetAgentRequest) (*dialogflowpb.Agent, 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].(*dialogflowpb.Agent), nil +} + +func (s *mockAgentsServer) SearchAgents(ctx context.Context, req *dialogflowpb.SearchAgentsRequest) (*dialogflowpb.SearchAgentsResponse, 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].(*dialogflowpb.SearchAgentsResponse), nil +} + +func (s *mockAgentsServer) TrainAgent(ctx context.Context, req *dialogflowpb.TrainAgentRequest) (*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 *mockAgentsServer) ExportAgent(ctx context.Context, req *dialogflowpb.ExportAgentRequest) (*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 *mockAgentsServer) ImportAgent(ctx context.Context, req *dialogflowpb.ImportAgentRequest) (*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 *mockAgentsServer) RestoreAgent(ctx context.Context, req *dialogflowpb.RestoreAgentRequest) (*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 +} + +type mockContextsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dialogflowpb.ContextsServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockContextsServer) ListContexts(ctx context.Context, req *dialogflowpb.ListContextsRequest) (*dialogflowpb.ListContextsResponse, 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].(*dialogflowpb.ListContextsResponse), nil +} + +func (s *mockContextsServer) GetContext(ctx context.Context, req *dialogflowpb.GetContextRequest) (*dialogflowpb.Context, 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].(*dialogflowpb.Context), nil +} + +func (s *mockContextsServer) CreateContext(ctx context.Context, req *dialogflowpb.CreateContextRequest) (*dialogflowpb.Context, 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].(*dialogflowpb.Context), nil +} + +func (s *mockContextsServer) UpdateContext(ctx context.Context, req *dialogflowpb.UpdateContextRequest) (*dialogflowpb.Context, 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].(*dialogflowpb.Context), nil +} + +func (s *mockContextsServer) DeleteContext(ctx context.Context, req *dialogflowpb.DeleteContextRequest) (*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 *mockContextsServer) DeleteAllContexts(ctx context.Context, req *dialogflowpb.DeleteAllContextsRequest) (*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 +} + +type mockEntityTypesServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dialogflowpb.EntityTypesServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockEntityTypesServer) ListEntityTypes(ctx context.Context, req *dialogflowpb.ListEntityTypesRequest) (*dialogflowpb.ListEntityTypesResponse, 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].(*dialogflowpb.ListEntityTypesResponse), nil +} + +func (s *mockEntityTypesServer) GetEntityType(ctx context.Context, req *dialogflowpb.GetEntityTypeRequest) (*dialogflowpb.EntityType, 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].(*dialogflowpb.EntityType), nil +} + +func (s *mockEntityTypesServer) CreateEntityType(ctx context.Context, req *dialogflowpb.CreateEntityTypeRequest) (*dialogflowpb.EntityType, 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].(*dialogflowpb.EntityType), nil +} + +func (s *mockEntityTypesServer) UpdateEntityType(ctx context.Context, req *dialogflowpb.UpdateEntityTypeRequest) (*dialogflowpb.EntityType, 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].(*dialogflowpb.EntityType), nil +} + +func (s *mockEntityTypesServer) DeleteEntityType(ctx context.Context, req *dialogflowpb.DeleteEntityTypeRequest) (*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 *mockEntityTypesServer) BatchUpdateEntityTypes(ctx context.Context, req *dialogflowpb.BatchUpdateEntityTypesRequest) (*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 *mockEntityTypesServer) BatchDeleteEntityTypes(ctx context.Context, req *dialogflowpb.BatchDeleteEntityTypesRequest) (*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 *mockEntityTypesServer) BatchCreateEntities(ctx context.Context, req *dialogflowpb.BatchCreateEntitiesRequest) (*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 *mockEntityTypesServer) BatchUpdateEntities(ctx context.Context, req *dialogflowpb.BatchUpdateEntitiesRequest) (*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 *mockEntityTypesServer) BatchDeleteEntities(ctx context.Context, req *dialogflowpb.BatchDeleteEntitiesRequest) (*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 +} + +type mockIntentsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dialogflowpb.IntentsServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockIntentsServer) ListIntents(ctx context.Context, req *dialogflowpb.ListIntentsRequest) (*dialogflowpb.ListIntentsResponse, 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].(*dialogflowpb.ListIntentsResponse), nil +} + +func (s *mockIntentsServer) GetIntent(ctx context.Context, req *dialogflowpb.GetIntentRequest) (*dialogflowpb.Intent, 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].(*dialogflowpb.Intent), nil +} + +func (s *mockIntentsServer) CreateIntent(ctx context.Context, req *dialogflowpb.CreateIntentRequest) (*dialogflowpb.Intent, 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].(*dialogflowpb.Intent), nil +} + +func (s *mockIntentsServer) UpdateIntent(ctx context.Context, req *dialogflowpb.UpdateIntentRequest) (*dialogflowpb.Intent, 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].(*dialogflowpb.Intent), nil +} + +func (s *mockIntentsServer) DeleteIntent(ctx context.Context, req *dialogflowpb.DeleteIntentRequest) (*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 *mockIntentsServer) BatchUpdateIntents(ctx context.Context, req *dialogflowpb.BatchUpdateIntentsRequest) (*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 *mockIntentsServer) BatchDeleteIntents(ctx context.Context, req *dialogflowpb.BatchDeleteIntentsRequest) (*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 +} + +type mockSessionEntityTypesServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dialogflowpb.SessionEntityTypesServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockSessionEntityTypesServer) ListSessionEntityTypes(ctx context.Context, req *dialogflowpb.ListSessionEntityTypesRequest) (*dialogflowpb.ListSessionEntityTypesResponse, 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].(*dialogflowpb.ListSessionEntityTypesResponse), nil +} + +func (s *mockSessionEntityTypesServer) GetSessionEntityType(ctx context.Context, req *dialogflowpb.GetSessionEntityTypeRequest) (*dialogflowpb.SessionEntityType, 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].(*dialogflowpb.SessionEntityType), nil +} + +func (s *mockSessionEntityTypesServer) CreateSessionEntityType(ctx context.Context, req *dialogflowpb.CreateSessionEntityTypeRequest) (*dialogflowpb.SessionEntityType, 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].(*dialogflowpb.SessionEntityType), nil +} + +func (s *mockSessionEntityTypesServer) UpdateSessionEntityType(ctx context.Context, req *dialogflowpb.UpdateSessionEntityTypeRequest) (*dialogflowpb.SessionEntityType, 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].(*dialogflowpb.SessionEntityType), nil +} + +func (s *mockSessionEntityTypesServer) DeleteSessionEntityType(ctx context.Context, req *dialogflowpb.DeleteSessionEntityTypeRequest) (*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 +} + +type mockSessionsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dialogflowpb.SessionsServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockSessionsServer) DetectIntent(ctx context.Context, req *dialogflowpb.DetectIntentRequest) (*dialogflowpb.DetectIntentResponse, 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].(*dialogflowpb.DetectIntentResponse), nil +} + +func (s *mockSessionsServer) StreamingDetectIntent(stream dialogflowpb.Sessions_StreamingDetectIntentServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + for { + if req, err := stream.Recv(); err == io.EOF { + break + } else if err != nil { + return err + } else { + s.reqs = append(s.reqs, req) + } + } + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*dialogflowpb.StreamingDetectIntentResponse)); err != nil { + return err + } + } + return nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockAgents mockAgentsServer + mockContexts mockContextsServer + mockEntityTypes mockEntityTypesServer + mockIntents mockIntentsServer + mockSessionEntityTypes mockSessionEntityTypesServer + mockSessions mockSessionsServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + dialogflowpb.RegisterAgentsServer(serv, &mockAgents) + dialogflowpb.RegisterContextsServer(serv, &mockContexts) + dialogflowpb.RegisterEntityTypesServer(serv, &mockEntityTypes) + dialogflowpb.RegisterIntentsServer(serv, &mockIntents) + dialogflowpb.RegisterSessionEntityTypesServer(serv, &mockSessionEntityTypes) + dialogflowpb.RegisterSessionsServer(serv, &mockSessions) + + 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 TestAgentsGetAgent(t *testing.T) { + var parent2 string = "parent21175163357" + var displayName string = "displayName1615086568" + var defaultLanguageCode string = "defaultLanguageCode856575222" + var timeZone string = "timeZone36848094" + var description string = "description-1724546052" + var avatarUri string = "avatarUri-402824826" + var enableLogging bool = false + var classificationThreshold float32 = 1.11581064E8 + var expectedResponse = &dialogflowpb.Agent{ + Parent: parent2, + DisplayName: displayName, + DefaultLanguageCode: defaultLanguageCode, + TimeZone: timeZone, + Description: description, + AvatarUri: avatarUri, + EnableLogging: enableLogging, + ClassificationThreshold: classificationThreshold, + } + + mockAgents.err = nil + mockAgents.reqs = nil + + mockAgents.resps = append(mockAgents.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.GetAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetAgent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAgents.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 TestAgentsGetAgentError(t *testing.T) { + errCode := codes.PermissionDenied + mockAgents.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.GetAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetAgent(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 TestAgentsSearchAgents(t *testing.T) { + var nextPageToken string = "" + var agentsElement *dialogflowpb.Agent = &dialogflowpb.Agent{} + var agents = []*dialogflowpb.Agent{agentsElement} + var expectedResponse = &dialogflowpb.SearchAgentsResponse{ + NextPageToken: nextPageToken, + Agents: agents, + } + + mockAgents.err = nil + mockAgents.reqs = nil + + mockAgents.resps = append(mockAgents.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.SearchAgentsRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SearchAgents(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAgents.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Agents[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 TestAgentsSearchAgentsError(t *testing.T) { + errCode := codes.PermissionDenied + mockAgents.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.SearchAgentsRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SearchAgents(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 TestAgentsTrainAgent(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockAgents.err = nil + mockAgents.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockAgents.resps = append(mockAgents.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.TrainAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.TrainAgent(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAgents.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestAgentsTrainAgentError(t *testing.T) { + errCode := codes.PermissionDenied + mockAgents.err = nil + mockAgents.resps = append(mockAgents.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 request = &dialogflowpb.TrainAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.TrainAgent(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestAgentsExportAgent(t *testing.T) { + var agentUri string = "agentUri-1700713166" + var expectedResponse = &dialogflowpb.ExportAgentResponse{ + Agent: &dialogflowpb.ExportAgentResponse_AgentUri{ + AgentUri: agentUri, + }, + } + + mockAgents.err = nil + mockAgents.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockAgents.resps = append(mockAgents.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.ExportAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.ExportAgent(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, mockAgents.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 TestAgentsExportAgentError(t *testing.T) { + errCode := codes.PermissionDenied + mockAgents.err = nil + mockAgents.resps = append(mockAgents.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 request = &dialogflowpb.ExportAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.ExportAgent(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 TestAgentsImportAgent(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockAgents.err = nil + mockAgents.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockAgents.resps = append(mockAgents.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.ImportAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.ImportAgent(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAgents.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestAgentsImportAgentError(t *testing.T) { + errCode := codes.PermissionDenied + mockAgents.err = nil + mockAgents.resps = append(mockAgents.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 request = &dialogflowpb.ImportAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.ImportAgent(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestAgentsRestoreAgent(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockAgents.err = nil + mockAgents.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockAgents.resps = append(mockAgents.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dialogflowpb.RestoreAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.RestoreAgent(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAgents.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestAgentsRestoreAgentError(t *testing.T) { + errCode := codes.PermissionDenied + mockAgents.err = nil + mockAgents.resps = append(mockAgents.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 request = &dialogflowpb.RestoreAgentRequest{ + Parent: formattedParent, + } + + c, err := NewAgentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.RestoreAgent(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestContextsListContexts(t *testing.T) { + var nextPageToken string = "" + var contextsElement *dialogflowpb.Context = &dialogflowpb.Context{} + var contexts = []*dialogflowpb.Context{contextsElement} + var expectedResponse = &dialogflowpb.ListContextsResponse{ + NextPageToken: nextPageToken, + Contexts: contexts, + } + + mockContexts.err = nil + mockContexts.reqs = nil + + mockContexts.resps = append(mockContexts.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var request = &dialogflowpb.ListContextsRequest{ + Parent: formattedParent, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListContexts(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockContexts.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Contexts[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 TestContextsListContextsError(t *testing.T) { + errCode := codes.PermissionDenied + mockContexts.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var request = &dialogflowpb.ListContextsRequest{ + Parent: formattedParent, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListContexts(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 TestContextsGetContext(t *testing.T) { + var name2 string = "name2-1052831874" + var lifespanCount int32 = 1178775510 + var expectedResponse = &dialogflowpb.Context{ + Name: name2, + LifespanCount: lifespanCount, + } + + mockContexts.err = nil + mockContexts.reqs = nil + + mockContexts.resps = append(mockContexts.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/contexts/%s", "[PROJECT]", "[SESSION]", "[CONTEXT]") + var request = &dialogflowpb.GetContextRequest{ + Name: formattedName, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetContext(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockContexts.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 TestContextsGetContextError(t *testing.T) { + errCode := codes.PermissionDenied + mockContexts.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/contexts/%s", "[PROJECT]", "[SESSION]", "[CONTEXT]") + var request = &dialogflowpb.GetContextRequest{ + Name: formattedName, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetContext(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 TestContextsCreateContext(t *testing.T) { + var name string = "name3373707" + var lifespanCount int32 = 1178775510 + var expectedResponse = &dialogflowpb.Context{ + Name: name, + LifespanCount: lifespanCount, + } + + mockContexts.err = nil + mockContexts.reqs = nil + + mockContexts.resps = append(mockContexts.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var context_ *dialogflowpb.Context = &dialogflowpb.Context{} + var request = &dialogflowpb.CreateContextRequest{ + Parent: formattedParent, + Context: context_, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateContext(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockContexts.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 TestContextsCreateContextError(t *testing.T) { + errCode := codes.PermissionDenied + mockContexts.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var context_ *dialogflowpb.Context = &dialogflowpb.Context{} + var request = &dialogflowpb.CreateContextRequest{ + Parent: formattedParent, + Context: context_, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateContext(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 TestContextsUpdateContext(t *testing.T) { + var name string = "name3373707" + var lifespanCount int32 = 1178775510 + var expectedResponse = &dialogflowpb.Context{ + Name: name, + LifespanCount: lifespanCount, + } + + mockContexts.err = nil + mockContexts.reqs = nil + + mockContexts.resps = append(mockContexts.resps[:0], expectedResponse) + + var context_ *dialogflowpb.Context = &dialogflowpb.Context{} + var request = &dialogflowpb.UpdateContextRequest{ + Context: context_, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateContext(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockContexts.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 TestContextsUpdateContextError(t *testing.T) { + errCode := codes.PermissionDenied + mockContexts.err = gstatus.Error(errCode, "test error") + + var context_ *dialogflowpb.Context = &dialogflowpb.Context{} + var request = &dialogflowpb.UpdateContextRequest{ + Context: context_, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateContext(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 TestContextsDeleteContext(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockContexts.err = nil + mockContexts.reqs = nil + + mockContexts.resps = append(mockContexts.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/contexts/%s", "[PROJECT]", "[SESSION]", "[CONTEXT]") + var request = &dialogflowpb.DeleteContextRequest{ + Name: formattedName, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteContext(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockContexts.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestContextsDeleteContextError(t *testing.T) { + errCode := codes.PermissionDenied + mockContexts.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/contexts/%s", "[PROJECT]", "[SESSION]", "[CONTEXT]") + var request = &dialogflowpb.DeleteContextRequest{ + Name: formattedName, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteContext(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 TestContextsDeleteAllContexts(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockContexts.err = nil + mockContexts.reqs = nil + + mockContexts.resps = append(mockContexts.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var request = &dialogflowpb.DeleteAllContextsRequest{ + Parent: formattedParent, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteAllContexts(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockContexts.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestContextsDeleteAllContextsError(t *testing.T) { + errCode := codes.PermissionDenied + mockContexts.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var request = &dialogflowpb.DeleteAllContextsRequest{ + Parent: formattedParent, + } + + c, err := NewContextsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteAllContexts(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 TestEntityTypesListEntityTypes(t *testing.T) { + var nextPageToken string = "" + var entityTypesElement *dialogflowpb.EntityType = &dialogflowpb.EntityType{} + var entityTypes = []*dialogflowpb.EntityType{entityTypesElement} + var expectedResponse = &dialogflowpb.ListEntityTypesResponse{ + NextPageToken: nextPageToken, + EntityTypes: entityTypes, + } + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var request = &dialogflowpb.ListEntityTypesRequest{ + Parent: formattedParent, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListEntityTypes(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.EntityTypes[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 TestEntityTypesListEntityTypesError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var request = &dialogflowpb.ListEntityTypesRequest{ + Parent: formattedParent, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListEntityTypes(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 TestEntityTypesGetEntityType(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var expectedResponse = &dialogflowpb.EntityType{ + Name: name2, + DisplayName: displayName, + } + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var request = &dialogflowpb.GetEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.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 TestEntityTypesGetEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var request = &dialogflowpb.GetEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetEntityType(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 TestEntityTypesCreateEntityType(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var expectedResponse = &dialogflowpb.EntityType{ + Name: name, + DisplayName: displayName, + } + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var entityType *dialogflowpb.EntityType = &dialogflowpb.EntityType{} + var request = &dialogflowpb.CreateEntityTypeRequest{ + Parent: formattedParent, + EntityType: entityType, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.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 TestEntityTypesCreateEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var entityType *dialogflowpb.EntityType = &dialogflowpb.EntityType{} + var request = &dialogflowpb.CreateEntityTypeRequest{ + Parent: formattedParent, + EntityType: entityType, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateEntityType(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 TestEntityTypesUpdateEntityType(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var expectedResponse = &dialogflowpb.EntityType{ + Name: name, + DisplayName: displayName, + } + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], expectedResponse) + + var entityType *dialogflowpb.EntityType = &dialogflowpb.EntityType{} + var request = &dialogflowpb.UpdateEntityTypeRequest{ + EntityType: entityType, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.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 TestEntityTypesUpdateEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = gstatus.Error(errCode, "test error") + + var entityType *dialogflowpb.EntityType = &dialogflowpb.EntityType{} + var request = &dialogflowpb.UpdateEntityTypeRequest{ + EntityType: entityType, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateEntityType(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 TestEntityTypesDeleteEntityType(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var request = &dialogflowpb.DeleteEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestEntityTypesDeleteEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var request = &dialogflowpb.DeleteEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteEntityType(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 TestEntityTypesBatchUpdateEntityTypes(t *testing.T) { + var expectedResponse *dialogflowpb.BatchUpdateEntityTypesResponse = &dialogflowpb.BatchUpdateEntityTypesResponse{} + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var request = &dialogflowpb.BatchUpdateEntityTypesRequest{ + Parent: formattedParent, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchUpdateEntityTypes(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, mockEntityTypes.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 TestEntityTypesBatchUpdateEntityTypesError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = nil + mockEntityTypes.resps = append(mockEntityTypes.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/agent", "[PROJECT]") + var request = &dialogflowpb.BatchUpdateEntityTypesRequest{ + Parent: formattedParent, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchUpdateEntityTypes(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 TestEntityTypesBatchDeleteEntityTypes(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var entityTypeNames []string = nil + var request = &dialogflowpb.BatchDeleteEntityTypesRequest{ + Parent: formattedParent, + EntityTypeNames: entityTypeNames, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchDeleteEntityTypes(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestEntityTypesBatchDeleteEntityTypesError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = nil + mockEntityTypes.resps = append(mockEntityTypes.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/agent", "[PROJECT]") + var entityTypeNames []string = nil + var request = &dialogflowpb.BatchDeleteEntityTypesRequest{ + Parent: formattedParent, + EntityTypeNames: entityTypeNames, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchDeleteEntityTypes(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestEntityTypesBatchCreateEntities(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var entities []*dialogflowpb.EntityType_Entity = nil + var request = &dialogflowpb.BatchCreateEntitiesRequest{ + Parent: formattedParent, + Entities: entities, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchCreateEntities(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestEntityTypesBatchCreateEntitiesError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = nil + mockEntityTypes.resps = append(mockEntityTypes.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/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var entities []*dialogflowpb.EntityType_Entity = nil + var request = &dialogflowpb.BatchCreateEntitiesRequest{ + Parent: formattedParent, + Entities: entities, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchCreateEntities(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestEntityTypesBatchUpdateEntities(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var entities []*dialogflowpb.EntityType_Entity = nil + var request = &dialogflowpb.BatchUpdateEntitiesRequest{ + Parent: formattedParent, + Entities: entities, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchUpdateEntities(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestEntityTypesBatchUpdateEntitiesError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = nil + mockEntityTypes.resps = append(mockEntityTypes.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/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var entities []*dialogflowpb.EntityType_Entity = nil + var request = &dialogflowpb.BatchUpdateEntitiesRequest{ + Parent: formattedParent, + Entities: entities, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchUpdateEntities(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestEntityTypesBatchDeleteEntities(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockEntityTypes.err = nil + mockEntityTypes.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockEntityTypes.resps = append(mockEntityTypes.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var entityValues []string = nil + var request = &dialogflowpb.BatchDeleteEntitiesRequest{ + Parent: formattedParent, + EntityValues: entityValues, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchDeleteEntities(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestEntityTypesBatchDeleteEntitiesError(t *testing.T) { + errCode := codes.PermissionDenied + mockEntityTypes.err = nil + mockEntityTypes.resps = append(mockEntityTypes.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/agent/entityTypes/%s", "[PROJECT]", "[ENTITY_TYPE]") + var entityValues []string = nil + var request = &dialogflowpb.BatchDeleteEntitiesRequest{ + Parent: formattedParent, + EntityValues: entityValues, + } + + c, err := NewEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchDeleteEntities(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestIntentsListIntents(t *testing.T) { + var nextPageToken string = "" + var intentsElement *dialogflowpb.Intent = &dialogflowpb.Intent{} + var intents = []*dialogflowpb.Intent{intentsElement} + var expectedResponse = &dialogflowpb.ListIntentsResponse{ + NextPageToken: nextPageToken, + Intents: intents, + } + + mockIntents.err = nil + mockIntents.reqs = nil + + mockIntents.resps = append(mockIntents.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var request = &dialogflowpb.ListIntentsRequest{ + Parent: formattedParent, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListIntents(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIntents.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Intents[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 TestIntentsListIntentsError(t *testing.T) { + errCode := codes.PermissionDenied + mockIntents.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var request = &dialogflowpb.ListIntentsRequest{ + Parent: formattedParent, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListIntents(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 TestIntentsGetIntent(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var priority int32 = 1165461084 + var isFallback bool = false + var mlDisabled bool = true + var action string = "action-1422950858" + var resetContexts bool = true + var rootFollowupIntentName string = "rootFollowupIntentName402253784" + var parentFollowupIntentName string = "parentFollowupIntentName-1131901680" + var expectedResponse = &dialogflowpb.Intent{ + Name: name2, + DisplayName: displayName, + Priority: priority, + IsFallback: isFallback, + MlDisabled: mlDisabled, + Action: action, + ResetContexts: resetContexts, + RootFollowupIntentName: rootFollowupIntentName, + ParentFollowupIntentName: parentFollowupIntentName, + } + + mockIntents.err = nil + mockIntents.reqs = nil + + mockIntents.resps = append(mockIntents.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/intents/%s", "[PROJECT]", "[INTENT]") + var request = &dialogflowpb.GetIntentRequest{ + Name: formattedName, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetIntent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIntents.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 TestIntentsGetIntentError(t *testing.T) { + errCode := codes.PermissionDenied + mockIntents.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/intents/%s", "[PROJECT]", "[INTENT]") + var request = &dialogflowpb.GetIntentRequest{ + Name: formattedName, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetIntent(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 TestIntentsCreateIntent(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var priority int32 = 1165461084 + var isFallback bool = false + var mlDisabled bool = true + var action string = "action-1422950858" + var resetContexts bool = true + var rootFollowupIntentName string = "rootFollowupIntentName402253784" + var parentFollowupIntentName string = "parentFollowupIntentName-1131901680" + var expectedResponse = &dialogflowpb.Intent{ + Name: name, + DisplayName: displayName, + Priority: priority, + IsFallback: isFallback, + MlDisabled: mlDisabled, + Action: action, + ResetContexts: resetContexts, + RootFollowupIntentName: rootFollowupIntentName, + ParentFollowupIntentName: parentFollowupIntentName, + } + + mockIntents.err = nil + mockIntents.reqs = nil + + mockIntents.resps = append(mockIntents.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var intent *dialogflowpb.Intent = &dialogflowpb.Intent{} + var request = &dialogflowpb.CreateIntentRequest{ + Parent: formattedParent, + Intent: intent, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateIntent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIntents.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 TestIntentsCreateIntentError(t *testing.T) { + errCode := codes.PermissionDenied + mockIntents.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var intent *dialogflowpb.Intent = &dialogflowpb.Intent{} + var request = &dialogflowpb.CreateIntentRequest{ + Parent: formattedParent, + Intent: intent, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateIntent(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 TestIntentsUpdateIntent(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var priority int32 = 1165461084 + var isFallback bool = false + var mlDisabled bool = true + var action string = "action-1422950858" + var resetContexts bool = true + var rootFollowupIntentName string = "rootFollowupIntentName402253784" + var parentFollowupIntentName string = "parentFollowupIntentName-1131901680" + var expectedResponse = &dialogflowpb.Intent{ + Name: name, + DisplayName: displayName, + Priority: priority, + IsFallback: isFallback, + MlDisabled: mlDisabled, + Action: action, + ResetContexts: resetContexts, + RootFollowupIntentName: rootFollowupIntentName, + ParentFollowupIntentName: parentFollowupIntentName, + } + + mockIntents.err = nil + mockIntents.reqs = nil + + mockIntents.resps = append(mockIntents.resps[:0], expectedResponse) + + var intent *dialogflowpb.Intent = &dialogflowpb.Intent{} + var languageCode string = "languageCode-412800396" + var request = &dialogflowpb.UpdateIntentRequest{ + Intent: intent, + LanguageCode: languageCode, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateIntent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIntents.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 TestIntentsUpdateIntentError(t *testing.T) { + errCode := codes.PermissionDenied + mockIntents.err = gstatus.Error(errCode, "test error") + + var intent *dialogflowpb.Intent = &dialogflowpb.Intent{} + var languageCode string = "languageCode-412800396" + var request = &dialogflowpb.UpdateIntentRequest{ + Intent: intent, + LanguageCode: languageCode, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateIntent(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 TestIntentsDeleteIntent(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockIntents.err = nil + mockIntents.reqs = nil + + mockIntents.resps = append(mockIntents.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/intents/%s", "[PROJECT]", "[INTENT]") + var request = &dialogflowpb.DeleteIntentRequest{ + Name: formattedName, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteIntent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIntents.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestIntentsDeleteIntentError(t *testing.T) { + errCode := codes.PermissionDenied + mockIntents.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/intents/%s", "[PROJECT]", "[INTENT]") + var request = &dialogflowpb.DeleteIntentRequest{ + Name: formattedName, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteIntent(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 TestIntentsBatchUpdateIntents(t *testing.T) { + var expectedResponse *dialogflowpb.BatchUpdateIntentsResponse = &dialogflowpb.BatchUpdateIntentsResponse{} + + mockIntents.err = nil + mockIntents.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockIntents.resps = append(mockIntents.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var languageCode string = "languageCode-412800396" + var request = &dialogflowpb.BatchUpdateIntentsRequest{ + Parent: formattedParent, + LanguageCode: languageCode, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchUpdateIntents(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, mockIntents.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 TestIntentsBatchUpdateIntentsError(t *testing.T) { + errCode := codes.PermissionDenied + mockIntents.err = nil + mockIntents.resps = append(mockIntents.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/agent", "[PROJECT]") + var languageCode string = "languageCode-412800396" + var request = &dialogflowpb.BatchUpdateIntentsRequest{ + Parent: formattedParent, + LanguageCode: languageCode, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchUpdateIntents(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 TestIntentsBatchDeleteIntents(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockIntents.err = nil + mockIntents.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockIntents.resps = append(mockIntents.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/agent", "[PROJECT]") + var intents []*dialogflowpb.Intent = nil + var request = &dialogflowpb.BatchDeleteIntentsRequest{ + Parent: formattedParent, + Intents: intents, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchDeleteIntents(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIntents.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestIntentsBatchDeleteIntentsError(t *testing.T) { + errCode := codes.PermissionDenied + mockIntents.err = nil + mockIntents.resps = append(mockIntents.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/agent", "[PROJECT]") + var intents []*dialogflowpb.Intent = nil + var request = &dialogflowpb.BatchDeleteIntentsRequest{ + Parent: formattedParent, + Intents: intents, + } + + c, err := NewIntentsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.BatchDeleteIntents(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestSessionEntityTypesListSessionEntityTypes(t *testing.T) { + var nextPageToken string = "" + var sessionEntityTypesElement *dialogflowpb.SessionEntityType = &dialogflowpb.SessionEntityType{} + var sessionEntityTypes = []*dialogflowpb.SessionEntityType{sessionEntityTypesElement} + var expectedResponse = &dialogflowpb.ListSessionEntityTypesResponse{ + NextPageToken: nextPageToken, + SessionEntityTypes: sessionEntityTypes, + } + + mockSessionEntityTypes.err = nil + mockSessionEntityTypes.reqs = nil + + mockSessionEntityTypes.resps = append(mockSessionEntityTypes.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var request = &dialogflowpb.ListSessionEntityTypesRequest{ + Parent: formattedParent, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSessionEntityTypes(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSessionEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.SessionEntityTypes[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 TestSessionEntityTypesListSessionEntityTypesError(t *testing.T) { + errCode := codes.PermissionDenied + mockSessionEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var request = &dialogflowpb.ListSessionEntityTypesRequest{ + Parent: formattedParent, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSessionEntityTypes(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 TestSessionEntityTypesGetSessionEntityType(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &dialogflowpb.SessionEntityType{ + Name: name2, + } + + mockSessionEntityTypes.err = nil + mockSessionEntityTypes.reqs = nil + + mockSessionEntityTypes.resps = append(mockSessionEntityTypes.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/entityTypes/%s", "[PROJECT]", "[SESSION]", "[ENTITY_TYPE]") + var request = &dialogflowpb.GetSessionEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSessionEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSessionEntityTypes.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 TestSessionEntityTypesGetSessionEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSessionEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/entityTypes/%s", "[PROJECT]", "[SESSION]", "[ENTITY_TYPE]") + var request = &dialogflowpb.GetSessionEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSessionEntityType(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 TestSessionEntityTypesCreateSessionEntityType(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &dialogflowpb.SessionEntityType{ + Name: name, + } + + mockSessionEntityTypes.err = nil + mockSessionEntityTypes.reqs = nil + + mockSessionEntityTypes.resps = append(mockSessionEntityTypes.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var sessionEntityType *dialogflowpb.SessionEntityType = &dialogflowpb.SessionEntityType{} + var request = &dialogflowpb.CreateSessionEntityTypeRequest{ + Parent: formattedParent, + SessionEntityType: sessionEntityType, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSessionEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSessionEntityTypes.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 TestSessionEntityTypesCreateSessionEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSessionEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var sessionEntityType *dialogflowpb.SessionEntityType = &dialogflowpb.SessionEntityType{} + var request = &dialogflowpb.CreateSessionEntityTypeRequest{ + Parent: formattedParent, + SessionEntityType: sessionEntityType, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSessionEntityType(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 TestSessionEntityTypesUpdateSessionEntityType(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &dialogflowpb.SessionEntityType{ + Name: name, + } + + mockSessionEntityTypes.err = nil + mockSessionEntityTypes.reqs = nil + + mockSessionEntityTypes.resps = append(mockSessionEntityTypes.resps[:0], expectedResponse) + + var sessionEntityType *dialogflowpb.SessionEntityType = &dialogflowpb.SessionEntityType{} + var request = &dialogflowpb.UpdateSessionEntityTypeRequest{ + SessionEntityType: sessionEntityType, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSessionEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSessionEntityTypes.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 TestSessionEntityTypesUpdateSessionEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSessionEntityTypes.err = gstatus.Error(errCode, "test error") + + var sessionEntityType *dialogflowpb.SessionEntityType = &dialogflowpb.SessionEntityType{} + var request = &dialogflowpb.UpdateSessionEntityTypeRequest{ + SessionEntityType: sessionEntityType, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSessionEntityType(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 TestSessionEntityTypesDeleteSessionEntityType(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSessionEntityTypes.err = nil + mockSessionEntityTypes.reqs = nil + + mockSessionEntityTypes.resps = append(mockSessionEntityTypes.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/entityTypes/%s", "[PROJECT]", "[SESSION]", "[ENTITY_TYPE]") + var request = &dialogflowpb.DeleteSessionEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSessionEntityType(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSessionEntityTypes.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSessionEntityTypesDeleteSessionEntityTypeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSessionEntityTypes.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/agent/sessions/%s/entityTypes/%s", "[PROJECT]", "[SESSION]", "[ENTITY_TYPE]") + var request = &dialogflowpb.DeleteSessionEntityTypeRequest{ + Name: formattedName, + } + + c, err := NewSessionEntityTypesClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSessionEntityType(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 TestSessionsDetectIntent(t *testing.T) { + var responseId string = "responseId1847552473" + var expectedResponse = &dialogflowpb.DetectIntentResponse{ + ResponseId: responseId, + } + + mockSessions.err = nil + mockSessions.reqs = nil + + mockSessions.resps = append(mockSessions.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var queryInput *dialogflowpb.QueryInput = &dialogflowpb.QueryInput{} + var request = &dialogflowpb.DetectIntentRequest{ + Session: formattedSession, + QueryInput: queryInput, + } + + c, err := NewSessionsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DetectIntent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSessions.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 TestSessionsDetectIntentError(t *testing.T) { + errCode := codes.PermissionDenied + mockSessions.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/agent/sessions/%s", "[PROJECT]", "[SESSION]") + var queryInput *dialogflowpb.QueryInput = &dialogflowpb.QueryInput{} + var request = &dialogflowpb.DetectIntentRequest{ + Session: formattedSession, + QueryInput: queryInput, + } + + c, err := NewSessionsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DetectIntent(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 TestSessionsStreamingDetectIntent(t *testing.T) { + var responseId string = "responseId1847552473" + var expectedResponse = &dialogflowpb.StreamingDetectIntentResponse{ + ResponseId: responseId, + } + + mockSessions.err = nil + mockSessions.reqs = nil + + mockSessions.resps = append(mockSessions.resps[:0], expectedResponse) + + var session string = "session1984987798" + var queryInput *dialogflowpb.QueryInput = &dialogflowpb.QueryInput{} + var request = &dialogflowpb.StreamingDetectIntentRequest{ + Session: session, + QueryInput: queryInput, + } + + c, err := NewSessionsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingDetectIntent(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSessions.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 TestSessionsStreamingDetectIntentError(t *testing.T) { + errCode := codes.PermissionDenied + mockSessions.err = gstatus.Error(errCode, "test error") + + var session string = "session1984987798" + var queryInput *dialogflowpb.QueryInput = &dialogflowpb.QueryInput{} + var request = &dialogflowpb.StreamingDetectIntentRequest{ + Session: session, + QueryInput: queryInput, + } + + c, err := NewSessionsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingDetectIntent(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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/vendor/cloud.google.com/go/dialogflow/apiv2/session_entity_types_client.go b/vendor/cloud.google.com/go/dialogflow/apiv2/session_entity_types_client.go new file mode 100644 index 0000000000..fe46d0d026 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/session_entity_types_client.go @@ -0,0 +1,279 @@ +// 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 dialogflow + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// SessionEntityTypesCallOptions contains the retry settings for each method of SessionEntityTypesClient. +type SessionEntityTypesCallOptions struct { + ListSessionEntityTypes []gax.CallOption + GetSessionEntityType []gax.CallOption + CreateSessionEntityType []gax.CallOption + UpdateSessionEntityType []gax.CallOption + DeleteSessionEntityType []gax.CallOption +} + +func defaultSessionEntityTypesClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dialogflow.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultSessionEntityTypesCallOptions() *SessionEntityTypesCallOptions { + 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 &SessionEntityTypesCallOptions{ + ListSessionEntityTypes: retry[[2]string{"default", "idempotent"}], + GetSessionEntityType: retry[[2]string{"default", "idempotent"}], + CreateSessionEntityType: retry[[2]string{"default", "non_idempotent"}], + UpdateSessionEntityType: retry[[2]string{"default", "non_idempotent"}], + DeleteSessionEntityType: retry[[2]string{"default", "idempotent"}], + } +} + +// SessionEntityTypesClient is a client for interacting with Dialogflow API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type SessionEntityTypesClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + sessionEntityTypesClient dialogflowpb.SessionEntityTypesClient + + // The call options for this service. + CallOptions *SessionEntityTypesCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewSessionEntityTypesClient creates a new session entity types client. +// +// Entities are extracted from user input and represent parameters that are +// meaningful to your application. For example, a date range, a proper name +// such as a geographic location or landmark, and so on. Entities represent +// actionable data for your application. +// +// Session entity types are referred to as **User** entity types and are +// entities that are built for an individual user such as +// favorites, preferences, playlists, and so on. You can redefine a session +// entity type at the session level. +// +// For more information about entity types, see the +// [Dialogflow documentation](https://dialogflow.com/docs/entities). +func NewSessionEntityTypesClient(ctx context.Context, opts ...option.ClientOption) (*SessionEntityTypesClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultSessionEntityTypesClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &SessionEntityTypesClient{ + conn: conn, + CallOptions: defaultSessionEntityTypesCallOptions(), + + sessionEntityTypesClient: dialogflowpb.NewSessionEntityTypesClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *SessionEntityTypesClient) 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 *SessionEntityTypesClient) 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 *SessionEntityTypesClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListSessionEntityTypes returns the list of all session entity types in the specified session. +func (c *SessionEntityTypesClient) ListSessionEntityTypes(ctx context.Context, req *dialogflowpb.ListSessionEntityTypesRequest, opts ...gax.CallOption) *SessionEntityTypeIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListSessionEntityTypes[0:len(c.CallOptions.ListSessionEntityTypes):len(c.CallOptions.ListSessionEntityTypes)], opts...) + it := &SessionEntityTypeIterator{} + req = proto.Clone(req).(*dialogflowpb.ListSessionEntityTypesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dialogflowpb.SessionEntityType, string, error) { + var resp *dialogflowpb.ListSessionEntityTypesResponse + 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.sessionEntityTypesClient.ListSessionEntityTypes(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.SessionEntityTypes, 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 +} + +// GetSessionEntityType retrieves the specified session entity type. +func (c *SessionEntityTypesClient) GetSessionEntityType(ctx context.Context, req *dialogflowpb.GetSessionEntityTypeRequest, opts ...gax.CallOption) (*dialogflowpb.SessionEntityType, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetSessionEntityType[0:len(c.CallOptions.GetSessionEntityType):len(c.CallOptions.GetSessionEntityType)], opts...) + var resp *dialogflowpb.SessionEntityType + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.sessionEntityTypesClient.GetSessionEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateSessionEntityType creates a session entity type. +func (c *SessionEntityTypesClient) CreateSessionEntityType(ctx context.Context, req *dialogflowpb.CreateSessionEntityTypeRequest, opts ...gax.CallOption) (*dialogflowpb.SessionEntityType, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateSessionEntityType[0:len(c.CallOptions.CreateSessionEntityType):len(c.CallOptions.CreateSessionEntityType)], opts...) + var resp *dialogflowpb.SessionEntityType + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.sessionEntityTypesClient.CreateSessionEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateSessionEntityType updates the specified session entity type. +func (c *SessionEntityTypesClient) UpdateSessionEntityType(ctx context.Context, req *dialogflowpb.UpdateSessionEntityTypeRequest, opts ...gax.CallOption) (*dialogflowpb.SessionEntityType, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateSessionEntityType[0:len(c.CallOptions.UpdateSessionEntityType):len(c.CallOptions.UpdateSessionEntityType)], opts...) + var resp *dialogflowpb.SessionEntityType + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.sessionEntityTypesClient.UpdateSessionEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteSessionEntityType deletes the specified session entity type. +func (c *SessionEntityTypesClient) DeleteSessionEntityType(ctx context.Context, req *dialogflowpb.DeleteSessionEntityTypeRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteSessionEntityType[0:len(c.CallOptions.DeleteSessionEntityType):len(c.CallOptions.DeleteSessionEntityType)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.sessionEntityTypesClient.DeleteSessionEntityType(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// SessionEntityTypeIterator manages a stream of *dialogflowpb.SessionEntityType. +type SessionEntityTypeIterator struct { + items []*dialogflowpb.SessionEntityType + 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 []*dialogflowpb.SessionEntityType, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *SessionEntityTypeIterator) 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 *SessionEntityTypeIterator) Next() (*dialogflowpb.SessionEntityType, error) { + var item *dialogflowpb.SessionEntityType + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *SessionEntityTypeIterator) bufLen() int { + return len(it.items) +} + +func (it *SessionEntityTypeIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/session_entity_types_client_example_test.go b/vendor/cloud.google.com/go/dialogflow/apiv2/session_entity_types_client_example_test.go new file mode 100644 index 0000000000..ec06524fa4 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/session_entity_types_client_example_test.go @@ -0,0 +1,128 @@ +// 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 dialogflow_test + +import ( + "cloud.google.com/go/dialogflow/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" +) + +func ExampleNewSessionEntityTypesClient() { + ctx := context.Background() + c, err := dialogflow.NewSessionEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleSessionEntityTypesClient_ListSessionEntityTypes() { + ctx := context.Background() + c, err := dialogflow.NewSessionEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.ListSessionEntityTypesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListSessionEntityTypes(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleSessionEntityTypesClient_GetSessionEntityType() { + ctx := context.Background() + c, err := dialogflow.NewSessionEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.GetSessionEntityTypeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetSessionEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSessionEntityTypesClient_CreateSessionEntityType() { + ctx := context.Background() + c, err := dialogflow.NewSessionEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.CreateSessionEntityTypeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateSessionEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSessionEntityTypesClient_UpdateSessionEntityType() { + ctx := context.Background() + c, err := dialogflow.NewSessionEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.UpdateSessionEntityTypeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateSessionEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSessionEntityTypesClient_DeleteSessionEntityType() { + ctx := context.Background() + c, err := dialogflow.NewSessionEntityTypesClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.DeleteSessionEntityTypeRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteSessionEntityType(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/sessions_client.go b/vendor/cloud.google.com/go/dialogflow/apiv2/sessions_client.go new file mode 100644 index 0000000000..9c3368b793 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/sessions_client.go @@ -0,0 +1,144 @@ +// 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 dialogflow + +import ( + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +// SessionsCallOptions contains the retry settings for each method of SessionsClient. +type SessionsCallOptions struct { + DetectIntent []gax.CallOption + StreamingDetectIntent []gax.CallOption +} + +func defaultSessionsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dialogflow.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultSessionsCallOptions() *SessionsCallOptions { + retry := map[[2]string][]gax.CallOption{} + return &SessionsCallOptions{ + DetectIntent: retry[[2]string{"default", "non_idempotent"}], + StreamingDetectIntent: retry[[2]string{"default", "non_idempotent"}], + } +} + +// SessionsClient is a client for interacting with Dialogflow API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type SessionsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + sessionsClient dialogflowpb.SessionsClient + + // The call options for this service. + CallOptions *SessionsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewSessionsClient creates a new sessions client. +// +// A session represents an interaction with a user. You retrieve user input +// and pass it to the [DetectIntent][google.cloud.dialogflow.v2.Sessions.DetectIntent] (or +// [StreamingDetectIntent][google.cloud.dialogflow.v2.Sessions.StreamingDetectIntent]) method to determine +// user intent and respond. +func NewSessionsClient(ctx context.Context, opts ...option.ClientOption) (*SessionsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultSessionsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &SessionsClient{ + conn: conn, + CallOptions: defaultSessionsCallOptions(), + + sessionsClient: dialogflowpb.NewSessionsClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *SessionsClient) 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 *SessionsClient) 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 *SessionsClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// DetectIntent processes a natural language query and returns structured, actionable data +// as a result. This method is not idempotent, because it may cause contexts +// and session entity types to be updated, which in turn might affect +// results of future queries. +func (c *SessionsClient) DetectIntent(ctx context.Context, req *dialogflowpb.DetectIntentRequest, opts ...gax.CallOption) (*dialogflowpb.DetectIntentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DetectIntent[0:len(c.CallOptions.DetectIntent):len(c.CallOptions.DetectIntent)], opts...) + var resp *dialogflowpb.DetectIntentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.sessionsClient.DetectIntent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// StreamingDetectIntent processes a natural language query in audio format in a streaming fashion +// and returns structured, actionable data as a result. This method is only +// available via the gRPC API (not REST). +func (c *SessionsClient) StreamingDetectIntent(ctx context.Context, opts ...gax.CallOption) (dialogflowpb.Sessions_StreamingDetectIntentClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.StreamingDetectIntent[0:len(c.CallOptions.StreamingDetectIntent):len(c.CallOptions.StreamingDetectIntent)], opts...) + var resp dialogflowpb.Sessions_StreamingDetectIntentClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.sessionsClient.StreamingDetectIntent(ctx, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/dialogflow/apiv2/sessions_client_example_test.go b/vendor/cloud.google.com/go/dialogflow/apiv2/sessions_client_example_test.go new file mode 100644 index 0000000000..3f57474d96 --- /dev/null +++ b/vendor/cloud.google.com/go/dialogflow/apiv2/sessions_client_example_test.go @@ -0,0 +1,87 @@ +// 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 dialogflow_test + +import ( + "io" + + "cloud.google.com/go/dialogflow/apiv2" + "golang.org/x/net/context" + dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" +) + +func ExampleNewSessionsClient() { + ctx := context.Background() + c, err := dialogflow.NewSessionsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleSessionsClient_DetectIntent() { + ctx := context.Background() + c, err := dialogflow.NewSessionsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dialogflowpb.DetectIntentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.DetectIntent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSessionsClient_StreamingDetectIntent() { + ctx := context.Background() + c, err := dialogflow.NewSessionsClient(ctx) + if err != nil { + // TODO: Handle error. + } + stream, err := c.StreamingDetectIntent(ctx) + if err != nil { + // TODO: Handle error. + } + go func() { + reqs := []*dialogflowpb.StreamingDetectIntentRequest{ + // TODO: Create requests. + } + for _, req := range reqs { + if err := stream.Send(req); err != nil { + // TODO: Handle error. + } + } + stream.CloseSend() + }() + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/dlp/apiv2/dlp_client.go b/vendor/cloud.google.com/go/dlp/apiv2/dlp_client.go new file mode 100644 index 0000000000..8af841c82f --- /dev/null +++ b/vendor/cloud.google.com/go/dlp/apiv2/dlp_client.go @@ -0,0 +1,877 @@ +// 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 dlp + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + dlppb "google.golang.org/genproto/googleapis/privacy/dlp/v2" + "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 { + InspectContent []gax.CallOption + RedactImage []gax.CallOption + DeidentifyContent []gax.CallOption + ReidentifyContent []gax.CallOption + ListInfoTypes []gax.CallOption + CreateInspectTemplate []gax.CallOption + UpdateInspectTemplate []gax.CallOption + GetInspectTemplate []gax.CallOption + ListInspectTemplates []gax.CallOption + DeleteInspectTemplate []gax.CallOption + CreateDeidentifyTemplate []gax.CallOption + UpdateDeidentifyTemplate []gax.CallOption + GetDeidentifyTemplate []gax.CallOption + ListDeidentifyTemplates []gax.CallOption + DeleteDeidentifyTemplate []gax.CallOption + CreateDlpJob []gax.CallOption + ListDlpJobs []gax.CallOption + GetDlpJob []gax.CallOption + DeleteDlpJob []gax.CallOption + CancelDlpJob []gax.CallOption + ListJobTriggers []gax.CallOption + GetJobTrigger []gax.CallOption + DeleteJobTrigger []gax.CallOption + UpdateJobTrigger []gax.CallOption + CreateJobTrigger []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("dlp.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{ + InspectContent: retry[[2]string{"default", "idempotent"}], + RedactImage: retry[[2]string{"default", "idempotent"}], + DeidentifyContent: retry[[2]string{"default", "idempotent"}], + ReidentifyContent: retry[[2]string{"default", "idempotent"}], + ListInfoTypes: retry[[2]string{"default", "idempotent"}], + CreateInspectTemplate: retry[[2]string{"default", "non_idempotent"}], + UpdateInspectTemplate: retry[[2]string{"default", "non_idempotent"}], + GetInspectTemplate: retry[[2]string{"default", "idempotent"}], + ListInspectTemplates: retry[[2]string{"default", "idempotent"}], + DeleteInspectTemplate: retry[[2]string{"default", "idempotent"}], + CreateDeidentifyTemplate: retry[[2]string{"default", "non_idempotent"}], + UpdateDeidentifyTemplate: retry[[2]string{"default", "non_idempotent"}], + GetDeidentifyTemplate: retry[[2]string{"default", "idempotent"}], + ListDeidentifyTemplates: retry[[2]string{"default", "idempotent"}], + DeleteDeidentifyTemplate: retry[[2]string{"default", "idempotent"}], + CreateDlpJob: retry[[2]string{"default", "non_idempotent"}], + ListDlpJobs: retry[[2]string{"default", "idempotent"}], + GetDlpJob: retry[[2]string{"default", "idempotent"}], + DeleteDlpJob: retry[[2]string{"default", "idempotent"}], + CancelDlpJob: retry[[2]string{"default", "non_idempotent"}], + ListJobTriggers: retry[[2]string{"default", "idempotent"}], + GetJobTrigger: retry[[2]string{"default", "idempotent"}], + DeleteJobTrigger: retry[[2]string{"default", "idempotent"}], + UpdateJobTrigger: retry[[2]string{"default", "non_idempotent"}], + CreateJobTrigger: retry[[2]string{"default", "non_idempotent"}], + } +} + +// Client is a client for interacting with Cloud Data Loss Prevention (DLP) 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 dlppb.DlpServiceClient + + // 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 dlp service client. +// +// The Cloud Data Loss Prevention (DLP) API is a service that allows clients +// to detect the presence of Personally Identifiable Information (PII) and other +// privacy-sensitive data in user-supplied, unstructured data streams, like text +// blocks or images. +// The service also includes methods for sensitive data redaction and +// scheduling of data scans on Google Cloud Platform based data sets. +// +// To learn more about concepts and find how-to guides see +// https://cloud.google.com/dlp/docs/. +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: dlppb.NewDlpServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// InspectContent finds potentially sensitive info in content. +// This method has limits on input size, processing time, and output size. +// +// When no InfoTypes or CustomInfoTypes are specified in this request, the +// system will automatically choose what detectors to run. By default this may +// be all types, but may change over time as detectors are updated. +// +// For how to guides, see https://cloud.google.com/dlp/docs/inspecting-images +// and https://cloud.google.com/dlp/docs/inspecting-text, +func (c *Client) InspectContent(ctx context.Context, req *dlppb.InspectContentRequest, opts ...gax.CallOption) (*dlppb.InspectContentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.InspectContent[0:len(c.CallOptions.InspectContent):len(c.CallOptions.InspectContent)], opts...) + var resp *dlppb.InspectContentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.InspectContent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// RedactImage redacts potentially sensitive info from an image. +// This method has limits on input size, processing time, and output size. +// See https://cloud.google.com/dlp/docs/redacting-sensitive-data-images to +// learn more. +// +// When no InfoTypes or CustomInfoTypes are specified in this request, the +// system will automatically choose what detectors to run. By default this may +// be all types, but may change over time as detectors are updated. +func (c *Client) RedactImage(ctx context.Context, req *dlppb.RedactImageRequest, opts ...gax.CallOption) (*dlppb.RedactImageResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.RedactImage[0:len(c.CallOptions.RedactImage):len(c.CallOptions.RedactImage)], opts...) + var resp *dlppb.RedactImageResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.RedactImage(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeidentifyContent de-identifies potentially sensitive info from a ContentItem. +// This method has limits on input size and output size. +// See https://cloud.google.com/dlp/docs/deidentify-sensitive-data to +// learn more. +// +// When no InfoTypes or CustomInfoTypes are specified in this request, the +// system will automatically choose what detectors to run. By default this may +// be all types, but may change over time as detectors are updated. +func (c *Client) DeidentifyContent(ctx context.Context, req *dlppb.DeidentifyContentRequest, opts ...gax.CallOption) (*dlppb.DeidentifyContentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeidentifyContent[0:len(c.CallOptions.DeidentifyContent):len(c.CallOptions.DeidentifyContent)], opts...) + var resp *dlppb.DeidentifyContentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.DeidentifyContent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ReidentifyContent re-identifies content that has been de-identified. +// See +// https://cloud.google.com/dlp/docs/pseudonymization#re-identification_in_free_text_code_example +// to learn more. +func (c *Client) ReidentifyContent(ctx context.Context, req *dlppb.ReidentifyContentRequest, opts ...gax.CallOption) (*dlppb.ReidentifyContentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ReidentifyContent[0:len(c.CallOptions.ReidentifyContent):len(c.CallOptions.ReidentifyContent)], opts...) + var resp *dlppb.ReidentifyContentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ReidentifyContent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListInfoTypes returns a list of the sensitive information types that the DLP API +// supports. See https://cloud.google.com/dlp/docs/infotypes-reference to +// learn more. +func (c *Client) ListInfoTypes(ctx context.Context, req *dlppb.ListInfoTypesRequest, opts ...gax.CallOption) (*dlppb.ListInfoTypesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListInfoTypes[0:len(c.CallOptions.ListInfoTypes):len(c.CallOptions.ListInfoTypes)], opts...) + var resp *dlppb.ListInfoTypesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ListInfoTypes(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateInspectTemplate creates an InspectTemplate for re-using frequently used configuration +// for inspecting content, images, and storage. +// See https://cloud.google.com/dlp/docs/creating-templates to learn more. +func (c *Client) CreateInspectTemplate(ctx context.Context, req *dlppb.CreateInspectTemplateRequest, opts ...gax.CallOption) (*dlppb.InspectTemplate, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateInspectTemplate[0:len(c.CallOptions.CreateInspectTemplate):len(c.CallOptions.CreateInspectTemplate)], opts...) + var resp *dlppb.InspectTemplate + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateInspectTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateInspectTemplate updates the InspectTemplate. +// See https://cloud.google.com/dlp/docs/creating-templates to learn more. +func (c *Client) UpdateInspectTemplate(ctx context.Context, req *dlppb.UpdateInspectTemplateRequest, opts ...gax.CallOption) (*dlppb.InspectTemplate, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateInspectTemplate[0:len(c.CallOptions.UpdateInspectTemplate):len(c.CallOptions.UpdateInspectTemplate)], opts...) + var resp *dlppb.InspectTemplate + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.UpdateInspectTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetInspectTemplate gets an InspectTemplate. +// See https://cloud.google.com/dlp/docs/creating-templates to learn more. +func (c *Client) GetInspectTemplate(ctx context.Context, req *dlppb.GetInspectTemplateRequest, opts ...gax.CallOption) (*dlppb.InspectTemplate, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetInspectTemplate[0:len(c.CallOptions.GetInspectTemplate):len(c.CallOptions.GetInspectTemplate)], opts...) + var resp *dlppb.InspectTemplate + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetInspectTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListInspectTemplates lists InspectTemplates. +// See https://cloud.google.com/dlp/docs/creating-templates to learn more. +func (c *Client) ListInspectTemplates(ctx context.Context, req *dlppb.ListInspectTemplatesRequest, opts ...gax.CallOption) *InspectTemplateIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListInspectTemplates[0:len(c.CallOptions.ListInspectTemplates):len(c.CallOptions.ListInspectTemplates)], opts...) + it := &InspectTemplateIterator{} + req = proto.Clone(req).(*dlppb.ListInspectTemplatesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dlppb.InspectTemplate, string, error) { + var resp *dlppb.ListInspectTemplatesResponse + 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.ListInspectTemplates(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.InspectTemplates, 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 +} + +// DeleteInspectTemplate deletes an InspectTemplate. +// See https://cloud.google.com/dlp/docs/creating-templates to learn more. +func (c *Client) DeleteInspectTemplate(ctx context.Context, req *dlppb.DeleteInspectTemplateRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteInspectTemplate[0:len(c.CallOptions.DeleteInspectTemplate):len(c.CallOptions.DeleteInspectTemplate)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteInspectTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// CreateDeidentifyTemplate creates a DeidentifyTemplate for re-using frequently used configuration +// for de-identifying content, images, and storage. +// See https://cloud.google.com/dlp/docs/creating-templates-deid to learn +// more. +func (c *Client) CreateDeidentifyTemplate(ctx context.Context, req *dlppb.CreateDeidentifyTemplateRequest, opts ...gax.CallOption) (*dlppb.DeidentifyTemplate, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateDeidentifyTemplate[0:len(c.CallOptions.CreateDeidentifyTemplate):len(c.CallOptions.CreateDeidentifyTemplate)], opts...) + var resp *dlppb.DeidentifyTemplate + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateDeidentifyTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateDeidentifyTemplate updates the DeidentifyTemplate. +// See https://cloud.google.com/dlp/docs/creating-templates-deid to learn +// more. +func (c *Client) UpdateDeidentifyTemplate(ctx context.Context, req *dlppb.UpdateDeidentifyTemplateRequest, opts ...gax.CallOption) (*dlppb.DeidentifyTemplate, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateDeidentifyTemplate[0:len(c.CallOptions.UpdateDeidentifyTemplate):len(c.CallOptions.UpdateDeidentifyTemplate)], opts...) + var resp *dlppb.DeidentifyTemplate + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.UpdateDeidentifyTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetDeidentifyTemplate gets a DeidentifyTemplate. +// See https://cloud.google.com/dlp/docs/creating-templates-deid to learn +// more. +func (c *Client) GetDeidentifyTemplate(ctx context.Context, req *dlppb.GetDeidentifyTemplateRequest, opts ...gax.CallOption) (*dlppb.DeidentifyTemplate, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetDeidentifyTemplate[0:len(c.CallOptions.GetDeidentifyTemplate):len(c.CallOptions.GetDeidentifyTemplate)], opts...) + var resp *dlppb.DeidentifyTemplate + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetDeidentifyTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListDeidentifyTemplates lists DeidentifyTemplates. +// See https://cloud.google.com/dlp/docs/creating-templates-deid to learn +// more. +func (c *Client) ListDeidentifyTemplates(ctx context.Context, req *dlppb.ListDeidentifyTemplatesRequest, opts ...gax.CallOption) *DeidentifyTemplateIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListDeidentifyTemplates[0:len(c.CallOptions.ListDeidentifyTemplates):len(c.CallOptions.ListDeidentifyTemplates)], opts...) + it := &DeidentifyTemplateIterator{} + req = proto.Clone(req).(*dlppb.ListDeidentifyTemplatesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dlppb.DeidentifyTemplate, string, error) { + var resp *dlppb.ListDeidentifyTemplatesResponse + 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.ListDeidentifyTemplates(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.DeidentifyTemplates, 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 +} + +// DeleteDeidentifyTemplate deletes a DeidentifyTemplate. +// See https://cloud.google.com/dlp/docs/creating-templates-deid to learn +// more. +func (c *Client) DeleteDeidentifyTemplate(ctx context.Context, req *dlppb.DeleteDeidentifyTemplateRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteDeidentifyTemplate[0:len(c.CallOptions.DeleteDeidentifyTemplate):len(c.CallOptions.DeleteDeidentifyTemplate)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteDeidentifyTemplate(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// CreateDlpJob creates a new job to inspect storage or calculate risk metrics. +// See https://cloud.google.com/dlp/docs/inspecting-storage and +// https://cloud.google.com/dlp/docs/compute-risk-analysis to learn more. +// +// When no InfoTypes or CustomInfoTypes are specified in inspect jobs, the +// system will automatically choose what detectors to run. By default this may +// be all types, but may change over time as detectors are updated. +func (c *Client) CreateDlpJob(ctx context.Context, req *dlppb.CreateDlpJobRequest, opts ...gax.CallOption) (*dlppb.DlpJob, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateDlpJob[0:len(c.CallOptions.CreateDlpJob):len(c.CallOptions.CreateDlpJob)], opts...) + var resp *dlppb.DlpJob + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateDlpJob(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListDlpJobs lists DlpJobs that match the specified filter in the request. +// See https://cloud.google.com/dlp/docs/inspecting-storage and +// https://cloud.google.com/dlp/docs/compute-risk-analysis to learn more. +func (c *Client) ListDlpJobs(ctx context.Context, req *dlppb.ListDlpJobsRequest, opts ...gax.CallOption) *DlpJobIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListDlpJobs[0:len(c.CallOptions.ListDlpJobs):len(c.CallOptions.ListDlpJobs)], opts...) + it := &DlpJobIterator{} + req = proto.Clone(req).(*dlppb.ListDlpJobsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dlppb.DlpJob, string, error) { + var resp *dlppb.ListDlpJobsResponse + 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.ListDlpJobs(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Jobs, 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 +} + +// GetDlpJob gets the latest state of a long-running DlpJob. +// See https://cloud.google.com/dlp/docs/inspecting-storage and +// https://cloud.google.com/dlp/docs/compute-risk-analysis to learn more. +func (c *Client) GetDlpJob(ctx context.Context, req *dlppb.GetDlpJobRequest, opts ...gax.CallOption) (*dlppb.DlpJob, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetDlpJob[0:len(c.CallOptions.GetDlpJob):len(c.CallOptions.GetDlpJob)], opts...) + var resp *dlppb.DlpJob + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetDlpJob(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteDlpJob deletes a long-running DlpJob. This method indicates that the client is +// no longer interested in the DlpJob result. The job will be cancelled if +// possible. +// See https://cloud.google.com/dlp/docs/inspecting-storage and +// https://cloud.google.com/dlp/docs/compute-risk-analysis to learn more. +func (c *Client) DeleteDlpJob(ctx context.Context, req *dlppb.DeleteDlpJobRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteDlpJob[0:len(c.CallOptions.DeleteDlpJob):len(c.CallOptions.DeleteDlpJob)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteDlpJob(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// CancelDlpJob starts asynchronous cancellation on a long-running DlpJob. The server +// makes a best effort to cancel the DlpJob, but success is not +// guaranteed. +// See https://cloud.google.com/dlp/docs/inspecting-storage and +// https://cloud.google.com/dlp/docs/compute-risk-analysis to learn more. +func (c *Client) CancelDlpJob(ctx context.Context, req *dlppb.CancelDlpJobRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CancelDlpJob[0:len(c.CallOptions.CancelDlpJob):len(c.CallOptions.CancelDlpJob)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.CancelDlpJob(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListJobTriggers lists job triggers. +// See https://cloud.google.com/dlp/docs/creating-job-triggers to learn more. +func (c *Client) ListJobTriggers(ctx context.Context, req *dlppb.ListJobTriggersRequest, opts ...gax.CallOption) *JobTriggerIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListJobTriggers[0:len(c.CallOptions.ListJobTriggers):len(c.CallOptions.ListJobTriggers)], opts...) + it := &JobTriggerIterator{} + req = proto.Clone(req).(*dlppb.ListJobTriggersRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*dlppb.JobTrigger, string, error) { + var resp *dlppb.ListJobTriggersResponse + 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.ListJobTriggers(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.JobTriggers, 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 +} + +// GetJobTrigger gets a job trigger. +// See https://cloud.google.com/dlp/docs/creating-job-triggers to learn more. +func (c *Client) GetJobTrigger(ctx context.Context, req *dlppb.GetJobTriggerRequest, opts ...gax.CallOption) (*dlppb.JobTrigger, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetJobTrigger[0:len(c.CallOptions.GetJobTrigger):len(c.CallOptions.GetJobTrigger)], opts...) + var resp *dlppb.JobTrigger + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetJobTrigger(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteJobTrigger deletes a job trigger. +// See https://cloud.google.com/dlp/docs/creating-job-triggers to learn more. +func (c *Client) DeleteJobTrigger(ctx context.Context, req *dlppb.DeleteJobTriggerRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteJobTrigger[0:len(c.CallOptions.DeleteJobTrigger):len(c.CallOptions.DeleteJobTrigger)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteJobTrigger(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// UpdateJobTrigger updates a job trigger. +// See https://cloud.google.com/dlp/docs/creating-job-triggers to learn more. +func (c *Client) UpdateJobTrigger(ctx context.Context, req *dlppb.UpdateJobTriggerRequest, opts ...gax.CallOption) (*dlppb.JobTrigger, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateJobTrigger[0:len(c.CallOptions.UpdateJobTrigger):len(c.CallOptions.UpdateJobTrigger)], opts...) + var resp *dlppb.JobTrigger + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.UpdateJobTrigger(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateJobTrigger creates a job trigger to run DLP actions such as scanning storage for +// sensitive information on a set schedule. +// See https://cloud.google.com/dlp/docs/creating-job-triggers to learn more. +func (c *Client) CreateJobTrigger(ctx context.Context, req *dlppb.CreateJobTriggerRequest, opts ...gax.CallOption) (*dlppb.JobTrigger, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateJobTrigger[0:len(c.CallOptions.CreateJobTrigger):len(c.CallOptions.CreateJobTrigger)], opts...) + var resp *dlppb.JobTrigger + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateJobTrigger(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeidentifyTemplateIterator manages a stream of *dlppb.DeidentifyTemplate. +type DeidentifyTemplateIterator struct { + items []*dlppb.DeidentifyTemplate + 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 []*dlppb.DeidentifyTemplate, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *DeidentifyTemplateIterator) 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 *DeidentifyTemplateIterator) Next() (*dlppb.DeidentifyTemplate, error) { + var item *dlppb.DeidentifyTemplate + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *DeidentifyTemplateIterator) bufLen() int { + return len(it.items) +} + +func (it *DeidentifyTemplateIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// DlpJobIterator manages a stream of *dlppb.DlpJob. +type DlpJobIterator struct { + items []*dlppb.DlpJob + 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 []*dlppb.DlpJob, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *DlpJobIterator) 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 *DlpJobIterator) Next() (*dlppb.DlpJob, error) { + var item *dlppb.DlpJob + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *DlpJobIterator) bufLen() int { + return len(it.items) +} + +func (it *DlpJobIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// InspectTemplateIterator manages a stream of *dlppb.InspectTemplate. +type InspectTemplateIterator struct { + items []*dlppb.InspectTemplate + 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 []*dlppb.InspectTemplate, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *InspectTemplateIterator) 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 *InspectTemplateIterator) Next() (*dlppb.InspectTemplate, error) { + var item *dlppb.InspectTemplate + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *InspectTemplateIterator) bufLen() int { + return len(it.items) +} + +func (it *InspectTemplateIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// JobTriggerIterator manages a stream of *dlppb.JobTrigger. +type JobTriggerIterator struct { + items []*dlppb.JobTrigger + 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 []*dlppb.JobTrigger, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *JobTriggerIterator) 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 *JobTriggerIterator) Next() (*dlppb.JobTrigger, error) { + var item *dlppb.JobTrigger + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *JobTriggerIterator) bufLen() int { + return len(it.items) +} + +func (it *JobTriggerIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/dlp/apiv2/dlp_client_example_test.go b/vendor/cloud.google.com/go/dlp/apiv2/dlp_client_example_test.go new file mode 100644 index 0000000000..9a0f129e49 --- /dev/null +++ b/vendor/cloud.google.com/go/dlp/apiv2/dlp_client_example_test.go @@ -0,0 +1,498 @@ +// 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 dlp_test + +import ( + "cloud.google.com/go/dlp/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + dlppb "google.golang.org/genproto/googleapis/privacy/dlp/v2" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_InspectContent() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.InspectContentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.InspectContent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_RedactImage() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.RedactImageRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.RedactImage(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_DeidentifyContent() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.DeidentifyContentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.DeidentifyContent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ReidentifyContent() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.ReidentifyContentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ReidentifyContent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListInfoTypes() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.ListInfoTypesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListInfoTypes(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_CreateInspectTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.CreateInspectTemplateRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateInspectTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_UpdateInspectTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.UpdateInspectTemplateRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateInspectTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_GetInspectTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.GetInspectTemplateRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetInspectTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListInspectTemplates() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.ListInspectTemplatesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListInspectTemplates(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_DeleteInspectTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.DeleteInspectTemplateRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteInspectTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_CreateDeidentifyTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.CreateDeidentifyTemplateRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateDeidentifyTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_UpdateDeidentifyTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.UpdateDeidentifyTemplateRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateDeidentifyTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_GetDeidentifyTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.GetDeidentifyTemplateRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetDeidentifyTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListDeidentifyTemplates() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.ListDeidentifyTemplatesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListDeidentifyTemplates(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_DeleteDeidentifyTemplate() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.DeleteDeidentifyTemplateRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteDeidentifyTemplate(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_CreateDlpJob() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.CreateDlpJobRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateDlpJob(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListDlpJobs() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.ListDlpJobsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListDlpJobs(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_GetDlpJob() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.GetDlpJobRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetDlpJob(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_DeleteDlpJob() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.DeleteDlpJobRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteDlpJob(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_CancelDlpJob() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.CancelDlpJobRequest{ + // TODO: Fill request struct fields. + } + err = c.CancelDlpJob(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_ListJobTriggers() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.ListJobTriggersRequest{ + // TODO: Fill request struct fields. + } + it := c.ListJobTriggers(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_GetJobTrigger() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.GetJobTriggerRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetJobTrigger(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_DeleteJobTrigger() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.DeleteJobTriggerRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteJobTrigger(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_UpdateJobTrigger() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.UpdateJobTriggerRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateJobTrigger(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_CreateJobTrigger() { + ctx := context.Background() + c, err := dlp.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &dlppb.CreateJobTriggerRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateJobTrigger(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/dlp/apiv2/doc.go b/vendor/cloud.google.com/go/dlp/apiv2/doc.go new file mode 100644 index 0000000000..4018e49498 --- /dev/null +++ b/vendor/cloud.google.com/go/dlp/apiv2/doc.go @@ -0,0 +1,48 @@ +// 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 dlp is an auto-generated package for the +// Cloud Data Loss Prevention (DLP) API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Provides methods for detection, risk analysis, and de-identification of +// privacy-sensitive fragments in text, images, and Google Cloud Platform +// storage repositories. +package dlp // import "cloud.google.com/go/dlp/apiv2" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/dlp/apiv2/mock_test.go b/vendor/cloud.google.com/go/dlp/apiv2/mock_test.go new file mode 100644 index 0000000000..cda3a74b72 --- /dev/null +++ b/vendor/cloud.google.com/go/dlp/apiv2/mock_test.go @@ -0,0 +1,1902 @@ +// 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 dlp + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + dlppb "google.golang.org/genproto/googleapis/privacy/dlp/v2" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockDlpServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + dlppb.DlpServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockDlpServer) InspectContent(ctx context.Context, req *dlppb.InspectContentRequest) (*dlppb.InspectContentResponse, 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].(*dlppb.InspectContentResponse), nil +} + +func (s *mockDlpServer) RedactImage(ctx context.Context, req *dlppb.RedactImageRequest) (*dlppb.RedactImageResponse, 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].(*dlppb.RedactImageResponse), nil +} + +func (s *mockDlpServer) DeidentifyContent(ctx context.Context, req *dlppb.DeidentifyContentRequest) (*dlppb.DeidentifyContentResponse, 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].(*dlppb.DeidentifyContentResponse), nil +} + +func (s *mockDlpServer) ReidentifyContent(ctx context.Context, req *dlppb.ReidentifyContentRequest) (*dlppb.ReidentifyContentResponse, 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].(*dlppb.ReidentifyContentResponse), nil +} + +func (s *mockDlpServer) ListInfoTypes(ctx context.Context, req *dlppb.ListInfoTypesRequest) (*dlppb.ListInfoTypesResponse, 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].(*dlppb.ListInfoTypesResponse), nil +} + +func (s *mockDlpServer) CreateInspectTemplate(ctx context.Context, req *dlppb.CreateInspectTemplateRequest) (*dlppb.InspectTemplate, 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].(*dlppb.InspectTemplate), nil +} + +func (s *mockDlpServer) UpdateInspectTemplate(ctx context.Context, req *dlppb.UpdateInspectTemplateRequest) (*dlppb.InspectTemplate, 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].(*dlppb.InspectTemplate), nil +} + +func (s *mockDlpServer) GetInspectTemplate(ctx context.Context, req *dlppb.GetInspectTemplateRequest) (*dlppb.InspectTemplate, 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].(*dlppb.InspectTemplate), nil +} + +func (s *mockDlpServer) ListInspectTemplates(ctx context.Context, req *dlppb.ListInspectTemplatesRequest) (*dlppb.ListInspectTemplatesResponse, 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].(*dlppb.ListInspectTemplatesResponse), nil +} + +func (s *mockDlpServer) DeleteInspectTemplate(ctx context.Context, req *dlppb.DeleteInspectTemplateRequest) (*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 *mockDlpServer) CreateDeidentifyTemplate(ctx context.Context, req *dlppb.CreateDeidentifyTemplateRequest) (*dlppb.DeidentifyTemplate, 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].(*dlppb.DeidentifyTemplate), nil +} + +func (s *mockDlpServer) UpdateDeidentifyTemplate(ctx context.Context, req *dlppb.UpdateDeidentifyTemplateRequest) (*dlppb.DeidentifyTemplate, 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].(*dlppb.DeidentifyTemplate), nil +} + +func (s *mockDlpServer) GetDeidentifyTemplate(ctx context.Context, req *dlppb.GetDeidentifyTemplateRequest) (*dlppb.DeidentifyTemplate, 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].(*dlppb.DeidentifyTemplate), nil +} + +func (s *mockDlpServer) ListDeidentifyTemplates(ctx context.Context, req *dlppb.ListDeidentifyTemplatesRequest) (*dlppb.ListDeidentifyTemplatesResponse, 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].(*dlppb.ListDeidentifyTemplatesResponse), nil +} + +func (s *mockDlpServer) DeleteDeidentifyTemplate(ctx context.Context, req *dlppb.DeleteDeidentifyTemplateRequest) (*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 *mockDlpServer) CreateJobTrigger(ctx context.Context, req *dlppb.CreateJobTriggerRequest) (*dlppb.JobTrigger, 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].(*dlppb.JobTrigger), nil +} + +func (s *mockDlpServer) UpdateJobTrigger(ctx context.Context, req *dlppb.UpdateJobTriggerRequest) (*dlppb.JobTrigger, 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].(*dlppb.JobTrigger), nil +} + +func (s *mockDlpServer) GetJobTrigger(ctx context.Context, req *dlppb.GetJobTriggerRequest) (*dlppb.JobTrigger, 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].(*dlppb.JobTrigger), nil +} + +func (s *mockDlpServer) ListJobTriggers(ctx context.Context, req *dlppb.ListJobTriggersRequest) (*dlppb.ListJobTriggersResponse, 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].(*dlppb.ListJobTriggersResponse), nil +} + +func (s *mockDlpServer) DeleteJobTrigger(ctx context.Context, req *dlppb.DeleteJobTriggerRequest) (*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 *mockDlpServer) CreateDlpJob(ctx context.Context, req *dlppb.CreateDlpJobRequest) (*dlppb.DlpJob, 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].(*dlppb.DlpJob), nil +} + +func (s *mockDlpServer) ListDlpJobs(ctx context.Context, req *dlppb.ListDlpJobsRequest) (*dlppb.ListDlpJobsResponse, 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].(*dlppb.ListDlpJobsResponse), nil +} + +func (s *mockDlpServer) GetDlpJob(ctx context.Context, req *dlppb.GetDlpJobRequest) (*dlppb.DlpJob, 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].(*dlppb.DlpJob), nil +} + +func (s *mockDlpServer) DeleteDlpJob(ctx context.Context, req *dlppb.DeleteDlpJobRequest) (*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 *mockDlpServer) CancelDlpJob(ctx context.Context, req *dlppb.CancelDlpJobRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockDlp mockDlpServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + dlppb.RegisterDlpServiceServer(serv, &mockDlp) + + 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 TestDlpServiceInspectContent(t *testing.T) { + var expectedResponse *dlppb.InspectContentResponse = &dlppb.InspectContentResponse{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.InspectContentRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.InspectContent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceInspectContentError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.InspectContentRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.InspectContent(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 TestDlpServiceRedactImage(t *testing.T) { + var redactedImage []byte = []byte("28") + var extractedText string = "extractedText998260012" + var expectedResponse = &dlppb.RedactImageResponse{ + RedactedImage: redactedImage, + ExtractedText: extractedText, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.RedactImageRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RedactImage(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceRedactImageError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.RedactImageRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RedactImage(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 TestDlpServiceDeidentifyContent(t *testing.T) { + var expectedResponse *dlppb.DeidentifyContentResponse = &dlppb.DeidentifyContentResponse{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.DeidentifyContentRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeidentifyContent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceDeidentifyContentError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.DeidentifyContentRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeidentifyContent(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 TestDlpServiceReidentifyContent(t *testing.T) { + var expectedResponse *dlppb.ReidentifyContentResponse = &dlppb.ReidentifyContentResponse{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.ReidentifyContentRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ReidentifyContent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceReidentifyContentError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.ReidentifyContentRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ReidentifyContent(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 TestDlpServiceListInfoTypes(t *testing.T) { + var expectedResponse *dlppb.ListInfoTypesResponse = &dlppb.ListInfoTypesResponse{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var request *dlppb.ListInfoTypesRequest = &dlppb.ListInfoTypesRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInfoTypes(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceListInfoTypesError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var request *dlppb.ListInfoTypesRequest = &dlppb.ListInfoTypesRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInfoTypes(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 TestDlpServiceCreateInspectTemplate(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.InspectTemplate{ + Name: name, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.CreateInspectTemplateRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateInspectTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceCreateInspectTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.CreateInspectTemplateRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateInspectTemplate(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 TestDlpServiceUpdateInspectTemplate(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.InspectTemplate{ + Name: name2, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("organizations/%s/inspectTemplates/%s", "[ORGANIZATION]", "[INSPECT_TEMPLATE]") + var request = &dlppb.UpdateInspectTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateInspectTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceUpdateInspectTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("organizations/%s/inspectTemplates/%s", "[ORGANIZATION]", "[INSPECT_TEMPLATE]") + var request = &dlppb.UpdateInspectTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateInspectTemplate(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 TestDlpServiceGetInspectTemplate(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.InspectTemplate{ + Name: name, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var request *dlppb.GetInspectTemplateRequest = &dlppb.GetInspectTemplateRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInspectTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceGetInspectTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var request *dlppb.GetInspectTemplateRequest = &dlppb.GetInspectTemplateRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInspectTemplate(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 TestDlpServiceListInspectTemplates(t *testing.T) { + var nextPageToken string = "" + var inspectTemplatesElement *dlppb.InspectTemplate = &dlppb.InspectTemplate{} + var inspectTemplates = []*dlppb.InspectTemplate{inspectTemplatesElement} + var expectedResponse = &dlppb.ListInspectTemplatesResponse{ + NextPageToken: nextPageToken, + InspectTemplates: inspectTemplates, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.ListInspectTemplatesRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInspectTemplates(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.InspectTemplates[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 TestDlpServiceListInspectTemplatesError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.ListInspectTemplatesRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInspectTemplates(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 TestDlpServiceDeleteInspectTemplate(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("organizations/%s/inspectTemplates/%s", "[ORGANIZATION]", "[INSPECT_TEMPLATE]") + var request = &dlppb.DeleteInspectTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteInspectTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDlpServiceDeleteInspectTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("organizations/%s/inspectTemplates/%s", "[ORGANIZATION]", "[INSPECT_TEMPLATE]") + var request = &dlppb.DeleteInspectTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteInspectTemplate(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 TestDlpServiceCreateDeidentifyTemplate(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.DeidentifyTemplate{ + Name: name, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.CreateDeidentifyTemplateRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateDeidentifyTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceCreateDeidentifyTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.CreateDeidentifyTemplateRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateDeidentifyTemplate(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 TestDlpServiceUpdateDeidentifyTemplate(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.DeidentifyTemplate{ + Name: name2, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("organizations/%s/deidentifyTemplates/%s", "[ORGANIZATION]", "[DEIDENTIFY_TEMPLATE]") + var request = &dlppb.UpdateDeidentifyTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateDeidentifyTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceUpdateDeidentifyTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("organizations/%s/deidentifyTemplates/%s", "[ORGANIZATION]", "[DEIDENTIFY_TEMPLATE]") + var request = &dlppb.UpdateDeidentifyTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateDeidentifyTemplate(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 TestDlpServiceGetDeidentifyTemplate(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.DeidentifyTemplate{ + Name: name2, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("organizations/%s/deidentifyTemplates/%s", "[ORGANIZATION]", "[DEIDENTIFY_TEMPLATE]") + var request = &dlppb.GetDeidentifyTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDeidentifyTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceGetDeidentifyTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("organizations/%s/deidentifyTemplates/%s", "[ORGANIZATION]", "[DEIDENTIFY_TEMPLATE]") + var request = &dlppb.GetDeidentifyTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDeidentifyTemplate(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 TestDlpServiceListDeidentifyTemplates(t *testing.T) { + var nextPageToken string = "" + var deidentifyTemplatesElement *dlppb.DeidentifyTemplate = &dlppb.DeidentifyTemplate{} + var deidentifyTemplates = []*dlppb.DeidentifyTemplate{deidentifyTemplatesElement} + var expectedResponse = &dlppb.ListDeidentifyTemplatesResponse{ + NextPageToken: nextPageToken, + DeidentifyTemplates: deidentifyTemplates, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.ListDeidentifyTemplatesRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDeidentifyTemplates(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.DeidentifyTemplates[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 TestDlpServiceListDeidentifyTemplatesError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("organizations/%s", "[ORGANIZATION]") + var request = &dlppb.ListDeidentifyTemplatesRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDeidentifyTemplates(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 TestDlpServiceDeleteDeidentifyTemplate(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("organizations/%s/deidentifyTemplates/%s", "[ORGANIZATION]", "[DEIDENTIFY_TEMPLATE]") + var request = &dlppb.DeleteDeidentifyTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteDeidentifyTemplate(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDlpServiceDeleteDeidentifyTemplateError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("organizations/%s/deidentifyTemplates/%s", "[ORGANIZATION]", "[DEIDENTIFY_TEMPLATE]") + var request = &dlppb.DeleteDeidentifyTemplateRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteDeidentifyTemplate(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 TestDlpServiceCreateDlpJob(t *testing.T) { + var name string = "name3373707" + var jobTriggerName string = "jobTriggerName1819490804" + var expectedResponse = &dlppb.DlpJob{ + Name: name, + JobTriggerName: jobTriggerName, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.CreateDlpJobRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateDlpJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceCreateDlpJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.CreateDlpJobRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateDlpJob(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 TestDlpServiceListDlpJobs(t *testing.T) { + var nextPageToken string = "" + var jobsElement *dlppb.DlpJob = &dlppb.DlpJob{} + var jobs = []*dlppb.DlpJob{jobsElement} + var expectedResponse = &dlppb.ListDlpJobsResponse{ + NextPageToken: nextPageToken, + Jobs: jobs, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.ListDlpJobsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDlpJobs(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Jobs[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 TestDlpServiceListDlpJobsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.ListDlpJobsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDlpJobs(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 TestDlpServiceGetDlpJob(t *testing.T) { + var name2 string = "name2-1052831874" + var jobTriggerName string = "jobTriggerName1819490804" + var expectedResponse = &dlppb.DlpJob{ + Name: name2, + JobTriggerName: jobTriggerName, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/dlpJobs/%s", "[PROJECT]", "[DLP_JOB]") + var request = &dlppb.GetDlpJobRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDlpJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceGetDlpJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/dlpJobs/%s", "[PROJECT]", "[DLP_JOB]") + var request = &dlppb.GetDlpJobRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDlpJob(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 TestDlpServiceDeleteDlpJob(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/dlpJobs/%s", "[PROJECT]", "[DLP_JOB]") + var request = &dlppb.DeleteDlpJobRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteDlpJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDlpServiceDeleteDlpJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/dlpJobs/%s", "[PROJECT]", "[DLP_JOB]") + var request = &dlppb.DeleteDlpJobRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteDlpJob(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 TestDlpServiceCancelDlpJob(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/dlpJobs/%s", "[PROJECT]", "[DLP_JOB]") + var request = &dlppb.CancelDlpJobRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CancelDlpJob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDlpServiceCancelDlpJobError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/dlpJobs/%s", "[PROJECT]", "[DLP_JOB]") + var request = &dlppb.CancelDlpJobRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CancelDlpJob(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 TestDlpServiceListJobTriggers(t *testing.T) { + var nextPageToken string = "" + var jobTriggersElement *dlppb.JobTrigger = &dlppb.JobTrigger{} + var jobTriggers = []*dlppb.JobTrigger{jobTriggersElement} + var expectedResponse = &dlppb.ListJobTriggersResponse{ + NextPageToken: nextPageToken, + JobTriggers: jobTriggers, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.ListJobTriggersRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListJobTriggers(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.JobTriggers[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 TestDlpServiceListJobTriggersError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.ListJobTriggersRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListJobTriggers(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 TestDlpServiceGetJobTrigger(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.JobTrigger{ + Name: name2, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/jobTriggers/%s", "[PROJECT]", "[JOB_TRIGGER]") + var request = &dlppb.GetJobTriggerRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetJobTrigger(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceGetJobTriggerError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/jobTriggers/%s", "[PROJECT]", "[JOB_TRIGGER]") + var request = &dlppb.GetJobTriggerRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetJobTrigger(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 TestDlpServiceDeleteJobTrigger(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var name string = "name3373707" + var request = &dlppb.DeleteJobTriggerRequest{ + Name: name, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteJobTrigger(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDlpServiceDeleteJobTriggerError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var name string = "name3373707" + var request = &dlppb.DeleteJobTriggerRequest{ + Name: name, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteJobTrigger(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 TestDlpServiceUpdateJobTrigger(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.JobTrigger{ + Name: name2, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/jobTriggers/%s", "[PROJECT]", "[JOB_TRIGGER]") + var request = &dlppb.UpdateJobTriggerRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateJobTrigger(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceUpdateJobTriggerError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/jobTriggers/%s", "[PROJECT]", "[JOB_TRIGGER]") + var request = &dlppb.UpdateJobTriggerRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateJobTrigger(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 TestDlpServiceCreateJobTrigger(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &dlppb.JobTrigger{ + Name: name, + DisplayName: displayName, + Description: description, + } + + mockDlp.err = nil + mockDlp.reqs = nil + + mockDlp.resps = append(mockDlp.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.CreateJobTriggerRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateJobTrigger(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDlp.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 TestDlpServiceCreateJobTriggerError(t *testing.T) { + errCode := codes.PermissionDenied + mockDlp.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &dlppb.CreateJobTriggerRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateJobTrigger(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/vendor/cloud.google.com/go/errorreporting/apiv1beta1/ReportErrorEvent_smoke_test.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/ReportErrorEvent_smoke_test.go new file mode 100644 index 0000000000..4aadfbd449 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/ReportErrorEvent_smoke_test.go @@ -0,0 +1,88 @@ +// 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 errorreporting + +import ( + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestReportErrorsServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewReportErrorsClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var formattedProjectName string = fmt.Sprintf("projects/%s", projectId) + var message string = "[MESSAGE]" + var service string = "[SERVICE]" + var serviceContext = &clouderrorreportingpb.ServiceContext{ + Service: service, + } + var filePath string = "path/to/file.lang" + var lineNumber int32 = 42 + var functionName string = "meaningOfLife" + var reportLocation = &clouderrorreportingpb.SourceLocation{ + FilePath: filePath, + LineNumber: lineNumber, + FunctionName: functionName, + } + var context_ = &clouderrorreportingpb.ErrorContext{ + ReportLocation: reportLocation, + } + var event = &clouderrorreportingpb.ReportedErrorEvent{ + Message: message, + ServiceContext: serviceContext, + Context: context_, + } + var request = &clouderrorreportingpb.ReportErrorEventRequest{ + ProjectName: formattedProjectName, + Event: event, + } + + if _, err := c.ReportErrorEvent(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/doc.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/doc.go new file mode 100644 index 0000000000..b78e1e35e0 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/doc.go @@ -0,0 +1,50 @@ +// 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 errorreporting is an auto-generated package for the +// Stackdriver Error Reporting API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Stackdriver Error Reporting groups and counts similar errors from cloud +// services. The Stackdriver Error Reporting API provides a way to report new +// errors and read access to error groups and their associated errors. +// +// Use the client at cloud.google.com/go/errorreporting in preference to this. +package errorreporting // import "cloud.google.com/go/errorreporting/apiv1beta1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_group_client.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_group_client.go new file mode 100644 index 0000000000..a97c398ed0 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_group_client.go @@ -0,0 +1,153 @@ +// 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 errorreporting + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ErrorGroupCallOptions contains the retry settings for each method of ErrorGroupClient. +type ErrorGroupCallOptions struct { + GetGroup []gax.CallOption + UpdateGroup []gax.CallOption +} + +func defaultErrorGroupClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("clouderrorreporting.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultErrorGroupCallOptions() *ErrorGroupCallOptions { + 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 &ErrorGroupCallOptions{ + GetGroup: retry[[2]string{"default", "idempotent"}], + UpdateGroup: retry[[2]string{"default", "idempotent"}], + } +} + +// ErrorGroupClient is a client for interacting with Stackdriver Error Reporting API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ErrorGroupClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + errorGroupClient clouderrorreportingpb.ErrorGroupServiceClient + + // The call options for this service. + CallOptions *ErrorGroupCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewErrorGroupClient creates a new error group service client. +// +// Service for retrieving and updating individual error groups. +func NewErrorGroupClient(ctx context.Context, opts ...option.ClientOption) (*ErrorGroupClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultErrorGroupClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ErrorGroupClient{ + conn: conn, + CallOptions: defaultErrorGroupCallOptions(), + + errorGroupClient: clouderrorreportingpb.NewErrorGroupServiceClient(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ErrorGroupClient) 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 *ErrorGroupClient) 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 *ErrorGroupClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// GetGroup get the specified group. +func (c *ErrorGroupClient) GetGroup(ctx context.Context, req *clouderrorreportingpb.GetGroupRequest, opts ...gax.CallOption) (*clouderrorreportingpb.ErrorGroup, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetGroup[0:len(c.CallOptions.GetGroup):len(c.CallOptions.GetGroup)], opts...) + var resp *clouderrorreportingpb.ErrorGroup + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.errorGroupClient.GetGroup(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateGroup replace the data for the specified group. +// Fails if the group does not exist. +func (c *ErrorGroupClient) UpdateGroup(ctx context.Context, req *clouderrorreportingpb.UpdateGroupRequest, opts ...gax.CallOption) (*clouderrorreportingpb.ErrorGroup, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateGroup[0:len(c.CallOptions.UpdateGroup):len(c.CallOptions.UpdateGroup)], opts...) + var resp *clouderrorreportingpb.ErrorGroup + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.errorGroupClient.UpdateGroup(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_group_client_example_test.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_group_client_example_test.go new file mode 100644 index 0000000000..b846a1ebbe --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_group_client_example_test.go @@ -0,0 +1,69 @@ +// 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 errorreporting_test + +import ( + "cloud.google.com/go/errorreporting/apiv1beta1" + "golang.org/x/net/context" + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" +) + +func ExampleNewErrorGroupClient() { + ctx := context.Background() + c, err := errorreporting.NewErrorGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleErrorGroupClient_GetGroup() { + ctx := context.Background() + c, err := errorreporting.NewErrorGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouderrorreportingpb.GetGroupRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetGroup(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleErrorGroupClient_UpdateGroup() { + ctx := context.Background() + c, err := errorreporting.NewErrorGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouderrorreportingpb.UpdateGroupRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateGroup(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_stats_client.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_stats_client.go new file mode 100644 index 0000000000..a6e51415d0 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_stats_client.go @@ -0,0 +1,300 @@ +// 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 errorreporting + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ErrorStatsCallOptions contains the retry settings for each method of ErrorStatsClient. +type ErrorStatsCallOptions struct { + ListGroupStats []gax.CallOption + ListEvents []gax.CallOption + DeleteEvents []gax.CallOption +} + +func defaultErrorStatsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("clouderrorreporting.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultErrorStatsCallOptions() *ErrorStatsCallOptions { + 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 &ErrorStatsCallOptions{ + ListGroupStats: retry[[2]string{"default", "idempotent"}], + ListEvents: retry[[2]string{"default", "idempotent"}], + DeleteEvents: retry[[2]string{"default", "idempotent"}], + } +} + +// ErrorStatsClient is a client for interacting with Stackdriver Error Reporting API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ErrorStatsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + errorStatsClient clouderrorreportingpb.ErrorStatsServiceClient + + // The call options for this service. + CallOptions *ErrorStatsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewErrorStatsClient creates a new error stats service client. +// +// An API for retrieving and managing error statistics as well as data for +// individual events. +func NewErrorStatsClient(ctx context.Context, opts ...option.ClientOption) (*ErrorStatsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultErrorStatsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ErrorStatsClient{ + conn: conn, + CallOptions: defaultErrorStatsCallOptions(), + + errorStatsClient: clouderrorreportingpb.NewErrorStatsServiceClient(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ErrorStatsClient) 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 *ErrorStatsClient) 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 *ErrorStatsClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListGroupStats lists the specified groups. +func (c *ErrorStatsClient) ListGroupStats(ctx context.Context, req *clouderrorreportingpb.ListGroupStatsRequest, opts ...gax.CallOption) *ErrorGroupStatsIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListGroupStats[0:len(c.CallOptions.ListGroupStats):len(c.CallOptions.ListGroupStats)], opts...) + it := &ErrorGroupStatsIterator{} + req = proto.Clone(req).(*clouderrorreportingpb.ListGroupStatsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*clouderrorreportingpb.ErrorGroupStats, string, error) { + var resp *clouderrorreportingpb.ListGroupStatsResponse + 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.errorStatsClient.ListGroupStats(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.ErrorGroupStats, 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 +} + +// ListEvents lists the specified events. +func (c *ErrorStatsClient) ListEvents(ctx context.Context, req *clouderrorreportingpb.ListEventsRequest, opts ...gax.CallOption) *ErrorEventIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListEvents[0:len(c.CallOptions.ListEvents):len(c.CallOptions.ListEvents)], opts...) + it := &ErrorEventIterator{} + req = proto.Clone(req).(*clouderrorreportingpb.ListEventsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*clouderrorreportingpb.ErrorEvent, string, error) { + var resp *clouderrorreportingpb.ListEventsResponse + 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.errorStatsClient.ListEvents(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.ErrorEvents, 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 +} + +// DeleteEvents deletes all error events of a given project. +func (c *ErrorStatsClient) DeleteEvents(ctx context.Context, req *clouderrorreportingpb.DeleteEventsRequest, opts ...gax.CallOption) (*clouderrorreportingpb.DeleteEventsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteEvents[0:len(c.CallOptions.DeleteEvents):len(c.CallOptions.DeleteEvents)], opts...) + var resp *clouderrorreportingpb.DeleteEventsResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.errorStatsClient.DeleteEvents(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ErrorEventIterator manages a stream of *clouderrorreportingpb.ErrorEvent. +type ErrorEventIterator struct { + items []*clouderrorreportingpb.ErrorEvent + 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 []*clouderrorreportingpb.ErrorEvent, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *ErrorEventIterator) 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 *ErrorEventIterator) Next() (*clouderrorreportingpb.ErrorEvent, error) { + var item *clouderrorreportingpb.ErrorEvent + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *ErrorEventIterator) bufLen() int { + return len(it.items) +} + +func (it *ErrorEventIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// ErrorGroupStatsIterator manages a stream of *clouderrorreportingpb.ErrorGroupStats. +type ErrorGroupStatsIterator struct { + items []*clouderrorreportingpb.ErrorGroupStats + 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 []*clouderrorreportingpb.ErrorGroupStats, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *ErrorGroupStatsIterator) 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 *ErrorGroupStatsIterator) Next() (*clouderrorreportingpb.ErrorGroupStats, error) { + var item *clouderrorreportingpb.ErrorGroupStats + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *ErrorGroupStatsIterator) bufLen() int { + return len(it.items) +} + +func (it *ErrorGroupStatsIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_stats_client_example_test.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_stats_client_example_test.go new file mode 100644 index 0000000000..6f1858c4a2 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/error_stats_client_example_test.go @@ -0,0 +1,100 @@ +// 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 errorreporting_test + +import ( + "cloud.google.com/go/errorreporting/apiv1beta1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" +) + +func ExampleNewErrorStatsClient() { + ctx := context.Background() + c, err := errorreporting.NewErrorStatsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleErrorStatsClient_ListGroupStats() { + ctx := context.Background() + c, err := errorreporting.NewErrorStatsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouderrorreportingpb.ListGroupStatsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListGroupStats(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleErrorStatsClient_ListEvents() { + ctx := context.Background() + c, err := errorreporting.NewErrorStatsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouderrorreportingpb.ListEventsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListEvents(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleErrorStatsClient_DeleteEvents() { + ctx := context.Background() + c, err := errorreporting.NewErrorStatsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouderrorreportingpb.DeleteEventsRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.DeleteEvents(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/mock_test.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/mock_test.go new file mode 100644 index 0000000000..870dc2286b --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/mock_test.go @@ -0,0 +1,587 @@ +// 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 errorreporting + +import ( + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockErrorGroupServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + clouderrorreportingpb.ErrorGroupServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockErrorGroupServer) GetGroup(ctx context.Context, req *clouderrorreportingpb.GetGroupRequest) (*clouderrorreportingpb.ErrorGroup, 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].(*clouderrorreportingpb.ErrorGroup), nil +} + +func (s *mockErrorGroupServer) UpdateGroup(ctx context.Context, req *clouderrorreportingpb.UpdateGroupRequest) (*clouderrorreportingpb.ErrorGroup, 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].(*clouderrorreportingpb.ErrorGroup), nil +} + +type mockErrorStatsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + clouderrorreportingpb.ErrorStatsServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockErrorStatsServer) ListGroupStats(ctx context.Context, req *clouderrorreportingpb.ListGroupStatsRequest) (*clouderrorreportingpb.ListGroupStatsResponse, 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].(*clouderrorreportingpb.ListGroupStatsResponse), nil +} + +func (s *mockErrorStatsServer) ListEvents(ctx context.Context, req *clouderrorreportingpb.ListEventsRequest) (*clouderrorreportingpb.ListEventsResponse, 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].(*clouderrorreportingpb.ListEventsResponse), nil +} + +func (s *mockErrorStatsServer) DeleteEvents(ctx context.Context, req *clouderrorreportingpb.DeleteEventsRequest) (*clouderrorreportingpb.DeleteEventsResponse, 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].(*clouderrorreportingpb.DeleteEventsResponse), nil +} + +type mockReportErrorsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + clouderrorreportingpb.ReportErrorsServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockReportErrorsServer) ReportErrorEvent(ctx context.Context, req *clouderrorreportingpb.ReportErrorEventRequest) (*clouderrorreportingpb.ReportErrorEventResponse, 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].(*clouderrorreportingpb.ReportErrorEventResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockErrorGroup mockErrorGroupServer + mockErrorStats mockErrorStatsServer + mockReportErrors mockReportErrorsServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + clouderrorreportingpb.RegisterErrorGroupServiceServer(serv, &mockErrorGroup) + clouderrorreportingpb.RegisterErrorStatsServiceServer(serv, &mockErrorStats) + clouderrorreportingpb.RegisterReportErrorsServiceServer(serv, &mockReportErrors) + + 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 TestErrorGroupServiceGetGroup(t *testing.T) { + var name string = "name3373707" + var groupId string = "groupId506361563" + var expectedResponse = &clouderrorreportingpb.ErrorGroup{ + Name: name, + GroupId: groupId, + } + + mockErrorGroup.err = nil + mockErrorGroup.reqs = nil + + mockErrorGroup.resps = append(mockErrorGroup.resps[:0], expectedResponse) + + var formattedGroupName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &clouderrorreportingpb.GetGroupRequest{ + GroupName: formattedGroupName, + } + + c, err := NewErrorGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetGroup(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockErrorGroup.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 TestErrorGroupServiceGetGroupError(t *testing.T) { + errCode := codes.PermissionDenied + mockErrorGroup.err = gstatus.Error(errCode, "test error") + + var formattedGroupName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &clouderrorreportingpb.GetGroupRequest{ + GroupName: formattedGroupName, + } + + c, err := NewErrorGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetGroup(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 TestErrorGroupServiceUpdateGroup(t *testing.T) { + var name string = "name3373707" + var groupId string = "groupId506361563" + var expectedResponse = &clouderrorreportingpb.ErrorGroup{ + Name: name, + GroupId: groupId, + } + + mockErrorGroup.err = nil + mockErrorGroup.reqs = nil + + mockErrorGroup.resps = append(mockErrorGroup.resps[:0], expectedResponse) + + var group *clouderrorreportingpb.ErrorGroup = &clouderrorreportingpb.ErrorGroup{} + var request = &clouderrorreportingpb.UpdateGroupRequest{ + Group: group, + } + + c, err := NewErrorGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateGroup(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockErrorGroup.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 TestErrorGroupServiceUpdateGroupError(t *testing.T) { + errCode := codes.PermissionDenied + mockErrorGroup.err = gstatus.Error(errCode, "test error") + + var group *clouderrorreportingpb.ErrorGroup = &clouderrorreportingpb.ErrorGroup{} + var request = &clouderrorreportingpb.UpdateGroupRequest{ + Group: group, + } + + c, err := NewErrorGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateGroup(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 TestErrorStatsServiceListGroupStats(t *testing.T) { + var nextPageToken string = "" + var errorGroupStatsElement *clouderrorreportingpb.ErrorGroupStats = &clouderrorreportingpb.ErrorGroupStats{} + var errorGroupStats = []*clouderrorreportingpb.ErrorGroupStats{errorGroupStatsElement} + var expectedResponse = &clouderrorreportingpb.ListGroupStatsResponse{ + NextPageToken: nextPageToken, + ErrorGroupStats: errorGroupStats, + } + + mockErrorStats.err = nil + mockErrorStats.reqs = nil + + mockErrorStats.resps = append(mockErrorStats.resps[:0], expectedResponse) + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var timeRange *clouderrorreportingpb.QueryTimeRange = &clouderrorreportingpb.QueryTimeRange{} + var request = &clouderrorreportingpb.ListGroupStatsRequest{ + ProjectName: formattedProjectName, + TimeRange: timeRange, + } + + c, err := NewErrorStatsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListGroupStats(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockErrorStats.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.ErrorGroupStats[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 TestErrorStatsServiceListGroupStatsError(t *testing.T) { + errCode := codes.PermissionDenied + mockErrorStats.err = gstatus.Error(errCode, "test error") + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var timeRange *clouderrorreportingpb.QueryTimeRange = &clouderrorreportingpb.QueryTimeRange{} + var request = &clouderrorreportingpb.ListGroupStatsRequest{ + ProjectName: formattedProjectName, + TimeRange: timeRange, + } + + c, err := NewErrorStatsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListGroupStats(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 TestErrorStatsServiceListEvents(t *testing.T) { + var nextPageToken string = "" + var errorEventsElement *clouderrorreportingpb.ErrorEvent = &clouderrorreportingpb.ErrorEvent{} + var errorEvents = []*clouderrorreportingpb.ErrorEvent{errorEventsElement} + var expectedResponse = &clouderrorreportingpb.ListEventsResponse{ + NextPageToken: nextPageToken, + ErrorEvents: errorEvents, + } + + mockErrorStats.err = nil + mockErrorStats.reqs = nil + + mockErrorStats.resps = append(mockErrorStats.resps[:0], expectedResponse) + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var groupId string = "groupId506361563" + var request = &clouderrorreportingpb.ListEventsRequest{ + ProjectName: formattedProjectName, + GroupId: groupId, + } + + c, err := NewErrorStatsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListEvents(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockErrorStats.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.ErrorEvents[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 TestErrorStatsServiceListEventsError(t *testing.T) { + errCode := codes.PermissionDenied + mockErrorStats.err = gstatus.Error(errCode, "test error") + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var groupId string = "groupId506361563" + var request = &clouderrorreportingpb.ListEventsRequest{ + ProjectName: formattedProjectName, + GroupId: groupId, + } + + c, err := NewErrorStatsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListEvents(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 TestErrorStatsServiceDeleteEvents(t *testing.T) { + var expectedResponse *clouderrorreportingpb.DeleteEventsResponse = &clouderrorreportingpb.DeleteEventsResponse{} + + mockErrorStats.err = nil + mockErrorStats.reqs = nil + + mockErrorStats.resps = append(mockErrorStats.resps[:0], expectedResponse) + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &clouderrorreportingpb.DeleteEventsRequest{ + ProjectName: formattedProjectName, + } + + c, err := NewErrorStatsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeleteEvents(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockErrorStats.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 TestErrorStatsServiceDeleteEventsError(t *testing.T) { + errCode := codes.PermissionDenied + mockErrorStats.err = gstatus.Error(errCode, "test error") + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &clouderrorreportingpb.DeleteEventsRequest{ + ProjectName: formattedProjectName, + } + + c, err := NewErrorStatsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DeleteEvents(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 TestReportErrorsServiceReportErrorEvent(t *testing.T) { + var expectedResponse *clouderrorreportingpb.ReportErrorEventResponse = &clouderrorreportingpb.ReportErrorEventResponse{} + + mockReportErrors.err = nil + mockReportErrors.reqs = nil + + mockReportErrors.resps = append(mockReportErrors.resps[:0], expectedResponse) + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var event *clouderrorreportingpb.ReportedErrorEvent = &clouderrorreportingpb.ReportedErrorEvent{} + var request = &clouderrorreportingpb.ReportErrorEventRequest{ + ProjectName: formattedProjectName, + Event: event, + } + + c, err := NewReportErrorsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ReportErrorEvent(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockReportErrors.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 TestReportErrorsServiceReportErrorEventError(t *testing.T) { + errCode := codes.PermissionDenied + mockReportErrors.err = gstatus.Error(errCode, "test error") + + var formattedProjectName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var event *clouderrorreportingpb.ReportedErrorEvent = &clouderrorreportingpb.ReportedErrorEvent{} + var request = &clouderrorreportingpb.ReportErrorEventRequest{ + ProjectName: formattedProjectName, + Event: event, + } + + c, err := NewReportErrorsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ReportErrorEvent(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/vendor/cloud.google.com/go/errorreporting/apiv1beta1/path_funcs.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/path_funcs.go new file mode 100644 index 0000000000..5ca5e9272f --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/path_funcs.go @@ -0,0 +1,51 @@ +// 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. + +package errorreporting + +// ResultPath returns the path for the result resource. +// +// Deprecated: Use +// fmt.Sprintf("inspect/results/%s", result) +// instead. +func ResultPath(result string) string { + return "" + + "inspect/results/" + + result + + "" +} + +// ErrorStatsProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func ErrorStatsProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// ReportErrorsProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func ReportErrorsProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/report_errors_client.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/report_errors_client.go new file mode 100644 index 0000000000..43a916d168 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/report_errors_client.go @@ -0,0 +1,124 @@ +// 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 errorreporting + +import ( + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +// ReportErrorsCallOptions contains the retry settings for each method of ReportErrorsClient. +type ReportErrorsCallOptions struct { + ReportErrorEvent []gax.CallOption +} + +func defaultReportErrorsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("clouderrorreporting.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultReportErrorsCallOptions() *ReportErrorsCallOptions { + retry := map[[2]string][]gax.CallOption{} + return &ReportErrorsCallOptions{ + ReportErrorEvent: retry[[2]string{"default", "non_idempotent"}], + } +} + +// ReportErrorsClient is a client for interacting with Stackdriver Error Reporting API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ReportErrorsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + reportErrorsClient clouderrorreportingpb.ReportErrorsServiceClient + + // The call options for this service. + CallOptions *ReportErrorsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewReportErrorsClient creates a new report errors service client. +// +// An API for reporting error events. +func NewReportErrorsClient(ctx context.Context, opts ...option.ClientOption) (*ReportErrorsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultReportErrorsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ReportErrorsClient{ + conn: conn, + CallOptions: defaultReportErrorsCallOptions(), + + reportErrorsClient: clouderrorreportingpb.NewReportErrorsServiceClient(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ReportErrorsClient) 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 *ReportErrorsClient) 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 *ReportErrorsClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ReportErrorEvent report an individual error event. +// +// This endpoint accepts either an OAuth token, +// or an +// API key +// for authentication. To use an API key, append it to the URL as the value of +// a key parameter. For example:
POST https://clouderrorreporting.googleapis.com/v1beta1/projects/example-project/events:report?key=123ABC456
+func (c *ReportErrorsClient) ReportErrorEvent(ctx context.Context, req *clouderrorreportingpb.ReportErrorEventRequest, opts ...gax.CallOption) (*clouderrorreportingpb.ReportErrorEventResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ReportErrorEvent[0:len(c.CallOptions.ReportErrorEvent):len(c.CallOptions.ReportErrorEvent)], opts...) + var resp *clouderrorreportingpb.ReportErrorEventResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.reportErrorsClient.ReportErrorEvent(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/errorreporting/apiv1beta1/report_errors_client_example_test.go b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/report_errors_client_example_test.go new file mode 100644 index 0000000000..999ad8c565 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/apiv1beta1/report_errors_client_example_test.go @@ -0,0 +1,51 @@ +// 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 errorreporting_test + +import ( + "cloud.google.com/go/errorreporting/apiv1beta1" + "golang.org/x/net/context" + clouderrorreportingpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" +) + +func ExampleNewReportErrorsClient() { + ctx := context.Background() + c, err := errorreporting.NewReportErrorsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleReportErrorsClient_ReportErrorEvent() { + ctx := context.Background() + c, err := errorreporting.NewReportErrorsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &clouderrorreportingpb.ReportErrorEventRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ReportErrorEvent(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/errorreporting/errors.go b/vendor/cloud.google.com/go/errorreporting/errors.go new file mode 100644 index 0000000000..d522895d60 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/errors.go @@ -0,0 +1,231 @@ +// 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 errorreporting is a Google Stackdriver Error Reporting library. +// +// This package is still experimental and subject to change. +// +// See https://cloud.google.com/error-reporting/ for more information. +package errorreporting // import "cloud.google.com/go/errorreporting" + +import ( + "bytes" + "fmt" + "log" + "net/http" + "runtime" + "time" + + vkit "cloud.google.com/go/errorreporting/apiv1beta1" + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/ptypes" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/support/bundler" + pb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" +) + +const ( + userAgent = `gcloud-golang-errorreporting/20160701` +) + +// Config is additional configuration for Client. +type Config struct { + // ServiceName identifies the running program and is included in the error reports. + // Optional. + ServiceName string + + // ServiceVersion identifies the version of the running program and is + // included in the error reports. + // Optional. + ServiceVersion string + + // OnError is the function to call if any background + // tasks errored. By default, errors are logged. + OnError func(err error) +} + +// Entry holds information about the reported error. +type Entry struct { + Error error + Req *http.Request // if error is associated with a request. + User string // an identifier for the user affected by the error + Stack []byte // if user does not provide a stack trace, runtime.Stack will be called +} + +// Client represents a Google Cloud Error Reporting client. +type Client struct { + projectName string + apiClient client + serviceContext *pb.ServiceContext + bundler *bundler.Bundler + + onErrorFn func(err error) +} + +var newClient = func(ctx context.Context, opts ...option.ClientOption) (client, error) { + client, err := vkit.NewReportErrorsClient(ctx, opts...) + if err != nil { + return nil, err + } + client.SetGoogleClientInfo("gccl", version.Repo) + return client, nil +} + +// NewClient returns a new error reporting client. Generally you will want +// to create a client on program initialization and use it through the lifetime +// of the process. +func NewClient(ctx context.Context, projectID string, cfg Config, opts ...option.ClientOption) (*Client, error) { + if cfg.ServiceName == "" { + cfg.ServiceName = "goapp" + } + c, err := newClient(ctx, opts...) + if err != nil { + return nil, fmt.Errorf("creating client: %v", err) + } + + client := &Client{ + apiClient: c, + projectName: "projects/" + projectID, + serviceContext: &pb.ServiceContext{ + Service: cfg.ServiceName, + Version: cfg.ServiceVersion, + }, + onErrorFn: cfg.OnError, + } + bundler := bundler.NewBundler((*pb.ReportErrorEventRequest)(nil), func(bundle interface{}) { + reqs := bundle.([]*pb.ReportErrorEventRequest) + for _, req := range reqs { + _, err = client.apiClient.ReportErrorEvent(ctx, req) + if err != nil { + client.onError(err) + } + } + }) + // TODO(jbd): Optimize bundler limits. + bundler.DelayThreshold = 2 * time.Second + bundler.BundleCountThreshold = 100 + bundler.BundleByteThreshold = 1000 + bundler.BundleByteLimit = 1000 + bundler.BufferedByteLimit = 10000 + client.bundler = bundler + return client, nil +} + +func (c *Client) onError(err error) { + if c.onErrorFn != nil { + c.onErrorFn(err) + return + } + log.Println(err) +} + +// Close calls Flush, then closes any resources held by the client. +// Close should be called when the client is no longer needed. +func (c *Client) Close() error { + c.Flush() + return c.apiClient.Close() +} + +// Report writes an error report. It doesn't block. Errors in +// writing the error report can be handled via Config.OnError. +func (c *Client) Report(e Entry) { + c.bundler.Add(c.newRequest(e), 1) +} + +// ReportSync writes an error report. It blocks until the entry is written. +func (c *Client) ReportSync(ctx context.Context, e Entry) error { + _, err := c.apiClient.ReportErrorEvent(ctx, c.newRequest(e)) + return err +} + +// Flush blocks until all currently buffered error reports are sent. +// +// If any errors occurred since the last call to Flush, or the +// creation of the client if this is the first call, then Flush reports the +// error via the Config.OnError handler. +func (c *Client) Flush() { + c.bundler.Flush() +} + +func (c *Client) newRequest(e Entry) *pb.ReportErrorEventRequest { + var stack string + if e.Stack != nil { + stack = string(e.Stack) + } else { + // limit the stack trace to 16k. + var buf [16 * 1024]byte + stack = chopStack(buf[0:runtime.Stack(buf[:], false)]) + } + message := e.Error.Error() + "\n" + stack + + var errorContext *pb.ErrorContext + if r := e.Req; r != nil { + errorContext = &pb.ErrorContext{ + HttpRequest: &pb.HttpRequestContext{ + Method: r.Method, + Url: r.Host + r.RequestURI, + UserAgent: r.UserAgent(), + Referrer: r.Referer(), + RemoteIp: r.RemoteAddr, + }, + } + } + if e.User != "" { + if errorContext == nil { + errorContext = &pb.ErrorContext{} + } + errorContext.User = e.User + } + return &pb.ReportErrorEventRequest{ + ProjectName: c.projectName, + Event: &pb.ReportedErrorEvent{ + EventTime: ptypes.TimestampNow(), + ServiceContext: c.serviceContext, + Message: message, + Context: errorContext, + }, + } +} + +// chopStack trims a stack trace so that the function which panics or calls +// Report is first. +func chopStack(s []byte) string { + f := []byte("cloud.google.com/go/errorreporting.(*Client).Report") + + lfFirst := bytes.IndexByte(s, '\n') + if lfFirst == -1 { + return string(s) + } + stack := s[lfFirst:] + panicLine := bytes.Index(stack, f) + if panicLine == -1 { + return string(s) + } + stack = stack[panicLine+1:] + for i := 0; i < 2; i++ { + nextLine := bytes.IndexByte(stack, '\n') + if nextLine == -1 { + return string(s) + } + stack = stack[nextLine+1:] + } + return string(s[:lfFirst+1]) + string(stack) +} + +type client interface { + ReportErrorEvent(ctx context.Context, req *pb.ReportErrorEventRequest, opts ...gax.CallOption) (*pb.ReportErrorEventResponse, error) + Close() error +} diff --git a/vendor/cloud.google.com/go/errorreporting/errors_test.go b/vendor/cloud.google.com/go/errorreporting/errors_test.go new file mode 100644 index 0000000000..42261cb571 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/errors_test.go @@ -0,0 +1,177 @@ +// 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 errorreporting + +import ( + "errors" + "strings" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + + gax "github.com/googleapis/gax-go" + + "golang.org/x/net/context" + "google.golang.org/api/option" + pb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1" +) + +type fakeReportErrorsClient struct { + req *pb.ReportErrorEventRequest + fail bool + doneCh chan struct{} +} + +func (c *fakeReportErrorsClient) ReportErrorEvent(ctx context.Context, req *pb.ReportErrorEventRequest, _ ...gax.CallOption) (*pb.ReportErrorEventResponse, error) { + defer close(c.doneCh) + if c.fail { + return nil, errors.New("request failed") + } + c.req = req + return &pb.ReportErrorEventResponse{}, nil +} + +func (c *fakeReportErrorsClient) Close() error { + return nil +} + +var defaultConfig = Config{ + ServiceName: "myservice", + ServiceVersion: "v1.0", +} + +func newFakeReportErrorsClient() *fakeReportErrorsClient { + c := &fakeReportErrorsClient{} + c.doneCh = make(chan struct{}) + return c +} + +func newTestClient(c *fakeReportErrorsClient, cfg Config) *Client { + newClient = func(ctx context.Context, opts ...option.ClientOption) (client, error) { + return c, nil + } + t, err := NewClient(context.Background(), testutil.ProjID(), cfg) + if err != nil { + panic(err) + } + return t +} + +func commonChecks(t *testing.T, req *pb.ReportErrorEventRequest, fn string) { + if req.Event.ServiceContext.Service != "myservice" { + t.Errorf("error report didn't contain service name") + } + if req.Event.ServiceContext.Version != "v1.0" { + t.Errorf("error report didn't contain version name") + } + if !strings.Contains(req.Event.Message, "error") { + t.Errorf("error report didn't contain message") + } + if !strings.Contains(req.Event.Message, fn) { + t.Errorf("error report didn't contain stack trace") + } + if got, want := req.Event.Context.User, "user"; got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestReport(t *testing.T) { + fc := newFakeReportErrorsClient() + c := newTestClient(fc, defaultConfig) + c.Report(Entry{Error: errors.New("error"), User: "user"}) + c.Flush() + <-fc.doneCh + r := fc.req + if r == nil { + t.Fatalf("got no error report, expected one") + } + commonChecks(t, r, "errorreporting.TestReport") +} + +func TestReportSync(t *testing.T) { + ctx := context.Background() + fc := newFakeReportErrorsClient() + c := newTestClient(fc, defaultConfig) + if err := c.ReportSync(ctx, Entry{Error: errors.New("error"), User: "user"}); err != nil { + t.Fatalf("cannot upload errors: %v", err) + } + + <-fc.doneCh + r := fc.req + if r == nil { + t.Fatalf("got no error report, expected one") + } + commonChecks(t, r, "errorreporting.TestReport") +} + +func TestOnError(t *testing.T) { + fc := newFakeReportErrorsClient() + fc.fail = true + cfg := defaultConfig + errc := make(chan error, 1) + cfg.OnError = func(err error) { errc <- err } + c := newTestClient(fc, cfg) + c.Report(Entry{Error: errors.New("error")}) + c.Flush() + <-fc.doneCh + select { + case err := <-errc: + if err == nil { + t.Error("got nil, want error") + } + case <-time.After(5 * time.Second): + t.Error("timeout") + } +} + +func TestChopStack(t *testing.T) { + for _, test := range []struct { + name string + in []byte + expected string + }{ + { + name: "Report", + in: []byte(` goroutine 39 [running]: +runtime/debug.Stack() + /gopath/runtime/debug/stack.go:24 +0x79 +cloud.google.com/go/errorreporting.(*Client).logInternal() + /gopath/cloud.google.com/go/errorreporting/errors.go:259 +0x18b +cloud.google.com/go/errorreporting.(*Client).Report() + /gopath/cloud.google.com/go/errorreporting/errors.go:248 +0x4ed +cloud.google.com/go/errorreporting.TestReport() + /gopath/cloud.google.com/go/errorreporting/errors_test.go:137 +0x2a1 +testing.tRunner() + /gopath/testing/testing.go:610 +0x81 +created by testing.(*T).Run + /gopath/testing/testing.go:646 +0x2ec +`), + expected: ` goroutine 39 [running]: +cloud.google.com/go/errorreporting.TestReport() + /gopath/cloud.google.com/go/errorreporting/errors_test.go:137 +0x2a1 +testing.tRunner() + /gopath/testing/testing.go:610 +0x81 +created by testing.(*T).Run + /gopath/testing/testing.go:646 +0x2ec +`, + }, + } { + out := chopStack(test.in) + if out != test.expected { + t.Errorf("case %q: chopStack(%q): got %q want %q", test.name, test.in, out, test.expected) + } + } +} diff --git a/vendor/cloud.google.com/go/errorreporting/example_test.go b/vendor/cloud.google.com/go/errorreporting/example_test.go new file mode 100644 index 0000000000..88731057b6 --- /dev/null +++ b/vendor/cloud.google.com/go/errorreporting/example_test.go @@ -0,0 +1,49 @@ +// Copyright 2017 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 errorreporting_test + +import ( + "errors" + "log" + + "cloud.google.com/go/errorreporting" + "golang.org/x/net/context" +) + +func Example() { + // Create the client. + ctx := context.Background() + ec, err := errorreporting.NewClient(ctx, "my-gcp-project", errorreporting.Config{ + ServiceName: "myservice", + ServiceVersion: "v1.0", + }) + defer func() { + if err := ec.Close(); err != nil { + log.Printf("failed to report errors to Stackdriver: %v", err) + } + }() + + // Report an error. + err = doSomething() + if err != nil { + ec.Report(errorreporting.Entry{ + Error: err, + }) + } +} + +func doSomething() error { + return errors.New("something went wrong") +} diff --git a/vendor/cloud.google.com/go/examples_test.go b/vendor/cloud.google.com/go/examples_test.go new file mode 100644 index 0000000000..9737f8aa69 --- /dev/null +++ b/vendor/cloud.google.com/go/examples_test.go @@ -0,0 +1,60 @@ +// 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 cloud_test + +import ( + "time" + + "cloud.google.com/go/bigquery" + "golang.org/x/net/context" +) + +// To set a timeout for an RPC, use context.WithTimeout. +func Example_timeout() { + ctx := context.Background() + // Do not set a timeout on the context passed to NewClient: dialing happens + // asynchronously, and the context is used to refresh credentials in the + // background. + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: handle error. + } + // Time out if it takes more than 10 seconds to create a dataset. + tctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() // Always call cancel. + + if err := client.Dataset("new-dataset").Create(tctx, nil); err != nil { + // TODO: handle error. + } +} + +// To arrange for an RPC to be canceled, use context.WithCancel. +func Example_cancellation() { + ctx := context.Background() + // Do not cancel the context passed to NewClient: dialing happens asynchronously, + // and the context is used to refresh credentials in the background. + client, err := bigquery.NewClient(ctx, "project-id") + if err != nil { + // TODO: handle error. + } + cctx, cancel := context.WithCancel(ctx) + defer cancel() // Always call cancel. + + // TODO: Make the cancel function available to whatever might want to cancel the + // call--perhaps a GUI button. + if err := client.Dataset("new-dataset").Create(cctx, nil); err != nil { + // TODO: handle error. + } +} diff --git a/vendor/cloud.google.com/go/firestore/Makefile b/vendor/cloud.google.com/go/firestore/Makefile new file mode 100644 index 0000000000..b1f9ff79ab --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/Makefile @@ -0,0 +1,13 @@ +# Copy textproto files in this directory from the source of truth. + +SRC=$(GOPATH)/src/github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore + +.PHONY: refresh-tests + +refresh-tests: + -rm genproto/*.pb.go + cp $(SRC)/genproto/*.pb.go genproto + -rm testdata/*.textproto + cp $(SRC)/testdata/*.textproto testdata + openssl dgst -sha1 $(SRC)/testdata/test-suite.binproto > testdata/VERSION + diff --git a/vendor/cloud.google.com/go/firestore/apiv1beta1/doc.go b/vendor/cloud.google.com/go/firestore/apiv1beta1/doc.go new file mode 100644 index 0000000000..3494e35177 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/apiv1beta1/doc.go @@ -0,0 +1,48 @@ +// 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 firestore is an auto-generated package for the +// Google Cloud Firestore API. +// +// NOTE: This package is in beta. It is not stable, and may be subject to changes. +// +// +// Use the client at cloud.google.com/go/firestore in preference to this. +package firestore // import "cloud.google.com/go/firestore/apiv1beta1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/datastore", + } +} diff --git a/vendor/cloud.google.com/go/firestore/apiv1beta1/firestore_client.go b/vendor/cloud.google.com/go/firestore/apiv1beta1/firestore_client.go new file mode 100644 index 0000000000..aee52e9048 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/apiv1beta1/firestore_client.go @@ -0,0 +1,504 @@ +// 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 firestore + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + firestorepb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "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 { + GetDocument []gax.CallOption + ListDocuments []gax.CallOption + CreateDocument []gax.CallOption + UpdateDocument []gax.CallOption + DeleteDocument []gax.CallOption + BatchGetDocuments []gax.CallOption + BeginTransaction []gax.CallOption + Commit []gax.CallOption + Rollback []gax.CallOption + RunQuery []gax.CallOption + Write []gax.CallOption + Listen []gax.CallOption + ListCollectionIds []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("firestore.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, + }) + }), + }, + {"streaming", "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{ + GetDocument: retry[[2]string{"default", "idempotent"}], + ListDocuments: retry[[2]string{"default", "idempotent"}], + CreateDocument: retry[[2]string{"default", "non_idempotent"}], + UpdateDocument: retry[[2]string{"default", "non_idempotent"}], + DeleteDocument: retry[[2]string{"default", "idempotent"}], + BatchGetDocuments: retry[[2]string{"streaming", "idempotent"}], + BeginTransaction: retry[[2]string{"default", "idempotent"}], + Commit: retry[[2]string{"default", "non_idempotent"}], + Rollback: retry[[2]string{"default", "idempotent"}], + RunQuery: retry[[2]string{"default", "idempotent"}], + Write: retry[[2]string{"streaming", "non_idempotent"}], + Listen: retry[[2]string{"streaming", "idempotent"}], + ListCollectionIds: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud Firestore 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 firestorepb.FirestoreClient + + // 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 firestore client. +// +// The Cloud Firestore service. +// +// This service exposes several types of comparable timestamps: +// +// create_time - The time at which a document was created. Changes only +// when a document is deleted, then re-created. Increases in a strict +// monotonic fashion. +// +// update_time - The time at which a document was last updated. Changes +// every time a document is modified. Does not change when a write results +// in no modifications. Increases in a strict monotonic fashion. +// +// read_time - The time at which a particular state was observed. Used +// to denote a consistent snapshot of the database or the time at which a +// Document was observed to not exist. +// +// commit_time - The time at which the writes in a transaction were +// committed. Any read with an equal or greater read_time is guaranteed +// to see the effects of the transaction. +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: firestorepb.NewFirestoreClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// GetDocument gets a single document. +func (c *Client) GetDocument(ctx context.Context, req *firestorepb.GetDocumentRequest, opts ...gax.CallOption) (*firestorepb.Document, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetDocument[0:len(c.CallOptions.GetDocument):len(c.CallOptions.GetDocument)], opts...) + var resp *firestorepb.Document + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetDocument(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListDocuments lists documents. +func (c *Client) ListDocuments(ctx context.Context, req *firestorepb.ListDocumentsRequest, opts ...gax.CallOption) *DocumentIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListDocuments[0:len(c.CallOptions.ListDocuments):len(c.CallOptions.ListDocuments)], opts...) + it := &DocumentIterator{} + req = proto.Clone(req).(*firestorepb.ListDocumentsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*firestorepb.Document, string, error) { + var resp *firestorepb.ListDocumentsResponse + 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.ListDocuments(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Documents, 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 +} + +// CreateDocument creates a new document. +func (c *Client) CreateDocument(ctx context.Context, req *firestorepb.CreateDocumentRequest, opts ...gax.CallOption) (*firestorepb.Document, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateDocument[0:len(c.CallOptions.CreateDocument):len(c.CallOptions.CreateDocument)], opts...) + var resp *firestorepb.Document + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateDocument(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateDocument updates or inserts a document. +func (c *Client) UpdateDocument(ctx context.Context, req *firestorepb.UpdateDocumentRequest, opts ...gax.CallOption) (*firestorepb.Document, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateDocument[0:len(c.CallOptions.UpdateDocument):len(c.CallOptions.UpdateDocument)], opts...) + var resp *firestorepb.Document + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.UpdateDocument(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteDocument deletes a document. +func (c *Client) DeleteDocument(ctx context.Context, req *firestorepb.DeleteDocumentRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteDocument[0:len(c.CallOptions.DeleteDocument):len(c.CallOptions.DeleteDocument)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteDocument(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// BatchGetDocuments gets multiple documents. +// +// Documents returned by this method are not guaranteed to be returned in the +// same order that they were requested. +func (c *Client) BatchGetDocuments(ctx context.Context, req *firestorepb.BatchGetDocumentsRequest, opts ...gax.CallOption) (firestorepb.Firestore_BatchGetDocumentsClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchGetDocuments[0:len(c.CallOptions.BatchGetDocuments):len(c.CallOptions.BatchGetDocuments)], opts...) + var resp firestorepb.Firestore_BatchGetDocumentsClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.BatchGetDocuments(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// BeginTransaction starts a new transaction. +func (c *Client) BeginTransaction(ctx context.Context, req *firestorepb.BeginTransactionRequest, opts ...gax.CallOption) (*firestorepb.BeginTransactionResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BeginTransaction[0:len(c.CallOptions.BeginTransaction):len(c.CallOptions.BeginTransaction)], opts...) + var resp *firestorepb.BeginTransactionResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.BeginTransaction(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Commit commits a transaction, while optionally updating documents. +func (c *Client) Commit(ctx context.Context, req *firestorepb.CommitRequest, opts ...gax.CallOption) (*firestorepb.CommitResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Commit[0:len(c.CallOptions.Commit):len(c.CallOptions.Commit)], opts...) + var resp *firestorepb.CommitResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.Commit(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Rollback rolls back a transaction. +func (c *Client) Rollback(ctx context.Context, req *firestorepb.RollbackRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Rollback[0:len(c.CallOptions.Rollback):len(c.CallOptions.Rollback)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.Rollback(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// RunQuery runs a query. +func (c *Client) RunQuery(ctx context.Context, req *firestorepb.RunQueryRequest, opts ...gax.CallOption) (firestorepb.Firestore_RunQueryClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.RunQuery[0:len(c.CallOptions.RunQuery):len(c.CallOptions.RunQuery)], opts...) + var resp firestorepb.Firestore_RunQueryClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.RunQuery(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Write streams batches of document updates and deletes, in order. +func (c *Client) Write(ctx context.Context, opts ...gax.CallOption) (firestorepb.Firestore_WriteClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Write[0:len(c.CallOptions.Write):len(c.CallOptions.Write)], opts...) + var resp firestorepb.Firestore_WriteClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.Write(ctx, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Listen listens to changes. +func (c *Client) Listen(ctx context.Context, opts ...gax.CallOption) (firestorepb.Firestore_ListenClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Listen[0:len(c.CallOptions.Listen):len(c.CallOptions.Listen)], opts...) + var resp firestorepb.Firestore_ListenClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.Listen(ctx, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListCollectionIds lists all the collection IDs underneath a document. +func (c *Client) ListCollectionIds(ctx context.Context, req *firestorepb.ListCollectionIdsRequest, opts ...gax.CallOption) *StringIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListCollectionIds[0:len(c.CallOptions.ListCollectionIds):len(c.CallOptions.ListCollectionIds)], opts...) + it := &StringIterator{} + req = proto.Clone(req).(*firestorepb.ListCollectionIdsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]string, string, error) { + var resp *firestorepb.ListCollectionIdsResponse + 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.ListCollectionIds(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.CollectionIds, 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 +} + +// DocumentIterator manages a stream of *firestorepb.Document. +type DocumentIterator struct { + items []*firestorepb.Document + 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 []*firestorepb.Document, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *DocumentIterator) 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 *DocumentIterator) Next() (*firestorepb.Document, error) { + var item *firestorepb.Document + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *DocumentIterator) bufLen() int { + return len(it.items) +} + +func (it *DocumentIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// StringIterator manages a stream of string. +type StringIterator struct { + items []string + 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 []string, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *StringIterator) 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 *StringIterator) Next() (string, error) { + var item string + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *StringIterator) bufLen() int { + return len(it.items) +} + +func (it *StringIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/firestore/apiv1beta1/firestore_client_example_test.go b/vendor/cloud.google.com/go/firestore/apiv1beta1/firestore_client_example_test.go new file mode 100644 index 0000000000..85b7db7d49 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/apiv1beta1/firestore_client_example_test.go @@ -0,0 +1,328 @@ +// 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 firestore_test + +import ( + "io" + + "cloud.google.com/go/firestore/apiv1beta1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + firestorepb "google.golang.org/genproto/googleapis/firestore/v1beta1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_GetDocument() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.GetDocumentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetDocument(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListDocuments() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.ListDocumentsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListDocuments(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_CreateDocument() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.CreateDocumentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateDocument(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_UpdateDocument() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.UpdateDocumentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateDocument(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_DeleteDocument() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.DeleteDocumentRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteDocument(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_BatchGetDocuments() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.BatchGetDocumentsRequest{ + // TODO: Fill request struct fields. + } + stream, err := c.BatchGetDocuments(ctx, req) + if err != nil { + // TODO: Handle error. + } + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_BeginTransaction() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.BeginTransactionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.BeginTransaction(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_Commit() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.CommitRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Commit(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_Rollback() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.RollbackRequest{ + // TODO: Fill request struct fields. + } + err = c.Rollback(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_RunQuery() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.RunQueryRequest{ + // TODO: Fill request struct fields. + } + stream, err := c.RunQuery(ctx, req) + if err != nil { + // TODO: Handle error. + } + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_Write() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + stream, err := c.Write(ctx) + if err != nil { + // TODO: Handle error. + } + go func() { + reqs := []*firestorepb.WriteRequest{ + // TODO: Create requests. + } + for _, req := range reqs { + if err := stream.Send(req); err != nil { + // TODO: Handle error. + } + } + stream.CloseSend() + }() + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_Listen() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + stream, err := c.Listen(ctx) + if err != nil { + // TODO: Handle error. + } + go func() { + reqs := []*firestorepb.ListenRequest{ + // TODO: Create requests. + } + for _, req := range reqs { + if err := stream.Send(req); err != nil { + // TODO: Handle error. + } + } + stream.CloseSend() + }() + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_ListCollectionIds() { + ctx := context.Background() + c, err := firestore.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &firestorepb.ListCollectionIdsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListCollectionIds(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/firestore/apiv1beta1/mock_test.go b/vendor/cloud.google.com/go/firestore/apiv1beta1/mock_test.go new file mode 100644 index 0000000000..71391bafa5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/apiv1beta1/mock_test.go @@ -0,0 +1,1153 @@ +// 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 firestore + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + firestorepb "google.golang.org/genproto/googleapis/firestore/v1beta1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockFirestoreServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + firestorepb.FirestoreServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockFirestoreServer) GetDocument(ctx context.Context, req *firestorepb.GetDocumentRequest) (*firestorepb.Document, 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].(*firestorepb.Document), nil +} + +func (s *mockFirestoreServer) ListDocuments(ctx context.Context, req *firestorepb.ListDocumentsRequest) (*firestorepb.ListDocumentsResponse, 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].(*firestorepb.ListDocumentsResponse), nil +} + +func (s *mockFirestoreServer) CreateDocument(ctx context.Context, req *firestorepb.CreateDocumentRequest) (*firestorepb.Document, 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].(*firestorepb.Document), nil +} + +func (s *mockFirestoreServer) UpdateDocument(ctx context.Context, req *firestorepb.UpdateDocumentRequest) (*firestorepb.Document, 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].(*firestorepb.Document), nil +} + +func (s *mockFirestoreServer) DeleteDocument(ctx context.Context, req *firestorepb.DeleteDocumentRequest) (*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 *mockFirestoreServer) BatchGetDocuments(req *firestorepb.BatchGetDocumentsRequest, stream firestorepb.Firestore_BatchGetDocumentsServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + s.reqs = append(s.reqs, req) + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*firestorepb.BatchGetDocumentsResponse)); err != nil { + return err + } + } + return nil +} + +func (s *mockFirestoreServer) BeginTransaction(ctx context.Context, req *firestorepb.BeginTransactionRequest) (*firestorepb.BeginTransactionResponse, 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].(*firestorepb.BeginTransactionResponse), nil +} + +func (s *mockFirestoreServer) Commit(ctx context.Context, req *firestorepb.CommitRequest) (*firestorepb.CommitResponse, 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].(*firestorepb.CommitResponse), nil +} + +func (s *mockFirestoreServer) Rollback(ctx context.Context, req *firestorepb.RollbackRequest) (*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 *mockFirestoreServer) RunQuery(req *firestorepb.RunQueryRequest, stream firestorepb.Firestore_RunQueryServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + s.reqs = append(s.reqs, req) + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*firestorepb.RunQueryResponse)); err != nil { + return err + } + } + return nil +} + +func (s *mockFirestoreServer) Write(stream firestorepb.Firestore_WriteServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + for { + if req, err := stream.Recv(); err == io.EOF { + break + } else if err != nil { + return err + } else { + s.reqs = append(s.reqs, req) + } + } + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*firestorepb.WriteResponse)); err != nil { + return err + } + } + return nil +} + +func (s *mockFirestoreServer) Listen(stream firestorepb.Firestore_ListenServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + for { + if req, err := stream.Recv(); err == io.EOF { + break + } else if err != nil { + return err + } else { + s.reqs = append(s.reqs, req) + } + } + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*firestorepb.ListenResponse)); err != nil { + return err + } + } + return nil +} + +func (s *mockFirestoreServer) ListCollectionIds(ctx context.Context, req *firestorepb.ListCollectionIdsRequest) (*firestorepb.ListCollectionIdsResponse, 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].(*firestorepb.ListCollectionIdsResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockFirestore mockFirestoreServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + firestorepb.RegisterFirestoreServer(serv, &mockFirestore) + + 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 TestFirestoreGetDocument(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &firestorepb.Document{ + Name: name2, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.GetDocumentRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDocument(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreGetDocumentError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.GetDocumentRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDocument(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 TestFirestoreListDocuments(t *testing.T) { + var nextPageToken string = "" + var documentsElement *firestorepb.Document = &firestorepb.Document{} + var documents = []*firestorepb.Document{documentsElement} + var expectedResponse = &firestorepb.ListDocumentsResponse{ + NextPageToken: nextPageToken, + Documents: documents, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var collectionId string = "collectionId-821242276" + var request = &firestorepb.ListDocumentsRequest{ + Parent: formattedParent, + CollectionId: collectionId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDocuments(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Documents[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 TestFirestoreListDocumentsError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var collectionId string = "collectionId-821242276" + var request = &firestorepb.ListDocumentsRequest{ + Parent: formattedParent, + CollectionId: collectionId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDocuments(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 TestFirestoreCreateDocument(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &firestorepb.Document{ + Name: name, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var collectionId string = "collectionId-821242276" + var documentId string = "documentId506676927" + var document *firestorepb.Document = &firestorepb.Document{} + var request = &firestorepb.CreateDocumentRequest{ + Parent: formattedParent, + CollectionId: collectionId, + DocumentId: documentId, + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateDocument(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreCreateDocumentError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var collectionId string = "collectionId-821242276" + var documentId string = "documentId506676927" + var document *firestorepb.Document = &firestorepb.Document{} + var request = &firestorepb.CreateDocumentRequest{ + Parent: formattedParent, + CollectionId: collectionId, + DocumentId: documentId, + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateDocument(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 TestFirestoreUpdateDocument(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &firestorepb.Document{ + Name: name, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var document *firestorepb.Document = &firestorepb.Document{} + var updateMask *firestorepb.DocumentMask = &firestorepb.DocumentMask{} + var request = &firestorepb.UpdateDocumentRequest{ + Document: document, + UpdateMask: updateMask, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateDocument(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreUpdateDocumentError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var document *firestorepb.Document = &firestorepb.Document{} + var updateMask *firestorepb.DocumentMask = &firestorepb.DocumentMask{} + var request = &firestorepb.UpdateDocumentRequest{ + Document: document, + UpdateMask: updateMask, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateDocument(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 TestFirestoreDeleteDocument(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.DeleteDocumentRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteDocument(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestFirestoreDeleteDocumentError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.DeleteDocumentRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteDocument(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 TestFirestoreBatchGetDocuments(t *testing.T) { + var missing string = "missing1069449574" + var transaction []byte = []byte("-34") + var expectedResponse = &firestorepb.BatchGetDocumentsResponse{ + Result: &firestorepb.BatchGetDocumentsResponse_Missing{ + Missing: missing, + }, + Transaction: transaction, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var documents []string = nil + var request = &firestorepb.BatchGetDocumentsRequest{ + Database: formattedDatabase, + Documents: documents, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.BatchGetDocuments(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreBatchGetDocumentsError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var documents []string = nil + var request = &firestorepb.BatchGetDocumentsRequest{ + Database: formattedDatabase, + Documents: documents, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.BatchGetDocuments(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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 TestFirestoreBeginTransaction(t *testing.T) { + var transaction []byte = []byte("-34") + var expectedResponse = &firestorepb.BeginTransactionResponse{ + Transaction: transaction, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var request = &firestorepb.BeginTransactionRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BeginTransaction(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreBeginTransactionError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var request = &firestorepb.BeginTransactionRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BeginTransaction(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 TestFirestoreCommit(t *testing.T) { + var expectedResponse *firestorepb.CommitResponse = &firestorepb.CommitResponse{} + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var writes []*firestorepb.Write = nil + var request = &firestorepb.CommitRequest{ + Database: formattedDatabase, + Writes: writes, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Commit(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreCommitError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var writes []*firestorepb.Write = nil + var request = &firestorepb.CommitRequest{ + Database: formattedDatabase, + Writes: writes, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Commit(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 TestFirestoreRollback(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var transaction []byte = []byte("-34") + var request = &firestorepb.RollbackRequest{ + Database: formattedDatabase, + Transaction: transaction, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.Rollback(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestFirestoreRollbackError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var transaction []byte = []byte("-34") + var request = &firestorepb.RollbackRequest{ + Database: formattedDatabase, + Transaction: transaction, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.Rollback(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 TestFirestoreRunQuery(t *testing.T) { + var transaction []byte = []byte("-34") + var skippedResults int32 = 880286183 + var expectedResponse = &firestorepb.RunQueryResponse{ + Transaction: transaction, + SkippedResults: skippedResults, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.RunQueryRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.RunQuery(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreRunQueryError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.RunQueryRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.RunQuery(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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 TestFirestoreWrite(t *testing.T) { + var streamId string = "streamId-315624902" + var streamToken []byte = []byte("122") + var expectedResponse = &firestorepb.WriteResponse{ + StreamId: streamId, + StreamToken: streamToken, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var request = &firestorepb.WriteRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.Write(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreWriteError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var request = &firestorepb.WriteRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.Write(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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 TestFirestoreListen(t *testing.T) { + var expectedResponse *firestorepb.ListenResponse = &firestorepb.ListenResponse{} + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var request = &firestorepb.ListenRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.Listen(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.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 TestFirestoreListenError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]") + var request = &firestorepb.ListenRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.Listen(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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 TestFirestoreListCollectionIds(t *testing.T) { + var nextPageToken string = "" + var collectionIdsElement string = "collectionIdsElement1368994900" + var collectionIds = []string{collectionIdsElement} + var expectedResponse = &firestorepb.ListCollectionIdsResponse{ + NextPageToken: nextPageToken, + CollectionIds: collectionIds, + } + + mockFirestore.err = nil + mockFirestore.reqs = nil + + mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.ListCollectionIdsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListCollectionIds(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockFirestore.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.CollectionIds[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 TestFirestoreListCollectionIdsError(t *testing.T) { + errCode := codes.PermissionDenied + mockFirestore.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]") + var request = &firestorepb.ListCollectionIdsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListCollectionIds(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 +} diff --git a/vendor/cloud.google.com/go/firestore/apiv1beta1/path_funcs.go b/vendor/cloud.google.com/go/firestore/apiv1beta1/path_funcs.go new file mode 100644 index 0000000000..7f4b0ad65a --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/apiv1beta1/path_funcs.go @@ -0,0 +1,78 @@ +// 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. + +package firestore + +// DatabaseRootPath returns the path for the database root resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/databases/%s", project, database) +// instead. +func DatabaseRootPath(project, database string) string { + return "" + + "projects/" + + project + + "/databases/" + + database + + "" +} + +// DocumentRootPath returns the path for the document root resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/databases/%s/documents", project, database) +// instead. +func DocumentRootPath(project, database string) string { + return "" + + "projects/" + + project + + "/databases/" + + database + + "/documents" + + "" +} + +// DocumentPathPath returns the path for the document path resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/databases/%s/documents/%s", project, database, documentPath) +// instead. +func DocumentPathPath(project, database, documentPath string) string { + return "" + + "projects/" + + project + + "/databases/" + + database + + "/documents/" + + documentPath + + "" +} + +// AnyPathPath returns the path for the any path resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", project, database, document, anyPath) +// instead. +func AnyPathPath(project, database, document, anyPath string) string { + return "" + + "projects/" + + project + + "/databases/" + + database + + "/documents/" + + document + + "/" + + anyPath + + "" +} diff --git a/vendor/cloud.google.com/go/firestore/client.go b/vendor/cloud.google.com/go/firestore/client.go new file mode 100644 index 0000000000..5f74293b5e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/client.go @@ -0,0 +1,283 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "fmt" + "io" + "strings" + "time" + + "google.golang.org/api/iterator" + + vkit "cloud.google.com/go/firestore/apiv1beta1" + + "cloud.google.com/go/internal/version" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "github.com/golang/protobuf/ptypes" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// resourcePrefixHeader is the name of the metadata header used to indicate +// the resource being operated on. +const resourcePrefixHeader = "google-cloud-resource-prefix" + +// A Client provides access to the Firestore service. +type Client struct { + c *vkit.Client + projectID string + databaseID string // A client is tied to a single database. +} + +// NewClient creates a new Firestore client that uses the given project. +func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) { + vc, err := vkit.NewClient(ctx, opts...) + if err != nil { + return nil, err + } + vc.SetGoogleClientInfo("gccl", version.Repo) + c := &Client{ + c: vc, + projectID: projectID, + databaseID: "(default)", // always "(default)", for now + } + return c, nil + +} + +// Close closes any resources held by the client. +// +// Close need not be called at program exit. +func (c *Client) Close() error { + return c.c.Close() +} + +func (c *Client) path() string { + return fmt.Sprintf("projects/%s/databases/%s", c.projectID, c.databaseID) +} + +func withResourceHeader(ctx context.Context, resource string) context.Context { + md, _ := metadata.FromOutgoingContext(ctx) + md = md.Copy() + md[resourcePrefixHeader] = []string{resource} + return metadata.NewOutgoingContext(ctx, md) +} + +// Collection creates a reference to a collection with the given path. +// A path is a sequence of IDs separated by slashes. +// +// Collection returns nil if path contains an even number of IDs or any ID is empty. +func (c *Client) Collection(path string) *CollectionRef { + coll, _ := c.idsToRef(strings.Split(path, "/"), c.path()) + return coll +} + +// Doc creates a reference to a document with the given path. +// A path is a sequence of IDs separated by slashes. +// +// Doc returns nil if path contains an odd number of IDs or any ID is empty. +func (c *Client) Doc(path string) *DocumentRef { + _, doc := c.idsToRef(strings.Split(path, "/"), c.path()) + return doc +} + +func (c *Client) idsToRef(IDs []string, dbPath string) (*CollectionRef, *DocumentRef) { + if len(IDs) == 0 { + return nil, nil + } + for _, id := range IDs { + if id == "" { + return nil, nil + } + } + coll := newTopLevelCollRef(c, dbPath, IDs[0]) + i := 1 + for i < len(IDs) { + doc := newDocRef(coll, IDs[i]) + i++ + if i == len(IDs) { + return nil, doc + } + coll = newCollRefWithParent(c, doc, IDs[i]) + i++ + } + return coll, nil +} + +// GetAll retrieves multiple documents with a single call. The DocumentSnapshots are +// returned in the order of the given DocumentRefs. +// +// If a document is not present, the corresponding DocumentSnapshot's Exists method will return false. +func (c *Client) GetAll(ctx context.Context, docRefs []*DocumentRef) ([]*DocumentSnapshot, error) { + if err := checkTransaction(ctx); err != nil { + return nil, err + } + return c.getAll(ctx, docRefs, nil) +} + +func (c *Client) getAll(ctx context.Context, docRefs []*DocumentRef, tid []byte) ([]*DocumentSnapshot, error) { + var docNames []string + docIndex := map[string]int{} // doc name to position in docRefs + for i, dr := range docRefs { + if dr == nil { + return nil, errNilDocRef + } + docNames = append(docNames, dr.Path) + docIndex[dr.Path] = i + } + req := &pb.BatchGetDocumentsRequest{ + Database: c.path(), + Documents: docNames, + } + if tid != nil { + req.ConsistencySelector = &pb.BatchGetDocumentsRequest_Transaction{tid} + } + streamClient, err := c.c.BatchGetDocuments(withResourceHeader(ctx, req.Database), req) + if err != nil { + return nil, err + } + + // Read and remember all results from the stream. + var resps []*pb.BatchGetDocumentsResponse + for { + resp, err := streamClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + resps = append(resps, resp) + } + + // Results may arrive out of order. Put each at the right index. + docs := make([]*DocumentSnapshot, len(docNames)) + for _, resp := range resps { + var ( + i int + doc *pb.Document + err error + ) + switch r := resp.Result.(type) { + case *pb.BatchGetDocumentsResponse_Found: + i = docIndex[r.Found.Name] + doc = r.Found + case *pb.BatchGetDocumentsResponse_Missing: + i = docIndex[r.Missing] + doc = nil + default: + return nil, errors.New("firestore: unknown BatchGetDocumentsResponse result type") + } + if docs[i] != nil { + return nil, fmt.Errorf("firestore: %q seen twice", docRefs[i].Path) + } + docs[i], err = newDocumentSnapshot(docRefs[i], doc, c, resp.ReadTime) + if err != nil { + return nil, err + } + } + return docs, nil +} + +// Collections returns an interator over the top-level collections. +func (c *Client) Collections(ctx context.Context) *CollectionIterator { + it := &CollectionIterator{ + err: checkTransaction(ctx), + client: c, + it: c.c.ListCollectionIds( + withResourceHeader(ctx, c.path()), + &pb.ListCollectionIdsRequest{Parent: c.path()}), + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// Batch returns a WriteBatch. +func (c *Client) Batch() *WriteBatch { + return &WriteBatch{c: c} +} + +// commit calls the Commit RPC outside of a transaction. +func (c *Client) commit(ctx context.Context, ws []*pb.Write) ([]*WriteResult, error) { + if err := checkTransaction(ctx); err != nil { + return nil, err + } + req := &pb.CommitRequest{ + Database: c.path(), + Writes: ws, + } + res, err := c.c.Commit(withResourceHeader(ctx, req.Database), req) + if err != nil { + return nil, err + } + if len(res.WriteResults) == 0 { + return nil, errors.New("firestore: missing WriteResult") + } + var wrs []*WriteResult + for _, pwr := range res.WriteResults { + wr, err := writeResultFromProto(pwr) + if err != nil { + return nil, err + } + wrs = append(wrs, wr) + } + return wrs, nil +} + +func (c *Client) commit1(ctx context.Context, ws []*pb.Write) (*WriteResult, error) { + wrs, err := c.commit(ctx, ws) + if err != nil { + return nil, err + } + return wrs[0], nil +} + +// A WriteResult is returned by methods that write documents. +type WriteResult struct { + // The time at which the document was updated, or created if it did not + // previously exist. Writes that do not actually change the document do + // not change the update time. + UpdateTime time.Time +} + +func writeResultFromProto(wr *pb.WriteResult) (*WriteResult, error) { + t, err := ptypes.Timestamp(wr.UpdateTime) + if err != nil { + t = time.Time{} + // TODO(jba): Follow up if Delete is supposed to return a nil timestamp. + } + return &WriteResult{UpdateTime: t}, nil +} + +func sleep(ctx context.Context, dur time.Duration) error { + switch err := gax.Sleep(ctx, dur); err { + case context.Canceled: + return status.Error(codes.Canceled, "context canceled") + case context.DeadlineExceeded: + return status.Error(codes.DeadlineExceeded, "context deadline exceeded") + default: + return err + } +} diff --git a/vendor/cloud.google.com/go/firestore/client_test.go b/vendor/cloud.google.com/go/firestore/client_test.go new file mode 100644 index 0000000000..b6e50f67f1 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/client_test.go @@ -0,0 +1,212 @@ +// Copyright 2017 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 firestore + +import ( + "testing" + + tspb "github.com/golang/protobuf/ptypes/timestamp" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var testClient = &Client{ + projectID: "projectID", + databaseID: "(default)", +} + +func TestClientCollectionAndDoc(t *testing.T) { + coll1 := testClient.Collection("X") + db := "projects/projectID/databases/(default)" + wantc1 := &CollectionRef{ + c: testClient, + parentPath: db, + Parent: nil, + ID: "X", + Path: "projects/projectID/databases/(default)/documents/X", + Query: Query{c: testClient, collectionID: "X", parentPath: db}, + } + if !testEqual(coll1, wantc1) { + t.Fatalf("got\n%+v\nwant\n%+v", coll1, wantc1) + } + doc1 := testClient.Doc("X/a") + wantd1 := &DocumentRef{ + Parent: coll1, + ID: "a", + Path: "projects/projectID/databases/(default)/documents/X/a", + } + + if !testEqual(doc1, wantd1) { + t.Fatalf("got %+v, want %+v", doc1, wantd1) + } + coll2 := testClient.Collection("X/a/Y") + parentPath := "projects/projectID/databases/(default)/documents/X/a" + wantc2 := &CollectionRef{ + c: testClient, + parentPath: parentPath, + Parent: doc1, + ID: "Y", + Path: "projects/projectID/databases/(default)/documents/X/a/Y", + Query: Query{c: testClient, collectionID: "Y", parentPath: parentPath}, + } + if !testEqual(coll2, wantc2) { + t.Fatalf("\ngot %+v\nwant %+v", coll2, wantc2) + } + doc2 := testClient.Doc("X/a/Y/b") + wantd2 := &DocumentRef{ + Parent: coll2, + ID: "b", + Path: "projects/projectID/databases/(default)/documents/X/a/Y/b", + } + if !testEqual(doc2, wantd2) { + t.Fatalf("got %+v, want %+v", doc2, wantd2) + } +} + +func TestClientCollDocErrors(t *testing.T) { + for _, badColl := range []string{"", "/", "/a/", "/a/b", "a/b/", "a//b"} { + coll := testClient.Collection(badColl) + if coll != nil { + t.Errorf("coll path %q: got %+v, want nil", badColl, coll) + } + } + for _, badDoc := range []string{"", "a", "/", "/a", "a/", "a/b/c", "a//b/c"} { + doc := testClient.Doc(badDoc) + if doc != nil { + t.Errorf("doc path %q: got %+v, want nil", badDoc, doc) + } + } +} + +func TestGetAll(t *testing.T) { + c, srv := newMock(t) + defer c.Close() + const dbPath = "projects/projectID/databases/(default)" + req := &pb.BatchGetDocumentsRequest{ + Database: dbPath, + Documents: []string{ + dbPath + "/documents/C/a", + dbPath + "/documents/C/b", + dbPath + "/documents/C/c", + }, + } + testGetAll(t, c, srv, dbPath, func(drs []*DocumentRef) ([]*DocumentSnapshot, error) { + return c.GetAll(context.Background(), drs) + }, req) +} + +func testGetAll(t *testing.T, c *Client, srv *mockServer, dbPath string, getAll func([]*DocumentRef) ([]*DocumentSnapshot, error), req *pb.BatchGetDocumentsRequest) { + wantPBDocs := []*pb.Document{ + { + Name: dbPath + "/documents/C/a", + CreateTime: aTimestamp, + UpdateTime: aTimestamp, + Fields: map[string]*pb.Value{"f": intval(2)}, + }, + nil, + { + Name: dbPath + "/documents/C/c", + CreateTime: aTimestamp, + UpdateTime: aTimestamp, + Fields: map[string]*pb.Value{"f": intval(1)}, + }, + } + wantReadTimes := []*tspb.Timestamp{aTimestamp, aTimestamp2, aTimestamp3} + srv.addRPC(req, + []interface{}{ + // deliberately put these out of order + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Found{wantPBDocs[2]}, + ReadTime: aTimestamp3, + }, + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Found{wantPBDocs[0]}, + ReadTime: aTimestamp, + }, + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Missing{dbPath + "/documents/C/b"}, + ReadTime: aTimestamp2, + }, + }, + ) + coll := c.Collection("C") + var docRefs []*DocumentRef + for _, name := range []string{"a", "b", "c"} { + docRefs = append(docRefs, coll.Doc(name)) + } + docs, err := getAll(docRefs) + if err != nil { + t.Fatal(err) + } + if got, want := len(docs), len(wantPBDocs); got != want { + t.Errorf("got %d docs, wanted %d", got, want) + } + for i, got := range docs { + want, err := newDocumentSnapshot(docRefs[i], wantPBDocs[i], c, wantReadTimes[i]) + if err != nil { + t.Fatal(err) + } + if diff := testDiff(got, want); diff != "" { + t.Errorf("#%d: got=--, want==++\n%s", i, diff) + } + } +} + +func TestGetAllErrors(t *testing.T) { + ctx := context.Background() + const ( + dbPath = "projects/projectID/databases/(default)" + docPath = dbPath + "/documents/C/a" + ) + c, srv := newMock(t) + if _, err := c.GetAll(ctx, []*DocumentRef{nil}); err != errNilDocRef { + t.Errorf("got %v, want errNilDocRef", err) + } + + // Internal server error. + srv.addRPC( + &pb.BatchGetDocumentsRequest{ + Database: dbPath, + Documents: []string{docPath}, + }, + []interface{}{status.Errorf(codes.Internal, "")}, + ) + _, err := c.GetAll(ctx, []*DocumentRef{c.Doc("C/a")}) + codeEq(t, "GetAll #1", codes.Internal, err) + + // Doc appears as both found and missing (server bug). + srv.reset() + srv.addRPC( + &pb.BatchGetDocumentsRequest{ + Database: dbPath, + Documents: []string{docPath}, + }, + []interface{}{ + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Found{&pb.Document{Name: docPath}}, + ReadTime: aTimestamp, + }, + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Missing{docPath}, + ReadTime: aTimestamp, + }, + }, + ) + if _, err := c.GetAll(ctx, []*DocumentRef{c.Doc("C/a")}); err == nil { + t.Error("got nil, want error") + } +} diff --git a/vendor/cloud.google.com/go/firestore/collref.go b/vendor/cloud.google.com/go/firestore/collref.go new file mode 100644 index 0000000000..0ccea3b4ab --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/collref.go @@ -0,0 +1,114 @@ +// Copyright 2017 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 firestore + +import ( + "math/rand" + "os" + "sync" + "time" + + "golang.org/x/net/context" +) + +// A CollectionRef is a reference to Firestore collection. +type CollectionRef struct { + c *Client + + // Typically Parent.Path, or c.path if Parent is nil. + // May be different if this CollectionRef was created from a stored reference + // to a different project/DB. + parentPath string + + // Parent is the document of which this collection is a part. It is + // nil for top-level collections. + Parent *DocumentRef + + // The full resource path of the collection: "projects/P/databases/D/documents..." + Path string + + // ID is the collection identifier. + ID string + + // Use the methods of Query on a CollectionRef to create and run queries. + Query +} + +func newTopLevelCollRef(c *Client, dbPath, id string) *CollectionRef { + return &CollectionRef{ + c: c, + ID: id, + parentPath: dbPath, + Path: dbPath + "/documents/" + id, + Query: Query{c: c, collectionID: id, parentPath: dbPath}, + } +} + +func newCollRefWithParent(c *Client, parent *DocumentRef, id string) *CollectionRef { + return &CollectionRef{ + c: c, + Parent: parent, + ID: id, + parentPath: parent.Path, + Path: parent.Path + "/" + id, + Query: Query{c: c, collectionID: id, parentPath: parent.Path}, + } +} + +// Doc returns a DocumentRef that refers to the document in the collection with the +// given identifier. +func (c *CollectionRef) Doc(id string) *DocumentRef { + if c == nil { + return nil + } + return newDocRef(c, id) +} + +// NewDoc returns a DocumentRef with a uniquely generated ID. +func (c *CollectionRef) NewDoc() *DocumentRef { + return c.Doc(uniqueID()) +} + +// Add generates a DocumentRef with a unique ID. It then creates the document +// with the given data, which can be a map[string]interface{}, a struct or a +// pointer to a struct. +// +// Add returns an error in the unlikely event that a document with the same ID +// already exists. +func (c *CollectionRef) Add(ctx context.Context, data interface{}) (*DocumentRef, *WriteResult, error) { + d := c.NewDoc() + wr, err := d.Create(ctx, data) + if err != nil { + return nil, nil, err + } + return d, wr, nil +} + +const alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +var ( + rngMu sync.Mutex + rng = rand.New(rand.NewSource(time.Now().UnixNano() ^ int64(os.Getpid()))) +) + +func uniqueID() string { + var b [20]byte + rngMu.Lock() + for i := 0; i < len(b); i++ { + b[i] = alphanum[rng.Intn(len(alphanum))] + } + rngMu.Unlock() + return string(b[:]) +} diff --git a/vendor/cloud.google.com/go/firestore/collref_test.go b/vendor/cloud.google.com/go/firestore/collref_test.go new file mode 100644 index 0000000000..6450858e40 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/collref_test.go @@ -0,0 +1,97 @@ +// Copyright 2017 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 firestore + +import ( + "testing" + + "github.com/golang/protobuf/proto" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "golang.org/x/net/context" +) + +func TestDoc(t *testing.T) { + coll := testClient.Collection("C") + got := coll.Doc("d") + want := &DocumentRef{ + Parent: coll, + ID: "d", + Path: "projects/projectID/databases/(default)/documents/C/d", + } + if !testEqual(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +func TestNewDoc(t *testing.T) { + c := &Client{} + coll := c.Collection("C") + got := coll.NewDoc() + if got.Parent != coll { + t.Errorf("got %v, want %v", got.Parent, coll) + } + if len(got.ID) != 20 { + t.Errorf("got %d-char ID, wanted 20", len(got.ID)) + } + + got2 := coll.NewDoc() + if got.ID == got2.ID { + t.Error("got same ID") + } +} + +func TestAdd(t *testing.T) { + ctx := context.Background() + c, srv := newMock(t) + wantReq := commitRequestForSet() + w := wantReq.Writes[0] + w.CurrentDocument = &pb.Precondition{ + ConditionType: &pb.Precondition_Exists{false}, + } + srv.addRPCAdjust(wantReq, commitResponseForSet, func(gotReq proto.Message) { + // We can't know the doc ID before Add is called, so we take it from + // the request. + w.Operation.(*pb.Write_Update).Update.Name = gotReq.(*pb.CommitRequest).Writes[0].Operation.(*pb.Write_Update).Update.Name + }) + _, wr, err := c.Collection("C").Add(ctx, testData) + if err != nil { + t.Fatal(err) + } + if !testEqual(wr, writeResultForSet) { + t.Errorf("got %v, want %v", wr, writeResultForSet) + } +} + +func TestNilErrors(t *testing.T) { + ctx := context.Background() + c, _ := newMock(t) + // Test that a nil CollectionRef results in a nil DocumentRef and errors + // where possible. + coll := c.Collection("a/b") // nil because "a/b" denotes a doc. + if coll != nil { + t.Fatal("collection not nil") + } + if got := coll.Doc("d"); got != nil { + t.Fatalf("got %v, want nil", got) + } + if got := coll.NewDoc(); got != nil { + t.Fatalf("got %v, want nil", got) + } + if _, _, err := coll.Add(ctx, testData); err != errNilDocRef { + t.Fatalf("got <%v>, want <%v>", err, errNilDocRef) + } +} diff --git a/vendor/cloud.google.com/go/firestore/conformance_test.go b/vendor/cloud.google.com/go/firestore/conformance_test.go new file mode 100644 index 0000000000..fb7d1cf166 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/conformance_test.go @@ -0,0 +1,432 @@ +// Copyright 2017 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. + +// A runner for the conformance tests. + +package firestore + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "math" + "path" + "path/filepath" + "strings" + "testing" + "time" + + pb "cloud.google.com/go/firestore/genproto" + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + ts "github.com/golang/protobuf/ptypes/timestamp" + "github.com/google/go-cmp/cmp" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + fspb "google.golang.org/genproto/googleapis/firestore/v1beta1" +) + +const conformanceTestWatchTargetID = 1 + +func TestConformanceTests(t *testing.T) { + const dir = "testdata" + fis, err := ioutil.ReadDir(dir) + if err != nil { + t.Fatal(err) + } + wtid := watchTargetID + watchTargetID = conformanceTestWatchTargetID + defer func() { watchTargetID = wtid }() + n := 0 + for _, fi := range fis { + if strings.HasSuffix(fi.Name(), ".textproto") { + runTestFromFile(t, filepath.Join(dir, fi.Name())) + n++ + } + } + t.Logf("ran %d conformance tests", n) +} + +func runTestFromFile(t *testing.T, filename string) { + bytes, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatalf("%s: %v", filename, err) + } + var test pb.Test + if err := proto.UnmarshalText(string(bytes), &test); err != nil { + t.Fatalf("unmarshalling %s: %v", filename, err) + } + msg := fmt.Sprintf("%s (file %s)", test.Description, filepath.Base(filename)) + runTest(t, msg, &test) +} + +func runTest(t *testing.T, msg string, test *pb.Test) { + check := func(gotErr error, wantErr bool) bool { + if wantErr && gotErr == nil { + t.Errorf("%s: got nil, want error", msg) + return false + } else if !wantErr && gotErr != nil { + t.Errorf("%s: %v", msg, gotErr) + return false + } + return true + } + + ctx := context.Background() + c, srv := newMock(t) + switch tt := test.Test.(type) { + case *pb.Test_Get: + req := &fspb.BatchGetDocumentsRequest{ + Database: c.path(), + Documents: []string{tt.Get.DocRefPath}, + } + srv.addRPC(req, []interface{}{ + &fspb.BatchGetDocumentsResponse{ + Result: &fspb.BatchGetDocumentsResponse_Found{&fspb.Document{ + Name: tt.Get.DocRefPath, + CreateTime: &ts.Timestamp{}, + UpdateTime: &ts.Timestamp{}, + }}, + ReadTime: &ts.Timestamp{}, + }, + }) + ref := docRefFromPath(tt.Get.DocRefPath, c) + _, err := ref.Get(ctx) + if err != nil { + t.Errorf("%s: %v", msg, err) + return + } + // Checking response would just be testing the function converting a Document + // proto to a DocumentSnapshot, hence uninteresting. + + case *pb.Test_Create: + srv.addRPC(tt.Create.Request, commitResponseForSet) + ref := docRefFromPath(tt.Create.DocRefPath, c) + data, err := convertData(tt.Create.JsonData) + if err != nil { + t.Errorf("%s: %v", msg, err) + return + } + _, err = ref.Create(ctx, data) + check(err, tt.Create.IsError) + + case *pb.Test_Set: + srv.addRPC(tt.Set.Request, commitResponseForSet) + ref := docRefFromPath(tt.Set.DocRefPath, c) + data, err := convertData(tt.Set.JsonData) + if err != nil { + t.Errorf("%s: %v", msg, err) + return + } + var opts []SetOption + if tt.Set.Option != nil { + opts = []SetOption{convertSetOption(tt.Set.Option)} + } + _, err = ref.Set(ctx, data, opts...) + check(err, tt.Set.IsError) + + case *pb.Test_Update: + // Ignore Update test because we only support UpdatePaths. + // Not to worry, every Update test has a corresponding UpdatePaths test. + + case *pb.Test_UpdatePaths: + srv.addRPC(tt.UpdatePaths.Request, commitResponseForSet) + ref := docRefFromPath(tt.UpdatePaths.DocRefPath, c) + preconds := convertPrecondition(t, tt.UpdatePaths.Precondition) + paths := convertFieldPaths(tt.UpdatePaths.FieldPaths) + var ups []Update + for i, path := range paths { + val, err := convertJSONValue(tt.UpdatePaths.JsonValues[i]) + if err != nil { + t.Fatalf("%s: %v", msg, err) + } + ups = append(ups, Update{ + FieldPath: path, + Value: val, + }) + } + _, err := ref.Update(ctx, ups, preconds...) + check(err, tt.UpdatePaths.IsError) + + case *pb.Test_Delete: + srv.addRPC(tt.Delete.Request, commitResponseForSet) + ref := docRefFromPath(tt.Delete.DocRefPath, c) + preconds := convertPrecondition(t, tt.Delete.Precondition) + _, err := ref.Delete(ctx, preconds...) + check(err, tt.Delete.IsError) + + case *pb.Test_Query: + q := convertQuery(t, tt.Query) + got, err := q.toProto() + if check(err, tt.Query.IsError) && err == nil { + if want := tt.Query.Query; !proto.Equal(got, want) { + t.Errorf("%s\ngot: %s\nwant: %s", msg, proto.MarshalTextString(got), proto.MarshalTextString(want)) + } + } + + case *pb.Test_Listen: + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + iter := c.Collection("C").OrderBy("a", Asc).Snapshots(ctx) + var rs []interface{} + for _, r := range tt.Listen.Responses { + rs = append(rs, r) + } + srv.addRPC(&fspb.ListenRequest{ + Database: "projects/projectID/databases/(default)", + TargetChange: &fspb.ListenRequest_AddTarget{iter.ws.target}, + }, rs) + got, err := nSnapshots(iter, len(tt.Listen.Snapshots)) + if err != nil { + t.Errorf("%s: %v", msg, err) + } else if diff := cmp.Diff(got, tt.Listen.Snapshots); diff != "" { + t.Errorf("%s:\n%s", msg, diff) + } + if tt.Listen.IsError { + _, err := iter.Next() + if err == nil { + t.Errorf("%s: got nil, want error", msg) + } + } + + default: + t.Fatalf("unknown test type %T", tt) + } +} + +func nSnapshots(iter *QuerySnapshotIterator, n int) ([]*pb.Snapshot, error) { + var snaps []*pb.Snapshot + for i := 0; i < n; i++ { + diter, err := iter.Next() + if err != nil { + return snaps, err + } + s := &pb.Snapshot{ReadTime: mustTimestampProto(iter.ReadTime)} + for { + doc, err := diter.Next() + if err == iterator.Done { + break + } + if err != nil { + return snaps, err + } + s.Docs = append(s.Docs, doc.proto) + } + for _, c := range iter.Changes { + var k pb.DocChange_Kind + switch c.Kind { + case DocumentAdded: + k = pb.DocChange_ADDED + case DocumentRemoved: + k = pb.DocChange_REMOVED + case DocumentModified: + k = pb.DocChange_MODIFIED + default: + panic("bad kind") + } + s.Changes = append(s.Changes, &pb.DocChange{ + Kind: k, + Doc: c.Doc.proto, + OldIndex: int32(c.OldIndex), + NewIndex: int32(c.NewIndex), + }) + } + snaps = append(snaps, s) + } + return snaps, nil +} + +func docRefFromPath(p string, c *Client) *DocumentRef { + return &DocumentRef{ + Path: p, + ID: path.Base(p), + Parent: &CollectionRef{c: c}, + } +} + +func convertJSONValue(jv string) (interface{}, error) { + var val interface{} + if err := json.Unmarshal([]byte(jv), &val); err != nil { + return nil, err + } + return convertTestValue(val), nil +} + +func convertData(jsonData string) (map[string]interface{}, error) { + var m map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &m); err != nil { + return nil, err + } + return convertTestMap(m), nil +} + +func convertTestMap(m map[string]interface{}) map[string]interface{} { + for k, v := range m { + m[k] = convertTestValue(v) + } + return m +} + +func convertTestValue(v interface{}) interface{} { + switch v := v.(type) { + case string: + switch v { + case "ServerTimestamp": + return ServerTimestamp + case "Delete": + return Delete + case "NaN": + return math.NaN() + default: + return v + } + case float64: + if v == float64(int(v)) { + return int(v) + } + return v + case []interface{}: + for i, e := range v { + v[i] = convertTestValue(e) + } + return v + case map[string]interface{}: + return convertTestMap(v) + default: + return v + } +} + +func convertSetOption(opt *pb.SetOption) SetOption { + if opt.All { + return MergeAll + } + return Merge(convertFieldPaths(opt.Fields)...) +} + +func convertFieldPaths(fps []*pb.FieldPath) []FieldPath { + var res []FieldPath + for _, fp := range fps { + res = append(res, fp.Field) + } + return res +} + +func convertPrecondition(t *testing.T, fp *fspb.Precondition) []Precondition { + if fp == nil { + return nil + } + var pc Precondition + switch fp := fp.ConditionType.(type) { + case *fspb.Precondition_Exists: + pc = exists(fp.Exists) + case *fspb.Precondition_UpdateTime: + tm, err := ptypes.Timestamp(fp.UpdateTime) + if err != nil { + t.Fatal(err) + } + pc = LastUpdateTime(tm) + default: + t.Fatalf("unknown precondition type %T", fp) + } + return []Precondition{pc} +} + +func convertQuery(t *testing.T, qt *pb.QueryTest) Query { + parts := strings.Split(qt.CollPath, "/") + q := Query{ + parentPath: strings.Join(parts[:len(parts)-2], "/"), + collectionID: parts[len(parts)-1], + } + for _, c := range qt.Clauses { + switch c := c.Clause.(type) { + case *pb.Clause_Select: + q = q.SelectPaths(convertFieldPaths(c.Select.Fields)...) + case *pb.Clause_OrderBy: + var dir Direction + switch c.OrderBy.Direction { + case "asc": + dir = Asc + case "desc": + dir = Desc + default: + t.Fatalf("bad direction: %q", c.OrderBy.Direction) + } + q = q.OrderByPath(FieldPath(c.OrderBy.Path.Field), dir) + case *pb.Clause_Where: + val, err := convertJSONValue(c.Where.JsonValue) + if err != nil { + t.Fatal(err) + } + q = q.WherePath(FieldPath(c.Where.Path.Field), c.Where.Op, val) + case *pb.Clause_Offset: + q = q.Offset(int(c.Offset)) + case *pb.Clause_Limit: + q = q.Limit(int(c.Limit)) + case *pb.Clause_StartAt: + q = q.StartAt(convertCursor(t, c.StartAt)...) + case *pb.Clause_StartAfter: + q = q.StartAfter(convertCursor(t, c.StartAfter)...) + case *pb.Clause_EndAt: + q = q.EndAt(convertCursor(t, c.EndAt)...) + case *pb.Clause_EndBefore: + q = q.EndBefore(convertCursor(t, c.EndBefore)...) + default: + t.Fatalf("bad clause type %T", c) + } + } + return q +} + +// Returns args to a cursor method (StartAt, etc.). +func convertCursor(t *testing.T, c *pb.Cursor) []interface{} { + if c.DocSnapshot != nil { + ds, err := convertDocSnapshot(c.DocSnapshot) + if err != nil { + t.Fatal(err) + } + return []interface{}{ds} + } + var vals []interface{} + for _, jv := range c.JsonValues { + v, err := convertJSONValue(jv) + if err != nil { + t.Fatal(err) + } + vals = append(vals, v) + } + return vals +} + +func convertDocSnapshot(ds *pb.DocSnapshot) (*DocumentSnapshot, error) { + data, err := convertData(ds.JsonData) + if err != nil { + return nil, err + } + doc, transformPaths, err := toProtoDocument(data) + if err != nil { + return nil, err + } + if len(transformPaths) > 0 { + return nil, errors.New("saw transform paths in DocSnapshot") + } + return &DocumentSnapshot{ + Ref: &DocumentRef{ + Path: ds.Path, + Parent: &CollectionRef{Path: path.Dir(ds.Path)}, + }, + proto: doc, + }, nil +} diff --git a/vendor/cloud.google.com/go/firestore/doc.go b/vendor/cloud.google.com/go/firestore/doc.go new file mode 100644 index 0000000000..e3eb0d6d54 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/doc.go @@ -0,0 +1,216 @@ +// Copyright 2017 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. + +// DO NOT EDIT doc.go. Modify internal/doc.template, then run make -C internal. + +/* +Package firestore provides a client for reading and writing to a Cloud Firestore +database. + +See https://cloud.google.com/firestore/docs for an introduction +to Cloud Firestore and additional help on using the Firestore API. + +See https://godoc.org/cloud.google.com/go for authentication, timeouts, +connection pooling and similar aspects of this package. + +Note: you can't use both Cloud Firestore and Cloud Datastore in the same +project. + +Creating a Client + +To start working with this package, create a client with a project ID: + + ctx := context.Background() + client, err := firestore.NewClient(ctx, "projectID") + if err != nil { + // TODO: Handle error. + } + +CollectionRefs and DocumentRefs + +In Firestore, documents are sets of key-value pairs, and collections are groups of +documents. A Firestore database consists of a hierarchy of alternating collections +and documents, referred to by slash-separated paths like +"States/California/Cities/SanFrancisco". + +This client is built around references to collections and documents. CollectionRefs +and DocumentRefs are lightweight values that refer to the corresponding database +entities. Creating a ref does not involve any network traffic. + + states := client.Collection("States") + ny := states.Doc("NewYork") + // Or, in a single call: + ny = client.Doc("States/NewYork") + +Reading + +Use DocumentRef.Get to read a document. The result is a DocumentSnapshot. +Call its Data method to obtain the entire document contents as a map. + + docsnap, err := ny.Get(ctx) + if err != nil { + // TODO: Handle error. + } + dataMap := docsnap.Data() + fmt.Println(dataMap) + +You can also obtain a single field with DataAt, or extract the data into a struct +with DataTo. With the type definition + + type State struct { + Capital string `firestore:"capital"` + Population float64 `firestore:"pop"` // in millions + } + +we can extract the document's data into a value of type State: + + var nyData State + if err := docsnap.DataTo(&nyData); err != nil { + // TODO: Handle error. + } + +Note that this client supports struct tags beginning with "firestore:" that work like +the tags of the encoding/json package, letting you rename fields, ignore them, or +omit their values when empty. + +To retrieve multiple documents from their references in a single call, use +Client.GetAll. + + docsnaps, err := client.GetAll(ctx, []*firestore.DocumentRef{ + states.Doc("Wisconsin"), states.Doc("Ohio"), + }) + if err != nil { + // TODO: Handle error. + } + for _, ds := range docsnaps { + _ = ds // TODO: Use ds. + } + + +Writing + +For writing individual documents, use the methods on DocumentReference. +Create creates a new document. + + wr, err := ny.Create(ctx, State{ + Capital: "Albany", + Population: 19.8, + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(wr) + +The first return value is a WriteResult, which contains the time +at which the document was updated. + +Create fails if the document exists. Another method, Set, either replaces an existing +document or creates a new one. + + ca := states.Doc("California") + _, err = ca.Set(ctx, State{ + Capital: "Sacramento", + Population: 39.14, + }) + +To update some fields of an existing document, use Update. It takes a list of +paths to update and their corresponding values. + + _, err = ca.Update(ctx, []firestore.Update{{Path: "capital", Value: "Sacramento"}}) + +Use DocumentRef.Delete to delete a document. + + _, err = ny.Delete(ctx) + +Preconditions + +You can condition Deletes or Updates on when a document was last changed. Specify +these preconditions as an option to a Delete or Update method. The check and the +write happen atomically with a single RPC. + + docsnap, err = ca.Get(ctx) + if err != nil { + // TODO: Handle error. + } + _, err = ca.Update(ctx, + []firestore.Update{{Path: "capital", Value: "Sacramento"}}, + firestore.LastUpdateTime(docsnap.UpdateTime)) + +Here we update a doc only if it hasn't changed since we read it. +You could also do this with a transaction. + +To perform multiple writes at once, use a WriteBatch. Its methods chain +for convenience. + +WriteBatch.Commit sends the collected writes to the server, where they happen +atomically. + + writeResults, err := client.Batch(). + Create(ny, State{Capital: "Albany"}). + Update(ca, []firestore.Update{{Path: "capital", Value: "Sacramento"}}). + Delete(client.Doc("States/WestDakota")). + Commit(ctx) + +Queries + +You can use SQL to select documents from a collection. Begin with the collection, and +build up a query using Select, Where and other methods of Query. + + q := states.Where("pop", ">", 10).OrderBy("pop", firestore.Desc) + +Call the Query's Documents method to get an iterator, and use it like +the other Google Cloud Client iterators. + + iter := q.Documents(ctx) + defer iter.Stop() + for { + doc, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(doc.Data()) + } + +To get all the documents in a collection, you can use the collection itself +as a query. + + iter = client.Collection("States").Documents(ctx) + +Transactions + +Use a transaction to execute reads and writes atomically. All reads must happen +before any writes. Transaction creation, commit, rollback and retry are handled for +you by the Client.RunTransaction method; just provide a function and use the +read and write methods of the Transaction passed to it. + + ny := client.Doc("States/NewYork") + err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error { + doc, err := tx.Get(ny) // tx.Get, NOT ny.Get! + if err != nil { + return err + } + pop, err := doc.DataAt("pop") + if err != nil { + return err + } + return tx.Update(ny, []firestore.Update{{Path: "pop", Value: pop.(float64) + 0.2}}) + }) + if err != nil { + // TODO: Handle error. + } +*/ +package firestore diff --git a/vendor/cloud.google.com/go/firestore/docref.go b/vendor/cloud.google.com/go/firestore/docref.go new file mode 100644 index 0000000000..89f224cd49 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/docref.go @@ -0,0 +1,621 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "fmt" + "io" + "reflect" + "sort" + + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + vkit "cloud.google.com/go/firestore/apiv1beta1" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" +) + +var errNilDocRef = errors.New("firestore: nil DocumentRef") + +// A DocumentRef is a reference to a Firestore document. +type DocumentRef struct { + // The CollectionRef that this document is a part of. Never nil. + Parent *CollectionRef + + // The full resource path of the document: "projects/P/databases/D/documents..." + Path string + + // The ID of the document: the last component of the resource path. + ID string +} + +func newDocRef(parent *CollectionRef, id string) *DocumentRef { + return &DocumentRef{ + Parent: parent, + ID: id, + Path: parent.Path + "/" + id, + } +} + +// Collection returns a reference to sub-collection of this document. +func (d *DocumentRef) Collection(id string) *CollectionRef { + return newCollRefWithParent(d.Parent.c, d, id) +} + +// Get retrieves the document. If the document does not exist, Get return a NotFound error, which +// can be checked with +// grpc.Code(err) == codes.NotFound +// In that case, Get returns a non-nil DocumentSnapshot whose Exists method return false and whose +// ReadTime is the time of the failed read operation. +func (d *DocumentRef) Get(ctx context.Context) (*DocumentSnapshot, error) { + if err := checkTransaction(ctx); err != nil { + return nil, err + } + if d == nil { + return nil, errNilDocRef + } + docsnaps, err := d.Parent.c.getAll(ctx, []*DocumentRef{d}, nil) + if err != nil { + return nil, err + } + ds := docsnaps[0] + if !ds.Exists() { + return ds, status.Errorf(codes.NotFound, "%q not found", d.Path) + } + return ds, nil +} + +// Create creates the document with the given data. +// It returns an error if a document with the same ID already exists. +// +// The data argument can be a map with string keys, a struct, or a pointer to a +// struct. The map keys or exported struct fields become the fields of the firestore +// document. +// The values of data are converted to Firestore values as follows: +// +// - bool converts to Bool. +// - string converts to String. +// - int, int8, int16, int32 and int64 convert to Integer. +// - uint8, uint16 and uint32 convert to Integer. uint64 is disallowed, +// because it can represent values that cannot be represented in an int64, which +// is the underlying type of a Integer. +// - float32 and float64 convert to Double. +// - []byte converts to Bytes. +// - time.Time and *ts.Timestamp convert to Timestamp. ts is the package +// "github.com/golang/protobuf/ptypes/timestamp". +// - *latlng.LatLng converts to GeoPoint. latlng is the package +// "google.golang.org/genproto/googleapis/type/latlng". You should always use +// a pointer to a LatLng. +// - Slices convert to Array. +// - Maps and structs convert to Map. +// - nils of any type convert to Null. +// +// Pointers and interface{} are also permitted, and their elements processed +// recursively. +// +// Struct fields can have tags like those used by the encoding/json package. Tags +// begin with "firestore:" and are followed by "-", meaning "ignore this field," or +// an alternative name for the field. Following the name, these comma-separated +// options may be provided: +// +// - omitempty: Do not encode this field if it is empty. A value is empty +// if it is a zero value, or an array, slice or map of length zero. +// - serverTimestamp: The field must be of type time.Time. When writing, if +// the field has the zero value, the server will populate the stored document with +// the time that the request is processed. +func (d *DocumentRef) Create(ctx context.Context, data interface{}) (*WriteResult, error) { + ws, err := d.newCreateWrites(data) + if err != nil { + return nil, err + } + return d.Parent.c.commit1(ctx, ws) +} + +func (d *DocumentRef) newCreateWrites(data interface{}) ([]*pb.Write, error) { + if d == nil { + return nil, errNilDocRef + } + doc, serverTimestampPaths, err := toProtoDocument(data) + if err != nil { + return nil, err + } + doc.Name = d.Path + pc, err := exists(false).preconditionProto() + if err != nil { + return nil, err + } + return d.newUpdateWithTransform(doc, nil, pc, serverTimestampPaths, false), nil +} + +// Set creates or overwrites the document with the given data. See DocumentRef.Create +// for the acceptable values of data. Without options, Set overwrites the document +// completely. Specify one of the Merge options to preserve an existing document's +// fields. +func (d *DocumentRef) Set(ctx context.Context, data interface{}, opts ...SetOption) (*WriteResult, error) { + ws, err := d.newSetWrites(data, opts) + if err != nil { + return nil, err + } + return d.Parent.c.commit1(ctx, ws) +} + +func (d *DocumentRef) newSetWrites(data interface{}, opts []SetOption) ([]*pb.Write, error) { + if d == nil { + return nil, errNilDocRef + } + if data == nil { + return nil, errors.New("firestore: nil document contents") + } + if len(opts) == 0 { // Set without merge + doc, serverTimestampPaths, err := toProtoDocument(data) + if err != nil { + return nil, err + } + doc.Name = d.Path + return d.newUpdateWithTransform(doc, nil, nil, serverTimestampPaths, true), nil + } + // Set with merge. + // This is just like Update, except for the existence precondition. + // So we turn data into a list of (FieldPath, interface{}) pairs (fpv's), as we do + // for Update. + fieldPaths, allPaths, err := processSetOptions(opts) + if err != nil { + return nil, err + } + var fpvs []fpv + v := reflect.ValueOf(data) + if allPaths { + // Set with MergeAll. Collect all the leaves of the map. + if v.Kind() != reflect.Map { + return nil, errors.New("firestore: MergeAll can only be specified with map data") + } + if v.Len() == 0 { + // Special case: MergeAll with an empty map. + return d.newUpdateWithTransform(&pb.Document{Name: d.Path}, []FieldPath{}, nil, nil, true), nil + } + fpvsFromData(v, nil, &fpvs) + } else { + // Set with merge paths. Collect only the values at the given paths. + for _, fp := range fieldPaths { + val, err := getAtPath(v, fp) + if err != nil { + return nil, err + } + fpvs = append(fpvs, fpv{fp, val}) + } + } + return d.fpvsToWrites(fpvs, nil) +} + +// fpvsFromData converts v into a list of (FieldPath, value) pairs. +func fpvsFromData(v reflect.Value, prefix FieldPath, fpvs *[]fpv) { + switch v.Kind() { + case reflect.Map: + for _, k := range v.MapKeys() { + fpvsFromData(v.MapIndex(k), prefix.with(k.String()), fpvs) + } + case reflect.Interface: + fpvsFromData(v.Elem(), prefix, fpvs) + + default: + var val interface{} + if v.IsValid() { + val = v.Interface() + } + *fpvs = append(*fpvs, fpv{prefix, val}) + } +} + +// removePathsIf creates a new slice of FieldPaths that contains +// exactly those elements of fps for which pred returns false. +func removePathsIf(fps []FieldPath, pred func(FieldPath) bool) []FieldPath { + // Return fps if it's empty to preserve the distinction betweeen nil and zero-length. + if len(fps) == 0 { + return fps + } + var result []FieldPath + for _, fp := range fps { + if !pred(fp) { + result = append(result, fp) + } + } + return result +} + +// Delete deletes the document. If the document doesn't exist, it does nothing +// and returns no error. +func (d *DocumentRef) Delete(ctx context.Context, preconds ...Precondition) (*WriteResult, error) { + ws, err := d.newDeleteWrites(preconds) + if err != nil { + return nil, err + } + return d.Parent.c.commit1(ctx, ws) +} + +func (d *DocumentRef) newDeleteWrites(preconds []Precondition) ([]*pb.Write, error) { + if d == nil { + return nil, errNilDocRef + } + pc, err := processPreconditionsForDelete(preconds) + if err != nil { + return nil, err + } + return []*pb.Write{{ + Operation: &pb.Write_Delete{d.Path}, + CurrentDocument: pc, + }}, nil +} + +func (d *DocumentRef) newUpdatePathWrites(updates []Update, preconds []Precondition) ([]*pb.Write, error) { + if len(updates) == 0 { + return nil, errors.New("firestore: no paths to update") + } + var fpvs []fpv + for _, u := range updates { + v, err := u.process() + if err != nil { + return nil, err + } + fpvs = append(fpvs, v) + } + pc, err := processPreconditionsForUpdate(preconds) + if err != nil { + return nil, err + } + return d.fpvsToWrites(fpvs, pc) +} + +func (d *DocumentRef) fpvsToWrites(fpvs []fpv, pc *pb.Precondition) ([]*pb.Write, error) { + // Make sure there are no duplications or prefixes among the field paths. + var fps []FieldPath + for _, fpv := range fpvs { + fps = append(fps, fpv.fieldPath) + } + if err := checkNoDupOrPrefix(fps); err != nil { + return nil, err + } + + // Process each fpv. + var updatePaths, transformPaths []FieldPath + doc := &pb.Document{ + Name: d.Path, + Fields: map[string]*pb.Value{}, + } + for _, fpv := range fpvs { + switch fpv.value { + case Delete: + // Send the field path without a corresponding value. + updatePaths = append(updatePaths, fpv.fieldPath) + + case ServerTimestamp: + // Use the path in a transform operation. + transformPaths = append(transformPaths, fpv.fieldPath) + + default: + updatePaths = append(updatePaths, fpv.fieldPath) + // Convert the value to a proto and put it into the document. + v := reflect.ValueOf(fpv.value) + pv, sawServerTimestamp, err := toProtoValue(v) + if err != nil { + return nil, err + } + setAtPath(doc.Fields, fpv.fieldPath, pv) + // Also accumulate any serverTimestamp values within the value. + if sawServerTimestamp { + stps, err := extractTransformPaths(v, nil) + if err != nil { + return nil, err + } + for _, p := range stps { + transformPaths = append(transformPaths, fpv.fieldPath.concat(p)) + } + } + } + } + return d.newUpdateWithTransform(doc, updatePaths, pc, transformPaths, false), nil +} + +var requestTimeTransform = &pb.DocumentTransform_FieldTransform_SetToServerValue{ + pb.DocumentTransform_FieldTransform_REQUEST_TIME, +} + +// newUpdateWithTransform constructs operations for a commit. Most generally, it +// returns an update operation followed by a transform. +// +// If there are no serverTimestampPaths, the transform is omitted. +// +// If doc.Fields is empty, there are no updatePaths, and there is no precondition, +// the update is omitted, unless updateOnEmpty is true. +func (d *DocumentRef) newUpdateWithTransform(doc *pb.Document, updatePaths []FieldPath, pc *pb.Precondition, serverTimestampPaths []FieldPath, updateOnEmpty bool) []*pb.Write { + // Remove server timestamp fields from updatePaths. Those fields were removed + // from the document by toProtoDocument, so they should not be in the update + // mask. + // Note: this is technically O(n^2), but it is unlikely that there is + // more than one server timestamp path. + updatePaths = removePathsIf(updatePaths, func(fp FieldPath) bool { + return fp.in(serverTimestampPaths) + }) + var ws []*pb.Write + if updateOnEmpty || len(doc.Fields) > 0 || + len(updatePaths) > 0 || (pc != nil && len(serverTimestampPaths) == 0) { + var mask *pb.DocumentMask + if updatePaths != nil { + sfps := toServiceFieldPaths(updatePaths) + sort.Strings(sfps) // TODO(jba): make tests pass without this + mask = &pb.DocumentMask{FieldPaths: sfps} + } + w := &pb.Write{ + Operation: &pb.Write_Update{doc}, + UpdateMask: mask, + CurrentDocument: pc, + } + ws = append(ws, w) + pc = nil // If the precondition is in the write, we don't need it in the transform. + } + if len(serverTimestampPaths) > 0 || pc != nil { + ws = append(ws, d.newTransform(serverTimestampPaths, pc)) + } + return ws +} + +func (d *DocumentRef) newTransform(serverTimestampFieldPaths []FieldPath, pc *pb.Precondition) *pb.Write { + sort.Sort(byPath(serverTimestampFieldPaths)) // TODO(jba): make tests pass without this + var fts []*pb.DocumentTransform_FieldTransform + for _, p := range serverTimestampFieldPaths { + fts = append(fts, &pb.DocumentTransform_FieldTransform{ + FieldPath: p.toServiceFieldPath(), + TransformType: requestTimeTransform, + }) + } + return &pb.Write{ + Operation: &pb.Write_Transform{ + &pb.DocumentTransform{ + Document: d.Path, + FieldTransforms: fts, + // TODO(jba): should the transform have the same preconditions as the write? + }, + }, + CurrentDocument: pc, + } +} + +type sentinel int + +const ( + // Delete is used as a value in a call to Update or Set with merge to indicate + // that the corresponding key should be deleted. + Delete sentinel = iota + + // ServerTimestamp is used as a value in a call to Update to indicate that the + // key's value should be set to the time at which the server processed + // the request. + ServerTimestamp +) + +func (s sentinel) String() string { + switch s { + case Delete: + return "Delete" + case ServerTimestamp: + return "ServerTimestamp" + default: + return "" + } +} + +func isStructOrStructPtr(x interface{}) bool { + v := reflect.ValueOf(x) + if v.Kind() == reflect.Struct { + return true + } + if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { + return true + } + return false +} + +// An Update describes an update to a value referred to by a path. +// An Update should have either a non-empty Path or a non-empty FieldPath, +// but not both. +// +// See DocumentRef.Create for acceptable values. +// To delete a field, specify firestore.Delete as the value. +type Update struct { + Path string // Will be split on dots, and must not contain any of "˜*/[]". + FieldPath FieldPath + Value interface{} +} + +// An fpv is a pair of validated FieldPath and value. +type fpv struct { + fieldPath FieldPath + value interface{} +} + +func (u *Update) process() (fpv, error) { + if (u.Path != "") == (u.FieldPath != nil) { + return fpv{}, fmt.Errorf("firestore: update %+v should have exactly one of Path or FieldPath", u) + } + fp := u.FieldPath + var err error + if fp == nil { + fp, err = parseDotSeparatedString(u.Path) + if err != nil { + return fpv{}, err + } + } + if err := fp.validate(); err != nil { + return fpv{}, err + } + return fpv{fp, u.Value}, nil +} + +// Update updates the document. The values at the given +// field paths are replaced, but other fields of the stored document are untouched. +func (d *DocumentRef) Update(ctx context.Context, updates []Update, preconds ...Precondition) (*WriteResult, error) { + ws, err := d.newUpdatePathWrites(updates, preconds) + if err != nil { + return nil, err + } + return d.Parent.c.commit1(ctx, ws) +} + +// Collections returns an interator over the immediate sub-collections of the document. +func (d *DocumentRef) Collections(ctx context.Context) *CollectionIterator { + client := d.Parent.c + it := &CollectionIterator{ + err: checkTransaction(ctx), + client: client, + parent: d, + it: client.c.ListCollectionIds( + withResourceHeader(ctx, client.path()), + &pb.ListCollectionIdsRequest{Parent: d.Path}), + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// CollectionIterator is an iterator over sub-collections of a document. +type CollectionIterator struct { + client *Client + parent *DocumentRef + it *vkit.StringIterator + pageInfo *iterator.PageInfo + nextFunc func() error + items []*CollectionRef + err error +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *CollectionIterator) 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 *CollectionIterator) Next() (*CollectionRef, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *CollectionIterator) fetch(pageSize int, pageToken string) (string, error) { + if it.err != nil { + return "", it.err + } + return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error { + id, err := it.it.Next() + if err != nil { + return err + } + var cr *CollectionRef + if it.parent == nil { + cr = newTopLevelCollRef(it.client, it.client.path(), id) + } else { + cr = newCollRefWithParent(it.client, it.parent, id) + } + it.items = append(it.items, cr) + return nil + }) +} + +// GetAll returns all the collections remaining from the iterator. +func (it *CollectionIterator) GetAll() ([]*CollectionRef, error) { + var crs []*CollectionRef + for { + cr, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return nil, err + } + crs = append(crs, cr) + } + return crs, nil +} + +// Common fetch code for iterators that are backed by vkit iterators. +// TODO(jba): dedup with same function in logging/logadmin. +func iterFetch(pageSize int, pageToken string, pi *iterator.PageInfo, next func() error) (string, error) { + pi.MaxSize = pageSize + pi.Token = pageToken + // Get one item, which will fill the buffer. + if err := next(); err != nil { + return "", err + } + // Collect the rest of the buffer. + for pi.Remaining() > 0 { + if err := next(); err != nil { + return "", err + } + } + return pi.Token, nil +} + +// Snapshots returns an iterator over snapshots of the document. Each time the document +// changes or is added or deleted, a new snapshot will be generated. +func (d *DocumentRef) Snapshots(ctx context.Context) *DocumentSnapshotIterator { + return &DocumentSnapshotIterator{ + docref: d, + ws: newWatchStreamForDocument(ctx, d), + } +} + +// DocumentSnapshotIterator is an iterator over snapshots of a document. +// Call Next on the iterator to get a snapshot of the document each time it changes. +// Call Stop on the iterator when done. +// +// For an example, see DocumentRef.Snapshots. +type DocumentSnapshotIterator struct { + docref *DocumentRef + ws *watchStream +} + +// Next blocks until the document changes, then returns the DocumentSnapshot for +// the current state of the document. If the document has been deleted, Next +// returns a DocumentSnapshot whose Exists method returns false. +// +// Next never returns iterator.Done unless it is called after Stop. +func (it *DocumentSnapshotIterator) Next() (*DocumentSnapshot, error) { + btree, _, readTime, err := it.ws.nextSnapshot() + if err != nil { + if err == io.EOF { + err = iterator.Done + } + // watchStream's error is sticky, so SnapshotIterator does not need to remember it. + return nil, err + } + if btree.Len() == 0 { // document deleted + return &DocumentSnapshot{Ref: it.docref, ReadTime: readTime}, nil + } + snap, _ := btree.At(0) + return snap.(*DocumentSnapshot), nil +} + +// Stop stops receiving snapshots. +// You should always call Stop when you are done with an iterator, to free up resources. +// It is not safe to call Stop concurrently with Next. +func (it *DocumentSnapshotIterator) Stop() { + it.ws.stop() +} diff --git a/vendor/cloud.google.com/go/firestore/docref_test.go b/vendor/cloud.google.com/go/firestore/docref_test.go new file mode 100644 index 0000000000..b1847662ce --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/docref_test.go @@ -0,0 +1,312 @@ +// Copyright 2017 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 firestore + +import ( + "reflect" + "sort" + "testing" + "time" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "golang.org/x/net/context" + "google.golang.org/genproto/googleapis/type/latlng" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var ( + writeResultForSet = &WriteResult{UpdateTime: aTime} + commitResponseForSet = &pb.CommitResponse{ + WriteResults: []*pb.WriteResult{{UpdateTime: aTimestamp}}, + } +) + +func TestDocGet(t *testing.T) { + ctx := context.Background() + c, srv := newMock(t) + path := "projects/projectID/databases/(default)/documents/C/a" + pdoc := &pb.Document{ + Name: path, + CreateTime: aTimestamp, + UpdateTime: aTimestamp, + Fields: map[string]*pb.Value{"f": intval(1)}, + } + srv.addRPC(&pb.BatchGetDocumentsRequest{ + Database: c.path(), + Documents: []string{path}, + }, []interface{}{ + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Found{pdoc}, + ReadTime: aTimestamp2, + }, + }) + ref := c.Collection("C").Doc("a") + gotDoc, err := ref.Get(ctx) + if err != nil { + t.Fatal(err) + } + wantDoc := &DocumentSnapshot{ + Ref: ref, + CreateTime: aTime, + UpdateTime: aTime, + ReadTime: aTime2, + proto: pdoc, + c: c, + } + if !testEqual(gotDoc, wantDoc) { + t.Fatalf("\ngot %+v\nwant %+v", gotDoc, wantDoc) + } + + path2 := "projects/projectID/databases/(default)/documents/C/b" + srv.addRPC( + &pb.BatchGetDocumentsRequest{ + Database: c.path(), + Documents: []string{path2}, + }, []interface{}{ + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Missing{path2}, + ReadTime: aTimestamp3, + }, + }) + _, err = c.Collection("C").Doc("b").Get(ctx) + if grpc.Code(err) != codes.NotFound { + t.Errorf("got %v, want NotFound", err) + } +} + +func TestDocSet(t *testing.T) { + // Most tests for Set are in the conformance tests. + ctx := context.Background() + c, srv := newMock(t) + + doc := c.Collection("C").Doc("d") + // Merge with a struct and FieldPaths. + srv.addRPC(&pb.CommitRequest{ + Database: "projects/projectID/databases/(default)", + Writes: []*pb.Write{ + { + Operation: &pb.Write_Update{ + Update: &pb.Document{ + Name: "projects/projectID/databases/(default)/documents/C/d", + Fields: map[string]*pb.Value{ + "*": mapval(map[string]*pb.Value{ + "~": boolval(true), + }), + }, + }, + }, + UpdateMask: &pb.DocumentMask{FieldPaths: []string{"`*`.`~`"}}, + }, + }, + }, commitResponseForSet) + data := struct { + A map[string]bool `firestore:"*"` + }{A: map[string]bool{"~": true}} + wr, err := doc.Set(ctx, data, Merge([]string{"*", "~"})) + if err != nil { + t.Fatal(err) + } + if !testEqual(wr, writeResultForSet) { + t.Errorf("got %v, want %v", wr, writeResultForSet) + } + + // MergeAll cannot be used with structs. + _, err = doc.Set(ctx, data, MergeAll) + if err == nil { + t.Errorf("got nil, want error") + } +} + +func TestDocCreate(t *testing.T) { + // Verify creation with structs. In particular, make sure zero values + // are handled well. + // Other tests for Create are handled by the conformance tests. + ctx := context.Background() + c, srv := newMock(t) + + type create struct { + Time time.Time + Bytes []byte + Geo *latlng.LatLng + } + srv.addRPC( + &pb.CommitRequest{ + Database: "projects/projectID/databases/(default)", + Writes: []*pb.Write{ + { + Operation: &pb.Write_Update{ + Update: &pb.Document{ + Name: "projects/projectID/databases/(default)/documents/C/d", + Fields: map[string]*pb.Value{ + "Time": tsval(time.Time{}), + "Bytes": bytesval(nil), + "Geo": nullValue, + }, + }, + }, + CurrentDocument: &pb.Precondition{ + ConditionType: &pb.Precondition_Exists{false}, + }, + }, + }, + }, + commitResponseForSet, + ) + _, err := c.Collection("C").Doc("d").Create(ctx, &create{}) + if err != nil { + t.Fatal(err) + } +} + +func TestDocDelete(t *testing.T) { + ctx := context.Background() + c, srv := newMock(t) + srv.addRPC( + &pb.CommitRequest{ + Database: "projects/projectID/databases/(default)", + Writes: []*pb.Write{ + {Operation: &pb.Write_Delete{"projects/projectID/databases/(default)/documents/C/d"}}, + }, + }, + &pb.CommitResponse{ + WriteResults: []*pb.WriteResult{{}}, + }) + wr, err := c.Collection("C").Doc("d").Delete(ctx) + if err != nil { + t.Fatal(err) + } + if !testEqual(wr, &WriteResult{}) { + t.Errorf("got %+v, want %+v", wr, writeResultForSet) + } +} + +var ( + testData = map[string]interface{}{"a": 1} + testFields = map[string]*pb.Value{"a": intval(1)} +) + +// Update is tested by the conformance tests. + +func TestFPVsFromData(t *testing.T) { + type S struct{ X int } + + for _, test := range []struct { + in interface{} + want []fpv + }{ + { + in: nil, + want: []fpv{{nil, nil}}, + }, + { + in: map[string]interface{}{"a": nil}, + want: []fpv{{[]string{"a"}, nil}}, + }, + { + in: map[string]interface{}{"a": 1}, + want: []fpv{{[]string{"a"}, 1}}, + }, + { + in: map[string]interface{}{ + "a": 1, + "b": map[string]interface{}{"c": 2}, + }, + want: []fpv{{[]string{"a"}, 1}, {[]string{"b", "c"}, 2}}, + }, + { + in: map[string]interface{}{"s": &S{X: 3}}, + want: []fpv{{[]string{"s"}, &S{X: 3}}}, + }, + } { + var got []fpv + fpvsFromData(reflect.ValueOf(test.in), nil, &got) + sort.Sort(byFieldPath(got)) + if !testEqual(got, test.want) { + t.Errorf("%+v: got %v, want %v", test.in, got, test.want) + } + } +} + +type byFieldPath []fpv + +func (b byFieldPath) Len() int { return len(b) } +func (b byFieldPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byFieldPath) Less(i, j int) bool { return b[i].fieldPath.less(b[j].fieldPath) } + +func commitRequestForSet() *pb.CommitRequest { + return &pb.CommitRequest{ + Database: "projects/projectID/databases/(default)", + Writes: []*pb.Write{ + { + Operation: &pb.Write_Update{ + Update: &pb.Document{ + Name: "projects/projectID/databases/(default)/documents/C/d", + Fields: testFields, + }, + }, + }, + }, + } +} + +func TestUpdateProcess(t *testing.T) { + for _, test := range []struct { + in Update + want fpv + wantErr bool + }{ + { + in: Update{Path: "a", Value: 1}, + want: fpv{fieldPath: []string{"a"}, value: 1}, + }, + { + in: Update{Path: "c.d", Value: Delete}, + want: fpv{fieldPath: []string{"c", "d"}, value: Delete}, + }, + { + in: Update{FieldPath: []string{"*", "~"}, Value: ServerTimestamp}, + want: fpv{fieldPath: []string{"*", "~"}, value: ServerTimestamp}, + }, + { + in: Update{Path: "*"}, + wantErr: true, // bad rune in path + }, + { + in: Update{Path: "a", FieldPath: []string{"b"}}, + wantErr: true, // both Path and FieldPath + }, + { + in: Update{Value: 1}, + wantErr: true, // neither Path nor FieldPath + }, + { + in: Update{FieldPath: []string{"", "a"}}, + wantErr: true, // empty FieldPath component + }, + } { + got, err := test.in.process() + if test.wantErr { + if err == nil { + t.Errorf("%+v: got nil, want error", test.in) + } + } else if err != nil { + t.Errorf("%+v: got error %v, want nil", test.in, err) + } else if !testEqual(got, test.want) { + t.Errorf("%+v: got %+v, want %+v", test.in, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/firestore/document.go b/vendor/cloud.google.com/go/firestore/document.go new file mode 100644 index 0000000000..bd76d4dd99 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/document.go @@ -0,0 +1,301 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "fmt" + "reflect" + "time" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/golang/protobuf/ptypes" + tspb "github.com/golang/protobuf/ptypes/timestamp" +) + +// A DocumentSnapshot contains document data and metadata. +type DocumentSnapshot struct { + // The DocumentRef for this document. + Ref *DocumentRef + + // Read-only. The time at which the document was created. + // Increases monotonically when a document is deleted then + // recreated. It can also be compared to values from other documents and + // the read time of a query. + CreateTime time.Time + + // Read-only. The time at which the document was last changed. This value + // is initially set to CreateTime then increases monotonically with each + // change to the document. It can also be compared to values from other + // documents and the read time of a query. + UpdateTime time.Time + + // Read-only. The time at which the document was read. + ReadTime time.Time + + c *Client + proto *pb.Document +} + +// Exists reports whether the DocumentSnapshot represents an existing document. +// Even if Exists returns false, the Ref and ReadTime fields of the DocumentSnapshot +// are valid. +func (d *DocumentSnapshot) Exists() bool { + return d.proto != nil +} + +// Data returns the DocumentSnapshot's fields as a map. +// It is equivalent to +// var m map[string]interface{} +// d.DataTo(&m) +// except that it returns nil if the document does not exist. +func (d *DocumentSnapshot) Data() map[string]interface{} { + if !d.Exists() { + return nil + } + m, err := createMapFromValueMap(d.proto.Fields, d.c) + // Any error here is a bug in the client. + if err != nil { + panic(fmt.Sprintf("firestore: %v", err)) + } + return m +} + +// DataTo uses the document's fields to populate p, which can be a pointer to a +// map[string]interface{} or a pointer to a struct. +// +// Firestore field values are converted to Go values as follows: +// - Null converts to nil. +// - Bool converts to bool. +// - String converts to string. +// - Integer converts int64. When setting a struct field, any signed or unsigned +// integer type is permitted except uint64. Overflow is detected and results in +// an error. +// - Double converts to float64. When setting a struct field, float32 is permitted. +// Overflow is detected and results in an error. +// - Bytes is converted to []byte. +// - Timestamp converts to time.Time. +// - GeoPoint converts to latlng.LatLng, where latlng is the package +// "google.golang.org/genproto/googleapis/type/latlng". +// - Arrays convert to []interface{}. When setting a struct field, the field +// may be a slice or array of any type and is populated recursively. +// Slices are resized to the incoming value's size, while arrays that are too +// long have excess elements filled with zero values. If the array is too short, +// excess incoming values will be dropped. +// - Maps convert to map[string]interface{}. When setting a struct field, +// maps of key type string and any value type are permitted, and are populated +// recursively. +// - References are converted to DocumentRefs. +// +// Field names given by struct field tags are observed, as described in +// DocumentRef.Create. +// +// If the document does not exist, DataTo returns a NotFound error. +func (d *DocumentSnapshot) DataTo(p interface{}) error { + if !d.Exists() { + return status.Errorf(codes.NotFound, "document %s does not exist", d.Ref.Path) + } + return setFromProtoValue(p, &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: d.proto.Fields}}}, d.c) +} + +// DataAt returns the data value denoted by path. +// +// The path argument can be a single field or a dot-separated sequence of +// fields, and must not contain any of the runes "˜*/[]". Use DataAtPath instead for +// such a path. +// +// See DocumentSnapshot.DataTo for how Firestore values are converted to Go values. +// +// If the document does not exist, DataAt returns a NotFound error. +func (d *DocumentSnapshot) DataAt(path string) (interface{}, error) { + if !d.Exists() { + return nil, status.Errorf(codes.NotFound, "document %s does not exist", d.Ref.Path) + } + fp, err := parseDotSeparatedString(path) + if err != nil { + return nil, err + } + return d.DataAtPath(fp) +} + +// DataAtPath returns the data value denoted by the FieldPath fp. +// If the document does not exist, DataAtPath returns a NotFound error. +func (d *DocumentSnapshot) DataAtPath(fp FieldPath) (interface{}, error) { + if !d.Exists() { + return nil, status.Errorf(codes.NotFound, "document %s does not exist", d.Ref.Path) + } + v, err := valueAtPath(fp, d.proto.Fields) + if err != nil { + return nil, err + } + return createFromProtoValue(v, d.c) +} + +// valueAtPath returns the value of m referred to by fp. +func valueAtPath(fp FieldPath, m map[string]*pb.Value) (*pb.Value, error) { + for _, k := range fp[:len(fp)-1] { + v := m[k] + if v == nil { + return nil, fmt.Errorf("firestore: no field %q", k) + } + mv := v.GetMapValue() + if mv == nil { + return nil, fmt.Errorf("firestore: value for field %q is not a map", k) + } + m = mv.Fields + } + k := fp[len(fp)-1] + v := m[k] + if v == nil { + return nil, fmt.Errorf("firestore: no field %q", k) + } + return v, nil +} + +// toProtoDocument converts a Go value to a Document proto. +// Valid values are: map[string]T, struct, or pointer to a valid value. +// It also returns a list of field paths for DocumentTransform (server timestamp). +func toProtoDocument(x interface{}) (*pb.Document, []FieldPath, error) { + if x == nil { + return nil, nil, errors.New("firestore: nil document contents") + } + v := reflect.ValueOf(x) + pv, sawTransform, err := toProtoValue(v) + if err != nil { + return nil, nil, err + } + var fieldPaths []FieldPath + if sawTransform { + fieldPaths, err = extractTransformPaths(v, nil) + if err != nil { + return nil, nil, err + } + } + var fields map[string]*pb.Value + if pv != nil { + m := pv.GetMapValue() + if m == nil { + return nil, nil, fmt.Errorf("firestore: cannot convert value of type %T into a map", x) + } + fields = m.Fields + } + return &pb.Document{Fields: fields}, fieldPaths, nil +} + +func extractTransformPaths(v reflect.Value, prefix FieldPath) ([]FieldPath, error) { + switch v.Kind() { + case reflect.Map: + return extractTransformPathsFromMap(v, prefix) + case reflect.Struct: + return extractTransformPathsFromStruct(v, prefix) + case reflect.Ptr: + if v.IsNil() { + return nil, nil + } + return extractTransformPaths(v.Elem(), prefix) + case reflect.Interface: + if v.NumMethod() == 0 { // empty interface: recurse on its contents + return extractTransformPaths(v.Elem(), prefix) + } + return nil, nil + default: + return nil, nil + } +} + +func extractTransformPathsFromMap(v reflect.Value, prefix FieldPath) ([]FieldPath, error) { + var paths []FieldPath + for _, k := range v.MapKeys() { + sk := k.Interface().(string) // assume keys are strings; checked in toProtoValue + path := prefix.with(sk) + mi := v.MapIndex(k) + if mi.Interface() == ServerTimestamp { + paths = append(paths, path) + } else { + ps, err := extractTransformPaths(mi, path) + if err != nil { + return nil, err + } + paths = append(paths, ps...) + } + } + return paths, nil +} + +func extractTransformPathsFromStruct(v reflect.Value, prefix FieldPath) ([]FieldPath, error) { + var paths []FieldPath + fields, err := fieldCache.Fields(v.Type()) + if err != nil { + return nil, err + } + for _, f := range fields { + fv := v.FieldByIndex(f.Index) + path := prefix.with(f.Name) + opts := f.ParsedTag.(tagOptions) + if opts.serverTimestamp { + var isZero bool + switch f.Type { + case typeOfGoTime: + isZero = fv.Interface().(time.Time).IsZero() + case reflect.PtrTo(typeOfGoTime): + isZero = fv.IsNil() || fv.Elem().Interface().(time.Time).IsZero() + default: + return nil, fmt.Errorf("firestore: field %s of struct %s with serverTimestamp tag must be of type time.Time or *time.Time", + f.Name, v.Type()) + } + if isZero { + paths = append(paths, path) + } + } else { + ps, err := extractTransformPaths(fv, path) + if err != nil { + return nil, err + } + paths = append(paths, ps...) + } + } + return paths, nil +} + +func newDocumentSnapshot(ref *DocumentRef, proto *pb.Document, c *Client, readTime *tspb.Timestamp) (*DocumentSnapshot, error) { + d := &DocumentSnapshot{ + Ref: ref, + c: c, + proto: proto, + } + if proto != nil { + ts, err := ptypes.Timestamp(proto.CreateTime) + if err != nil { + return nil, err + } + d.CreateTime = ts + ts, err = ptypes.Timestamp(proto.UpdateTime) + if err != nil { + return nil, err + } + d.UpdateTime = ts + } + if readTime != nil { + ts, err := ptypes.Timestamp(readTime) + if err != nil { + return nil, err + } + d.ReadTime = ts + } + return d, nil +} diff --git a/vendor/cloud.google.com/go/firestore/document_test.go b/vendor/cloud.google.com/go/firestore/document_test.go new file mode 100644 index 0000000000..3c4f5e96e0 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/document_test.go @@ -0,0 +1,239 @@ +// Copyright 2017 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 firestore + +import ( + "reflect" + "sort" + "testing" + "time" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + tspb "github.com/golang/protobuf/ptypes/timestamp" +) + +func TestToProtoDocument(t *testing.T) { + type s struct{ I int } + + for _, test := range []struct { + in interface{} + want *pb.Document + wantErr bool + }{ + {nil, nil, true}, + {[]int{1}, nil, true}, + {map[string]int{"a": 1}, + &pb.Document{Fields: map[string]*pb.Value{"a": intval(1)}}, + false}, + {s{2}, &pb.Document{Fields: map[string]*pb.Value{"I": intval(2)}}, false}, + {&s{3}, &pb.Document{Fields: map[string]*pb.Value{"I": intval(3)}}, false}, + } { + got, _, gotErr := toProtoDocument(test.in) + if (gotErr != nil) != test.wantErr { + t.Errorf("%v: got error %v, want %t", test.in, gotErr, test.wantErr) + } + if gotErr != nil { + continue + } + if !testEqual(got, test.want) { + t.Errorf("%v: got %v, want %v", test.in, got, test.want) + } + } +} + +func TestNewDocumentSnapshot(t *testing.T) { + c := &Client{ + projectID: "projID", + databaseID: "(database)", + } + docRef := c.Doc("C/a") + in := &pb.Document{ + CreateTime: &tspb.Timestamp{Seconds: 10}, + UpdateTime: &tspb.Timestamp{Seconds: 20}, + Fields: map[string]*pb.Value{"a": intval(1)}, + } + want := &DocumentSnapshot{ + Ref: docRef, + CreateTime: time.Unix(10, 0).UTC(), + UpdateTime: time.Unix(20, 0).UTC(), + ReadTime: aTime, + proto: in, + c: c, + } + got, err := newDocumentSnapshot(docRef, in, c, aTimestamp) + if err != nil { + t.Fatal(err) + } + if !testEqual(got, want) { + t.Errorf("got %+v\nwant %+v", got, want) + } +} + +func TestData(t *testing.T) { + doc := &DocumentSnapshot{ + proto: &pb.Document{ + Fields: map[string]*pb.Value{"a": intval(1), "b": strval("x")}, + }, + } + got := doc.Data() + want := map[string]interface{}{"a": int64(1), "b": "x"} + if !testEqual(got, want) { + t.Errorf("got %#v\nwant %#v", got, want) + } + var got2 map[string]interface{} + if err := doc.DataTo(&got2); err != nil { + t.Fatal(err) + } + if !testEqual(got2, want) { + t.Errorf("got %#v\nwant %#v", got2, want) + } + + type s struct { + A int + B string + } + var got3 s + if err := doc.DataTo(&got3); err != nil { + t.Fatal(err) + } + want2 := s{A: 1, B: "x"} + if !testEqual(got3, want2) { + t.Errorf("got %#v\nwant %#v", got3, want2) + } +} + +var testDoc = &DocumentSnapshot{ + proto: &pb.Document{ + Fields: map[string]*pb.Value{ + "a": intval(1), + "b": mapval(map[string]*pb.Value{ + "`": intval(2), + "~": mapval(map[string]*pb.Value{ + "x": intval(3), + }), + }), + }, + }, +} + +func TestDataAt(t *testing.T) { + for _, test := range []struct { + fieldPath string + want interface{} + }{ + {"a", int64(1)}, + {"b.`", int64(2)}, + } { + got, err := testDoc.DataAt(test.fieldPath) + if err != nil { + t.Errorf("%q: %v", test.fieldPath, err) + continue + } + if !testEqual(got, test.want) { + t.Errorf("%q: got %v, want %v", test.fieldPath, got, test.want) + } + } + + for _, bad := range []string{ + "c.~.x", // bad field path + "a.b", // "a" isn't a map + "z.b", // bad non-final key + "b.z", // bad final key + } { + _, err := testDoc.DataAt(bad) + if err == nil { + t.Errorf("%q: got nil, want error", bad) + } + } +} + +func TestDataAtPath(t *testing.T) { + for _, test := range []struct { + fieldPath FieldPath + want interface{} + }{ + {[]string{"a"}, int64(1)}, + {[]string{"b", "`"}, int64(2)}, + {[]string{"b", "~"}, map[string]interface{}{"x": int64(3)}}, + {[]string{"b", "~", "x"}, int64(3)}, + } { + got, err := testDoc.DataAtPath(test.fieldPath) + if err != nil { + t.Errorf("%v: %v", test.fieldPath, err) + continue + } + if !testEqual(got, test.want) { + t.Errorf("%v: got %v, want %v", test.fieldPath, got, test.want) + } + } + + for _, bad := range []FieldPath{ + []string{"c", "", "x"}, // bad field path + []string{"a", "b"}, // "a" isn't a map + []string{"z", "~"}, // bad non-final key + []string{"b", "z"}, // bad final key + } { + _, err := testDoc.DataAtPath(bad) + if err == nil { + t.Errorf("%v: got nil, want error", bad) + } + } +} + +func TestExtractTransformPaths(t *testing.T) { + type S struct { + A time.Time `firestore:",serverTimestamp"` + B time.Time `firestore:",serverTimestamp"` + C *time.Time `firestore:",serverTimestamp"` + D *time.Time `firestore:"d.d,serverTimestamp"` + E *time.Time `firestore:",serverTimestamp"` + F time.Time + G int + } + + m := map[string]interface{}{ + "x": 1, + "y": &S{ + // A is a zero time: included + B: aTime, // not a zero time: excluded + // C is nil: included + D: &time.Time{}, // pointer to a zero time: included + E: &aTime, // pointer to a non-zero time: excluded + // F is a zero time, but does not have the right tag: excluded + G: 15, // not a time.Time + }, + "z": map[string]interface{}{"w": ServerTimestamp}, + } + got, err := extractTransformPaths(reflect.ValueOf(m), nil) + if err != nil { + t.Fatal(err) + } + sort.Sort(byPath(got)) + want := []FieldPath{{"y", "A"}, {"y", "C"}, {"y", "d.d"}, {"z", "w"}} + if !testEqual(got, want) { + t.Errorf("got %#v, want %#v", got, want) + } +} + +func TestExtractTransformPathsErrors(t *testing.T) { + type S struct { + A int `firestore:",serverTimestamp"` + } + _, err := extractTransformPaths(reflect.ValueOf(S{}), nil) + if err == nil { + t.Error("got nil, want error") + } +} diff --git a/vendor/cloud.google.com/go/firestore/examples_test.go b/vendor/cloud.google.com/go/firestore/examples_test.go new file mode 100644 index 0000000000..2ee9f28834 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/examples_test.go @@ -0,0 +1,528 @@ +// Copyright 2017 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. + +// TODO(jba): add Output comments to examples when feasible. + +package firestore_test + +import ( + "fmt" + + "cloud.google.com/go/firestore" + "golang.org/x/net/context" + + "google.golang.org/api/iterator" +) + +func ExampleNewClient() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() // Close client when done. + _ = client // TODO: Use client. +} + +func ExampleClient_Collection() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + coll1 := client.Collection("States") + coll2 := client.Collection("States/NewYork/Cities") + fmt.Println(coll1, coll2) +} + +func ExampleClient_Doc() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + doc1 := client.Doc("States/NewYork") + doc2 := client.Doc("States/NewYork/Cities/Albany") + fmt.Println(doc1, doc2) +} + +func ExampleClient_GetAll() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + docs, err := client.GetAll(ctx, []*firestore.DocumentRef{ + client.Doc("States/NorthCarolina"), + client.Doc("States/SouthCarolina"), + client.Doc("States/WestCarolina"), + client.Doc("States/EastCarolina"), + }) + if err != nil { + // TODO: Handle error. + } + // docs is a slice with four DocumentSnapshots, but the last two are + // nil because there is no West or East Carolina. + fmt.Println(docs) +} + +func ExampleClient_Batch() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + b := client.Batch() + _ = b // TODO: Use batch. +} + +func ExampleWriteBatch_Commit() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + type State struct { + Capital string `firestore:"capital"` + Population float64 `firestore:"pop"` // in millions + } + + ny := client.Doc("States/NewYork") + ca := client.Doc("States/California") + + writeResults, err := client.Batch(). + Create(ny, State{Capital: "Albany", Population: 19.8}). + Set(ca, State{Capital: "Sacramento", Population: 39.14}). + Delete(client.Doc("States/WestDakota")). + Commit(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Println(writeResults) +} + +func ExampleCollectionRef_Add() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + doc, wr, err := client.Collection("Users").Add(ctx, map[string]interface{}{ + "name": "Alice", + "email": "aj@example.com", + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(doc, wr) +} + +func ExampleCollectionRef_Doc() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + fl := client.Collection("States").Doc("Florida") + ta := client.Collection("States").Doc("Florida/Cities/Tampa") + + fmt.Println(fl, ta) +} + +func ExampleCollectionRef_NewDoc() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + doc := client.Collection("Users").NewDoc() + + fmt.Println(doc) +} + +func ExampleDocumentRef_Collection() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + mi := client.Collection("States").Doc("Michigan") + cities := mi.Collection("Cities") + + fmt.Println(cities) +} + +func ExampleDocumentRef_Create_map() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + wr, err := client.Doc("States/Colorado").Create(ctx, map[string]interface{}{ + "capital": "Denver", + "pop": 5.5, + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(wr.UpdateTime) +} + +func ExampleDocumentRef_Create_struct() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + type State struct { + Capital string `firestore:"capital"` + Population float64 `firestore:"pop"` // in millions + } + + wr, err := client.Doc("States/Colorado").Create(ctx, State{ + Capital: "Denver", + Population: 5.5, + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(wr.UpdateTime) +} + +func ExampleDocumentRef_Set() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + // Overwrite the document with the given data. Any other fields currently + // in the document will be removed. + wr, err := client.Doc("States/Alabama").Set(ctx, map[string]interface{}{ + "capital": "Montgomery", + "pop": 4.9, + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(wr.UpdateTime) +} + +func ExampleDocumentRef_Set_merge() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + // Overwrite only the fields in the map; preserve all others. + _, err = client.Doc("States/Alabama").Set(ctx, map[string]interface{}{ + "pop": 5.2, + }, firestore.MergeAll) + if err != nil { + // TODO: Handle error. + } + + type State struct { + Capital string `firestore:"capital"` + Population float64 `firestore:"pop"` // in millions + } + + // To do a merging Set with struct data, specify the exact fields to overwrite. + // MergeAll is disallowed here, because it would probably be a mistake: the "capital" + // field would be overwritten with the empty string. + _, err = client.Doc("States/Alabama").Set(ctx, State{Population: 5.2}, firestore.Merge([]string{"pop"})) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleDocumentRef_Update() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + tenn := client.Doc("States/Tennessee") + wr, err := tenn.Update(ctx, []firestore.Update{ + {Path: "pop", Value: 6.6}, + {FieldPath: []string{".", "*", "/"}, Value: "odd"}, + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(wr.UpdateTime) +} + +func ExampleDocumentRef_Delete() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + // Oops, Ontario is a Canadian province... + if _, err = client.Doc("States/Ontario").Delete(ctx); err != nil { + // TODO: Handle error. + } +} + +func ExampleDocumentRef_Get() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + docsnap, err := client.Doc("States/Ohio").Get(ctx) + if err != nil { + // TODO: Handle error. + } + _ = docsnap // TODO: Use DocumentSnapshot. +} + +func ExampleDocumentRef_Snapshots() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + iter := client.Doc("States/Idaho").Snapshots(ctx) + defer iter.Stop() + for { + docsnap, err := iter.Next() + if err != nil { + // TODO: Handle error. + } + _ = docsnap // TODO: Use DocumentSnapshot. + } +} + +func ExampleDocumentSnapshot_Data() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + docsnap, err := client.Doc("States/Ohio").Get(ctx) + if err != nil { + // TODO: Handle error. + } + ohioMap := docsnap.Data() + fmt.Println(ohioMap["capital"]) +} + +func ExampleDocumentSnapshot_DataAt() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + docsnap, err := client.Doc("States/Ohio").Get(ctx) + if err != nil { + // TODO: Handle error. + } + cap, err := docsnap.DataAt("capital") + if err != nil { + // TODO: Handle error. + } + fmt.Println(cap) +} + +func ExampleDocumentSnapshot_DataAtPath() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + docsnap, err := client.Doc("States/Ohio").Get(ctx) + if err != nil { + // TODO: Handle error. + } + pop, err := docsnap.DataAtPath([]string{"capital", "population"}) + if err != nil { + // TODO: Handle error. + } + fmt.Println(pop) +} + +func ExampleDocumentSnapshot_DataTo() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + docsnap, err := client.Doc("States/Ohio").Get(ctx) + if err != nil { + // TODO: Handle error. + } + + type State struct { + Capital string `firestore:"capital"` + Population float64 `firestore:"pop"` // in millions + } + + var s State + if err := docsnap.DataTo(&s); err != nil { + // TODO: Handle error. + } + fmt.Println(s) +} + +func ExampleQuery_Documents() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + q := client.Collection("States").Select("pop"). + Where("pop", ">", 10). + OrderBy("pop", firestore.Desc). + Limit(10) + iter1 := q.Documents(ctx) + _ = iter1 // TODO: Use iter1. + + // You can call Documents directly on a CollectionRef as well. + iter2 := client.Collection("States").Documents(ctx) + _ = iter2 // TODO: Use iter2. +} + +// This example is just like the one above, but illustrates +// how to use the XXXPath methods of Query for field paths +// that can't be expressed as a dot-separated string. +func ExampleQuery_Documents_path_methods() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + q := client.Collection("Unusual").SelectPaths([]string{"*"}, []string{"[~]"}). + WherePath([]string{"/"}, ">", 10). + OrderByPath([]string{"/"}, firestore.Desc). + Limit(10) + iter1 := q.Documents(ctx) + _ = iter1 // TODO: Use iter1. + + // You can call Documents directly on a CollectionRef as well. + iter2 := client.Collection("States").Documents(ctx) + _ = iter2 // TODO: Use iter2. +} + +func ExampleDocumentIterator_Next() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + q := client.Collection("States"). + Where("pop", ">", 10). + OrderBy("pop", firestore.Desc) + iter := q.Documents(ctx) + defer iter.Stop() + for { + doc, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(doc.Data()) + } +} + +func ExampleDocumentIterator_GetAll() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + q := client.Collection("States"). + Where("pop", ">", 10). + OrderBy("pop", firestore.Desc). + Limit(10) // a good idea with GetAll, to avoid filling memory + docs, err := q.Documents(ctx).GetAll() + if err != nil { + // TODO: Handle error. + } + for _, doc := range docs { + fmt.Println(doc.Data()) + } +} + +func ExampleClient_RunTransaction() { + ctx := context.Background() + client, err := firestore.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + + nm := client.Doc("States/NewMexico") + err = client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error { + doc, err := tx.Get(nm) // tx.Get, NOT nm.Get! + if err != nil { + return err + } + pop, err := doc.DataAt("pop") + if err != nil { + return err + } + return tx.Update(nm, []firestore.Update{{Path: "pop", Value: pop.(float64) + 0.2}}) + }) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/firestore/fieldpath.go b/vendor/cloud.google.com/go/firestore/fieldpath.go new file mode 100644 index 0000000000..128e2e3db6 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/fieldpath.go @@ -0,0 +1,275 @@ +// Copyright 2017 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 firestore + +import ( + "bytes" + "errors" + "fmt" + "reflect" + "regexp" + "sort" + "strings" + + "cloud.google.com/go/internal/atomiccache" + "cloud.google.com/go/internal/fields" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" +) + +// A FieldPath is a non-empty sequence of non-empty fields that reference a value. +// +// A FieldPath value should only be necessary if one of the field names contains +// one of the runes ".˜*/[]". Most methods accept a simpler form of field path +// as a string in which the individual fields are separated by dots. +// For example, +// []string{"a", "b"} +// is equivalent to the string form +// "a.b" +// but +// []string{"*"} +// has no equivalent string form. +type FieldPath []string + +// parseDotSeparatedString constructs a FieldPath from a string that separates +// path components with dots. Other than splitting at dots and checking for invalid +// characters, it ignores everything else about the string, +// including attempts to quote field path compontents. So "a.`b.c`.d" is parsed into +// four parts, "a", "`b", "c`" and "d". +func parseDotSeparatedString(s string) (FieldPath, error) { + const invalidRunes = "~*/[]" + if strings.ContainsAny(s, invalidRunes) { + return nil, fmt.Errorf("firestore: %q contains an invalid rune (one of %s)", s, invalidRunes) + } + fp := FieldPath(strings.Split(s, ".")) + if err := fp.validate(); err != nil { + return nil, err + } + return fp, nil +} + +func (fp1 FieldPath) equal(fp2 FieldPath) bool { + if len(fp1) != len(fp2) { + return false + } + for i, c1 := range fp1 { + if c1 != fp2[i] { + return false + } + } + return true +} + +func (fp1 FieldPath) prefixOf(fp2 FieldPath) bool { + return len(fp1) <= len(fp2) && fp1.equal(fp2[:len(fp1)]) +} + +// Lexicographic ordering. +func (fp1 FieldPath) less(fp2 FieldPath) bool { + for i := range fp1 { + switch { + case i >= len(fp2): + return false + case fp1[i] < fp2[i]: + return true + case fp1[i] > fp2[i]: + return false + } + } + // fp1 and fp2 are equal up to len(fp1). + return len(fp1) < len(fp2) +} + +// validate checks the validity of fp and returns an error if it is invalid. +func (fp FieldPath) validate() error { + if len(fp) == 0 { + return errors.New("firestore: empty field path") + } + for _, c := range fp { + if len(c) == 0 { + return errors.New("firestore: empty component in field path") + } + } + return nil +} + +// with creates a new FieldPath consisting of fp followed by k. +func (fp FieldPath) with(k string) FieldPath { + r := make(FieldPath, len(fp), len(fp)+1) + copy(r, fp) + return append(r, k) +} + +// concat creates a new FieldPath consisting of fp1 followed by fp2. +func (fp1 FieldPath) concat(fp2 FieldPath) FieldPath { + r := make(FieldPath, len(fp1)+len(fp2)) + copy(r, fp1) + copy(r[len(fp1):], fp2) + return r +} + +// in reports whether fp is equal to one of the fps. +func (fp FieldPath) in(fps []FieldPath) bool { + for _, e := range fps { + if fp.equal(e) { + return true + } + } + return false +} + +// checkNoDupOrPrefix checks whether any FieldPath is a prefix of (or equal to) +// another. +// It modifies the order of FieldPaths in its argument (via sorting). +func checkNoDupOrPrefix(fps []FieldPath) error { + // Sort fps lexicographically. + sort.Sort(byPath(fps)) + // Check adjacent pairs for prefix. + for i := 1; i < len(fps); i++ { + if fps[i-1].prefixOf(fps[i]) { + return fmt.Errorf("field path %v cannot be used in the same update as %v", fps[i-1], fps[i]) + } + } + return nil +} + +type byPath []FieldPath + +func (b byPath) Len() int { return len(b) } +func (b byPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byPath) Less(i, j int) bool { return b[i].less(b[j]) } + +// setAtPath sets val at the location in m specified by fp, creating sub-maps as +// needed. m must not be nil. fp is assumed to be valid. +func setAtPath(m map[string]*pb.Value, fp FieldPath, val *pb.Value) { + if val == nil { + return + } + if len(fp) == 1 { + m[fp[0]] = val + } else { + v, ok := m[fp[0]] + if !ok { + v = &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: map[string]*pb.Value{}}}} + m[fp[0]] = v + } + // The type assertion below cannot fail, because setAtPath is only called + // with either an empty map or one filled by setAtPath itself, and the + // set of FieldPaths it is called with has been checked to make sure that + // no path is the prefix of any other. + setAtPath(v.GetMapValue().Fields, fp[1:], val) + } +} + +// getAtPath gets the value in data referred to by fp. The data argument can +// be a map or a struct. +// Compare with valueAtPath, which does the same thing for a document. +func getAtPath(v reflect.Value, fp FieldPath) (interface{}, error) { + var err error + for _, k := range fp { + v, err = getAtField(v, k) + if err != nil { + return nil, err + } + } + return v.Interface(), nil +} + +// getAtField returns the equivalent of v[k], if v is a map, or v.k if v is a struct. +func getAtField(v reflect.Value, k string) (reflect.Value, error) { + switch v.Kind() { + case reflect.Map: + if r := v.MapIndex(reflect.ValueOf(k)); r.IsValid() { + return r, nil + } + + case reflect.Struct: + fm, err := fieldMap(v.Type()) + if err != nil { + return reflect.Value{}, err + } + if f, ok := fm[k]; ok { + return v.FieldByIndex(f.Index), nil + } + + case reflect.Interface: + return getAtField(v.Elem(), k) + + case reflect.Ptr: + return getAtField(v.Elem(), k) + } + return reflect.Value{}, fmt.Errorf("firestore: no field %q for value %#v", k, v) +} + +// fieldMapCache holds maps from from Firestore field name to struct field, +// keyed by struct type. +// TODO(jba): replace with sync.Map for Go 1.9. +var fieldMapCache atomiccache.Cache + +func fieldMap(t reflect.Type) (map[string]fields.Field, error) { + x := fieldMapCache.Get(t, func() interface{} { + fieldList, err := fieldCache.Fields(t) + if err != nil { + return err + } + m := map[string]fields.Field{} + for _, f := range fieldList { + m[f.Name] = f + } + return m + }) + if err, ok := x.(error); ok { + return nil, err + } + return x.(map[string]fields.Field), nil +} + +// toServiceFieldPath converts fp the form required by the Firestore service. +// It assumes fp has been validated. +func (fp FieldPath) toServiceFieldPath() string { + cs := make([]string, len(fp)) + for i, c := range fp { + cs[i] = toServiceFieldPathComponent(c) + } + return strings.Join(cs, ".") +} + +func toServiceFieldPaths(fps []FieldPath) []string { + var sfps []string + for _, fp := range fps { + sfps = append(sfps, fp.toServiceFieldPath()) + } + return sfps +} + +// Google SQL syntax for an unquoted field. +var unquotedFieldRegexp = regexp.MustCompile("^[A-Za-z_][A-Za-z_0-9]*$") + +// toServiceFieldPathComponent returns a string that represents key and is a valid +// field path component. +func toServiceFieldPathComponent(key string) string { + if unquotedFieldRegexp.MatchString(key) { + return key + } + var buf bytes.Buffer + buf.WriteRune('`') + for _, r := range key { + if r == '`' || r == '\\' { + buf.WriteRune('\\') + } + buf.WriteRune(r) + } + buf.WriteRune('`') + return buf.String() +} diff --git a/vendor/cloud.google.com/go/firestore/fieldpath_test.go b/vendor/cloud.google.com/go/firestore/fieldpath_test.go new file mode 100644 index 0000000000..2c8072fdcc --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/fieldpath_test.go @@ -0,0 +1,232 @@ +// Copyright 2017 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 firestore + +import ( + "reflect" + "strings" + "testing" + + "cloud.google.com/go/internal/testutil" +) + +func TestFieldPathValidate(t *testing.T) { + for _, in := range [][]string{nil, {}, {"a", "", "b"}} { + if err := FieldPath(in).validate(); err == nil { + t.Errorf("%v: want error, got nil", in) + } + } +} + +func TestFieldPathLess(t *testing.T) { + for _, test := range []struct { + in1, in2 string + want bool + }{ + {"a b", "a b", false}, + {"a", "b", true}, + {"b", "a", false}, + {"a", "a b", true}, + {"a b", "a", false}, + {"a b c", "a b d", true}, + {"a b d", "a b c", false}, + } { + fp1 := FieldPath(strings.Fields(test.in1)) + fp2 := FieldPath(strings.Fields(test.in2)) + got := fp1.less(fp2) + if got != test.want { + t.Errorf("%q.less(%q): got %t, want %t", test.in1, test.in2, got, test.want) + } + } +} + +func TestCheckForPrefix(t *testing.T) { + for _, test := range []struct { + in []string // field paths as space-separated strings + wantErr bool + }{ + {in: []string{"a", "b", "c"}, wantErr: false}, + {in: []string{"a b", "b", "c d"}, wantErr: false}, + {in: []string{"a b", "a c", "a d"}, wantErr: false}, + {in: []string{"a b", "b", "b d"}, wantErr: true}, + {in: []string{"a b", "b", "b d"}, wantErr: true}, + {in: []string{"b c d", "c d", "b c"}, wantErr: true}, + } { + var fps []FieldPath + for _, s := range test.in { + fps = append(fps, strings.Fields(s)) + } + err := checkNoDupOrPrefix(fps) + if got, want := (err != nil), test.wantErr; got != want { + t.Errorf("%#v: got '%v', want %t", test.in, err, want) + } + } +} + +func TestGetAtPath(t *testing.T) { + type S struct { + X int + Y int `firestore:"y"` + M map[string]interface{} + Next *S + } + + const fail = "ERROR" // value for expected error + + for _, test := range []struct { + val interface{} + fp FieldPath + want interface{} + }{ + { + val: map[string]int(nil), + fp: nil, + want: map[string]int(nil), + }, + { + val: 1, + fp: nil, + want: 1, + }, + { + val: 1, + fp: []string{"a"}, + want: fail, + }, + { + val: map[string]int{"a": 2}, + fp: []string{"a"}, + want: 2, + }, + { + val: map[string]int{"a": 2}, + fp: []string{"b"}, + want: fail, + }, + { + val: map[string]interface{}{"a": map[string]int{"b": 3}}, + fp: []string{"a", "b"}, + want: 3, + }, + { + val: map[string]interface{}{"a": map[string]int{"b": 3}}, + fp: []string{"a", "b", "c"}, + want: fail, + }, + { + val: S{X: 1, Y: 2}, + fp: nil, + want: S{X: 1, Y: 2}, + }, + { + val: S{X: 1, Y: 2}, + fp: []string{"X"}, + want: 1, + }, + { + val: S{X: 1, Y: 2}, + fp: []string{"Y"}, + want: fail, // because Y is tagged with name "y" + }, + { + val: S{X: 1, Y: 2}, + fp: []string{"y"}, + want: 2, + }, + { + val: &S{X: 1}, + fp: []string{"X"}, + want: 1, + }, + { + val: &S{X: 1, Next: nil}, + fp: []string{"Next"}, + want: (*S)(nil), + }, + { + val: &S{X: 1, Next: nil}, + fp: []string{"Next", "Next"}, + want: fail, + }, + { + val: map[string]S{"a": {X: 1, Y: 2}}, + fp: []string{"a", "y"}, + want: 2, + }, + { + val: map[string]S{"a": {X: 1, Y: 2}}, + fp: []string{"a", "z"}, + want: fail, + }, + { + val: map[string]*S{ + "a": { + M: map[string]interface{}{ + "b": S{ + Next: &S{ + X: 17, + }, + }, + }, + }, + }, + fp: []string{"a", "M", "b", "Next", "X"}, + want: 17, + }, + } { + got, err := getAtPath(reflect.ValueOf(test.val), test.fp) + if err != nil && test.want != fail { + t.Errorf("%+v: got error <%v>, want nil", test, err) + } + if err == nil && !testutil.Equal(got, test.want) { + t.Errorf("%+v: got %v, want %v, want nil", test, got, test.want) + } + } +} + +func TestToServiceFieldPath(t *testing.T) { + for _, test := range []struct { + in FieldPath + want string + }{ + {[]string{"a"}, "a"}, + {[]string{"a", "b"}, "a.b"}, + {[]string{"a.", "[b*", "c2"}, "`a.`.`[b*`.c2"}, + {[]string{"`a", `b\`}, "`\\`a`.`b\\\\`"}, + } { + got := test.in.toServiceFieldPath() + if got != test.want { + t.Errorf("%v: got %s, want %s", test.in, got, test.want) + } + } +} + +func TestToServiceFieldPathComponent(t *testing.T) { + for _, test := range []struct { + in, want string + }{ + {"", "``"}, + {"clam_chowder23", "clam_chowder23"}, + {"23skidoo", "`23skidoo`"}, + {"bak`tik", "`bak\\`tik`"}, + {"a\\b", "`a\\\\b`"}, + {"dots.are.confusing", "`dots.are.confusing`"}, + } { + got := toServiceFieldPathComponent(test.in) + if got != test.want { + t.Errorf("%q: got %q, want %q", test.in, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/firestore/from_value.go b/vendor/cloud.google.com/go/firestore/from_value.go new file mode 100644 index 0000000000..7c8aa03269 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/from_value.go @@ -0,0 +1,431 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "fmt" + "reflect" + "strings" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "github.com/golang/protobuf/ptypes" +) + +func setFromProtoValue(x interface{}, vproto *pb.Value, c *Client) error { + v := reflect.ValueOf(x) + if v.Kind() != reflect.Ptr || v.IsNil() { + return errors.New("firestore: nil or not a pointer") + } + return setReflectFromProtoValue(v.Elem(), vproto, c) +} + +// setReflectFromProtoValue sets v from a Firestore Value. +// v must be a settable value. +func setReflectFromProtoValue(v reflect.Value, vproto *pb.Value, c *Client) error { + typeErr := func() error { + return fmt.Errorf("firestore: cannot set type %s to %s", v.Type(), typeString(vproto)) + } + + val := vproto.ValueType + // A Null value sets anything nullable to nil, and has no effect + // on anything else. + if _, ok := val.(*pb.Value_NullValue); ok { + switch v.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + v.Set(reflect.Zero(v.Type())) + } + return nil + } + + // Handle special types first. + switch v.Type() { + case typeOfByteSlice: + x, ok := val.(*pb.Value_BytesValue) + if !ok { + return typeErr() + } + v.SetBytes(x.BytesValue) + return nil + + case typeOfGoTime: + x, ok := val.(*pb.Value_TimestampValue) + if !ok { + return typeErr() + } + t, err := ptypes.Timestamp(x.TimestampValue) + if err != nil { + return err + } + v.Set(reflect.ValueOf(t)) + return nil + + case typeOfProtoTimestamp: + x, ok := val.(*pb.Value_TimestampValue) + if !ok { + return typeErr() + } + v.Set(reflect.ValueOf(x.TimestampValue)) + return nil + + case typeOfLatLng: + x, ok := val.(*pb.Value_GeoPointValue) + if !ok { + return typeErr() + } + v.Set(reflect.ValueOf(x.GeoPointValue)) + return nil + + case typeOfDocumentRef: + x, ok := val.(*pb.Value_ReferenceValue) + if !ok { + return typeErr() + } + dr, err := pathToDoc(x.ReferenceValue, c) + if err != nil { + return err + } + v.Set(reflect.ValueOf(dr)) + return nil + } + + switch v.Kind() { + case reflect.Bool: + x, ok := val.(*pb.Value_BooleanValue) + if !ok { + return typeErr() + } + v.SetBool(x.BooleanValue) + + case reflect.String: + x, ok := val.(*pb.Value_StringValue) + if !ok { + return typeErr() + } + v.SetString(x.StringValue) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + var i int64 + switch x := val.(type) { + case *pb.Value_IntegerValue: + i = x.IntegerValue + case *pb.Value_DoubleValue: + f := x.DoubleValue + i = int64(f) + if float64(i) != f { + return fmt.Errorf("firestore: float %f does not fit into %s", f, v.Type()) + } + default: + return typeErr() + } + if v.OverflowInt(i) { + return overflowErr(v, i) + } + v.SetInt(i) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + var u uint64 + switch x := val.(type) { + case *pb.Value_IntegerValue: + u = uint64(x.IntegerValue) + case *pb.Value_DoubleValue: + f := x.DoubleValue + u = uint64(f) + if float64(u) != f { + return fmt.Errorf("firestore: float %f does not fit into %s", f, v.Type()) + } + default: + return typeErr() + } + if v.OverflowUint(u) { + return overflowErr(v, u) + } + v.SetUint(u) + + case reflect.Float32, reflect.Float64: + var f float64 + switch x := val.(type) { + case *pb.Value_DoubleValue: + f = x.DoubleValue + case *pb.Value_IntegerValue: + f = float64(x.IntegerValue) + if int64(f) != x.IntegerValue { + return overflowErr(v, x.IntegerValue) + } + default: + return typeErr() + } + if v.OverflowFloat(f) { + return overflowErr(v, f) + } + v.SetFloat(f) + + case reflect.Slice: + x, ok := val.(*pb.Value_ArrayValue) + if !ok { + return typeErr() + } + vals := x.ArrayValue.Values + vlen := v.Len() + xlen := len(vals) + // Make a slice of the right size, avoiding allocation if possible. + switch { + case vlen < xlen: + v.Set(reflect.MakeSlice(v.Type(), xlen, xlen)) + case vlen > xlen: + v.SetLen(xlen) + } + return populateRepeated(v, vals, xlen, c) + + case reflect.Array: + x, ok := val.(*pb.Value_ArrayValue) + if !ok { + return typeErr() + } + vals := x.ArrayValue.Values + xlen := len(vals) + vlen := v.Len() + minlen := vlen + // Set extra elements to their zero value. + if vlen > xlen { + z := reflect.Zero(v.Type().Elem()) + for i := xlen; i < vlen; i++ { + v.Index(i).Set(z) + } + minlen = xlen + } + return populateRepeated(v, vals, minlen, c) + + case reflect.Map: + x, ok := val.(*pb.Value_MapValue) + if !ok { + return typeErr() + } + return populateMap(v, x.MapValue.Fields, c) + + case reflect.Ptr: + // If the pointer is nil, set it to a zero value. + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + return setReflectFromProtoValue(v.Elem(), vproto, c) + + case reflect.Struct: + x, ok := val.(*pb.Value_MapValue) + if !ok { + return typeErr() + } + return populateStruct(v, x.MapValue.Fields, c) + + case reflect.Interface: + if v.NumMethod() == 0 { // empty interface + // If v holds a pointer, set the pointer. + if !v.IsNil() && v.Elem().Kind() == reflect.Ptr { + return setReflectFromProtoValue(v.Elem(), vproto, c) + } + // Otherwise, create a fresh value. + x, err := createFromProtoValue(vproto, c) + if err != nil { + return err + } + v.Set(reflect.ValueOf(x)) + return nil + } + // Any other kind of interface is an error. + fallthrough + + default: + return fmt.Errorf("firestore: cannot set type %s", v.Type()) + } + return nil +} + +// populateRepeated sets the first n elements of vr, which must be a slice or +// array, to the corresponding elements of vals. +func populateRepeated(vr reflect.Value, vals []*pb.Value, n int, c *Client) error { + for i := 0; i < n; i++ { + if err := setReflectFromProtoValue(vr.Index(i), vals[i], c); err != nil { + return err + } + } + return nil +} + +// populateMap sets the elements of vm, which must be a map, from the +// corresponding elements of pm. +// +// Since a map value is not settable, this function always creates a new +// element for each corresponding map key. Existing values of vm are +// overwritten. This happens even if the map value is something like a pointer +// to a struct, where we could in theory populate the existing struct value +// instead of discarding it. This behavior matches encoding/json. +func populateMap(vm reflect.Value, pm map[string]*pb.Value, c *Client) error { + t := vm.Type() + if t.Key().Kind() != reflect.String { + return errors.New("firestore: map key type is not string") + } + if vm.IsNil() { + vm.Set(reflect.MakeMap(t)) + } + et := t.Elem() + for k, vproto := range pm { + el := reflect.New(et).Elem() + if err := setReflectFromProtoValue(el, vproto, c); err != nil { + return err + } + vm.SetMapIndex(reflect.ValueOf(k), el) + } + return nil +} + +// createMapFromValueMap creates a fresh map and populates it with pm. +func createMapFromValueMap(pm map[string]*pb.Value, c *Client) (map[string]interface{}, error) { + m := map[string]interface{}{} + for k, pv := range pm { + v, err := createFromProtoValue(pv, c) + if err != nil { + return nil, err + } + m[k] = v + } + return m, nil +} + +// populateStruct sets the fields of vs, which must be a struct, from +// the matching elements of pm. +func populateStruct(vs reflect.Value, pm map[string]*pb.Value, c *Client) error { + fields, err := fieldCache.Fields(vs.Type()) + if err != nil { + return err + } + for k, vproto := range pm { + f := fields.Match(k) + if f == nil { + continue + } + if err := setReflectFromProtoValue(vs.FieldByIndex(f.Index), vproto, c); err != nil { + return fmt.Errorf("%s.%s: %v", vs.Type(), f.Name, err) + } + } + return nil +} + +func createFromProtoValue(vproto *pb.Value, c *Client) (interface{}, error) { + switch v := vproto.ValueType.(type) { + case *pb.Value_NullValue: + return nil, nil + case *pb.Value_BooleanValue: + return v.BooleanValue, nil + case *pb.Value_IntegerValue: + return v.IntegerValue, nil + case *pb.Value_DoubleValue: + return v.DoubleValue, nil + case *pb.Value_TimestampValue: + return ptypes.Timestamp(v.TimestampValue) + case *pb.Value_StringValue: + return v.StringValue, nil + case *pb.Value_BytesValue: + return v.BytesValue, nil + case *pb.Value_ReferenceValue: + return pathToDoc(v.ReferenceValue, c) + case *pb.Value_GeoPointValue: + return v.GeoPointValue, nil + + case *pb.Value_ArrayValue: + vals := v.ArrayValue.Values + ret := make([]interface{}, len(vals)) + for i, v := range vals { + r, err := createFromProtoValue(v, c) + if err != nil { + return nil, err + } + ret[i] = r + } + return ret, nil + + case *pb.Value_MapValue: + fields := v.MapValue.Fields + ret := make(map[string]interface{}, len(fields)) + for k, v := range fields { + r, err := createFromProtoValue(v, c) + if err != nil { + return nil, err + } + ret[k] = r + } + return ret, nil + + default: + return nil, fmt.Errorf("firestore: unknown value type %T", v) + } +} + +// Convert a document path to a DocumentRef. +func pathToDoc(docPath string, c *Client) (*DocumentRef, error) { + projID, dbID, docIDs, err := parseDocumentPath(docPath) + if err != nil { + return nil, err + } + parentResourceName := fmt.Sprintf("projects/%s/databases/%s", projID, dbID) + _, doc := c.idsToRef(docIDs, parentResourceName) + return doc, nil +} + +// A document path should be of the form "projects/P/databases/D/documents/coll1/doc1/coll2/doc2/...". +func parseDocumentPath(path string) (projectID, databaseID string, docPath []string, err error) { + parts := strings.Split(path, "/") + if len(parts) < 6 || parts[0] != "projects" || parts[2] != "databases" || parts[4] != "documents" { + return "", "", nil, fmt.Errorf("firestore: malformed document path %q", path) + } + docp := parts[5:] + if len(docp)%2 != 0 { + return "", "", nil, fmt.Errorf("firestore: path %q refers to collection, not document", path) + } + return parts[1], parts[3], docp, nil +} + +func typeString(vproto *pb.Value) string { + switch vproto.ValueType.(type) { + case *pb.Value_NullValue: + return "null" + case *pb.Value_BooleanValue: + return "bool" + case *pb.Value_IntegerValue: + return "int" + case *pb.Value_DoubleValue: + return "float" + case *pb.Value_TimestampValue: + return "timestamp" + case *pb.Value_StringValue: + return "string" + case *pb.Value_BytesValue: + return "bytes" + case *pb.Value_ReferenceValue: + return "reference" + case *pb.Value_GeoPointValue: + return "GeoPoint" + case *pb.Value_MapValue: + return "map" + case *pb.Value_ArrayValue: + return "array" + default: + return "" + } +} + +func overflowErr(v reflect.Value, x interface{}) error { + return fmt.Errorf("firestore: value %v overflows type %s", x, v.Type()) +} diff --git a/vendor/cloud.google.com/go/firestore/from_value_test.go b/vendor/cloud.google.com/go/firestore/from_value_test.go new file mode 100644 index 0000000000..f5168acaff --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/from_value_test.go @@ -0,0 +1,550 @@ +// Copyright 2017 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 firestore + +import ( + "encoding/json" + "fmt" + "io" + "math" + "reflect" + "strings" + "testing" + "time" + + ts "github.com/golang/protobuf/ptypes/timestamp" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "google.golang.org/genproto/googleapis/type/latlng" +) + +var ( + tm = time.Date(2016, 12, 25, 0, 0, 0, 123456789, time.UTC) + ll = &latlng.LatLng{Latitude: 20, Longitude: 30} + ptm = &ts.Timestamp{Seconds: 12345, Nanos: 67890} +) + +func TestCreateFromProtoValue(t *testing.T) { + for _, test := range []struct { + in *pb.Value + want interface{} + }{ + {in: nullValue, want: nil}, + {in: boolval(true), want: true}, + {in: intval(3), want: int64(3)}, + {in: floatval(1.5), want: 1.5}, + {in: strval("str"), want: "str"}, + {in: tsval(tm), want: tm}, + { + in: bytesval([]byte{1, 2}), + want: []byte{1, 2}, + }, + { + in: &pb.Value{ValueType: &pb.Value_GeoPointValue{ll}}, + want: ll, + }, + { + in: arrayval(intval(1), intval(2)), + want: []interface{}{int64(1), int64(2)}, + }, + { + in: arrayval(), + want: []interface{}{}, + }, + { + in: mapval(map[string]*pb.Value{"a": intval(1), "b": intval(2)}), + want: map[string]interface{}{"a": int64(1), "b": int64(2)}, + }, + { + in: mapval(map[string]*pb.Value{}), + want: map[string]interface{}{}, + }, + { + in: refval("projects/P/databases/D/documents/c/d"), + want: &DocumentRef{ + ID: "d", + Parent: &CollectionRef{ + ID: "c", + parentPath: "projects/P/databases/D", + Path: "projects/P/databases/D/documents/c", + Query: Query{collectionID: "c", parentPath: "projects/P/databases/D"}, + }, + Path: "projects/P/databases/D/documents/c/d", + }, + }, + } { + got, err := createFromProtoValue(test.in, nil) + if err != nil { + t.Errorf("%v: %v", test.in, err) + continue + } + if !testEqual(got, test.want) { + t.Errorf("%+v:\ngot\n%#v\nwant\n%#v", test.in, got, test.want) + } + } +} + +func TestSetFromProtoValue(t *testing.T) { + testSetFromProtoValue(t, "json", jsonTester{}) + testSetFromProtoValue(t, "firestore", protoTester{}) +} + +func testSetFromProtoValue(t *testing.T, prefix string, r tester) { + pi := newfloat(7) + s := []float64{7, 8} + ar1 := [1]float64{7} + ar2 := [2]float64{7, 8} + ar3 := [3]float64{7, 8, 9} + mf := map[string]float64{"a": 7} + + type T struct { + I **float64 + J float64 + } + + one := newfloat(1) + six := newfloat(6) + st := []*T{{I: &six}, nil, {I: &six, J: 7}} + vs := interface{}(T{J: 1}) + vm := interface{}(map[string]float64{"i": 1}) + var ( + i int + i8 int8 + i16 int16 + i32 int32 + i64 int64 + u8 uint8 + u16 uint16 + u32 uint32 + b bool + ll *latlng.LatLng + mi map[string]interface{} + ms map[string]T + ) + + for i, test := range []struct { + in interface{} + val interface{} + want interface{} + }{ + {&pi, r.Null(), (*float64)(nil)}, + {pi, r.Float(1), 1.0}, + {&s, r.Null(), ([]float64)(nil)}, + {&s, r.Array(r.Float(1), r.Float(2)), []float64{1, 2}}, + {&ar1, r.Array(r.Float(1), r.Float(2)), [1]float64{1}}, + {&ar2, r.Array(r.Float(1), r.Float(2)), [2]float64{1, 2}}, + {&ar3, r.Array(r.Float(1), r.Float(2)), [3]float64{1, 2, 0}}, + {&mf, r.Null(), (map[string]float64)(nil)}, + {&mf, r.Map("a", r.Float(1), "b", r.Float(2)), map[string]float64{"a": 1, "b": 2}}, + {&st, r.Array( + r.Null(), // overwrites st[0] with nil + r.Map("i", r.Float(1)), // sets st[1] to a new struct + r.Map("i", r.Float(2)), // modifies st[2] + ), + []*T{nil, {I: &one}, {I: &six, J: 7}}}, + {&mi, r.Map("a", r.Float(1), "b", r.Float(2)), map[string]interface{}{"a": 1.0, "b": 2.0}}, + {&ms, r.Map("a", r.Map("j", r.Float(1))), map[string]T{"a": {J: 1}}}, + {&vs, r.Map("i", r.Float(2)), map[string]interface{}{"i": 2.0}}, + {&vm, r.Map("i", r.Float(2)), map[string]interface{}{"i": 2.0}}, + {&ll, r.Null(), (*latlng.LatLng)(nil)}, + {&i, r.Int(1), int(1)}, + {&i8, r.Int(1), int8(1)}, + {&i16, r.Int(1), int16(1)}, + {&i32, r.Int(1), int32(1)}, + {&i64, r.Int(1), int64(1)}, + {&u8, r.Int(1), uint8(1)}, + {&u16, r.Int(1), uint16(1)}, + {&u32, r.Int(1), uint32(1)}, + {&b, r.Bool(true), true}, + {&i, r.Float(1), int(1)}, // can put a float with no fractional part into an int + {pi, r.Int(1), float64(1)}, // can put an int into a float + } { + if err := r.Set(test.in, test.val); err != nil { + t.Errorf("%s: #%d: got error %v", prefix, i, err) + continue + } + got := reflect.ValueOf(test.in).Elem().Interface() + if !testEqual(got, test.want) { + t.Errorf("%s: #%d, %v:\ngot\n%+v (%T)\nwant\n%+v (%T)", + prefix, i, test.val, got, got, test.want, test.want) + } + } +} + +func TestSetFromProtoValueNoJSON(t *testing.T) { + // Test code paths that we cannot compare to JSON. + var ( + bs []byte + tmi time.Time + lli *latlng.LatLng + tmp *ts.Timestamp + ) + bytes := []byte{1, 2, 3} + + for i, test := range []struct { + in interface{} + val *pb.Value + want interface{} + }{ + {&bs, bytesval(bytes), bytes}, + {&tmi, tsval(tm), tm}, + {&tmp, &pb.Value{ValueType: &pb.Value_TimestampValue{ptm}}, ptm}, + {&lli, geoval(ll), ll}, + } { + if err := setFromProtoValue(test.in, test.val, &Client{}); err != nil { + t.Errorf("#%d: got error %v", i, err) + continue + } + got := reflect.ValueOf(test.in).Elem().Interface() + if !testEqual(got, test.want) { + t.Errorf("#%d, %v:\ngot\n%+v (%T)\nwant\n%+v (%T)", + i, test.val, got, got, test.want, test.want) + } + } +} + +func TestSetFromProtoValueErrors(t *testing.T) { + c := &Client{} + ival := intval(3) + for i, test := range []struct { + in interface{} + val *pb.Value + }{ + {3, ival}, // not a pointer + {new(int8), intval(128)}, // int overflow + {new(uint8), intval(256)}, // uint overflow + {new(float32), floatval(2 * math.MaxFloat32)}, // float overflow + {new(uint), ival}, // cannot set type + {new(uint64), ival}, // cannot set type + {new(io.Reader), ival}, // cannot set type + {new(map[int]int), + mapval(map[string]*pb.Value{"x": ival})}, // map key type is not string + // the rest are all type mismatches + {new(bool), ival}, + {new(*latlng.LatLng), ival}, + {new(time.Time), ival}, + {new(string), ival}, + {new([]byte), ival}, + {new([]int), ival}, + {new([1]int), ival}, + {new(map[string]int), ival}, + {new(*bool), ival}, + {new(struct{}), ival}, + {new(int), floatval(2.5)}, // float must be integral + {new(uint16), intval(-1)}, // uint cannot be negative + {new(int16), floatval(math.MaxFloat32)}, // doesn't fit + {new(uint16), floatval(math.MaxFloat32)}, // doesn't fit + {new(float32), + &pb.Value{ValueType: &pb.Value_IntegerValue{math.MaxInt64}}}, // overflow + } { + err := setFromProtoValue(test.in, test.val, c) + if err == nil { + t.Errorf("#%d: %v, %v: got nil, want error", i, test.in, test.val) + } + } +} + +func TestSetFromProtoValuePointers(t *testing.T) { + // Verify that pointers are set, instead of being replaced. + // Confirm that the behavior matches encoding/json. + testSetPointer(t, "json", jsonTester{}) + testSetPointer(t, "firestore", protoTester{&Client{}}) +} + +func testSetPointer(t *testing.T, prefix string, r tester) { + // If an interface{} holds a pointer, the pointer is set. + + set := func(x, val interface{}) { + if err := r.Set(x, val); err != nil { + t.Fatalf("%s: set(%v, %v): %v", prefix, x, val, err) + } + } + p := new(float64) + var st struct { + I interface{} + } + + // A pointer in a slice of interface{} is set. + s := []interface{}{p} + set(&s, r.Array(r.Float(1))) + if s[0] != p { + t.Errorf("%s: pointers not identical", prefix) + } + if *p != 1 { + t.Errorf("%s: got %f, want 1", prefix, *p) + } + // Setting a null will set the pointer to nil. + set(&s, r.Array(r.Null())) + if got := s[0]; got != nil { + t.Errorf("%s: got %v, want null", prefix, got) + } + + // It doesn't matter how deep the pointers nest. + p = new(float64) + p2 := &p + p3 := &p2 + s = []interface{}{p3} + set(&s, r.Array(r.Float(1))) + if s[0] != p3 { + t.Errorf("%s: pointers not identical", prefix) + } + if *p != 1 { + t.Errorf("%s: got %f, want 1", prefix, *p) + } + + // A pointer in an interface{} field is set. + p = new(float64) + st.I = p + set(&st, r.Map("i", r.Float(1))) + if st.I != p { + t.Errorf("%s: pointers not identical", prefix) + } + if *p != 1 { + t.Errorf("%s: got %f, want 1", prefix, *p) + } + // Setting a null will set the pointer to nil. + set(&st, r.Map("i", r.Null())) + if got := st.I; got != nil { + t.Errorf("%s: got %v, want null", prefix, got) + } + + // A pointer to a slice (instead of to float64) is set. + psi := &[]float64{7, 8, 9} + st.I = psi + set(&st, r.Map("i", r.Array(r.Float(1)))) + if st.I != psi { + t.Errorf("%s: pointers not identical", prefix) + } + // The slice itself should be truncated and filled, not replaced. + if got, want := cap(*psi), 3; got != want { + t.Errorf("cap: got %d, want %d", got, want) + } + if want := &[]float64{1}; !testEqual(st.I, want) { + t.Errorf("got %+v, want %+v", st.I, want) + } + + // A pointer to a map is set. + pmf := &map[string]float64{"a": 7, "b": 8} + st.I = pmf + set(&st, r.Map("i", r.Map("a", r.Float(1)))) + if st.I != pmf { + t.Errorf("%s: pointers not identical", prefix) + } + if want := map[string]float64{"a": 1, "b": 8}; !testEqual(*pmf, want) { + t.Errorf("%s: got %+v, want %+v", prefix, *pmf, want) + } + + // Maps are different: since the map values aren't addressable, they + // are always discarded, even if the map element type is not interface{}. + + // A map's values are discarded if the value type is a pointer type. + p = new(float64) + m := map[string]*float64{"i": p} + set(&m, r.Map("i", r.Float(1))) + if m["i"] == p { + t.Errorf("%s: pointers are identical", prefix) + } + if got, want := *m["i"], 1.0; got != want { + t.Errorf("%s: got %v, want %v", prefix, got, want) + } + // A map's values are discarded if the value type is interface{}. + p = new(float64) + m2 := map[string]interface{}{"i": p} + set(&m2, r.Map("i", r.Float(1))) + if m2["i"] == p { + t.Errorf("%s: pointers are identical", prefix) + } + if got, want := m2["i"].(float64), 1.0; got != want { + t.Errorf("%s: got %f, want %f", prefix, got, want) + } +} + +// An interface for setting and building values, to facilitate comparing firestore deserialization +// with encoding/json. +type tester interface { + Set(x, val interface{}) error + Null() interface{} + Int(int) interface{} + Float(float64) interface{} + Bool(bool) interface{} + Array(...interface{}) interface{} + Map(keysvals ...interface{}) interface{} +} + +type protoTester struct { + c *Client +} + +func (p protoTester) Set(x, val interface{}) error { return setFromProtoValue(x, val.(*pb.Value), p.c) } +func (protoTester) Null() interface{} { return nullValue } +func (protoTester) Int(i int) interface{} { return intval(i) } +func (protoTester) Float(f float64) interface{} { return floatval(f) } +func (protoTester) Bool(b bool) interface{} { return boolval(b) } + +func (protoTester) Array(els ...interface{}) interface{} { + var s []*pb.Value + for _, el := range els { + s = append(s, el.(*pb.Value)) + } + return arrayval(s...) +} + +func (protoTester) Map(keysvals ...interface{}) interface{} { + m := map[string]*pb.Value{} + for i := 0; i < len(keysvals); i += 2 { + m[keysvals[i].(string)] = keysvals[i+1].(*pb.Value) + } + return mapval(m) +} + +type jsonTester struct{} + +func (jsonTester) Set(x, val interface{}) error { return json.Unmarshal([]byte(val.(string)), x) } +func (jsonTester) Null() interface{} { return "null" } +func (jsonTester) Int(i int) interface{} { return fmt.Sprint(i) } +func (jsonTester) Float(f float64) interface{} { return fmt.Sprint(f) } + +func (jsonTester) Bool(b bool) interface{} { + if b { + return "true" + } + return "false" +} + +func (jsonTester) Array(els ...interface{}) interface{} { + var s []string + for _, el := range els { + s = append(s, el.(string)) + } + return "[" + strings.Join(s, ", ") + "]" +} + +func (jsonTester) Map(keysvals ...interface{}) interface{} { + var s []string + for i := 0; i < len(keysvals); i += 2 { + s = append(s, fmt.Sprintf("%q: %v", keysvals[i], keysvals[i+1])) + } + return "{" + strings.Join(s, ", ") + "}" +} + +func newfloat(f float64) *float64 { + p := new(float64) + *p = f + return p +} + +func TestParseDocumentPath(t *testing.T) { + for _, test := range []struct { + in string + pid, dbid string + dpath []string + }{ + {"projects/foo-bar/databases/db2/documents/c1/d1", + "foo-bar", "db2", []string{"c1", "d1"}}, + {"projects/P/databases/D/documents/c1/d1/c2/d2", + "P", "D", []string{"c1", "d1", "c2", "d2"}}, + } { + gotPid, gotDbid, gotDpath, err := parseDocumentPath(test.in) + if err != nil { + t.Fatal(err) + } + if got, want := gotPid, test.pid; got != want { + t.Errorf("project ID: got %q, want %q", got, want) + } + if got, want := gotDbid, test.dbid; got != want { + t.Errorf("db ID: got %q, want %q", got, want) + } + if got, want := gotDpath, test.dpath; !testEqual(got, want) { + t.Errorf("doc path: got %q, want %q", got, want) + } + } +} + +func TestParseDocumentPathErrors(t *testing.T) { + for _, badPath := range []string{ + "projects/P/databases/D/documents/c", // collection path + "/projects/P/databases/D/documents/c/d", // initial slash + "projects/P/databases/D/c/d", // missing "documents" + "project/P/database/D/document/c/d", + } { + // Every prefix of a bad path is also bad. + for i := 0; i <= len(badPath); i++ { + in := badPath[:i] + _, _, _, err := parseDocumentPath(in) + if err == nil { + t.Errorf("%q: got nil, want error", in) + } + } + } +} + +func TestPathToDoc(t *testing.T) { + c := &Client{} + path := "projects/P/databases/D/documents/c1/d1/c2/d2" + got, err := pathToDoc(path, c) + if err != nil { + t.Fatal(err) + } + want := &DocumentRef{ + ID: "d2", + Path: "projects/P/databases/D/documents/c1/d1/c2/d2", + Parent: &CollectionRef{ + ID: "c2", + parentPath: "projects/P/databases/D/documents/c1/d1", + Path: "projects/P/databases/D/documents/c1/d1/c2", + c: c, + Query: Query{c: c, collectionID: "c2", parentPath: "projects/P/databases/D/documents/c1/d1"}, + Parent: &DocumentRef{ + ID: "d1", + Path: "projects/P/databases/D/documents/c1/d1", + Parent: &CollectionRef{ + ID: "c1", + c: c, + parentPath: "projects/P/databases/D", + Path: "projects/P/databases/D/documents/c1", + Parent: nil, + Query: Query{c: c, collectionID: "c1", parentPath: "projects/P/databases/D"}, + }, + }, + }, + } + if !testEqual(got, want) { + t.Errorf("\ngot %+v\nwant %+v", got, want) + } +} + +func TestTypeString(t *testing.T) { + for _, test := range []struct { + in *pb.Value + want string + }{ + {nullValue, "null"}, + {intval(1), "int"}, + {floatval(1), "float"}, + {boolval(true), "bool"}, + {strval(""), "string"}, + {tsval(tm), "timestamp"}, + {geoval(ll), "GeoPoint"}, + {bytesval(nil), "bytes"}, + {refval(""), "reference"}, + {arrayval(nil), "array"}, + {mapval(nil), "map"}, + } { + got := typeString(test.in) + if got != test.want { + t.Errorf("%+v: got %q, want %q", test.in, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/firestore/genproto/test.pb.go b/vendor/cloud.google.com/go/firestore/genproto/test.pb.go new file mode 100644 index 0000000000..39ca3bb660 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/genproto/test.pb.go @@ -0,0 +1,1443 @@ +// Code generated by protoc-gen-go. +// source: test.proto +// DO NOT EDIT! + +/* +Package tests is a generated protocol buffer package. + +It is generated from these files: + test.proto + +It has these top-level messages: + TestSuite + Test + GetTest + CreateTest + SetTest + UpdateTest + UpdatePathsTest + DeleteTest + SetOption + QueryTest + Clause + Select + Where + OrderBy + Cursor + DocSnapshot + FieldPath + ListenTest + Snapshot + DocChange +*/ +package tests + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_firestore_v1beta1 "google.golang.org/genproto/googleapis/firestore/v1beta1" +import google_firestore_v1beta11 "google.golang.org/genproto/googleapis/firestore/v1beta1" +import google_firestore_v1beta14 "google.golang.org/genproto/googleapis/firestore/v1beta1" +import google_firestore_v1beta12 "google.golang.org/genproto/googleapis/firestore/v1beta1" +import google_protobuf1 "github.com/golang/protobuf/ptypes/timestamp" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type DocChange_Kind int32 + +const ( + DocChange_KIND_UNSPECIFIED DocChange_Kind = 0 + DocChange_ADDED DocChange_Kind = 1 + DocChange_REMOVED DocChange_Kind = 2 + DocChange_MODIFIED DocChange_Kind = 3 +) + +var DocChange_Kind_name = map[int32]string{ + 0: "KIND_UNSPECIFIED", + 1: "ADDED", + 2: "REMOVED", + 3: "MODIFIED", +} +var DocChange_Kind_value = map[string]int32{ + "KIND_UNSPECIFIED": 0, + "ADDED": 1, + "REMOVED": 2, + "MODIFIED": 3, +} + +func (x DocChange_Kind) String() string { + return proto.EnumName(DocChange_Kind_name, int32(x)) +} +func (DocChange_Kind) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{19, 0} } + +// A collection of tests. +type TestSuite struct { + Tests []*Test `protobuf:"bytes,1,rep,name=tests" json:"tests,omitempty"` +} + +func (m *TestSuite) Reset() { *m = TestSuite{} } +func (m *TestSuite) String() string { return proto.CompactTextString(m) } +func (*TestSuite) ProtoMessage() {} +func (*TestSuite) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *TestSuite) GetTests() []*Test { + if m != nil { + return m.Tests + } + return nil +} + +// A Test describes a single client method call and its expected result. +type Test struct { + Description string `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"` + // Types that are valid to be assigned to Test: + // *Test_Get + // *Test_Create + // *Test_Set + // *Test_Update + // *Test_UpdatePaths + // *Test_Delete + // *Test_Query + // *Test_Listen + Test isTest_Test `protobuf_oneof:"test"` +} + +func (m *Test) Reset() { *m = Test{} } +func (m *Test) String() string { return proto.CompactTextString(m) } +func (*Test) ProtoMessage() {} +func (*Test) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type isTest_Test interface { + isTest_Test() +} + +type Test_Get struct { + Get *GetTest `protobuf:"bytes,2,opt,name=get,oneof"` +} +type Test_Create struct { + Create *CreateTest `protobuf:"bytes,3,opt,name=create,oneof"` +} +type Test_Set struct { + Set *SetTest `protobuf:"bytes,4,opt,name=set,oneof"` +} +type Test_Update struct { + Update *UpdateTest `protobuf:"bytes,5,opt,name=update,oneof"` +} +type Test_UpdatePaths struct { + UpdatePaths *UpdatePathsTest `protobuf:"bytes,6,opt,name=update_paths,json=updatePaths,oneof"` +} +type Test_Delete struct { + Delete *DeleteTest `protobuf:"bytes,7,opt,name=delete,oneof"` +} +type Test_Query struct { + Query *QueryTest `protobuf:"bytes,8,opt,name=query,oneof"` +} +type Test_Listen struct { + Listen *ListenTest `protobuf:"bytes,9,opt,name=listen,oneof"` +} + +func (*Test_Get) isTest_Test() {} +func (*Test_Create) isTest_Test() {} +func (*Test_Set) isTest_Test() {} +func (*Test_Update) isTest_Test() {} +func (*Test_UpdatePaths) isTest_Test() {} +func (*Test_Delete) isTest_Test() {} +func (*Test_Query) isTest_Test() {} +func (*Test_Listen) isTest_Test() {} + +func (m *Test) GetTest() isTest_Test { + if m != nil { + return m.Test + } + return nil +} + +func (m *Test) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *Test) GetGet() *GetTest { + if x, ok := m.GetTest().(*Test_Get); ok { + return x.Get + } + return nil +} + +func (m *Test) GetCreate() *CreateTest { + if x, ok := m.GetTest().(*Test_Create); ok { + return x.Create + } + return nil +} + +func (m *Test) GetSet() *SetTest { + if x, ok := m.GetTest().(*Test_Set); ok { + return x.Set + } + return nil +} + +func (m *Test) GetUpdate() *UpdateTest { + if x, ok := m.GetTest().(*Test_Update); ok { + return x.Update + } + return nil +} + +func (m *Test) GetUpdatePaths() *UpdatePathsTest { + if x, ok := m.GetTest().(*Test_UpdatePaths); ok { + return x.UpdatePaths + } + return nil +} + +func (m *Test) GetDelete() *DeleteTest { + if x, ok := m.GetTest().(*Test_Delete); ok { + return x.Delete + } + return nil +} + +func (m *Test) GetQuery() *QueryTest { + if x, ok := m.GetTest().(*Test_Query); ok { + return x.Query + } + return nil +} + +func (m *Test) GetListen() *ListenTest { + if x, ok := m.GetTest().(*Test_Listen); ok { + return x.Listen + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Test) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Test_OneofMarshaler, _Test_OneofUnmarshaler, _Test_OneofSizer, []interface{}{ + (*Test_Get)(nil), + (*Test_Create)(nil), + (*Test_Set)(nil), + (*Test_Update)(nil), + (*Test_UpdatePaths)(nil), + (*Test_Delete)(nil), + (*Test_Query)(nil), + (*Test_Listen)(nil), + } +} + +func _Test_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Test) + // test + switch x := m.Test.(type) { + case *Test_Get: + b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Get); err != nil { + return err + } + case *Test_Create: + b.EncodeVarint(3<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Create); err != nil { + return err + } + case *Test_Set: + b.EncodeVarint(4<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Set); err != nil { + return err + } + case *Test_Update: + b.EncodeVarint(5<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Update); err != nil { + return err + } + case *Test_UpdatePaths: + b.EncodeVarint(6<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.UpdatePaths); err != nil { + return err + } + case *Test_Delete: + b.EncodeVarint(7<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Delete); err != nil { + return err + } + case *Test_Query: + b.EncodeVarint(8<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Query); err != nil { + return err + } + case *Test_Listen: + b.EncodeVarint(9<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Listen); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Test.Test has unexpected type %T", x) + } + return nil +} + +func _Test_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Test) + switch tag { + case 2: // test.get + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(GetTest) + err := b.DecodeMessage(msg) + m.Test = &Test_Get{msg} + return true, err + case 3: // test.create + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(CreateTest) + err := b.DecodeMessage(msg) + m.Test = &Test_Create{msg} + return true, err + case 4: // test.set + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(SetTest) + err := b.DecodeMessage(msg) + m.Test = &Test_Set{msg} + return true, err + case 5: // test.update + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(UpdateTest) + err := b.DecodeMessage(msg) + m.Test = &Test_Update{msg} + return true, err + case 6: // test.update_paths + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(UpdatePathsTest) + err := b.DecodeMessage(msg) + m.Test = &Test_UpdatePaths{msg} + return true, err + case 7: // test.delete + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(DeleteTest) + err := b.DecodeMessage(msg) + m.Test = &Test_Delete{msg} + return true, err + case 8: // test.query + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(QueryTest) + err := b.DecodeMessage(msg) + m.Test = &Test_Query{msg} + return true, err + case 9: // test.listen + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ListenTest) + err := b.DecodeMessage(msg) + m.Test = &Test_Listen{msg} + return true, err + default: + return false, nil + } +} + +func _Test_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Test) + // test + switch x := m.Test.(type) { + case *Test_Get: + s := proto.Size(x.Get) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Test_Create: + s := proto.Size(x.Create) + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Test_Set: + s := proto.Size(x.Set) + n += proto.SizeVarint(4<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Test_Update: + s := proto.Size(x.Update) + n += proto.SizeVarint(5<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Test_UpdatePaths: + s := proto.Size(x.UpdatePaths) + n += proto.SizeVarint(6<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Test_Delete: + s := proto.Size(x.Delete) + n += proto.SizeVarint(7<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Test_Query: + s := proto.Size(x.Query) + n += proto.SizeVarint(8<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Test_Listen: + s := proto.Size(x.Listen) + n += proto.SizeVarint(9<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +// Call to the DocumentRef.Get method. +type GetTest struct { + // The path of the doc, e.g. "projects/projectID/databases/(default)/documents/C/d" + DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"` + // The request that the call should send to the Firestore service. + Request *google_firestore_v1beta14.GetDocumentRequest `protobuf:"bytes,2,opt,name=request" json:"request,omitempty"` +} + +func (m *GetTest) Reset() { *m = GetTest{} } +func (m *GetTest) String() string { return proto.CompactTextString(m) } +func (*GetTest) ProtoMessage() {} +func (*GetTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *GetTest) GetDocRefPath() string { + if m != nil { + return m.DocRefPath + } + return "" +} + +func (m *GetTest) GetRequest() *google_firestore_v1beta14.GetDocumentRequest { + if m != nil { + return m.Request + } + return nil +} + +// Call to DocumentRef.Create. +type CreateTest struct { + // The path of the doc, e.g. "projects/projectID/databases/(default)/documents/C/d" + DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"` + // The data passed to Create, as JSON. The strings "Delete" and "ServerTimestamp" + // denote the two special sentinel values. Values that could be interpreted as integers + // (i.e. digit strings) should be treated as integers. + JsonData string `protobuf:"bytes,2,opt,name=json_data,json=jsonData" json:"json_data,omitempty"` + // The request that the call should generate. + Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,3,opt,name=request" json:"request,omitempty"` + // If true, the call should result in an error without generating a request. + // If this is true, request should not be set. + IsError bool `protobuf:"varint,4,opt,name=is_error,json=isError" json:"is_error,omitempty"` +} + +func (m *CreateTest) Reset() { *m = CreateTest{} } +func (m *CreateTest) String() string { return proto.CompactTextString(m) } +func (*CreateTest) ProtoMessage() {} +func (*CreateTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *CreateTest) GetDocRefPath() string { + if m != nil { + return m.DocRefPath + } + return "" +} + +func (m *CreateTest) GetJsonData() string { + if m != nil { + return m.JsonData + } + return "" +} + +func (m *CreateTest) GetRequest() *google_firestore_v1beta14.CommitRequest { + if m != nil { + return m.Request + } + return nil +} + +func (m *CreateTest) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +// A call to DocumentRef.Set. +type SetTest struct { + DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"` + Option *SetOption `protobuf:"bytes,2,opt,name=option" json:"option,omitempty"` + JsonData string `protobuf:"bytes,3,opt,name=json_data,json=jsonData" json:"json_data,omitempty"` + Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,4,opt,name=request" json:"request,omitempty"` + IsError bool `protobuf:"varint,5,opt,name=is_error,json=isError" json:"is_error,omitempty"` +} + +func (m *SetTest) Reset() { *m = SetTest{} } +func (m *SetTest) String() string { return proto.CompactTextString(m) } +func (*SetTest) ProtoMessage() {} +func (*SetTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *SetTest) GetDocRefPath() string { + if m != nil { + return m.DocRefPath + } + return "" +} + +func (m *SetTest) GetOption() *SetOption { + if m != nil { + return m.Option + } + return nil +} + +func (m *SetTest) GetJsonData() string { + if m != nil { + return m.JsonData + } + return "" +} + +func (m *SetTest) GetRequest() *google_firestore_v1beta14.CommitRequest { + if m != nil { + return m.Request + } + return nil +} + +func (m *SetTest) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +// A call to the form of DocumentRef.Update that represents the data as a map +// or dictionary. +type UpdateTest struct { + DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"` + Precondition *google_firestore_v1beta1.Precondition `protobuf:"bytes,2,opt,name=precondition" json:"precondition,omitempty"` + JsonData string `protobuf:"bytes,3,opt,name=json_data,json=jsonData" json:"json_data,omitempty"` + Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,4,opt,name=request" json:"request,omitempty"` + IsError bool `protobuf:"varint,5,opt,name=is_error,json=isError" json:"is_error,omitempty"` +} + +func (m *UpdateTest) Reset() { *m = UpdateTest{} } +func (m *UpdateTest) String() string { return proto.CompactTextString(m) } +func (*UpdateTest) ProtoMessage() {} +func (*UpdateTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *UpdateTest) GetDocRefPath() string { + if m != nil { + return m.DocRefPath + } + return "" +} + +func (m *UpdateTest) GetPrecondition() *google_firestore_v1beta1.Precondition { + if m != nil { + return m.Precondition + } + return nil +} + +func (m *UpdateTest) GetJsonData() string { + if m != nil { + return m.JsonData + } + return "" +} + +func (m *UpdateTest) GetRequest() *google_firestore_v1beta14.CommitRequest { + if m != nil { + return m.Request + } + return nil +} + +func (m *UpdateTest) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +// A call to the form of DocumentRef.Update that represents the data as a list +// of field paths and their values. +type UpdatePathsTest struct { + DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"` + Precondition *google_firestore_v1beta1.Precondition `protobuf:"bytes,2,opt,name=precondition" json:"precondition,omitempty"` + // parallel sequences: field_paths[i] corresponds to json_values[i] + FieldPaths []*FieldPath `protobuf:"bytes,3,rep,name=field_paths,json=fieldPaths" json:"field_paths,omitempty"` + JsonValues []string `protobuf:"bytes,4,rep,name=json_values,json=jsonValues" json:"json_values,omitempty"` + Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,5,opt,name=request" json:"request,omitempty"` + IsError bool `protobuf:"varint,6,opt,name=is_error,json=isError" json:"is_error,omitempty"` +} + +func (m *UpdatePathsTest) Reset() { *m = UpdatePathsTest{} } +func (m *UpdatePathsTest) String() string { return proto.CompactTextString(m) } +func (*UpdatePathsTest) ProtoMessage() {} +func (*UpdatePathsTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *UpdatePathsTest) GetDocRefPath() string { + if m != nil { + return m.DocRefPath + } + return "" +} + +func (m *UpdatePathsTest) GetPrecondition() *google_firestore_v1beta1.Precondition { + if m != nil { + return m.Precondition + } + return nil +} + +func (m *UpdatePathsTest) GetFieldPaths() []*FieldPath { + if m != nil { + return m.FieldPaths + } + return nil +} + +func (m *UpdatePathsTest) GetJsonValues() []string { + if m != nil { + return m.JsonValues + } + return nil +} + +func (m *UpdatePathsTest) GetRequest() *google_firestore_v1beta14.CommitRequest { + if m != nil { + return m.Request + } + return nil +} + +func (m *UpdatePathsTest) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +// A call to DocmentRef.Delete +type DeleteTest struct { + DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"` + Precondition *google_firestore_v1beta1.Precondition `protobuf:"bytes,2,opt,name=precondition" json:"precondition,omitempty"` + Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,3,opt,name=request" json:"request,omitempty"` + IsError bool `protobuf:"varint,4,opt,name=is_error,json=isError" json:"is_error,omitempty"` +} + +func (m *DeleteTest) Reset() { *m = DeleteTest{} } +func (m *DeleteTest) String() string { return proto.CompactTextString(m) } +func (*DeleteTest) ProtoMessage() {} +func (*DeleteTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *DeleteTest) GetDocRefPath() string { + if m != nil { + return m.DocRefPath + } + return "" +} + +func (m *DeleteTest) GetPrecondition() *google_firestore_v1beta1.Precondition { + if m != nil { + return m.Precondition + } + return nil +} + +func (m *DeleteTest) GetRequest() *google_firestore_v1beta14.CommitRequest { + if m != nil { + return m.Request + } + return nil +} + +func (m *DeleteTest) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +// An option to the DocumentRef.Set call. +type SetOption struct { + All bool `protobuf:"varint,1,opt,name=all" json:"all,omitempty"` + Fields []*FieldPath `protobuf:"bytes,2,rep,name=fields" json:"fields,omitempty"` +} + +func (m *SetOption) Reset() { *m = SetOption{} } +func (m *SetOption) String() string { return proto.CompactTextString(m) } +func (*SetOption) ProtoMessage() {} +func (*SetOption) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *SetOption) GetAll() bool { + if m != nil { + return m.All + } + return false +} + +func (m *SetOption) GetFields() []*FieldPath { + if m != nil { + return m.Fields + } + return nil +} + +type QueryTest struct { + CollPath string `protobuf:"bytes,1,opt,name=coll_path,json=collPath" json:"coll_path,omitempty"` + Clauses []*Clause `protobuf:"bytes,2,rep,name=clauses" json:"clauses,omitempty"` + Query *google_firestore_v1beta12.StructuredQuery `protobuf:"bytes,3,opt,name=query" json:"query,omitempty"` + IsError bool `protobuf:"varint,4,opt,name=is_error,json=isError" json:"is_error,omitempty"` +} + +func (m *QueryTest) Reset() { *m = QueryTest{} } +func (m *QueryTest) String() string { return proto.CompactTextString(m) } +func (*QueryTest) ProtoMessage() {} +func (*QueryTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *QueryTest) GetCollPath() string { + if m != nil { + return m.CollPath + } + return "" +} + +func (m *QueryTest) GetClauses() []*Clause { + if m != nil { + return m.Clauses + } + return nil +} + +func (m *QueryTest) GetQuery() *google_firestore_v1beta12.StructuredQuery { + if m != nil { + return m.Query + } + return nil +} + +func (m *QueryTest) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +type Clause struct { + // Types that are valid to be assigned to Clause: + // *Clause_Select + // *Clause_Where + // *Clause_OrderBy + // *Clause_Offset + // *Clause_Limit + // *Clause_StartAt + // *Clause_StartAfter + // *Clause_EndAt + // *Clause_EndBefore + Clause isClause_Clause `protobuf_oneof:"clause"` +} + +func (m *Clause) Reset() { *m = Clause{} } +func (m *Clause) String() string { return proto.CompactTextString(m) } +func (*Clause) ProtoMessage() {} +func (*Clause) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +type isClause_Clause interface { + isClause_Clause() +} + +type Clause_Select struct { + Select *Select `protobuf:"bytes,1,opt,name=select,oneof"` +} +type Clause_Where struct { + Where *Where `protobuf:"bytes,2,opt,name=where,oneof"` +} +type Clause_OrderBy struct { + OrderBy *OrderBy `protobuf:"bytes,3,opt,name=order_by,json=orderBy,oneof"` +} +type Clause_Offset struct { + Offset int32 `protobuf:"varint,4,opt,name=offset,oneof"` +} +type Clause_Limit struct { + Limit int32 `protobuf:"varint,5,opt,name=limit,oneof"` +} +type Clause_StartAt struct { + StartAt *Cursor `protobuf:"bytes,6,opt,name=start_at,json=startAt,oneof"` +} +type Clause_StartAfter struct { + StartAfter *Cursor `protobuf:"bytes,7,opt,name=start_after,json=startAfter,oneof"` +} +type Clause_EndAt struct { + EndAt *Cursor `protobuf:"bytes,8,opt,name=end_at,json=endAt,oneof"` +} +type Clause_EndBefore struct { + EndBefore *Cursor `protobuf:"bytes,9,opt,name=end_before,json=endBefore,oneof"` +} + +func (*Clause_Select) isClause_Clause() {} +func (*Clause_Where) isClause_Clause() {} +func (*Clause_OrderBy) isClause_Clause() {} +func (*Clause_Offset) isClause_Clause() {} +func (*Clause_Limit) isClause_Clause() {} +func (*Clause_StartAt) isClause_Clause() {} +func (*Clause_StartAfter) isClause_Clause() {} +func (*Clause_EndAt) isClause_Clause() {} +func (*Clause_EndBefore) isClause_Clause() {} + +func (m *Clause) GetClause() isClause_Clause { + if m != nil { + return m.Clause + } + return nil +} + +func (m *Clause) GetSelect() *Select { + if x, ok := m.GetClause().(*Clause_Select); ok { + return x.Select + } + return nil +} + +func (m *Clause) GetWhere() *Where { + if x, ok := m.GetClause().(*Clause_Where); ok { + return x.Where + } + return nil +} + +func (m *Clause) GetOrderBy() *OrderBy { + if x, ok := m.GetClause().(*Clause_OrderBy); ok { + return x.OrderBy + } + return nil +} + +func (m *Clause) GetOffset() int32 { + if x, ok := m.GetClause().(*Clause_Offset); ok { + return x.Offset + } + return 0 +} + +func (m *Clause) GetLimit() int32 { + if x, ok := m.GetClause().(*Clause_Limit); ok { + return x.Limit + } + return 0 +} + +func (m *Clause) GetStartAt() *Cursor { + if x, ok := m.GetClause().(*Clause_StartAt); ok { + return x.StartAt + } + return nil +} + +func (m *Clause) GetStartAfter() *Cursor { + if x, ok := m.GetClause().(*Clause_StartAfter); ok { + return x.StartAfter + } + return nil +} + +func (m *Clause) GetEndAt() *Cursor { + if x, ok := m.GetClause().(*Clause_EndAt); ok { + return x.EndAt + } + return nil +} + +func (m *Clause) GetEndBefore() *Cursor { + if x, ok := m.GetClause().(*Clause_EndBefore); ok { + return x.EndBefore + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Clause) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Clause_OneofMarshaler, _Clause_OneofUnmarshaler, _Clause_OneofSizer, []interface{}{ + (*Clause_Select)(nil), + (*Clause_Where)(nil), + (*Clause_OrderBy)(nil), + (*Clause_Offset)(nil), + (*Clause_Limit)(nil), + (*Clause_StartAt)(nil), + (*Clause_StartAfter)(nil), + (*Clause_EndAt)(nil), + (*Clause_EndBefore)(nil), + } +} + +func _Clause_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Clause) + // clause + switch x := m.Clause.(type) { + case *Clause_Select: + b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Select); err != nil { + return err + } + case *Clause_Where: + b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Where); err != nil { + return err + } + case *Clause_OrderBy: + b.EncodeVarint(3<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.OrderBy); err != nil { + return err + } + case *Clause_Offset: + b.EncodeVarint(4<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.Offset)) + case *Clause_Limit: + b.EncodeVarint(5<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.Limit)) + case *Clause_StartAt: + b.EncodeVarint(6<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.StartAt); err != nil { + return err + } + case *Clause_StartAfter: + b.EncodeVarint(7<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.StartAfter); err != nil { + return err + } + case *Clause_EndAt: + b.EncodeVarint(8<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.EndAt); err != nil { + return err + } + case *Clause_EndBefore: + b.EncodeVarint(9<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.EndBefore); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Clause.Clause has unexpected type %T", x) + } + return nil +} + +func _Clause_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Clause) + switch tag { + case 1: // clause.select + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Select) + err := b.DecodeMessage(msg) + m.Clause = &Clause_Select{msg} + return true, err + case 2: // clause.where + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Where) + err := b.DecodeMessage(msg) + m.Clause = &Clause_Where{msg} + return true, err + case 3: // clause.order_by + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(OrderBy) + err := b.DecodeMessage(msg) + m.Clause = &Clause_OrderBy{msg} + return true, err + case 4: // clause.offset + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Clause = &Clause_Offset{int32(x)} + return true, err + case 5: // clause.limit + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Clause = &Clause_Limit{int32(x)} + return true, err + case 6: // clause.start_at + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Cursor) + err := b.DecodeMessage(msg) + m.Clause = &Clause_StartAt{msg} + return true, err + case 7: // clause.start_after + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Cursor) + err := b.DecodeMessage(msg) + m.Clause = &Clause_StartAfter{msg} + return true, err + case 8: // clause.end_at + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Cursor) + err := b.DecodeMessage(msg) + m.Clause = &Clause_EndAt{msg} + return true, err + case 9: // clause.end_before + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Cursor) + err := b.DecodeMessage(msg) + m.Clause = &Clause_EndBefore{msg} + return true, err + default: + return false, nil + } +} + +func _Clause_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Clause) + // clause + switch x := m.Clause.(type) { + case *Clause_Select: + s := proto.Size(x.Select) + n += proto.SizeVarint(1<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Clause_Where: + s := proto.Size(x.Where) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Clause_OrderBy: + s := proto.Size(x.OrderBy) + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Clause_Offset: + n += proto.SizeVarint(4<<3 | proto.WireVarint) + n += proto.SizeVarint(uint64(x.Offset)) + case *Clause_Limit: + n += proto.SizeVarint(5<<3 | proto.WireVarint) + n += proto.SizeVarint(uint64(x.Limit)) + case *Clause_StartAt: + s := proto.Size(x.StartAt) + n += proto.SizeVarint(6<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Clause_StartAfter: + s := proto.Size(x.StartAfter) + n += proto.SizeVarint(7<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Clause_EndAt: + s := proto.Size(x.EndAt) + n += proto.SizeVarint(8<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Clause_EndBefore: + s := proto.Size(x.EndBefore) + n += proto.SizeVarint(9<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type Select struct { + Fields []*FieldPath `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty"` +} + +func (m *Select) Reset() { *m = Select{} } +func (m *Select) String() string { return proto.CompactTextString(m) } +func (*Select) ProtoMessage() {} +func (*Select) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *Select) GetFields() []*FieldPath { + if m != nil { + return m.Fields + } + return nil +} + +type Where struct { + Path *FieldPath `protobuf:"bytes,1,opt,name=path" json:"path,omitempty"` + Op string `protobuf:"bytes,2,opt,name=op" json:"op,omitempty"` + JsonValue string `protobuf:"bytes,3,opt,name=json_value,json=jsonValue" json:"json_value,omitempty"` +} + +func (m *Where) Reset() { *m = Where{} } +func (m *Where) String() string { return proto.CompactTextString(m) } +func (*Where) ProtoMessage() {} +func (*Where) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *Where) GetPath() *FieldPath { + if m != nil { + return m.Path + } + return nil +} + +func (m *Where) GetOp() string { + if m != nil { + return m.Op + } + return "" +} + +func (m *Where) GetJsonValue() string { + if m != nil { + return m.JsonValue + } + return "" +} + +type OrderBy struct { + Path *FieldPath `protobuf:"bytes,1,opt,name=path" json:"path,omitempty"` + Direction string `protobuf:"bytes,2,opt,name=direction" json:"direction,omitempty"` +} + +func (m *OrderBy) Reset() { *m = OrderBy{} } +func (m *OrderBy) String() string { return proto.CompactTextString(m) } +func (*OrderBy) ProtoMessage() {} +func (*OrderBy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *OrderBy) GetPath() *FieldPath { + if m != nil { + return m.Path + } + return nil +} + +func (m *OrderBy) GetDirection() string { + if m != nil { + return m.Direction + } + return "" +} + +type Cursor struct { + // one of: + DocSnapshot *DocSnapshot `protobuf:"bytes,1,opt,name=doc_snapshot,json=docSnapshot" json:"doc_snapshot,omitempty"` + JsonValues []string `protobuf:"bytes,2,rep,name=json_values,json=jsonValues" json:"json_values,omitempty"` +} + +func (m *Cursor) Reset() { *m = Cursor{} } +func (m *Cursor) String() string { return proto.CompactTextString(m) } +func (*Cursor) ProtoMessage() {} +func (*Cursor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *Cursor) GetDocSnapshot() *DocSnapshot { + if m != nil { + return m.DocSnapshot + } + return nil +} + +func (m *Cursor) GetJsonValues() []string { + if m != nil { + return m.JsonValues + } + return nil +} + +type DocSnapshot struct { + Path string `protobuf:"bytes,1,opt,name=path" json:"path,omitempty"` + JsonData string `protobuf:"bytes,2,opt,name=json_data,json=jsonData" json:"json_data,omitempty"` +} + +func (m *DocSnapshot) Reset() { *m = DocSnapshot{} } +func (m *DocSnapshot) String() string { return proto.CompactTextString(m) } +func (*DocSnapshot) ProtoMessage() {} +func (*DocSnapshot) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +func (m *DocSnapshot) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *DocSnapshot) GetJsonData() string { + if m != nil { + return m.JsonData + } + return "" +} + +type FieldPath struct { + Field []string `protobuf:"bytes,1,rep,name=field" json:"field,omitempty"` +} + +func (m *FieldPath) Reset() { *m = FieldPath{} } +func (m *FieldPath) String() string { return proto.CompactTextString(m) } +func (*FieldPath) ProtoMessage() {} +func (*FieldPath) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *FieldPath) GetField() []string { + if m != nil { + return m.Field + } + return nil +} + +// A test of the Listen streaming RPC (a.k.a. FireStore watch). +// If the sequence of responses is provided to the implementation, +// it should produce the sequence of snapshots. +// If is_error is true, an error should occur after the snapshots. +// +// The tests assume that the query is +// Collection("projects/projectID/databases/(default)/documents/C").OrderBy("a", Ascending) +// +// The watch target ID used in these tests is 1. Test interpreters +// should either change their client's ID for testing, +// or change the ID in the tests before running them. +type ListenTest struct { + Responses []*google_firestore_v1beta14.ListenResponse `protobuf:"bytes,1,rep,name=responses" json:"responses,omitempty"` + Snapshots []*Snapshot `protobuf:"bytes,2,rep,name=snapshots" json:"snapshots,omitempty"` + IsError bool `protobuf:"varint,3,opt,name=is_error,json=isError" json:"is_error,omitempty"` +} + +func (m *ListenTest) Reset() { *m = ListenTest{} } +func (m *ListenTest) String() string { return proto.CompactTextString(m) } +func (*ListenTest) ProtoMessage() {} +func (*ListenTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *ListenTest) GetResponses() []*google_firestore_v1beta14.ListenResponse { + if m != nil { + return m.Responses + } + return nil +} + +func (m *ListenTest) GetSnapshots() []*Snapshot { + if m != nil { + return m.Snapshots + } + return nil +} + +func (m *ListenTest) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +type Snapshot struct { + Docs []*google_firestore_v1beta11.Document `protobuf:"bytes,1,rep,name=docs" json:"docs,omitempty"` + Changes []*DocChange `protobuf:"bytes,2,rep,name=changes" json:"changes,omitempty"` + ReadTime *google_protobuf1.Timestamp `protobuf:"bytes,3,opt,name=read_time,json=readTime" json:"read_time,omitempty"` +} + +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (m *Snapshot) String() string { return proto.CompactTextString(m) } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *Snapshot) GetDocs() []*google_firestore_v1beta11.Document { + if m != nil { + return m.Docs + } + return nil +} + +func (m *Snapshot) GetChanges() []*DocChange { + if m != nil { + return m.Changes + } + return nil +} + +func (m *Snapshot) GetReadTime() *google_protobuf1.Timestamp { + if m != nil { + return m.ReadTime + } + return nil +} + +type DocChange struct { + Kind DocChange_Kind `protobuf:"varint,1,opt,name=kind,enum=tests.DocChange_Kind" json:"kind,omitempty"` + Doc *google_firestore_v1beta11.Document `protobuf:"bytes,2,opt,name=doc" json:"doc,omitempty"` + OldIndex int32 `protobuf:"varint,3,opt,name=old_index,json=oldIndex" json:"old_index,omitempty"` + NewIndex int32 `protobuf:"varint,4,opt,name=new_index,json=newIndex" json:"new_index,omitempty"` +} + +func (m *DocChange) Reset() { *m = DocChange{} } +func (m *DocChange) String() string { return proto.CompactTextString(m) } +func (*DocChange) ProtoMessage() {} +func (*DocChange) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +func (m *DocChange) GetKind() DocChange_Kind { + if m != nil { + return m.Kind + } + return DocChange_KIND_UNSPECIFIED +} + +func (m *DocChange) GetDoc() *google_firestore_v1beta11.Document { + if m != nil { + return m.Doc + } + return nil +} + +func (m *DocChange) GetOldIndex() int32 { + if m != nil { + return m.OldIndex + } + return 0 +} + +func (m *DocChange) GetNewIndex() int32 { + if m != nil { + return m.NewIndex + } + return 0 +} + +func init() { + proto.RegisterType((*TestSuite)(nil), "tests.TestSuite") + proto.RegisterType((*Test)(nil), "tests.Test") + proto.RegisterType((*GetTest)(nil), "tests.GetTest") + proto.RegisterType((*CreateTest)(nil), "tests.CreateTest") + proto.RegisterType((*SetTest)(nil), "tests.SetTest") + proto.RegisterType((*UpdateTest)(nil), "tests.UpdateTest") + proto.RegisterType((*UpdatePathsTest)(nil), "tests.UpdatePathsTest") + proto.RegisterType((*DeleteTest)(nil), "tests.DeleteTest") + proto.RegisterType((*SetOption)(nil), "tests.SetOption") + proto.RegisterType((*QueryTest)(nil), "tests.QueryTest") + proto.RegisterType((*Clause)(nil), "tests.Clause") + proto.RegisterType((*Select)(nil), "tests.Select") + proto.RegisterType((*Where)(nil), "tests.Where") + proto.RegisterType((*OrderBy)(nil), "tests.OrderBy") + proto.RegisterType((*Cursor)(nil), "tests.Cursor") + proto.RegisterType((*DocSnapshot)(nil), "tests.DocSnapshot") + proto.RegisterType((*FieldPath)(nil), "tests.FieldPath") + proto.RegisterType((*ListenTest)(nil), "tests.ListenTest") + proto.RegisterType((*Snapshot)(nil), "tests.Snapshot") + proto.RegisterType((*DocChange)(nil), "tests.DocChange") + proto.RegisterEnum("tests.DocChange_Kind", DocChange_Kind_name, DocChange_Kind_value) +} + +func init() { proto.RegisterFile("test.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 1292 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xc4, 0x57, 0x4d, 0x73, 0xdb, 0xc4, + 0x1b, 0xaf, 0x6c, 0x4b, 0x96, 0x1e, 0xe7, 0xdf, 0xe6, 0xbf, 0x53, 0x3a, 0x26, 0xc0, 0x34, 0xd5, + 0x94, 0xc6, 0x6d, 0xc1, 0xa1, 0xe1, 0xed, 0xc0, 0x0c, 0x4c, 0x12, 0x3b, 0x6d, 0x28, 0x6d, 0x82, + 0xdc, 0x96, 0x4b, 0x66, 0x8c, 0xa2, 0x5d, 0x27, 0x02, 0x59, 0xeb, 0xee, 0xae, 0xfa, 0xf2, 0x39, + 0xb8, 0x73, 0x65, 0x60, 0x86, 0x03, 0x5f, 0x83, 0x13, 0x47, 0x3e, 0x03, 0x77, 0x38, 0x33, 0xfb, + 0x26, 0x59, 0x69, 0x5d, 0x72, 0x28, 0xe5, 0xb6, 0xfb, 0x3c, 0xbf, 0xe7, 0xfd, 0x65, 0x25, 0x00, + 0x41, 0xb8, 0xe8, 0xcf, 0x18, 0x15, 0x14, 0xb9, 0xf2, 0xcc, 0x57, 0xde, 0x3e, 0xa2, 0xf4, 0x28, + 0x23, 0xeb, 0x93, 0x94, 0x11, 0x2e, 0x28, 0x23, 0xeb, 0x8f, 0x6e, 0x1c, 0x12, 0x11, 0xdf, 0x58, + 0x4f, 0xe8, 0x74, 0x4a, 0x73, 0x8d, 0x5e, 0x59, 0x5b, 0x08, 0xc3, 0x34, 0x29, 0xa6, 0x24, 0x37, + 0x6a, 0x57, 0x7a, 0x0b, 0x81, 0x25, 0xc5, 0x20, 0x2f, 0x2f, 0x44, 0x3e, 0x2c, 0x08, 0x7b, 0x6a, + 0x50, 0x17, 0x0d, 0x4a, 0xdd, 0x0e, 0x8b, 0xc9, 0xba, 0x48, 0xa7, 0x84, 0x8b, 0x78, 0x3a, 0xd3, + 0x80, 0xb0, 0x0f, 0xc1, 0x3d, 0xc2, 0xc5, 0xa8, 0x48, 0x05, 0x41, 0x97, 0x40, 0x87, 0xd5, 0x75, + 0x56, 0x9b, 0xbd, 0xce, 0x46, 0xa7, 0xaf, 0x6e, 0x7d, 0x09, 0x88, 0x34, 0x27, 0xfc, 0xae, 0x09, + 0x2d, 0x79, 0x47, 0xab, 0xd0, 0xc1, 0x84, 0x27, 0x2c, 0x9d, 0x89, 0x94, 0xe6, 0x5d, 0x67, 0xd5, + 0xe9, 0x05, 0xd1, 0x3c, 0x09, 0x85, 0xd0, 0x3c, 0x22, 0xa2, 0xdb, 0x58, 0x75, 0x7a, 0x9d, 0x8d, + 0xb3, 0x46, 0xd7, 0x4d, 0x22, 0xa4, 0xf8, 0xad, 0x33, 0x91, 0x64, 0xa2, 0xeb, 0xe0, 0x25, 0x8c, + 0xc4, 0x82, 0x74, 0x9b, 0x0a, 0xf6, 0x7f, 0x03, 0xdb, 0x56, 0x44, 0x83, 0x34, 0x10, 0xa9, 0x90, + 0x13, 0xd1, 0x6d, 0xd5, 0x14, 0x8e, 0x2a, 0x85, 0x5c, 0x2b, 0x2c, 0x66, 0x58, 0x2a, 0x74, 0x6b, + 0x0a, 0xef, 0x2b, 0xa2, 0x55, 0xa8, 0x21, 0xe8, 0x13, 0x58, 0xd2, 0xa7, 0xf1, 0x2c, 0x16, 0xc7, + 0xbc, 0xeb, 0x29, 0x91, 0x0b, 0x35, 0x91, 0x7d, 0xc9, 0x31, 0x72, 0x9d, 0xa2, 0x22, 0x49, 0x4b, + 0x98, 0x64, 0x44, 0x90, 0x6e, 0xbb, 0x66, 0x69, 0xa0, 0x88, 0xd6, 0x92, 0x86, 0xa0, 0x1e, 0xb8, + 0xaa, 0x2c, 0x5d, 0x5f, 0x61, 0x97, 0x0d, 0xf6, 0x4b, 0x49, 0x33, 0x50, 0x0d, 0x90, 0x6a, 0xb3, + 0x94, 0x0b, 0x92, 0x77, 0x83, 0x9a, 0xda, 0x2f, 0x14, 0xd1, 0xaa, 0xd5, 0x90, 0x2d, 0x0f, 0x5a, + 0x92, 0x1b, 0x72, 0x68, 0x9b, 0xc4, 0xa2, 0x55, 0x58, 0xc2, 0x34, 0x19, 0x33, 0x32, 0x51, 0x41, + 0x99, 0xc2, 0x00, 0xa6, 0x49, 0x44, 0x26, 0xd2, 0x73, 0xb4, 0x03, 0x6d, 0x46, 0x1e, 0x16, 0x84, + 0xdb, 0xda, 0xbc, 0xd3, 0xd7, 0x5d, 0xd2, 0xaf, 0x7a, 0xcc, 0xf4, 0x92, 0x2c, 0xd7, 0xc0, 0x74, + 0x68, 0xa4, 0x65, 0x22, 0x2b, 0x1c, 0xfe, 0xe8, 0x00, 0x54, 0x75, 0x3a, 0x85, 0xe1, 0x37, 0x20, + 0xf8, 0x86, 0xd3, 0x7c, 0x8c, 0x63, 0x11, 0x2b, 0xd3, 0x41, 0xe4, 0x4b, 0xc2, 0x20, 0x16, 0x31, + 0xda, 0xac, 0xbc, 0xd2, 0xad, 0xb0, 0xb6, 0xd8, 0xab, 0x6d, 0x3a, 0x9d, 0xa6, 0xcf, 0x38, 0x84, + 0x5e, 0x07, 0x3f, 0xe5, 0x63, 0xc2, 0x18, 0x65, 0xaa, 0x49, 0xfc, 0xa8, 0x9d, 0xf2, 0xa1, 0xbc, + 0x86, 0xbf, 0x39, 0xd0, 0x1e, 0x9d, 0x3a, 0x43, 0x3d, 0xf0, 0xa8, 0x6e, 0xeb, 0x46, 0xad, 0x5c, + 0x23, 0x22, 0xf6, 0x14, 0x3d, 0x32, 0xfc, 0x7a, 0x48, 0xcd, 0xc5, 0x21, 0xb5, 0x5e, 0x42, 0x48, + 0x6e, 0x3d, 0xa4, 0x3f, 0x1d, 0x80, 0xaa, 0xab, 0x4f, 0x11, 0xd5, 0xe7, 0xb0, 0x34, 0x63, 0x24, + 0xa1, 0x39, 0x4e, 0xe7, 0x62, 0xbb, 0xb2, 0xd8, 0xa7, 0xfd, 0x39, 0x74, 0x54, 0x93, 0xfd, 0x2f, + 0xe3, 0xfe, 0xa5, 0x01, 0xe7, 0x4e, 0x8c, 0xe6, 0x2b, 0x0e, 0xfe, 0x06, 0x74, 0x26, 0x29, 0xc9, + 0xb0, 0xd9, 0x1a, 0x4d, 0xb5, 0x2c, 0x6d, 0x8f, 0xec, 0x48, 0x8e, 0x34, 0x19, 0xc1, 0xc4, 0x1e, + 0x39, 0xba, 0x08, 0x1d, 0x95, 0xaf, 0x47, 0x71, 0x56, 0x10, 0xde, 0x6d, 0xad, 0x36, 0xa5, 0x7f, + 0x92, 0xf4, 0x40, 0x51, 0xe6, 0x73, 0xe6, 0xbe, 0x84, 0x9c, 0x79, 0xf5, 0x9c, 0xfd, 0xee, 0x00, + 0x54, 0x7b, 0xe9, 0x15, 0xa7, 0xeb, 0xdf, 0x9d, 0xec, 0x9b, 0x10, 0x94, 0x63, 0x89, 0x96, 0xa1, + 0x19, 0x67, 0x99, 0x8a, 0xc7, 0x8f, 0xe4, 0x51, 0x8e, 0xb2, 0x2a, 0x03, 0xef, 0x36, 0x16, 0x94, + 0xc9, 0xf0, 0xc3, 0x9f, 0x1d, 0x08, 0xca, 0x7d, 0x2c, 0x1b, 0x3c, 0xa1, 0x59, 0x36, 0x9f, 0x1f, + 0x5f, 0x12, 0x54, 0x76, 0xd6, 0xa0, 0x9d, 0x64, 0x71, 0xc1, 0x89, 0xd5, 0xfa, 0x3f, 0xfb, 0x6c, + 0x29, 0x6a, 0x64, 0xb9, 0xe8, 0x33, 0xbb, 0xf6, 0x75, 0xe0, 0x57, 0x17, 0x07, 0x3e, 0x12, 0xac, + 0x48, 0x44, 0xc1, 0x08, 0x56, 0x3e, 0xd8, 0xd7, 0xe0, 0x05, 0x81, 0xff, 0xd5, 0x00, 0x4f, 0xdb, + 0x43, 0x6b, 0xe0, 0x71, 0x92, 0x91, 0x44, 0x28, 0x4f, 0x2b, 0x77, 0x46, 0x8a, 0x28, 0xdf, 0x0b, + 0xcd, 0x46, 0x97, 0xc1, 0x7d, 0x7c, 0x4c, 0x18, 0x31, 0xf5, 0x5c, 0x32, 0xb8, 0xaf, 0x24, 0x4d, + 0x3e, 0x41, 0x8a, 0x89, 0xae, 0x83, 0x4f, 0x19, 0x26, 0x6c, 0x7c, 0x68, 0x1d, 0xb7, 0x8f, 0xed, + 0x9e, 0x24, 0x6f, 0x3d, 0xbd, 0x75, 0x26, 0x6a, 0x53, 0x7d, 0x44, 0x5d, 0xf0, 0xe8, 0x64, 0x62, + 0xdf, 0x65, 0x57, 0x1a, 0xd3, 0x77, 0x74, 0x01, 0xdc, 0x2c, 0x9d, 0xa6, 0xba, 0xa1, 0x25, 0x43, + 0x5f, 0xd1, 0x35, 0xf0, 0xb9, 0x88, 0x99, 0x18, 0xc7, 0xc2, 0xbc, 0xb8, 0x65, 0xfa, 0x0a, 0xc6, + 0x29, 0x93, 0xda, 0x15, 0x60, 0x53, 0xa0, 0xf7, 0xa0, 0x63, 0xb0, 0x13, 0x41, 0x98, 0x79, 0x69, + 0x9f, 0x81, 0x83, 0x86, 0x4b, 0x08, 0xba, 0x02, 0x1e, 0xc9, 0xb1, 0xd4, 0xed, 0x3f, 0x1f, 0xec, + 0x92, 0x1c, 0x6f, 0x0a, 0xd4, 0x07, 0x90, 0xb8, 0x43, 0x32, 0xa1, 0x8c, 0x98, 0xb7, 0xf6, 0x19, + 0x6c, 0x40, 0x72, 0xbc, 0xa5, 0x10, 0x5b, 0x3e, 0x78, 0xba, 0xaa, 0xe1, 0x06, 0x78, 0x3a, 0xb1, + 0x73, 0xcd, 0xe5, 0xfc, 0x43, 0x73, 0x1d, 0x80, 0xab, 0x92, 0x8c, 0x2e, 0x43, 0xab, 0x6c, 0xa9, + 0xe7, 0x09, 0x28, 0x2e, 0x3a, 0x0b, 0x0d, 0x3a, 0x33, 0x4f, 0x64, 0x83, 0xce, 0xd0, 0x5b, 0x00, + 0xd5, 0xfa, 0x30, 0xfb, 0x36, 0x28, 0xb7, 0x47, 0x78, 0x07, 0xda, 0xa6, 0x32, 0xa7, 0xd4, 0xff, + 0x26, 0x04, 0x38, 0x65, 0x24, 0x29, 0x67, 0x3b, 0x88, 0x2a, 0x42, 0xf8, 0x35, 0x78, 0x3a, 0x03, + 0xe8, 0x43, 0xbd, 0x28, 0x78, 0x1e, 0xcf, 0xf8, 0x31, 0xb5, 0xed, 0x85, 0xec, 0x97, 0x0e, 0x4d, + 0x46, 0x86, 0x13, 0x75, 0x70, 0x75, 0x39, 0xb9, 0xed, 0x1a, 0x27, 0xb7, 0x5d, 0xf8, 0x29, 0x74, + 0xe6, 0x84, 0x11, 0x9a, 0x73, 0x3a, 0x30, 0x2e, 0xbe, 0xe8, 0x63, 0x21, 0xbc, 0x04, 0x41, 0x19, + 0x12, 0x3a, 0x0f, 0xae, 0xca, 0xb2, 0x2a, 0x42, 0x10, 0xe9, 0x4b, 0xf8, 0xbd, 0x03, 0x50, 0x7d, + 0x33, 0xa1, 0x1d, 0x08, 0x18, 0xe1, 0x33, 0x9a, 0xcb, 0xa1, 0xd5, 0xd5, 0xea, 0x2d, 0x9e, 0x46, + 0x2d, 0x18, 0x19, 0x81, 0xa8, 0x12, 0x45, 0xef, 0x42, 0x60, 0xb3, 0x61, 0x87, 0xff, 0x9c, 0x9d, + 0x36, 0x9b, 0x8b, 0x0a, 0x51, 0x9b, 0xdf, 0x66, 0x7d, 0x7e, 0x7f, 0x70, 0xc0, 0x2f, 0x33, 0xf0, + 0x11, 0xb4, 0x30, 0x4d, 0xac, 0x67, 0xe1, 0x62, 0xcf, 0xca, 0xaf, 0x31, 0x85, 0x47, 0xd7, 0xa0, + 0x9d, 0x1c, 0xc7, 0xf9, 0x11, 0x39, 0xb9, 0xdf, 0x06, 0x34, 0xd9, 0x56, 0x8c, 0xc8, 0x02, 0xd0, + 0xc7, 0x32, 0x05, 0x31, 0x1e, 0xcb, 0x5f, 0x00, 0x33, 0xd7, 0x2b, 0xd6, 0x90, 0xfd, 0x3f, 0xe8, + 0xdf, 0xb3, 0xff, 0x07, 0x91, 0x2f, 0xc1, 0xf2, 0x1a, 0xfe, 0xe1, 0x40, 0x50, 0xea, 0x43, 0x57, + 0xa1, 0xf5, 0x6d, 0x9a, 0x63, 0x55, 0xac, 0xb3, 0x1b, 0xaf, 0x9d, 0xb4, 0xd7, 0xbf, 0x9d, 0xe6, + 0x38, 0x52, 0x10, 0xf4, 0x01, 0x34, 0x31, 0x4d, 0xcc, 0xb2, 0x39, 0x4d, 0x50, 0x12, 0x2e, 0x2b, + 0x4f, 0x33, 0x3c, 0x4e, 0x73, 0x4c, 0x9e, 0x28, 0x3f, 0xdd, 0xc8, 0xa7, 0x19, 0xde, 0x95, 0x77, + 0xc9, 0xcc, 0xc9, 0x63, 0xc3, 0x6c, 0x69, 0x66, 0x4e, 0x1e, 0x2b, 0x66, 0xb8, 0x05, 0x2d, 0x69, + 0x1d, 0x9d, 0x87, 0xe5, 0xdb, 0xbb, 0x77, 0x07, 0xe3, 0xfb, 0x77, 0x47, 0xfb, 0xc3, 0xed, 0xdd, + 0x9d, 0xdd, 0xe1, 0x60, 0xf9, 0x0c, 0x0a, 0xc0, 0xdd, 0x1c, 0x0c, 0x86, 0x83, 0x65, 0x07, 0x75, + 0xa0, 0x1d, 0x0d, 0xef, 0xec, 0x3d, 0x18, 0x0e, 0x96, 0x1b, 0x68, 0x09, 0xfc, 0x3b, 0x7b, 0x03, + 0x8d, 0x6a, 0x6e, 0x3d, 0x81, 0x2b, 0x09, 0x9d, 0x5a, 0x5f, 0x93, 0x8c, 0x16, 0x78, 0xce, 0xe3, + 0x84, 0xe6, 0x13, 0xca, 0xa6, 0x71, 0x9e, 0x90, 0x9f, 0x1a, 0xe1, 0x4d, 0x0d, 0xda, 0x56, 0xa0, + 0x9d, 0x12, 0x74, 0x4f, 0x65, 0x64, 0x5f, 0xa6, 0xf4, 0xd7, 0x46, 0x4f, 0x83, 0x0e, 0x14, 0xe8, + 0xa0, 0x04, 0x1d, 0x28, 0xd0, 0xc1, 0x76, 0xa5, 0xef, 0xd0, 0x53, 0x45, 0x78, 0xff, 0xef, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xc2, 0x7a, 0x63, 0xd5, 0x67, 0x0e, 0x00, 0x00, +} diff --git a/vendor/cloud.google.com/go/firestore/integration_test.go b/vendor/cloud.google.com/go/firestore/integration_test.go new file mode 100644 index 0000000000..b02b2d354b --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/integration_test.go @@ -0,0 +1,1146 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "flag" + "fmt" + "log" + "math" + "os" + "path/filepath" + "runtime" + "sort" + "testing" + "time" + + "cloud.google.com/go/internal/pretty" + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/internal/uid" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/genproto/googleapis/type/latlng" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +func TestMain(m *testing.M) { + initIntegrationTest() + status := m.Run() + cleanupIntegrationTest() + os.Exit(status) +} + +const ( + envProjID = "GCLOUD_TESTS_GOLANG_FIRESTORE_PROJECT_ID" + envPrivateKey = "GCLOUD_TESTS_GOLANG_FIRESTORE_KEY" +) + +var ( + iClient *Client + iColl *CollectionRef + collectionIDs = uid.NewSpace("go-integration-test", nil) +) + +func initIntegrationTest() { + flag.Parse() // needed for testing.Short() + if testing.Short() { + return + } + ctx := context.Background() + testProjectID := os.Getenv(envProjID) + if testProjectID == "" { + log.Println("Integration tests skipped. See CONTRIBUTING.md for details") + return + } + ts := testutil.TokenSourceEnv(ctx, envPrivateKey, + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/datastore") + if ts == nil { + log.Fatal("The project key must be set. See CONTRIBUTING.md for details") + } + ti := &testInterceptor{dbPath: "projects/" + testProjectID + "/databases/(default)"} + c, err := NewClient(ctx, testProjectID, + option.WithTokenSource(ts), + option.WithGRPCDialOption(grpc.WithUnaryInterceptor(ti.interceptUnary)), + option.WithGRPCDialOption(grpc.WithStreamInterceptor(ti.interceptStream)), + ) + if err != nil { + log.Fatalf("NewClient: %v", err) + } + iClient = c + iColl = c.Collection(collectionIDs.New()) + refDoc := iColl.NewDoc() + integrationTestMap["ref"] = refDoc + wantIntegrationTestMap["ref"] = refDoc + integrationTestStruct.Ref = refDoc +} + +type testInterceptor struct { + dbPath string +} + +func (ti *testInterceptor) interceptUnary(ctx context.Context, method string, req, res interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + ti.checkMetadata(ctx, method) + return invoker(ctx, method, req, res, cc, opts...) +} + +func (ti *testInterceptor) interceptStream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + ti.checkMetadata(ctx, method) + return streamer(ctx, desc, cc, method, opts...) +} + +func (ti *testInterceptor) checkMetadata(ctx context.Context, method string) { + md, ok := metadata.FromOutgoingContext(ctx) + if !ok { + log.Fatalf("method %s: bad metadata", method) + } + for _, h := range []string{"google-cloud-resource-prefix", "x-goog-api-client"} { + v, ok := md[h] + if !ok { + log.Fatalf("method %s, header %s missing", method, h) + } + if len(v) != 1 { + log.Fatalf("method %s, header %s: bad value %v", method, h, v) + } + } + v := md["google-cloud-resource-prefix"][0] + if v != ti.dbPath { + log.Fatalf("method %s: bad resource prefix header: %q", method, v) + } +} + +func cleanupIntegrationTest() { + if iClient == nil { + return + } + // TODO(jba): delete everything in integrationColl. + iClient.Close() +} + +// integrationClient should be called by integration tests to get a valid client. It will never +// return nil. If integrationClient returns, an integration test can proceed without +// further checks. +func integrationClient(t *testing.T) *Client { + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + if iClient == nil { + t.SkipNow() // log message printed in initIntegrationTest + } + return iClient +} + +func integrationColl(t *testing.T) *CollectionRef { + _ = integrationClient(t) + return iColl +} + +type integrationTestStructType struct { + Int int + Str string + Bool bool + Float float32 + Null interface{} + Bytes []byte + Time time.Time + Geo, NilGeo *latlng.LatLng + Ref *DocumentRef +} + +var ( + integrationTime = time.Date(2017, 3, 20, 1, 2, 3, 456789, time.UTC) + // Firestore times are accurate only to microseconds. + wantIntegrationTime = time.Date(2017, 3, 20, 1, 2, 3, 456000, time.UTC) + + integrationGeo = &latlng.LatLng{Latitude: 30, Longitude: 70} + + // Use this when writing a doc. + integrationTestMap = map[string]interface{}{ + "int": 1, + "str": "two", + "bool": true, + "float": 3.14, + "null": nil, + "bytes": []byte("bytes"), + "*": map[string]interface{}{"`": 4}, + "time": integrationTime, + "geo": integrationGeo, + "ref": nil, // populated by initIntegrationTest + } + + // The returned data is slightly different. + wantIntegrationTestMap = map[string]interface{}{ + "int": int64(1), + "str": "two", + "bool": true, + "float": 3.14, + "null": nil, + "bytes": []byte("bytes"), + "*": map[string]interface{}{"`": int64(4)}, + "time": wantIntegrationTime, + "geo": integrationGeo, + "ref": nil, // populated by initIntegrationTest + } + + integrationTestStruct = integrationTestStructType{ + Int: 1, + Str: "two", + Bool: true, + Float: 3.14, + Null: nil, + Bytes: []byte("bytes"), + Time: integrationTime, + Geo: integrationGeo, + NilGeo: nil, + Ref: nil, // populated by initIntegrationTest + } +) + +func TestIntegration_Create(t *testing.T) { + ctx := context.Background() + doc := integrationColl(t).NewDoc() + start := time.Now() + h := testHelper{t} + wr := h.mustCreate(doc, integrationTestMap) + end := time.Now() + checkTimeBetween(t, wr.UpdateTime, start, end) + _, err := doc.Create(ctx, integrationTestMap) + codeEq(t, "Create on a present doc", codes.AlreadyExists, err) + // OK to create an empty document. + _, err = integrationColl(t).NewDoc().Create(ctx, map[string]interface{}{}) + codeEq(t, "Create empty doc", codes.OK, err) +} + +func TestIntegration_Get(t *testing.T) { + ctx := context.Background() + doc := integrationColl(t).NewDoc() + h := testHelper{t} + h.mustCreate(doc, integrationTestMap) + ds := h.mustGet(doc) + if ds.CreateTime != ds.UpdateTime { + t.Errorf("create time %s != update time %s", ds.CreateTime, ds.UpdateTime) + } + got := ds.Data() + if want := wantIntegrationTestMap; !testEqual(got, want) { + t.Errorf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want)) + } + + doc = integrationColl(t).NewDoc() + empty := map[string]interface{}{} + h.mustCreate(doc, empty) + ds = h.mustGet(doc) + if ds.CreateTime != ds.UpdateTime { + t.Errorf("create time %s != update time %s", ds.CreateTime, ds.UpdateTime) + } + if got, want := ds.Data(), empty; !testEqual(got, want) { + t.Errorf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want)) + } + + ds, err := integrationColl(t).NewDoc().Get(ctx) + codeEq(t, "Get on a missing doc", codes.NotFound, err) + if ds == nil || ds.Exists() { + t.Fatal("got nil or existing doc snapshot, want !ds.Exists") + } + if ds.ReadTime.IsZero() { + t.Error("got zero read time") + } +} + +func TestIntegration_GetAll(t *testing.T) { + type getAll struct{ N int } + + h := testHelper{t} + coll := integrationColl(t) + ctx := context.Background() + var docRefs []*DocumentRef + for i := 0; i < 5; i++ { + doc := coll.NewDoc() + docRefs = append(docRefs, doc) + if i != 3 { + h.mustCreate(doc, getAll{N: i}) + } + } + docSnapshots, err := iClient.GetAll(ctx, docRefs) + if err != nil { + t.Fatal(err) + } + if got, want := len(docSnapshots), len(docRefs); got != want { + t.Fatalf("got %d snapshots, want %d", got, want) + } + for i, ds := range docSnapshots { + if i == 3 { + if ds == nil || ds.Exists() { + t.Fatal("got nil or existing doc snapshot, want !ds.Exists") + } + err := ds.DataTo(nil) + codeEq(t, "DataTo on a missing doc", codes.NotFound, err) + } else { + var got getAll + if err := ds.DataTo(&got); err != nil { + t.Fatal(err) + } + want := getAll{N: i} + if got != want { + t.Errorf("%d: got %+v, want %+v", i, got, want) + } + } + if ds.ReadTime.IsZero() { + t.Errorf("%d: got zero read time", i) + } + } +} + +func TestIntegration_Add(t *testing.T) { + start := time.Now() + _, wr, err := integrationColl(t).Add(context.Background(), integrationTestMap) + if err != nil { + t.Fatal(err) + } + end := time.Now() + checkTimeBetween(t, wr.UpdateTime, start, end) +} + +func TestIntegration_Set(t *testing.T) { + coll := integrationColl(t) + ctx := context.Background() + h := testHelper{t} + + // Set Should be able to create a new doc. + doc := coll.NewDoc() + wr1, err := doc.Set(ctx, integrationTestMap) + if err != nil { + t.Fatal(err) + } + // Calling Set on the doc completely replaces the contents. + // The update time should increase. + newData := map[string]interface{}{ + "str": "change", + "x": "1", + } + wr2, err := doc.Set(ctx, newData) + if err != nil { + t.Fatal(err) + } + if !wr1.UpdateTime.Before(wr2.UpdateTime) { + t.Errorf("update time did not increase: old=%s, new=%s", wr1.UpdateTime, wr2.UpdateTime) + } + ds := h.mustGet(doc) + if got := ds.Data(); !testEqual(got, newData) { + t.Errorf("got %v, want %v", got, newData) + } + + newData = map[string]interface{}{ + "str": "1", + "x": "2", + "y": "3", + } + // SetOptions: + // Only fields mentioned in the Merge option will be changed. + // In this case, "str" will not be changed to "1". + wr3, err := doc.Set(ctx, newData, Merge([]string{"x"}, []string{"y"})) + if err != nil { + t.Fatal(err) + } + ds = h.mustGet(doc) + want := map[string]interface{}{ + "str": "change", + "x": "2", + "y": "3", + } + if got := ds.Data(); !testEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } + if !wr2.UpdateTime.Before(wr3.UpdateTime) { + t.Errorf("update time did not increase: old=%s, new=%s", wr2.UpdateTime, wr3.UpdateTime) + } + + // Another way to change only x and y is to pass a map with only + // those keys, and use MergeAll. + wr4, err := doc.Set(ctx, map[string]interface{}{"x": "4", "y": "5"}, MergeAll) + if err != nil { + t.Fatal(err) + } + ds = h.mustGet(doc) + want = map[string]interface{}{ + "str": "change", + "x": "4", + "y": "5", + } + if got := ds.Data(); !testEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } + if !wr3.UpdateTime.Before(wr4.UpdateTime) { + t.Errorf("update time did not increase: old=%s, new=%s", wr3.UpdateTime, wr4.UpdateTime) + } + + // Writing an empty doc with MergeAll should create the doc. + doc2 := coll.NewDoc() + want = map[string]interface{}{} + _, err = doc2.Set(ctx, want, MergeAll) + if err != nil { + t.Fatal(err) + } + ds = h.mustGet(doc2) + if got := ds.Data(); !testEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestIntegration_Delete(t *testing.T) { + ctx := context.Background() + doc := integrationColl(t).NewDoc() + h := testHelper{t} + h.mustCreate(doc, integrationTestMap) + wr := h.mustDelete(doc) + // Confirm that doc doesn't exist. + if _, err := doc.Get(ctx); grpc.Code(err) != codes.NotFound { + t.Fatalf("got error <%v>, want NotFound", err) + } + + er := func(_ *WriteResult, err error) error { return err } + + codeEq(t, "Delete on a missing doc", codes.OK, + er(doc.Delete(ctx))) + // TODO(jba): confirm that the server should return InvalidArgument instead of + // FailedPrecondition. + wr = h.mustCreate(doc, integrationTestMap) + codeEq(t, "Delete with wrong LastUpdateTime", codes.FailedPrecondition, + er(doc.Delete(ctx, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond))))) + codeEq(t, "Delete with right LastUpdateTime", codes.OK, + er(doc.Delete(ctx, LastUpdateTime(wr.UpdateTime)))) +} + +func TestIntegration_Update(t *testing.T) { + ctx := context.Background() + doc := integrationColl(t).NewDoc() + h := testHelper{t} + + h.mustCreate(doc, integrationTestMap) + fpus := []Update{ + {Path: "bool", Value: false}, + {Path: "time", Value: 17}, + {FieldPath: []string{"*", "`"}, Value: 18}, + {Path: "null", Value: Delete}, + {Path: "noSuchField", Value: Delete}, // deleting a non-existent field is a no-op + } + wr := h.mustUpdate(doc, fpus) + ds := h.mustGet(doc) + got := ds.Data() + want := copyMap(wantIntegrationTestMap) + want["bool"] = false + want["time"] = int64(17) + want["*"] = map[string]interface{}{"`": int64(18)} + delete(want, "null") + if !testEqual(got, want) { + t.Errorf("got\n%#v\nwant\n%#v", got, want) + } + + er := func(_ *WriteResult, err error) error { return err } + + codeEq(t, "Update on missing doc", codes.NotFound, + er(integrationColl(t).NewDoc().Update(ctx, fpus))) + codeEq(t, "Update with wrong LastUpdateTime", codes.FailedPrecondition, + er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond))))) + codeEq(t, "Update with right LastUpdateTime", codes.OK, + er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime)))) +} + +func TestIntegration_Collections(t *testing.T) { + ctx := context.Background() + c := integrationClient(t) + h := testHelper{t} + got, err := c.Collections(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + // There should be at least one collection. + if len(got) == 0 { + t.Error("got 0 top-level collections, want at least one") + } + + doc := integrationColl(t).NewDoc() + got, err = doc.Collections(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + if len(got) != 0 { + t.Errorf("got %d collections, want 0", len(got)) + } + var want []*CollectionRef + for i := 0; i < 3; i++ { + id := collectionIDs.New() + cr := doc.Collection(id) + want = append(want, cr) + h.mustCreate(cr.NewDoc(), integrationTestMap) + } + got, err = doc.Collections(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + if !testEqual(got, want) { + t.Errorf("got\n%#v\nwant\n%#v", got, want) + } +} + +func TestIntegration_ServerTimestamp(t *testing.T) { + type S struct { + A int + B time.Time + C time.Time `firestore:"C.C,serverTimestamp"` + D map[string]interface{} + E time.Time `firestore:",omitempty,serverTimestamp"` + } + data := S{ + A: 1, + B: aTime, + // C is unset, so will get the server timestamp. + D: map[string]interface{}{"x": ServerTimestamp}, + // E is unset, so will get the server timestamp. + } + h := testHelper{t} + doc := integrationColl(t).NewDoc() + // Bound times of the RPC, with some slack for clock skew. + start := time.Now() + h.mustCreate(doc, data) + end := time.Now() + ds := h.mustGet(doc) + var got S + if err := ds.DataTo(&got); err != nil { + t.Fatal(err) + } + if !testEqual(got.B, aTime) { + t.Errorf("B: got %s, want %s", got.B, aTime) + } + checkTimeBetween(t, got.C, start, end) + if g, w := got.D["x"], got.C; !testEqual(g, w) { + t.Errorf(`D["x"] = %s, want equal to C (%s)`, g, w) + } + if g, w := got.E, got.C; !testEqual(g, w) { + t.Errorf(`E = %s, want equal to C (%s)`, g, w) + } +} + +func TestIntegration_MergeServerTimestamp(t *testing.T) { + ctx := context.Background() + doc := integrationColl(t).NewDoc() + h := testHelper{t} + + // Create a doc with an ordinary field "a" and a ServerTimestamp field "b". + _, err := doc.Set(ctx, map[string]interface{}{ + "a": 1, + "b": ServerTimestamp}) + if err != nil { + t.Fatal(err) + } + docSnap := h.mustGet(doc) + data1 := docSnap.Data() + // Merge with a document with a different value of "a". However, + // specify only "b" in the list of merge fields. + _, err = doc.Set(ctx, + map[string]interface{}{"a": 2, "b": ServerTimestamp}, + Merge([]string{"b"})) + if err != nil { + t.Fatal(err) + } + // The result should leave "a" unchanged, while "b" is updated. + docSnap = h.mustGet(doc) + data2 := docSnap.Data() + if got, want := data2["a"], data1["a"]; got != want { + t.Errorf("got %v, want %v", got, want) + } + t1 := data1["b"].(time.Time) + t2 := data2["b"].(time.Time) + if !t1.Before(t2) { + t.Errorf("got t1=%s, t2=%s; want t1 before t2", t1, t2) + } +} + +func TestIntegration_MergeNestedServerTimestamp(t *testing.T) { + ctx := context.Background() + doc := integrationColl(t).NewDoc() + h := testHelper{t} + + // Create a doc with an ordinary field "a" a ServerTimestamp field "b", + // and a second ServerTimestamp field "c.d". + _, err := doc.Set(ctx, map[string]interface{}{ + "a": 1, + "b": ServerTimestamp, + "c": map[string]interface{}{"d": ServerTimestamp}, + }) + if err != nil { + t.Fatal(err) + } + data1 := h.mustGet(doc).Data() + // Merge with a document with a different value of "a". However, + // specify only "c.d" in the list of merge fields. + _, err = doc.Set(ctx, + map[string]interface{}{ + "a": 2, + "b": ServerTimestamp, + "c": map[string]interface{}{"d": ServerTimestamp}, + }, + Merge([]string{"c", "d"})) + if err != nil { + t.Fatal(err) + } + // The result should leave "a" and "b" unchanged, while "c.d" is updated. + data2 := h.mustGet(doc).Data() + if got, want := data2["a"], data1["a"]; got != want { + t.Errorf("a: got %v, want %v", got, want) + } + want := data1["b"].(time.Time) + got := data2["b"].(time.Time) + if !got.Equal(want) { + t.Errorf("b: got %s, want %s", got, want) + } + t1 := data1["c"].(map[string]interface{})["d"].(time.Time) + t2 := data2["c"].(map[string]interface{})["d"].(time.Time) + if !t1.Before(t2) { + t.Errorf("got t1=%s, t2=%s; want t1 before t2", t1, t2) + } +} + +func TestIntegration_WriteBatch(t *testing.T) { + ctx := context.Background() + b := integrationClient(t).Batch() + h := testHelper{t} + doc1 := iColl.NewDoc() + doc2 := iColl.NewDoc() + b.Create(doc1, integrationTestMap) + b.Set(doc2, integrationTestMap) + b.Update(doc1, []Update{{Path: "bool", Value: false}}) + b.Update(doc1, []Update{{Path: "str", Value: Delete}}) + + wrs, err := b.Commit(ctx) + if err != nil { + t.Fatal(err) + } + if got, want := len(wrs), 4; got != want { + t.Fatalf("got %d WriteResults, want %d", got, want) + } + got1 := h.mustGet(doc1).Data() + want := copyMap(wantIntegrationTestMap) + want["bool"] = false + delete(want, "str") + if !testEqual(got1, want) { + t.Errorf("got\n%#v\nwant\n%#v", got1, want) + } + got2 := h.mustGet(doc2).Data() + if !testEqual(got2, wantIntegrationTestMap) { + t.Errorf("got\n%#v\nwant\n%#v", got2, wantIntegrationTestMap) + } + // TODO(jba): test two updates to the same document when it is supported. + // TODO(jba): test verify when it is supported. +} + +func TestIntegration_Query(t *testing.T) { + ctx := context.Background() + coll := integrationColl(t) + h := testHelper{t} + var docs []*DocumentRef + var wants []map[string]interface{} + for i := 0; i < 3; i++ { + doc := coll.NewDoc() + docs = append(docs, doc) + // To support running this test in parallel with the others, use a field name + // that we don't use anywhere else. + h.mustCreate(doc, map[string]interface{}{"q": i, "x": 1}) + wants = append(wants, map[string]interface{}{"q": int64(i)}) + } + q := coll.Select("q").OrderBy("q", Asc) + for i, test := range []struct { + q Query + want []map[string]interface{} + }{ + {q, wants}, + {q.Where("q", ">", 1), wants[2:]}, + {q.WherePath([]string{"q"}, ">", 1), wants[2:]}, + {q.Offset(1).Limit(1), wants[1:2]}, + {q.StartAt(1), wants[1:]}, + {q.StartAfter(1), wants[2:]}, + {q.EndAt(1), wants[:2]}, + {q.EndBefore(1), wants[:1]}, + } { + gotDocs, err := test.q.Documents(ctx).GetAll() + if err != nil { + t.Errorf("#%d: %+v: %v", i, test.q, err) + continue + } + if len(gotDocs) != len(test.want) { + t.Errorf("#%d: %+v: got %d docs, want %d", i, test.q, len(gotDocs), len(test.want)) + continue + } + for j, g := range gotDocs { + if got, want := g.Data(), test.want[j]; !testEqual(got, want) { + t.Errorf("#%d: %+v, #%d: got\n%+v\nwant\n%+v", i, test.q, j, got, want) + } + } + } + _, err := coll.Select("q").Where("x", "==", 1).OrderBy("q", Asc).Documents(ctx).GetAll() + codeEq(t, "Where and OrderBy on different fields without an index", codes.FailedPrecondition, err) + + // Using the collection itself as the query should return the full documents. + allDocs, err := coll.Documents(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + seen := map[int64]bool{} // "q" values we see + for _, d := range allDocs { + data := d.Data() + q, ok := data["q"] + if !ok { + // A document from another test. + continue + } + if seen[q.(int64)] { + t.Errorf("%v: duplicate doc", data) + } + seen[q.(int64)] = true + if data["x"] != int64(1) { + t.Errorf("%v: wrong or missing 'x'", data) + } + if len(data) != 2 { + t.Errorf("%v: want two keys", data) + } + } + if got, want := len(seen), len(wants); got != want { + t.Errorf("got %d docs with 'q', want %d", len(seen), len(wants)) + } +} + +// Test unary filters. +func TestIntegration_QueryUnary(t *testing.T) { + ctx := context.Background() + coll := integrationColl(t) + h := testHelper{t} + h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": "a"}) + h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": nil}) + h.mustCreate(coll.NewDoc(), map[string]interface{}{"x": 2, "q": math.NaN()}) + wantNull := map[string]interface{}{"q": nil} + wantNaN := map[string]interface{}{"q": math.NaN()} + + base := coll.Select("q").Where("x", "==", 2) + for _, test := range []struct { + q Query + want map[string]interface{} + }{ + {base.Where("q", "==", nil), wantNull}, + {base.Where("q", "==", math.NaN()), wantNaN}, + } { + got, err := test.q.Documents(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + if len(got) != 1 { + t.Errorf("got %d responses, want 1", len(got)) + continue + } + if g, w := got[0].Data(), test.want; !testEqual(g, w) { + t.Errorf("%v: got %v, want %v", test.q, g, w) + } + } +} + +// Test the special DocumentID field in queries. +func TestIntegration_QueryName(t *testing.T) { + ctx := context.Background() + h := testHelper{t} + + checkIDs := func(q Query, wantIDs []string) { + gots, err := q.Documents(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + if len(gots) != len(wantIDs) { + t.Fatalf("got %d, want %d", len(gots), len(wantIDs)) + } + for i, g := range gots { + if got, want := g.Ref.ID, wantIDs[i]; got != want { + t.Errorf("#%d: got %s, want %s", i, got, want) + } + } + } + + coll := integrationColl(t) + var wantIDs []string + for i := 0; i < 3; i++ { + doc := coll.NewDoc() + h.mustCreate(doc, map[string]interface{}{"nm": 1}) + wantIDs = append(wantIDs, doc.ID) + } + sort.Strings(wantIDs) + q := coll.Where("nm", "==", 1).OrderBy(DocumentID, Asc) + checkIDs(q, wantIDs) + + // Empty Select. + q = coll.Select().Where("nm", "==", 1).OrderBy(DocumentID, Asc) + checkIDs(q, wantIDs) + + // Test cursors with __name__. + checkIDs(q.StartAt(wantIDs[1]), wantIDs[1:]) + checkIDs(q.EndAt(wantIDs[1]), wantIDs[:2]) +} + +func TestIntegration_QueryNested(t *testing.T) { + ctx := context.Background() + h := testHelper{t} + coll1 := integrationColl(t) + doc1 := coll1.NewDoc() + coll2 := doc1.Collection(collectionIDs.New()) + doc2 := coll2.NewDoc() + wantData := map[string]interface{}{"x": int64(1)} + h.mustCreate(doc2, wantData) + q := coll2.Select("x") + got, err := q.Documents(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + if len(got) != 1 { + t.Fatalf("got %d docs, want 1", len(got)) + } + if gotData := got[0].Data(); !testEqual(gotData, wantData) { + t.Errorf("got\n%+v\nwant\n%+v", gotData, wantData) + } +} + +func TestIntegration_RunTransaction(t *testing.T) { + ctx := context.Background() + h := testHelper{t} + + type Player struct { + Name string + Score int + Star bool `firestore:"*"` + } + + pat := Player{Name: "Pat", Score: 3, Star: false} + client := integrationClient(t) + patDoc := iColl.Doc("pat") + var anError error + incPat := func(_ context.Context, tx *Transaction) error { + doc, err := tx.Get(patDoc) + if err != nil { + return err + } + score, err := doc.DataAt("Score") + if err != nil { + return err + } + // Since the Star field is called "*", we must use DataAtPath to get it. + star, err := doc.DataAtPath([]string{"*"}) + if err != nil { + return err + } + err = tx.Update(patDoc, []Update{{Path: "Score", Value: int(score.(int64) + 7)}}) + if err != nil { + return err + } + // Since the Star field is called "*", we must use Update to change it. + err = tx.Update(patDoc, + []Update{{FieldPath: []string{"*"}, Value: !star.(bool)}}) + if err != nil { + return err + } + return anError + } + + h.mustCreate(patDoc, pat) + err := client.RunTransaction(ctx, incPat) + if err != nil { + t.Fatal(err) + } + ds := h.mustGet(patDoc) + var got Player + if err := ds.DataTo(&got); err != nil { + t.Fatal(err) + } + want := Player{Name: "Pat", Score: 10, Star: true} + if got != want { + t.Errorf("got %+v, want %+v", got, want) + } + + // Function returns error, so transaction is rolled back and no writes happen. + anError = errors.New("bad") + err = client.RunTransaction(ctx, incPat) + if err != anError { + t.Fatalf("got %v, want %v", err, anError) + } + if err := ds.DataTo(&got); err != nil { + t.Fatal(err) + } + // want is same as before. + if got != want { + t.Errorf("got %+v, want %+v", got, want) + } +} + +func TestIntegration_TransactionGetAll(t *testing.T) { + ctx := context.Background() + h := testHelper{t} + type Player struct { + Name string + Score int + } + lee := Player{Name: "Lee", Score: 3} + sam := Player{Name: "Sam", Score: 1} + client := integrationClient(t) + leeDoc := iColl.Doc("lee") + samDoc := iColl.Doc("sam") + h.mustCreate(leeDoc, lee) + h.mustCreate(samDoc, sam) + + err := client.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { + docs, err := tx.GetAll([]*DocumentRef{samDoc, leeDoc}) + if err != nil { + return err + } + for i, want := range []Player{sam, lee} { + var got Player + if err := docs[i].DataTo(&got); err != nil { + return err + } + if !testutil.Equal(got, want) { + return fmt.Errorf("got %+v, want %+v", got, want) + } + } + return nil + }) + if err != nil { + t.Fatal(err) + } +} + +func TestIntegration_WatchDocument(t *testing.T) { + coll := integrationColl(t) + ctx := context.Background() + h := testHelper{t} + doc := coll.NewDoc() + it := doc.Snapshots(ctx) + defer it.Stop() + + next := func() *DocumentSnapshot { + snap, err := it.Next() + if err != nil { + t.Fatal(err) + } + return snap + } + + snap := next() + if snap.Exists() { + t.Fatal("snapshot exists; it should not") + } + want := map[string]interface{}{"a": int64(1), "b": "two"} + h.mustCreate(doc, want) + snap = next() + if got := snap.Data(); !testutil.Equal(got, want) { + t.Fatalf("got %v, want %v", got, want) + } + + h.mustUpdate(doc, []Update{{Path: "a", Value: int64(2)}}) + want["a"] = int64(2) + snap = next() + if got := snap.Data(); !testutil.Equal(got, want) { + t.Fatalf("got %v, want %v", got, want) + } + + h.mustDelete(doc) + snap = next() + if snap.Exists() { + t.Fatal("snapshot exists; it should not") + } + + h.mustCreate(doc, want) + snap = next() + if got := snap.Data(); !testutil.Equal(got, want) { + t.Fatalf("got %v, want %v", got, want) + } +} + +type imap map[string]interface{} + +func TestIntegration_WatchQuery(t *testing.T) { + ctx := context.Background() + coll := integrationColl(t) + h := testHelper{t} + + q := coll.Where("e", ">", 1).OrderBy("e", Asc) + it := q.Snapshots(ctx) + defer it.Stop() + + next := func() ([]*DocumentSnapshot, []DocumentChange) { + diter, err := it.Next() + if err != nil { + t.Fatal(err) + } + if it.ReadTime.IsZero() { + t.Fatal("zero time") + } + ds, err := diter.GetAll() + if err != nil { + t.Fatal(err) + } + if it.Size != len(ds) { + t.Fatalf("Size=%d but we have %d docs", it.Size, len(ds)) + } + return ds, it.Changes + } + + copts := append([]cmp.Option{cmpopts.IgnoreFields(DocumentSnapshot{}, "ReadTime")}, cmpOpts...) + check := func(msg string, wantd []*DocumentSnapshot, wantc []DocumentChange) { + gotd, gotc := next() + if diff := testutil.Diff(gotd, wantd, copts...); diff != "" { + t.Errorf("%s: %s", msg, diff) + } + if diff := testutil.Diff(gotc, wantc, copts...); diff != "" { + t.Errorf("%s: %s", msg, diff) + } + } + + check("initial", nil, nil) + doc1 := coll.NewDoc() + h.mustCreate(doc1, imap{"e": int64(2), "b": "two"}) + wds := h.mustGet(doc1) + check("one", + []*DocumentSnapshot{wds}, + []DocumentChange{{Kind: DocumentAdded, Doc: wds, OldIndex: -1, NewIndex: 0}}) + + // Add a doc that does not match. We won't see a snapshot for this. + doc2 := coll.NewDoc() + h.mustCreate(doc2, imap{"e": int64(1)}) + + // Update the first doc. We should see the change. We won't see doc2. + h.mustUpdate(doc1, []Update{{Path: "e", Value: int64(3)}}) + wds = h.mustGet(doc1) + check("update", + []*DocumentSnapshot{wds}, + []DocumentChange{{Kind: DocumentModified, Doc: wds, OldIndex: 0, NewIndex: 0}}) + + // Now update doc so that it is not in the query. We should see a snapshot with no docs. + h.mustUpdate(doc1, []Update{{Path: "e", Value: int64(0)}}) + check("update2", nil, []DocumentChange{{Kind: DocumentRemoved, Doc: wds, OldIndex: 0, NewIndex: -1}}) + + // Add two docs out of order. We should see them in order. + doc3 := coll.NewDoc() + doc4 := coll.NewDoc() + want3 := imap{"e": int64(5)} + want4 := imap{"e": int64(4)} + h.mustCreate(doc3, want3) + h.mustCreate(doc4, want4) + wds4 := h.mustGet(doc4) + wds3 := h.mustGet(doc3) + check("two#1", + []*DocumentSnapshot{wds3}, + []DocumentChange{{Kind: DocumentAdded, Doc: wds3, OldIndex: -1, NewIndex: 0}}) + check("two#2", + []*DocumentSnapshot{wds4, wds3}, + []DocumentChange{{Kind: DocumentAdded, Doc: wds4, OldIndex: -1, NewIndex: 0}}) + // Delete a doc. + h.mustDelete(doc4) + check("after del", []*DocumentSnapshot{wds3}, []DocumentChange{{Kind: DocumentRemoved, Doc: wds4, OldIndex: 0, NewIndex: -1}}) +} + +func TestIntegration_WatchQueryCancel(t *testing.T) { + ctx := context.Background() + coll := integrationColl(t) + + q := coll.Where("e", ">", 1).OrderBy("e", Asc) + ctx, cancel := context.WithCancel(ctx) + it := q.Snapshots(ctx) + defer it.Stop() + + // First call opens the stream. + _, err := it.Next() + if err != nil { + t.Fatal(err) + } + cancel() + _, err = it.Next() + codeEq(t, "after cancel", codes.Canceled, err) +} + +func codeEq(t *testing.T, msg string, code codes.Code, err error) { + if grpc.Code(err) != code { + t.Fatalf("%s:\ngot <%v>\nwant code %s", msg, err, code) + } +} + +func loc() string { + _, file, line, ok := runtime.Caller(2) + if !ok { + return "???" + } + return fmt.Sprintf("%s:%d", filepath.Base(file), line) +} + +func copyMap(m map[string]interface{}) map[string]interface{} { + c := map[string]interface{}{} + for k, v := range m { + c[k] = v + } + return c +} + +func checkTimeBetween(t *testing.T, got, low, high time.Time) { + // Allow slack for clock skew. + const slack = 4 * time.Second + low = low.Add(-slack) + high = high.Add(slack) + if got.Before(low) || got.After(high) { + t.Fatalf("got %s, not in [%s, %s]", got, low, high) + } +} + +type testHelper struct { + t *testing.T +} + +func (h testHelper) mustCreate(doc *DocumentRef, data interface{}) *WriteResult { + wr, err := doc.Create(context.Background(), data) + if err != nil { + h.t.Fatalf("%s: creating: %v", loc(), err) + } + return wr +} + +func (h testHelper) mustUpdate(doc *DocumentRef, updates []Update) *WriteResult { + wr, err := doc.Update(context.Background(), updates) + if err != nil { + h.t.Fatalf("%s: updating: %v", loc(), err) + } + return wr +} + +func (h testHelper) mustGet(doc *DocumentRef) *DocumentSnapshot { + d, err := doc.Get(context.Background()) + if err != nil { + h.t.Fatalf("%s: getting: %v", loc(), err) + } + return d +} + +func (h testHelper) mustDelete(doc *DocumentRef) *WriteResult { + wr, err := doc.Delete(context.Background()) + if err != nil { + h.t.Fatalf("%s: updating: %v", loc(), err) + } + return wr +} diff --git a/vendor/cloud.google.com/go/firestore/internal/Makefile b/vendor/cloud.google.com/go/firestore/internal/Makefile new file mode 100644 index 0000000000..6769cb36ea --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/internal/Makefile @@ -0,0 +1,16 @@ +# Build doc.go from template and snippets. + +SHELL=/bin/bash + +../doc.go: build doc-snippets.go doc.template snipdoc.awk + @tmp=$$(mktemp) && \ + awk -f snipdoc.awk doc-snippets.go doc.template > $$tmp && \ + chmod +w $@ && \ + mv $$tmp $@ && \ + chmod -w $@ + @echo "wrote $@" + +.PHONY: build + +build: + go build doc-snippets.go diff --git a/vendor/cloud.google.com/go/firestore/internal/doc-snippets.go b/vendor/cloud.google.com/go/firestore/internal/doc-snippets.go new file mode 100644 index 0000000000..f199f0d187 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/internal/doc-snippets.go @@ -0,0 +1,161 @@ +// Copyright 2017 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 internal + +import ( + "fmt" + + firestore "cloud.google.com/go/firestore" + + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +const ELLIPSIS = 0 + +//[ structDef +type State struct { + Capital string `firestore:"capital"` + Population float64 `firestore:"pop"` // in millions +} + +//] + +func f1() { + //[ NewClient + ctx := context.Background() + client, err := firestore.NewClient(ctx, "projectID") + if err != nil { + // TODO: Handle error. + } + //] + //[ refs + states := client.Collection("States") + ny := states.Doc("NewYork") + // Or, in a single call: + ny = client.Doc("States/NewYork") + //] + //[ docref.Get + docsnap, err := ny.Get(ctx) + if err != nil { + // TODO: Handle error. + } + dataMap := docsnap.Data() + fmt.Println(dataMap) + //] + //[ DataTo + var nyData State + if err := docsnap.DataTo(&nyData); err != nil { + // TODO: Handle error. + } + //] + //[ GetAll + docsnaps, err := client.GetAll(ctx, []*firestore.DocumentRef{ + states.Doc("Wisconsin"), states.Doc("Ohio"), + }) + if err != nil { + // TODO: Handle error. + } + for _, ds := range docsnaps { + _ = ds // TODO: Use ds. + } + //[ docref.Create + wr, err := ny.Create(ctx, State{ + Capital: "Albany", + Population: 19.8, + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(wr) + //] + //[ docref.Set + ca := states.Doc("California") + _, err = ca.Set(ctx, State{ + Capital: "Sacramento", + Population: 39.14, + }) + //] + + //[ docref.Update + _, err = ca.Update(ctx, []firestore.Update{{Path: "capital", Value: "Sacramento"}}) + //] + + //[ docref.Delete + _, err = ny.Delete(ctx) + //] + + //[ LUT-precond + docsnap, err = ca.Get(ctx) + if err != nil { + // TODO: Handle error. + } + _, err = ca.Update(ctx, + []firestore.Update{{Path: "capital", Value: "Sacramento"}}, + firestore.LastUpdateTime(docsnap.UpdateTime)) + //] + + //[ WriteBatch + writeResults, err := client.Batch(). + Create(ny, State{Capital: "Albany"}). + Update(ca, []firestore.Update{{Path: "capital", Value: "Sacramento"}}). + Delete(client.Doc("States/WestDakota")). + Commit(ctx) + //] + _ = writeResults + + //[ Query + q := states.Where("pop", ">", 10).OrderBy("pop", firestore.Desc) + //] + //[ Documents + iter := q.Documents(ctx) + for { + doc, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(doc.Data()) + } + //] + + //[ CollQuery + iter = client.Collection("States").Documents(ctx) + //] +} + +func txn() { + var ctx context.Context + var client *firestore.Client + //[ Transaction + ny := client.Doc("States/NewYork") + err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error { + doc, err := tx.Get(ny) // tx.Get, NOT ny.Get! + if err != nil { + return err + } + pop, err := doc.DataAt("pop") + if err != nil { + return err + } + return tx.Update(ny, []firestore.Update{{Path: "pop", Value: pop.(float64) + 0.2}}) + }) + if err != nil { + // TODO: Handle error. + } + //] +} diff --git a/vendor/cloud.google.com/go/firestore/internal/doc.template b/vendor/cloud.google.com/go/firestore/internal/doc.template new file mode 100644 index 0000000000..6d428235a8 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/internal/doc.template @@ -0,0 +1,145 @@ +// Copyright 2017 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. + +// DO NOT EDIT doc.go. Modify internal/doc.template, then run make -C internal. + +/* +Package firestore provides a client for reading and writing to a Cloud Firestore +database. + +See https://cloud.google.com/firestore/docs for an introduction +to Cloud Firestore and additional help on using the Firestore API. + +Note: you can't use both Cloud Firestore and Cloud Datastore in the same +project. + +Creating a Client + +To start working with this package, create a client with a project ID: + +[NewClient] + +CollectionRefs and DocumentRefs + +In Firestore, documents are sets of key-value pairs, and collections are groups of +documents. A Firestore database consists of a hierarchy of alternating collections +and documents, referred to by slash-separated paths like +"States/California/Cities/SanFrancisco". + +This client is built around references to collections and documents. CollectionRefs +and DocumentRefs are lightweight values that refer to the corresponding database +entities. Creating a ref does not involve any network traffic. + +[refs] + +Reading + +Use DocumentRef.Get to read a document. The result is a DocumentSnapshot. +Call its Data method to obtain the entire document contents as a map. + +[docref.Get] + +You can also obtain a single field with DataAt, or extract the data into a struct +with DataTo. With the type definition + +[structDef] + +we can extract the document's data into a value of type State: + +[DataTo] + +Note that this client supports struct tags beginning with "firestore:" that work like +the tags of the encoding/json package, letting you rename fields, ignore them, or +omit their values when empty. + +To retrieve multiple documents from their references in a single call, use +Client.GetAll. + +[GetAll] + +Writing + +For writing individual documents, use the methods on DocumentReference. +Create creates a new document. + +[docref.Create] + +The first return value is a WriteResult, which contains the time +at which the document was updated. + +Create fails if the document exists. Another method, Set, either replaces an existing +document or creates a new one. + +[docref.Set] + +To update some fields of an existing document, use Update. It takes a list of +paths to update and their corresponding values. + +[docref.Update] + +Use DocumentRef.Delete to delete a document. + +[docref.Delete] + +Preconditions + +You can condition Deletes or Updates on when a document was last changed. Specify +these preconditions as an option to a Delete or Update method. The check and the +write happen atomically with a single RPC. + +[LUT-precond] + +Here we update a doc only if it hasn't changed since we read it. +You could also do this with a transaction. + +To perform multiple writes at once, use a WriteBatch. Its methods chain +for convenience. + +WriteBatch.Commit sends the collected writes to the server, where they happen +atomically. + +[WriteBatch] + +Queries + +You can use SQL to select documents from a collection. Begin with the collection, and +build up a query using Select, Where and other methods of Query. + +[Query] + +Call the Query's Documents method to get an iterator, and use it like +the other Google Cloud Client iterators. + +[Documents] + +To get all the documents in a collection, you can use the collection itself +as a query. + +[CollQuery] + +Transactions + +Use a transaction to execute reads and writes atomically. All reads must happen +before any writes. Transaction creation, commit, rollback and retry are handled for +you by the Client.RunTransaction method; just provide a function and use the +read and write methods of the Transaction passed to it. + +[Transaction] + +Authentication + +See examples of authorization and authentication at +https://godoc.org/cloud.google.com/go#pkg-examples. +*/ +package firestore diff --git a/vendor/cloud.google.com/go/firestore/internal/snipdoc.awk b/vendor/cloud.google.com/go/firestore/internal/snipdoc.awk new file mode 100644 index 0000000000..98e788ebdf --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/internal/snipdoc.awk @@ -0,0 +1,116 @@ +# Copyright 2017 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. + +# snipdoc merges code snippets from Go source files into a template to +# produce another go file (typically doc.go). +# +# Call with one or more .go files and a template file. +# +# awk -f snipmd.awk foo.go bar.go doc.template +# +# In the Go files, start a snippet with +# //[ NAME +# and end it with +# //] +# +# In the template, write +# [NAME] +# on a line by itself to insert the snippet NAME on that line. +# +# The following transformations are made to the Go code: +# - Trailing blank lines are removed. +# - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...` + + +/^[ \t]*\/\/\[/ { # start snippet in Go file + if (inGo()) { + if ($2 == "") { + die("missing snippet name") + } + curSnip = $2 + next + } +} + +/^[ \t]*\/\/]/ { # end snippet in Go file + if (inGo()) { + if (curSnip != "") { + # Remove all trailing newlines. + gsub(/[\t\n]+$/, "", snips[curSnip]) + curSnip = "" + next + } else { + die("//] without corresponding //[") + } + } +} + +ENDFILE { + if (curSnip != "") { + die("unclosed snippet: " curSnip) + } +} + +/^\[.*\]$/ { # Snippet marker in template file. + if (inTemplate()) { + name = substr($1, 2, length($1)-2) + if (snips[name] == "") { + die("no snippet named " name) + } + printf("%s\n", snips[name]) + afterSnip = 1 + next + } +} + +# Matches every line. +{ + if (curSnip != "") { + # If the first line in the snip has no indent, add the indent. + if (snips[curSnip] == "") { + if (index($0, "\t") == 1) { + extraIndent = "" + } else { + extraIndent = "\t" + } + } + + line = $0 + # Replace ELLIPSIS. + gsub(/_ = ELLIPSIS/, "...", line) + gsub(/ELLIPSIS/, "...", line) + + snips[curSnip] = snips[curSnip] extraIndent line "\n" + } else if (inTemplate()) { + afterSnip = 0 + # Copy to output. + print + } +} + + + +function inTemplate() { + return match(FILENAME, /\.template$/) +} + +function inGo() { + return match(FILENAME, /\.go$/) +} + + +function die(msg) { + printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr" + exit 1 +} diff --git a/vendor/cloud.google.com/go/firestore/mock_test.go b/vendor/cloud.google.com/go/firestore/mock_test.go new file mode 100644 index 0000000000..a3dcb7b4c2 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/mock_test.go @@ -0,0 +1,207 @@ +// Copyright 2017 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 firestore + +// A simple mock server. + +import ( + "fmt" + "strings" + + "cloud.google.com/go/internal/testutil" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" +) + +type mockServer struct { + pb.FirestoreServer + + Addr string + + reqItems []reqItem + resps []interface{} +} + +type reqItem struct { + wantReq proto.Message + adjust func(gotReq proto.Message) +} + +func newMockServer() (*mockServer, error) { + srv, err := testutil.NewServer() + if err != nil { + return nil, err + } + mock := &mockServer{Addr: srv.Addr} + pb.RegisterFirestoreServer(srv.Gsrv, mock) + srv.Start() + return mock, nil +} + +// addRPC adds a (request, response) pair to the server's list of expected +// interactions. The server will compare the incoming request with wantReq +// using proto.Equal. The response can be a message or an error. +// +// For the Listen RPC, resp should be a []interface{}, where each element +// is either ListenResponse or an error. +// +// Passing nil for wantReq disables the request check. +func (s *mockServer) addRPC(wantReq proto.Message, resp interface{}) { + s.addRPCAdjust(wantReq, resp, nil) +} + +// addRPCAdjust is like addRPC, but accepts a function that can be used +// to tweak the requests before comparison, for example to adjust for +// randomness. +func (s *mockServer) addRPCAdjust(wantReq proto.Message, resp interface{}, adjust func(proto.Message)) { + s.reqItems = append(s.reqItems, reqItem{wantReq, adjust}) + s.resps = append(s.resps, resp) +} + +// popRPC compares the request with the next expected (request, response) pair. +// It returns the response, or an error if the request doesn't match what +// was expected or there are no expected rpcs. +func (s *mockServer) popRPC(gotReq proto.Message) (interface{}, error) { + if len(s.reqItems) == 0 { + panic("out of RPCs") + } + ri := s.reqItems[0] + s.reqItems = s.reqItems[1:] + if ri.wantReq != nil { + if ri.adjust != nil { + ri.adjust(gotReq) + } + if !proto.Equal(gotReq, ri.wantReq) { + return nil, fmt.Errorf("mockServer: bad request\ngot: %T\n%s\nwant: %T\n%s", + gotReq, proto.MarshalTextString(gotReq), + ri.wantReq, proto.MarshalTextString(ri.wantReq)) + } + } + resp := s.resps[0] + s.resps = s.resps[1:] + if err, ok := resp.(error); ok { + return nil, err + } + return resp, nil +} + +func (s *mockServer) reset() { + s.reqItems = nil + s.resps = nil +} + +func (s *mockServer) GetDocument(_ context.Context, req *pb.GetDocumentRequest) (*pb.Document, error) { + res, err := s.popRPC(req) + if err != nil { + return nil, err + } + return res.(*pb.Document), nil +} + +func (s *mockServer) Commit(_ context.Context, req *pb.CommitRequest) (*pb.CommitResponse, error) { + res, err := s.popRPC(req) + if err != nil { + return nil, err + } + return res.(*pb.CommitResponse), nil +} + +func (s *mockServer) BatchGetDocuments(req *pb.BatchGetDocumentsRequest, bs pb.Firestore_BatchGetDocumentsServer) error { + res, err := s.popRPC(req) + if err != nil { + return err + } + responses := res.([]interface{}) + for _, res := range responses { + switch res := res.(type) { + case *pb.BatchGetDocumentsResponse: + if err := bs.Send(res); err != nil { + return err + } + case error: + return res + default: + panic(fmt.Sprintf("bad response type in BatchGetDocuments: %+v", res)) + } + } + return nil +} + +func (s *mockServer) RunQuery(req *pb.RunQueryRequest, qs pb.Firestore_RunQueryServer) error { + res, err := s.popRPC(req) + if err != nil { + return err + } + responses := res.([]interface{}) + for _, res := range responses { + switch res := res.(type) { + case *pb.RunQueryResponse: + if err := qs.Send(res); err != nil { + return err + } + case error: + return res + default: + panic(fmt.Sprintf("bad response type in RunQuery: %+v", res)) + } + } + return nil +} + +func (s *mockServer) BeginTransaction(_ context.Context, req *pb.BeginTransactionRequest) (*pb.BeginTransactionResponse, error) { + res, err := s.popRPC(req) + if err != nil { + return nil, err + } + return res.(*pb.BeginTransactionResponse), nil +} + +func (s *mockServer) Rollback(_ context.Context, req *pb.RollbackRequest) (*empty.Empty, error) { + res, err := s.popRPC(req) + if err != nil { + return nil, err + } + return res.(*empty.Empty), nil +} + +func (s *mockServer) Listen(stream pb.Firestore_ListenServer) error { + req, err := stream.Recv() + if err != nil { + return err + } + responses, err := s.popRPC(req) + if err != nil { + if status.Code(err) == codes.Unknown && strings.Contains(err.Error(), "mockServer") { + // The stream will retry on Unknown, but we don't want that to happen if + // the error comes from us. + panic(err) + } + return err + } + for _, res := range responses.([]interface{}) { + if err, ok := res.(error); ok { + return err + } + if err := stream.Send(res.(*pb.ListenResponse)); err != nil { + return err + } + } + return nil +} diff --git a/vendor/cloud.google.com/go/firestore/options.go b/vendor/cloud.google.com/go/firestore/options.go new file mode 100644 index 0000000000..874f8a866e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/options.go @@ -0,0 +1,177 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "fmt" + "time" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "github.com/golang/protobuf/ptypes" +) + +// A Precondition modifies a Firestore update or delete operation. +type Precondition interface { + // Returns the corresponding Precondition proto. + preconditionProto() (*pb.Precondition, error) +} + +// Exists is a Precondition that checks for the existence of a resource before +// writing to it. If the check fails, the write does not occur. +var Exists Precondition + +func init() { + // Initialize here so godoc doesn't show the internal value. + Exists = exists(true) +} + +type exists bool + +func (e exists) preconditionProto() (*pb.Precondition, error) { + return &pb.Precondition{ + ConditionType: &pb.Precondition_Exists{bool(e)}, + }, nil +} + +func (e exists) String() string { + if e { + return "Exists" + } else { + return "DoesNotExist" + } +} + +// LastUpdateTime returns a Precondition that checks that a resource must exist and +// must have last been updated at the given time. If the check fails, the write +// does not occur. +func LastUpdateTime(t time.Time) Precondition { return lastUpdateTime(t) } + +type lastUpdateTime time.Time + +func (u lastUpdateTime) preconditionProto() (*pb.Precondition, error) { + ts, err := ptypes.TimestampProto(time.Time(u)) + if err != nil { + return nil, err + } + return &pb.Precondition{ + ConditionType: &pb.Precondition_UpdateTime{ts}, + }, nil +} + +func (u lastUpdateTime) String() string { return fmt.Sprintf("LastUpdateTime(%s)", time.Time(u)) } + +func processPreconditionsForDelete(preconds []Precondition) (*pb.Precondition, error) { + // At most one option permitted. + switch len(preconds) { + case 0: + return nil, nil + case 1: + return preconds[0].preconditionProto() + default: + return nil, fmt.Errorf("firestore: conflicting preconditions: %+v", preconds) + } +} + +func processPreconditionsForUpdate(preconds []Precondition) (*pb.Precondition, error) { + // At most one option permitted, and it cannot be Exists. + switch len(preconds) { + case 0: + // If the user doesn't provide any options, default to Exists(true). + return exists(true).preconditionProto() + case 1: + if _, ok := preconds[0].(exists); ok { + return nil, errors.New("Cannot use Exists with Update") + } + return preconds[0].preconditionProto() + default: + return nil, fmt.Errorf("firestore: conflicting preconditions: %+v", preconds) + } +} + +func processPreconditionsForVerify(preconds []Precondition) (*pb.Precondition, error) { + // At most one option permitted. + switch len(preconds) { + case 0: + return nil, nil + case 1: + return preconds[0].preconditionProto() + default: + return nil, fmt.Errorf("firestore: conflicting preconditions: %+v", preconds) + } +} + +// A SetOption modifies a Firestore set operation. +type SetOption interface { + fieldPaths() (fps []FieldPath, all bool, err error) +} + +// MergeAll is a SetOption that causes all the field paths given in the data argument +// to Set to be overwritten. It is not supported for struct data. +var MergeAll SetOption = merge{all: true} + +// Merge returns a SetOption that causes only the given field paths to be +// overwritten. Other fields on the existing document will be untouched. It is an +// error if a provided field path does not refer to a value in the data passed to +// Set. +func Merge(fps ...FieldPath) SetOption { + for _, fp := range fps { + if err := fp.validate(); err != nil { + return merge{err: err} + } + } + return merge{paths: fps} +} + +type merge struct { + all bool + paths []FieldPath + err error +} + +func (m merge) String() string { + if m.err != nil { + return fmt.Sprintf("", m.err) + } + if m.all { + return "MergeAll" + } + return fmt.Sprintf("Merge(%+v)", m.paths) +} + +func (m merge) fieldPaths() (fps []FieldPath, all bool, err error) { + if m.err != nil { + return nil, false, m.err + } + if err := checkNoDupOrPrefix(m.paths); err != nil { + return nil, false, err + } + if m.all { + return nil, true, nil + } + return m.paths, false, nil +} + +func processSetOptions(opts []SetOption) (fps []FieldPath, all bool, err error) { + switch len(opts) { + case 0: + return nil, false, nil + case 1: + return opts[0].fieldPaths() + default: + return nil, false, fmt.Errorf("conflicting options: %+v", opts) + } +} diff --git a/vendor/cloud.google.com/go/firestore/options_test.go b/vendor/cloud.google.com/go/firestore/options_test.go new file mode 100644 index 0000000000..999a0a610e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/options_test.go @@ -0,0 +1,151 @@ +// Copyright 2017 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 firestore + +import ( + "testing" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" +) + +func TestProcessPreconditionsForVerify(t *testing.T) { + for _, test := range []struct { + in []Precondition + want *pb.Precondition + wantErr bool + }{ + { + in: nil, + want: nil, + }, + { + in: []Precondition{}, + want: nil, + }, + { + in: []Precondition{Exists}, + want: &pb.Precondition{ConditionType: &pb.Precondition_Exists{true}}, + }, + { + in: []Precondition{LastUpdateTime(aTime)}, + want: &pb.Precondition{ConditionType: &pb.Precondition_UpdateTime{aTimestamp}}, + }, + { + in: []Precondition{Exists, LastUpdateTime(aTime)}, + wantErr: true, + }, + { + in: []Precondition{Exists, Exists}, + wantErr: true, + }, + } { + got, err := processPreconditionsForVerify(test.in) + switch { + case test.wantErr && err == nil: + t.Errorf("%v: got nil, want error", test.in) + case !test.wantErr && err != nil: + t.Errorf("%v: got <%v>, want no error", test.in, err) + case !test.wantErr && err == nil && !testEqual(got, test.want): + t.Errorf("%v: got %+v, want %v", test.in, got, test.want) + } + } +} + +func TestProcessPreconditionsForDelete(t *testing.T) { + for _, test := range []struct { + in []Precondition + want *pb.Precondition + wantErr bool + }{ + { + in: nil, + want: nil, + }, + { + in: []Precondition{}, + want: nil, + }, + { + in: []Precondition{Exists}, + want: &pb.Precondition{ConditionType: &pb.Precondition_Exists{true}}, + }, + { + in: []Precondition{LastUpdateTime(aTime)}, + want: &pb.Precondition{ConditionType: &pb.Precondition_UpdateTime{aTimestamp}}, + }, + { + in: []Precondition{Exists, LastUpdateTime(aTime)}, + wantErr: true, + }, + { + in: []Precondition{Exists, Exists}, + wantErr: true, + }, + } { + got, err := processPreconditionsForDelete(test.in) + switch { + case test.wantErr && err == nil: + t.Errorf("%v: got nil, want error", test.in) + case !test.wantErr && err != nil: + t.Errorf("%v: got <%v>, want no error", test.in, err) + case !test.wantErr && err == nil && !testEqual(got, test.want): + t.Errorf("%v: got %+v, want %v", test.in, got, test.want) + } + } +} + +func TestProcessPreconditionsForUpdate(t *testing.T) { + for _, test := range []struct { + in []Precondition + want *pb.Precondition + wantErr bool + }{ + { + in: nil, + want: &pb.Precondition{ConditionType: &pb.Precondition_Exists{true}}, + }, + { + in: []Precondition{}, + want: &pb.Precondition{ConditionType: &pb.Precondition_Exists{true}}, + }, + + { + in: []Precondition{Exists}, + wantErr: true, + }, + { + in: []Precondition{LastUpdateTime(aTime)}, + want: &pb.Precondition{ConditionType: &pb.Precondition_UpdateTime{aTimestamp}}, + }, + { + in: []Precondition{Exists, LastUpdateTime(aTime)}, + wantErr: true, + }, + { + in: []Precondition{Exists, Exists}, + wantErr: true, + }, + } { + got, err := processPreconditionsForUpdate(test.in) + switch { + case test.wantErr && err == nil: + t.Errorf("%v: got nil, want error", test.in) + case !test.wantErr && err != nil: + t.Errorf("%v: got <%v>, want no error", test.in, err) + case !test.wantErr && err == nil && !testEqual(got, test.want): + t.Errorf("%v: got %+v, want %v", test.in, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/firestore/order.go b/vendor/cloud.google.com/go/firestore/order.go new file mode 100644 index 0000000000..c3f59ce18b --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/order.go @@ -0,0 +1,216 @@ +// 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 firestore + +import ( + "bytes" + "fmt" + "math" + "sort" + "strings" + + tspb "github.com/golang/protobuf/ptypes/timestamp" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" +) + +// Returns a negative number, zero, or a positive number depending on whether a is +// less than, equal to, or greater than b according to Firestore's ordering of +// values. +func compareValues(a, b *pb.Value) int { + ta := typeOrder(a) + tb := typeOrder(b) + if ta != tb { + return compareInt64s(int64(ta), int64(tb)) + } + switch a := a.ValueType.(type) { + case *pb.Value_NullValue: + return 0 // nulls are equal + + case *pb.Value_BooleanValue: + av := a.BooleanValue + bv := b.GetBooleanValue() + switch { + case av && !bv: + return 1 + case bv && !av: + return -1 + default: + return 0 + } + + case *pb.Value_IntegerValue: + return compareNumbers(float64(a.IntegerValue), toFloat(b)) + + case *pb.Value_DoubleValue: + return compareNumbers(a.DoubleValue, toFloat(b)) + + case *pb.Value_TimestampValue: + return compareTimestamps(a.TimestampValue, b.GetTimestampValue()) + + case *pb.Value_StringValue: + return strings.Compare(a.StringValue, b.GetStringValue()) + + case *pb.Value_BytesValue: + return bytes.Compare(a.BytesValue, b.GetBytesValue()) + + case *pb.Value_ReferenceValue: + return compareReferences(a.ReferenceValue, b.GetReferenceValue()) + + case *pb.Value_GeoPointValue: + ag := a.GeoPointValue + bg := b.GetGeoPointValue() + if ag.Latitude != bg.Latitude { + return compareFloat64s(ag.Latitude, bg.Latitude) + } + return compareFloat64s(ag.Longitude, bg.Longitude) + + case *pb.Value_ArrayValue: + return compareArrays(a.ArrayValue.Values, b.GetArrayValue().Values) + + case *pb.Value_MapValue: + return compareMaps(a.MapValue.Fields, b.GetMapValue().Fields) + + default: + panic(fmt.Sprintf("bad value type: %v", a)) + } +} + +// Treats NaN as less than any non-NaN. +func compareNumbers(a, b float64) int { + switch { + case math.IsNaN(a): + if math.IsNaN(b) { + return 0 + } + return -1 + case math.IsNaN(b): + return 1 + default: + return compareFloat64s(a, b) + } +} + +// Return v as a float64, assuming it's an Integer or Double. +func toFloat(v *pb.Value) float64 { + if x, ok := v.ValueType.(*pb.Value_IntegerValue); ok { + return float64(x.IntegerValue) + } + return v.GetDoubleValue() +} + +func compareTimestamps(a, b *tspb.Timestamp) int { + if c := compareInt64s(a.Seconds, b.Seconds); c != 0 { + return c + } + return compareInt64s(int64(a.Nanos), int64(b.Nanos)) +} + +func compareReferences(a, b string) int { + // Compare path components lexicographically. + pa := strings.Split(a, "/") + pb := strings.Split(b, "/") + return compareSequences(len(pa), len(pb), func(i int) int { + return strings.Compare(pa[i], pb[i]) + }) +} + +func compareArrays(a, b []*pb.Value) int { + return compareSequences(len(a), len(b), func(i int) int { + return compareValues(a[i], b[i]) + }) +} + +func compareMaps(a, b map[string]*pb.Value) int { + sortedKeys := func(m map[string]*pb.Value) []string { + var ks []string + for k := range m { + ks = append(ks, k) + } + sort.Strings(ks) + return ks + } + + aks := sortedKeys(a) + bks := sortedKeys(b) + return compareSequences(len(aks), len(bks), func(i int) int { + if c := strings.Compare(aks[i], bks[i]); c != 0 { + return c + } + k := aks[i] + return compareValues(a[k], b[k]) + }) +} + +func compareSequences(len1, len2 int, compare func(int) int) int { + for i := 0; i < len1 && i < len2; i++ { + if c := compare(i); c != 0 { + return c + } + } + return compareInt64s(int64(len1), int64(len2)) +} + +func compareFloat64s(a, b float64) int { + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } +} + +func compareInt64s(a, b int64) int { + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } +} + +// Return an integer corresponding to the type of value stored in v, such that +// comparing the resulting integers gives the Firestore ordering for types. +func typeOrder(v *pb.Value) int { + switch v.ValueType.(type) { + case *pb.Value_NullValue: + return 0 + case *pb.Value_BooleanValue: + return 1 + case *pb.Value_IntegerValue: + return 2 + case *pb.Value_DoubleValue: + return 2 + case *pb.Value_TimestampValue: + return 3 + case *pb.Value_StringValue: + return 4 + case *pb.Value_BytesValue: + return 5 + case *pb.Value_ReferenceValue: + return 6 + case *pb.Value_GeoPointValue: + return 7 + case *pb.Value_ArrayValue: + return 8 + case *pb.Value_MapValue: + return 9 + default: + panic(fmt.Sprintf("bad value type: %v", v)) + } +} diff --git a/vendor/cloud.google.com/go/firestore/order_test.go b/vendor/cloud.google.com/go/firestore/order_test.go new file mode 100644 index 0000000000..ac8e06e204 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/order_test.go @@ -0,0 +1,118 @@ +// 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 firestore + +import ( + "math" + "testing" + "time" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "google.golang.org/genproto/googleapis/type/latlng" +) + +func TestCompareValues(t *testing.T) { + // Ordered list of values. + vals := []*pb.Value{ + nullValue, + boolval(false), + boolval(true), + floatval(math.NaN()), + floatval(math.Inf(-1)), + floatval(-math.MaxFloat64), + int64val(math.MinInt64), + floatval(-1.1), + intval(-1), + intval(0), + floatval(math.SmallestNonzeroFloat64), + intval(1), + floatval(1.1), + intval(2), + int64val(math.MaxInt64), + floatval(math.MaxFloat64), + floatval(math.Inf(1)), + tsval(time.Date(2016, 5, 20, 10, 20, 0, 0, time.UTC)), + tsval(time.Date(2016, 10, 21, 15, 32, 0, 0, time.UTC)), + strval(""), + strval("\u0000\ud7ff\ue000\uffff"), + strval("(╯°□°)╯︵ ┻━┻"), + strval("a"), + strval("abc def"), + strval("e\u0301b"), + strval("æ"), + strval("\u00e9a"), + bytesval([]byte{}), + bytesval([]byte{0}), + bytesval([]byte{0, 1, 2, 3, 4}), + bytesval([]byte{0, 1, 2, 4, 3}), + bytesval([]byte{255}), + refval("projects/p1/databases/d1/documents/c1/doc1"), + refval("projects/p1/databases/d1/documents/c1/doc2"), + refval("projects/p1/databases/d1/documents/c1/doc2/c2/doc1"), + refval("projects/p1/databases/d1/documents/c1/doc2/c2/doc2"), + refval("projects/p1/databases/d1/documents/c10/doc1"), + refval("projects/p1/databases/dkkkkklkjnjkkk1/documents/c2/doc1"), + refval("projects/p2/databases/d2/documents/c1/doc1"), + refval("projects/p2/databases/d2/documents/c1-/doc1"), + geopoint(-90, -180), + geopoint(-90, 0), + geopoint(-90, 180), + geopoint(0, -180), + geopoint(0, 0), + geopoint(0, 180), + geopoint(1, -180), + geopoint(1, 0), + geopoint(1, 180), + geopoint(90, -180), + geopoint(90, 0), + geopoint(90, 180), + arrayval(), + arrayval(strval("bar")), + arrayval(strval("foo")), + arrayval(strval("foo"), intval(1)), + arrayval(strval("foo"), intval(2)), + arrayval(strval("foo"), strval("0")), + mapval(map[string]*pb.Value{"bar": intval(0)}), + mapval(map[string]*pb.Value{"bar": intval(0), "foo": intval(1)}), + mapval(map[string]*pb.Value{"foo": intval(1)}), + mapval(map[string]*pb.Value{"foo": intval(2)}), + mapval(map[string]*pb.Value{"foo": strval("0")}), + } + + for i, v1 := range vals { + if got := compareValues(v1, v1); got != 0 { + t.Errorf("compare(%v, %v) == %d, want 0", v1, v1, got) + } + for _, v2 := range vals[i+1:] { + if got := compareValues(v1, v2); got != -1 { + t.Errorf("compare(%v, %v) == %d, want -1", v1, v2, got) + } + if got := compareValues(v2, v1); got != 1 { + t.Errorf("compare(%v, %v) == %d, want 1", v1, v2, got) + } + } + } + + // Integers and Doubles order the same. + n1 := intval(17) + n2 := floatval(17) + if got := compareValues(n1, n2); got != 0 { + t.Errorf("compare(%v, %v) == %d, want 0", n1, n2, got) + } +} + +func geopoint(lat, lng float64) *pb.Value { + return geoval(&latlng.LatLng{Latitude: lat, Longitude: lng}) +} diff --git a/vendor/cloud.google.com/go/firestore/query.go b/vendor/cloud.google.com/go/firestore/query.go new file mode 100644 index 0000000000..327eeae6b5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/query.go @@ -0,0 +1,757 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "fmt" + "io" + "math" + "reflect" + "time" + + "golang.org/x/net/context" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "cloud.google.com/go/internal/btree" + "github.com/golang/protobuf/ptypes/wrappers" + "google.golang.org/api/iterator" +) + +// Query represents a Firestore query. +// +// Query values are immutable. Each Query method creates +// a new Query; it does not modify the old. +type Query struct { + c *Client + parentPath string // path of the collection's parent + collectionID string + selection []FieldPath + filters []filter + orders []order + offset int32 + limit *wrappers.Int32Value + startVals, endVals []interface{} + startDoc, endDoc *DocumentSnapshot + startBefore, endBefore bool + err error +} + +func (q *Query) collectionPath() string { + return q.parentPath + "/documents/" + q.collectionID +} + +// DocumentID is the special field name representing the ID of a document +// in queries. +const DocumentID = "__name__" + +// Select returns a new Query that specifies the paths +// to return from the result documents. +// Each path argument can be a single field or a dot-separated sequence of +// fields, and must not contain any of the runes "˜*/[]". +func (q Query) Select(paths ...string) Query { + var fps []FieldPath + for _, s := range paths { + fp, err := parseDotSeparatedString(s) + if err != nil { + q.err = err + return q + } + fps = append(fps, fp) + } + return q.SelectPaths(fps...) +} + +// SelectPaths returns a new Query that specifies the field paths +// to return from the result documents. +func (q Query) SelectPaths(fieldPaths ...FieldPath) Query { + if len(fieldPaths) == 0 { + q.selection = []FieldPath{{DocumentID}} + } else { + q.selection = fieldPaths + } + return q +} + +// Where returns a new Query that filters the set of results. +// A Query can have multiple filters. +// The path argument can be a single field or a dot-separated sequence of +// fields, and must not contain any of the runes "˜*/[]". +// The op argument must be one of "==", "<", "<=", ">" or ">=". +func (q Query) Where(path, op string, value interface{}) Query { + fp, err := parseDotSeparatedString(path) + if err != nil { + q.err = err + return q + } + q.filters = append(append([]filter(nil), q.filters...), filter{fp, op, value}) + return q +} + +// WherePath returns a new Query that filters the set of results. +// A Query can have multiple filters. +// The op argument must be one of "==", "<", "<=", ">" or ">=". +func (q Query) WherePath(fp FieldPath, op string, value interface{}) Query { + q.filters = append(append([]filter(nil), q.filters...), filter{fp, op, value}) + return q +} + +// Direction is the sort direction for result ordering. +type Direction int32 + +const ( + // Asc sorts results from smallest to largest. + Asc Direction = Direction(pb.StructuredQuery_ASCENDING) + + // Desc sorts results from largest to smallest. + Desc Direction = Direction(pb.StructuredQuery_DESCENDING) +) + +// OrderBy returns a new Query that specifies the order in which results are +// returned. A Query can have multiple OrderBy/OrderByPath specifications. OrderBy +// appends the specification to the list of existing ones. +// +// The path argument can be a single field or a dot-separated sequence of +// fields, and must not contain any of the runes "˜*/[]". +// +// To order by document name, use the special field path DocumentID. +func (q Query) OrderBy(path string, dir Direction) Query { + fp, err := parseDotSeparatedString(path) + if err != nil { + q.err = err + return q + } + q.orders = append(q.copyOrders(), order{fp, dir}) + return q +} + +// OrderByPath returns a new Query that specifies the order in which results are +// returned. A Query can have multiple OrderBy/OrderByPath specifications. +// OrderByPath appends the specification to the list of existing ones. +func (q Query) OrderByPath(fp FieldPath, dir Direction) Query { + q.orders = append(q.copyOrders(), order{fp, dir}) + return q +} + +func (q *Query) copyOrders() []order { + return append([]order(nil), q.orders...) +} + +// Offset returns a new Query that specifies the number of initial results to skip. +// It must not be negative. +func (q Query) Offset(n int) Query { + q.offset = trunc32(n) + return q +} + +// Limit returns a new Query that specifies the maximum number of results to return. +// It must not be negative. +func (q Query) Limit(n int) Query { + q.limit = &wrappers.Int32Value{Value: trunc32(n)} + return q +} + +// StartAt returns a new Query that specifies that results should start at +// the document with the given field values. +// +// If StartAt is called with a single DocumentSnapshot, its field values are used. +// The DocumentSnapshot must have all the fields mentioned in the OrderBy clauses. +// +// Otherwise, StartAt should be called with one field value for each OrderBy clause, +// in the order that they appear. For example, in +// q.OrderBy("X", Asc).OrderBy("Y", Desc).StartAt(1, 2) +// results will begin at the first document where X = 1 and Y = 2. +// +// If an OrderBy call uses the special DocumentID field path, the corresponding value +// should be the document ID relative to the query's collection. For example, to +// start at the document "NewYork" in the "States" collection, write +// +// client.Collection("States").OrderBy(DocumentID, firestore.Asc).StartAt("NewYork") +// +// Calling StartAt overrides a previous call to StartAt or StartAfter. +func (q Query) StartAt(docSnapshotOrFieldValues ...interface{}) Query { + q.startBefore = true + q.startVals, q.startDoc, q.err = q.processCursorArg("StartAt", docSnapshotOrFieldValues) + return q +} + +// StartAfter returns a new Query that specifies that results should start just after +// the document with the given field values. See Query.StartAt for more information. +// +// Calling StartAfter overrides a previous call to StartAt or StartAfter. +func (q Query) StartAfter(docSnapshotOrFieldValues ...interface{}) Query { + q.startBefore = false + q.startVals, q.startDoc, q.err = q.processCursorArg("StartAfter", docSnapshotOrFieldValues) + return q +} + +// EndAt returns a new Query that specifies that results should end at the +// document with the given field values. See Query.StartAt for more information. +// +// Calling EndAt overrides a previous call to EndAt or EndBefore. +func (q Query) EndAt(docSnapshotOrFieldValues ...interface{}) Query { + q.endBefore = false + q.endVals, q.endDoc, q.err = q.processCursorArg("EndAt", docSnapshotOrFieldValues) + return q +} + +// EndBefore returns a new Query that specifies that results should end just before +// the document with the given field values. See Query.StartAt for more information. +// +// Calling EndBefore overrides a previous call to EndAt or EndBefore. +func (q Query) EndBefore(docSnapshotOrFieldValues ...interface{}) Query { + q.endBefore = true + q.endVals, q.endDoc, q.err = q.processCursorArg("EndBefore", docSnapshotOrFieldValues) + return q +} + +func (q *Query) processCursorArg(name string, docSnapshotOrFieldValues []interface{}) ([]interface{}, *DocumentSnapshot, error) { + for _, e := range docSnapshotOrFieldValues { + if ds, ok := e.(*DocumentSnapshot); ok { + if len(docSnapshotOrFieldValues) == 1 { + return nil, ds, nil + } + return nil, nil, fmt.Errorf("firestore: a document snapshot must be the only argument to %s", name) + } + } + return docSnapshotOrFieldValues, nil, nil +} + +func (q Query) query() *Query { return &q } + +func (q Query) toProto() (*pb.StructuredQuery, error) { + if q.err != nil { + return nil, q.err + } + if q.collectionID == "" { + return nil, errors.New("firestore: query created without CollectionRef") + } + p := &pb.StructuredQuery{ + From: []*pb.StructuredQuery_CollectionSelector{{CollectionId: q.collectionID}}, + Offset: q.offset, + Limit: q.limit, + } + if len(q.selection) > 0 { + p.Select = &pb.StructuredQuery_Projection{} + for _, fp := range q.selection { + if err := fp.validate(); err != nil { + return nil, err + } + p.Select.Fields = append(p.Select.Fields, fref(fp)) + } + } + // If there is only filter, use it directly. Otherwise, construct + // a CompositeFilter. + if len(q.filters) == 1 { + pf, err := q.filters[0].toProto() + if err != nil { + return nil, err + } + p.Where = pf + } else if len(q.filters) > 1 { + cf := &pb.StructuredQuery_CompositeFilter{ + Op: pb.StructuredQuery_CompositeFilter_AND, + } + p.Where = &pb.StructuredQuery_Filter{ + FilterType: &pb.StructuredQuery_Filter_CompositeFilter{cf}, + } + for _, f := range q.filters { + pf, err := f.toProto() + if err != nil { + return nil, err + } + cf.Filters = append(cf.Filters, pf) + } + } + orders := q.orders + if q.startDoc != nil || q.endDoc != nil { + orders = q.adjustOrders() + } + for _, ord := range orders { + po, err := ord.toProto() + if err != nil { + return nil, err + } + p.OrderBy = append(p.OrderBy, po) + } + + cursor, err := q.toCursor(q.startVals, q.startDoc, q.startBefore, orders) + if err != nil { + return nil, err + } + p.StartAt = cursor + cursor, err = q.toCursor(q.endVals, q.endDoc, q.endBefore, orders) + if err != nil { + return nil, err + } + p.EndAt = cursor + return p, nil +} + +// If there is a start/end that uses a Document Snapshot, we may need to adjust the OrderBy +// clauses that the user provided: we add OrderBy(__name__) if it isn't already present, and +// we make sure we don't invalidate the original query by adding an OrderBy for inequality filters. +func (q *Query) adjustOrders() []order { + // If the user is already ordering by document ID, don't change anything. + for _, ord := range q.orders { + if ord.isDocumentID() { + return q.orders + } + } + // If there are OrderBy clauses, append an OrderBy(DocumentID), using the direction of the last OrderBy clause. + if len(q.orders) > 0 { + return append(q.copyOrders(), order{ + fieldPath: FieldPath{DocumentID}, + dir: q.orders[len(q.orders)-1].dir, + }) + } + // If there are no OrderBy clauses but there is an inequality, add an OrderBy clause + // for the field of the first inequality. + var orders []order + for _, f := range q.filters { + if f.op != "==" { + orders = []order{{fieldPath: f.fieldPath, dir: Asc}} + break + } + } + // Add an ascending OrderBy(DocumentID). + return append(orders, order{fieldPath: FieldPath{DocumentID}, dir: Asc}) +} + +func (q *Query) toCursor(fieldValues []interface{}, ds *DocumentSnapshot, before bool, orders []order) (*pb.Cursor, error) { + var vals []*pb.Value + var err error + if ds != nil { + vals, err = q.docSnapshotToCursorValues(ds, orders) + } else if len(fieldValues) != 0 { + vals, err = q.fieldValuesToCursorValues(fieldValues) + } else { + return nil, nil + } + if err != nil { + return nil, err + } + return &pb.Cursor{Values: vals, Before: before}, nil +} + +// toPositionValues converts the field values to protos. +func (q *Query) fieldValuesToCursorValues(fieldValues []interface{}) ([]*pb.Value, error) { + if len(fieldValues) != len(q.orders) { + return nil, errors.New("firestore: number of field values in StartAt/StartAfter/EndAt/EndBefore does not match number of OrderBy fields") + } + vals := make([]*pb.Value, len(fieldValues)) + var err error + for i, ord := range q.orders { + fval := fieldValues[i] + if ord.isDocumentID() { + // TODO(jba): support DocumentRefs as well as strings. + // TODO(jba): error if document ref does not belong to the right collection. + docID, ok := fval.(string) + if !ok { + return nil, fmt.Errorf("firestore: expected doc ID for DocumentID field, got %T", fval) + } + vals[i] = &pb.Value{ValueType: &pb.Value_ReferenceValue{q.collectionPath() + "/" + docID}} + } else { + var sawTransform bool + vals[i], sawTransform, err = toProtoValue(reflect.ValueOf(fval)) + if err != nil { + return nil, err + } + if sawTransform { + return nil, errors.New("firestore: ServerTimestamp disallowed in query value") + } + } + } + return vals, nil +} + +func (q *Query) docSnapshotToCursorValues(ds *DocumentSnapshot, orders []order) ([]*pb.Value, error) { + // TODO(jba): error if doc snap does not belong to the right collection. + vals := make([]*pb.Value, len(orders)) + for i, ord := range orders { + if ord.isDocumentID() { + dp, qp := ds.Ref.Parent.Path, q.collectionPath() + if dp != qp { + return nil, fmt.Errorf("firestore: document snapshot for %s passed to query on %s", dp, qp) + } + vals[i] = &pb.Value{ValueType: &pb.Value_ReferenceValue{ds.Ref.Path}} + } else { + val, err := valueAtPath(ord.fieldPath, ds.proto.Fields) + if err != nil { + return nil, err + } + vals[i] = val + } + } + return vals, nil +} + +// Returns a function that compares DocumentSnapshots according to q's ordering. +func (q Query) compareFunc() func(d1, d2 *DocumentSnapshot) (int, error) { + // Add implicit sorting by name, using the last specified direction. + lastDir := Asc + if len(q.orders) > 0 { + lastDir = q.orders[len(q.orders)-1].dir + } + orders := append(q.copyOrders(), order{[]string{DocumentID}, lastDir}) + return func(d1, d2 *DocumentSnapshot) (int, error) { + for _, ord := range orders { + var cmp int + if len(ord.fieldPath) == 1 && ord.fieldPath[0] == DocumentID { + cmp = compareReferences(d1.Ref.Path, d2.Ref.Path) + } else { + v1, err := valueAtPath(ord.fieldPath, d1.proto.Fields) + if err != nil { + return 0, err + } + v2, err := valueAtPath(ord.fieldPath, d2.proto.Fields) + if err != nil { + return 0, err + } + cmp = compareValues(v1, v2) + } + if cmp != 0 { + if ord.dir == Desc { + cmp = -cmp + } + return cmp, nil + } + } + return 0, nil + } +} + +type filter struct { + fieldPath FieldPath + op string + value interface{} +} + +func (f filter) toProto() (*pb.StructuredQuery_Filter, error) { + if err := f.fieldPath.validate(); err != nil { + return nil, err + } + if uop, ok := unaryOpFor(f.value); ok { + if f.op != "==" { + return nil, fmt.Errorf("firestore: must use '==' when comparing %v", f.value) + } + return &pb.StructuredQuery_Filter{ + FilterType: &pb.StructuredQuery_Filter_UnaryFilter{ + UnaryFilter: &pb.StructuredQuery_UnaryFilter{ + OperandType: &pb.StructuredQuery_UnaryFilter_Field{ + Field: fref(f.fieldPath), + }, + Op: uop, + }, + }, + }, nil + } + var op pb.StructuredQuery_FieldFilter_Operator + switch f.op { + case "<": + op = pb.StructuredQuery_FieldFilter_LESS_THAN + case "<=": + op = pb.StructuredQuery_FieldFilter_LESS_THAN_OR_EQUAL + case ">": + op = pb.StructuredQuery_FieldFilter_GREATER_THAN + case ">=": + op = pb.StructuredQuery_FieldFilter_GREATER_THAN_OR_EQUAL + case "==": + op = pb.StructuredQuery_FieldFilter_EQUAL + default: + return nil, fmt.Errorf("firestore: invalid operator %q", f.op) + } + val, sawTransform, err := toProtoValue(reflect.ValueOf(f.value)) + if err != nil { + return nil, err + } + if sawTransform { + return nil, errors.New("firestore: ServerTimestamp disallowed in query value") + } + return &pb.StructuredQuery_Filter{ + FilterType: &pb.StructuredQuery_Filter_FieldFilter{ + FieldFilter: &pb.StructuredQuery_FieldFilter{ + Field: fref(f.fieldPath), + Op: op, + Value: val, + }, + }, + }, nil +} + +func unaryOpFor(value interface{}) (pb.StructuredQuery_UnaryFilter_Operator, bool) { + switch { + case value == nil: + return pb.StructuredQuery_UnaryFilter_IS_NULL, true + case isNaN(value): + return pb.StructuredQuery_UnaryFilter_IS_NAN, true + default: + return pb.StructuredQuery_UnaryFilter_OPERATOR_UNSPECIFIED, false + } +} + +func isNaN(x interface{}) bool { + switch x := x.(type) { + case float32: + return math.IsNaN(float64(x)) + case float64: + return math.IsNaN(x) + default: + return false + } +} + +type order struct { + fieldPath FieldPath + dir Direction +} + +func (r order) isDocumentID() bool { + return len(r.fieldPath) == 1 && r.fieldPath[0] == DocumentID +} + +func (r order) toProto() (*pb.StructuredQuery_Order, error) { + if err := r.fieldPath.validate(); err != nil { + return nil, err + } + return &pb.StructuredQuery_Order{ + Field: fref(r.fieldPath), + Direction: pb.StructuredQuery_Direction(r.dir), + }, nil +} + +func fref(fp FieldPath) *pb.StructuredQuery_FieldReference { + return &pb.StructuredQuery_FieldReference{FieldPath: fp.toServiceFieldPath()} +} + +func trunc32(i int) int32 { + if i > math.MaxInt32 { + i = math.MaxInt32 + } + return int32(i) +} + +// Documents returns an iterator over the query's resulting documents. +func (q Query) Documents(ctx context.Context) *DocumentIterator { + return &DocumentIterator{ + iter: newQueryDocumentIterator(withResourceHeader(ctx, q.c.path()), &q, nil), + err: checkTransaction(ctx), + } +} + +// DocumentIterator is an iterator over documents returned by a query. +type DocumentIterator struct { + iter docIterator + err error +} + +// Unexported interface so we can have two different kinds of DocumentIterator: one +// for straight queries, and one for query snapshots. We do it this way instead of +// making DocumentIterator an interface because in the client libraries, iterators are +// always concrete types, and the fact that this one has two different implementations +// is an internal detail. +type docIterator interface { + next() (*DocumentSnapshot, error) + stop() +} + +// 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 *DocumentIterator) Next() (*DocumentSnapshot, error) { + if it.err != nil { + return nil, it.err + } + ds, err := it.iter.next() + if err != nil { + it.err = err + } + return ds, err +} + +// Stop stops the iterator, freeing its resources. +// Always call Stop when you are done with an iterator. +// It is not safe to call Stop concurrently with Next. +func (it *DocumentIterator) Stop() { + if it.iter != nil { // possible in error cases + it.iter.stop() + } + if it.err == nil { + it.err = iterator.Done + } +} + +// GetAll returns all the documents remaining from the iterator. +// It is not necessary to call Stop on the iterator after calling GetAll. +func (it *DocumentIterator) GetAll() ([]*DocumentSnapshot, error) { + defer it.Stop() + var docs []*DocumentSnapshot + for { + doc, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return nil, err + } + docs = append(docs, doc) + } + return docs, nil +} + +type queryDocumentIterator struct { + ctx context.Context + cancel func() + q *Query + tid []byte // transaction ID, if any + streamClient pb.Firestore_RunQueryClient +} + +func newQueryDocumentIterator(ctx context.Context, q *Query, tid []byte) *queryDocumentIterator { + ctx, cancel := context.WithCancel(ctx) + return &queryDocumentIterator{ + ctx: ctx, + cancel: cancel, + q: q, + tid: tid, + } +} + +func (it *queryDocumentIterator) next() (*DocumentSnapshot, error) { + client := it.q.c + if it.streamClient == nil { + sq, err := it.q.toProto() + if err != nil { + return nil, err + } + req := &pb.RunQueryRequest{ + Parent: it.q.parentPath, + QueryType: &pb.RunQueryRequest_StructuredQuery{sq}, + } + if it.tid != nil { + req.ConsistencySelector = &pb.RunQueryRequest_Transaction{it.tid} + } + it.streamClient, err = client.c.RunQuery(it.ctx, req) + if err != nil { + return nil, err + } + } + var res *pb.RunQueryResponse + var err error + for { + res, err = it.streamClient.Recv() + if err == io.EOF { + return nil, iterator.Done + } + if err != nil { + return nil, err + } + if res.Document != nil { + break + } + // No document => partial progress; keep receiving. + } + docRef, err := pathToDoc(res.Document.Name, client) + if err != nil { + return nil, err + } + doc, err := newDocumentSnapshot(docRef, res.Document, client, res.ReadTime) + if err != nil { + return nil, err + } + return doc, nil +} + +func (it *queryDocumentIterator) stop() { + it.cancel() +} + +// Snapshots returns an iterator over snapshots of the query. Each time the query +// results change, a new snapshot will be generated. +func (q Query) Snapshots(ctx context.Context) *QuerySnapshotIterator { + ws, err := newWatchStreamForQuery(ctx, q) + if err != nil { + return &QuerySnapshotIterator{err: err} + } + return &QuerySnapshotIterator{ + Query: q, + ws: ws, + } +} + +// QuerySnapshotIterator is an iterator over snapshots of a query. +// Call Next on the iterator to get a snapshot of the query's results each time they change. +// Call Stop on the iterator when done. +// +// For an example, see Query.Snapshots. +type QuerySnapshotIterator struct { + // The Query used to construct this iterator. + Query Query + + // The time at which the most recent snapshot was obtained from Firestore. + ReadTime time.Time + + // The number of results in the most recent snapshot. + Size int + + // The changes since the previous snapshot. + Changes []DocumentChange + + ws *watchStream + err error +} + +// Next blocks until the query's results change, then returns a DocumentIterator for +// the current results. +// +// Next never returns iterator.Done unless it is called after Stop. +func (it *QuerySnapshotIterator) Next() (*DocumentIterator, error) { + if it.err != nil { + return nil, it.err + } + btree, changes, readTime, err := it.ws.nextSnapshot() + if err != nil { + if err == io.EOF { + err = iterator.Done + } + it.err = err + return nil, it.err + } + it.Changes = changes + it.ReadTime = readTime + it.Size = btree.Len() + return &DocumentIterator{ + iter: (*btreeDocumentIterator)(btree.BeforeIndex(0)), + }, nil +} + +// Stop stops receiving snapshots. +// You should always call Stop when you are done with an iterator, to free up resources. +// It is not safe to call Stop concurrently with Next. +func (it *QuerySnapshotIterator) Stop() { + it.ws.stop() +} + +type btreeDocumentIterator btree.Iterator + +func (it *btreeDocumentIterator) next() (*DocumentSnapshot, error) { + if !(*btree.Iterator)(it).Next() { + return nil, iterator.Done + } + return it.Key.(*DocumentSnapshot), nil +} + +func (*btreeDocumentIterator) stop() {} diff --git a/vendor/cloud.google.com/go/firestore/query_test.go b/vendor/cloud.google.com/go/firestore/query_test.go new file mode 100644 index 0000000000..86f036ffa0 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/query_test.go @@ -0,0 +1,717 @@ +// Copyright 2017 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 firestore + +import ( + "math" + "sort" + "testing" + + "golang.org/x/net/context" + + "cloud.google.com/go/internal/pretty" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + tspb "github.com/golang/protobuf/ptypes/timestamp" + "github.com/golang/protobuf/ptypes/wrappers" +) + +func TestFilterToProto(t *testing.T) { + for _, test := range []struct { + in filter + want *pb.StructuredQuery_Filter + }{ + { + filter{[]string{"a"}, ">", 1}, + &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_FieldFilter{ + FieldFilter: &pb.StructuredQuery_FieldFilter{ + Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"}, + Op: pb.StructuredQuery_FieldFilter_GREATER_THAN, + Value: intval(1), + }, + }}, + }, + { + filter{[]string{"a"}, "==", nil}, + &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{ + UnaryFilter: &pb.StructuredQuery_UnaryFilter{ + OperandType: &pb.StructuredQuery_UnaryFilter_Field{ + Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"}, + }, + Op: pb.StructuredQuery_UnaryFilter_IS_NULL, + }, + }}, + }, + { + filter{[]string{"a"}, "==", math.NaN()}, + &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{ + UnaryFilter: &pb.StructuredQuery_UnaryFilter{ + OperandType: &pb.StructuredQuery_UnaryFilter_Field{ + Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"}, + }, + Op: pb.StructuredQuery_UnaryFilter_IS_NAN, + }, + }}, + }, + } { + got, err := test.in.toProto() + if err != nil { + t.Fatal(err) + } + if !testEqual(got, test.want) { + t.Errorf("%+v:\ngot\n%v\nwant\n%v", test.in, pretty.Value(got), pretty.Value(test.want)) + } + } +} + +func TestQueryToProto(t *testing.T) { + filtr := func(path []string, op string, val interface{}) *pb.StructuredQuery_Filter { + f, err := filter{path, op, val}.toProto() + if err != nil { + t.Fatal(err) + } + return f + } + + c := &Client{projectID: "P", databaseID: "DB"} + coll := c.Collection("C") + q := coll.Query + type S struct { + A int `firestore:"a"` + } + docsnap := &DocumentSnapshot{ + Ref: coll.Doc("D"), + proto: &pb.Document{ + Fields: map[string]*pb.Value{"a": intval(7), "b": intval(8)}, + }, + } + for _, test := range []struct { + desc string + in Query + want *pb.StructuredQuery + }{ + { + desc: "q.Select()", + in: q.Select(), + want: &pb.StructuredQuery{ + Select: &pb.StructuredQuery_Projection{ + Fields: []*pb.StructuredQuery_FieldReference{fref1("__name__")}, + }, + }, + }, + { + desc: `q.Select("a", "b")`, + in: q.Select("a", "b"), + want: &pb.StructuredQuery{ + Select: &pb.StructuredQuery_Projection{ + Fields: []*pb.StructuredQuery_FieldReference{fref1("a"), fref1("b")}, + }, + }, + }, + { + desc: `q.Select("a", "b").Select("c")`, + in: q.Select("a", "b").Select("c"), // last wins + want: &pb.StructuredQuery{ + Select: &pb.StructuredQuery_Projection{ + Fields: []*pb.StructuredQuery_FieldReference{fref1("c")}, + }, + }, + }, + { + desc: `q.SelectPaths([]string{"*"}, []string{"/"})`, + in: q.SelectPaths([]string{"*"}, []string{"/"}), + want: &pb.StructuredQuery{ + Select: &pb.StructuredQuery_Projection{ + Fields: []*pb.StructuredQuery_FieldReference{fref1("*"), fref1("/")}, + }, + }, + }, + { + desc: `q.Where("a", ">", 5)`, + in: q.Where("a", ">", 5), + want: &pb.StructuredQuery{Where: filtr([]string{"a"}, ">", 5)}, + }, + { + desc: `q.Where("a", "==", NaN)`, + in: q.Where("a", "==", float32(math.NaN())), + want: &pb.StructuredQuery{Where: filtr([]string{"a"}, "==", math.NaN())}, + }, + { + desc: `q.Where("a", ">", 5).Where("b", "<", "foo")`, + in: q.Where("a", ">", 5).Where("b", "<", "foo"), + want: &pb.StructuredQuery{ + Where: &pb.StructuredQuery_Filter{ + FilterType: &pb.StructuredQuery_Filter_CompositeFilter{ + &pb.StructuredQuery_CompositeFilter{ + Op: pb.StructuredQuery_CompositeFilter_AND, + Filters: []*pb.StructuredQuery_Filter{ + filtr([]string{"a"}, ">", 5), filtr([]string{"b"}, "<", "foo"), + }, + }, + }, + }, + }, + }, + { + desc: ` q.WherePath([]string{"/", "*"}, ">", 5)`, + in: q.WherePath([]string{"/", "*"}, ">", 5), + want: &pb.StructuredQuery{Where: filtr([]string{"/", "*"}, ">", 5)}, + }, + { + desc: `q.OrderBy("b", Asc).OrderBy("a", Desc).OrderByPath([]string{"~"}, Asc)`, + in: q.OrderBy("b", Asc).OrderBy("a", Desc).OrderByPath([]string{"~"}, Asc), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("b"), Direction: pb.StructuredQuery_ASCENDING}, + {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING}, + {Field: fref1("~"), Direction: pb.StructuredQuery_ASCENDING}, + }, + }, + }, + { + desc: `q.Offset(2).Limit(3)`, + in: q.Offset(2).Limit(3), + want: &pb.StructuredQuery{ + Offset: 2, + Limit: &wrappers.Int32Value{Value: 3}, + }, + }, + { + desc: `q.Offset(2).Limit(3).Limit(4).Offset(5)`, + in: q.Offset(2).Limit(3).Limit(4).Offset(5), // last wins + want: &pb.StructuredQuery{ + Offset: 5, + Limit: &wrappers.Int32Value{Value: 4}, + }, + }, + { + desc: `q.OrderBy("a", Asc).StartAt(7).EndBefore(9)`, + in: q.OrderBy("a", Asc).StartAt(7).EndBefore(9), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7)}, + Before: true, + }, + EndAt: &pb.Cursor{ + Values: []*pb.Value{intval(9)}, + Before: true, + }, + }, + }, + { + desc: `q.OrderBy("a", Asc).StartAt(7).EndAt(9)`, + in: q.OrderBy("a", Asc).StartAt(7).EndAt(9), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7)}, + Before: true, + }, + EndAt: &pb.Cursor{ + Values: []*pb.Value{intval(9)}, + Before: false, + }, + }, + }, + { + desc: `q.OrderBy("a", Asc).StartAfter(7).EndAt(9)`, + in: q.OrderBy("a", Asc).StartAfter(7).EndAt(9), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7)}, + Before: false, + }, + EndAt: &pb.Cursor{ + Values: []*pb.Value{intval(9)}, + Before: false, + }, + }, + }, + { + desc: `q.OrderBy(DocumentID, Asc).StartAfter("foo").EndBefore("bar")`, + in: q.OrderBy(DocumentID, Asc).StartAfter("foo").EndBefore("bar"), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{refval(coll.parentPath + "/documents/C/foo")}, + Before: false, + }, + EndAt: &pb.Cursor{ + Values: []*pb.Value{refval(coll.parentPath + "/documents/C/bar")}, + Before: true, + }, + }, + }, + { + desc: `q.OrderBy("a", Asc).OrderBy("b", Desc).StartAfter(7, 8).EndAt(9, 10)`, + in: q.OrderBy("a", Asc).OrderBy("b", Desc).StartAfter(7, 8).EndAt(9, 10), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + {Field: fref1("b"), Direction: pb.StructuredQuery_DESCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7), intval(8)}, + Before: false, + }, + EndAt: &pb.Cursor{ + Values: []*pb.Value{intval(9), intval(10)}, + Before: false, + }, + }, + }, + { + // last of StartAt/After wins, same for End + desc: `q.OrderBy("a", Asc).StartAfter(1).StartAt(2).EndAt(3).EndBefore(4)`, + in: q.OrderBy("a", Asc). + StartAfter(1).StartAt(2). + EndAt(3).EndBefore(4), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(2)}, + Before: true, + }, + EndAt: &pb.Cursor{ + Values: []*pb.Value{intval(4)}, + Before: true, + }, + }, + }, + // Start/End with DocumentSnapshot + // These tests are from the "Document Snapshot Cursors" doc. + { + desc: `q.StartAt(docsnap)`, + in: q.StartAt(docsnap), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{refval(coll.parentPath + "/documents/C/D")}, + Before: true, + }, + }, + }, + { + desc: `q.OrderBy("a", Asc).StartAt(docsnap)`, + in: q.OrderBy("a", Asc).StartAt(docsnap), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")}, + Before: true, + }, + }, + }, + + { + desc: `q.OrderBy("a", Desc).StartAt(docsnap)`, + in: q.OrderBy("a", Desc).StartAt(docsnap), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING}, + {Field: fref1("__name__"), Direction: pb.StructuredQuery_DESCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")}, + Before: true, + }, + }, + }, + { + desc: `q.OrderBy("a", Desc).OrderBy("b", Asc).StartAt(docsnap)`, + in: q.OrderBy("a", Desc).OrderBy("b", Asc).StartAt(docsnap), + want: &pb.StructuredQuery{ + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING}, + {Field: fref1("b"), Direction: pb.StructuredQuery_ASCENDING}, + {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7), intval(8), refval(coll.parentPath + "/documents/C/D")}, + Before: true, + }, + }, + }, + { + desc: `q.Where("a", "==", 3).StartAt(docsnap)`, + in: q.Where("a", "==", 3).StartAt(docsnap), + want: &pb.StructuredQuery{ + Where: filtr([]string{"a"}, "==", 3), + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{refval(coll.parentPath + "/documents/C/D")}, + Before: true, + }, + }, + }, + { + desc: `q.Where("a", "<", 3).StartAt(docsnap)`, + in: q.Where("a", "<", 3).StartAt(docsnap), + want: &pb.StructuredQuery{ + Where: filtr([]string{"a"}, "<", 3), + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")}, + Before: true, + }, + }, + }, + { + desc: `q.Where("b", "==", 1).Where("a", "<", 3).StartAt(docsnap)`, + in: q.Where("b", "==", 1).Where("a", "<", 3).StartAt(docsnap), + want: &pb.StructuredQuery{ + Where: &pb.StructuredQuery_Filter{ + FilterType: &pb.StructuredQuery_Filter_CompositeFilter{ + &pb.StructuredQuery_CompositeFilter{ + Op: pb.StructuredQuery_CompositeFilter_AND, + Filters: []*pb.StructuredQuery_Filter{ + filtr([]string{"b"}, "==", 1), + filtr([]string{"a"}, "<", 3), + }, + }, + }, + }, + OrderBy: []*pb.StructuredQuery_Order{ + {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING}, + {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING}, + }, + StartAt: &pb.Cursor{ + Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")}, + Before: true, + }, + }, + }, + } { + got, err := test.in.toProto() + if err != nil { + t.Errorf("%s: %v", test.desc, err) + continue + } + test.want.From = []*pb.StructuredQuery_CollectionSelector{{CollectionId: "C"}} + if !testEqual(got, test.want) { + t.Errorf("%s:\ngot\n%v\nwant\n%v", test.desc, pretty.Value(got), pretty.Value(test.want)) + } + } +} + +func fref1(s string) *pb.StructuredQuery_FieldReference { + return fref([]string{s}) +} + +func TestQueryToProtoErrors(t *testing.T) { + st := map[string]interface{}{"a": ServerTimestamp} + del := map[string]interface{}{"a": Delete} + c := &Client{projectID: "P", databaseID: "DB"} + coll := c.Collection("C") + docsnap := &DocumentSnapshot{ + Ref: coll.Doc("D"), + proto: &pb.Document{ + Fields: map[string]*pb.Value{"a": intval(7)}, + }, + } + q := coll.Query + for _, query := range []Query{ + {}, // no collection ID + q.Where("x", "!=", 1), // invalid operator + q.Where("~", ">", 1), // invalid path + q.WherePath([]string{"*", ""}, ">", 1), // invalid path + q.StartAt(1), // no OrderBy + q.StartAt(2).OrderBy("x", Asc).OrderBy("y", Desc), // wrong # OrderBy + q.Select("*"), // invalid path + q.SelectPaths([]string{"/", "", "~"}), // invalid path + q.OrderBy("[", Asc), // invalid path + q.OrderByPath([]string{""}, Desc), // invalid path + q.Where("x", "==", st), // ServerTimestamp in filter + q.OrderBy("a", Asc).StartAt(st), // ServerTimestamp in Start + q.OrderBy("a", Asc).EndAt(st), // ServerTimestamp in End + q.Where("x", "==", del), // Delete in filter + q.OrderBy("a", Asc).StartAt(del), // Delete in Start + q.OrderBy("a", Asc).EndAt(del), // Delete in End + q.OrderBy(DocumentID, Asc).StartAt(7), // wrong type for __name__ + q.OrderBy(DocumentID, Asc).EndAt(7), // wrong type for __name__ + q.OrderBy("b", Asc).StartAt(docsnap), // doc snapshot does not have order-by field + q.StartAt(docsnap).EndAt("x"), // mixed doc snapshot and fields + q.StartAfter("x").EndBefore(docsnap), // mixed doc snapshot and fields + } { + _, err := query.toProto() + if err == nil { + t.Errorf("%+v: got nil, want error", query) + } + } +} + +func TestQueryMethodsDoNotModifyReceiver(t *testing.T) { + var empty Query + + q := Query{} + _ = q.Select("a", "b") + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + + q = Query{} + q1 := q.Where("a", ">", 3) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + // Extra check because Where appends to a slice. + q1before := q.Where("a", ">", 3) // same as q1 + _ = q1.Where("b", "<", "foo") + if !testEqual(q1, q1before) { + t.Errorf("got %+v, want %+v", q1, q1before) + } + + q = Query{} + q1 = q.OrderBy("a", Asc) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + // Extra check because Where appends to a slice. + q1before = q.OrderBy("a", Asc) // same as q1 + _ = q1.OrderBy("b", Desc) + if !testEqual(q1, q1before) { + t.Errorf("got %+v, want %+v", q1, q1before) + } + + q = Query{} + _ = q.Offset(5) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + + q = Query{} + _ = q.Limit(5) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + + q = Query{} + _ = q.StartAt(5) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + + q = Query{} + _ = q.StartAfter(5) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + + q = Query{} + _ = q.EndAt(5) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } + + q = Query{} + _ = q.EndBefore(5) + if !testEqual(q, empty) { + t.Errorf("got %+v, want empty", q) + } +} + +func TestQueryFromCollectionRef(t *testing.T) { + c := &Client{} + coll := c.Collection("C") + got := coll.Select("x").Offset(8) + want := Query{ + c: c, + parentPath: c.path(), + collectionID: "C", + selection: []FieldPath{{"x"}}, + offset: 8, + } + if !testEqual(got, want) { + t.Fatalf("got %+v, want %+v", got, want) + } +} + +func TestQueryGetAll(t *testing.T) { + // This implicitly tests DocumentIterator as well. + const dbPath = "projects/projectID/databases/(default)" + ctx := context.Background() + c, srv := newMock(t) + docNames := []string{"C/a", "C/b"} + wantPBDocs := []*pb.Document{ + { + Name: dbPath + "/documents/" + docNames[0], + CreateTime: aTimestamp, + UpdateTime: aTimestamp, + Fields: map[string]*pb.Value{"f": intval(2)}, + }, + { + Name: dbPath + "/documents/" + docNames[1], + CreateTime: aTimestamp2, + UpdateTime: aTimestamp3, + Fields: map[string]*pb.Value{"f": intval(1)}, + }, + } + wantReadTimes := []*tspb.Timestamp{aTimestamp, aTimestamp2} + srv.addRPC(nil, []interface{}{ + &pb.RunQueryResponse{Document: wantPBDocs[0], ReadTime: aTimestamp}, + &pb.RunQueryResponse{Document: wantPBDocs[1], ReadTime: aTimestamp2}, + }) + gotDocs, err := c.Collection("C").Documents(ctx).GetAll() + if err != nil { + t.Fatal(err) + } + if got, want := len(gotDocs), len(wantPBDocs); got != want { + t.Errorf("got %d docs, wanted %d", got, want) + } + for i, got := range gotDocs { + want, err := newDocumentSnapshot(c.Doc(docNames[i]), wantPBDocs[i], c, wantReadTimes[i]) + if err != nil { + t.Fatal(err) + } + if !testEqual(got, want) { + // avoid writing a cycle + got.c = nil + want.c = nil + t.Errorf("#%d: got %+v, want %+v", i, pretty.Value(got), pretty.Value(want)) + } + } +} + +func TestQueryCompareFunc(t *testing.T) { + mv := func(fields ...interface{}) map[string]*pb.Value { + m := map[string]*pb.Value{} + for i := 0; i < len(fields); i += 2 { + m[fields[i].(string)] = fields[i+1].(*pb.Value) + } + return m + } + snap := func(ref *DocumentRef, fields map[string]*pb.Value) *DocumentSnapshot { + return &DocumentSnapshot{Ref: ref, proto: &pb.Document{Fields: fields}} + } + + c := &Client{} + coll := c.Collection("C") + doc1 := coll.Doc("doc1") + doc2 := coll.Doc("doc2") + doc3 := coll.Doc("doc3") + doc4 := coll.Doc("doc4") + for _, test := range []struct { + q Query + in []*DocumentSnapshot + want []*DocumentSnapshot + }{ + { + q: coll.OrderBy("foo", Asc), + in: []*DocumentSnapshot{ + snap(doc3, mv("foo", intval(2))), + snap(doc4, mv("foo", intval(1))), + snap(doc2, mv("foo", intval(2))), + }, + want: []*DocumentSnapshot{ + snap(doc4, mv("foo", intval(1))), + snap(doc2, mv("foo", intval(2))), + snap(doc3, mv("foo", intval(2))), + }, + }, + { + q: coll.OrderBy("foo", Desc), + in: []*DocumentSnapshot{ + snap(doc3, mv("foo", intval(2))), + snap(doc4, mv("foo", intval(1))), + snap(doc2, mv("foo", intval(2))), + }, + want: []*DocumentSnapshot{ + snap(doc3, mv("foo", intval(2))), + snap(doc2, mv("foo", intval(2))), + snap(doc4, mv("foo", intval(1))), + }, + }, + { + q: coll.OrderBy("foo.bar", Asc), + in: []*DocumentSnapshot{ + snap(doc1, mv("foo", mapval(mv("bar", intval(1))))), + snap(doc2, mv("foo", mapval(mv("bar", intval(2))))), + snap(doc3, mv("foo", mapval(mv("bar", intval(2))))), + }, + want: []*DocumentSnapshot{ + snap(doc1, mv("foo", mapval(mv("bar", intval(1))))), + snap(doc2, mv("foo", mapval(mv("bar", intval(2))))), + snap(doc3, mv("foo", mapval(mv("bar", intval(2))))), + }, + }, + { + q: coll.OrderBy("foo.bar", Desc), + in: []*DocumentSnapshot{ + snap(doc1, mv("foo", mapval(mv("bar", intval(1))))), + snap(doc2, mv("foo", mapval(mv("bar", intval(2))))), + snap(doc3, mv("foo", mapval(mv("bar", intval(2))))), + }, + want: []*DocumentSnapshot{ + snap(doc3, mv("foo", mapval(mv("bar", intval(2))))), + snap(doc2, mv("foo", mapval(mv("bar", intval(2))))), + snap(doc1, mv("foo", mapval(mv("bar", intval(1))))), + }, + }, + } { + got := append([]*DocumentSnapshot(nil), test.in...) + sort.Sort(byQuery{test.q.compareFunc(), got}) + if diff := testDiff(got, test.want); diff != "" { + t.Errorf("%+v: %s", test.q, diff) + } + } + + // Want error on missing field. + q := coll.OrderBy("bar", Asc) + if q.err != nil { + t.Fatalf("bad query: %v", q.err) + } + cf := q.compareFunc() + s := snap(doc1, mv("foo", intval(1))) + if _, err := cf(s, s); err == nil { + t.Error("got nil, want error") + } +} + +type byQuery struct { + compare func(d1, d2 *DocumentSnapshot) (int, error) + docs []*DocumentSnapshot +} + +func (b byQuery) Len() int { return len(b.docs) } +func (b byQuery) Swap(i, j int) { b.docs[i], b.docs[j] = b.docs[j], b.docs[i] } +func (b byQuery) Less(i, j int) bool { + c, err := b.compare(b.docs[i], b.docs[j]) + if err != nil { + panic(err) + } + return c < 0 +} diff --git a/vendor/cloud.google.com/go/firestore/testdata/VERSION b/vendor/cloud.google.com/go/firestore/testdata/VERSION new file mode 100644 index 0000000000..672cd45198 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/VERSION @@ -0,0 +1 @@ +SHA1(/usr/local/google/home/jba/go/src/github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/testdata/test-suite.binproto)= 6c622d8affaf1ea2ebeb063f9898f5d942343cb7 diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-basic.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-basic.textproto new file mode 100644 index 0000000000..433ffda727 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-basic.textproto @@ -0,0 +1,27 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A simple call, resulting in a single update operation. + +description: "create: basic" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + current_document: < + exists: false + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-complex.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-complex.textproto new file mode 100644 index 0000000000..00a994e204 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-complex.textproto @@ -0,0 +1,61 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A call to a write method with complicated input data. + +description: "create: complex" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + array_value: < + values: < + integer_value: 1 + > + values: < + double_value: 2.5 + > + > + > + > + fields: < + key: "b" + value: < + map_value: < + fields: < + key: "c" + value: < + array_value: < + values: < + string_value: "three" + > + values: < + map_value: < + fields: < + key: "d" + value: < + boolean_value: true + > + > + > + > + > + > + > + > + > + > + > + current_document: < + exists: false + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-del-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-del-noarray-nested.textproto new file mode 100644 index 0000000000..60694e1371 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-del-noarray-nested.textproto @@ -0,0 +1,13 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "create: Delete cannot be anywhere inside an array value" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-del-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-del-noarray.textproto new file mode 100644 index 0000000000..5731be1c73 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-del-noarray.textproto @@ -0,0 +1,13 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "create: Delete cannot be in an array value" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2, \"Delete\"]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-empty.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-empty.textproto new file mode 100644 index 0000000000..2b6fec7efa --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-empty.textproto @@ -0,0 +1,20 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + + +description: "create: creating or setting an empty map" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + current_document: < + exists: false + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-nodel.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-nodel.textproto new file mode 100644 index 0000000000..c878814b11 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-nodel.textproto @@ -0,0 +1,11 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel cannot be used in Create, or in Set without a Merge option. + +description: "create: Delete cannot appear in data" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"Delete\"}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-nosplit.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-nosplit.textproto new file mode 100644 index 0000000000..e9e1ee2755 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-nosplit.textproto @@ -0,0 +1,40 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Create and Set treat their map keys literally. They do not split on dots. + +description: "create: don\342\200\231t split on dots" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{ \"a.b\": { \"c.d\": 1 }, \"e\": 2 }" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a.b" + value: < + map_value: < + fields: < + key: "c.d" + value: < + integer_value: 1 + > + > + > + > + > + fields: < + key: "e" + value: < + integer_value: 2 + > + > + > + current_document: < + exists: false + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-special-chars.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-special-chars.textproto new file mode 100644 index 0000000000..3a7acd3075 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-special-chars.textproto @@ -0,0 +1,41 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Create and Set treat their map keys literally. They do not escape special +# characters. + +description: "create: non-alpha characters in map keys" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{ \"*\": { \".\": 1 }, \"~\": 2 }" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "*" + value: < + map_value: < + fields: < + key: "." + value: < + integer_value: 1 + > + > + > + > + > + fields: < + key: "~" + value: < + integer_value: 2 + > + > + > + current_document: < + exists: false + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-st-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-st-alone.textproto new file mode 100644 index 0000000000..9803a676bb --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-st-alone.textproto @@ -0,0 +1,26 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the only values in the input are ServerTimestamps, then no update operation +# should be produced. + +description: "create: ServerTimestamp alone" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "a" + set_to_server_value: REQUEST_TIME + > + > + current_document: < + exists: false + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-st-multi.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-st-multi.textproto new file mode 100644 index 0000000000..cb3db48099 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-st-multi.textproto @@ -0,0 +1,41 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A document can have more than one ServerTimestamp field. Since all the +# ServerTimestamp fields are removed, the only field in the update is "a". + +description: "create: multiple ServerTimestamp fields" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + current_document: < + exists: false + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + field_transforms: < + field_path: "c.d" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-st-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-st-nested.textproto new file mode 100644 index 0000000000..6bc03e8e7c --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-st-nested.textproto @@ -0,0 +1,38 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A ServerTimestamp value can occur at any depth. In this case, the transform +# applies to the field path "b.c". Since "c" is removed from the update, "b" +# becomes empty, so it is also removed from the update. + +description: "create: nested ServerTimestamp field" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + current_document: < + exists: false + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b.c" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-st-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-st-noarray-nested.textproto new file mode 100644 index 0000000000..0cec0aebd4 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-st-noarray-nested.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# There cannot be an array value anywhere on the path from the document root to +# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. + +description: "create: ServerTimestamp cannot be anywhere inside an array value" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-st-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-st-noarray.textproto new file mode 100644 index 0000000000..56d91c2cfb --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-st-noarray.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The ServerTimestamp sentinel must be the value of a field. Firestore transforms +# don't support array indexing. + +description: "create: ServerTimestamp cannot be in an array value" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/create-st.textproto b/vendor/cloud.google.com/go/firestore/testdata/create-st.textproto new file mode 100644 index 0000000000..ddfc6a177e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/create-st.textproto @@ -0,0 +1,39 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A key with the special ServerTimestamp sentinel is removed from the data in the +# update operation. Instead it appears in a separate Transform operation. Note +# that in these tests, the string "ServerTimestamp" should be replaced with the +# special ServerTimestamp value. + +description: "create: ServerTimestamp with data" +create: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + current_document: < + exists: false + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/delete-exists-precond.textproto b/vendor/cloud.google.com/go/firestore/testdata/delete-exists-precond.textproto new file mode 100644 index 0000000000..c9cf2ddea4 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/delete-exists-precond.textproto @@ -0,0 +1,21 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Delete supports an exists precondition. + +description: "delete: delete with exists precondition" +delete: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + precondition: < + exists: true + > + request: < + database: "projects/projectID/databases/(default)" + writes: < + delete: "projects/projectID/databases/(default)/documents/C/d" + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/delete-no-precond.textproto b/vendor/cloud.google.com/go/firestore/testdata/delete-no-precond.textproto new file mode 100644 index 0000000000..a396cdb8c4 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/delete-no-precond.textproto @@ -0,0 +1,15 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# An ordinary Delete call. + +description: "delete: delete without precondition" +delete: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + request: < + database: "projects/projectID/databases/(default)" + writes: < + delete: "projects/projectID/databases/(default)/documents/C/d" + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/delete-time-precond.textproto b/vendor/cloud.google.com/go/firestore/testdata/delete-time-precond.textproto new file mode 100644 index 0000000000..5798f5f3b2 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/delete-time-precond.textproto @@ -0,0 +1,25 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Delete supports a last-update-time precondition. + +description: "delete: delete with last-update-time precondition" +delete: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + precondition: < + update_time: < + seconds: 42 + > + > + request: < + database: "projects/projectID/databases/(default)" + writes: < + delete: "projects/projectID/databases/(default)/documents/C/d" + current_document: < + update_time: < + seconds: 42 + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/get-basic.textproto b/vendor/cloud.google.com/go/firestore/testdata/get-basic.textproto new file mode 100644 index 0000000000..2a44816825 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/get-basic.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A call to DocumentRef.Get. + +description: "get: get a document" +get: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + request: < + name: "projects/projectID/databases/(default)/documents/C/d" + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-add-mod-del-add.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-add-mod-del-add.textproto new file mode 100644 index 0000000000..1aa8dcbc36 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-add-mod-del-add.textproto @@ -0,0 +1,246 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Various changes to a single document. + +description: "listen: add a doc, modify it, delete it, then add it again" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + responses: < + document_delete: < + document: "projects/projectID/databases/(default)/documents/C/d1" + > + > + responses: < + target_change: < + read_time: < + seconds: 3 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + read_time: < + seconds: 4 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + read_time: < + seconds: 1 + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + changes: < + kind: MODIFIED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + > + read_time: < + seconds: 2 + > + > + snapshots: < + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + new_index: -1 + > + read_time: < + seconds: 3 + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + old_index: -1 + > + read_time: < + seconds: 4 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-add-one.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-add-one.textproto new file mode 100644 index 0000000000..2ad1d8e976 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-add-one.textproto @@ -0,0 +1,79 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Snapshot with a single document. + +description: "listen: add a doc" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + read_time: < + seconds: 2 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-add-three.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-add-three.textproto new file mode 100644 index 0000000000..ac846f7626 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-add-three.textproto @@ -0,0 +1,190 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A snapshot with three documents. The documents are sorted first by the "a" +# field, then by their path. The changes are ordered the same way. + +description: "listen: add three documents" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 2 + > + read_time: < + seconds: 2 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-doc-remove.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-doc-remove.textproto new file mode 100644 index 0000000000..975200f973 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-doc-remove.textproto @@ -0,0 +1,115 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The DocumentRemove response behaves exactly like DocumentDelete. + +description: "listen: DocumentRemove behaves like DocumentDelete" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + responses: < + document_remove: < + document: "projects/projectID/databases/(default)/documents/C/d1" + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + read_time: < + seconds: 1 + > + > + snapshots: < + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + new_index: -1 + > + read_time: < + seconds: 2 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-empty.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-empty.textproto new file mode 100644 index 0000000000..4d04b79096 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-empty.textproto @@ -0,0 +1,25 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# There are no changes, so the snapshot should be empty. + +description: "listen: no changes; empty snapshot" +listen: < + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + snapshots: < + read_time: < + seconds: 1 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-filter-nop.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-filter-nop.textproto new file mode 100644 index 0000000000..48fd72d3ae --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-filter-nop.textproto @@ -0,0 +1,247 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Filter response whose count matches the size of the current state (docs in +# last snapshot + docs added - docs deleted) is a no-op. + +description: "listen: Filter response with same size is a no-op" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_delete: < + document: "projects/projectID/databases/(default)/documents/C/d1" + > + > + responses: < + filter: < + count: 2 + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 1 + > + read_time: < + seconds: 1 + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: 1 + new_index: -1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 1 + > + read_time: < + seconds: 2 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-multi-docs.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-multi-docs.textproto new file mode 100644 index 0000000000..8778acc3d1 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-multi-docs.textproto @@ -0,0 +1,524 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Changes should be ordered with deletes first, then additions, then mods, each in +# query order. Old indices refer to the immediately previous state, not the +# previous snapshot + +description: "listen: multiple documents, added, deleted and updated" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d4" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d5" + fields: < + key: "a" + value: < + integer_value: 4 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_delete: < + document: "projects/projectID/databases/(default)/documents/C/d3" + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: -1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d6" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_delete: < + document: "projects/projectID/databases/(default)/documents/C/d2" + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d4" + fields: < + key: "a" + value: < + integer_value: -2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + read_time: < + seconds: 4 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d4" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d4" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 2 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 3 + > + read_time: < + seconds: 2 + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d4" + fields: < + key: "a" + value: < + integer_value: -2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: -1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d6" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d5" + fields: < + key: "a" + value: < + integer_value: 4 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + new_index: -1 + > + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + new_index: -1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d6" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 2 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d5" + fields: < + key: "a" + value: < + integer_value: 4 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 3 + > + changes: < + kind: MODIFIED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d4" + fields: < + key: "a" + value: < + integer_value: -2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + > + changes: < + kind: MODIFIED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: -1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + old_index: 1 + new_index: 1 + > + read_time: < + seconds: 4 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-nocurrent.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-nocurrent.textproto new file mode 100644 index 0000000000..24239b6456 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-nocurrent.textproto @@ -0,0 +1,141 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the watch state is not marked CURRENT, no snapshot is issued. + +description: "listen: no snapshot if we don't see CURRENT" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + old_index: -1 + new_index: 1 + > + read_time: < + seconds: 2 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-nomod.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-nomod.textproto new file mode 100644 index 0000000000..2a99edc350 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-nomod.textproto @@ -0,0 +1,143 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Document updates are recognized by a change in the update time, not the data. +# This shouldn't actually happen. It is just a test of the update logic. + +description: "listen: add a doc, then change it but without changing its update time" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + responses: < + document_delete: < + document: "projects/projectID/databases/(default)/documents/C/d1" + > + > + responses: < + target_change: < + read_time: < + seconds: 3 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + read_time: < + seconds: 1 + > + > + snapshots: < + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + new_index: -1 + > + read_time: < + seconds: 3 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-removed-target-ids.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-removed-target-ids.textproto new file mode 100644 index 0000000000..1e8ead2d80 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-removed-target-ids.textproto @@ -0,0 +1,131 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A DocumentChange with the watch target ID in the removed_target_ids field is the +# same as deleting a document. + +description: "listen: DocumentChange with removed_target_id is like a delete." +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + removed_target_ids: 1 + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + read_time: < + seconds: 1 + > + > + snapshots: < + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + new_index: -1 + > + read_time: < + seconds: 2 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-reset.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-reset.textproto new file mode 100644 index 0000000000..0c6cf8a55b --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-reset.textproto @@ -0,0 +1,258 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A RESET message turns off the CURRENT state, and marks all documents as deleted. +# If a document appeared on the stream but was never part of a snapshot ("d3" in +# this test), a reset will make it disappear completely. + +description: "listen: RESET turns off CURRENT" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d3" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: RESET + > + > + responses: < + target_change: < + read_time: < + seconds: 2 + > + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + read_time: < + seconds: 3 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 2 + > + > + old_index: -1 + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + new_index: 1 + > + read_time: < + seconds: 1 + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + changes: < + kind: REMOVED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 2 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: 1 + new_index: -1 + > + changes: < + kind: MODIFIED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d2" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 3 + > + > + > + read_time: < + seconds: 3 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-target-add-nop.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-target-add-nop.textproto new file mode 100644 index 0000000000..3fa7cce56e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-target-add-nop.textproto @@ -0,0 +1,88 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A TargetChange_ADD response must have the same watch target ID. + +description: "listen: TargetChange_ADD is a no-op if it has the same target ID" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + target_change_type: ADD + target_ids: 1 + read_time: < + seconds: 2 + > + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + snapshots: < + docs: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + changes: < + kind: ADDED + doc: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + old_index: -1 + > + read_time: < + seconds: 1 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-target-add-wrong-id.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-target-add-wrong-id.textproto new file mode 100644 index 0000000000..87544637b5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-target-add-wrong-id.textproto @@ -0,0 +1,50 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A TargetChange_ADD response must have the same watch target ID. + +description: "listen: TargetChange_ADD is an error if it has a different target ID" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + target_change_type: ADD + target_ids: 2 + read_time: < + seconds: 2 + > + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/listen-target-remove.textproto b/vendor/cloud.google.com/go/firestore/testdata/listen-target-remove.textproto new file mode 100644 index 0000000000..f34b0890c3 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/listen-target-remove.textproto @@ -0,0 +1,46 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A TargetChange_REMOVE response should never be sent. + +description: "listen: TargetChange_REMOVE should not appear" +listen: < + responses: < + document_change: < + document: < + name: "projects/projectID/databases/(default)/documents/C/d1" + fields: < + key: "a" + value: < + integer_value: 3 + > + > + create_time: < + seconds: 1 + > + update_time: < + seconds: 1 + > + > + target_ids: 1 + > + > + responses: < + target_change: < + target_change_type: CURRENT + > + > + responses: < + target_change: < + target_change_type: REMOVE + > + > + responses: < + target_change: < + read_time: < + seconds: 1 + > + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-bad-NaN.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-bad-NaN.textproto new file mode 100644 index 0000000000..6806dd04ab --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-bad-NaN.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# You can only compare NaN for equality. + +description: "query: where clause with non-== comparison with NaN" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "<" + json_value: "\"NaN\"" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-bad-null.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-bad-null.textproto new file mode 100644 index 0000000000..7fdfb3f2b5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-bad-null.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# You can only compare Null for equality. + +description: "query: where clause with non-== comparison with Null" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: ">" + json_value: "null" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-order.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-order.textproto new file mode 100644 index 0000000000..bab8601e8d --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-order.textproto @@ -0,0 +1,68 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# When a document snapshot is used, the client appends a __name__ order-by clause +# with the direction of the last order-by clause. + +description: "query: cursor methods with a document snapshot, existing orderBy" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + order_by: < + path: < + field: "b" + > + direction: "desc" + > + > + clauses: < + start_after: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + order_by: < + field: < + field_path: "b" + > + direction: DESCENDING + > + order_by: < + field: < + field_path: "__name__" + > + direction: DESCENDING + > + start_at: < + values: < + integer_value: 7 + > + values: < + integer_value: 8 + > + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-orderby-name.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-orderby-name.textproto new file mode 100644 index 0000000000..d0ce3df45a --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-orderby-name.textproto @@ -0,0 +1,76 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If there is an existing orderBy clause on __name__, no changes are made to the +# list of orderBy clauses. + +description: "query: cursor method, doc snapshot, existing orderBy __name__" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "desc" + > + > + clauses: < + order_by: < + path: < + field: "__name__" + > + direction: "asc" + > + > + clauses: < + start_at: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + clauses: < + end_at: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: DESCENDING + > + order_by: < + field: < + field_path: "__name__" + > + direction: ASCENDING + > + start_at: < + values: < + integer_value: 7 + > + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D" + > + before: true + > + end_at: < + values: < + integer_value: 7 + > + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-eq.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-eq.textproto new file mode 100644 index 0000000000..8b1e217df5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-eq.textproto @@ -0,0 +1,53 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Where clause using equality doesn't change the implicit orderBy clauses. + +description: "query: cursor methods with a document snapshot and an equality where clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "==" + json_value: "3" + > + > + clauses: < + end_at: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + query: < + from: < + collection_id: "C" + > + where: < + field_filter: < + field: < + field_path: "a" + > + op: EQUAL + value: < + integer_value: 3 + > + > + > + order_by: < + field: < + field_path: "__name__" + > + direction: ASCENDING + > + end_at: < + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-neq-orderby.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-neq-orderby.textproto new file mode 100644 index 0000000000..a69edfc50d --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-neq-orderby.textproto @@ -0,0 +1,72 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If there is an OrderBy clause, the inequality Where clause does not result in a +# new OrderBy clause. We still add a __name__ OrderBy clause + +description: "query: cursor method, doc snapshot, inequality where clause, and existing orderBy clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "desc" + > + > + clauses: < + where: < + path: < + field: "a" + > + op: "<" + json_value: "4" + > + > + clauses: < + start_at: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + query: < + from: < + collection_id: "C" + > + where: < + field_filter: < + field: < + field_path: "a" + > + op: LESS_THAN + value: < + integer_value: 4 + > + > + > + order_by: < + field: < + field_path: "a" + > + direction: DESCENDING + > + order_by: < + field: < + field_path: "__name__" + > + direction: DESCENDING + > + start_at: < + values: < + integer_value: 7 + > + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D" + > + before: true + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-neq.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-neq.textproto new file mode 100644 index 0000000000..871dd0ba33 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap-where-neq.textproto @@ -0,0 +1,64 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Where clause with an inequality results in an OrderBy clause on that clause's +# path, if there are no other OrderBy clauses. + +description: "query: cursor method with a document snapshot and an inequality where clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "<=" + json_value: "3" + > + > + clauses: < + end_before: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + query: < + from: < + collection_id: "C" + > + where: < + field_filter: < + field: < + field_path: "a" + > + op: LESS_THAN_OR_EQUAL + value: < + integer_value: 3 + > + > + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + order_by: < + field: < + field_path: "__name__" + > + direction: ASCENDING + > + end_at: < + values: < + integer_value: 7 + > + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D" + > + before: true + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap.textproto new file mode 100644 index 0000000000..184bffc2d3 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-docsnap.textproto @@ -0,0 +1,34 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# When a document snapshot is used, the client appends a __name__ order-by clause. + +description: "query: cursor methods with a document snapshot" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + start_at: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "__name__" + > + direction: ASCENDING + > + start_at: < + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D" + > + before: true + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-no-order.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-no-order.textproto new file mode 100644 index 0000000000..fb999ddabb --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-no-order.textproto @@ -0,0 +1,16 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a cursor method with a list of values is provided, there must be at least as +# many explicit orderBy clauses as values. + +description: "query: cursor method without orderBy" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + start_at: < + json_values: "2" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-1a.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-1a.textproto new file mode 100644 index 0000000000..bb08ab7d4d --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-1a.textproto @@ -0,0 +1,50 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor methods take the same number of values as there are OrderBy clauses. + +description: "query: StartAt/EndBefore with values" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + start_at: < + json_values: "7" + > + > + clauses: < + end_before: < + json_values: "9" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + start_at: < + values: < + integer_value: 7 + > + before: true + > + end_at: < + values: < + integer_value: 9 + > + before: true + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-1b.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-1b.textproto new file mode 100644 index 0000000000..41e69e9e6f --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-1b.textproto @@ -0,0 +1,48 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor methods take the same number of values as there are OrderBy clauses. + +description: "query: StartAfter/EndAt with values" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + start_after: < + json_values: "7" + > + > + clauses: < + end_at: < + json_values: "9" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + start_at: < + values: < + integer_value: 7 + > + > + end_at: < + values: < + integer_value: 9 + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-2.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-2.textproto new file mode 100644 index 0000000000..8e37ad0035 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-2.textproto @@ -0,0 +1,71 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor methods take the same number of values as there are OrderBy clauses. + +description: "query: Start/End with two values" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + order_by: < + path: < + field: "b" + > + direction: "desc" + > + > + clauses: < + start_at: < + json_values: "7" + json_values: "8" + > + > + clauses: < + end_at: < + json_values: "9" + json_values: "10" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + order_by: < + field: < + field_path: "b" + > + direction: DESCENDING + > + start_at: < + values: < + integer_value: 7 + > + values: < + integer_value: 8 + > + before: true + > + end_at: < + values: < + integer_value: 9 + > + values: < + integer_value: 10 + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-docid.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-docid.textproto new file mode 100644 index 0000000000..91af3486c9 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-docid.textproto @@ -0,0 +1,50 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Cursor values corresponding to a __name__ field take the document path relative +# to the query's collection. + +description: "query: cursor methods with __name__" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "__name__" + > + direction: "asc" + > + > + clauses: < + start_after: < + json_values: "\"D1\"" + > + > + clauses: < + end_before: < + json_values: "\"D2\"" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "__name__" + > + direction: ASCENDING + > + start_at: < + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D1" + > + > + end_at: < + values: < + reference_value: "projects/projectID/databases/(default)/documents/C/D2" + > + before: true + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-last-wins.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-last-wins.textproto new file mode 100644 index 0000000000..9e8fbb19f3 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-cursor-vals-last-wins.textproto @@ -0,0 +1,60 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# When multiple Start* or End* calls occur, the values of the last one are used. + +description: "query: cursor methods, last one wins" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + start_after: < + json_values: "1" + > + > + clauses: < + start_at: < + json_values: "2" + > + > + clauses: < + end_at: < + json_values: "3" + > + > + clauses: < + end_before: < + json_values: "4" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "a" + > + direction: ASCENDING + > + start_at: < + values: < + integer_value: 2 + > + before: true + > + end_at: < + values: < + integer_value: 4 + > + before: true + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-del-cursor.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-del-cursor.textproto new file mode 100644 index 0000000000..c9d4adb7c5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-del-cursor.textproto @@ -0,0 +1,23 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Sentinel values are not permitted in queries. + +description: "query: Delete in cursor method" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + end_before: < + json_values: "\"Delete\"" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-del-where.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-del-where.textproto new file mode 100644 index 0000000000..8e92529492 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-del-where.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Sentinel values are not permitted in queries. + +description: "query: Delete in Where" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "==" + json_value: "\"Delete\"" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-invalid-operator.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-operator.textproto new file mode 100644 index 0000000000..e580c64a75 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-operator.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The != operator is not supported. + +description: "query: invalid operator in Where clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "!=" + json_value: "4" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-order.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-order.textproto new file mode 100644 index 0000000000..e0a7205762 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-order.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The path has an empty component. + +description: "query: invalid path in OrderBy clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "*" + field: "" + > + direction: "asc" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-select.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-select.textproto new file mode 100644 index 0000000000..944f984f7f --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-select.textproto @@ -0,0 +1,18 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The path has an empty component. + +description: "query: invalid path in Where clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + select: < + fields: < + field: "*" + field: "" + > + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-where.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-where.textproto new file mode 100644 index 0000000000..527923b097 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-invalid-path-where.textproto @@ -0,0 +1,20 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The path has an empty component. + +description: "query: invalid path in Where clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "*" + field: "" + > + op: "==" + json_value: "4" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-offset-limit-last-wins.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-offset-limit-last-wins.textproto new file mode 100644 index 0000000000..dc301f439e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-offset-limit-last-wins.textproto @@ -0,0 +1,30 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# With multiple Offset or Limit clauses, the last one wins. + +description: "query: multiple Offset and Limit clauses" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + offset: 2 + > + clauses: < + limit: 3 + > + clauses: < + limit: 4 + > + clauses: < + offset: 5 + > + query: < + from: < + collection_id: "C" + > + offset: 5 + limit: < + value: 4 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-offset-limit.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-offset-limit.textproto new file mode 100644 index 0000000000..136d9d46a6 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-offset-limit.textproto @@ -0,0 +1,24 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Offset and Limit clauses. + +description: "query: Offset and Limit clauses" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + offset: 2 + > + clauses: < + limit: 3 + > + query: < + from: < + collection_id: "C" + > + offset: 2 + limit: < + value: 3 + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-order.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-order.textproto new file mode 100644 index 0000000000..7ed4c4ead8 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-order.textproto @@ -0,0 +1,42 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Multiple OrderBy clauses combine. + +description: "query: basic OrderBy clauses" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "b" + > + direction: "asc" + > + > + clauses: < + order_by: < + path: < + field: "a" + > + direction: "desc" + > + > + query: < + from: < + collection_id: "C" + > + order_by: < + field: < + field_path: "b" + > + direction: ASCENDING + > + order_by: < + field: < + field_path: "a" + > + direction: DESCENDING + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-select-empty.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-select-empty.textproto new file mode 100644 index 0000000000..def8b55ac5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-select-empty.textproto @@ -0,0 +1,23 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# An empty Select clause selects just the document ID. + +description: "query: empty Select clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + select: < + > + > + query: < + select: < + fields: < + field_path: "__name__" + > + > + from: < + collection_id: "C" + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-select-last-wins.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-select-last-wins.textproto new file mode 100644 index 0000000000..bd78d09eb9 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-select-last-wins.textproto @@ -0,0 +1,36 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The last Select clause is the only one used. + +description: "query: two Select clauses" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + select: < + fields: < + field: "a" + > + fields: < + field: "b" + > + > + > + clauses: < + select: < + fields: < + field: "c" + > + > + > + query: < + select: < + fields: < + field_path: "c" + > + > + from: < + collection_id: "C" + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-select.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-select.textproto new file mode 100644 index 0000000000..15e1124973 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-select.textproto @@ -0,0 +1,32 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# An ordinary Select clause. + +description: "query: Select clause with some fields" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + select: < + fields: < + field: "a" + > + fields: < + field: "b" + > + > + > + query: < + select: < + fields: < + field_path: "a" + > + fields: < + field_path: "b" + > + > + from: < + collection_id: "C" + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-st-cursor.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-st-cursor.textproto new file mode 100644 index 0000000000..66885d0dd5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-st-cursor.textproto @@ -0,0 +1,23 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Sentinel values are not permitted in queries. + +description: "query: ServerTimestamp in cursor method" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + order_by: < + path: < + field: "a" + > + direction: "asc" + > + > + clauses: < + end_before: < + json_values: "\"ServerTimestamp\"" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-st-where.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-st-where.textproto new file mode 100644 index 0000000000..05da28d542 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-st-where.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Sentinel values are not permitted in queries. + +description: "query: ServerTimestamp in Where" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "==" + json_value: "\"ServerTimestamp\"" + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-where-2.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-where-2.textproto new file mode 100644 index 0000000000..1034463079 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-where-2.textproto @@ -0,0 +1,59 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Multiple Where clauses are combined into a composite filter. + +description: "query: two Where clauses" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: ">=" + json_value: "5" + > + > + clauses: < + where: < + path: < + field: "b" + > + op: "<" + json_value: "\"foo\"" + > + > + query: < + from: < + collection_id: "C" + > + where: < + composite_filter: < + op: AND + filters: < + field_filter: < + field: < + field_path: "a" + > + op: GREATER_THAN_OR_EQUAL + value: < + integer_value: 5 + > + > + > + filters: < + field_filter: < + field: < + field_path: "b" + > + op: LESS_THAN + value: < + string_value: "foo" + > + > + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-where-NaN.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-where-NaN.textproto new file mode 100644 index 0000000000..4a97ca7dde --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-where-NaN.textproto @@ -0,0 +1,31 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Where clause that tests for equality with NaN results in a unary filter. + +description: "query: a Where clause comparing to NaN" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "==" + json_value: "\"NaN\"" + > + > + query: < + from: < + collection_id: "C" + > + where: < + unary_filter: < + op: IS_NAN + field: < + field_path: "a" + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-where-null.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-where-null.textproto new file mode 100644 index 0000000000..1869c60c72 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-where-null.textproto @@ -0,0 +1,31 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Where clause that tests for equality with null results in a unary filter. + +description: "query: a Where clause comparing to null" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: "==" + json_value: "null" + > + > + query: < + from: < + collection_id: "C" + > + where: < + unary_filter: < + op: IS_NULL + field: < + field_path: "a" + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-where.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-where.textproto new file mode 100644 index 0000000000..045c2befab --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-where.textproto @@ -0,0 +1,34 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A simple Where clause. + +description: "query: Where clause" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + where: < + path: < + field: "a" + > + op: ">" + json_value: "5" + > + > + query: < + from: < + collection_id: "C" + > + where: < + field_filter: < + field: < + field_path: "a" + > + op: GREATER_THAN + value: < + integer_value: 5 + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/query-wrong-collection.textproto b/vendor/cloud.google.com/go/firestore/testdata/query-wrong-collection.textproto new file mode 100644 index 0000000000..ad6f353d5f --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/query-wrong-collection.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a document snapshot is passed to a Start*/End* method, it must be in the same +# collection as the query. + +description: "query: doc snapshot with wrong collection in cursor method" +query: < + coll_path: "projects/projectID/databases/(default)/documents/C" + clauses: < + end_before: < + doc_snapshot: < + path: "projects/projectID/databases/(default)/documents/C2/D" + json_data: "{\"a\": 7, \"b\": 8}" + > + > + > + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-basic.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-basic.textproto new file mode 100644 index 0000000000..e9b292e3cd --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-basic.textproto @@ -0,0 +1,24 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A simple call, resulting in a single update operation. + +description: "set: basic" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-complex.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-complex.textproto new file mode 100644 index 0000000000..6ec19500a2 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-complex.textproto @@ -0,0 +1,58 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A call to a write method with complicated input data. + +description: "set: complex" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + array_value: < + values: < + integer_value: 1 + > + values: < + double_value: 2.5 + > + > + > + > + fields: < + key: "b" + value: < + map_value: < + fields: < + key: "c" + value: < + array_value: < + values: < + string_value: "three" + > + values: < + map_value: < + fields: < + key: "d" + value: < + boolean_value: true + > + > + > + > + > + > + > + > + > + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-merge-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-merge-alone.textproto new file mode 100644 index 0000000000..811ab8dfe7 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-merge-alone.textproto @@ -0,0 +1,28 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Delete sentinel can appear with a merge option. If the delete paths are the +# only ones to be merged, then no document is sent, just an update mask. + +description: "set-merge: Delete with merge" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "b" + field: "c" + > + > + json_data: "{\"a\": 1, \"b\": {\"c\": \"Delete\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + update_mask: < + field_paths: "b.c" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-merge.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-merge.textproto new file mode 100644 index 0000000000..b8d8631051 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-merge.textproto @@ -0,0 +1,37 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Delete sentinel can appear with a merge option. + +description: "set-merge: Delete with merge" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "a" + > + fields: < + field: "b" + field: "c" + > + > + json_data: "{\"a\": 1, \"b\": {\"c\": \"Delete\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b.c" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-mergeall.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-mergeall.textproto new file mode 100644 index 0000000000..af1e84524b --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-mergeall.textproto @@ -0,0 +1,31 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A Delete sentinel can appear with a mergeAll option. + +description: "set: Delete with MergeAll" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + all: true + > + json_data: "{\"a\": 1, \"b\": {\"c\": \"Delete\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b.c" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-noarray-nested.textproto new file mode 100644 index 0000000000..bbf6a3d00a --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-noarray-nested.textproto @@ -0,0 +1,13 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "set: Delete cannot be anywhere inside an array value" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-noarray.textproto new file mode 100644 index 0000000000..07fc6497dc --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-noarray.textproto @@ -0,0 +1,13 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "set: Delete cannot be in an array value" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2, \"Delete\"]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-nomerge.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-nomerge.textproto new file mode 100644 index 0000000000..cb6ef4f858 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-nomerge.textproto @@ -0,0 +1,17 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The client signals an error if the Delete sentinel is in the input data, but not +# selected by a merge option, because this is most likely a programming bug. + +description: "set-merge: Delete cannot appear in an unmerged field" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "a" + > + > + json_data: "{\"a\": 1, \"b\": \"Delete\"}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-nonleaf.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-nonleaf.textproto new file mode 100644 index 0000000000..54f22d95c5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-nonleaf.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a Delete is part of the value at a merge path, then the user is confused: +# their merge path says "replace this entire value" but their Delete says "delete +# this part of the value". This should be an error, just as if they specified +# Delete in a Set with no merge. + +description: "set-merge: Delete cannot appear as part of a merge path" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "h" + > + > + json_data: "{\"h\": {\"g\": \"Delete\"}}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-del-wo-merge.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-del-wo-merge.textproto new file mode 100644 index 0000000000..29196628bf --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-del-wo-merge.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Without a merge option, Set replaces the document with the input data. A Delete +# sentinel in the data makes no sense in this case. + +description: "set: Delete cannot appear unless a merge option is specified" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"Delete\"}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-empty.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-empty.textproto new file mode 100644 index 0000000000..c2b73d3ff9 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-empty.textproto @@ -0,0 +1,17 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + + +description: "set: creating or setting an empty map" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-merge-fp.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-merge-fp.textproto new file mode 100644 index 0000000000..68690f6f16 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-merge-fp.textproto @@ -0,0 +1,40 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A merge with fields that use special characters. + +description: "set-merge: Merge with FieldPaths" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "*" + field: "~" + > + > + json_data: "{\"*\": {\"~\": true}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "*" + value: < + map_value: < + fields: < + key: "~" + value: < + boolean_value: true + > + > + > + > + > + > + update_mask: < + field_paths: "`*`.`~`" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-merge-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-merge-nested.textproto new file mode 100644 index 0000000000..0d1282818d --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-merge-nested.textproto @@ -0,0 +1,41 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A merge option where the field is not at top level. Only fields mentioned in the +# option are present in the update operation. + +description: "set-merge: Merge with a nested field" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "h" + field: "g" + > + > + json_data: "{\"h\": {\"g\": 4, \"f\": 5}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "h" + value: < + map_value: < + fields: < + key: "g" + value: < + integer_value: 4 + > + > + > + > + > + > + update_mask: < + field_paths: "h.g" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-merge-nonleaf.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-merge-nonleaf.textproto new file mode 100644 index 0000000000..ca41cb0340 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-merge-nonleaf.textproto @@ -0,0 +1,46 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a field path is in a merge option, the value at that path replaces the stored +# value. That is true even if the value is complex. + +description: "set-merge: Merge field is not a leaf" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "h" + > + > + json_data: "{\"h\": {\"f\": 5, \"g\": 6}, \"e\": 7}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "h" + value: < + map_value: < + fields: < + key: "f" + value: < + integer_value: 5 + > + > + fields: < + key: "g" + value: < + integer_value: 6 + > + > + > + > + > + > + update_mask: < + field_paths: "h" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-merge-prefix.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-merge-prefix.textproto new file mode 100644 index 0000000000..1e2c2c5022 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-merge-prefix.textproto @@ -0,0 +1,21 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The prefix would make the other path meaningless, so this is probably a +# programming error. + +description: "set-merge: One merge path cannot be the prefix of another" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "a" + > + fields: < + field: "a" + field: "b" + > + > + json_data: "{\"a\": {\"b\": 1}}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-merge-present.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-merge-present.textproto new file mode 100644 index 0000000000..f6665de5cd --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-merge-present.textproto @@ -0,0 +1,20 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The client signals an error if a merge option mentions a path that is not in the +# input data. + +description: "set-merge: Merge fields must all be present in data" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "b" + > + fields: < + field: "a" + > + > + json_data: "{\"a\": 1}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-merge.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-merge.textproto new file mode 100644 index 0000000000..279125253c --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-merge.textproto @@ -0,0 +1,32 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Fields in the input data but not in a merge option are pruned. + +description: "set-merge: Merge with a field" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "a" + > + > + json_data: "{\"a\": 1, \"b\": 2}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-mergeall-empty.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-mergeall-empty.textproto new file mode 100644 index 0000000000..16df8a22be --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-mergeall-empty.textproto @@ -0,0 +1,23 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# This is a valid call that can be used to ensure a document exists. + +description: "set: MergeAll can be specified with empty data." +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + all: true + > + json_data: "{}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + update_mask: < + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-mergeall-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-mergeall-nested.textproto new file mode 100644 index 0000000000..1fbc6973cd --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-mergeall-nested.textproto @@ -0,0 +1,45 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# MergeAll with nested fields results in an update mask that includes entries for +# all the leaf fields. + +description: "set: MergeAll with nested fields" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + all: true + > + json_data: "{\"h\": { \"g\": 3, \"f\": 4 }}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "h" + value: < + map_value: < + fields: < + key: "f" + value: < + integer_value: 4 + > + > + fields: < + key: "g" + value: < + integer_value: 3 + > + > + > + > + > + > + update_mask: < + field_paths: "h.f" + field_paths: "h.g" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-mergeall.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-mergeall.textproto new file mode 100644 index 0000000000..cb2ebc52bc --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-mergeall.textproto @@ -0,0 +1,37 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The MergeAll option with a simple piece of data. + +description: "set: MergeAll" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + all: true + > + json_data: "{\"a\": 1, \"b\": 2}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + fields: < + key: "b" + value: < + integer_value: 2 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-nodel.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-nodel.textproto new file mode 100644 index 0000000000..0fb887d461 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-nodel.textproto @@ -0,0 +1,11 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel cannot be used in Create, or in Set without a Merge option. + +description: "set: Delete cannot appear in data" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"Delete\"}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-nosplit.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-nosplit.textproto new file mode 100644 index 0000000000..0ff3fadcf4 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-nosplit.textproto @@ -0,0 +1,37 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Create and Set treat their map keys literally. They do not split on dots. + +description: "set: don\342\200\231t split on dots" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{ \"a.b\": { \"c.d\": 1 }, \"e\": 2 }" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a.b" + value: < + map_value: < + fields: < + key: "c.d" + value: < + integer_value: 1 + > + > + > + > + > + fields: < + key: "e" + value: < + integer_value: 2 + > + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-special-chars.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-special-chars.textproto new file mode 100644 index 0000000000..f4122c9f00 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-special-chars.textproto @@ -0,0 +1,38 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Create and Set treat their map keys literally. They do not escape special +# characters. + +description: "set: non-alpha characters in map keys" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{ \"*\": { \".\": 1 }, \"~\": 2 }" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "*" + value: < + map_value: < + fields: < + key: "." + value: < + integer_value: 1 + > + > + > + > + > + fields: < + key: "~" + value: < + integer_value: 2 + > + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-alone-mergeall.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-alone-mergeall.textproto new file mode 100644 index 0000000000..16ce4cfbd9 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-alone-mergeall.textproto @@ -0,0 +1,26 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the only values in the input are ServerTimestamps, then no update operation +# should be produced. + +description: "set: ServerTimestamp alone with MergeAll" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + all: true + > + json_data: "{\"a\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "a" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-alone.textproto new file mode 100644 index 0000000000..6ce46d7f1a --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-alone.textproto @@ -0,0 +1,28 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the only values in the input are ServerTimestamps, then an update operation +# with an empty map should be produced. + +description: "set: ServerTimestamp alone" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "a" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-both.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-both.textproto new file mode 100644 index 0000000000..5cc7bbc9ef --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-both.textproto @@ -0,0 +1,45 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Just as when no merge option is specified, ServerTimestamp sentinel values are +# removed from the data in the update operation and become transforms. + +description: "set-merge: ServerTimestamp with Merge of both fields" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "a" + > + fields: < + field: "b" + > + > + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nonleaf-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nonleaf-alone.textproto new file mode 100644 index 0000000000..f513b6c804 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nonleaf-alone.textproto @@ -0,0 +1,37 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a field path is in a merge option, the value at that path replaces the stored +# value. If the value has only ServerTimestamps, they become transforms and we +# clear the value by including the field path in the update mask. + +description: "set-merge: non-leaf merge field with ServerTimestamp alone" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "h" + > + > + json_data: "{\"h\": {\"g\": \"ServerTimestamp\"}, \"e\": 7}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + update_mask: < + field_paths: "h" + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "h.g" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nonleaf.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nonleaf.textproto new file mode 100644 index 0000000000..e53e7e2682 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nonleaf.textproto @@ -0,0 +1,49 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a field path is in a merge option, the value at that path replaces the stored +# value, and ServerTimestamps inside that value become transforms as usual. + +description: "set-merge: non-leaf merge field with ServerTimestamp" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "h" + > + > + json_data: "{\"h\": {\"f\": 5, \"g\": \"ServerTimestamp\"}, \"e\": 7}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "h" + value: < + map_value: < + fields: < + key: "f" + value: < + integer_value: 5 + > + > + > + > + > + > + update_mask: < + field_paths: "h" + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "h.g" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nowrite.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nowrite.textproto new file mode 100644 index 0000000000..3222230dc5 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-merge-nowrite.textproto @@ -0,0 +1,28 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If all the fields in the merge option have ServerTimestamp values, then no +# update operation is produced, only a transform. + +description: "set-merge: If no ordinary values in Merge, no write" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "b" + > + > + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-mergeall.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-mergeall.textproto new file mode 100644 index 0000000000..b8c53a566f --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-mergeall.textproto @@ -0,0 +1,40 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Just as when no merge option is specified, ServerTimestamp sentinel values are +# removed from the data in the update operation and become transforms. + +description: "set: ServerTimestamp with MergeAll" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + all: true + > + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-multi.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-multi.textproto new file mode 100644 index 0000000000..375ec18d68 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-multi.textproto @@ -0,0 +1,38 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A document can have more than one ServerTimestamp field. Since all the +# ServerTimestamp fields are removed, the only field in the update is "a". + +description: "set: multiple ServerTimestamp fields" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + field_transforms: < + field_path: "c.d" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-nested.textproto new file mode 100644 index 0000000000..abfd2e8fd8 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-nested.textproto @@ -0,0 +1,35 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A ServerTimestamp value can occur at any depth. In this case, the transform +# applies to the field path "b.c". Since "c" is removed from the update, "b" +# becomes empty, so it is also removed from the update. + +description: "set: nested ServerTimestamp field" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b.c" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-noarray-nested.textproto new file mode 100644 index 0000000000..241d79151a --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-noarray-nested.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# There cannot be an array value anywhere on the path from the document root to +# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. + +description: "set: ServerTimestamp cannot be anywhere inside an array value" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-noarray.textproto new file mode 100644 index 0000000000..591fb03438 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-noarray.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The ServerTimestamp sentinel must be the value of a field. Firestore transforms +# don't support array indexing. + +description: "set: ServerTimestamp cannot be in an array value" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st-nomerge.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st-nomerge.textproto new file mode 100644 index 0000000000..20c0ae1fbb --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st-nomerge.textproto @@ -0,0 +1,33 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the ServerTimestamp value is not mentioned in a merge option, then it is +# pruned from the data but does not result in a transform. + +description: "set-merge: If is ServerTimestamp not in Merge, no transform" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + option: < + fields: < + field: "a" + > + > + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/set-st.textproto b/vendor/cloud.google.com/go/firestore/testdata/set-st.textproto new file mode 100644 index 0000000000..8bceddceea --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/set-st.textproto @@ -0,0 +1,36 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A key with the special ServerTimestamp sentinel is removed from the data in the +# update operation. Instead it appears in a separate Transform operation. Note +# that in these tests, the string "ServerTimestamp" should be replaced with the +# special ServerTimestamp value. + +description: "set: ServerTimestamp with data" +set: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-badchar.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-badchar.textproto new file mode 100644 index 0000000000..656ff53b68 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-badchar.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The keys of the data given to Update are interpreted, unlike those of Create and +# Set. They cannot contain special characters. + +description: "update: invalid character" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a~b\": 1}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-basic.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-basic.textproto new file mode 100644 index 0000000000..9da316f58e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-basic.textproto @@ -0,0 +1,30 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A simple call, resulting in a single update operation. + +description: "update: basic" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-complex.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-complex.textproto new file mode 100644 index 0000000000..1a6d9eff64 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-complex.textproto @@ -0,0 +1,65 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A call to a write method with complicated input data. + +description: "update: complex" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + array_value: < + values: < + integer_value: 1 + > + values: < + double_value: 2.5 + > + > + > + > + fields: < + key: "b" + value: < + map_value: < + fields: < + key: "c" + value: < + array_value: < + values: < + string_value: "three" + > + values: < + map_value: < + fields: < + key: "d" + value: < + boolean_value: true + > + > + > + > + > + > + > + > + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-del-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-del-alone.textproto new file mode 100644 index 0000000000..8f558233f0 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-del-alone.textproto @@ -0,0 +1,25 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the input data consists solely of Deletes, then the update operation has no +# map, just an update mask. + +description: "update: Delete alone" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": \"Delete\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + update_mask: < + field_paths: "a" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-del-dot.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-del-dot.textproto new file mode 100644 index 0000000000..c0ebdf61f7 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-del-dot.textproto @@ -0,0 +1,46 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# After expanding top-level dotted fields, fields with Delete values are pruned +# from the output data, but appear in the update mask. + +description: "update: Delete with a dotted field" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b.c\": \"Delete\", \"b.d\": 2}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + fields: < + key: "b" + value: < + map_value: < + fields: < + key: "d" + value: < + integer_value: 2 + > + > + > + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b.c" + field_paths: "b.d" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-del-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-del-nested.textproto new file mode 100644 index 0000000000..ed102697e6 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-del-nested.textproto @@ -0,0 +1,11 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a top-level key. + +description: "update: Delete cannot be nested" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": {\"b\": \"Delete\"}}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-del-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-del-noarray-nested.textproto new file mode 100644 index 0000000000..a2eec49661 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-del-noarray-nested.textproto @@ -0,0 +1,13 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "update: Delete cannot be anywhere inside an array value" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-del-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-del-noarray.textproto new file mode 100644 index 0000000000..a7eea87ef4 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-del-noarray.textproto @@ -0,0 +1,13 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "update: Delete cannot be in an array value" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2, \"Delete\"]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-del.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-del.textproto new file mode 100644 index 0000000000..ec443e6c70 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-del.textproto @@ -0,0 +1,32 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a field's value is the Delete sentinel, then it doesn't appear in the update +# data, but does in the mask. + +description: "update: Delete" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"Delete\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-exists-precond.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-exists-precond.textproto new file mode 100644 index 0000000000..3c6fef4e22 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-exists-precond.textproto @@ -0,0 +1,14 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Update method does not support an explicit exists precondition. + +description: "update: Exists precondition is invalid" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + precondition: < + exists: true + > + json_data: "{\"a\": 1}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-fp-empty-component.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-fp-empty-component.textproto new file mode 100644 index 0000000000..c3bceff3e4 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-fp-empty-component.textproto @@ -0,0 +1,11 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Empty fields are not allowed. + +description: "update: empty field path component" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a..b\": 1}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-no-paths.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-no-paths.textproto new file mode 100644 index 0000000000..b524b7483f --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-no-paths.textproto @@ -0,0 +1,11 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# It is a client-side error to call Update with empty data. + +description: "update: no paths" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-basic.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-basic.textproto new file mode 100644 index 0000000000..515f29d6af --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-basic.textproto @@ -0,0 +1,33 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A simple call, resulting in a single update operation. + +description: "update-paths: basic" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "1" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-complex.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-complex.textproto new file mode 100644 index 0000000000..38a832239f --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-complex.textproto @@ -0,0 +1,72 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A call to a write method with complicated input data. + +description: "update-paths: complex" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "b" + > + json_values: "[1, 2.5]" + json_values: "{\"c\": [\"three\", {\"d\": true}]}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + array_value: < + values: < + integer_value: 1 + > + values: < + double_value: 2.5 + > + > + > + > + fields: < + key: "b" + value: < + map_value: < + fields: < + key: "c" + value: < + array_value: < + values: < + string_value: "three" + > + values: < + map_value: < + fields: < + key: "d" + value: < + boolean_value: true + > + > + > + > + > + > + > + > + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-alone.textproto new file mode 100644 index 0000000000..5dbb787de9 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-alone.textproto @@ -0,0 +1,28 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the input data consists solely of Deletes, then the update operation has no +# map, just an update mask. + +description: "update-paths: Delete alone" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "\"Delete\"" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + > + update_mask: < + field_paths: "a" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-nested.textproto new file mode 100644 index 0000000000..bdf65fb0ad --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-nested.textproto @@ -0,0 +1,14 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a top-level key. + +description: "update-paths: Delete cannot be nested" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "{\"b\": \"Delete\"}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-noarray-nested.textproto new file mode 100644 index 0000000000..d3da15dda8 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-noarray-nested.textproto @@ -0,0 +1,16 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "update-paths: Delete cannot be anywhere inside an array value" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "[1, {\"b\": \"Delete\"}]" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-noarray.textproto new file mode 100644 index 0000000000..9ebdd09451 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del-noarray.textproto @@ -0,0 +1,16 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Delete sentinel must be the value of a field. Deletes are implemented by +# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not +# support array indexing. + +description: "update-paths: Delete cannot be in an array value" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "[1, 2, \"Delete\"]" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-del.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del.textproto new file mode 100644 index 0000000000..5197a78488 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-del.textproto @@ -0,0 +1,39 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If a field's value is the Delete sentinel, then it doesn't appear in the update +# data, but does in the mask. + +description: "update-paths: Delete" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "b" + > + json_values: "1" + json_values: "\"Delete\"" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-exists-precond.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-exists-precond.textproto new file mode 100644 index 0000000000..084e07726e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-exists-precond.textproto @@ -0,0 +1,17 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Update method does not support an explicit exists precondition. + +description: "update-paths: Exists precondition is invalid" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + precondition: < + exists: true + > + field_paths: < + field: "a" + > + json_values: "1" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-del.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-del.textproto new file mode 100644 index 0000000000..5c92aeb8ca --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-del.textproto @@ -0,0 +1,47 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If one nested field is deleted, and another isn't, preserve the second. + +description: "update-paths: field paths with delete" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "foo" + field: "bar" + > + field_paths: < + field: "foo" + field: "delete" + > + json_values: "1" + json_values: "\"Delete\"" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "foo" + value: < + map_value: < + fields: < + key: "bar" + value: < + integer_value: 1 + > + > + > + > + > + > + update_mask: < + field_paths: "foo.bar" + field_paths: "foo.delete" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-dup.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-dup.textproto new file mode 100644 index 0000000000..fedbd3aab9 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-dup.textproto @@ -0,0 +1,22 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The same field cannot occur more than once. + +description: "update-paths: duplicate field path" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "b" + > + field_paths: < + field: "a" + > + json_values: "1" + json_values: "2" + json_values: "3" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-empty-component.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-empty-component.textproto new file mode 100644 index 0000000000..7a5df25b7e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-empty-component.textproto @@ -0,0 +1,15 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Empty fields are not allowed. + +description: "update-paths: empty field path component" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "*" + field: "" + > + json_values: "1" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-empty.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-empty.textproto new file mode 100644 index 0000000000..311e309326 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-empty.textproto @@ -0,0 +1,13 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A FieldPath of length zero is invalid. + +description: "update-paths: empty field path" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + > + json_values: "1" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-multi.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-multi.textproto new file mode 100644 index 0000000000..9ba41e3981 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-multi.textproto @@ -0,0 +1,42 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The UpdatePaths or equivalent method takes a list of FieldPaths. Each FieldPath +# is a sequence of uninterpreted path components. + +description: "update-paths: multiple-element field path" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + field: "b" + > + json_values: "1" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + map_value: < + fields: < + key: "b" + value: < + integer_value: 1 + > + > + > + > + > + > + update_mask: < + field_paths: "a.b" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-nosplit.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-nosplit.textproto new file mode 100644 index 0000000000..5164952667 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-fp-nosplit.textproto @@ -0,0 +1,48 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# FieldPath components are not split on dots. + +description: "update-paths: FieldPath elements are not split on dots" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a.b" + field: "f.g" + > + json_values: "{\"n.o\": 7}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a.b" + value: < + map_value: < + fields: < + key: "f.g" + value: < + map_value: < + fields: < + key: "n.o" + value: < + integer_value: 7 + > + > + > + > + > + > + > + > + > + update_mask: < + field_paths: "`a.b`.`f.g`" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-no-paths.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-no-paths.textproto new file mode 100644 index 0000000000..d9939dc947 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-no-paths.textproto @@ -0,0 +1,10 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# It is a client-side error to call Update with empty data. + +description: "update-paths: no paths" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-1.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-1.textproto new file mode 100644 index 0000000000..1710b91097 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-1.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# In the input data, one field cannot be a prefix of another. + +description: "update-paths: prefix #1" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + field: "b" + > + field_paths: < + field: "a" + > + json_values: "1" + json_values: "2" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-2.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-2.textproto new file mode 100644 index 0000000000..be78ab58a6 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-2.textproto @@ -0,0 +1,19 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# In the input data, one field cannot be a prefix of another. + +description: "update-paths: prefix #2" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "a" + field: "b" + > + json_values: "1" + json_values: "2" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-3.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-3.textproto new file mode 100644 index 0000000000..b8a84c9d1f --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-prefix-3.textproto @@ -0,0 +1,20 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# In the input data, one field cannot be a prefix of another, even if the values +# could in principle be combined. + +description: "update-paths: prefix #3" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "a" + field: "d" + > + json_values: "{\"b\": 1}" + json_values: "2" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-special-chars.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-special-chars.textproto new file mode 100644 index 0000000000..51cb33b312 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-special-chars.textproto @@ -0,0 +1,53 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# FieldPaths can contain special characters. + +description: "update-paths: special characters" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "*" + field: "~" + > + field_paths: < + field: "*" + field: "`" + > + json_values: "1" + json_values: "2" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "*" + value: < + map_value: < + fields: < + key: "`" + value: < + integer_value: 2 + > + > + fields: < + key: "~" + value: < + integer_value: 1 + > + > + > + > + > + > + update_mask: < + field_paths: "`*`.`\\``" + field_paths: "`*`.`~`" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-alone.textproto new file mode 100644 index 0000000000..abc44f55b4 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-alone.textproto @@ -0,0 +1,29 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the only values in the input are ServerTimestamps, then no update operation +# should be produced. + +description: "update-paths: ServerTimestamp alone" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "\"ServerTimestamp\"" + request: < + database: "projects/projectID/databases/(default)" + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "a" + set_to_server_value: REQUEST_TIME + > + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-multi.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-multi.textproto new file mode 100644 index 0000000000..b0b7df17d8 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-multi.textproto @@ -0,0 +1,56 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A document can have more than one ServerTimestamp field. Since all the +# ServerTimestamp fields are removed, the only field in the update is "a". + +description: "update-paths: multiple ServerTimestamp fields" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "b" + > + field_paths: < + field: "c" + > + json_values: "1" + json_values: "\"ServerTimestamp\"" + json_values: "{\"d\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "c" + > + current_document: < + exists: true + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + field_transforms: < + field_path: "c.d" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-nested.textproto new file mode 100644 index 0000000000..3077368318 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-nested.textproto @@ -0,0 +1,49 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A ServerTimestamp value can occur at any depth. In this case, the transform +# applies to the field path "b.c". Since "c" is removed from the update, "b" +# becomes empty, so it is also removed from the update. + +description: "update-paths: nested ServerTimestamp field" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "b" + > + json_values: "1" + json_values: "{\"c\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b" + > + current_document: < + exists: true + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b.c" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-noarray-nested.textproto new file mode 100644 index 0000000000..2c2cb89b62 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-noarray-nested.textproto @@ -0,0 +1,15 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# There cannot be an array value anywhere on the path from the document root to +# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. + +description: "update-paths: ServerTimestamp cannot be anywhere inside an array value" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "[1, {\"b\": \"ServerTimestamp\"}]" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-noarray.textproto new file mode 100644 index 0000000000..a2baa66f57 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st-noarray.textproto @@ -0,0 +1,15 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The ServerTimestamp sentinel must be the value of a field. Firestore transforms +# don't support array indexing. + +description: "update-paths: ServerTimestamp cannot be in an array value" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + json_values: "[1, 2, \"ServerTimestamp\"]" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-st.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st.textproto new file mode 100644 index 0000000000..40634c1658 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-st.textproto @@ -0,0 +1,49 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A key with the special ServerTimestamp sentinel is removed from the data in the +# update operation. Instead it appears in a separate Transform operation. Note +# that in these tests, the string "ServerTimestamp" should be replaced with the +# special ServerTimestamp value. + +description: "update-paths: ServerTimestamp with data" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + field_paths: < + field: "a" + > + field_paths: < + field: "b" + > + json_values: "1" + json_values: "\"ServerTimestamp\"" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + current_document: < + exists: true + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-paths-uptime.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-paths-uptime.textproto new file mode 100644 index 0000000000..7a15874bea --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-paths-uptime.textproto @@ -0,0 +1,40 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Update call supports a last-update-time precondition. + +description: "update-paths: last-update-time precondition" +update_paths: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + precondition: < + update_time: < + seconds: 42 + > + > + field_paths: < + field: "a" + > + json_values: "1" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + current_document: < + update_time: < + seconds: 42 + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-prefix-1.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-prefix-1.textproto new file mode 100644 index 0000000000..e5c895e73b --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-prefix-1.textproto @@ -0,0 +1,11 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# In the input data, one field cannot be a prefix of another. + +description: "update: prefix #1" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a.b\": 1, \"a\": 2}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-prefix-2.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-prefix-2.textproto new file mode 100644 index 0000000000..4870176186 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-prefix-2.textproto @@ -0,0 +1,11 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# In the input data, one field cannot be a prefix of another. + +description: "update: prefix #2" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"a.b\": 2}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-prefix-3.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-prefix-3.textproto new file mode 100644 index 0000000000..0c03b0d6b8 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-prefix-3.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# In the input data, one field cannot be a prefix of another, even if the values +# could in principle be combined. + +description: "update: prefix #3" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": {\"b\": 1}, \"a.d\": 2}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-quoting.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-quoting.textproto new file mode 100644 index 0000000000..20e530a760 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-quoting.textproto @@ -0,0 +1,45 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# In a field path, any component beginning with a non-letter or underscore is +# quoted. + +description: "update: non-letter starting chars are quoted, except underscore" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"_0.1.+2\": 1}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "_0" + value: < + map_value: < + fields: < + key: "1" + value: < + map_value: < + fields: < + key: "+2" + value: < + integer_value: 1 + > + > + > + > + > + > + > + > + > + update_mask: < + field_paths: "_0.`1`.`+2`" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-split-top-level.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-split-top-level.textproto new file mode 100644 index 0000000000..d1b0ca0da1 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-split-top-level.textproto @@ -0,0 +1,45 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Update method splits only top-level keys at dots. Keys at other levels are +# taken literally. + +description: "update: Split on dots for top-level keys only" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"h.g\": {\"j.k\": 6}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "h" + value: < + map_value: < + fields: < + key: "g" + value: < + map_value: < + fields: < + key: "j.k" + value: < + integer_value: 6 + > + > + > + > + > + > + > + > + > + update_mask: < + field_paths: "h.g" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-split.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-split.textproto new file mode 100644 index 0000000000..b96fd6a4f7 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-split.textproto @@ -0,0 +1,44 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Update method splits top-level keys at dots. + +description: "update: split on dots" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a.b.c\": 1}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + map_value: < + fields: < + key: "b" + value: < + map_value: < + fields: < + key: "c" + value: < + integer_value: 1 + > + > + > + > + > + > + > + > + > + update_mask: < + field_paths: "a.b.c" + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-st-alone.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-st-alone.textproto new file mode 100644 index 0000000000..0d5ab6e9fb --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-st-alone.textproto @@ -0,0 +1,26 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# If the only values in the input are ServerTimestamps, then no update operation +# should be produced. + +description: "update: ServerTimestamp alone" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "a" + set_to_server_value: REQUEST_TIME + > + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-st-dot.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-st-dot.textproto new file mode 100644 index 0000000000..19d4d18432 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-st-dot.textproto @@ -0,0 +1,27 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# Like other uses of ServerTimestamp, the data is pruned and the field does not +# appear in the update mask, because it is in the transform. In this case An +# update operation is produced just to hold the precondition. + +description: "update: ServerTimestamp with dotted field" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a.b.c\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "a.b.c" + set_to_server_value: REQUEST_TIME + > + > + current_document: < + exists: true + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-st-multi.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-st-multi.textproto new file mode 100644 index 0000000000..0434cb59ab --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-st-multi.textproto @@ -0,0 +1,49 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A document can have more than one ServerTimestamp field. Since all the +# ServerTimestamp fields are removed, the only field in the update is "a". + +# b is not in the mask because it will be set in the transform. c must be in the +# mask: it should be replaced entirely. The transform will set c.d to the +# timestamp, but the update will delete the rest of c. + +description: "update: multiple ServerTimestamp fields" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "c" + > + current_document: < + exists: true + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + field_transforms: < + field_path: "c.d" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-st-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-st-nested.textproto new file mode 100644 index 0000000000..f79d9c6a07 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-st-nested.textproto @@ -0,0 +1,42 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A ServerTimestamp value can occur at any depth. In this case, the transform +# applies to the field path "b.c". Since "c" is removed from the update, "b" +# becomes empty, so it is also removed from the update. + +description: "update: nested ServerTimestamp field" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + field_paths: "b" + > + current_document: < + exists: true + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b.c" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-st-noarray-nested.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-st-noarray-nested.textproto new file mode 100644 index 0000000000..2939dd6464 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-st-noarray-nested.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# There cannot be an array value anywhere on the path from the document root to +# the ServerTimestamp sentinel. Firestore transforms don't support array indexing. + +description: "update: ServerTimestamp cannot be anywhere inside an array value" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-st-noarray.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-st-noarray.textproto new file mode 100644 index 0000000000..f3879cdf22 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-st-noarray.textproto @@ -0,0 +1,12 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The ServerTimestamp sentinel must be the value of a field. Firestore transforms +# don't support array indexing. + +description: "update: ServerTimestamp cannot be in an array value" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}" + is_error: true +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-st.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-st.textproto new file mode 100644 index 0000000000..12045a9220 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-st.textproto @@ -0,0 +1,42 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# A key with the special ServerTimestamp sentinel is removed from the data in the +# update operation. Instead it appears in a separate Transform operation. Note +# that in these tests, the string "ServerTimestamp" should be replaced with the +# special ServerTimestamp value. + +description: "update: ServerTimestamp with data" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + current_document: < + exists: true + > + > + writes: < + transform: < + document: "projects/projectID/databases/(default)/documents/C/d" + field_transforms: < + field_path: "b" + set_to_server_value: REQUEST_TIME + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/testdata/update-uptime.textproto b/vendor/cloud.google.com/go/firestore/testdata/update-uptime.textproto new file mode 100644 index 0000000000..66119ac61c --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/testdata/update-uptime.textproto @@ -0,0 +1,37 @@ +# DO NOT MODIFY. This file was generated by +# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go. + +# The Update call supports a last-update-time precondition. + +description: "update: last-update-time precondition" +update: < + doc_ref_path: "projects/projectID/databases/(default)/documents/C/d" + precondition: < + update_time: < + seconds: 42 + > + > + json_data: "{\"a\": 1}" + request: < + database: "projects/projectID/databases/(default)" + writes: < + update: < + name: "projects/projectID/databases/(default)/documents/C/d" + fields: < + key: "a" + value: < + integer_value: 1 + > + > + > + update_mask: < + field_paths: "a" + > + current_document: < + update_time: < + seconds: 42 + > + > + > + > +> diff --git a/vendor/cloud.google.com/go/firestore/to_value.go b/vendor/cloud.google.com/go/firestore/to_value.go new file mode 100644 index 0000000000..5646531f10 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/to_value.go @@ -0,0 +1,278 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + "fmt" + "reflect" + "time" + + "cloud.google.com/go/internal/fields" + "github.com/golang/protobuf/ptypes" + ts "github.com/golang/protobuf/ptypes/timestamp" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "google.golang.org/genproto/googleapis/type/latlng" +) + +var nullValue = &pb.Value{ValueType: &pb.Value_NullValue{}} + +var ( + typeOfByteSlice = reflect.TypeOf([]byte{}) + typeOfGoTime = reflect.TypeOf(time.Time{}) + typeOfLatLng = reflect.TypeOf((*latlng.LatLng)(nil)) + typeOfDocumentRef = reflect.TypeOf((*DocumentRef)(nil)) + typeOfProtoTimestamp = reflect.TypeOf((*ts.Timestamp)(nil)) +) + +// toProtoValue converts a Go value to a Firestore Value protobuf. +// Some corner cases: +// - All nils (nil interface, nil slice, nil map, nil pointer) are converted to +// a NullValue (not a nil *pb.Value). toProtoValue never returns (nil, false, nil). +// It returns (nil, true, nil) if everything in the value is ServerTimestamp. +// - An error is returned for uintptr, uint and uint64, because Firestore uses +// an int64 to represent integral values, and those types can't be properly +// represented in an int64. +// - An error is returned for the special Delete value. +func toProtoValue(v reflect.Value) (pbv *pb.Value, sawServerTimestamp bool, err error) { + if !v.IsValid() { + return nullValue, false, nil + } + vi := v.Interface() + if vi == Delete { + return nil, false, errors.New("firestore: cannot use Delete in value") + } + if vi == ServerTimestamp { + return nil, false, errors.New("firestore: must use ServerTimestamp as a map value") + } + switch x := vi.(type) { + case []byte: + return &pb.Value{ValueType: &pb.Value_BytesValue{x}}, false, nil + case time.Time: + ts, err := ptypes.TimestampProto(x) + if err != nil { + return nil, false, err + } + return &pb.Value{ValueType: &pb.Value_TimestampValue{ts}}, false, nil + case *ts.Timestamp: + if x == nil { + // gRPC doesn't like nil oneofs. Use NullValue. + return nullValue, false, nil + } + return &pb.Value{ValueType: &pb.Value_TimestampValue{x}}, false, nil + case *latlng.LatLng: + if x == nil { + // gRPC doesn't like nil oneofs. Use NullValue. + return nullValue, false, nil + } + return &pb.Value{ValueType: &pb.Value_GeoPointValue{x}}, false, nil + case *DocumentRef: + if x == nil { + // gRPC doesn't like nil oneofs. Use NullValue. + return nullValue, false, nil + } + return &pb.Value{ValueType: &pb.Value_ReferenceValue{x.Path}}, false, nil + // Do not add bool, string, int, etc. to this switch; leave them in the + // reflect-based switch below. Moving them here would drop support for + // types whose underlying types are those primitives. + // E.g. Given "type mybool bool", an ordinary type switch on bool will + // not catch a mybool, but the reflect.Kind of a mybool is reflect.Bool. + } + switch v.Kind() { + case reflect.Bool: + return &pb.Value{ValueType: &pb.Value_BooleanValue{v.Bool()}}, false, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return &pb.Value{ValueType: &pb.Value_IntegerValue{v.Int()}}, false, nil + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + return &pb.Value{ValueType: &pb.Value_IntegerValue{int64(v.Uint())}}, false, nil + case reflect.Float32, reflect.Float64: + return &pb.Value{ValueType: &pb.Value_DoubleValue{v.Float()}}, false, nil + case reflect.String: + return &pb.Value{ValueType: &pb.Value_StringValue{v.String()}}, false, nil + case reflect.Slice: + return sliceToProtoValue(v) + case reflect.Map: + return mapToProtoValue(v) + case reflect.Struct: + return structToProtoValue(v) + case reflect.Ptr: + if v.IsNil() { + return nullValue, false, nil + } + return toProtoValue(v.Elem()) + case reflect.Interface: + if v.NumMethod() == 0 { // empty interface: recurse on its contents + return toProtoValue(v.Elem()) + } + fallthrough // any other interface value is an error + + default: + return nil, false, fmt.Errorf("firestore: cannot convert type %s to value", v.Type()) + } +} + +func sliceToProtoValue(v reflect.Value) (*pb.Value, bool, error) { + // A nil slice is converted to a null value. + if v.IsNil() { + return nullValue, false, nil + } + vals := make([]*pb.Value, v.Len()) + for i := 0; i < v.Len(); i++ { + val, sawServerTimestamp, err := toProtoValue(v.Index(i)) + if err != nil { + return nil, false, err + } + if sawServerTimestamp { + return nil, false, errors.New("firestore: ServerTimestamp cannot occur in an array") + } + vals[i] = val + } + return &pb.Value{ValueType: &pb.Value_ArrayValue{&pb.ArrayValue{Values: vals}}}, false, nil +} + +func mapToProtoValue(v reflect.Value) (*pb.Value, bool, error) { + if v.Type().Key().Kind() != reflect.String { + return nil, false, errors.New("firestore: map key type must be string") + } + // A nil map is converted to a null value. + if v.IsNil() { + return nullValue, false, nil + } + m := map[string]*pb.Value{} + sawServerTimestamp := false + for _, k := range v.MapKeys() { + mi := v.MapIndex(k) + if mi.Interface() == ServerTimestamp { + sawServerTimestamp = true + continue + } + val, sst, err := toProtoValue(mi) + if err != nil { + return nil, false, err + } + if sst { + sawServerTimestamp = true + } + if val == nil { // value was a map with all ServerTimestamp values + continue + } + m[k.String()] = val + } + var pv *pb.Value + if len(m) == 0 && sawServerTimestamp { + // The entire map consisted of ServerTimestamp values. + pv = nil + } else { + pv = &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: m}}} + } + return pv, sawServerTimestamp, nil +} + +func structToProtoValue(v reflect.Value) (*pb.Value, bool, error) { + m := map[string]*pb.Value{} + fields, err := fieldCache.Fields(v.Type()) + if err != nil { + return nil, false, err + } + sawServerTimestamp := false + for _, f := range fields { + fv := v.FieldByIndex(f.Index) + opts := f.ParsedTag.(tagOptions) + if opts.serverTimestamp { + // TODO(jba): should we return a non-zero time? + sawServerTimestamp = true + continue + } + if opts.omitEmpty && isEmptyValue(fv) { + continue + } + val, sst, err := toProtoValue(fv) + if err != nil { + return nil, false, err + } + if sst { + sawServerTimestamp = true + } + if val == nil { // value was a map with all ServerTimestamp values + continue + } + m[f.Name] = val + } + var pv *pb.Value + if len(m) == 0 && sawServerTimestamp { + // The entire struct consisted of ServerTimestamp or omitempty values. + pv = nil + } else { + pv = &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: m}}} + } + return pv, sawServerTimestamp, nil +} + +type tagOptions struct { + omitEmpty bool // do not marshal value if empty + serverTimestamp bool // set time.Time to server timestamp on write +} + +// parseTag interprets firestore struct field tags. +func parseTag(t reflect.StructTag) (name string, keep bool, other interface{}, err error) { + name, keep, opts, err := fields.ParseStandardTag("firestore", t) + if err != nil { + return "", false, nil, fmt.Errorf("firestore: %v", err) + } + tagOpts := tagOptions{} + for _, opt := range opts { + switch opt { + case "omitempty": + tagOpts.omitEmpty = true + case "serverTimestamp": + tagOpts.serverTimestamp = true + default: + return "", false, nil, fmt.Errorf("firestore: unknown tag option: %q", opt) + } + } + return name, keep, tagOpts, nil +} + +// isLeafType determines whether or not a type is a 'leaf type' +// and should not be recursed into, but considered one field. +func isLeafType(t reflect.Type) bool { + return t == typeOfGoTime || t == typeOfLatLng || t == typeOfProtoTimestamp +} + +var fieldCache = fields.NewCache(parseTag, nil, isLeafType) + +// isEmptyValue is taken from the encoding/json package in the +// standard library. +// TODO(jba): move to the fields package +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + if v.Type() == typeOfGoTime { + return v.Interface().(time.Time).IsZero() + } + return false +} diff --git a/vendor/cloud.google.com/go/firestore/to_value_test.go b/vendor/cloud.google.com/go/firestore/to_value_test.go new file mode 100644 index 0000000000..e6617241b2 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/to_value_test.go @@ -0,0 +1,276 @@ +// Copyright 2017 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 firestore + +import ( + "fmt" + "reflect" + "testing" + "time" + + ts "github.com/golang/protobuf/ptypes/timestamp" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "google.golang.org/genproto/googleapis/type/latlng" +) + +type testStruct1 struct { + B bool + I int + U uint32 + F float64 + S string + Y []byte + T time.Time + Ts *ts.Timestamp + G *latlng.LatLng + L []int + M map[string]int + P *int +} + +var ( + p = new(int) + + testVal1 = testStruct1{ + B: true, + I: 1, + U: 2, + F: 3.0, + S: "four", + Y: []byte{5}, + T: tm, + Ts: ptm, + G: ll, + L: []int{6}, + M: map[string]int{"a": 7}, + P: p, + } + + mapVal1 = mapval(map[string]*pb.Value{ + "B": boolval(true), + "I": intval(1), + "U": intval(2), + "F": floatval(3), + "S": {ValueType: &pb.Value_StringValue{"four"}}, + "Y": bytesval([]byte{5}), + "T": tsval(tm), + "Ts": {ValueType: &pb.Value_TimestampValue{ptm}}, + "G": geoval(ll), + "L": arrayval(intval(6)), + "M": mapval(map[string]*pb.Value{"a": intval(7)}), + "P": intval(8), + }) +) + +func TestToProtoValue(t *testing.T) { + *p = 8 + for _, test := range []struct { + in interface{} + want *pb.Value + }{ + {nil, nullValue}, + {[]int(nil), nullValue}, + {map[string]int(nil), nullValue}, + {(*testStruct1)(nil), nullValue}, + {(*ts.Timestamp)(nil), nullValue}, + {(*latlng.LatLng)(nil), nullValue}, + {(*DocumentRef)(nil), nullValue}, + {true, boolval(true)}, + {3, intval(3)}, + {uint32(3), intval(3)}, + {1.5, floatval(1.5)}, + {"str", strval("str")}, + {[]byte{1, 2}, bytesval([]byte{1, 2})}, + {tm, tsval(tm)}, + {ptm, &pb.Value{ValueType: &pb.Value_TimestampValue{ptm}}}, + {ll, geoval(ll)}, + {[]int{1, 2}, arrayval(intval(1), intval(2))}, + {&[]int{1, 2}, arrayval(intval(1), intval(2))}, + {[]int{}, arrayval()}, + {map[string]int{"a": 1, "b": 2}, + mapval(map[string]*pb.Value{"a": intval(1), "b": intval(2)})}, + {map[string]int{}, mapval(map[string]*pb.Value{})}, + {p, intval(8)}, + {&p, intval(8)}, + {map[string]interface{}{"a": 1, "p": p, "s": "str"}, + mapval(map[string]*pb.Value{"a": intval(1), "p": intval(8), "s": strval("str")})}, + {map[string]fmt.Stringer{"a": tm}, + mapval(map[string]*pb.Value{"a": tsval(tm)})}, + {testVal1, mapVal1}, + { + &DocumentRef{ + ID: "d", + Path: "projects/P/databases/D/documents/c/d", + Parent: &CollectionRef{ + ID: "c", + parentPath: "projects/P/databases/D", + Path: "projects/P/databases/D/documents/c", + Query: Query{collectionID: "c", parentPath: "projects/P/databases/D"}, + }, + }, + refval("projects/P/databases/D/documents/c/d"), + }, + // ServerTimestamps are removed, possibly leaving nil. + {map[string]interface{}{"a": ServerTimestamp}, nil}, + { + map[string]interface{}{ + "a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": ServerTimestamp, + }, + }, + }, + nil, + }, + { + map[string]interface{}{ + "a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": ServerTimestamp, + "d": ServerTimestamp, + }, + }, + }, + nil, + }, + { + map[string]interface{}{ + "a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": ServerTimestamp, + "d": ServerTimestamp, + "e": 1, + }, + }, + }, + mapval(map[string]*pb.Value{ + "a": mapval(map[string]*pb.Value{ + "b": mapval(map[string]*pb.Value{"e": intval(1)}), + }), + }), + }, + } { + got, _, err := toProtoValue(reflect.ValueOf(test.in)) + if err != nil { + t.Errorf("%v (%T): %v", test.in, test.in, err) + continue + } + if !testEqual(got, test.want) { + t.Errorf("%+v (%T):\ngot\n%+v\nwant\n%+v", test.in, test.in, got, test.want) + } + } +} + +type stringy struct{} + +func (stringy) String() string { return "stringy" } + +func TestToProtoValueErrors(t *testing.T) { + for _, in := range []interface{}{ + uint64(0), // a bad fit for int64 + map[int]bool{}, // map key type is not string + make(chan int), // can't handle type + map[string]fmt.Stringer{"a": stringy{}}, // only empty interfaces + ServerTimestamp, // ServerTimestamp can only be a field value + []interface{}{ServerTimestamp}, + map[string]interface{}{"a": []interface{}{ServerTimestamp}}, + map[string]interface{}{"a": []interface{}{ + map[string]interface{}{"b": ServerTimestamp}, + }}, + Delete, // Delete should never appear + []interface{}{Delete}, + map[string]interface{}{"a": Delete}, + map[string]interface{}{"a": []interface{}{Delete}}, + } { + _, _, err := toProtoValue(reflect.ValueOf(in)) + if err == nil { + t.Errorf("%v: got nil, want error", in) + } + } +} + +type testStruct2 struct { + Ignore int `firestore:"-"` + Rename int `firestore:"a"` + OmitEmpty int `firestore:",omitempty"` + OmitEmptyTime time.Time `firestore:",omitempty"` +} + +func TestToProtoValueTags(t *testing.T) { + in := &testStruct2{ + Ignore: 1, + Rename: 2, + OmitEmpty: 3, + OmitEmptyTime: aTime, + } + got, _, err := toProtoValue(reflect.ValueOf(in)) + if err != nil { + t.Fatal(err) + } + want := mapval(map[string]*pb.Value{ + "a": intval(2), + "OmitEmpty": intval(3), + "OmitEmptyTime": tsval(aTime), + }) + if !testEqual(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + got, _, err = toProtoValue(reflect.ValueOf(testStruct2{})) + if err != nil { + t.Fatal(err) + } + want = mapval(map[string]*pb.Value{"a": intval(0)}) + if !testEqual(got, want) { + t.Errorf("got\n%+v\nwant\n%+v", got, want) + } +} + +func TestToProtoValueEmbedded(t *testing.T) { + // Embedded time.Time, LatLng, or Timestamp should behave like non-embedded. + type embed struct { + time.Time + *latlng.LatLng + *ts.Timestamp + } + + got, _, err := toProtoValue(reflect.ValueOf(embed{tm, ll, ptm})) + if err != nil { + t.Fatal(err) + } + want := mapval(map[string]*pb.Value{ + "Time": tsval(tm), + "LatLng": geoval(ll), + "Timestamp": {ValueType: &pb.Value_TimestampValue{ptm}}, + }) + if !testEqual(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +func TestIsEmpty(t *testing.T) { + for _, e := range []interface{}{int(0), float32(0), false, "", []int{}, []int(nil), (*int)(nil)} { + if !isEmptyValue(reflect.ValueOf(e)) { + t.Errorf("%v (%T): want true, got false", e, e) + } + } + i := 3 + for _, n := range []interface{}{int(1), float32(1), true, "x", []int{1}, &i} { + if isEmptyValue(reflect.ValueOf(n)) { + t.Errorf("%v (%T): want false, got true", n, n) + } + } +} diff --git a/vendor/cloud.google.com/go/firestore/transaction.go b/vendor/cloud.google.com/go/firestore/transaction.go new file mode 100644 index 0000000000..8f2e5eb246 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/transaction.go @@ -0,0 +1,276 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Transaction represents a Firestore transaction. +type Transaction struct { + c *Client + ctx context.Context + id []byte + writes []*pb.Write + maxAttempts int + readOnly bool + readAfterWrite bool +} + +// A TransactionOption is an option passed to Client.Transaction. +type TransactionOption interface { + config(t *Transaction) +} + +// MaxAttempts is a TransactionOption that configures the maximum number of times to +// try a transaction. In defaults to DefaultTransactionMaxAttempts. +func MaxAttempts(n int) maxAttempts { return maxAttempts(n) } + +type maxAttempts int + +func (m maxAttempts) config(t *Transaction) { t.maxAttempts = int(m) } + +// DefaultTransactionMaxAttempts is the default number of times to attempt a transaction. +const DefaultTransactionMaxAttempts = 5 + +// ReadOnly is a TransactionOption that makes the transaction read-only. Read-only +// transactions cannot issue write operations, but are more efficient. +var ReadOnly = ro{} + +type ro struct{} + +func (ro) config(t *Transaction) { t.readOnly = true } + +var ( + // ErrConcurrentTransaction is returned when a transaction is rolled back due + // to a conflict with a concurrent transaction. + ErrConcurrentTransaction = errors.New("firestore: concurrent transaction") + + // Defined here for testing. + errReadAfterWrite = errors.New("firestore: read after write in transaction") + errWriteReadOnly = errors.New("firestore: write in read-only transaction") + errNonTransactionalOp = errors.New("firestore: non-transactional operation inside a transaction") + errNestedTransaction = errors.New("firestore: nested transaction") +) + +type transactionInProgressKey struct{} + +func checkTransaction(ctx context.Context) error { + if ctx.Value(transactionInProgressKey{}) != nil { + return errNonTransactionalOp + } + return nil +} + +// RunTransaction runs f in a transaction. f should use the transaction it is given +// for all Firestore operations. For any operation requiring a context, f should use +// the context it is passed, not the first argument to RunTransaction. +// +// f must not call Commit or Rollback on the provided Transaction. +// +// If f returns nil, RunTransaction commits the transaction. If the commit fails due +// to a conflicting transaction, RunTransaction retries f. It gives up and returns +// ErrConcurrentTransaction after a number of attempts that can be configured with +// the MaxAttempts option. If the commit succeeds, RunTransaction returns a nil error. +// +// If f returns non-nil, then the transaction will be rolled back and +// this method will return the same error. The function f is not retried. +// +// Note that when f returns, the transaction is not committed. Calling code +// must not assume that any of f's changes have been committed until +// RunTransaction returns nil. +// +// Since f may be called more than once, f should usually be idempotent – that is, it +// should have the same result when called multiple times. +func (c *Client) RunTransaction(ctx context.Context, f func(context.Context, *Transaction) error, opts ...TransactionOption) error { + if ctx.Value(transactionInProgressKey{}) != nil { + return errNestedTransaction + } + db := c.path() + t := &Transaction{ + c: c, + ctx: withResourceHeader(ctx, db), + maxAttempts: DefaultTransactionMaxAttempts, + } + for _, opt := range opts { + opt.config(t) + } + var txOpts *pb.TransactionOptions + if t.readOnly { + txOpts = &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadOnly_{&pb.TransactionOptions_ReadOnly{}}, + } + } + var backoff gax.Backoff + // TODO(jba): use other than the standard backoff parameters? + // TODO(jba): get backoff time from gRPC trailer metadata? See extractRetryDelay in https://code.googlesource.com/gocloud/+/master/spanner/retry.go. + var err error + for i := 0; i < t.maxAttempts; i++ { + var res *pb.BeginTransactionResponse + res, err = t.c.c.BeginTransaction(t.ctx, &pb.BeginTransactionRequest{ + Database: db, + Options: txOpts, + }) + if err != nil { + return err + } + t.id = res.Transaction + err = f(context.WithValue(ctx, transactionInProgressKey{}, 1), t) + // Read after write can only be checked client-side, so we make sure to check + // even if the user does not. + if err == nil && t.readAfterWrite { + err = errReadAfterWrite + } + if err != nil { + t.rollback() + // Prefer f's returned error to rollback error. + return err + } + _, err = t.c.c.Commit(t.ctx, &pb.CommitRequest{ + Database: t.c.path(), + Writes: t.writes, + Transaction: t.id, + }) + // If a read-write transaction returns Aborted, retry. + // On success or other failures, return here. + if t.readOnly || grpc.Code(err) != codes.Aborted { + // According to the Firestore team, we should not roll back here + // if err != nil. But spanner does. + // See https://code.googlesource.com/gocloud/+/master/spanner/transaction.go#740. + return err + } + + if txOpts == nil { + // txOpts can only be nil if is the first retry of a read-write transaction. + // (It is only set here and in the body of "if t.readOnly" above.) + // Mention the transaction ID in BeginTransaction so the service + // knows it is a retry. + txOpts = &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadWrite_{ + &pb.TransactionOptions_ReadWrite{RetryTransaction: t.id}, + }, + } + } + // Use exponential backoff to avoid contention with other running + // transactions. + if cerr := sleep(ctx, backoff.Pause()); cerr != nil { + err = cerr + break + } + } + // If we run out of retries, return the last error we saw (which should + // be the Aborted from Commit, or a context error). + if err != nil { + t.rollback() + } + return err +} + +func (t *Transaction) rollback() { + _ = t.c.c.Rollback(t.ctx, &pb.RollbackRequest{ + Database: t.c.path(), + Transaction: t.id, + }) + // Ignore the rollback error. + // TODO(jba): Log it? + // Note: Rollback is idempotent so it will be retried by the gapic layer. +} + +// Get gets the document in the context of the transaction. The transaction holds a +// pessimistic lock on the returned document. +func (t *Transaction) Get(dr *DocumentRef) (*DocumentSnapshot, error) { + docsnaps, err := t.GetAll([]*DocumentRef{dr}) + if err != nil { + return nil, err + } + ds := docsnaps[0] + if !ds.Exists() { + return ds, status.Errorf(codes.NotFound, "%q not found", dr.Path) + } + return ds, nil +} + +// GetAll retrieves multiple documents with a single call. The DocumentSnapshots are +// returned in the order of the given DocumentRefs. If a document is not present, the +// corresponding DocumentSnapshot's Exists method will return false. The transaction +// holds a pessimistic lock on all of the returned documents. +func (t *Transaction) GetAll(drs []*DocumentRef) ([]*DocumentSnapshot, error) { + if len(t.writes) > 0 { + t.readAfterWrite = true + return nil, errReadAfterWrite + } + return t.c.getAll(t.ctx, drs, t.id) +} + +// A Queryer is a Query or a CollectionRef. CollectionRefs act as queries whose +// results are all the documents in the collection. +type Queryer interface { + query() *Query +} + +// Documents returns a DocumentIterator based on given Query or CollectionRef. The +// results will be in the context of the transaction. +func (t *Transaction) Documents(q Queryer) *DocumentIterator { + if len(t.writes) > 0 { + t.readAfterWrite = true + return &DocumentIterator{err: errReadAfterWrite} + } + return &DocumentIterator{ + iter: newQueryDocumentIterator(t.ctx, q.query(), t.id), + } +} + +// Create adds a Create operation to the Transaction. +// See DocumentRef.Create for details. +func (t *Transaction) Create(dr *DocumentRef, data interface{}) error { + return t.addWrites(dr.newCreateWrites(data)) +} + +// Set adds a Set operation to the Transaction. +// See DocumentRef.Set for details. +func (t *Transaction) Set(dr *DocumentRef, data interface{}, opts ...SetOption) error { + return t.addWrites(dr.newSetWrites(data, opts)) +} + +// Delete adds a Delete operation to the Transaction. +// See DocumentRef.Delete for details. +func (t *Transaction) Delete(dr *DocumentRef, opts ...Precondition) error { + return t.addWrites(dr.newDeleteWrites(opts)) +} + +// Update adds a new Update operation to the Transaction. +// See DocumentRef.Update for details. +func (t *Transaction) Update(dr *DocumentRef, data []Update, opts ...Precondition) error { + return t.addWrites(dr.newUpdatePathWrites(data, opts)) +} + +func (t *Transaction) addWrites(ws []*pb.Write, err error) error { + if t.readOnly { + return errWriteReadOnly + } + if err != nil { + return err + } + t.writes = append(t.writes, ws...) + return nil +} diff --git a/vendor/cloud.google.com/go/firestore/transaction_test.go b/vendor/cloud.google.com/go/firestore/transaction_test.go new file mode 100644 index 0000000000..e3dc26b4ce --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/transaction_test.go @@ -0,0 +1,396 @@ +// Copyright 2017 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 firestore + +import ( + "testing" + + "golang.org/x/net/context" + "google.golang.org/grpc/status" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/api/iterator" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +func TestRunTransaction(t *testing.T) { + ctx := context.Background() + const db = "projects/projectID/databases/(default)" + tid := []byte{1} + c, srv := newMock(t) + beginReq := &pb.BeginTransactionRequest{Database: db} + beginRes := &pb.BeginTransactionResponse{Transaction: tid} + commitReq := &pb.CommitRequest{Database: db, Transaction: tid} + // Empty transaction. + srv.addRPC(beginReq, beginRes) + srv.addRPC(commitReq, &pb.CommitResponse{CommitTime: aTimestamp}) + err := c.RunTransaction(ctx, func(context.Context, *Transaction) error { return nil }) + if err != nil { + t.Fatal(err) + } + + // Transaction with read and write. + srv.reset() + srv.addRPC(beginReq, beginRes) + aDoc := &pb.Document{ + Name: db + "/documents/C/a", + CreateTime: aTimestamp, + UpdateTime: aTimestamp2, + Fields: map[string]*pb.Value{"count": intval(1)}, + } + srv.addRPC( + &pb.BatchGetDocumentsRequest{ + Database: c.path(), + Documents: []string{db + "/documents/C/a"}, + ConsistencySelector: &pb.BatchGetDocumentsRequest_Transaction{tid}, + }, []interface{}{ + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Found{aDoc}, + ReadTime: aTimestamp2, + }, + }) + aDoc2 := &pb.Document{ + Name: aDoc.Name, + Fields: map[string]*pb.Value{"count": intval(2)}, + } + srv.addRPC( + &pb.CommitRequest{ + Database: db, + Transaction: tid, + Writes: []*pb.Write{{ + Operation: &pb.Write_Update{aDoc2}, + UpdateMask: &pb.DocumentMask{FieldPaths: []string{"count"}}, + CurrentDocument: &pb.Precondition{ + ConditionType: &pb.Precondition_Exists{true}, + }, + }}, + }, + &pb.CommitResponse{CommitTime: aTimestamp3}, + ) + err = c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { + docref := c.Collection("C").Doc("a") + doc, err := tx.Get(docref) + if err != nil { + return err + } + count, err := doc.DataAt("count") + if err != nil { + return err + } + return tx.Update(docref, []Update{{Path: "count", Value: count.(int64) + 1}}) + }) + if err != nil { + t.Fatal(err) + } + + // Query + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC( + &pb.RunQueryRequest{ + Parent: db, + QueryType: &pb.RunQueryRequest_StructuredQuery{ + &pb.StructuredQuery{ + From: []*pb.StructuredQuery_CollectionSelector{{CollectionId: "C"}}, + }, + }, + ConsistencySelector: &pb.RunQueryRequest_Transaction{tid}, + }, + []interface{}{}, + ) + srv.addRPC(commitReq, &pb.CommitResponse{CommitTime: aTimestamp3}) + err = c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { + it := tx.Documents(c.Collection("C")) + defer it.Stop() + _, err := it.Next() + if err != iterator.Done { + return err + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + // Retry entire transaction. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(commitReq, status.Errorf(codes.Aborted, "")) + srv.addRPC( + &pb.BeginTransactionRequest{ + Database: db, + Options: &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadWrite_{ + &pb.TransactionOptions_ReadWrite{RetryTransaction: tid}, + }, + }, + }, + beginRes, + ) + srv.addRPC(commitReq, &pb.CommitResponse{CommitTime: aTimestamp}) + err = c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { return nil }) + if err != nil { + t.Fatal(err) + } +} + +func TestTransactionErrors(t *testing.T) { + ctx := context.Background() + const db = "projects/projectID/databases/(default)" + c, srv := newMock(t) + var ( + tid = []byte{1} + internalErr = status.Errorf(codes.Internal, "so sad") + beginReq = &pb.BeginTransactionRequest{ + Database: db, + } + beginRes = &pb.BeginTransactionResponse{Transaction: tid} + getReq = &pb.BatchGetDocumentsRequest{ + Database: c.path(), + Documents: []string{db + "/documents/C/a"}, + ConsistencySelector: &pb.BatchGetDocumentsRequest_Transaction{tid}, + } + rollbackReq = &pb.RollbackRequest{Database: db, Transaction: tid} + commitReq = &pb.CommitRequest{Database: db, Transaction: tid} + ) + + // BeginTransaction has a permanent error. + srv.addRPC(beginReq, internalErr) + err := c.RunTransaction(ctx, func(context.Context, *Transaction) error { return nil }) + if grpc.Code(err) != codes.Internal { + t.Errorf("got <%v>, want Internal", err) + } + + // Get has a permanent error. + get := func(_ context.Context, tx *Transaction) error { + _, err := tx.Get(c.Doc("C/a")) + return err + } + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(getReq, internalErr) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, get) + if grpc.Code(err) != codes.Internal { + t.Errorf("got <%v>, want Internal", err) + } + + // Get has a permanent error, but the rollback fails. We still + // return Get's error. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(getReq, internalErr) + srv.addRPC(rollbackReq, status.Errorf(codes.FailedPrecondition, "")) + err = c.RunTransaction(ctx, get) + if grpc.Code(err) != codes.Internal { + t.Errorf("got <%v>, want Internal", err) + } + + // Commit has a permanent error. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(getReq, []interface{}{ + &pb.BatchGetDocumentsResponse{ + Result: &pb.BatchGetDocumentsResponse_Found{&pb.Document{ + Name: "projects/projectID/databases/(default)/documents/C/a", + CreateTime: aTimestamp, + UpdateTime: aTimestamp2, + }}, + ReadTime: aTimestamp2, + }, + }) + srv.addRPC(commitReq, internalErr) + err = c.RunTransaction(ctx, get) + if grpc.Code(err) != codes.Internal { + t.Errorf("got <%v>, want Internal", err) + } + + // Read after write. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { + if err := tx.Delete(c.Doc("C/a")); err != nil { + return err + } + if _, err := tx.Get(c.Doc("C/a")); err != nil { + return err + } + return nil + }) + if err != errReadAfterWrite { + t.Errorf("got <%v>, want <%v>", err, errReadAfterWrite) + } + + // Read after write, with query. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { + if err := tx.Delete(c.Doc("C/a")); err != nil { + return err + } + it := tx.Documents(c.Collection("C").Select("x")) + defer it.Stop() + if _, err := it.Next(); err != iterator.Done { + return err + } + return nil + }) + if err != errReadAfterWrite { + t.Errorf("got <%v>, want <%v>", err, errReadAfterWrite) + } + + // Read after write fails even if the user ignores the read's error. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { + if err := tx.Delete(c.Doc("C/a")); err != nil { + return err + } + if _, err := tx.Get(c.Doc("C/a")); err != nil { + return err + } + return nil + }) + if err != errReadAfterWrite { + t.Errorf("got <%v>, want <%v>", err, errReadAfterWrite) + } + + // Write in read-only transaction. + srv.reset() + srv.addRPC( + &pb.BeginTransactionRequest{ + Database: db, + Options: &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadOnly_{&pb.TransactionOptions_ReadOnly{}}, + }, + }, + beginRes, + ) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error { + return tx.Delete(c.Doc("C/a")) + }, ReadOnly) + if err != errWriteReadOnly { + t.Errorf("got <%v>, want <%v>", err, errWriteReadOnly) + } + + // Too many retries. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(commitReq, status.Errorf(codes.Aborted, "")) + srv.addRPC( + &pb.BeginTransactionRequest{ + Database: db, + Options: &pb.TransactionOptions{ + Mode: &pb.TransactionOptions_ReadWrite_{ + &pb.TransactionOptions_ReadWrite{RetryTransaction: tid}, + }, + }, + }, + beginRes, + ) + srv.addRPC(commitReq, status.Errorf(codes.Aborted, "")) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, func(context.Context, *Transaction) error { return nil }, + MaxAttempts(2)) + if grpc.Code(err) != codes.Aborted { + t.Errorf("got <%v>, want Aborted", err) + } + + // Nested transaction. + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, func(ctx context.Context, tx *Transaction) error { + return c.RunTransaction(ctx, func(context.Context, *Transaction) error { return nil }) + }) + if got, want := err, errNestedTransaction; got != want { + t.Errorf("got <%v>, want <%v>", got, want) + } + + // Non-transactional operation. + dr := c.Doc("C/d") + + for i, op := range []func(ctx context.Context) error{ + func(ctx context.Context) error { _, err := c.GetAll(ctx, []*DocumentRef{dr}); return err }, + func(ctx context.Context) error { _, _, err := c.Collection("C").Add(ctx, testData); return err }, + func(ctx context.Context) error { _, err := dr.Get(ctx); return err }, + func(ctx context.Context) error { _, err := dr.Create(ctx, testData); return err }, + func(ctx context.Context) error { _, err := dr.Set(ctx, testData); return err }, + func(ctx context.Context) error { _, err := dr.Delete(ctx); return err }, + func(ctx context.Context) error { + _, err := dr.Update(ctx, []Update{{FieldPath: []string{"*"}, Value: 1}}) + return err + }, + func(ctx context.Context) error { it := c.Collections(ctx); _, err := it.Next(); return err }, + func(ctx context.Context) error { it := dr.Collections(ctx); _, err := it.Next(); return err }, + func(ctx context.Context) error { + _, err := c.Batch().Set(dr, testData).Commit(ctx) + return err + }, + func(ctx context.Context) error { + it := c.Collection("C").Documents(ctx) + defer it.Stop() + _, err := it.Next() + return err + }, + } { + srv.reset() + srv.addRPC(beginReq, beginRes) + srv.addRPC(rollbackReq, &empty.Empty{}) + err = c.RunTransaction(ctx, func(ctx context.Context, _ *Transaction) error { + return op(ctx) + }) + if got, want := err, errNonTransactionalOp; got != want { + t.Errorf("#%d: got <%v>, want <%v>", i, got, want) + } + } +} + +func TestTransactionGetAll(t *testing.T) { + c, srv := newMock(t) + defer c.Close() + const dbPath = "projects/projectID/databases/(default)" + tid := []byte{1} + beginReq := &pb.BeginTransactionRequest{Database: dbPath} + beginRes := &pb.BeginTransactionResponse{Transaction: tid} + srv.addRPC(beginReq, beginRes) + req := &pb.BatchGetDocumentsRequest{ + Database: dbPath, + Documents: []string{ + dbPath + "/documents/C/a", + dbPath + "/documents/C/b", + dbPath + "/documents/C/c", + }, + ConsistencySelector: &pb.BatchGetDocumentsRequest_Transaction{tid}, + } + err := c.RunTransaction(context.Background(), func(_ context.Context, tx *Transaction) error { + testGetAll(t, c, srv, dbPath, + func(drs []*DocumentRef) ([]*DocumentSnapshot, error) { return tx.GetAll(drs) }, + req) + commitReq := &pb.CommitRequest{Database: dbPath, Transaction: tid} + srv.addRPC(commitReq, &pb.CommitResponse{CommitTime: aTimestamp}) + return nil + }) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/cloud.google.com/go/firestore/util_test.go b/vendor/cloud.google.com/go/firestore/util_test.go new file mode 100644 index 0000000000..812708efe7 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/util_test.go @@ -0,0 +1,150 @@ +// Copyright 2017 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 firestore + +import ( + "fmt" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + + "golang.org/x/net/context" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "github.com/golang/protobuf/ptypes" + tspb "github.com/golang/protobuf/ptypes/timestamp" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "google.golang.org/api/option" + "google.golang.org/genproto/googleapis/type/latlng" + "google.golang.org/grpc" +) + +var ( + aTime = time.Date(2017, 1, 26, 0, 0, 0, 0, time.UTC) + aTime2 = time.Date(2017, 2, 5, 0, 0, 0, 0, time.UTC) + aTime3 = time.Date(2017, 3, 20, 0, 0, 0, 0, time.UTC) + aTimestamp = mustTimestampProto(aTime) + aTimestamp2 = mustTimestampProto(aTime2) + aTimestamp3 = mustTimestampProto(aTime3) +) + +func mustTimestampProto(t time.Time) *tspb.Timestamp { + ts, err := ptypes.TimestampProto(t) + if err != nil { + panic(err) + } + return ts +} + +var cmpOpts = []cmp.Option{ + cmp.AllowUnexported(DocumentRef{}, CollectionRef{}, DocumentSnapshot{}, + Query{}, filter{}, order{}, fpv{}), + cmpopts.IgnoreTypes(Client{}, &Client{}), +} + +// testEqual implements equality for Firestore tests. +func testEqual(a, b interface{}) bool { + return testutil.Equal(a, b, cmpOpts...) +} + +func testDiff(a, b interface{}) string { + return testutil.Diff(a, b, cmpOpts...) +} + +func TestTestEqual(t *testing.T) { + for _, test := range []struct { + a, b interface{} + want bool + }{ + {nil, nil, true}, + {([]int)(nil), nil, false}, + {nil, ([]int)(nil), false}, + {([]int)(nil), ([]int)(nil), true}, + } { + if got := testEqual(test.a, test.b); got != test.want { + t.Errorf("testEqual(%#v, %#v) == %t, want %t", test.a, test.b, got, test.want) + } + } +} + +func newMock(t *testing.T) (*Client, *mockServer) { + srv, err := newMockServer() + if err != nil { + t.Fatal(err) + } + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + t.Fatal(err) + } + client, err := NewClient(context.Background(), "projectID", option.WithGRPCConn(conn)) + if err != nil { + t.Fatal(err) + } + return client, srv +} + +func intval(i int) *pb.Value { + return int64val(int64(i)) +} + +func int64val(i int64) *pb.Value { + return &pb.Value{ValueType: &pb.Value_IntegerValue{i}} +} + +func boolval(b bool) *pb.Value { + return &pb.Value{ValueType: &pb.Value_BooleanValue{b}} +} + +func floatval(f float64) *pb.Value { + return &pb.Value{ValueType: &pb.Value_DoubleValue{f}} +} + +func strval(s string) *pb.Value { + return &pb.Value{ValueType: &pb.Value_StringValue{s}} +} + +func bytesval(b []byte) *pb.Value { + return &pb.Value{ValueType: &pb.Value_BytesValue{b}} +} + +func tsval(t time.Time) *pb.Value { + ts, err := ptypes.TimestampProto(t) + if err != nil { + panic(fmt.Sprintf("bad time %s in test: %v", t, err)) + } + return &pb.Value{ValueType: &pb.Value_TimestampValue{ts}} +} + +func geoval(ll *latlng.LatLng) *pb.Value { + return &pb.Value{ValueType: &pb.Value_GeoPointValue{ll}} +} + +func arrayval(s ...*pb.Value) *pb.Value { + if s == nil { + s = []*pb.Value{} + } + return &pb.Value{ValueType: &pb.Value_ArrayValue{&pb.ArrayValue{Values: s}}} +} + +func mapval(m map[string]*pb.Value) *pb.Value { + return &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: m}}} +} + +func refval(path string) *pb.Value { + return &pb.Value{ValueType: &pb.Value_ReferenceValue{path}} +} diff --git a/vendor/cloud.google.com/go/firestore/watch.go b/vendor/cloud.google.com/go/firestore/watch.go new file mode 100644 index 0000000000..4f66adacc0 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/watch.go @@ -0,0 +1,516 @@ +// 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 firestore + +import ( + "errors" + "fmt" + "io" + "log" + "sort" + "time" + + "cloud.google.com/go/internal/btree" + "github.com/golang/protobuf/ptypes" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// LogWatchStreams controls whether watch stream status changes are logged. +// This feature is EXPERIMENTAL and may disappear at any time. +var LogWatchStreams bool = false + +// DocumentChangeKind describes the kind of change to a document between +// query snapshots. +type DocumentChangeKind int + +const ( + DocumentAdded DocumentChangeKind = iota + DocumentRemoved + DocumentModified +) + +// A DocumentChange describes the change to a document from one query snapshot to the next. +type DocumentChange struct { + Kind DocumentChangeKind + Doc *DocumentSnapshot + // The zero-based index of the document in the sequence of query results prior to this change, + // or -1 if the document was not present. + OldIndex int + // The zero-based index of the document in the sequence of query results after this change, + // or -1 if the document is no longer present. + NewIndex int +} + +// Implementation of realtime updates (a.k.a. watch). +// This code is closely based on the Node.js implementation, +// https://github.com/googleapis/nodejs-firestore/blob/master/src/watch.js. + +// The sole target ID for all streams from this client. +// Variable for testing. +var watchTargetID int32 = 'g' + 'o' + +var defaultBackoff = gax.Backoff{ + // Values from https://github.com/googleapis/nodejs-firestore/blob/master/src/backoff.js. + Initial: 1 * time.Second, + Max: 60 * time.Second, + Multiplier: 1.5, +} + +// not goroutine-safe +type watchStream struct { + ctx context.Context + c *Client + lc pb.Firestore_ListenClient // the gRPC stream + target *pb.Target // document or query being watched + backoff gax.Backoff // for stream retries + err error // sticky permanent error + readTime time.Time // time of most recent snapshot + current bool // saw CURRENT, but not RESET; precondition for a snapshot + hasReturned bool // have we returned a snapshot yet? + compare func(a, b *DocumentSnapshot) (int, error) // compare documents according to query + + // An ordered tree where DocumentSnapshots are the keys. + docTree *btree.BTree + // Map of document name to DocumentSnapshot for the last returned snapshot. + docMap map[string]*DocumentSnapshot + // Map of document name to DocumentSnapshot for accumulated changes for the current snapshot. + // A nil value means the document was removed. + changeMap map[string]*DocumentSnapshot +} + +func newWatchStreamForDocument(ctx context.Context, dr *DocumentRef) *watchStream { + // A single document is always equal to itself. + compare := func(_, _ *DocumentSnapshot) (int, error) { return 0, nil } + return newWatchStream(ctx, dr.Parent.c, compare, &pb.Target{ + TargetType: &pb.Target_Documents{ + Documents: &pb.Target_DocumentsTarget{Documents: []string{dr.Path}}, + }, + TargetId: watchTargetID, + }) +} + +func newWatchStreamForQuery(ctx context.Context, q Query) (*watchStream, error) { + qp, err := q.toProto() + if err != nil { + return nil, err + } + target := &pb.Target{ + TargetType: &pb.Target_Query{ + Query: &pb.Target_QueryTarget{ + Parent: q.parentPath, + QueryType: &pb.Target_QueryTarget_StructuredQuery{qp}, + }, + }, + TargetId: watchTargetID, + } + return newWatchStream(ctx, q.c, q.compareFunc(), target), nil +} + +const btreeDegree = 4 + +func newWatchStream(ctx context.Context, c *Client, compare func(_, _ *DocumentSnapshot) (int, error), target *pb.Target) *watchStream { + w := &watchStream{ + ctx: ctx, + c: c, + compare: compare, + target: target, + backoff: defaultBackoff, + docMap: map[string]*DocumentSnapshot{}, + changeMap: map[string]*DocumentSnapshot{}, + } + w.docTree = btree.New(btreeDegree, func(a, b interface{}) bool { + return w.less(a.(*DocumentSnapshot), b.(*DocumentSnapshot)) + }) + return w +} + +func (s *watchStream) less(a, b *DocumentSnapshot) bool { + c, err := s.compare(a, b) + if err != nil { + s.err = err + return false + } + return c < 0 +} + +// Once nextSnapshot returns an error, it will always return the same error. +func (s *watchStream) nextSnapshot() (*btree.BTree, []DocumentChange, time.Time, error) { + if s.err != nil { + return nil, nil, time.Time{}, s.err + } + var changes []DocumentChange + for { + // Process messages until we are in a consistent state. + for !s.handleNextMessage() { + } + if s.err != nil { + _ = s.close() // ignore error + return nil, nil, time.Time{}, s.err + } + var newDocTree *btree.BTree + newDocTree, changes = s.computeSnapshot(s.docTree, s.docMap, s.changeMap, s.readTime) + if s.err != nil { + return nil, nil, time.Time{}, s.err + } + // Only return a snapshot if something has changed, or this is the first snapshot. + if !s.hasReturned || newDocTree != s.docTree { + s.docTree = newDocTree + break + } + } + s.changeMap = map[string]*DocumentSnapshot{} + s.hasReturned = true + return s.docTree, changes, s.readTime, nil +} + +// Read a message from the stream and handle it. Return true when +// we're in a consistent state, or there is a permanent error. +func (s *watchStream) handleNextMessage() bool { + res, err := s.recv() + if err != nil { + s.err = err + // Errors returned by recv are permanent. + return true + } + switch r := res.ResponseType.(type) { + case *pb.ListenResponse_TargetChange: + return s.handleTargetChange(r.TargetChange) + + case *pb.ListenResponse_DocumentChange: + name := r.DocumentChange.Document.Name + s.logf("DocumentChange %q", name) + if hasWatchTargetID(r.DocumentChange.TargetIds) { // document changed + ref, err := pathToDoc(name, s.c) + if err == nil { + s.changeMap[name], err = newDocumentSnapshot(ref, r.DocumentChange.Document, s.c, nil) + } + if err != nil { + s.err = err + return true + } + } else if hasWatchTargetID(r.DocumentChange.RemovedTargetIds) { // document removed + s.changeMap[name] = nil + } + + case *pb.ListenResponse_DocumentDelete: + s.logf("Delete %q", r.DocumentDelete.Document) + s.changeMap[r.DocumentDelete.Document] = nil + + case *pb.ListenResponse_DocumentRemove: + s.logf("Remove %q", r.DocumentRemove.Document) + s.changeMap[r.DocumentRemove.Document] = nil + + case *pb.ListenResponse_Filter: + s.logf("Filter %d", r.Filter.Count) + if int(r.Filter.Count) != s.currentSize() { + s.resetDocs() // Remove all the current results. + // The filter didn't match; close the stream so it will be re-opened on the next + // call to nextSnapshot. + _ = s.close() // ignore error + s.lc = nil + } + + default: + s.err = fmt.Errorf("unknown response type %T", r) + return true + } + return false +} + +// Return true iff in a consistent state, or there is a permanent error. +func (s *watchStream) handleTargetChange(tc *pb.TargetChange) bool { + switch tc.TargetChangeType { + case pb.TargetChange_NO_CHANGE: + s.logf("TargetNoChange %d %v", len(tc.TargetIds), tc.ReadTime) + if len(tc.TargetIds) == 0 && tc.ReadTime != nil && s.current { + // Everything is up-to-date, so we are ready to return a snapshot. + rt, err := ptypes.Timestamp(tc.ReadTime) + if err != nil { + s.err = err + return true + } + s.readTime = rt + s.target.ResumeType = &pb.Target_ResumeToken{tc.ResumeToken} + return true + } + + case pb.TargetChange_ADD: + s.logf("TargetAdd") + if tc.TargetIds[0] != watchTargetID { + s.err = errors.New("unexpected target ID sent by server") + return true + } + + case pb.TargetChange_REMOVE: + s.logf("TargetRemove") + // We should never see a remove. + if tc.Cause != nil { + s.err = status.Error(codes.Code(tc.Cause.Code), tc.Cause.Message) + } else { + s.err = status.Error(codes.Internal, "firestore: client saw REMOVE") + } + return true + + // The targets reflect all changes committed before the targets were added + // to the stream. + case pb.TargetChange_CURRENT: + s.logf("TargetCurrent") + s.current = true + + // The targets have been reset, and a new initial state for the targets will be + // returned in subsequent changes. Whatever changes have happened so far no + // longer matter. + case pb.TargetChange_RESET: + s.logf("TargetReset") + s.resetDocs() + + default: + s.err = fmt.Errorf("firestore: unknown TargetChange type %s", tc.TargetChangeType) + return true + } + // If we see a resume token and our watch ID is affected, we assume the stream + // is now healthy, so we reset our backoff time to the minimum. + if tc.ResumeToken != nil && (len(tc.TargetIds) == 0 || hasWatchTargetID(tc.TargetIds)) { + s.backoff = defaultBackoff + } + return false // not in a consistent state, keep receiving +} + +func (s *watchStream) resetDocs() { + s.target.ResumeType = nil // clear resume token + s.current = false + s.changeMap = map[string]*DocumentSnapshot{} + // Mark each document as deleted. If documents are not deleted, they + // will be send again by the server. + it := s.docTree.BeforeIndex(0) + for it.Next() { + s.changeMap[it.Key.(*DocumentSnapshot).Ref.Path] = nil + } +} + +func (s *watchStream) currentSize() int { + _, adds, deletes := extractChanges(s.docMap, s.changeMap) + return len(s.docMap) + len(adds) - len(deletes) +} + +// Return the changes that have occurred since the last snapshot. +func extractChanges(docMap, changeMap map[string]*DocumentSnapshot) (updates, adds []*DocumentSnapshot, deletes []string) { + for name, doc := range changeMap { + switch { + case doc == nil: + if _, ok := docMap[name]; ok { + deletes = append(deletes, name) + } + case docMap[name] != nil: + updates = append(updates, doc) + default: + adds = append(adds, doc) + } + } + return updates, adds, deletes +} + +// For development only. +// TODO(jba): remove. +func assert(b bool) { + if !b { + panic("assertion failed") + } +} + +// Applies the mutations in changeMap to both the document tree and the +// document lookup map. Modifies docMap in place and returns a new docTree. +// If there were no changes, returns docTree unmodified. +func (s *watchStream) computeSnapshot(docTree *btree.BTree, docMap, changeMap map[string]*DocumentSnapshot, readTime time.Time) (*btree.BTree, []DocumentChange) { + var changes []DocumentChange + updatedTree := docTree + assert(docTree.Len() == len(docMap)) + updates, adds, deletes := extractChanges(docMap, changeMap) + if len(adds) > 0 || len(deletes) > 0 { + updatedTree = docTree.Clone() + } + // Process the sorted changes in the order that is expected by our clients + // (removals, additions, and then modifications). We also need to sort the + // individual changes to assure that oldIndex/newIndex keep incrementing. + deldocs := make([]*DocumentSnapshot, len(deletes)) + for i, d := range deletes { + deldocs[i] = docMap[d] + } + sort.Sort(byLess{deldocs, s.less}) + for _, oldDoc := range deldocs { + assert(oldDoc != nil) + delete(docMap, oldDoc.Ref.Path) + _, oldi := updatedTree.GetWithIndex(oldDoc) + // TODO(jba): have btree.Delete return old index + _, found := updatedTree.Delete(oldDoc) + assert(found) + changes = append(changes, DocumentChange{ + Kind: DocumentRemoved, + Doc: oldDoc, + OldIndex: oldi, + NewIndex: -1, + }) + } + sort.Sort(byLess{adds, s.less}) + for _, newDoc := range adds { + name := newDoc.Ref.Path + assert(docMap[name] == nil) + newDoc.ReadTime = readTime + docMap[name] = newDoc + updatedTree.Set(newDoc, nil) + // TODO(jba): change btree so Set returns index as second value. + _, newi := updatedTree.GetWithIndex(newDoc) + changes = append(changes, DocumentChange{ + Kind: DocumentAdded, + Doc: newDoc, + OldIndex: -1, + NewIndex: newi, + }) + } + sort.Sort(byLess{updates, s.less}) + for _, newDoc := range updates { + name := newDoc.Ref.Path + oldDoc := docMap[name] + assert(oldDoc != nil) + if newDoc.UpdateTime.Equal(oldDoc.UpdateTime) { + continue + } + if updatedTree == docTree { + updatedTree = docTree.Clone() + } + newDoc.ReadTime = readTime + docMap[name] = newDoc + _, oldi := updatedTree.GetWithIndex(oldDoc) + updatedTree.Delete(oldDoc) + updatedTree.Set(newDoc, nil) + _, newi := updatedTree.GetWithIndex(newDoc) + changes = append(changes, DocumentChange{ + Kind: DocumentModified, + Doc: newDoc, + OldIndex: oldi, + NewIndex: newi, + }) + } + assert(updatedTree.Len() == len(docMap)) + return updatedTree, changes +} + +type byLess struct { + s []*DocumentSnapshot + less func(a, b *DocumentSnapshot) bool +} + +func (b byLess) Len() int { return len(b.s) } +func (b byLess) Swap(i, j int) { b.s[i], b.s[j] = b.s[j], b.s[i] } +func (b byLess) Less(i, j int) bool { return b.less(b.s[i], b.s[j]) } + +func hasWatchTargetID(ids []int32) bool { + for _, id := range ids { + if id == watchTargetID { + return true + } + } + return false +} + +func (s *watchStream) logf(format string, args ...interface{}) { + if LogWatchStreams { + log.Printf(format, args...) + } +} + +// Close the stream. From this point on, calls to nextSnapshot will return +// io.EOF, or the error from CloseSend. +func (s *watchStream) stop() { + err := s.close() + if s.err != nil { // don't change existing error + return + } + if err != nil { + s.err = err + } + s.err = io.EOF // normal shutdown +} + +func (s *watchStream) close() error { + if s.lc == nil { + return nil + } + return s.lc.CloseSend() +} + +// recv receives the next message from the stream. It also handles opening the stream +// initially, and reopening it on non-permanent errors. +// recv doesn't have to be goroutine-safe. +func (s *watchStream) recv() (*pb.ListenResponse, error) { + var err error + for { + if s.lc == nil { + s.lc, err = s.open() + if err != nil { + // Do not retry if open fails. + return nil, err + } + } + res, err := s.lc.Recv() + if err == nil || isPermanentWatchError(err) { + return res, err + } + // Non-permanent error. Sleep and retry. + s.changeMap = map[string]*DocumentSnapshot{} // clear changeMap + dur := s.backoff.Pause() + // If we're out of quota, wait a long time before retrying. + if status.Code(err) == codes.ResourceExhausted { + dur = s.backoff.Max + } + if err := sleep(s.ctx, dur); err != nil { + return nil, err + } + s.lc = nil + } +} + +func (s *watchStream) open() (pb.Firestore_ListenClient, error) { + dbPath := s.c.path() + lc, err := s.c.c.Listen(withResourceHeader(s.ctx, dbPath)) + if err == nil { + err = lc.Send(&pb.ListenRequest{ + Database: dbPath, + TargetChange: &pb.ListenRequest_AddTarget{AddTarget: s.target}, + }) + } + if err != nil { + return nil, err + } + return lc, nil +} + +func isPermanentWatchError(err error) bool { + if err == io.EOF { + // Retry on normal end-of-stream. + return false + } + switch status.Code(err) { + case codes.Unknown, codes.DeadlineExceeded, codes.ResourceExhausted, + codes.Internal, codes.Unavailable, codes.Unauthenticated: + return false + default: + return true + } +} diff --git a/vendor/cloud.google.com/go/firestore/watch_test.go b/vendor/cloud.google.com/go/firestore/watch_test.go new file mode 100644 index 0000000000..2bc1b15a57 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/watch_test.go @@ -0,0 +1,224 @@ +// 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 firestore + +import ( + "sort" + "testing" + "time" + + "cloud.google.com/go/internal/btree" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestWatchRecv(t *testing.T) { + ctx := context.Background() + c, srv := newMock(t) + db := defaultBackoff + defaultBackoff = gax.Backoff{Initial: 1, Max: 1, Multiplier: 1} + defer func() { defaultBackoff = db }() + + ws := newWatchStream(ctx, c, nil, &pb.Target{}) + request := &pb.ListenRequest{ + Database: "projects/projectID/databases/(default)", + TargetChange: &pb.ListenRequest_AddTarget{&pb.Target{}}, + } + response := &pb.ListenResponse{ResponseType: &pb.ListenResponse_DocumentChange{&pb.DocumentChange{}}} + // Stream should retry on non-permanent errors, returning only the responses. + srv.addRPC(request, []interface{}{response, status.Error(codes.Unknown, "")}) + srv.addRPC(request, []interface{}{response}) // stream will return io.EOF + srv.addRPC(request, []interface{}{response, status.Error(codes.DeadlineExceeded, "")}) + srv.addRPC(request, []interface{}{status.Error(codes.ResourceExhausted, "")}) + srv.addRPC(request, []interface{}{status.Error(codes.Internal, "")}) + srv.addRPC(request, []interface{}{status.Error(codes.Unavailable, "")}) + srv.addRPC(request, []interface{}{status.Error(codes.Unauthenticated, "")}) + srv.addRPC(request, []interface{}{response}) + for i := 0; i < 4; i++ { + res, err := ws.recv() + if err != nil { + t.Fatal(err) + } + if !proto.Equal(res, response) { + t.Fatalf("got %v, want %v", res, response) + } + } + + // Stream should not retry on a permanent error. + srv.addRPC(request, []interface{}{status.Error(codes.AlreadyExists, "")}) + _, err := ws.recv() + if got, want := status.Code(err), codes.AlreadyExists; got != want { + t.Fatalf("got %s, want %s", got, want) + } +} + +func TestComputeSnapshot(t *testing.T) { + c := &Client{ + projectID: "projID", + databaseID: "(database)", + } + ws := newWatchStream(context.Background(), c, nil, &pb.Target{}) + tm := time.Now() + i := 0 + doc := func(path, value string) *DocumentSnapshot { + i++ + return &DocumentSnapshot{ + Ref: c.Doc(path), + proto: &pb.Document{Fields: map[string]*pb.Value{"foo": strval(value)}}, + UpdateTime: tm.Add(time.Duration(i) * time.Second), // need unique time for updates + } + } + val := func(d *DocumentSnapshot) string { return d.proto.Fields["foo"].GetStringValue() } + less := func(a, b *DocumentSnapshot) bool { return val(a) < val(b) } + + type dmap map[string]*DocumentSnapshot + + ds1 := doc("C/d1", "a") + ds2 := doc("C/d2", "b") + ds2c := doc("C/d2", "c") + docTree := btree.New(4, func(a, b interface{}) bool { return less(a.(*DocumentSnapshot), b.(*DocumentSnapshot)) }) + var gotChanges []DocumentChange + docMap := dmap{} + // The following test cases are not independent; each builds on the output of the previous. + for _, test := range []struct { + desc string + changeMap dmap + wantDocs []*DocumentSnapshot + wantChanges []DocumentChange + }{ + { + "no changes", + nil, + nil, + nil, + }, + { + "add a doc", + dmap{ds1.Ref.Path: ds1}, + []*DocumentSnapshot{ds1}, + []DocumentChange{{Kind: DocumentAdded, Doc: ds1, OldIndex: -1, NewIndex: 0}}, + }, + { + "add, remove", + dmap{ds1.Ref.Path: nil, ds2.Ref.Path: ds2}, + []*DocumentSnapshot{ds2}, + []DocumentChange{ + {Kind: DocumentRemoved, Doc: ds1, OldIndex: 0, NewIndex: -1}, + {Kind: DocumentAdded, Doc: ds2, OldIndex: -1, NewIndex: 0}, + }, + }, + { + "add back, modify", + dmap{ds1.Ref.Path: ds1, ds2c.Ref.Path: ds2c}, + []*DocumentSnapshot{ds1, ds2c}, + []DocumentChange{ + {Kind: DocumentAdded, Doc: ds1, OldIndex: -1, NewIndex: 0}, + {Kind: DocumentModified, Doc: ds2c, OldIndex: 1, NewIndex: 1}, + }, + }, + } { + docTree, gotChanges = ws.computeSnapshot(docTree, docMap, test.changeMap, time.Time{}) + gotDocs := treeDocs(docTree) + if diff := testDiff(gotDocs, test.wantDocs); diff != "" { + t.Fatalf("%s: %s", test.desc, diff) + } + mgot := mapDocs(docMap, less) + if diff := testDiff(gotDocs, mgot); diff != "" { + t.Fatalf("%s: docTree and docMap disagree: %s", test.desc, diff) + } + if diff := testDiff(gotChanges, test.wantChanges); diff != "" { + t.Fatalf("%s: %s", test.desc, diff) + } + } + + // Verify that if there are no changes, the returned docTree is identical to the first arg. + // docTree already has ds2c. + got, _ := ws.computeSnapshot(docTree, docMap, dmap{ds2c.Ref.Path: ds2c}, time.Time{}) + if got != docTree { + t.Error("returned docTree != arg docTree") + } +} + +func treeDocs(bt *btree.BTree) []*DocumentSnapshot { + var ds []*DocumentSnapshot + it := bt.BeforeIndex(0) + for it.Next() { + ds = append(ds, it.Key.(*DocumentSnapshot)) + } + return ds +} + +func mapDocs(m map[string]*DocumentSnapshot, less func(a, b *DocumentSnapshot) bool) []*DocumentSnapshot { + var ds []*DocumentSnapshot + for _, d := range m { + ds = append(ds, d) + } + sort.Sort(byLess{ds, less}) + return ds +} + +func TestWatchCancel(t *testing.T) { + // Canceling the context of a watch should result in a codes.Canceled error from the next + // call to the iterator's Next method. + ctx := context.Background() + c, srv := newMock(t) + q := Query{c: c, collectionID: "x"} + + // Cancel before open. + ctx2, cancel := context.WithCancel(ctx) + ws, err := newWatchStreamForQuery(ctx2, q) + if err != nil { + t.Fatal(err) + } + cancel() + _, _, _, err = ws.nextSnapshot() + codeEq(t, "cancel before open", codes.Canceled, err) + + request := &pb.ListenRequest{ + Database: "projects/projectID/databases/(default)", + TargetChange: &pb.ListenRequest_AddTarget{ws.target}, + } + current := &pb.ListenResponse{ResponseType: &pb.ListenResponse_TargetChange{&pb.TargetChange{ + TargetChangeType: pb.TargetChange_CURRENT, + }}} + noChange := &pb.ListenResponse{ResponseType: &pb.ListenResponse_TargetChange{&pb.TargetChange{ + TargetChangeType: pb.TargetChange_NO_CHANGE, + ReadTime: aTimestamp, + }}} + + // Cancel from gax.Sleep. We should still see a gRPC error with codes.Canceled, not a + // context.Canceled error. + ctx2, cancel = context.WithCancel(ctx) + ws, err = newWatchStreamForQuery(ctx2, q) + if err != nil { + t.Fatal(err) + } + srv.addRPC(request, []interface{}{current, noChange}) + _, _, _, _ = ws.nextSnapshot() + cancel() + // Because of how the mock works, the following results in an EOF on the stream, which + // is a non-permanent error that causes a retry. That retry ends up in gax.Sleep, which + // finds that the context is done and returns ctx.Err(), which is context.Canceled. + // Verify that we transform that context.Canceled into a gRPC Status with code Canceled. + _, _, _, err = ws.nextSnapshot() + codeEq(t, "cancel from gax.Sleep", codes.Canceled, err) + + // TODO(jba): Test that we get codes.Canceled when canceling an RPC. + // We had a test for this in a21236af, but it was flaky for unclear reasons. +} diff --git a/vendor/cloud.google.com/go/firestore/writebatch.go b/vendor/cloud.google.com/go/firestore/writebatch.go new file mode 100644 index 0000000000..1732a9dd7e --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/writebatch.go @@ -0,0 +1,82 @@ +// Copyright 2017 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 firestore + +import ( + "errors" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "golang.org/x/net/context" +) + +// A WriteBatch holds multiple database updates. Build a batch with the Create, Set, +// Update and Delete methods, then run it with the Commit method. Errors in Create, +// Set, Update or Delete are recorded instead of being returned immediately. The +// first such error is returned by Commit. +type WriteBatch struct { + c *Client + err error + writes []*pb.Write +} + +func (b *WriteBatch) add(ws []*pb.Write, err error) *WriteBatch { + if b.err != nil { + return b + } + if err != nil { + b.err = err + return b + } + b.writes = append(b.writes, ws...) + return b +} + +// Create adds a Create operation to the batch. +// See DocumentRef.Create for details. +func (b *WriteBatch) Create(dr *DocumentRef, data interface{}) *WriteBatch { + return b.add(dr.newCreateWrites(data)) +} + +// Set adds a Set operation to the batch. +// See DocumentRef.Set for details. +func (b *WriteBatch) Set(dr *DocumentRef, data interface{}, opts ...SetOption) *WriteBatch { + return b.add(dr.newSetWrites(data, opts)) +} + +// Delete adds a Delete operation to the batch. +// See DocumentRef.Delete for details. +func (b *WriteBatch) Delete(dr *DocumentRef, opts ...Precondition) *WriteBatch { + return b.add(dr.newDeleteWrites(opts)) +} + +// Update adds an Update operation to the batch. +// See DocumentRef.Update for details. +func (b *WriteBatch) Update(dr *DocumentRef, data []Update, opts ...Precondition) *WriteBatch { + return b.add(dr.newUpdatePathWrites(data, opts)) +} + +// Commit applies all the writes in the batch to the database atomically. Commit +// returns an error if there are no writes in the batch, if any errors occurred in +// constructing the writes, or if the Commmit operation fails. +func (b *WriteBatch) Commit(ctx context.Context) ([]*WriteResult, error) { + if b.err != nil { + return nil, b.err + } + if len(b.writes) == 0 { + return nil, errors.New("firestore: cannot commit empty WriteBatch") + } + return b.c.commit(ctx, b.writes) +} diff --git a/vendor/cloud.google.com/go/firestore/writebatch_test.go b/vendor/cloud.google.com/go/firestore/writebatch_test.go new file mode 100644 index 0000000000..9946ff1c79 --- /dev/null +++ b/vendor/cloud.google.com/go/firestore/writebatch_test.go @@ -0,0 +1,119 @@ +// Copyright 2017 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 firestore + +import ( + "testing" + + pb "google.golang.org/genproto/googleapis/firestore/v1beta1" + + "golang.org/x/net/context" +) + +func TestWriteBatch(t *testing.T) { + type update struct{ A int } + + c, srv := newMock(t) + docPrefix := c.Collection("C").Path + "/" + srv.addRPC( + &pb.CommitRequest{ + Database: c.path(), + Writes: []*pb.Write{ + { // Create + Operation: &pb.Write_Update{ + Update: &pb.Document{ + Name: docPrefix + "a", + Fields: testFields, + }, + }, + CurrentDocument: &pb.Precondition{ + ConditionType: &pb.Precondition_Exists{false}, + }, + }, + { // Set + Operation: &pb.Write_Update{ + Update: &pb.Document{ + Name: docPrefix + "b", + Fields: testFields, + }, + }, + }, + { // Delete + Operation: &pb.Write_Delete{ + Delete: docPrefix + "c", + }, + }, + { // Update + Operation: &pb.Write_Update{ + Update: &pb.Document{ + Name: docPrefix + "f", + Fields: map[string]*pb.Value{"*": intval(3)}, + }, + }, + UpdateMask: &pb.DocumentMask{FieldPaths: []string{"`*`"}}, + CurrentDocument: &pb.Precondition{ + ConditionType: &pb.Precondition_Exists{true}, + }, + }, + }, + }, + &pb.CommitResponse{ + WriteResults: []*pb.WriteResult{ + {UpdateTime: aTimestamp}, + {UpdateTime: aTimestamp2}, + {UpdateTime: aTimestamp3}, + }, + }, + ) + gotWRs, err := c.Batch(). + Create(c.Doc("C/a"), testData). + Set(c.Doc("C/b"), testData). + Delete(c.Doc("C/c")). + Update(c.Doc("C/f"), []Update{{FieldPath: []string{"*"}, Value: 3}}). + Commit(context.Background()) + if err != nil { + t.Fatal(err) + } + wantWRs := []*WriteResult{{aTime}, {aTime2}, {aTime3}} + if !testEqual(gotWRs, wantWRs) { + t.Errorf("got %+v\nwant %+v", gotWRs, wantWRs) + } +} + +func TestWriteBatchErrors(t *testing.T) { + ctx := context.Background() + c, _ := newMock(t) + for _, test := range []struct { + desc string + batch *WriteBatch + }{ + { + "empty batch", + c.Batch(), + }, + { + "bad doc reference", + c.Batch().Create(c.Doc("a"), testData), + }, + { + "bad data", + c.Batch().Create(c.Doc("a/b"), 3), + }, + } { + if _, err := test.batch.Commit(ctx); err == nil { + t.Errorf("%s: got nil, want error", test.desc) + } + } +} diff --git a/vendor/cloud.google.com/go/httpreplay/cmd/httpr/README.md b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/README.md new file mode 100644 index 0000000000..443541dfee --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/README.md @@ -0,0 +1,70 @@ +# httpr, a Record/Replay Proxy + +httpr is an HTTP proxy that records and replays traffic. It is designed +specifically for Google APIs that use HTTP exclusively. These include the Google +Cloud Storage and BigQuery clients, as well as the clients in the +`github.com/google/google-api-*-client` repos. + +If you are writing Go code, you should use the `cloud.google.com/go/httpreplay` package, which +is a simpler way to use the proxy. + +## Using a Record/Replay Proxy + +A record/replay proxy lets you run an "integration" test that accesses a +backend like a Google service and record the interaction. Subsequent runs of the +test can replay the server's responses without actually contacting the server, +turning the integration test into a fast and inexpensive unit test. + +## Usage + +First, obtain the `httpr` binary. If you have the Go toolchain, you can run `go +get -u cloud.google.com/go/httpreplay/cmd/httpr`. Otherwise, precompiled +binaries for various architectures will be available shortly. + +### Recording + +1. Start `httpr` in record mode by passing it the `-record` flag with a filename: + ``` + httpr -record myclient.replay + ``` + By default, `httpr` will run on port 8080, and open a control port on 8181. + You can change these with the `-port` and `-control-port` flags. + You will want to run `httpr` in the background or in another window. +1. In order for `httpr` to record HTTPS traffic, your client must trust it. It + does so by installing a CA certificate created by `httpr` during the + recording session. To obtain the certificate in PEM form, GET the URL + `http://localhost:8181/authority.cer`. (If you changed the control port, use + it in place of 8181.) Consult your language to determine + how to install the certificate. Note that the certificate is different for each run + of `httpr`. +1. Arrange for your test program to use `httpr` as a proxy. This may be as + simple as setting the `HTTPS_PROXY` environment variable. +1. Run your test program, using whatever authentication for your Google API + clients that you wish. +1. Send `httpr` a SIGINT signal (`kill -2`). `httpr` will write + the replay file, then exit. + +### Replaying + +1. Start `httpr` in replay mode, in the background or another window: + ``` + httpr -replay myclient.replay + ``` +1. Install the CA certificate as described above. +1. Have your test program treat `httpr` as a proxy, as described above. +1. Run your test program. Your Google API clients should use no authentication. + +## Tips + +You must remove all randomness from your interaction while recording, +so that the replay is fully deterministic. + +Note that BigQuery clients choose random values for job IDs and insert ID if you +do not supply them. Either supply your own, or seed the client's random number +generator if possible. + +## Examples + +Examples of running `httpr` can be found in `examples` under this file's directory. + + diff --git a/vendor/cloud.google.com/go/httpreplay/cmd/httpr/examples/python/httpr-demo.py b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/examples/python/httpr-demo.py new file mode 100644 index 0000000000..5f68498f00 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/examples/python/httpr-demo.py @@ -0,0 +1,26 @@ +from __future__ import print_function + +import sys +from google.auth.credentials import AnonymousCredentials +from google.cloud import storage + + +if len(sys.argv)-1 != 3: + print('args: PROJECT BUCKET record|replay') + sys.exit(1) +project = sys.argv[1] +bucket_name = sys.argv[2] +mode = sys.argv[3] + +if mode == 'record': + creds = None # use default creds for demo purposes; not recommended + client = storage.Client(project=project) +elif mode == 'replay': + creds = AnonymousCredentials() +else: + print('want record or replay') + sys.exit(1) + +client = storage.Client(project=project, credentials=creds) +bucket = client.get_bucket(bucket_name) +print('bucket %s created %s' %(bucket.id, bucket.time_created)) diff --git a/vendor/cloud.google.com/go/httpreplay/cmd/httpr/examples/python/httpr-demo.sh b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/examples/python/httpr-demo.sh new file mode 100755 index 0000000000..aae45b877d --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/examples/python/httpr-demo.sh @@ -0,0 +1,55 @@ +#!/bin/sh -e + +# This script and the accompanying program, httpr-demo.py, demonstrate how to use +# httpr with Python. +# +# Prerequisites: +# - httpr is on your path. +# - The google-cloud and google-auth Python packages have been installed: +# pip install --upgrade google-cloud +# pip install --upgrade google-auth + +# Execution: +# 1. Pick a project and a GCS bucket. +# 2. Invoke this script to record an interaction: +# http-demo.sh PROJECT BUCKET record +# 3. Invoke the script again to replay: +# http-demo.sh PROJECT BUCKET replay + +project=$1 +bucket=$2 +mode=$3 + +if [[ $mode != "record" && $mode != "replay" ]]; then + echo >&2 "usage: $0 PROJECT BUCKET record|replay" + exit 1 +fi + +if [[ $(which httpr) == "" ]]; then + echo >&2 "httpr is not on PATH" + exit 1 +fi + +# Start the proxy and wait for it to come up. +httpr -$mode /tmp/demo.replay & +proxy_pid=$! + +# Stop the proxy on exit. +# When the proxy is recording, this will cause it to write the replay file. +trap "kill -2 $proxy_pid" EXIT + +sleep 1 + +# Download the CA certificate from the proxy's control port +# and inform Python of the cert via an environment variable. +cert_file=/tmp/httpr.cer +curl -s localhost:8181/authority.cer > $cert_file +export REQUESTS_CA_BUNDLE=$cert_file + +# Tell Python to use the proxy. +# If you passed the -port argument to httpr, use that port here. +export HTTPS_PROXY=localhost:8080 + +# Run the program. +python httpr-demo.py $project $bucket $mode + diff --git a/vendor/cloud.google.com/go/httpreplay/cmd/httpr/httpr.go b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/httpr.go new file mode 100644 index 0000000000..eb5583c989 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/httpr.go @@ -0,0 +1,112 @@ +// 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. + +// httpr is a proxy that can record or replay HTTP requests. +// Start httpr with either the -record or -replay flags, providing a filename. +// Terminate the process with an interrupt (kill -2) to write the log file when recording. +// To get the CA certificate of the proxy, issue a GET to http://localhost:CP/authority.cer, where +// CP is the control port. + +// +build go1.8 + +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "os" + "os/signal" + + "cloud.google.com/go/httpreplay/internal/proxy" + "github.com/google/martian/martianhttp" +) + +var ( + port = flag.Int("port", 8080, "port of the proxy") + controlPort = flag.Int("control-port", 8181, "port for controlling the proxy") + record = flag.String("record", "", "record traffic and save to filename") + replay = flag.String("replay", "", "read filename and replay traffic") + debugHeaders = flag.Bool("debug-headers", false, "log header mismatches") +) + +func main() { + flag.Parse() + if *record == "" && *replay == "" { + log.Fatal("provide either -record or -replay") + } + if *record != "" && *replay != "" { + log.Fatal("provide only one of -record and -replay") + } + log.Printf("httpr: starting proxy on port %d and control on port %d", *port, *controlPort) + + var pr *proxy.Proxy + var err error + if *record != "" { + pr, err = proxy.ForRecording(*record, *port) + } else { + pr, err = proxy.ForReplaying(*replay, *port) + } + if err != nil { + log.Fatal(err) + } + proxy.DebugHeaders = *debugHeaders + + // Expose handlers on the control port. + mux := http.NewServeMux() + mux.Handle("/authority.cer", martianhttp.NewAuthorityHandler(pr.CACert)) + mux.HandleFunc("/initial", handleInitial(pr)) + lControl, err := net.Listen("tcp", fmt.Sprintf(":%d", *controlPort)) + if err != nil { + log.Fatal(err) + } + go http.Serve(lControl, mux) + + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt) + + <-sigc + + log.Println("httpr: shutting down") + if err := pr.Close(); err != nil { + log.Fatal(err) + } +} + +func handleInitial(pr *proxy.Proxy) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + switch req.Method { + case "GET": + if pr.Initial != nil { + w.Write(pr.Initial) + } + + case "POST": + bytes, err := ioutil.ReadAll(req.Body) + req.Body.Close() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "reading body: %v", err) + } + pr.Initial = bytes + + default: + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, "use GET to retrieve initial or POST to set it") + } + } +} diff --git a/vendor/cloud.google.com/go/httpreplay/cmd/httpr/integration_test.go b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/integration_test.go new file mode 100644 index 0000000000..af46f421a7 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/cmd/httpr/integration_test.go @@ -0,0 +1,230 @@ +// 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. + +// +build go1.8 + +package main_test + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "os/exec" + "strings" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/storage" + "golang.org/x/net/context" + "golang.org/x/oauth2" + "google.golang.org/api/option" +) + +const initial = "initial state" + +func TestIntegration_HTTPR(t *testing.T) { + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + if testutil.ProjID() == "" { + t.Fatal("set GCLOUD_TESTS_GOLANG_PROJECT_ID and GCLOUD_TESTS_GOLANG_KEY") + } + // Get a unique temporary filename. + f, err := ioutil.TempFile("", "httpreplay") + if err != nil { + t.Fatal(err) + } + replayFilename := f.Name() + if err := f.Close(); err != nil { + t.Fatal(err) + } + defer os.Remove(replayFilename) + + if err := exec.Command("go", "build").Run(); err != nil { + t.Fatalf("running 'go build': %v", err) + } + defer os.Remove("./httpr") + want := runRecord(t, replayFilename) + got := runReplay(t, replayFilename) + if got != want { + t.Fatalf("got %q, want %q", got, want) + } +} + +func runRecord(t *testing.T, filename string) string { + cmd, tr, cport, err := start("-record", filename) + if err != nil { + t.Fatal(err) + } + defer stop(t, cmd) + + ctx := context.Background() + hc := &http.Client{ + Transport: &oauth2.Transport{ + Base: tr, + Source: testutil.TokenSource(ctx, storage.ScopeFullControl), + }, + } + res, err := http.Post( + fmt.Sprintf("http://localhost:%s/initial", cport), + "text/plain", + strings.NewReader(initial)) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != 200 { + t.Fatalf("from POST: %s", res.Status) + } + info, err := getBucketInfo(ctx, hc) + if err != nil { + t.Fatal(err) + } + return info +} + +func runReplay(t *testing.T, filename string) string { + cmd, tr, cport, err := start("-replay", filename) + if err != nil { + t.Fatal(err) + } + defer stop(t, cmd) + + hc := &http.Client{Transport: tr} + res, err := http.Get(fmt.Sprintf("http://localhost:%s/initial", cport)) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != 200 { + t.Fatalf("from GET: %s", res.Status) + } + bytes, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + t.Fatal(err) + } + if got, want := string(bytes), initial; got != want { + t.Errorf("initial: got %q, want %q", got, want) + } + info, err := getBucketInfo(context.Background(), hc) + if err != nil { + t.Fatal(err) + } + return info +} + +// Start the proxy binary and wait for it to come up. +// Return a transport that talks to the proxy, as well as the control port. +// modeFlag must be either "-record" or "-replay". +func start(modeFlag, filename string) (*exec.Cmd, *http.Transport, string, error) { + pport, err := pickPort() + if err != nil { + return nil, nil, "", err + } + cport, err := pickPort() + if err != nil { + return nil, nil, "", err + } + cmd := exec.Command("./httpr", "-port", pport, "-control-port", cport, modeFlag, filename, "-debug-headers") + if err := cmd.Start(); err != nil { + return nil, nil, "", err + } + // Wait for the server to come up. + serverUp := false + for i := 0; i < 10; i++ { + if conn, err := net.Dial("tcp", "localhost:"+cport); err == nil { + conn.Close() + serverUp = true + break + } + time.Sleep(time.Second) + } + if !serverUp { + return nil, nil, "", errors.New("server never came up") + } + tr, err := proxyTransport(pport, cport) + if err != nil { + return nil, nil, "", err + } + return cmd, tr, cport, nil +} + +func stop(t *testing.T, cmd *exec.Cmd) { + if err := cmd.Process.Signal(os.Interrupt); err != nil { + t.Fatal(err) + } +} + +// pickPort picks an unused port. +func pickPort() (string, error) { + l, err := net.Listen("tcp", ":0") + if err != nil { + return "", err + } + addr := l.Addr().String() + _, port, err := net.SplitHostPort(addr) + if err != nil { + return "", err + } + l.Close() + return port, nil +} + +func proxyTransport(pport, cport string) (*http.Transport, error) { + caCert, err := getBody(fmt.Sprintf("http://localhost:%s/authority.cer", cport)) + if err != nil { + return nil, err + } + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM([]byte(caCert)) { + return nil, errors.New("bad CA Cert") + } + return &http.Transport{ + Proxy: http.ProxyURL(&url.URL{Host: "localhost:" + pport}), + TLSClientConfig: &tls.Config{RootCAs: caCertPool}, + }, nil +} + +func getBucketInfo(ctx context.Context, hc *http.Client) (string, error) { + client, err := storage.NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + return "", err + } + defer client.Close() + b := client.Bucket(testutil.ProjID()) + attrs, err := b.Attrs(ctx) + if err != nil { + return "", err + } + return fmt.Sprintf("name:%s reqpays:%v location:%s sclass:%s", + attrs.Name, attrs.RequesterPays, attrs.Location, attrs.StorageClass), nil +} + +func getBody(url string) ([]byte, error) { + res, err := http.Get(url) + if err != nil { + return nil, err + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("response: %s", res.Status) + } + defer res.Body.Close() + return ioutil.ReadAll(res.Body) +} diff --git a/vendor/cloud.google.com/go/httpreplay/httpreplay.go b/vendor/cloud.google.com/go/httpreplay/httpreplay.go new file mode 100644 index 0000000000..14fa1a7153 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/httpreplay.go @@ -0,0 +1,131 @@ +// 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. + +// +build go1.8 + +// Package httpreplay provides an API for recording and replaying traffic +// from HTTP-based Google API clients. +// +// To record: +// 1. Call NewRecorder to get a Recorder. +// 2. Use its Client method to obtain an HTTP client to use when making API calls. +// 3. Close the Recorder when you're done. That will save the +// log of interactions to the file you provided to NewRecorder. +// +// To replay: +// 1. Call NewReplayer with the same filename you used to record to get a Replayer. +// 2. Call its Client method and use the client to make the same API calls. +// You will get back the recorded responses. +// 3. Close the Replayer when you're done. +// +// This package is EXPERIMENTAL and is subject to change or removal without notice. +// It requires Go version 1.8 or higher. +package httpreplay + +// TODO(jba): add examples. + +import ( + "net/http" + + "cloud.google.com/go/httpreplay/internal/proxy" + "golang.org/x/net/context" + "google.golang.org/api/option" + htransport "google.golang.org/api/transport/http" +) + +// A Recorder records HTTP interactions. +type Recorder struct { + filename string + proxy *proxy.Proxy +} + +// NewRecorder creates a recorder that writes to filename. The file will +// also store initial state that can be retrieved to configure replay. +// +// You must call Close on the Recorder to ensure that all data is written. +func NewRecorder(filename string, initial []byte) (*Recorder, error) { + p, err := proxy.ForRecording(filename, 0) + if err != nil { + return nil, err + } + p.Initial = initial + return &Recorder{proxy: p}, nil +} + +// Client returns an http.Client to be used for recording. Provide authentication options +// like option.WithTokenSource as you normally would, or omit them to use Application Default +// Credentials. +func (r *Recorder) Client(ctx context.Context, opts ...option.ClientOption) (*http.Client, error) { + return proxyClient(ctx, r.proxy, opts...) +} + +func proxyClient(ctx context.Context, p *proxy.Proxy, opts ...option.ClientOption) (*http.Client, error) { + trans, err := htransport.NewTransport(ctx, p.Transport(), opts...) + if err != nil { + return nil, err + } + return &http.Client{Transport: trans}, nil +} + +// Close closes the Recorder and saves the log file. +func (r *Recorder) Close() error { + return r.proxy.Close() +} + +// A Replayer replays previously recorded HTTP interactions. +type Replayer struct { + proxy *proxy.Proxy +} + +// NewReplayer creates a replayer that reads from filename. +func NewReplayer(filename string) (*Replayer, error) { + p, err := proxy.ForReplaying(filename, 0) + if err != nil { + return nil, err + } + return &Replayer{proxy: p}, nil +} + +// Client returns an HTTP client for replaying. The client does not need to be +// configured with credentials for authenticating to a server, since it never +// contacts a real backend. +func (r *Replayer) Client(ctx context.Context) (*http.Client, error) { + return proxyClient(ctx, r.proxy, option.WithoutAuthentication()) +} + +// Initial returns the initial state saved by the Recorder. +func (r *Replayer) Initial() []byte { + return r.proxy.Initial +} + +// IgnoreHeader will not use h when matching requests. +func (r *Replayer) IgnoreHeader(h string) { + r.proxy.IgnoreHeader(h) +} + +// Close closes the replayer. +func (r *Replayer) Close() error { + return r.proxy.Close() +} + +// DebugHeaders helps to determine whether a header should be ignored. +// When true, if requests have the same method, URL and body but differ +// in a header, the first mismatched header is logged. +func DebugHeaders() { + proxy.DebugHeaders = true +} + +// Supported reports whether httpreplay is supported in the current version of Go. +// For Go 1.8 and above, the answer is true. +func Supported() bool { return true } diff --git a/vendor/cloud.google.com/go/httpreplay/httpreplay_not18.go b/vendor/cloud.google.com/go/httpreplay/httpreplay_not18.go new file mode 100644 index 0000000000..f8bf9081c1 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/httpreplay_not18.go @@ -0,0 +1,52 @@ +// 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. + +// +build !go1.8 + +// httpreplay is available in go1.8 and forward. This file exists only for 1.6 and 1.7 to +// compile, since every package must have some buildable file else go test ./... fails. +package httpreplay + +import ( + "net/http" + + "golang.org/x/net/context" + "google.golang.org/api/option" +) + +// Supported reports whether httpreplay is supported in the current version of Go. +// For Go 1.7 and below, the answer is false. +func Supported() bool { return false } + +type ( + Recorder struct{} + + Replayer struct{} +) + +func NewRecorder(string, []byte) (*Recorder, error) { return nil, nil } + +func (*Recorder) Client(context.Context, ...option.ClientOption) (*http.Client, error) { + return nil, nil +} +func (*Recorder) Close() error { return nil } + +func NewReplayer(string) (*Replayer, error) { return nil, nil } + +func (*Replayer) Initial() []byte { return nil } +func (*Replayer) IgnoreHeader(string) {} +func (*Replayer) Client(context.Context) (*http.Client, error) { return nil, nil } +func (*Replayer) Close() error { return nil } + +func DebugHeaders() {} diff --git a/vendor/cloud.google.com/go/httpreplay/httpreplay_test.go b/vendor/cloud.google.com/go/httpreplay/httpreplay_test.go new file mode 100644 index 0000000000..a413ed430f --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/httpreplay_test.go @@ -0,0 +1,268 @@ +// 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. + +// +build go1.8 + +package httpreplay_test + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "os" + "testing" + "time" + + "cloud.google.com/go/httpreplay" + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/storage" + "golang.org/x/net/context" + "google.golang.org/api/option" +) + +func TestIntegration_RecordAndReplay(t *testing.T) { + httpreplay.DebugHeaders() + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + f, err := ioutil.TempFile("", "httpreplay") + if err != nil { + t.Fatal(err) + } + replayFilename := f.Name() + if err := f.Close(); err != nil { + t.Fatal(err) + } + defer os.Remove(replayFilename) + projectID := testutil.ProjID() + if projectID == "" { + t.Skip("Need project ID. See CONTRIBUTING.md for details.") + } + ctx := context.Background() + + // Record. + initial := time.Now() + ibytes, err := json.Marshal(initial) + if err != nil { + t.Fatal(err) + } + rec, err := httpreplay.NewRecorder(replayFilename, ibytes) + if err != nil { + t.Fatal(err) + } + hc, err := rec.Client(ctx, option.WithTokenSource( + testutil.TokenSource(ctx, storage.ScopeFullControl))) + if err != nil { + t.Fatal(err) + } + wanta, wantc := run(t, hc) + testReadCRC(t, hc, "recording") + if err := rec.Close(); err != nil { + t.Fatalf("rec.Close: %v", err) + } + + // Replay. + rep, err := httpreplay.NewReplayer(replayFilename) + if err != nil { + t.Fatal(err) + } + defer rep.Close() + hc, err = rep.Client(ctx) + if err != nil { + t.Fatal(err) + } + gota, gotc := run(t, hc) + testReadCRC(t, hc, "replaying") + + if diff := testutil.Diff(gota, wanta); diff != "" { + t.Error(diff) + } + if !bytes.Equal(gotc, wantc) { + t.Errorf("got %q, want %q", gotc, wantc) + } + var gotInitial time.Time + if err := json.Unmarshal(rep.Initial(), &gotInitial); err != nil { + t.Fatal(err) + } + if !gotInitial.Equal(initial) { + t.Errorf("initial: got %v, want %v", gotInitial, initial) + } +} + +// TODO(jba): test errors + +func run(t *testing.T, hc *http.Client) (*storage.BucketAttrs, []byte) { + ctx := context.Background() + client, err := storage.NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatal(err) + } + defer client.Close() + b := client.Bucket(testutil.ProjID()) + attrs, err := b.Attrs(ctx) + if err != nil { + t.Fatal(err) + } + obj := b.Object("replay-test") + w := obj.NewWriter(ctx) + data := []byte{150, 151, 152} + if _, err := w.Write(data); err != nil { + t.Fatal(err) + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + + r, err := obj.NewReader(ctx) + if err != nil { + t.Fatal(err) + } + defer r.Close() + contents, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + return attrs, contents +} + +func testReadCRC(t *testing.T, hc *http.Client, mode string) { + const ( + // This is an uncompressed file. + // See https://cloud.google.com/storage/docs/public-datasets/landsat + uncompressedBucket = "gcp-public-data-landsat" + uncompressedObject = "LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt" + + gzippedBucket = "storage-library-test-bucket" + gzippedObject = "gzipped-text.txt" + gzippedContents = "hello world" // uncompressed contents of the file + ) + ctx := context.Background() + client, err := storage.NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatalf("%s: %v", mode, err) + } + defer client.Close() + + uncompressedObj := client.Bucket(uncompressedBucket).Object(uncompressedObject) + gzippedObj := client.Bucket(gzippedBucket).Object(gzippedObject) + + for _, test := range []struct { + desc string + obj *storage.ObjectHandle + offset, length int64 + readCompressed bool // don't decompress a gzipped file + + wantErr bool + wantLen int // length of contents + }{ + { + desc: "uncompressed, entire file", + obj: uncompressedObj, + offset: 0, + length: -1, + readCompressed: false, + wantLen: 7903, + }, + { + desc: "uncompressed, entire file, don't decompress", + obj: uncompressedObj, + offset: 0, + length: -1, + readCompressed: true, + wantLen: 7903, + }, + { + desc: "uncompressed, suffix", + obj: uncompressedObj, + offset: 3, + length: -1, + readCompressed: false, + wantLen: 7900, + }, + { + desc: "uncompressed, prefix", + obj: uncompressedObj, + offset: 0, + length: 18, + readCompressed: false, + wantLen: 18, + }, + { + // When a gzipped file is unzipped by GCS, we can't verify the checksum + // because it was computed against the zipped contents. There is no + // header that indicates that a gzipped file is being served unzipped. + // But our CRC check only happens if there is a Content-Length header, + // and that header is absent for this read. + desc: "compressed, entire file, server unzips", + obj: gzippedObj, + offset: 0, + length: -1, + readCompressed: false, + wantLen: 11, + }, + { + // When we read a gzipped file uncompressed, it's like reading a regular file: + // the served content and the CRC match. + desc: "compressed, entire file, read compressed", + obj: gzippedObj, + offset: 0, + length: -1, + readCompressed: true, + wantLen: 31, + }, + { + desc: "compressed, partial, read compressed", + obj: gzippedObj, + offset: 1, + length: 8, + readCompressed: true, + wantLen: 8, + }, + { + desc: "uncompressed, HEAD", + obj: uncompressedObj, + offset: 0, + length: 0, + wantLen: 0, + }, + { + desc: "compressed, HEAD", + obj: gzippedObj, + offset: 0, + length: 0, + wantLen: 0, + }, + } { + obj := test.obj.ReadCompressed(test.readCompressed) + r, err := obj.NewRangeReader(ctx, test.offset, test.length) + if err != nil { + if test.wantErr { + continue + } + t.Errorf("%s: %s: %v", mode, test.desc, err) + continue + } + data, err := ioutil.ReadAll(r) + _ = r.Close() + if err != nil { + t.Errorf("%s: %s: %v", mode, test.desc, err) + continue + } + if got, want := len(data), test.wantLen; got != want { + t.Errorf("%s: %s: len: got %d, want %d", mode, test.desc, got, want) + } + } +} diff --git a/vendor/cloud.google.com/go/httpreplay/internal/proxy/debug.go b/vendor/cloud.google.com/go/httpreplay/internal/proxy/debug.go new file mode 100644 index 0000000000..20acbe9467 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/internal/proxy/debug.go @@ -0,0 +1,63 @@ +// 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. + +// +build go1.8 + +package proxy + +import ( + "bytes" + "fmt" + "log" + "net/http" +) + +// Useful things for when we need to figure out what's actually going on under the hood. + +type debugTransport struct { + prefix string + t *http.Transport +} + +func (d debugTransport) RoundTrip(req *http.Request) (*http.Response, error) { + log.Printf("proxy %s: %s %s", d.prefix, req.Method, req.URL) + logHeaders(req.Header) + res, err := d.t.RoundTrip(req) + if err != nil { + log.Printf("proxy %s: error %v", d.prefix, err) + } else { + log.Printf("proxy %s: %s", d.prefix, res.Status) + log.Printf("Uncompressed = %v", res.Uncompressed) + log.Printf("ContentLength = %d", res.ContentLength) + logHeaders(res.Header) + log.Printf("Trailers:") + logHeaders(res.Trailer) + } + return res, err +} + +func logHeaders(hs http.Header) { + for k, v := range hs { + log.Printf(" %s: %s", k, v) + } +} + +func (r *requestBody) String() string { + buf := &bytes.Buffer{} + fmt.Fprintf(buf, "media type: %q\n", r.mediaType) + for i, p := range r.parts { + fmt.Fprintf(buf, "part #%d: %q\n", i, string(p)) + } + return buf.String() +} diff --git a/vendor/cloud.google.com/go/httpreplay/internal/proxy/log.go b/vendor/cloud.google.com/go/httpreplay/internal/proxy/log.go new file mode 100644 index 0000000000..fec75116b1 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/internal/proxy/log.go @@ -0,0 +1,232 @@ +// 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. + +// +build go1.8 + +package proxy + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "strconv" + "sync" + + "github.com/google/martian" +) + +// Replacement for the HAR logging that comes with martian. HAR is not designed for +// replay. In particular, response bodies are interpreted (e.g. decompressed), and we +// just want them to be stored literally. This isn't something we can fix in martian: it +// is required in the HAR spec (http://www.softwareishard.com/blog/har-12-spec/#content). + +const LogVersion = "0.1" + +// A Log is a record of HTTP interactions, suitable for replay. It can be serialized to JSON. +type Log struct { + Initial []byte // initial data for replay + Version string // version of this log format + Entries []*Entry +} + +// An Entry single request-response pair. +type Entry struct { + ID string // unique ID + Request *Request + Response *Response +} + +// A Request represents an http.Request in the log. +type Request struct { + Method string // http.Request.Method + URL string // http.Request.URL, as a string + Proto string // http.Request.Proto + Header http.Header // http.Request.Header + Body []byte // http.Request.Body, read to completion + Trailer http.Header `json:",omitempty"` // http.Request.Trailer +} + +// A Response represents an http.Response in the log. +type Response struct { + StatusCode int // http.Response.StatusCode + Proto string // http.Response.Proto + ProtoMajor int // http.Response.ProtoMajor + ProtoMinor int // http.Response.ProtoMinor + Header http.Header // http.Response.Header + Body []byte // http.Response.Body, read to completion + Trailer http.Header `json:",omitempty"` // http.Response.Trailer +} + +// A Logger maintains a request-response log. +type Logger struct { + mu sync.Mutex + entries map[string]*Entry // from ID + log *Log +} + +// NewLogger creates a new logger. +func NewLogger() *Logger { + return &Logger{ + log: &Log{Version: LogVersion}, + entries: map[string]*Entry{}, + } +} + +// ModifyRequest logs requests. +func (l *Logger) ModifyRequest(req *http.Request) error { + if req.Method == "CONNECT" { + return nil + } + ctx := martian.NewContext(req) + if ctx.SkippingLogging() { + return nil + } + lreq, err := fromHTTPRequest(req) + if err != nil { + return err + } + id := ctx.ID() + entry := &Entry{ID: id, Request: lreq} + + l.mu.Lock() + defer l.mu.Unlock() + + if _, ok := l.entries[id]; ok { + panic(fmt.Sprintf("proxy: duplicate request ID: %s", id)) + } + l.entries[id] = entry + l.log.Entries = append(l.log.Entries, entry) + return nil +} + +// ModifyResponse logs responses. +func (l *Logger) ModifyResponse(res *http.Response) error { + ctx := martian.NewContext(res.Request) + if ctx.SkippingLogging() { + return nil + } + id := ctx.ID() + lres, err := fromHTTPResponse(res) + if err != nil { + return err + } + + l.mu.Lock() + defer l.mu.Unlock() + + if e, ok := l.entries[id]; ok { + e.Response = lres + } + // Ignore the response if we haven't seen the request. + return nil +} + +// Extract returns the Log and removes it. The Logger is not usable +// after this call. +func (l *Logger) Extract() *Log { + l.mu.Lock() + defer l.mu.Unlock() + r := l.log + l.log = nil + l.entries = nil + return r +} + +func fromHTTPRequest(req *http.Request) (*Request, error) { + data, err := snapshotBody(&req.Body) + if err != nil { + return nil, err + } + return &Request{ + Method: req.Method, + URL: req.URL.String(), + Proto: req.Proto, + Header: redactHeaders(req.Header), + Body: data, + Trailer: req.Trailer, + }, nil +} + +func fromHTTPResponse(res *http.Response) (*Response, error) { + data, err := snapshotBody(&res.Body) + if err != nil { + return nil, err + } + return &Response{ + StatusCode: res.StatusCode, + Proto: res.Proto, + ProtoMajor: res.ProtoMajor, + ProtoMinor: res.ProtoMinor, + Header: res.Header, + Body: data, + Trailer: res.Trailer, + }, nil +} + +func toHTTPResponse(lr *Response, req *http.Request) *http.Response { + res := &http.Response{ + StatusCode: lr.StatusCode, + Proto: lr.Proto, + ProtoMajor: lr.ProtoMajor, + ProtoMinor: lr.ProtoMinor, + Header: lr.Header, + Body: ioutil.NopCloser(bytes.NewReader(lr.Body)), + ContentLength: int64(len(lr.Body)), + } + res.Request = req + // For HEAD, set ContentLength to the value of the Content-Length header, or -1 + // if there isn't one. + if req.Method == "HEAD" { + res.ContentLength = -1 + if c := res.Header["Content-Length"]; len(c) == 1 { + if c64, err := strconv.ParseInt(c[0], 10, 64); err == nil { + res.ContentLength = c64 + } + } + } + return res +} + +func snapshotBody(body *io.ReadCloser) ([]byte, error) { + data, err := ioutil.ReadAll(*body) + if err != nil { + return nil, err + } + (*body).Close() + *body = ioutil.NopCloser(bytes.NewReader(data)) + return data, nil +} + +// Headers that may contain sensitive data (auth tokens, keys). +var sensitiveHeaders = map[string]bool{ + "Authorization": true, + "Proxy-Authorization": true, + "X-Goog-Encryption-Key": true, // used by Cloud Storage for customer-supplied encryption + "X-Goog-Copy-Source-Encryption-Key": true, // ditto +} + +// Copy headers, redacting sensitive ones. +func redactHeaders(hs http.Header) http.Header { + rh := http.Header{} + for k, v := range hs { + if sensitiveHeaders[k] { + rh.Set(k, "REDACTED") + } else { + rh[k] = v + } + } + return rh +} diff --git a/vendor/cloud.google.com/go/httpreplay/internal/proxy/log_test.go b/vendor/cloud.google.com/go/httpreplay/internal/proxy/log_test.go new file mode 100644 index 0000000000..f1de6857b9 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/internal/proxy/log_test.go @@ -0,0 +1,184 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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 go1.8 + +package proxy + +import ( + "io/ioutil" + "net/http" + "net/url" + "strings" + "testing" + + "cloud.google.com/go/internal/testutil" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/martian" +) + +func TestRedactHeaders(t *testing.T) { + clone := func(h http.Header) http.Header { + h2 := http.Header{} + for k, v := range h { + h2[k] = v + } + return h2 + } + + in := http.Header{ + "Content-Type": {"text/plain"}, + "Authorization": {"oauth2-token"}, + "X-Goog-Encryption-Key": {"a-secret-key"}, + "X-Goog-Copy-Source-Encryption-Key": {"another-secret-key"}, + } + orig := clone(in) + got := redactHeaders(in) + // Logged headers should be redacted. + want := http.Header{ + "Content-Type": {"text/plain"}, + "Authorization": {"REDACTED"}, + "X-Goog-Encryption-Key": {"REDACTED"}, + "X-Goog-Copy-Source-Encryption-Key": {"REDACTED"}, + } + if !testutil.Equal(got, want) { + t.Errorf("got %+v\nwant %+v", got, want) + } + // The original headers should be the same. + if got, want := in, orig; !testutil.Equal(got, want) { + t.Errorf("got %+v\nwant %+v", got, want) + } +} + +func TestLogger(t *testing.T) { + req := &http.Request{ + Method: "POST", + URL: &url.URL{ + Scheme: "https", + Host: "example.com", + Path: "a/b/c", + }, + Header: http.Header{"H1": {"v1", "v2"}}, + Body: ioutil.NopCloser(strings.NewReader("hello")), + Trailer: http.Header{"T1": {"v3", "v4"}}, + } + res := &http.Response{ + Request: req, + StatusCode: 204, + Body: ioutil.NopCloser(strings.NewReader("goodbye")), + Header: http.Header{"H2": {"v5"}}, + Trailer: http.Header{"T2": {"v6", "v7"}}, + } + l := NewLogger() + _, remove, err := martian.TestContext(req, nil, nil) + if err != nil { + t.Fatal(err) + } + defer remove() + if err := l.ModifyRequest(req); err != nil { + t.Fatal(err) + } + if err := l.ModifyResponse(res); err != nil { + t.Fatal(err) + } + lg := l.Extract() + want := []*Entry{ + { + ID: lg.Entries[0].ID, + Request: &Request{ + Method: "POST", + URL: "https://example.com/a/b/c", + Header: http.Header{"H1": {"v1", "v2"}}, + Body: []byte("hello"), + Trailer: http.Header{"T1": {"v3", "v4"}}, + }, + Response: &Response{ + StatusCode: 204, + Body: []byte("goodbye"), + Header: http.Header{"H2": {"v5"}}, + Trailer: http.Header{"T2": {"v6", "v7"}}, + }, + }, + } + if diff := testutil.Diff(lg.Entries, want); diff != "" { + t.Error(diff) + } +} + +func TestToHTTPResponse(t *testing.T) { + for _, test := range []struct { + desc string + lr *Response + req *http.Request + want *http.Response + }{ + { + desc: "GET request", + lr: &Response{ + StatusCode: 201, + Proto: "1.1", + Header: http.Header{"h": {"v"}}, + Body: []byte("text"), + }, + req: &http.Request{Method: "GET"}, + want: &http.Response{ + Request: &http.Request{Method: "GET"}, + StatusCode: 201, + Proto: "1.1", + Header: http.Header{"h": {"v"}}, + ContentLength: 4, + }, + }, + { + desc: "HEAD request with no Content-Length header", + lr: &Response{ + StatusCode: 201, + Proto: "1.1", + Header: http.Header{"h": {"v"}}, + Body: []byte("text"), + }, + req: &http.Request{Method: "HEAD"}, + want: &http.Response{ + Request: &http.Request{Method: "HEAD"}, + StatusCode: 201, + Proto: "1.1", + Header: http.Header{"h": {"v"}}, + ContentLength: -1, + }, + }, + { + desc: "HEAD request with Content-Length header", + lr: &Response{ + StatusCode: 201, + Proto: "1.1", + Header: http.Header{"h": {"v"}, "Content-Length": {"17"}}, + Body: []byte("text"), + }, + req: &http.Request{Method: "HEAD"}, + want: &http.Response{ + Request: &http.Request{Method: "HEAD"}, + StatusCode: 201, + Proto: "1.1", + Header: http.Header{"h": {"v"}, "Content-Length": {"17"}}, + ContentLength: 17, + }, + }, + } { + got := toHTTPResponse(test.lr, test.req) + got.Body = nil + if diff := testutil.Diff(got, test.want, cmpopts.IgnoreUnexported(http.Request{})); diff != "" { + t.Errorf("%s: %s", test.desc, diff) + } + } +} diff --git a/vendor/cloud.google.com/go/httpreplay/internal/proxy/record.go b/vendor/cloud.google.com/go/httpreplay/internal/proxy/record.go new file mode 100644 index 0000000000..1816c53e5c --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/internal/proxy/record.go @@ -0,0 +1,190 @@ +// 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. + +// +build go1.8 + +// The proxy package provides a record/replay HTTP proxy. It is designed to support +// both an in-memory API (cloud.google.com/go/httpreplay) and a standalone server +// (cloud.google.com/go/httpreplay/cmd/httpr). +package proxy + +// See github.com/google/martian/cmd/proxy/main.go for the origin of much of this. + +import ( + "crypto/tls" + "crypto/x509" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "strings" + "time" + + "github.com/google/martian" + "github.com/google/martian/fifo" + "github.com/google/martian/httpspec" + "github.com/google/martian/martianlog" + "github.com/google/martian/mitm" +) + +// A Proxy is an HTTP proxy that supports recording or replaying requests. +type Proxy struct { + // The certificate that the proxy uses to participate in TLS. + CACert *x509.Certificate + + // The URL of the proxy. + URL *url.URL + + // Initial state of the client. + Initial []byte + + mproxy *martian.Proxy + filename string // for log + logger *Logger // for recording only + ignoreHeaders map[string]bool // headers the user has asked to ignore +} + +// ForRecording returns a Proxy configured to record. +func ForRecording(filename string, port int) (*Proxy, error) { + p, err := newProxy(filename) + if err != nil { + return nil, err + } + + // Construct a group that performs the standard proxy stack of request/response + // modifications. + stack, _ := httpspec.NewStack("httpr") // second arg is an internal group that we don't need + p.mproxy.SetRequestModifier(stack) + p.mproxy.SetResponseModifier(stack) + + // Make a group for logging requests and responses. + logGroup := fifo.NewGroup() + skipAuth := skipLoggingByHost("accounts.google.com") + logGroup.AddRequestModifier(skipAuth) + logGroup.AddResponseModifier(skipAuth) + p.logger = NewLogger() + logGroup.AddRequestModifier(p.logger) + logGroup.AddResponseModifier(p.logger) + + stack.AddRequestModifier(logGroup) + stack.AddResponseModifier(logGroup) + + // Ordinary debug logging. + logger := martianlog.NewLogger() + logger.SetDecode(true) + stack.AddRequestModifier(logger) + stack.AddResponseModifier(logger) + + if err := p.start(port); err != nil { + return nil, err + } + return p, nil +} + +type hideTransport http.Transport + +func (t *hideTransport) RoundTrip(req *http.Request) (*http.Response, error) { + return (*http.Transport)(t).RoundTrip(req) +} + +func newProxy(filename string) (*Proxy, error) { + mproxy := martian.NewProxy() + // Set up a man-in-the-middle configuration with a CA certificate so the proxy can + // participate in TLS. + x509c, priv, err := mitm.NewAuthority("cloud.google.com/go/httpreplay", "HTTPReplay Authority", time.Hour) + if err != nil { + return nil, err + } + mc, err := mitm.NewConfig(x509c, priv) + if err != nil { + return nil, err + } + mc.SetValidity(time.Hour) + mc.SetOrganization("cloud.google.com/go/httpreplay") + mc.SkipTLSVerify(false) + if err != nil { + return nil, err + } + mproxy.SetMITM(mc) + ih := map[string]bool{} + for k, v := range ignoreHeaders { + ih[k] = v + } + return &Proxy{ + mproxy: mproxy, + CACert: x509c, + filename: filename, + ignoreHeaders: ih, + }, nil +} + +func (p *Proxy) start(port int) error { + l, err := net.Listen("tcp4", fmt.Sprintf(":%d", port)) + if err != nil { + return err + } + p.URL = &url.URL{Scheme: "http", Host: l.Addr().String()} + go p.mproxy.Serve(l) + return nil +} + +// Transport returns an http.Transport for clients who want to talk to the proxy. +func (p *Proxy) Transport() *http.Transport { + caCertPool := x509.NewCertPool() + caCertPool.AddCert(p.CACert) + return &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: caCertPool}, + Proxy: func(*http.Request) (*url.URL, error) { return p.URL, nil }, + } +} + +// IgnoreHeader will cause h to be ignored during matching on replay. +func (p *Proxy) IgnoreHeader(h string) { + p.ignoreHeaders[http.CanonicalHeaderKey(h)] = true +} + +// Close closes the proxy. If the proxy is recording, it also writes the log. +func (p *Proxy) Close() error { + p.mproxy.Close() + if p.logger != nil { + return p.writeLog() + } + return nil +} + +func (p *Proxy) writeLog() error { + lg := p.logger.Extract() + lg.Initial = p.Initial + bytes, err := json.MarshalIndent(lg, "", " ") + if err != nil { + return err + } + return ioutil.WriteFile(p.filename, bytes, 0600) // only accessible by owner +} + +// skipLoggingByHost disables logging for traffic to a particular host. +type skipLoggingByHost string + +func (s skipLoggingByHost) ModifyRequest(req *http.Request) error { + if strings.HasPrefix(req.Host, string(s)) { + martian.NewContext(req).SkipLogging() + } + return nil +} + +func (s skipLoggingByHost) ModifyResponse(res *http.Response) error { + return s.ModifyRequest(res.Request) +} diff --git a/vendor/cloud.google.com/go/httpreplay/internal/proxy/replay.go b/vendor/cloud.google.com/go/httpreplay/internal/proxy/replay.go new file mode 100644 index 0000000000..e02f1d085f --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/internal/proxy/replay.go @@ -0,0 +1,311 @@ +// 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. + +// +build go1.8 + +package proxy + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "mime" + "mime/multipart" + "net/http" + "reflect" + "strings" + + "github.com/google/martian/martianlog" +) + +// ForReplaying returns a Proxy configured to replay. +func ForReplaying(filename string, port int) (*Proxy, error) { + p, err := newProxy(filename) + if err != nil { + return nil, err + } + calls, initial, err := readLog(filename) + if err != nil { + return nil, err + } + p.mproxy.SetRoundTripper(replayRoundTripper{ + calls: calls, + ignoreHeaders: p.ignoreHeaders, + }) + p.Initial = initial + + // Debug logging. + // TODO(jba): factor out from here and ForRecording. + logger := martianlog.NewLogger() + logger.SetDecode(true) + p.mproxy.SetRequestModifier(logger) + p.mproxy.SetResponseModifier(logger) + + if err := p.start(port); err != nil { + return nil, err + } + return p, nil +} + +// A call is an HTTP request and its matching response. +type call struct { + req *Request + reqBody *requestBody // parsed request body + res *Response +} + +func readLog(filename string) ([]*call, []byte, error) { + bytes, err := ioutil.ReadFile(filename) + if err != nil { + return nil, nil, err + } + var lg Log + if err := json.Unmarshal(bytes, &lg); err != nil { + return nil, nil, fmt.Errorf("%s: %v", filename, err) + } + if lg.Version != LogVersion { + return nil, nil, fmt.Errorf("httpreplay proxy: read log version %s but current version is %s", + lg.Version, LogVersion) + } + ignoreIDs := map[string]bool{} // IDs of requests to ignore + callsByID := map[string]*call{} + var calls []*call + for _, e := range lg.Entries { + if ignoreIDs[e.ID] { + continue + } + c, ok := callsByID[e.ID] + switch { + case !ok: + if e.Request == nil { + return nil, nil, fmt.Errorf("first entry for ID %s does not have a request", e.ID) + } + if e.Request.Method == "CONNECT" { + // Ignore CONNECT methods. + ignoreIDs[e.ID] = true + } else { + reqBody, err := newRequestBodyFromLog(e.Request) + if err != nil { + return nil, nil, err + } + c := &call{e.Request, reqBody, e.Response} + calls = append(calls, c) + callsByID[e.ID] = c + } + case e.Request != nil: + if e.Response != nil { + return nil, nil, errors.New("HAR entry has both request and response") + } + c.req = e.Request + case e.Response != nil: + c.res = e.Response + default: + return nil, nil, errors.New("HAR entry has neither request nor response") + } + } + for _, c := range calls { + if c.req == nil || c.res == nil { + return nil, nil, fmt.Errorf("missing request or response: %+v", c) + } + } + return calls, lg.Initial, nil +} + +type replayRoundTripper struct { + calls []*call + ignoreHeaders map[string]bool +} + +func (r replayRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + reqBody, err := newRequestBodyFromHTTP(req) + if err != nil { + return nil, err + } + for i, call := range r.calls { + if call == nil { + continue + } + if requestsMatch(req, reqBody, call.req, call.reqBody, r.ignoreHeaders) { + r.calls[i] = nil // nil out this call so we don't reuse it + return toHTTPResponse(call.res, req), nil + } + } + return nil, fmt.Errorf("no matching request for %+v", req) +} + +// Headers that shouldn't be compared, because they may differ on different executions +// of the same code, or may not be present during record or replay. +var ignoreHeaders = map[string]bool{} + +func init() { + // Sensitive headers are redacted in the log, so they won't be equal to incoming values. + for h := range sensitiveHeaders { + ignoreHeaders[h] = true + } + for _, h := range []string{ + "Content-Type", // handled by requestBody + "Connection", + "Date", + "Host", + "Transfer-Encoding", + "Via", + "X-Forwarded-For", + "X-Forwarded-Host", + "X-Forwarded-Proto", + "X-Forwarded-Url", + "X-Cloud-Trace-Context", // OpenCensus traces have a random ID + "X-Goog-Api-Client", // can differ for, e.g., different Go versions + } { + ignoreHeaders[h] = true + } +} + +// Report whether the incoming request in matches the candidate request cand. +func requestsMatch(in *http.Request, inBody *requestBody, cand *Request, candBody *requestBody, ignoreHeaders map[string]bool) bool { + if in.Method != cand.Method { + return false + } + if in.URL.String() != cand.URL { + return false + } + if !inBody.equal(candBody) { + return false + } + // Check headers last. See DebugHeaders. + return headersMatch(in.Header, cand.Header, ignoreHeaders) +} + +// A requestBody represents the body of a request. If the content type is multipart, the +// body is split into parts. +// +// The replaying proxy needs to understand multipart bodies because the boundaries are +// generated randomly, so we can't just compare the entire bodies for equality. +type requestBody struct { + mediaType string // the media type part of the Content-Type header + parts [][]byte // the parts of the body, or just a single []byte if not multipart +} + +func newRequestBodyFromHTTP(req *http.Request) (*requestBody, error) { + defer req.Body.Close() + return newRequestBody(req.Header.Get("Content-Type"), req.Body) +} + +func newRequestBodyFromLog(req *Request) (*requestBody, error) { + if req.Body == nil { + return nil, nil + } + return newRequestBody(req.Header.Get("Content-Type"), bytes.NewReader(req.Body)) +} + +// newRequestBody parses the Content-Type header, reads the body, and splits it into +// parts if necessary. +func newRequestBody(contentType string, body io.Reader) (*requestBody, error) { + if contentType == "" { + // No content-type header. There should not be a body. + if _, err := body.Read(make([]byte, 1)); err != io.EOF { + return nil, errors.New("no Content-Type, but body") + } + return nil, nil + } + mediaType, params, err := mime.ParseMediaType(contentType) + if err != nil { + return nil, err + } + rb := &requestBody{mediaType: mediaType} + if strings.HasPrefix(mediaType, "multipart/") { + mr := multipart.NewReader(body, params["boundary"]) + for { + p, err := mr.NextPart() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + part, err := ioutil.ReadAll(p) + if err != nil { + return nil, err + } + // TODO(jba): care about part headers? + rb.parts = append(rb.parts, part) + } + } else { + bytes, err := ioutil.ReadAll(body) + if err != nil { + return nil, err + } + rb.parts = [][]byte{bytes} + } + return rb, nil +} + +func (r1 *requestBody) equal(r2 *requestBody) bool { + if r1 == nil || r2 == nil { + return r1 == r2 + } + if r1.mediaType != r2.mediaType { + return false + } + if len(r1.parts) != len(r2.parts) { + return false + } + for i, p1 := range r1.parts { + if !bytes.Equal(p1, r2.parts[i]) { + return false + } + } + return true +} + +// DebugHeaders helps to determine whether a header should be ignored. +// When true, if requests have the same method, URL and body but differ +// in a header, the first mismatched header is logged. +var DebugHeaders = false + +func headersMatch(in, cand http.Header, ignores map[string]bool) bool { + for k1, v1 := range in { + if ignores[k1] { + continue + } + v2 := cand[k1] + if v2 == nil { + if DebugHeaders { + log.Printf("header %s: present in incoming request but not candidate", k1) + } + return false + } + if !reflect.DeepEqual(v1, v2) { + if DebugHeaders { + log.Printf("header %s: incoming %v, candidate %v", k1, v1, v2) + } + return false + } + } + for k2 := range cand { + if ignores[k2] { + continue + } + if in[k2] == nil { + if DebugHeaders { + log.Printf("header %s: not in incoming request but present in candidate", k2) + } + return false + } + } + return true +} diff --git a/vendor/cloud.google.com/go/httpreplay/internal/proxy/replay_test.go b/vendor/cloud.google.com/go/httpreplay/internal/proxy/replay_test.go new file mode 100644 index 0000000000..0d0cb6e776 --- /dev/null +++ b/vendor/cloud.google.com/go/httpreplay/internal/proxy/replay_test.go @@ -0,0 +1,114 @@ +// 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. + +// +build go1.8 + +package proxy + +import ( + "io/ioutil" + "net/http" + "strings" + "testing" + + "cloud.google.com/go/internal/testutil" + "github.com/google/go-cmp/cmp" +) + +func TestRequestBody(t *testing.T) { + req1 := &http.Request{ + Header: http.Header{"Content-Type": {"multipart/mixed; boundary=foo"}}, + Body: ioutil.NopCloser(strings.NewReader( + "--foo\r\nFoo: one\r\n\r\nA section\r\n" + + "--foo\r\nFoo: two\r\n\r\nAnd another\r\n" + + "--foo--\r\n")), + } + rb1, err := newRequestBodyFromHTTP(req1) + if err != nil { + t.Fatal(err) + } + want := &requestBody{ + mediaType: "multipart/mixed", + parts: [][]byte{ + []byte("A section"), + []byte("And another"), + }, + } + if diff := testutil.Diff(rb1, want, cmp.AllowUnexported(requestBody{})); diff != "" { + t.Error(diff) + } + + // Same contents, different boundary. + req2 := &http.Request{ + Header: http.Header{"Content-Type": {"multipart/mixed; boundary=bar"}}, + Body: ioutil.NopCloser(strings.NewReader( + "--bar\r\nFoo: one\r\n\r\nA section\r\n" + + "--bar\r\nFoo: two\r\n\r\nAnd another\r\n" + + "--bar--\r\n")), + } + rb2, err := newRequestBodyFromHTTP(req2) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(rb1, want, cmp.AllowUnexported(requestBody{})); diff != "" { + t.Error(diff) + } + + if !rb1.equal(rb2) { + t.Error("equal returned false, want true") + } +} + +func TestHeadersMatch(t *testing.T) { + for _, test := range []struct { + h1, h2 http.Header + want bool + }{ + { + http.Header{"A": {"x"}, "B": {"y", "z"}}, + http.Header{"A": {"x"}, "B": {"y", "z"}}, + true, + }, + { + http.Header{"A": {"x"}, "B": {"y", "z"}}, + http.Header{"A": {"x"}, "B": {"w"}}, + false, + }, + { + http.Header{"A": {"x"}, "B": {"y", "z"}, "I": {"foo"}}, + http.Header{"A": {"x"}, "B": {"y", "z"}, "I": {"bar"}}, + true, + }, + { + http.Header{"A": {"x"}, "B": {"y", "z"}}, + http.Header{"A": {"x"}, "B": {"y", "z"}, "I": {"bar"}}, + true, + }, + { + http.Header{"A": {"x"}, "B": {"y", "z"}, "I": {"foo"}}, + http.Header{"A": {"x"}, "I": {"bar"}}, + false, + }, + { + http.Header{"A": {"x"}, "I": {"foo"}}, + http.Header{"A": {"x"}, "B": {"y", "z"}, "I": {"bar"}}, + false, + }, + } { + got := headersMatch(test.h1, test.h2, map[string]bool{"I": true}) + if got != test.want { + t.Errorf("%v, %v: got %t, want %t", test.h1, test.h2, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/iam/admin/apiv1/doc.go b/vendor/cloud.google.com/go/iam/admin/apiv1/doc.go new file mode 100644 index 0000000000..10cfdf3dff --- /dev/null +++ b/vendor/cloud.google.com/go/iam/admin/apiv1/doc.go @@ -0,0 +1,49 @@ +// 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 admin is an auto-generated package for the +// Google Identity and Access Management (IAM) API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Manages identity and access control for Google Cloud Platform resources, +// including the creation of service accounts, which you can use to +// authenticate to Google and make API calls. +package admin // import "cloud.google.com/go/iam/admin/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/iam", + } +} diff --git a/vendor/cloud.google.com/go/iam/admin/apiv1/iam_client.go b/vendor/cloud.google.com/go/iam/admin/apiv1/iam_client.go new file mode 100644 index 0000000000..84c3e6a348 --- /dev/null +++ b/vendor/cloud.google.com/go/iam/admin/apiv1/iam_client.go @@ -0,0 +1,476 @@ +// 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 admin + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + adminpb "google.golang.org/genproto/googleapis/iam/admin/v1" + iampb "google.golang.org/genproto/googleapis/iam/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// IamCallOptions contains the retry settings for each method of IamClient. +type IamCallOptions struct { + ListServiceAccounts []gax.CallOption + GetServiceAccount []gax.CallOption + CreateServiceAccount []gax.CallOption + UpdateServiceAccount []gax.CallOption + DeleteServiceAccount []gax.CallOption + ListServiceAccountKeys []gax.CallOption + GetServiceAccountKey []gax.CallOption + CreateServiceAccountKey []gax.CallOption + DeleteServiceAccountKey []gax.CallOption + SignBlob []gax.CallOption + GetIamPolicy []gax.CallOption + SetIamPolicy []gax.CallOption + TestIamPermissions []gax.CallOption + QueryGrantableRoles []gax.CallOption + SignJwt []gax.CallOption +} + +func defaultIamClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("iam.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultIamCallOptions() *IamCallOptions { + 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 &IamCallOptions{ + ListServiceAccounts: retry[[2]string{"default", "idempotent"}], + GetServiceAccount: retry[[2]string{"default", "idempotent"}], + CreateServiceAccount: retry[[2]string{"default", "non_idempotent"}], + UpdateServiceAccount: retry[[2]string{"default", "idempotent"}], + DeleteServiceAccount: retry[[2]string{"default", "idempotent"}], + ListServiceAccountKeys: retry[[2]string{"default", "idempotent"}], + GetServiceAccountKey: retry[[2]string{"default", "idempotent"}], + CreateServiceAccountKey: retry[[2]string{"default", "non_idempotent"}], + DeleteServiceAccountKey: retry[[2]string{"default", "idempotent"}], + SignBlob: retry[[2]string{"default", "non_idempotent"}], + GetIamPolicy: retry[[2]string{"default", "non_idempotent"}], + SetIamPolicy: retry[[2]string{"default", "non_idempotent"}], + TestIamPermissions: retry[[2]string{"default", "non_idempotent"}], + QueryGrantableRoles: retry[[2]string{"default", "non_idempotent"}], + SignJwt: retry[[2]string{"default", "non_idempotent"}], + } +} + +// IamClient is a client for interacting with Google Identity and Access Management (IAM) API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type IamClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + iamClient adminpb.IAMClient + + // The call options for this service. + CallOptions *IamCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewIamClient creates a new iam client. +// +// Creates and manages service account objects. +// +// Service account is an account that belongs to your project instead +// of to an individual end user. It is used to authenticate calls +// to a Google API. +// +// To create a service account, specify the project_id and account_id +// for the account. The account_id is unique within the project, and used +// to generate the service account email address and a stable +// unique_id. +// +// All other methods can identify accounts using the format +// projects/{PROJECT_ID}/serviceAccounts/{SERVICE_ACCOUNT_EMAIL}. +// Using - as a wildcard for the project will infer the project from +// the account. The account value can be the email address or the +// unique_id of the service account. +func NewIamClient(ctx context.Context, opts ...option.ClientOption) (*IamClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultIamClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &IamClient{ + conn: conn, + CallOptions: defaultIamCallOptions(), + + iamClient: adminpb.NewIAMClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *IamClient) 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 *IamClient) 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 *IamClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListServiceAccounts lists [ServiceAccounts][google.iam.admin.v1.ServiceAccount] for a project. +func (c *IamClient) ListServiceAccounts(ctx context.Context, req *adminpb.ListServiceAccountsRequest, opts ...gax.CallOption) *ServiceAccountIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListServiceAccounts[0:len(c.CallOptions.ListServiceAccounts):len(c.CallOptions.ListServiceAccounts)], opts...) + it := &ServiceAccountIterator{} + req = proto.Clone(req).(*adminpb.ListServiceAccountsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*adminpb.ServiceAccount, string, error) { + var resp *adminpb.ListServiceAccountsResponse + 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.iamClient.ListServiceAccounts(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Accounts, 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 +} + +// GetServiceAccount gets a [ServiceAccount][google.iam.admin.v1.ServiceAccount]. +func (c *IamClient) GetServiceAccount(ctx context.Context, req *adminpb.GetServiceAccountRequest, opts ...gax.CallOption) (*adminpb.ServiceAccount, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetServiceAccount[0:len(c.CallOptions.GetServiceAccount):len(c.CallOptions.GetServiceAccount)], opts...) + var resp *adminpb.ServiceAccount + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.GetServiceAccount(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateServiceAccount creates a [ServiceAccount][google.iam.admin.v1.ServiceAccount] +// and returns it. +func (c *IamClient) CreateServiceAccount(ctx context.Context, req *adminpb.CreateServiceAccountRequest, opts ...gax.CallOption) (*adminpb.ServiceAccount, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateServiceAccount[0:len(c.CallOptions.CreateServiceAccount):len(c.CallOptions.CreateServiceAccount)], opts...) + var resp *adminpb.ServiceAccount + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.CreateServiceAccount(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateServiceAccount updates a [ServiceAccount][google.iam.admin.v1.ServiceAccount]. +// +// Currently, only the following fields are updatable: +// display_name . +// The etag is mandatory. +func (c *IamClient) UpdateServiceAccount(ctx context.Context, req *adminpb.ServiceAccount, opts ...gax.CallOption) (*adminpb.ServiceAccount, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateServiceAccount[0:len(c.CallOptions.UpdateServiceAccount):len(c.CallOptions.UpdateServiceAccount)], opts...) + var resp *adminpb.ServiceAccount + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.UpdateServiceAccount(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteServiceAccount deletes a [ServiceAccount][google.iam.admin.v1.ServiceAccount]. +func (c *IamClient) DeleteServiceAccount(ctx context.Context, req *adminpb.DeleteServiceAccountRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteServiceAccount[0:len(c.CallOptions.DeleteServiceAccount):len(c.CallOptions.DeleteServiceAccount)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.iamClient.DeleteServiceAccount(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListServiceAccountKeys lists [ServiceAccountKeys][google.iam.admin.v1.ServiceAccountKey]. +func (c *IamClient) ListServiceAccountKeys(ctx context.Context, req *adminpb.ListServiceAccountKeysRequest, opts ...gax.CallOption) (*adminpb.ListServiceAccountKeysResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListServiceAccountKeys[0:len(c.CallOptions.ListServiceAccountKeys):len(c.CallOptions.ListServiceAccountKeys)], opts...) + var resp *adminpb.ListServiceAccountKeysResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.ListServiceAccountKeys(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetServiceAccountKey gets the [ServiceAccountKey][google.iam.admin.v1.ServiceAccountKey] +// by key id. +func (c *IamClient) GetServiceAccountKey(ctx context.Context, req *adminpb.GetServiceAccountKeyRequest, opts ...gax.CallOption) (*adminpb.ServiceAccountKey, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetServiceAccountKey[0:len(c.CallOptions.GetServiceAccountKey):len(c.CallOptions.GetServiceAccountKey)], opts...) + var resp *adminpb.ServiceAccountKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.GetServiceAccountKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateServiceAccountKey creates a [ServiceAccountKey][google.iam.admin.v1.ServiceAccountKey] +// and returns it. +func (c *IamClient) CreateServiceAccountKey(ctx context.Context, req *adminpb.CreateServiceAccountKeyRequest, opts ...gax.CallOption) (*adminpb.ServiceAccountKey, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateServiceAccountKey[0:len(c.CallOptions.CreateServiceAccountKey):len(c.CallOptions.CreateServiceAccountKey)], opts...) + var resp *adminpb.ServiceAccountKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.CreateServiceAccountKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteServiceAccountKey deletes a [ServiceAccountKey][google.iam.admin.v1.ServiceAccountKey]. +func (c *IamClient) DeleteServiceAccountKey(ctx context.Context, req *adminpb.DeleteServiceAccountKeyRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteServiceAccountKey[0:len(c.CallOptions.DeleteServiceAccountKey):len(c.CallOptions.DeleteServiceAccountKey)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.iamClient.DeleteServiceAccountKey(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// SignBlob signs a blob using a service account's system-managed private key. +func (c *IamClient) SignBlob(ctx context.Context, req *adminpb.SignBlobRequest, opts ...gax.CallOption) (*adminpb.SignBlobResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SignBlob[0:len(c.CallOptions.SignBlob):len(c.CallOptions.SignBlob)], opts...) + var resp *adminpb.SignBlobResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.SignBlob(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// getIamPolicy returns the IAM access control policy for a +// [ServiceAccount][google.iam.admin.v1.ServiceAccount]. +func (c *IamClient) getIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.iamClient.GetIamPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// setIamPolicy sets the IAM access control policy for a +// [ServiceAccount][google.iam.admin.v1.ServiceAccount]. +func (c *IamClient) setIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.iamClient.SetIamPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// TestIamPermissions tests the specified permissions against the IAM access control policy +// for a [ServiceAccount][google.iam.admin.v1.ServiceAccount]. +func (c *IamClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.iamClient.TestIamPermissions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// QueryGrantableRoles queries roles that can be granted on a particular resource. +// A role is grantable if it can be used as the role in a binding for a policy +// for that resource. +func (c *IamClient) QueryGrantableRoles(ctx context.Context, req *adminpb.QueryGrantableRolesRequest, opts ...gax.CallOption) (*adminpb.QueryGrantableRolesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.QueryGrantableRoles[0:len(c.CallOptions.QueryGrantableRoles):len(c.CallOptions.QueryGrantableRoles)], opts...) + var resp *adminpb.QueryGrantableRolesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.QueryGrantableRoles(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SignJwt signs a JWT using a service account's system-managed private key. +// +// If no expiry time (exp) is provided in the SignJwtRequest, IAM sets an +// an expiry time of one hour by default. If you request an expiry time of +// more than one hour, the request will fail. +func (c *IamClient) SignJwt(ctx context.Context, req *adminpb.SignJwtRequest, opts ...gax.CallOption) (*adminpb.SignJwtResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SignJwt[0:len(c.CallOptions.SignJwt):len(c.CallOptions.SignJwt)], opts...) + var resp *adminpb.SignJwtResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.iamClient.SignJwt(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ServiceAccountIterator manages a stream of *adminpb.ServiceAccount. +type ServiceAccountIterator struct { + items []*adminpb.ServiceAccount + 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 []*adminpb.ServiceAccount, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *ServiceAccountIterator) 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 *ServiceAccountIterator) Next() (*adminpb.ServiceAccount, error) { + var item *adminpb.ServiceAccount + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *ServiceAccountIterator) bufLen() int { + return len(it.items) +} + +func (it *ServiceAccountIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/iam/admin/apiv1/iam_client_example_test.go b/vendor/cloud.google.com/go/iam/admin/apiv1/iam_client_example_test.go new file mode 100644 index 0000000000..c9ead18b4b --- /dev/null +++ b/vendor/cloud.google.com/go/iam/admin/apiv1/iam_client_example_test.go @@ -0,0 +1,271 @@ +// 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 admin_test + +import ( + "cloud.google.com/go/iam/admin/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + adminpb "google.golang.org/genproto/googleapis/iam/admin/v1" + iampb "google.golang.org/genproto/googleapis/iam/v1" +) + +func ExampleNewIamClient() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleIamClient_ListServiceAccounts() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.ListServiceAccountsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListServiceAccounts(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleIamClient_GetServiceAccount() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.GetServiceAccountRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetServiceAccount(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_CreateServiceAccount() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.CreateServiceAccountRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateServiceAccount(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_UpdateServiceAccount() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.ServiceAccount{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateServiceAccount(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_DeleteServiceAccount() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.DeleteServiceAccountRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteServiceAccount(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleIamClient_ListServiceAccountKeys() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.ListServiceAccountKeysRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListServiceAccountKeys(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_GetServiceAccountKey() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.GetServiceAccountKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetServiceAccountKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_CreateServiceAccountKey() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.CreateServiceAccountKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateServiceAccountKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_DeleteServiceAccountKey() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.DeleteServiceAccountKeyRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteServiceAccountKey(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleIamClient_SignBlob() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.SignBlobRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SignBlob(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_TestIamPermissions() { + ctx := context.Background() + c, err := admin.NewIamClient(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 ExampleIamClient_QueryGrantableRoles() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.QueryGrantableRolesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.QueryGrantableRoles(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleIamClient_SignJwt() { + ctx := context.Background() + c, err := admin.NewIamClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &adminpb.SignJwtRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SignJwt(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/iam/admin/apiv1/mock_test.go b/vendor/cloud.google.com/go/iam/admin/apiv1/mock_test.go new file mode 100644 index 0000000000..d4f5ef7ed1 --- /dev/null +++ b/vendor/cloud.google.com/go/iam/admin/apiv1/mock_test.go @@ -0,0 +1,1223 @@ +// 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 admin + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + adminpb "google.golang.org/genproto/googleapis/iam/admin/v1" + iampb "google.golang.org/genproto/googleapis/iam/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockIamServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + adminpb.IAMServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockIamServer) ListServiceAccounts(ctx context.Context, req *adminpb.ListServiceAccountsRequest) (*adminpb.ListServiceAccountsResponse, 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].(*adminpb.ListServiceAccountsResponse), nil +} + +func (s *mockIamServer) GetServiceAccount(ctx context.Context, req *adminpb.GetServiceAccountRequest) (*adminpb.ServiceAccount, 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].(*adminpb.ServiceAccount), nil +} + +func (s *mockIamServer) CreateServiceAccount(ctx context.Context, req *adminpb.CreateServiceAccountRequest) (*adminpb.ServiceAccount, 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].(*adminpb.ServiceAccount), nil +} + +func (s *mockIamServer) UpdateServiceAccount(ctx context.Context, req *adminpb.ServiceAccount) (*adminpb.ServiceAccount, 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].(*adminpb.ServiceAccount), nil +} + +func (s *mockIamServer) DeleteServiceAccount(ctx context.Context, req *adminpb.DeleteServiceAccountRequest) (*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 *mockIamServer) ListServiceAccountKeys(ctx context.Context, req *adminpb.ListServiceAccountKeysRequest) (*adminpb.ListServiceAccountKeysResponse, 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].(*adminpb.ListServiceAccountKeysResponse), nil +} + +func (s *mockIamServer) GetServiceAccountKey(ctx context.Context, req *adminpb.GetServiceAccountKeyRequest) (*adminpb.ServiceAccountKey, 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].(*adminpb.ServiceAccountKey), nil +} + +func (s *mockIamServer) CreateServiceAccountKey(ctx context.Context, req *adminpb.CreateServiceAccountKeyRequest) (*adminpb.ServiceAccountKey, 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].(*adminpb.ServiceAccountKey), nil +} + +func (s *mockIamServer) DeleteServiceAccountKey(ctx context.Context, req *adminpb.DeleteServiceAccountKeyRequest) (*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 *mockIamServer) SignBlob(ctx context.Context, req *adminpb.SignBlobRequest) (*adminpb.SignBlobResponse, 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].(*adminpb.SignBlobResponse), nil +} + +func (s *mockIamServer) SignJwt(ctx context.Context, req *adminpb.SignJwtRequest) (*adminpb.SignJwtResponse, 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].(*adminpb.SignJwtResponse), nil +} + +func (s *mockIamServer) 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 *mockIamServer) 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 *mockIamServer) 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 *mockIamServer) QueryGrantableRoles(ctx context.Context, req *adminpb.QueryGrantableRolesRequest) (*adminpb.QueryGrantableRolesResponse, 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].(*adminpb.QueryGrantableRolesResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockIam mockIamServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + adminpb.RegisterIAMServer(serv, &mockIam) + + 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 TestIamListServiceAccounts(t *testing.T) { + var nextPageToken string = "" + var accountsElement *adminpb.ServiceAccount = &adminpb.ServiceAccount{} + var accounts = []*adminpb.ServiceAccount{accountsElement} + var expectedResponse = &adminpb.ListServiceAccountsResponse{ + NextPageToken: nextPageToken, + Accounts: accounts, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &adminpb.ListServiceAccountsRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListServiceAccounts(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Accounts[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 TestIamListServiceAccountsError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &adminpb.ListServiceAccountsRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListServiceAccounts(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 TestIamGetServiceAccount(t *testing.T) { + var name2 string = "name2-1052831874" + var projectId string = "projectId-1969970175" + var uniqueId string = "uniqueId-538310583" + var email string = "email96619420" + var displayName string = "displayName1615086568" + var etag []byte = []byte("21") + var oauth2ClientId string = "oauth2ClientId-1833466037" + var expectedResponse = &adminpb.ServiceAccount{ + Name: name2, + ProjectId: projectId, + UniqueId: uniqueId, + Email: email, + DisplayName: displayName, + Etag: etag, + Oauth2ClientId: oauth2ClientId, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.GetServiceAccountRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetServiceAccount(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamGetServiceAccountError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.GetServiceAccountRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetServiceAccount(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 TestIamCreateServiceAccount(t *testing.T) { + var name2 string = "name2-1052831874" + var projectId string = "projectId-1969970175" + var uniqueId string = "uniqueId-538310583" + var email string = "email96619420" + var displayName string = "displayName1615086568" + var etag []byte = []byte("21") + var oauth2ClientId string = "oauth2ClientId-1833466037" + var expectedResponse = &adminpb.ServiceAccount{ + Name: name2, + ProjectId: projectId, + UniqueId: uniqueId, + Email: email, + DisplayName: displayName, + Etag: etag, + Oauth2ClientId: oauth2ClientId, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var accountId string = "accountId-803333011" + var request = &adminpb.CreateServiceAccountRequest{ + Name: formattedName, + AccountId: accountId, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateServiceAccount(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamCreateServiceAccountError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var accountId string = "accountId-803333011" + var request = &adminpb.CreateServiceAccountRequest{ + Name: formattedName, + AccountId: accountId, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateServiceAccount(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 TestIamUpdateServiceAccount(t *testing.T) { + var name string = "name3373707" + var projectId string = "projectId-1969970175" + var uniqueId string = "uniqueId-538310583" + var email string = "email96619420" + var displayName string = "displayName1615086568" + var etag2 []byte = []byte("-120") + var oauth2ClientId string = "oauth2ClientId-1833466037" + var expectedResponse = &adminpb.ServiceAccount{ + Name: name, + ProjectId: projectId, + UniqueId: uniqueId, + Email: email, + DisplayName: displayName, + Etag: etag2, + Oauth2ClientId: oauth2ClientId, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var etag []byte = []byte("21") + var request = &adminpb.ServiceAccount{ + Etag: etag, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateServiceAccount(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamUpdateServiceAccountError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var etag []byte = []byte("21") + var request = &adminpb.ServiceAccount{ + Etag: etag, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateServiceAccount(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 TestIamDeleteServiceAccount(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.DeleteServiceAccountRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteServiceAccount(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestIamDeleteServiceAccountError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.DeleteServiceAccountRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteServiceAccount(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 TestIamListServiceAccountKeys(t *testing.T) { + var expectedResponse *adminpb.ListServiceAccountKeysResponse = &adminpb.ListServiceAccountKeysResponse{} + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.ListServiceAccountKeysRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListServiceAccountKeys(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamListServiceAccountKeysError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.ListServiceAccountKeysRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListServiceAccountKeys(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 TestIamGetServiceAccountKey(t *testing.T) { + var name2 string = "name2-1052831874" + var privateKeyData []byte = []byte("-58") + var publicKeyData []byte = []byte("-96") + var expectedResponse = &adminpb.ServiceAccountKey{ + Name: name2, + PrivateKeyData: privateKeyData, + PublicKeyData: publicKeyData, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s/keys/%s", "[PROJECT]", "[SERVICE_ACCOUNT]", "[KEY]") + var request = &adminpb.GetServiceAccountKeyRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetServiceAccountKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamGetServiceAccountKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s/keys/%s", "[PROJECT]", "[SERVICE_ACCOUNT]", "[KEY]") + var request = &adminpb.GetServiceAccountKeyRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetServiceAccountKey(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 TestIamCreateServiceAccountKey(t *testing.T) { + var name2 string = "name2-1052831874" + var privateKeyData []byte = []byte("-58") + var publicKeyData []byte = []byte("-96") + var expectedResponse = &adminpb.ServiceAccountKey{ + Name: name2, + PrivateKeyData: privateKeyData, + PublicKeyData: publicKeyData, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.CreateServiceAccountKeyRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateServiceAccountKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamCreateServiceAccountKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &adminpb.CreateServiceAccountKeyRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateServiceAccountKey(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 TestIamDeleteServiceAccountKey(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s/keys/%s", "[PROJECT]", "[SERVICE_ACCOUNT]", "[KEY]") + var request = &adminpb.DeleteServiceAccountKeyRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteServiceAccountKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestIamDeleteServiceAccountKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s/keys/%s", "[PROJECT]", "[SERVICE_ACCOUNT]", "[KEY]") + var request = &adminpb.DeleteServiceAccountKeyRequest{ + Name: formattedName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteServiceAccountKey(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 TestIamSignBlob(t *testing.T) { + var keyId string = "keyId-1134673157" + var signature []byte = []byte("-72") + var expectedResponse = &adminpb.SignBlobResponse{ + KeyId: keyId, + Signature: signature, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var bytesToSign []byte = []byte("45") + var request = &adminpb.SignBlobRequest{ + Name: formattedName, + BytesToSign: bytesToSign, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SignBlob(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamSignBlobError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var bytesToSign []byte = []byte("45") + var request = &adminpb.SignBlobRequest{ + Name: formattedName, + BytesToSign: bytesToSign, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SignBlob(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 TestIamGetIamPolicy(t *testing.T) { + var version int32 = 351608024 + var etag []byte = []byte("21") + var expectedResponse = &iampb.Policy{ + Version: version, + Etag: etag, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &iampb.GetIamPolicyRequest{ + Resource: formattedResource, + } + + c, err := NewIamClient(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, mockIam.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 TestIamGetIamPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var request = &iampb.GetIamPolicyRequest{ + Resource: formattedResource, + } + + c, err := NewIamClient(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 TestIamSetIamPolicy(t *testing.T) { + var version int32 = 351608024 + var etag []byte = []byte("21") + var expectedResponse = &iampb.Policy{ + Version: version, + Etag: etag, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var policy *iampb.Policy = &iampb.Policy{} + var request = &iampb.SetIamPolicyRequest{ + Resource: formattedResource, + Policy: policy, + } + + c, err := NewIamClient(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, mockIam.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 TestIamSetIamPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var policy *iampb.Policy = &iampb.Policy{} + var request = &iampb.SetIamPolicyRequest{ + Resource: formattedResource, + Policy: policy, + } + + c, err := NewIamClient(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 TestIamTestIamPermissions(t *testing.T) { + var expectedResponse *iampb.TestIamPermissionsResponse = &iampb.TestIamPermissionsResponse{} + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var permissions []string = nil + var request = &iampb.TestIamPermissionsRequest{ + Resource: formattedResource, + Permissions: permissions, + } + + c, err := NewIamClient(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, mockIam.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 TestIamTestIamPermissionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/serviceAccounts/%s", "[PROJECT]", "[SERVICE_ACCOUNT]") + var permissions []string = nil + var request = &iampb.TestIamPermissionsRequest{ + Resource: formattedResource, + Permissions: permissions, + } + + c, err := NewIamClient(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 TestIamQueryGrantableRoles(t *testing.T) { + var nextPageToken string = "nextPageToken-1530815211" + var expectedResponse = &adminpb.QueryGrantableRolesResponse{ + NextPageToken: nextPageToken, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var fullResourceName string = "fullResourceName1300993644" + var request = &adminpb.QueryGrantableRolesRequest{ + FullResourceName: fullResourceName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.QueryGrantableRoles(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamQueryGrantableRolesError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var fullResourceName string = "fullResourceName1300993644" + var request = &adminpb.QueryGrantableRolesRequest{ + FullResourceName: fullResourceName, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.QueryGrantableRoles(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 TestIamSignJwt(t *testing.T) { + var keyId string = "keyId-1134673157" + var signedJwt string = "signedJwt-979546844" + var expectedResponse = &adminpb.SignJwtResponse{ + KeyId: keyId, + SignedJwt: signedJwt, + } + + mockIam.err = nil + mockIam.reqs = nil + + mockIam.resps = append(mockIam.resps[:0], expectedResponse) + + var name string = "name3373707" + var payload string = "payload-786701938" + var request = &adminpb.SignJwtRequest{ + Name: name, + Payload: payload, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SignJwt(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockIam.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 TestIamSignJwtError(t *testing.T) { + errCode := codes.PermissionDenied + mockIam.err = gstatus.Error(errCode, "test error") + + var name string = "name3373707" + var payload string = "payload-786701938" + var request = &adminpb.SignJwtRequest{ + Name: name, + Payload: payload, + } + + c, err := NewIamClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SignJwt(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/vendor/cloud.google.com/go/iam/admin/apiv1/path_funcs.go b/vendor/cloud.google.com/go/iam/admin/apiv1/path_funcs.go new file mode 100644 index 0000000000..9818266ffd --- /dev/null +++ b/vendor/cloud.google.com/go/iam/admin/apiv1/path_funcs.go @@ -0,0 +1,45 @@ +// 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 admin + +// IamProjectPath returns the path for the project resource. +func IamProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// IamServiceAccountPath returns the path for the service account resource. +func IamServiceAccountPath(project, serviceAccount string) string { + return "" + + "projects/" + + project + + "/serviceAccounts/" + + serviceAccount + + "" +} + +// IamKeyPath returns the path for the key resource. +func IamKeyPath(project, serviceAccount, key string) string { + return "" + + "projects/" + + project + + "/serviceAccounts/" + + serviceAccount + + "/keys/" + + key + + "" +} diff --git a/vendor/cloud.google.com/go/iam/admin/apiv1/policy_methods.go b/vendor/cloud.google.com/go/iam/admin/apiv1/policy_methods.go new file mode 100644 index 0000000000..fe31df1794 --- /dev/null +++ b/vendor/cloud.google.com/go/iam/admin/apiv1/policy_methods.go @@ -0,0 +1,52 @@ +// 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. + +// This is handwritten code. These methods are implemented by hand so they can use +// the iam.Policy type. + +package admin + +import ( + "cloud.google.com/go/iam" + "golang.org/x/net/context" + iampb "google.golang.org/genproto/googleapis/iam/v1" +) + +// GetIamPolicy returns the IAM access control policy for a ServiceAccount. +func (c *IamClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest) (*iam.Policy, error) { + policy, err := c.getIamPolicy(ctx, req) + if err != nil { + return nil, err + } + return &iam.Policy{InternalProto: policy}, nil +} + +// SetIamPolicyRequest is the request type for the SetIamPolicy method. +type SetIamPolicyRequest struct { + Resource string + Policy *iam.Policy +} + +// SetIamPolicy sets the IAM access control policy for a ServiceAccount. +func (c *IamClient) SetIamPolicy(ctx context.Context, req *SetIamPolicyRequest) (*iam.Policy, error) { + preq := &iampb.SetIamPolicyRequest{ + Resource: req.Resource, + Policy: req.Policy.InternalProto, + } + policy, err := c.setIamPolicy(ctx, preq) + if err != nil { + return nil, err + } + return &iam.Policy{InternalProto: policy}, nil +} diff --git a/vendor/cloud.google.com/go/iam/iam.go b/vendor/cloud.google.com/go/iam/iam.go new file mode 100644 index 0000000000..87d468a812 --- /dev/null +++ b/vendor/cloud.google.com/go/iam/iam.go @@ -0,0 +1,292 @@ +// 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 iam supports the resource-specific operations of Google Cloud +// IAM (Identity and Access Management) for the Google Cloud Libraries. +// See https://cloud.google.com/iam for more about IAM. +// +// Users of the Google Cloud Libraries will typically not use this package +// directly. Instead they will begin with some resource that supports IAM, like +// a pubsub topic, and call its IAM method to get a Handle for that resource. +package iam + +import ( + "time" + + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/iam/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// client abstracts the IAMPolicy API to allow multiple implementations. +type client interface { + Get(ctx context.Context, resource string) (*pb.Policy, error) + Set(ctx context.Context, resource string, p *pb.Policy) error + Test(ctx context.Context, resource string, perms []string) ([]string, error) +} + +// grpcClient implements client for the standard gRPC-based IAMPolicy service. +type grpcClient struct { + c pb.IAMPolicyClient +} + +var withRetry = gax.WithRetry(func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.DeadlineExceeded, + codes.Unavailable, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 60 * time.Second, + Multiplier: 1.3, + }) +}) + +func (g *grpcClient) Get(ctx context.Context, resource string) (*pb.Policy, error) { + var proto *pb.Policy + err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error { + var err error + proto, err = g.c.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{Resource: resource}) + return err + }, withRetry) + if err != nil { + return nil, err + } + return proto, nil +} + +func (g *grpcClient) Set(ctx context.Context, resource string, p *pb.Policy) error { + return gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error { + _, err := g.c.SetIamPolicy(ctx, &pb.SetIamPolicyRequest{ + Resource: resource, + Policy: p, + }) + return err + }, withRetry) +} + +func (g *grpcClient) Test(ctx context.Context, resource string, perms []string) ([]string, error) { + var res *pb.TestIamPermissionsResponse + err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error { + var err error + res, err = g.c.TestIamPermissions(ctx, &pb.TestIamPermissionsRequest{ + Resource: resource, + Permissions: perms, + }) + return err + }, withRetry) + if err != nil { + return nil, err + } + return res.Permissions, nil +} + +// A Handle provides IAM operations for a resource. +type Handle struct { + c client + resource string +} + +// InternalNewHandle is for use by the Google Cloud Libraries only. +// +// InternalNewHandle returns a Handle for resource. +// The conn parameter refers to a server that must support the IAMPolicy service. +func InternalNewHandle(conn *grpc.ClientConn, resource string) *Handle { + return InternalNewHandleGRPCClient(pb.NewIAMPolicyClient(conn), resource) +} + +// InternalNewHandleGRPCClient is for use by the Google Cloud Libraries only. +// +// InternalNewHandleClient returns a Handle for resource using the given +// grpc service that implements IAM as a mixin +func InternalNewHandleGRPCClient(c pb.IAMPolicyClient, resource string) *Handle { + return InternalNewHandleClient(&grpcClient{c: c}, resource) +} + +// InternalNewHandleClient is for use by the Google Cloud Libraries only. +// +// InternalNewHandleClient returns a Handle for resource using the given +// client implementation. +func InternalNewHandleClient(c client, resource string) *Handle { + return &Handle{ + c: c, + resource: resource, + } +} + +// Policy retrieves the IAM policy for the resource. +func (h *Handle) Policy(ctx context.Context) (*Policy, error) { + proto, err := h.c.Get(ctx, h.resource) + if err != nil { + return nil, err + } + return &Policy{InternalProto: proto}, nil +} + +// SetPolicy replaces the resource's current policy with the supplied Policy. +// +// If policy was created from a prior call to Get, then the modification will +// only succeed if the policy has not changed since the Get. +func (h *Handle) SetPolicy(ctx context.Context, policy *Policy) error { + return h.c.Set(ctx, h.resource, policy.InternalProto) +} + +// TestPermissions returns the subset of permissions that the caller has on the resource. +func (h *Handle) TestPermissions(ctx context.Context, permissions []string) ([]string, error) { + return h.c.Test(ctx, h.resource, permissions) +} + +// A RoleName is a name representing a collection of permissions. +type RoleName string + +// Common role names. +const ( + Owner RoleName = "roles/owner" + Editor RoleName = "roles/editor" + Viewer RoleName = "roles/viewer" +) + +const ( + // AllUsers is a special member that denotes all users, even unauthenticated ones. + AllUsers = "allUsers" + + // AllAuthenticatedUsers is a special member that denotes all authenticated users. + AllAuthenticatedUsers = "allAuthenticatedUsers" +) + +// A Policy is a list of Bindings representing roles +// granted to members. +// +// The zero Policy is a valid policy with no bindings. +type Policy struct { + // TODO(jba): when type aliases are available, put Policy into an internal package + // and provide an exported alias here. + + // This field is exported for use by the Google Cloud Libraries only. + // It may become unexported in a future release. + InternalProto *pb.Policy +} + +// Members returns the list of members with the supplied role. +// The return value should not be modified. Use Add and Remove +// to modify the members of a role. +func (p *Policy) Members(r RoleName) []string { + b := p.binding(r) + if b == nil { + return nil + } + return b.Members +} + +// HasRole reports whether member has role r. +func (p *Policy) HasRole(member string, r RoleName) bool { + return memberIndex(member, p.binding(r)) >= 0 +} + +// Add adds member member to role r if it is not already present. +// A new binding is created if there is no binding for the role. +func (p *Policy) Add(member string, r RoleName) { + b := p.binding(r) + if b == nil { + if p.InternalProto == nil { + p.InternalProto = &pb.Policy{} + } + p.InternalProto.Bindings = append(p.InternalProto.Bindings, &pb.Binding{ + Role: string(r), + Members: []string{member}, + }) + return + } + if memberIndex(member, b) < 0 { + b.Members = append(b.Members, member) + return + } +} + +// Remove removes member from role r if it is present. +func (p *Policy) Remove(member string, r RoleName) { + bi := p.bindingIndex(r) + if bi < 0 { + return + } + bindings := p.InternalProto.Bindings + b := bindings[bi] + mi := memberIndex(member, b) + if mi < 0 { + return + } + // Order doesn't matter for bindings or members, so to remove, move the last item + // into the removed spot and shrink the slice. + if len(b.Members) == 1 { + // Remove binding. + last := len(bindings) - 1 + bindings[bi] = bindings[last] + bindings[last] = nil + p.InternalProto.Bindings = bindings[:last] + return + } + // Remove member. + // TODO(jba): worry about multiple copies of m? + last := len(b.Members) - 1 + b.Members[mi] = b.Members[last] + b.Members[last] = "" + b.Members = b.Members[:last] +} + +// Roles returns the names of all the roles that appear in the Policy. +func (p *Policy) Roles() []RoleName { + if p.InternalProto == nil { + return nil + } + var rns []RoleName + for _, b := range p.InternalProto.Bindings { + rns = append(rns, RoleName(b.Role)) + } + return rns +} + +// binding returns the Binding for the suppied role, or nil if there isn't one. +func (p *Policy) binding(r RoleName) *pb.Binding { + i := p.bindingIndex(r) + if i < 0 { + return nil + } + return p.InternalProto.Bindings[i] +} + +func (p *Policy) bindingIndex(r RoleName) int { + if p.InternalProto == nil { + return -1 + } + for i, b := range p.InternalProto.Bindings { + if b.Role == string(r) { + return i + } + } + return -1 +} + +// memberIndex returns the index of m in b's Members, or -1 if not found. +func memberIndex(m string, b *pb.Binding) int { + if b == nil { + return -1 + } + for i, mm := range b.Members { + if mm == m { + return i + } + } + return -1 +} diff --git a/vendor/cloud.google.com/go/iam/iam_test.go b/vendor/cloud.google.com/go/iam/iam_test.go new file mode 100644 index 0000000000..67a9f05c0c --- /dev/null +++ b/vendor/cloud.google.com/go/iam/iam_test.go @@ -0,0 +1,87 @@ +// 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 iam + +import ( + "fmt" + "sort" + "testing" + + "cloud.google.com/go/internal/testutil" +) + +func TestPolicy(t *testing.T) { + p := &Policy{} + + add := func(member string, role RoleName) { + p.Add(member, role) + } + remove := func(member string, role RoleName) { + p.Remove(member, role) + } + + if msg, ok := checkMembers(p, Owner, nil); !ok { + t.Fatal(msg) + } + add("m1", Owner) + if msg, ok := checkMembers(p, Owner, []string{"m1"}); !ok { + t.Fatal(msg) + } + add("m2", Owner) + if msg, ok := checkMembers(p, Owner, []string{"m1", "m2"}); !ok { + t.Fatal(msg) + } + add("m1", Owner) // duplicate adds ignored + if msg, ok := checkMembers(p, Owner, []string{"m1", "m2"}); !ok { + t.Fatal(msg) + } + // No other roles populated yet. + if msg, ok := checkMembers(p, Viewer, nil); !ok { + t.Fatal(msg) + } + remove("m1", Owner) + if msg, ok := checkMembers(p, Owner, []string{"m2"}); !ok { + t.Fatal(msg) + } + if msg, ok := checkMembers(p, Viewer, nil); !ok { + t.Fatal(msg) + } + remove("m3", Owner) // OK to remove non-existent member. + if msg, ok := checkMembers(p, Owner, []string{"m2"}); !ok { + t.Fatal(msg) + } + remove("m2", Owner) + if msg, ok := checkMembers(p, Owner, nil); !ok { + t.Fatal(msg) + } + if got, want := p.Roles(), []RoleName(nil); !testutil.Equal(got, want) { + t.Fatalf("roles: got %v, want %v", got, want) + } +} + +func checkMembers(p *Policy, role RoleName, wantMembers []string) (string, bool) { + gotMembers := p.Members(role) + sort.Strings(gotMembers) + sort.Strings(wantMembers) + if !testutil.Equal(gotMembers, wantMembers) { + return fmt.Sprintf("got %v, want %v", gotMembers, wantMembers), false + } + for _, m := range wantMembers { + if !p.HasRole(m, role) { + return fmt.Sprintf("member %q should have role %s but does not", m, role), false + } + } + return "", true +} diff --git a/vendor/cloud.google.com/go/import_test.go b/vendor/cloud.google.com/go/import_test.go new file mode 100644 index 0000000000..e8b16cd6f1 --- /dev/null +++ b/vendor/cloud.google.com/go/import_test.go @@ -0,0 +1,61 @@ +// Copyright 2017 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 + +import ( + "go/parser" + "go/token" + "os" + "path/filepath" + "strconv" + "testing" +) + +func TestContextImport(t *testing.T) { + t.Parallel() + + whiteList := map[string]bool{ + "storage/go17.go": true, + } + + err := filepath.Walk(".", func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + if filepath.Ext(path) != ".go" || whiteList[path] { + return nil + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly) + if err != nil { + return err + } + + for _, imp := range file.Imports { + impPath, err := strconv.Unquote(imp.Path.Value) + if err != nil { + return err + } + if impPath == "context" { + t.Errorf(`file %q import "context", want "golang.org/x/net/context"`, path) + } + } + return nil + }) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/cloud.google.com/go/internal/annotate.go b/vendor/cloud.google.com/go/internal/annotate.go new file mode 100644 index 0000000000..6435695ba3 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/annotate.go @@ -0,0 +1,54 @@ +// Copyright 2017 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 internal + +import ( + "fmt" + + "google.golang.org/api/googleapi" + "google.golang.org/grpc/status" +) + +// Annotate prepends msg to the error message in err, attempting +// to preserve other information in err, like an error code. +// +// Annotate panics if err is nil. +// +// Annotate knows about these error types: +// - "google.golang.org/grpc/status".Status +// - "google.golang.org/api/googleapi".Error +// If the error is not one of these types, Annotate behaves +// like +// fmt.Errorf("%s: %v", msg, err) +func Annotate(err error, msg string) error { + if err == nil { + panic("Annotate called with nil") + } + if s, ok := status.FromError(err); ok { + p := s.Proto() + p.Message = msg + ": " + p.Message + return status.ErrorProto(p) + } + if g, ok := err.(*googleapi.Error); ok { + g.Message = msg + ": " + g.Message + return g + } + return fmt.Errorf("%s: %v", msg, err) +} + +// Annotatef uses format and args to format a string, then calls Annotate. +func Annotatef(err error, format string, args ...interface{}) error { + return Annotate(err, fmt.Sprintf(format, args...)) +} diff --git a/vendor/cloud.google.com/go/internal/annotate_test.go b/vendor/cloud.google.com/go/internal/annotate_test.go new file mode 100644 index 0000000000..7203f51a93 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/annotate_test.go @@ -0,0 +1,65 @@ +// Copyright 2017 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 internal + +import ( + "errors" + "testing" + + "google.golang.org/api/googleapi" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const wantMessage = "prefix: msg" + +func TestAnnotateGRPC(t *testing.T) { + // grpc Status error + err := status.Error(codes.NotFound, "msg") + err = Annotate(err, "prefix") + got, ok := status.FromError(err) + if !ok { + t.Fatalf("got %T, wanted a status", got) + } + if g, w := got.Code(), codes.NotFound; g != w { + t.Errorf("got code %v, want %v", g, w) + } + if g, w := got.Message(), wantMessage; g != w { + t.Errorf("got message %q, want %q", g, w) + } +} + +func TestAnnotateGoogleapi(t *testing.T) { + // googleapi error + var err error = &googleapi.Error{Code: 403, Message: "msg"} + err = Annotate(err, "prefix") + got2, ok := err.(*googleapi.Error) + if !ok { + t.Fatalf("got %T, wanted a googleapi.Error", got2) + } + if g, w := got2.Code, 403; g != w { + t.Errorf("got code %d, want %d", g, w) + } + if g, w := got2.Message, wantMessage; g != w { + t.Errorf("got message %q, want %q", g, w) + } +} + +func TestAnnotateUnknownError(t *testing.T) { + err := Annotate(errors.New("msg"), "prefix") + if g, w := err.Error(), wantMessage; g != w { + t.Errorf("got message %q, want %q", g, w) + } +} diff --git a/vendor/cloud.google.com/go/internal/atomiccache/atomiccache.go b/vendor/cloud.google.com/go/internal/atomiccache/atomiccache.go new file mode 100644 index 0000000000..c965438eb2 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/atomiccache/atomiccache.go @@ -0,0 +1,58 @@ +// 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 atomiccache provides a map-based cache that supports very fast +// reads. +package atomiccache + +import ( + "sync" + "sync/atomic" +) + +type mapType map[interface{}]interface{} + +// Cache is a map-based cache that supports fast reads via use of atomics. +// Writes are slow, requiring a copy of the entire cache. +// The zero Cache is an empty cache, ready for use. +type Cache struct { + val atomic.Value // mapType + mu sync.Mutex // used only by writers +} + +// Get returns the value of the cache at key. If there is no value, +// getter is called to provide one, and the cache is updated. +// The getter function may be called concurrently. It should be pure, +// returning the same value for every call. +func (c *Cache) Get(key interface{}, getter func() interface{}) interface{} { + mp, _ := c.val.Load().(mapType) + if v, ok := mp[key]; ok { + return v + } + + // Compute value without lock. + // Might duplicate effort but won't hold other computations back. + newV := getter() + + c.mu.Lock() + mp, _ = c.val.Load().(mapType) + newM := make(mapType, len(mp)+1) + for k, v := range mp { + newM[k] = v + } + newM[key] = newV + c.val.Store(newM) + c.mu.Unlock() + return newV +} diff --git a/vendor/cloud.google.com/go/internal/atomiccache/atomiccache_test.go b/vendor/cloud.google.com/go/internal/atomiccache/atomiccache_test.go new file mode 100644 index 0000000000..835e5e7df4 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/atomiccache/atomiccache_test.go @@ -0,0 +1,46 @@ +// 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 atomiccache + +import ( + "fmt" + "testing" +) + +func TestGet(t *testing.T) { + var c Cache + called := false + get := func(k interface{}) interface{} { + return c.Get(k, func() interface{} { + called = true + return fmt.Sprintf("v%d", k) + }) + } + got := get(1) + if want := "v1"; got != want { + t.Errorf("got %v, want %v", got, want) + } + if !called { + t.Error("getter not called, expected a call") + } + called = false + got = get(1) + if want := "v1"; got != want { + t.Errorf("got %v, want %v", got, want) + } + if called { + t.Error("getter unexpectedly called") + } +} diff --git a/vendor/cloud.google.com/go/internal/btree/README.md b/vendor/cloud.google.com/go/internal/btree/README.md new file mode 100644 index 0000000000..601ff544a5 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/btree/README.md @@ -0,0 +1,11 @@ +This package is a fork of github.com/jba/btree at commit +d4edd57f39b8425fc2c631047ff4dc6024d82a4f, which itself was a fork of +github.com/google/btree at 316fb6d3f031ae8f4d457c6c5186b9e3ded70435. + +This directory makes the following modifications: + +- Updated copyright notice. +- removed LICENSE (it is the same as the repo-wide license, Apache 2.0) +- Removed examples_test.go and .travis.yml. +- Added this file. + diff --git a/vendor/cloud.google.com/go/internal/btree/benchmarks_test.go b/vendor/cloud.google.com/go/internal/btree/benchmarks_test.go new file mode 100644 index 0000000000..cc3da577d4 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/btree/benchmarks_test.go @@ -0,0 +1,268 @@ +// 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. + +// +build go1.7 + +package btree + +import ( + "fmt" + "sort" + "testing" +) + +const benchmarkTreeSize = 10000 + +var degrees = []int{2, 8, 32, 64} + +func BenchmarkInsert(b *testing.B) { + insertP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + i := 0 + for i < b.N { + tr := New(d, less) + for _, m := range insertP { + tr.Set(m.Key, m.Value) + i++ + if i >= b.N { + return + } + } + } + }) + } +} + +func BenchmarkDeleteInsert(b *testing.B) { + insertP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + tr := New(d, less) + for _, m := range insertP { + tr.Set(m.Key, m.Value) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + m := insertP[i%benchmarkTreeSize] + tr.Delete(m.Key) + tr.Set(m.Key, m.Value) + } + }) + } +} + +func BenchmarkDeleteInsertCloneOnce(b *testing.B) { + insertP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + tr := New(d, less) + for _, m := range insertP { + tr.Set(m.Key, m.Value) + } + tr = tr.Clone() + b.ResetTimer() + for i := 0; i < b.N; i++ { + m := insertP[i%benchmarkTreeSize] + tr.Delete(m.Key) + tr.Set(m.Key, m.Value) + } + }) + } +} + +func BenchmarkDeleteInsertCloneEachTime(b *testing.B) { + insertP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + tr := New(d, less) + for _, m := range insertP { + tr.Set(m.Key, m.Value) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + tr = tr.Clone() + m := insertP[i%benchmarkTreeSize] + tr.Delete(m.Key) + tr.Set(m.Key, m.Value) + } + }) + } +} + +func BenchmarkDelete(b *testing.B) { + insertP := perm(benchmarkTreeSize) + removeP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + i := 0 + for i < b.N { + b.StopTimer() + tr := New(d, less) + for _, v := range insertP { + tr.Set(v.Key, v.Value) + } + b.StartTimer() + for _, m := range removeP { + tr.Delete(m.Key) + i++ + if i >= b.N { + return + } + } + if tr.Len() > 0 { + panic(tr.Len()) + } + } + }) + } +} + +func BenchmarkGet(b *testing.B) { + insertP := perm(benchmarkTreeSize) + getP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + i := 0 + for i < b.N { + b.StopTimer() + tr := New(d, less) + for _, v := range insertP { + tr.Set(v.Key, v.Value) + } + b.StartTimer() + for _, m := range getP { + tr.Get(m.Key) + i++ + if i >= b.N { + return + } + } + } + }) + } +} + +func BenchmarkGetWithIndex(b *testing.B) { + insertP := perm(benchmarkTreeSize) + getP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + i := 0 + for i < b.N { + b.StopTimer() + tr := New(d, less) + for _, v := range insertP { + tr.Set(v.Key, v.Value) + } + b.StartTimer() + for _, m := range getP { + tr.GetWithIndex(m.Key) + i++ + if i >= b.N { + return + } + } + } + }) + } +} + +func BenchmarkGetCloneEachTime(b *testing.B) { + insertP := perm(benchmarkTreeSize) + getP := perm(benchmarkTreeSize) + for _, d := range degrees { + b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) { + i := 0 + for i < b.N { + b.StopTimer() + tr := New(d, less) + for _, m := range insertP { + tr.Set(m.Key, m.Value) + } + b.StartTimer() + for _, m := range getP { + tr = tr.Clone() + tr.Get(m.Key) + i++ + if i >= b.N { + return + } + } + } + }) + } +} + +func BenchmarkFind(b *testing.B) { + for _, d := range degrees { + var items []item + for i := 0; i < 2*d; i++ { + items = append(items, item{i, i}) + } + b.Run(fmt.Sprintf("size=%d", len(items)), func(b *testing.B) { + for _, alg := range []struct { + name string + fun func(Key, []item) (int, bool) + }{ + {"binary", findBinary}, + {"linear", findLinear}, + } { + b.Run(alg.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + for j := 0; j < len(items); j++ { + alg.fun(items[j].key, items) + } + } + }) + } + }) + } +} + +func findBinary(k Key, s []item) (int, bool) { + i := sort.Search(len(s), func(i int) bool { return less(k, s[i].key) }) + // i is the smallest index of s for which key.Less(s[i].Key), or len(s). + if i > 0 && !less(s[i-1], k) { + return i - 1, true + } + return i, false +} + +func findLinear(k Key, s []item) (int, bool) { + var i int + for i = 0; i < len(s); i++ { + if less(k, s[i].key) { + break + } + } + if i > 0 && !less(s[i-1].key, k) { + return i - 1, true + } + return i, false +} + +type byInts []item + +func (a byInts) Len() int { + return len(a) +} + +func (a byInts) Less(i, j int) bool { + return a[i].key.(int) < a[j].key.(int) +} + +func (a byInts) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} diff --git a/vendor/cloud.google.com/go/internal/btree/btree.go b/vendor/cloud.google.com/go/internal/btree/btree.go new file mode 100644 index 0000000000..b507748006 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/btree/btree.go @@ -0,0 +1,1018 @@ +// Copyright 2014 Google LLC +// Modified 2018 by Jonathan Amsterdam (jbamsterdam@gmail.com) +// +// 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 btree implements in-memory B-Trees of arbitrary degree. +// +// This implementation is based on google/btree (http://github.com/google/btree), and +// much of the code is taken from there. But the API has been changed significantly, +// particularly around iteration, and support for indexing by position has been +// added. +// +// btree implements an in-memory B-Tree for use as an ordered data structure. +// It is not meant for persistent storage solutions. +// +// It has a flatter structure than an equivalent red-black or other binary tree, +// which in some cases yields better memory usage and/or performance. +// See some discussion on the matter here: +// http://google-opensource.blogspot.com/2013/01/c-containers-that-save-memory-and-time.html +// Note, though, that this project is in no way related to the C++ B-Tree +// implementation written about there. +// +// Within this tree, each node contains a slice of items and a (possibly nil) +// slice of children. For basic numeric values or raw structs, this can cause +// efficiency differences when compared to equivalent C++ template code that +// stores values in arrays within the node: +// * Due to the overhead of storing values as interfaces (each +// value needs to be stored as the value itself, then 2 words for the +// interface pointing to that value and its type), resulting in higher +// memory use. +// * Since interfaces can point to values anywhere in memory, values are +// most likely not stored in contiguous blocks, resulting in a higher +// number of cache misses. +// These issues don't tend to matter, though, when working with strings or other +// heap-allocated structures, since C++-equivalent structures also must store +// pointers and also distribute their values across the heap. +package btree + +import ( + "fmt" + "sort" + "sync" +) + +// Key represents a key into the tree. +type Key interface{} + +type Value interface{} + +// item is a key-value pair. +type item struct { + key Key + value Value +} + +type lessFunc func(interface{}, interface{}) bool + +// New creates a new B-Tree with the given degree and comparison function. +// +// New(2, less), for example, will create a 2-3-4 tree (each node contains 1-3 items +// and 2-4 children). +// +// The less function tests whether the current item is less than the given argument. +// It must provide a strict weak ordering. +// If !less(a, b) && !less(b, a), we treat this to mean a == b (i.e. the tree +// can hold only one of a or b). +func New(degree int, less func(interface{}, interface{}) bool) *BTree { + if degree <= 1 { + panic("bad degree") + } + return &BTree{ + degree: degree, + less: less, + cow: ©OnWriteContext{}, + } +} + +// items stores items in a node. +type items []item + +// insertAt inserts a value into the given index, pushing all subsequent values +// forward. +func (s *items) insertAt(index int, m item) { + *s = append(*s, item{}) + if index < len(*s) { + copy((*s)[index+1:], (*s)[index:]) + } + (*s)[index] = m +} + +// removeAt removes a value at a given index, pulling all subsequent values +// back. +func (s *items) removeAt(index int) item { + m := (*s)[index] + copy((*s)[index:], (*s)[index+1:]) + (*s)[len(*s)-1] = item{} + *s = (*s)[:len(*s)-1] + return m +} + +// pop removes and returns the last element in the list. +func (s *items) pop() item { + index := len(*s) - 1 + out := (*s)[index] + (*s)[index] = item{} + *s = (*s)[:index] + return out +} + +var nilItems = make(items, 16) + +// truncate truncates this instance at index so that it contains only the +// first index items. index must be less than or equal to length. +func (s *items) truncate(index int) { + var toClear items + *s, toClear = (*s)[:index], (*s)[index:] + for len(toClear) > 0 { + toClear = toClear[copy(toClear, nilItems):] + } +} + +// find returns the index where an item with key should be inserted into this +// list. 'found' is true if the item already exists in the list at the given +// index. +func (s items) find(k Key, less lessFunc) (index int, found bool) { + i := sort.Search(len(s), func(i int) bool { return less(k, s[i].key) }) + // i is the smallest index of s for which k.Less(s[i].Key), or len(s). + if i > 0 && !less(s[i-1].key, k) { + return i - 1, true + } + return i, false +} + +// children stores child nodes in a node. +type children []*node + +// insertAt inserts a value into the given index, pushing all subsequent values +// forward. +func (s *children) insertAt(index int, n *node) { + *s = append(*s, nil) + if index < len(*s) { + copy((*s)[index+1:], (*s)[index:]) + } + (*s)[index] = n +} + +// removeAt removes a value at a given index, pulling all subsequent values +// back. +func (s *children) removeAt(index int) *node { + n := (*s)[index] + copy((*s)[index:], (*s)[index+1:]) + (*s)[len(*s)-1] = nil + *s = (*s)[:len(*s)-1] + return n +} + +// pop removes and returns the last element in the list. +func (s *children) pop() (out *node) { + index := len(*s) - 1 + out = (*s)[index] + (*s)[index] = nil + *s = (*s)[:index] + return +} + +var nilChildren = make(children, 16) + +// truncate truncates this instance at index so that it contains only the +// first index children. index must be less than or equal to length. +func (s *children) truncate(index int) { + var toClear children + *s, toClear = (*s)[:index], (*s)[index:] + for len(toClear) > 0 { + toClear = toClear[copy(toClear, nilChildren):] + } +} + +// node is an internal node in a tree. +// +// It must at all times maintain the invariant that either +// * len(children) == 0, len(items) unconstrained +// * len(children) == len(items) + 1 +type node struct { + items items + children children + size int // number of items in the subtree: len(items) + sum over i of children[i].size + cow *copyOnWriteContext +} + +func (n *node) computeSize() int { + sz := len(n.items) + for _, c := range n.children { + sz += c.size + } + return sz +} + +func (n *node) checkSize() { + sz := n.computeSize() + if n.size != sz { + panic(fmt.Sprintf("n.size = %d, computed size = %d", n.size, sz)) + } +} + +func (n *node) mutableFor(cow *copyOnWriteContext) *node { + if n.cow == cow { + return n + } + out := cow.newNode() + if cap(out.items) >= len(n.items) { + out.items = out.items[:len(n.items)] + } else { + out.items = make(items, len(n.items), cap(n.items)) + } + copy(out.items, n.items) + // Copy children + if cap(out.children) >= len(n.children) { + out.children = out.children[:len(n.children)] + } else { + out.children = make(children, len(n.children), cap(n.children)) + } + copy(out.children, n.children) + out.size = n.size + return out +} + +func (n *node) mutableChild(i int) *node { + c := n.children[i].mutableFor(n.cow) + n.children[i] = c + return c +} + +// split splits the given node at the given index. The current node shrinks, +// and this function returns the item that existed at that index and a new node +// containing all items/children after it. +func (n *node) split(i int) (item, *node) { + item := n.items[i] + next := n.cow.newNode() + next.items = append(next.items, n.items[i+1:]...) + n.items.truncate(i) + if len(n.children) > 0 { + next.children = append(next.children, n.children[i+1:]...) + n.children.truncate(i + 1) + } + n.size = n.computeSize() + next.size = next.computeSize() + return item, next +} + +// maybeSplitChild checks if a child should be split, and if so splits it. +// Returns whether or not a split occurred. +func (n *node) maybeSplitChild(i, maxItems int) bool { + if len(n.children[i].items) < maxItems { + return false + } + first := n.mutableChild(i) + item, second := first.split(maxItems / 2) + n.items.insertAt(i, item) + n.children.insertAt(i+1, second) + // The size of n doesn't change. + return true +} + +// insert inserts an item into the subtree rooted at this node, making sure +// no nodes in the subtree exceed maxItems items. Should an equivalent item be +// be found/replaced by insert, its value will be returned. +// +// If computeIndex is true, the third return value is the index of the value with respect to n. +func (n *node) insert(m item, maxItems int, less lessFunc, computeIndex bool) (old Value, present bool, idx int) { + i, found := n.items.find(m.key, less) + if found { + out := n.items[i] + n.items[i] = m + if computeIndex { + idx = n.itemIndex(i) + } + return out.value, true, idx + } + if len(n.children) == 0 { + n.items.insertAt(i, m) + n.size++ + return old, false, i + } + if n.maybeSplitChild(i, maxItems) { + inTree := n.items[i] + switch { + case less(m.key, inTree.key): + // no change, we want first split node + case less(inTree.key, m.key): + i++ // we want second split node + default: + out := n.items[i] + n.items[i] = m + if computeIndex { + idx = n.itemIndex(i) + } + return out.value, true, idx + } + } + old, present, idx = n.mutableChild(i).insert(m, maxItems, less, computeIndex) + if !present { + n.size++ + } + if computeIndex { + idx += n.partialSize(i) + } + return old, present, idx +} + +// get finds the given key in the subtree and returns the corresponding item, along with a boolean reporting +// whether it was found. +// If computeIndex is true, it also returns the index of the key relative to the node's subtree. +func (n *node) get(k Key, computeIndex bool, less lessFunc) (item, bool, int) { + i, found := n.items.find(k, less) + if found { + return n.items[i], true, n.itemIndex(i) + } + if len(n.children) > 0 { + m, found, idx := n.children[i].get(k, computeIndex, less) + if computeIndex && found { + idx += n.partialSize(i) + } + return m, found, idx + } + return item{}, false, -1 +} + +// itemIndex returns the index w.r.t. n of the ith item in n. +func (n *node) itemIndex(i int) int { + if len(n.children) == 0 { + return i + } + // Get the size of the node up to but not including the child to the right of + // item i. Subtract 1 because the index is 0-based. + return n.partialSize(i+1) - 1 +} + +// Returns the size of the non-leaf node up to but not including child i. +func (n *node) partialSize(i int) int { + var sz int + for j, c := range n.children { + if j == i { + break + } + sz += c.size + 1 + } + return sz +} + +// cursorStackForKey returns a stack of cursors for the key, along with whether the key was found and the index. +func (n *node) cursorStackForKey(k Key, cs cursorStack, less lessFunc) (cursorStack, bool, int) { + i, found := n.items.find(k, less) + cs.push(cursor{n, i}) + idx := i + if found { + if len(n.children) > 0 { + idx = n.partialSize(i+1) - 1 + } + return cs, true, idx + } + if len(n.children) > 0 { + cs, found, idx := n.children[i].cursorStackForKey(k, cs, less) + return cs, found, idx + n.partialSize(i) + } + return cs, false, idx +} + +// at returns the item at the i'th position in the subtree rooted at n. +// It assumes i is in range. +func (n *node) at(i int) item { + if len(n.children) == 0 { + return n.items[i] + } + for j, c := range n.children { + if i < c.size { + return c.at(i) + } + i -= c.size + if i == 0 { + return n.items[j] + } + i-- + } + panic("impossible") +} + +// cursorStackForIndex returns a stack of cursors for the index. +// It assumes i is in range. +func (n *node) cursorStackForIndex(i int, cs cursorStack) cursorStack { + if len(n.children) == 0 { + return cs.push(cursor{n, i}) + } + for j, c := range n.children { + if i < c.size { + return c.cursorStackForIndex(i, cs.push(cursor{n, j})) + } + i -= c.size + if i == 0 { + return cs.push(cursor{n, j}) + } + i-- + } + panic("impossible") +} + +// toRemove details what item to remove in a node.remove call. +type toRemove int + +const ( + removeItem toRemove = iota // removes the given item + removeMin // removes smallest item in the subtree + removeMax // removes largest item in the subtree +) + +// remove removes an item from the subtree rooted at this node. +func (n *node) remove(key Key, minItems int, typ toRemove, less lessFunc) (item, bool) { + var i int + var found bool + switch typ { + case removeMax: + if len(n.children) == 0 { + n.size-- + return n.items.pop(), true + + } + i = len(n.items) + case removeMin: + if len(n.children) == 0 { + n.size-- + return n.items.removeAt(0), true + } + i = 0 + case removeItem: + i, found = n.items.find(key, less) + if len(n.children) == 0 { + if found { + n.size-- + return n.items.removeAt(i), true + } + return item{}, false + } + default: + panic("invalid type") + } + // If we get to here, we have children. + if len(n.children[i].items) <= minItems { + return n.growChildAndRemove(i, key, minItems, typ, less) + } + child := n.mutableChild(i) + // Either we had enough items to begin with, or we've done some + // merging/stealing, because we've got enough now and we're ready to return + // stuff. + if found { + // The item exists at index 'i', and the child we've selected can give us a + // predecessor, since if we've gotten here it's got > minItems items in it. + out := n.items[i] + // We use our special-case 'remove' call with typ=maxItem to pull the + // predecessor of item i (the rightmost leaf of our immediate left child) + // and set it into where we pulled the item from. + n.items[i], _ = child.remove(nil, minItems, removeMax, less) + n.size-- + return out, true + } + // Final recursive call. Once we're here, we know that the item isn't in this + // node and that the child is big enough to remove from. + m, removed := child.remove(key, minItems, typ, less) + if removed { + n.size-- + } + return m, removed +} + +// growChildAndRemove grows child 'i' to make sure it's possible to remove an +// item from it while keeping it at minItems, then calls remove to actually +// remove it. +// +// Most documentation says we have to do two sets of special casing: +// 1) item is in this node +// 2) item is in child +// In both cases, we need to handle the two subcases: +// A) node has enough values that it can spare one +// B) node doesn't have enough values +// For the latter, we have to check: +// a) left sibling has node to spare +// b) right sibling has node to spare +// c) we must merge +// To simplify our code here, we handle cases #1 and #2 the same: +// If a node doesn't have enough items, we make sure it does (using a,b,c). +// We then simply redo our remove call, and the second time (regardless of +// whether we're in case 1 or 2), we'll have enough items and can guarantee +// that we hit case A. +func (n *node) growChildAndRemove(i int, key Key, minItems int, typ toRemove, less lessFunc) (item, bool) { + if i > 0 && len(n.children[i-1].items) > minItems { + // Steal from left child + child := n.mutableChild(i) + stealFrom := n.mutableChild(i - 1) + stolenItem := stealFrom.items.pop() + stealFrom.size-- + child.items.insertAt(0, n.items[i-1]) + child.size++ + n.items[i-1] = stolenItem + if len(stealFrom.children) > 0 { + c := stealFrom.children.pop() + stealFrom.size -= c.size + child.children.insertAt(0, c) + child.size += c.size + } + } else if i < len(n.items) && len(n.children[i+1].items) > minItems { + // steal from right child + child := n.mutableChild(i) + stealFrom := n.mutableChild(i + 1) + stolenItem := stealFrom.items.removeAt(0) + stealFrom.size-- + child.items = append(child.items, n.items[i]) + child.size++ + n.items[i] = stolenItem + if len(stealFrom.children) > 0 { + c := stealFrom.children.removeAt(0) + stealFrom.size -= c.size + child.children = append(child.children, c) + child.size += c.size + } + } else { + if i >= len(n.items) { + i-- + } + child := n.mutableChild(i) + // merge with right child + mergeItem := n.items.removeAt(i) + mergeChild := n.children.removeAt(i + 1) + child.items = append(child.items, mergeItem) + child.items = append(child.items, mergeChild.items...) + child.children = append(child.children, mergeChild.children...) + child.size = child.computeSize() + n.cow.freeNode(mergeChild) + } + return n.remove(key, minItems, typ, less) +} + +// BTree is an implementation of a B-Tree. +// +// BTree stores item instances in an ordered structure, allowing easy insertion, +// removal, and iteration. +// +// Write operations are not safe for concurrent mutation by multiple +// goroutines, but Read operations are. +type BTree struct { + degree int + less lessFunc + root *node + cow *copyOnWriteContext +} + +// copyOnWriteContext pointers determine node ownership. A tree with a cow +// context equivalent to a node's cow context is allowed to modify that node. +// A tree whose write context does not match a node's is not allowed to modify +// it, and must create a new, writable copy (IE: it's a Clone). +// +// When doing any write operation, we maintain the invariant that the current +// node's context is equal to the context of the tree that requested the write. +// We do this by, before we descend into any node, creating a copy with the +// correct context if the contexts don't match. +// +// Since the node we're currently visiting on any write has the requesting +// tree's context, that node is modifiable in place. Children of that node may +// not share context, but before we descend into them, we'll make a mutable +// copy. +type copyOnWriteContext struct{ byte } // non-empty, because empty structs may have same addr + +// Clone clones the btree, lazily. Clone should not be called concurrently, +// but the original tree (t) and the new tree (t2) can be used concurrently +// once the Clone call completes. +// +// The internal tree structure of b is marked read-only and shared between t and +// t2. Writes to both t and t2 use copy-on-write logic, creating new nodes +// whenever one of b's original nodes would have been modified. Read operations +// should have no performance degredation. Write operations for both t and t2 +// will initially experience minor slow-downs caused by additional allocs and +// copies due to the aforementioned copy-on-write logic, but should converge to +// the original performance characteristics of the original tree. +func (t *BTree) Clone() *BTree { + // Create two entirely new copy-on-write contexts. + // This operation effectively creates three trees: + // the original, shared nodes (old b.cow) + // the new b.cow nodes + // the new out.cow nodes + cow1, cow2 := *t.cow, *t.cow + out := *t + t.cow = &cow1 + out.cow = &cow2 + return &out +} + +// maxItems returns the max number of items to allow per node. +func (t *BTree) maxItems() int { + return t.degree*2 - 1 +} + +// minItems returns the min number of items to allow per node (ignored for the +// root node). +func (t *BTree) minItems() int { + return t.degree - 1 +} + +var nodePool = sync.Pool{New: func() interface{} { return new(node) }} + +func (c *copyOnWriteContext) newNode() *node { + n := nodePool.Get().(*node) + n.cow = c + return n +} + +func (c *copyOnWriteContext) freeNode(n *node) { + if n.cow == c { + // clear to allow GC + n.items.truncate(0) + n.children.truncate(0) + n.cow = nil + nodePool.Put(n) + } +} + +// Set sets the given key to the given value in the tree. If the key is present in +// the tree, its value is changed and the old value is returned along with a second +// return value of true. If the key is not in the tree, it is added, and the second +// return value is false. +func (t *BTree) Set(k Key, v Value) (old Value, present bool) { + old, present, _ = t.set(k, v, false) + return old, present +} + +func (t *BTree) SetWithIndex(k Key, v Value) (old Value, present bool, index int) { + return t.set(k, v, true) +} + +func (t *BTree) set(k Key, v Value, computeIndex bool) (old Value, present bool, idx int) { + if t.root == nil { + t.root = t.cow.newNode() + t.root.items = append(t.root.items, item{k, v}) + t.root.size = 1 + return old, false, 0 + } + t.root = t.root.mutableFor(t.cow) + if len(t.root.items) >= t.maxItems() { + sz := t.root.size + item2, second := t.root.split(t.maxItems() / 2) + oldroot := t.root + t.root = t.cow.newNode() + t.root.items = append(t.root.items, item2) + t.root.children = append(t.root.children, oldroot, second) + t.root.size = sz + } + + return t.root.insert(item{k, v}, t.maxItems(), t.less, computeIndex) +} + +// Delete removes the item with the given key, returning its value. The second return value +// reports whether the key was found. +func (t *BTree) Delete(k Key) (Value, bool) { + m, removed := t.deleteItem(k, removeItem) + return m.value, removed +} + +// DeleteMin removes the smallest item in the tree and returns its key and value. +// If the tree is empty, it returns zero values. +func (t *BTree) DeleteMin() (Key, Value) { + item, _ := t.deleteItem(nil, removeMin) + return item.key, item.value +} + +// DeleteMax removes the largest item in the tree and returns its key and value. +// If the tree is empty, it returns zero values. +func (t *BTree) DeleteMax() (Key, Value) { + item, _ := t.deleteItem(nil, removeMax) + return item.key, item.value +} + +func (t *BTree) deleteItem(key Key, typ toRemove) (item, bool) { + if t.root == nil || len(t.root.items) == 0 { + return item{}, false + } + t.root = t.root.mutableFor(t.cow) + out, removed := t.root.remove(key, t.minItems(), typ, t.less) + if len(t.root.items) == 0 && len(t.root.children) > 0 { + oldroot := t.root + t.root = t.root.children[0] + t.cow.freeNode(oldroot) + } + return out, removed +} + +// Get returns the value for the given key in the tree, or the zero value if the +// key is not in the tree. +// +// To distinguish a zero value from a key that is not present, use GetWithIndex. +func (t *BTree) Get(k Key) Value { + var z Value + if t.root == nil { + return z + } + item, ok, _ := t.root.get(k, false, t.less) + if !ok { + return z + } + return item.value +} + +// GetWithIndex returns the value and index for the given key in the tree, or the +// zero value and -1 if the key is not in the tree. +func (t *BTree) GetWithIndex(k Key) (Value, int) { + var z Value + if t.root == nil { + return z, -1 + } + item, _, index := t.root.get(k, true, t.less) + return item.value, index +} + +// At returns the key and value at index i. The minimum item has index 0. +// If i is outside the range [0, t.Len()), At panics. +func (t *BTree) At(i int) (Key, Value) { + if i < 0 || i >= t.Len() { + panic("btree: index out of range") + } + item := t.root.at(i) + return item.key, item.value +} + +// Has reports whether the given key is in the tree. +func (t *BTree) Has(k Key) bool { + if t.root == nil { + return false + } + _, ok, _ := t.root.get(k, false, t.less) + return ok +} + +// Min returns the smallest key in the tree and its value. If the tree is empty, it +// returns zero values. +func (t *BTree) Min() (Key, Value) { + var k Key + var v Value + if t.root == nil { + return k, v + } + n := t.root + for len(n.children) > 0 { + n = n.children[0] + } + if len(n.items) == 0 { + return k, v + } + return n.items[0].key, n.items[0].value +} + +// Max returns the largest key in the tree and its value. If the tree is empty, both +// return values are zero values. +func (t *BTree) Max() (Key, Value) { + var k Key + var v Value + if t.root == nil { + return k, v + } + n := t.root + for len(n.children) > 0 { + n = n.children[len(n.children)-1] + } + if len(n.items) == 0 { + return k, v + } + m := n.items[len(n.items)-1] + return m.key, m.value +} + +// Len returns the number of items currently in the tree. +func (t *BTree) Len() int { + if t.root == nil { + return 0 + } + return t.root.size +} + +// Before returns an iterator positioned just before k. After the first call to Next, +// the Iterator will be at k, or at the key just greater than k if k is not in the tree. +// Subsequent calls to Next will traverse the tree's items in ascending order. +func (t *BTree) Before(k Key) *Iterator { + if t.root == nil { + return &Iterator{} + } + var cs cursorStack + cs, found, idx := t.root.cursorStackForKey(k, cs, t.less) + // If we found the key, the cursor stack is pointing to it. Since that is + // the first element we want, don't advance the iterator on the initial call to Next. + // If we haven't found the key, then the top of the cursor stack is either pointing at the + // item just after k, in which case we do not want to move the iterator; or the index + // is past the end of the items slice, in which case we do. + var stay bool + top := cs[len(cs)-1] + if found { + stay = true + } else if top.index < len(top.node.items) { + stay = true + } else { + idx-- + } + return &Iterator{ + cursors: cs, + stay: stay, + descending: false, + Index: idx, + } +} + +// After returns an iterator positioned just after k. After the first call to Next, +// the Iterator will be at k, or at the key just less than k if k is not in the tree. +// Subsequent calls to Next will traverse the tree's items in descending order. +func (t *BTree) After(k Key) *Iterator { + if t.root == nil { + return &Iterator{} + } + var cs cursorStack + cs, found, idx := t.root.cursorStackForKey(k, cs, t.less) + // If we found the key, the cursor stack is pointing to it. Since that is + // the first element we want, don't advance the iterator on the initial call to Next. + // If we haven't found the key, the the cursor stack is pointing just after the first item, + // so we do want to advance. + return &Iterator{ + cursors: cs, + stay: found, + descending: true, + Index: idx, + } +} + +// BeforeIndex returns an iterator positioned just before the item with the given index. +// The iterator will traverse the tree's items in ascending order. +// If i is not in the range [0, tr.Len()], BeforeIndex panics. +// Note that it is not an error to provide an index of tr.Len(). +func (t *BTree) BeforeIndex(i int) *Iterator { + return t.indexIterator(i, false) +} + +// AfterIndex returns an iterator positioned just after the item with the given index. +// The iterator will traverse the tree's items in descending order. +// If i is not in the range [0, tr.Len()], AfterIndex panics. +// Note that it is not an error to provide an index of tr.Len(). +func (t *BTree) AfterIndex(i int) *Iterator { + return t.indexIterator(i, true) +} + +func (t *BTree) indexIterator(i int, descending bool) *Iterator { + if i < 0 || i > t.Len() { + panic("btree: index out of range") + } + if i == t.Len() { + return &Iterator{} + } + var cs cursorStack + return &Iterator{ + cursors: t.root.cursorStackForIndex(i, cs), + stay: true, + descending: descending, + Index: i, + } +} + +// An Iterator supports traversing the items in the tree. +type Iterator struct { + Key Key + Value Value + // Index is the position of the item in the tree viewed as a sequence. + // The minimum item has index zero. + Index int + + cursors cursorStack // stack of nodes with indices; last element is the top + stay bool // don't do anything on the first call to Next. + descending bool // traverse the items in descending order +} + +// Next advances the Iterator to the next item in the tree. If Next returns true, +// the Iterator's Key, Value and Index fields refer to the next item. If Next returns +// false, there are no more items and the values of Key, Value and Index are undefined. +// +// If the tree is modified during iteration, the behavior is undefined. +func (it *Iterator) Next() bool { + var more bool + switch { + case len(it.cursors) == 0: + more = false + case it.stay: + it.stay = false + more = true + case it.descending: + more = it.dec() + default: + more = it.inc() + } + if !more { + return false + } + top := it.cursors[len(it.cursors)-1] + item := top.node.items[top.index] + it.Key = item.key + it.Value = item.value + return true +} + +// When inc returns true, the top cursor on the stack refers to the new current item. +func (it *Iterator) inc() bool { + // Useful invariants for understanding this function: + // - Leaf nodes have zero children, and zero or more items. + // - Nonleaf nodes have one more child than item, and children[i] < items[i] < children[i+1]. + // - The current item in the iterator is top.node.items[top.index]. + + it.Index++ + // If we are at a non-leaf node, the current item is items[i], so + // now we want to continue with children[i+1], which must exist + // by the node invariant. We want the minimum item in that child's subtree. + top := it.cursors.incTop(1) + for len(top.node.children) > 0 { + top = cursor{top.node.children[top.index], 0} + it.cursors.push(top) + } + // Here, we are at a leaf node. top.index points to + // the new current item, if it's within the items slice. + for top.index >= len(top.node.items) { + // We've gone through everything in this node. Pop it off the stack. + it.cursors.pop() + // If the stack is now empty,we're past the last item in the tree. + if it.cursors.empty() { + return false + } + top = it.cursors.top() + // The new top's index points to a child, which we've just finished + // exploring. The next item is the one at the same index in the items slice. + } + // Here, the top cursor on the stack points to the new current item. + return true +} + +func (it *Iterator) dec() bool { + // See the invariants for inc, above. + it.Index-- + top := it.cursors.top() + // If we are at a non-leaf node, the current item is items[i], so + // now we want to continue with children[i]. We want the maximum item in that child's subtree. + for len(top.node.children) > 0 { + c := top.node.children[top.index] + top = cursor{c, len(c.items)} + it.cursors.push(top) + } + top = it.cursors.incTop(-1) + // Here, we are at a leaf node. top.index points to + // the new current item, if it's within the items slice. + for top.index < 0 { + // We've gone through everything in this node. Pop it off the stack. + it.cursors.pop() + // If the stack is now empty,we're past the last item in the tree. + if it.cursors.empty() { + return false + } + // The new top's index points to a child, which we've just finished + // exploring. That child is to the right of the item we want to advance to, + // so decrement the index. + top = it.cursors.incTop(-1) + } + return true +} + +// A cursor is effectively a pointer into a node. A stack of cursors identifies an item in the tree, +// and makes it possible to move to the next or previous item efficiently. +// +// If the cursor is on the top of the stack, its index points into the node's items slice, selecting +// the current item. Otherwise, the index points into the children slice and identifies the child +// that is next in the stack. +type cursor struct { + node *node + index int +} + +// A cursorStack is a stack of cursors, representing a path of nodes from the root of the tree. +type cursorStack []cursor + +func (s *cursorStack) push(c cursor) cursorStack { + *s = append(*s, c) + return *s +} + +func (s *cursorStack) pop() cursor { + last := len(*s) - 1 + t := (*s)[last] + *s = (*s)[:last] + return t +} + +func (s *cursorStack) top() cursor { + return (*s)[len(*s)-1] +} + +func (s *cursorStack) empty() bool { + return len(*s) == 0 +} + +// incTop increments top's index by n and returns it. +func (s *cursorStack) incTop(n int) cursor { + (*s)[len(*s)-1].index += n // Don't call top: modify the original, not a copy. + return s.top() +} diff --git a/vendor/cloud.google.com/go/internal/btree/btree_test.go b/vendor/cloud.google.com/go/internal/btree/btree_test.go new file mode 100644 index 0000000000..4e550355af --- /dev/null +++ b/vendor/cloud.google.com/go/internal/btree/btree_test.go @@ -0,0 +1,422 @@ +// Copyright 2014 Google LLC +// Modified 2018 by Jonathan Amsterdam (jbamsterdam@gmail.com) +// +// 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 btree + +import ( + "flag" + "fmt" + "math/rand" + "os" + "sort" + "sync" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func init() { + seed := time.Now().Unix() + fmt.Println(seed) + rand.Seed(seed) +} + +type itemWithIndex struct { + Key Key + Value Value + Index int +} + +// perm returns a random permutation of n Int items in the range [0, n). +func perm(n int) []itemWithIndex { + var out []itemWithIndex + for _, v := range rand.Perm(n) { + out = append(out, itemWithIndex{v, v, v}) + } + return out +} + +// rang returns an ordered list of Int items in the range [0, n). +func rang(n int) []itemWithIndex { + var out []itemWithIndex + for i := 0; i < n; i++ { + out = append(out, itemWithIndex{i, i, i}) + } + return out +} + +// all extracts all items from an iterator. +func all(it *Iterator) []itemWithIndex { + var out []itemWithIndex + for it.Next() { + out = append(out, itemWithIndex{it.Key, it.Value, it.Index}) + } + return out +} + +// rangerev returns a reversed ordered list of Int items in the range [0, n). +func rangrev(n int) []itemWithIndex { + var out []itemWithIndex + for i := n - 1; i >= 0; i-- { + out = append(out, itemWithIndex{i, i, i}) + } + return out +} + +func reverse(s []itemWithIndex) { + for i := 0; i < len(s)/2; i++ { + s[i], s[len(s)-i-1] = s[len(s)-i-1], s[i] + } +} + +var btreeDegree = flag.Int("degree", 32, "B-Tree degree") + +func TestBTree(t *testing.T) { + tr := New(*btreeDegree, less) + const treeSize = 10000 + for i := 0; i < 10; i++ { + if min, _ := tr.Min(); min != nil { + t.Fatalf("empty min, got %+v", min) + } + if max, _ := tr.Max(); max != nil { + t.Fatalf("empty max, got %+v", max) + } + for _, m := range perm(treeSize) { + if _, ok := tr.Set(m.Key, m.Value); ok { + t.Fatal("set found item", m) + } + } + for _, m := range perm(treeSize) { + _, ok, idx := tr.SetWithIndex(m.Key, m.Value) + if !ok { + t.Fatal("set didn't find item", m) + } + if idx != m.Index { + t.Fatalf("got index %d, want %d", idx, m.Index) + } + } + mink, minv := tr.Min() + if want := 0; mink != want || minv != want { + t.Fatalf("min: want %+v, got %+v, %+v", want, mink, minv) + } + maxk, maxv := tr.Max() + if want := treeSize - 1; maxk != want || maxv != want { + t.Fatalf("max: want %+v, got %+v, %+v", want, maxk, maxv) + } + got := all(tr.BeforeIndex(0)) + want := rang(treeSize) + if !cmp.Equal(got, want) { + t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want) + } + + for _, m := range perm(treeSize) { + if _, removed := tr.Delete(m.Key); !removed { + t.Fatalf("didn't find %v", m) + } + } + if got = all(tr.BeforeIndex(0)); len(got) > 0 { + t.Fatalf("some left!: %v", got) + } + } +} + +func TestAt(t *testing.T) { + tr := New(*btreeDegree, less) + for _, m := range perm(100) { + tr.Set(m.Key, m.Value) + } + for i := 0; i < tr.Len(); i++ { + gotk, gotv := tr.At(i) + if want := i; gotk != want || gotv != want { + t.Fatalf("At(%d) = (%v, %v), want (%v, %v)", i, gotk, gotv, want, want) + } + } +} + +func TestGetWithIndex(t *testing.T) { + tr := New(*btreeDegree, less) + for _, m := range perm(100) { + tr.Set(m.Key, m.Value) + } + for i := 0; i < tr.Len(); i++ { + gotv, goti := tr.GetWithIndex(i) + wantv, wanti := i, i + if gotv != wantv || goti != wanti { + t.Errorf("GetWithIndex(%d) = (%v, %v), want (%v, %v)", + i, gotv, goti, wantv, wanti) + } + } + _, got := tr.GetWithIndex(100) + if want := -1; got != want { + t.Errorf("got %d, want %d", got, want) + } +} + +func TestSetWithIndex(t *testing.T) { + tr := New(4, less) // use a small degree to cover more cases + var contents []int + for _, m := range perm(100) { + _, _, idx := tr.SetWithIndex(m.Key, m.Value) + contents = append(contents, m.Index) + sort.Ints(contents) + want := -1 + for i, c := range contents { + if c == m.Index { + want = i + break + } + } + if idx != want { + t.Fatalf("got %d, want %d", idx, want) + } + } +} + +func TestDeleteMin(t *testing.T) { + tr := New(3, less) + for _, m := range perm(100) { + tr.Set(m.Key, m.Value) + } + var got []itemWithIndex + for i := 0; tr.Len() > 0; i++ { + k, v := tr.DeleteMin() + got = append(got, itemWithIndex{k, v, i}) + } + if want := rang(100); !cmp.Equal(got, want) { + t.Fatalf("got: %v\nwant: %v", got, want) + } +} + +func TestDeleteMax(t *testing.T) { + tr := New(3, less) + for _, m := range perm(100) { + tr.Set(m.Key, m.Value) + } + var got []itemWithIndex + for tr.Len() > 0 { + k, v := tr.DeleteMax() + got = append(got, itemWithIndex{k, v, tr.Len()}) + } + reverse(got) + if want := rang(100); !cmp.Equal(got, want) { + t.Fatalf("got: %v\nwant: %v", got, want) + } +} + +func TestIterator(t *testing.T) { + const size = 10 + + tr := New(2, less) + // Empty tree. + for i, it := range []*Iterator{ + tr.BeforeIndex(0), + tr.Before(3), + tr.After(3), + } { + if got, want := it.Next(), false; got != want { + t.Errorf("empty, #%d: got %t, want %t", i, got, want) + } + } + + // Root with zero children. + tr.Set(1, nil) + tr.Delete(1) + if !(tr.root != nil && len(tr.root.children) == 0 && len(tr.root.items) == 0) { + t.Fatal("wrong shape tree") + } + for i, it := range []*Iterator{ + tr.BeforeIndex(0), + tr.Before(3), + tr.After(3), + } { + if got, want := it.Next(), false; got != want { + t.Errorf("zero root, #%d: got %t, want %t", i, got, want) + } + } + + // Tree with size elements. + p := perm(size) + for _, v := range p { + tr.Set(v.Key, v.Value) + } + + it := tr.BeforeIndex(0) + got := all(it) + want := rang(size) + if !cmp.Equal(got, want) { + t.Fatalf("got %+v\nwant %+v\n", got, want) + } + + for i, w := range want { + it := tr.Before(w.Key) + got = all(it) + wn := want[w.Key.(int):] + if !cmp.Equal(got, wn) { + t.Fatalf("got %+v\nwant %+v\n", got, wn) + } + + it = tr.BeforeIndex(i) + got = all(it) + if !cmp.Equal(got, wn) { + t.Fatalf("got %+v\nwant %+v\n", got, wn) + } + + it = tr.After(w.Key) + got = all(it) + wn = append([]itemWithIndex(nil), want[:w.Key.(int)+1]...) + reverse(wn) + if !cmp.Equal(got, wn) { + t.Fatalf("got %+v\nwant %+v\n", got, wn) + } + + it = tr.AfterIndex(i) + got = all(it) + if !cmp.Equal(got, wn) { + t.Fatalf("got %+v\nwant %+v\n", got, wn) + } + } + + // Non-existent keys. + tr = New(2, less) + for _, v := range p { + tr.Set(v.Key.(int)*2, v.Value) + } + // tr has only even keys: 0, 2, 4, ... Iterate from odd keys. + for i := -1; i <= size+1; i += 2 { + it := tr.Before(i) + got := all(it) + var want []itemWithIndex + for j := (i + 1) / 2; j < size; j++ { + want = append(want, itemWithIndex{j * 2, j, j}) + } + if !cmp.Equal(got, want) { + tr.print(os.Stdout) + t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want) + } + + it = tr.After(i) + got = all(it) + want = nil + for j := (i - 1) / 2; j >= 0; j-- { + want = append(want, itemWithIndex{j * 2, j, j}) + } + if !cmp.Equal(got, want) { + t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want) + } + } +} + +func TestMixed(t *testing.T) { + // Test random, mixed insertions and deletions. + const maxSize = 1000 + tr := New(3, less) + has := map[int]bool{} + for i := 0; i < 10000; i++ { + r := rand.Intn(maxSize) + if r >= tr.Len() { + old, ok := tr.Set(r, r) + if has[r] != ok { + t.Fatalf("%d: has=%t, ok=%t", r, has[r], ok) + } + if ok && old.(int) != r { + t.Fatalf("%d: bad old", r) + } + has[r] = true + if got, want := tr.Get(r), r; got != want { + t.Fatalf("Get(%d) = %d, want %d", r, got, want) + } + } else { + // Expoit random map iteration order. + var d int + for d = range has { + break + } + old, removed := tr.Delete(d) + if !removed { + t.Fatalf("%d not found", d) + } + if old.(int) != d { + t.Fatalf("%d: bad old", d) + } + delete(has, d) + } + } +} + +const cloneTestSize = 10000 + +func cloneTest(t *testing.T, b *BTree, start int, p []itemWithIndex, wg *sync.WaitGroup, treec chan<- *BTree) { + treec <- b + for i := start; i < cloneTestSize; i++ { + b.Set(p[i].Key, p[i].Value) + if i%(cloneTestSize/5) == 0 { + wg.Add(1) + go cloneTest(t, b.Clone(), i+1, p, wg, treec) + } + } + wg.Done() +} + +func TestCloneConcurrentOperations(t *testing.T) { + b := New(*btreeDegree, less) + treec := make(chan *BTree) + p := perm(cloneTestSize) + var wg sync.WaitGroup + wg.Add(1) + go cloneTest(t, b, 0, p, &wg, treec) + var trees []*BTree + donec := make(chan struct{}) + go func() { + for t := range treec { + trees = append(trees, t) + } + close(donec) + }() + wg.Wait() + close(treec) + <-donec + want := rang(cloneTestSize) + for i, tree := range trees { + if !cmp.Equal(want, all(tree.BeforeIndex(0))) { + t.Errorf("tree %v mismatch", i) + } + } + toRemove := rang(cloneTestSize)[cloneTestSize/2:] + for i := 0; i < len(trees)/2; i++ { + tree := trees[i] + wg.Add(1) + go func() { + for _, m := range toRemove { + tree.Delete(m.Key) + } + wg.Done() + }() + } + wg.Wait() + for i, tree := range trees { + var wantpart []itemWithIndex + if i < len(trees)/2 { + wantpart = want[:cloneTestSize/2] + } else { + wantpart = want + } + if got := all(tree.BeforeIndex(0)); !cmp.Equal(wantpart, got) { + t.Errorf("tree %v mismatch, want %v got %v", i, len(want), len(got)) + } + } +} + +func less(a, b interface{}) bool { return a.(int) < b.(int) } diff --git a/vendor/cloud.google.com/go/internal/btree/debug.go b/vendor/cloud.google.com/go/internal/btree/debug.go new file mode 100644 index 0000000000..e8a54df369 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/btree/debug.go @@ -0,0 +1,37 @@ +// 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 btree + +import ( + "fmt" + "io" + "strings" +) + +func (t *BTree) print(w io.Writer) { + t.root.print(w, 0) +} + +func (n *node) print(w io.Writer, level int) { + indent := strings.Repeat(" ", level) + if n == nil { + fmt.Fprintf(w, "%s\n", indent) + return + } + fmt.Fprintf(w, "%s%v\n", indent, n.items) + for _, c := range n.children { + c.print(w, level+1) + } +} diff --git a/vendor/cloud.google.com/go/internal/fields/fields.go b/vendor/cloud.google.com/go/internal/fields/fields.go new file mode 100644 index 0000000000..341ada86fd --- /dev/null +++ b/vendor/cloud.google.com/go/internal/fields/fields.go @@ -0,0 +1,468 @@ +// 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 fields provides a view of the fields of a struct that follows the Go +// rules, amended to consider tags and case insensitivity. +// +// Usage +// +// First define a function that interprets tags: +// +// func parseTag(st reflect.StructTag) (name string, keep bool, other interface{}, err error) { ... } +// +// The function's return values describe whether to ignore the field +// completely or provide an alternate name, as well as other data from the +// parse that is stored to avoid re-parsing. +// +// Then define a function to validate the type: +// +// func validate(t reflect.Type) error { ... } +// +// Then, if necessary, define a function to specify leaf types - types +// which should be considered one field and not be recursed into: +// +// func isLeafType(t reflect.Type) bool { ... } +// +// eg: +// +// func isLeafType(t reflect.Type) bool { +// return t == reflect.TypeOf(time.Time{}) +// } +// +// Next, construct a Cache, passing your functions. As its name suggests, a +// Cache remembers validation and field information for a type, so subsequent +// calls with the same type are very fast. +// +// cache := fields.NewCache(parseTag, validate, isLeafType) +// +// To get the fields of a struct type as determined by the above rules, call +// the Fields method: +// +// fields, err := cache.Fields(reflect.TypeOf(MyStruct{})) +// +// The return value can be treated as a slice of Fields. +// +// Given a string, such as a key or column name obtained during unmarshalling, +// call Match on the list of fields to find a field whose name is the best +// match: +// +// field := fields.Match(name) +// +// Match looks for an exact match first, then falls back to a case-insensitive +// comparison. +package fields + +import ( + "bytes" + "errors" + "reflect" + "sort" + "strings" + + "cloud.google.com/go/internal/atomiccache" +) + +// A Field records information about a struct field. +type Field struct { + Name string // effective field name + NameFromTag bool // did Name come from a tag? + Type reflect.Type // field type + Index []int // index sequence, for reflect.Value.FieldByIndex + ParsedTag interface{} // third return value of the parseTag function + + nameBytes []byte + equalFold func(s, t []byte) bool +} + +type ParseTagFunc func(reflect.StructTag) (name string, keep bool, other interface{}, err error) + +type ValidateFunc func(reflect.Type) error + +type LeafTypesFunc func(reflect.Type) bool + +// A Cache records information about the fields of struct types. +// +// A Cache is safe for use by multiple goroutines. +type Cache struct { + parseTag ParseTagFunc + validate ValidateFunc + leafTypes LeafTypesFunc + cache atomiccache.Cache // from reflect.Type to cacheValue +} + +// NewCache constructs a Cache. +// +// Its first argument should be a function that accepts +// a struct tag and returns four values: an alternative name for the field +// extracted from the tag, a boolean saying whether to keep the field or ignore +// it, additional data that is stored with the field information to avoid +// having to parse the tag again, and an error. +// +// Its second argument should be a function that accepts a reflect.Type and +// returns an error if the struct type is invalid in any way. For example, it +// may check that all of the struct field tags are valid, or that all fields +// are of an appropriate type. +func NewCache(parseTag ParseTagFunc, validate ValidateFunc, leafTypes LeafTypesFunc) *Cache { + if parseTag == nil { + parseTag = func(reflect.StructTag) (string, bool, interface{}, error) { + return "", true, nil, nil + } + } + if validate == nil { + validate = func(reflect.Type) error { + return nil + } + } + if leafTypes == nil { + leafTypes = func(reflect.Type) bool { + return false + } + } + + return &Cache{ + parseTag: parseTag, + validate: validate, + leafTypes: leafTypes, + } +} + +// A fieldScan represents an item on the fieldByNameFunc scan work list. +type fieldScan struct { + typ reflect.Type + index []int +} + +// Fields returns all the exported fields of t, which must be a struct type. It +// follows the standard Go rules for embedded fields, modified by the presence +// of tags. The result is sorted lexicographically by index. +// +// These rules apply in the absence of tags: +// Anonymous struct fields are treated as if their inner exported fields were +// fields in the outer struct (embedding). The result includes all fields that +// aren't shadowed by fields at higher level of embedding. If more than one +// field with the same name exists at the same level of embedding, it is +// excluded. An anonymous field that is not of struct type is treated as having +// its type as its name. +// +// Tags modify these rules as follows: +// A field's tag is used as its name. +// An anonymous struct field with a name given in its tag is treated as +// a field having that name, rather than an embedded struct (the struct's +// fields will not be returned). +// If more than one field with the same name exists at the same level of embedding, +// but exactly one of them is tagged, then the tagged field is reported and the others +// are ignored. +func (c *Cache) Fields(t reflect.Type) (List, error) { + if t.Kind() != reflect.Struct { + panic("fields: Fields of non-struct type") + } + return c.cachedTypeFields(t) +} + +// A List is a list of Fields. +type List []Field + +// Match returns the field in the list whose name best matches the supplied +// name, nor nil if no field does. If there is a field with the exact name, it +// is returned. Otherwise the first field (sorted by index) whose name matches +// case-insensitively is returned. +func (l List) Match(name string) *Field { + return l.MatchBytes([]byte(name)) +} + +// MatchBytes is identical to Match, except that the argument is a byte slice. +func (l List) MatchBytes(name []byte) *Field { + var f *Field + for i := range l { + ff := &l[i] + if bytes.Equal(ff.nameBytes, name) { + return ff + } + if f == nil && ff.equalFold(ff.nameBytes, name) { + f = ff + } + } + return f +} + +type cacheValue struct { + fields List + err error +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +// This code has been copied and modified from +// https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/encode.go. +func (c *Cache) cachedTypeFields(t reflect.Type) (List, error) { + cv := c.cache.Get(t, func() interface{} { + if err := c.validate(t); err != nil { + return cacheValue{nil, err} + } + f, err := c.typeFields(t) + return cacheValue{List(f), err} + }).(cacheValue) + return cv.fields, cv.err +} + +func (c *Cache) typeFields(t reflect.Type) ([]Field, error) { + fields, err := c.listFields(t) + if err != nil { + return nil, err + } + sort.Sort(byName(fields)) + // Delete all fields that are hidden by the Go rules for embedded fields. + + // The fields are sorted in primary order of name, secondary order of field + // index length. So the first field with a given name is the dominant one. + var out []Field + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.Name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.Name != name { + break + } + } + // Find the dominant field, if any, out of all fields that have the same name. + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + sort.Sort(byIndex(out)) + return out, nil +} + +func (c *Cache) listFields(t reflect.Type) ([]Field, error) { + // This uses the same condition that the Go language does: there must be a unique instance + // of the match at a given depth level. If there are multiple instances of a match at the + // same depth, they annihilate each other and inhibit any possible match at a lower level. + // The algorithm is breadth first search, one depth level at a time. + + // The current and next slices are work queues: + // current lists the fields to visit on this depth level, + // and next lists the fields on the next lower level. + current := []fieldScan{} + next := []fieldScan{{typ: t}} + + // nextCount records the number of times an embedded type has been + // encountered and considered for queueing in the 'next' slice. + // We only queue the first one, but we increment the count on each. + // If a struct type T can be reached more than once at a given depth level, + // then it annihilates itself and need not be considered at all when we + // process that next depth level. + var nextCount map[reflect.Type]int + + // visited records the structs that have been considered already. + // Embedded pointer fields can create cycles in the graph of + // reachable embedded types; visited avoids following those cycles. + // It also avoids duplicated effort: if we didn't find the field in an + // embedded type T at level 2, we won't find it in one at level 4 either. + visited := map[reflect.Type]bool{} + + var fields []Field // Fields found. + + for len(next) > 0 { + current, next = next, current[:0] + count := nextCount + nextCount = nil + + // Process all the fields at this depth, now listed in 'current'. + // The loop queues embedded fields found in 'next', for processing during the next + // iteration. The multiplicity of the 'current' field counts is recorded + // in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'. + for _, scan := range current { + t := scan.typ + if visited[t] { + // We've looked through this type before, at a higher level. + // That higher level would shadow the lower level we're now at, + // so this one can't be useful to us. Ignore it. + continue + } + visited[t] = true + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + + exported := (f.PkgPath == "") + + // If a named field is unexported, ignore it. An anonymous + // unexported field is processed, because it may contain + // exported fields, which are visible. + if !exported && !f.Anonymous { + continue + } + + // Examine the tag. + tagName, keep, other, err := c.parseTag(f.Tag) + if err != nil { + return nil, err + } + if !keep { + continue + } + if c.leafTypes(f.Type) { + fields = append(fields, newField(f, tagName, other, scan.index, i)) + continue + } + + var ntyp reflect.Type + if f.Anonymous { + // Anonymous field of type T or *T. + ntyp = f.Type + if ntyp.Kind() == reflect.Ptr { + ntyp = ntyp.Elem() + } + } + + // Record fields with a tag name, non-anonymous fields, or + // anonymous non-struct fields. + if tagName != "" || ntyp == nil || ntyp.Kind() != reflect.Struct { + if !exported { + continue + } + fields = append(fields, newField(f, tagName, other, scan.index, i)) + if count[t] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Queue embedded struct fields for processing with next level, + // but only if the embedded types haven't already been queued. + if nextCount[ntyp] > 0 { + nextCount[ntyp] = 2 // exact multiple doesn't matter + continue + } + if nextCount == nil { + nextCount = map[reflect.Type]int{} + } + nextCount[ntyp] = 1 + if count[t] > 1 { + nextCount[ntyp] = 2 // exact multiple doesn't matter + } + var index []int + index = append(index, scan.index...) + index = append(index, i) + next = append(next, fieldScan{ntyp, index}) + } + } + } + return fields, nil +} + +func newField(f reflect.StructField, tagName string, other interface{}, index []int, i int) Field { + name := tagName + if name == "" { + name = f.Name + } + sf := Field{ + Name: name, + NameFromTag: tagName != "", + Type: f.Type, + ParsedTag: other, + nameBytes: []byte(name), + } + sf.equalFold = foldFunc(sf.nameBytes) + sf.Index = append(sf.Index, index...) + sf.Index = append(sf.Index, i) + return sf +} + +// byName sorts fields using the following criteria, in order: +// 1. name +// 2. embedding depth +// 3. tag presence (preferring a tagged field) +// 4. index sequence. +type byName []Field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].Name != x[j].Name { + return x[i].Name < x[j].Name + } + if len(x[i].Index) != len(x[j].Index) { + return len(x[i].Index) < len(x[j].Index) + } + if x[i].NameFromTag != x[j].NameFromTag { + return x[i].NameFromTag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []Field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + xi := x[i].Index + xj := x[j].Index + ln := len(xi) + if l := len(xj); l < ln { + ln = l + } + for k := 0; k < ln; k++ { + if xi[k] != xj[k] { + return xi[k] < xj[k] + } + } + return len(xi) < len(xj) +} + +// dominantField looks through the fields, all of which are known to have the +// same name, to find the single field that dominates the others using Go's +// embedding rules, modified by the presence of tags. If there are multiple +// top-level fields, the boolean will be false: This condition is an error in +// Go and we skip all the fields. +func dominantField(fs []Field) (Field, bool) { + // The fields are sorted in increasing index-length order, then by presence of tag. + // That means that the first field is the dominant one. We need only check + // for error cases: two fields at top level, either both tagged or neither tagged. + if len(fs) > 1 && len(fs[0].Index) == len(fs[1].Index) && fs[0].NameFromTag == fs[1].NameFromTag { + return Field{}, false + } + return fs[0], true +} + +// ParseStandardTag extracts the sub-tag named by key, then parses it using the +// de facto standard format introduced in encoding/json: +// "-" means "ignore this tag". It must occur by itself. (parseStandardTag returns an error +// in this case, whereas encoding/json accepts the "-" even if it is not alone.) +// "" provides an alternative name for the field +// ",opt1,opt2,..." specifies options after the name. +// The options are returned as a []string. +func ParseStandardTag(key string, t reflect.StructTag) (name string, keep bool, options []string, err error) { + s := t.Get(key) + parts := strings.Split(s, ",") + if parts[0] == "-" { + if len(parts) > 1 { + return "", false, nil, errors.New(`"-" field tag with options`) + } + return "", false, nil, nil + } + if len(parts) > 1 { + options = parts[1:] + } + return parts[0], true, options, nil +} diff --git a/vendor/cloud.google.com/go/internal/fields/fields_test.go b/vendor/cloud.google.com/go/internal/fields/fields_test.go new file mode 100644 index 0000000000..66ac0c5c67 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/fields/fields_test.go @@ -0,0 +1,563 @@ +// 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 fields + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "cloud.google.com/go/internal/testutil" +) + +type embed1 struct { + Em1 int + Dup int // annihilates with embed2.Dup + Shadow int + embed3 +} + +type embed2 struct { + Dup int + embed3 + embed4 +} + +type embed3 struct { + Em3 int // annihilated because embed3 is in both embed1 and embed2 + embed5 +} + +type embed4 struct { + Em4 int + Dup int // annihilation of Dup in embed1, embed2 hides this Dup + *embed1 // ignored because it occurs at a higher level +} + +type embed5 struct { + x int +} + +type Anonymous int + +type S1 struct { + Exported int + unexported int + Shadow int // shadows S1.Shadow + embed1 + *embed2 + Anonymous +} + +type Time struct { + time.Time +} + +var intType = reflect.TypeOf(int(0)) + +func field(name string, tval interface{}, index ...int) *Field { + return &Field{ + Name: name, + Type: reflect.TypeOf(tval), + Index: index, + ParsedTag: []string(nil), + } +} + +func tfield(name string, tval interface{}, index ...int) *Field { + return &Field{ + Name: name, + Type: reflect.TypeOf(tval), + Index: index, + NameFromTag: true, + ParsedTag: []string(nil), + } +} + +func TestFieldsNoTags(t *testing.T) { + c := NewCache(nil, nil, nil) + got, err := c.Fields(reflect.TypeOf(S1{})) + if err != nil { + t.Fatal(err) + } + want := []*Field{ + field("Exported", int(0), 0), + field("Shadow", int(0), 2), + field("Em1", int(0), 3, 0), + field("Em4", int(0), 4, 2, 0), + field("Anonymous", Anonymous(0), 5), + } + for _, f := range want { + f.ParsedTag = nil + } + if msg, ok := compareFields(got, want); !ok { + t.Error(msg) + } +} + +func TestAgainstJSONEncodingNoTags(t *testing.T) { + // Demonstrates that this package produces the same set of fields as encoding/json. + s1 := S1{ + Exported: 1, + unexported: 2, + Shadow: 3, + embed1: embed1{ + Em1: 4, + Dup: 5, + Shadow: 6, + embed3: embed3{ + Em3: 7, + embed5: embed5{x: 8}, + }, + }, + embed2: &embed2{ + Dup: 9, + embed3: embed3{ + Em3: 10, + embed5: embed5{x: 11}, + }, + embed4: embed4{ + Em4: 12, + Dup: 13, + embed1: &embed1{Em1: 14}, + }, + }, + Anonymous: Anonymous(15), + } + var want S1 + want.embed2 = &embed2{} // need this because reflection won't create it + jsonRoundTrip(t, s1, &want) + var got S1 + got.embed2 = &embed2{} + fields, err := NewCache(nil, nil, nil).Fields(reflect.TypeOf(got)) + if err != nil { + t.Fatal(err) + } + setFields(fields, &got, s1) + if !testutil.Equal(got, want, + cmp.AllowUnexported(S1{}, embed1{}, embed2{}, embed3{}, embed4{}, embed5{})) { + t.Errorf("got\n%+v\nwant\n%+v", got, want) + } +} + +// Tests use of LeafTypes parameter to NewCache +func TestAgainstJSONEncodingEmbeddedTime(t *testing.T) { + timeLeafFn := func(t reflect.Type) bool { + return t == reflect.TypeOf(time.Time{}) + } + // Demonstrates that this package can produce the same set of + // fields as encoding/json for a struct with an embedded time.Time. + now := time.Now().UTC() + myt := Time{ + now, + } + var want Time + jsonRoundTrip(t, myt, &want) + var got Time + fields, err := NewCache(nil, nil, timeLeafFn).Fields(reflect.TypeOf(got)) + if err != nil { + t.Fatal(err) + } + setFields(fields, &got, myt) + if !testutil.Equal(got, want) { + t.Errorf("got\n%+v\nwant\n%+v", got, want) + } +} + +type S2 struct { + NoTag int + XXX int `json:"tag"` // tag name takes precedence + Anonymous `json:"anon"` // anonymous non-structs also get their name from the tag + unexported int `json:"tag"` + Embed `json:"em"` // embedded structs with tags become fields + Tag int + YYY int `json:"Tag"` // tag takes precedence over untagged field of the same name + Empty int `json:""` // empty tag is noop + tEmbed1 + tEmbed2 +} + +type Embed struct { + Em int +} + +type tEmbed1 struct { + Dup int + X int `json:"Dup2"` +} + +type tEmbed2 struct { + Y int `json:"Dup"` // takes precedence over tEmbed1.Dup because it is tagged + Z int `json:"Dup2"` // same name as tEmbed1.X and both tagged, so ignored +} + +func jsonTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) { + return ParseStandardTag("json", t) +} + +func validateFunc(t reflect.Type) (err error) { + if t.Kind() != reflect.Struct { + return errors.New("non-struct type used") + } + + for i := 0; i < t.NumField(); i++ { + if t.Field(i).Type.Kind() == reflect.Slice { + return fmt.Errorf("slice field found at field %s on struct %s", t.Field(i).Name, t.Name()) + } + } + + return nil +} + +func TestFieldsWithTags(t *testing.T) { + got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{})) + if err != nil { + t.Fatal(err) + } + want := []*Field{ + field("NoTag", int(0), 0), + tfield("tag", int(0), 1), + tfield("anon", Anonymous(0), 2), + tfield("em", Embed{}, 4), + tfield("Tag", int(0), 6), + field("Empty", int(0), 7), + tfield("Dup", int(0), 8, 0), + } + if msg, ok := compareFields(got, want); !ok { + t.Error(msg) + } +} + +func TestAgainstJSONEncodingWithTags(t *testing.T) { + // Demonstrates that this package produces the same set of fields as encoding/json. + s2 := S2{ + NoTag: 1, + XXX: 2, + Anonymous: 3, + Embed: Embed{ + Em: 4, + }, + tEmbed1: tEmbed1{ + Dup: 5, + X: 6, + }, + tEmbed2: tEmbed2{ + Y: 7, + Z: 8, + }, + } + var want S2 + jsonRoundTrip(t, s2, &want) + var got S2 + fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(got)) + if err != nil { + t.Fatal(err) + } + setFields(fields, &got, s2) + if !testutil.Equal(got, want, cmp.AllowUnexported(S2{})) { + t.Errorf("got\n%+v\nwant\n%+v", got, want) + } +} + +func TestUnexportedAnonymousNonStruct(t *testing.T) { + // An unexported anonymous non-struct field should not be recorded. + // This is currently a bug in encoding/json. + // https://github.com/golang/go/issues/18009 + type ( + u int + v int + S struct { + u + v `json:"x"` + int + } + ) + + got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{})) + if err != nil { + t.Fatal(err) + } + if len(got) != 0 { + t.Errorf("got %d fields, want 0", len(got)) + } +} + +func TestUnexportedAnonymousStruct(t *testing.T) { + // An unexported anonymous struct with a tag is ignored. + // This is currently a bug in encoding/json. + // https://github.com/golang/go/issues/18009 + type ( + s1 struct{ X int } + S2 struct { + s1 `json:"Y"` + } + ) + got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{})) + if err != nil { + t.Fatal(err) + } + if len(got) != 0 { + t.Errorf("got %d fields, want 0", len(got)) + } +} + +func TestDominantField(t *testing.T) { + // With fields sorted by index length and then by tag presence, + // the dominant field is always the first. Make sure all error + // cases are caught. + for _, test := range []struct { + fields []Field + wantOK bool + }{ + // A single field is OK. + {[]Field{{Index: []int{0}}}, true}, + {[]Field{{Index: []int{0}, NameFromTag: true}}, true}, + // A single field at top level is OK. + {[]Field{{Index: []int{0}}, {Index: []int{1, 0}}}, true}, + {[]Field{{Index: []int{0}}, {Index: []int{1, 0}, NameFromTag: true}}, true}, + {[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1, 0}, NameFromTag: true}}, true}, + // A single tagged field is OK. + {[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}}}, true}, + // Two untagged fields at the same level is an error. + {[]Field{{Index: []int{0}}, {Index: []int{1}}}, false}, + // Two tagged fields at the same level is an error. + {[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}, NameFromTag: true}}, false}, + } { + _, gotOK := dominantField(test.fields) + if gotOK != test.wantOK { + t.Errorf("%v: got %t, want %t", test.fields, gotOK, test.wantOK) + } + } +} + +func TestIgnore(t *testing.T) { + type S struct { + X int `json:"-"` + } + got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{})) + if err != nil { + t.Fatal(err) + } + if len(got) != 0 { + t.Errorf("got %d fields, want 0", len(got)) + } +} + +func TestParsedTag(t *testing.T) { + type S struct { + X int `json:"name,omitempty"` + } + got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{})) + if err != nil { + t.Fatal(err) + } + want := []*Field{ + {Name: "name", NameFromTag: true, Type: intType, + Index: []int{0}, ParsedTag: []string{"omitempty"}}, + } + if msg, ok := compareFields(got, want); !ok { + t.Error(msg) + } +} + +func TestValidateFunc(t *testing.T) { + type MyInvalidStruct struct { + A string + B []int + } + + _, err := NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyInvalidStruct{})) + if err == nil { + t.Fatal("expected error, got nil") + } + + type MyValidStruct struct { + A string + B int + } + _, err = NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyValidStruct{})) + if err != nil { + t.Fatalf("expected nil, got error: %s\n", err) + } +} + +func compareFields(got []Field, want []*Field) (msg string, ok bool) { + if len(got) != len(want) { + return fmt.Sprintf("got %d fields, want %d", len(got), len(want)), false + } + for i, g := range got { + w := *want[i] + if !fieldsEqual(&g, &w) { + return fmt.Sprintf("got\n%+v\nwant\n%+v", g, w), false + } + } + return "", true +} + +// Need this because Field contains a function, which cannot be compared even +// by testutil.Equal. +func fieldsEqual(f1, f2 *Field) bool { + if f1 == nil || f2 == nil { + return f1 == f2 + } + return f1.Name == f2.Name && + f1.NameFromTag == f2.NameFromTag && + f1.Type == f2.Type && + testutil.Equal(f1.ParsedTag, f2.ParsedTag) +} + +// Set the fields of dst from those of src. +// dst must be a pointer to a struct value. +// src must be a struct value. +func setFields(fields []Field, dst, src interface{}) { + vsrc := reflect.ValueOf(src) + vdst := reflect.ValueOf(dst).Elem() + for _, f := range fields { + fdst := vdst.FieldByIndex(f.Index) + fsrc := vsrc.FieldByIndex(f.Index) + fdst.Set(fsrc) + } +} + +func jsonRoundTrip(t *testing.T, in, out interface{}) { + bytes, err := json.Marshal(in) + if err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(bytes, out); err != nil { + t.Fatal(err) + } +} + +type S3 struct { + S4 + Abc int + AbC int + Tag int + X int `json:"Tag"` + unexported int +} + +type S4 struct { + ABc int + Y int `json:"Abc"` // ignored because of top-level Abc +} + +func TestMatchingField(t *testing.T) { + fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{})) + if err != nil { + t.Fatal(err) + } + for _, test := range []struct { + name string + want *Field + }{ + // Exact match wins. + {"Abc", field("Abc", int(0), 1)}, + {"AbC", field("AbC", int(0), 2)}, + {"ABc", field("ABc", int(0), 0, 0)}, + // If there are multiple matches but no exact match or tag, + // the first field wins, lexicographically by index. + // Here, "ABc" is at a deeper embedding level, but since S4 appears + // first in S3, its index precedes the other fields of S3. + {"abc", field("ABc", int(0), 0, 0)}, + // Tag name takes precedence over untagged field of the same name. + {"Tag", tfield("Tag", int(0), 4)}, + // Unexported fields disappear. + {"unexported", nil}, + // Untagged embedded structs disappear. + {"S4", nil}, + } { + if got := fields.Match(test.name); !fieldsEqual(got, test.want) { + t.Errorf("match %q:\ngot %+v\nwant %+v", test.name, got, test.want) + } + } +} + +func TestAgainstJSONMatchingField(t *testing.T) { + s3 := S3{ + S4: S4{ABc: 1, Y: 2}, + Abc: 3, + AbC: 4, + Tag: 5, + X: 6, + unexported: 7, + } + var want S3 + jsonRoundTrip(t, s3, &want) + v := reflect.ValueOf(want) + fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{})) + if err != nil { + t.Fatal(err) + } + for _, test := range []struct { + name string + got int + }{ + {"Abc", 3}, + {"AbC", 4}, + {"ABc", 1}, + {"abc", 1}, + {"Tag", 6}, + } { + f := fields.Match(test.name) + if f == nil { + t.Fatalf("%s: no match", test.name) + } + w := v.FieldByIndex(f.Index).Interface() + if test.got != w { + t.Errorf("%s: got %d, want %d", test.name, test.got, w) + } + } +} + +func TestTagErrors(t *testing.T) { + called := false + c := NewCache(func(t reflect.StructTag) (string, bool, interface{}, error) { + called = true + s := t.Get("f") + if s == "bad" { + return "", false, nil, errors.New("error") + } + return s, true, nil, nil + }, nil, nil) + + type T struct { + X int `f:"ok"` + Y int `f:"bad"` + } + + _, err := c.Fields(reflect.TypeOf(T{})) + if !called { + t.Fatal("tag parser not called") + } + if err == nil { + t.Error("want error, got nil") + } + // Second time, we should cache the error. + called = false + _, err = c.Fields(reflect.TypeOf(T{})) + if called { + t.Fatal("tag parser called on second time") + } + if err == nil { + t.Error("want error, got nil") + } +} diff --git a/vendor/cloud.google.com/go/internal/fields/fold.go b/vendor/cloud.google.com/go/internal/fields/fold.go new file mode 100644 index 0000000000..68e2588c42 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/fields/fold.go @@ -0,0 +1,156 @@ +// 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 fields + +// This file was copied from https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/fold.go. +// Only the license and package were changed. + +import ( + "bytes" + "unicode/utf8" +) + +const ( + caseMask = ^byte(0x20) // Mask to ignore case in ASCII. + kelvin = '\u212a' + smallLongEss = '\u017f' +) + +// foldFunc returns one of four different case folding equivalence +// functions, from most general (and slow) to fastest: +// +// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 +// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') +// 3) asciiEqualFold, no special, but includes non-letters (including _) +// 4) simpleLetterEqualFold, no specials, no non-letters. +// +// The letters S and K are special because they map to 3 runes, not just 2: +// * S maps to s and to U+017F 'ſ' Latin small letter long s +// * k maps to K and to U+212A 'K' Kelvin sign +// See https://play.golang.org/p/tTxjOc0OGo +// +// The returned function is specialized for matching against s and +// should only be given s. It's not curried for performance reasons. +func foldFunc(s []byte) func(s, t []byte) bool { + nonLetter := false + special := false // special letter + for _, b := range s { + if b >= utf8.RuneSelf { + return bytes.EqualFold + } + upper := b & caseMask + if upper < 'A' || upper > 'Z' { + nonLetter = true + } else if upper == 'K' || upper == 'S' { + // See above for why these letters are special. + special = true + } + } + if special { + return equalFoldRight + } + if nonLetter { + return asciiEqualFold + } + return simpleLetterEqualFold +} + +// equalFoldRight is a specialization of bytes.EqualFold when s is +// known to be all ASCII (including punctuation), but contains an 's', +// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. +// See comments on foldFunc. +func equalFoldRight(s, t []byte) bool { + for _, sb := range s { + if len(t) == 0 { + return false + } + tb := t[0] + if tb < utf8.RuneSelf { + if sb != tb { + sbUpper := sb & caseMask + if 'A' <= sbUpper && sbUpper <= 'Z' { + if sbUpper != tb&caseMask { + return false + } + } else { + return false + } + } + t = t[1:] + continue + } + // sb is ASCII and t is not. t must be either kelvin + // sign or long s; sb must be s, S, k, or K. + tr, size := utf8.DecodeRune(t) + switch sb { + case 's', 'S': + if tr != smallLongEss { + return false + } + case 'k', 'K': + if tr != kelvin { + return false + } + default: + return false + } + t = t[size:] + + } + if len(t) > 0 { + return false + } + return true +} + +// asciiEqualFold is a specialization of bytes.EqualFold for use when +// s is all ASCII (but may contain non-letters) and contains no +// special-folding letters. +// See comments on foldFunc. +func asciiEqualFold(s, t []byte) bool { + if len(s) != len(t) { + return false + } + for i, sb := range s { + tb := t[i] + if sb == tb { + continue + } + if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { + if sb&caseMask != tb&caseMask { + return false + } + } else { + return false + } + } + return true +} + +// simpleLetterEqualFold is a specialization of bytes.EqualFold for +// use when s is all ASCII letters (no underscores, etc) and also +// doesn't contain 'k', 'K', 's', or 'S'. +// See comments on foldFunc. +func simpleLetterEqualFold(s, t []byte) bool { + if len(s) != len(t) { + return false + } + for i, b := range s { + if b&caseMask != t[i]&caseMask { + return false + } + } + return true +} diff --git a/vendor/cloud.google.com/go/internal/fields/fold_test.go b/vendor/cloud.google.com/go/internal/fields/fold_test.go new file mode 100644 index 0000000000..77cb80ba04 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/fields/fold_test.go @@ -0,0 +1,129 @@ +// 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 fields + +// This file was copied from https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/fold_test.go. +// Only the license and package were changed. + +import ( + "bytes" + "strings" + "testing" + "unicode/utf8" +) + +var foldTests = []struct { + fn func(s, t []byte) bool + s, t string + want bool +}{ + {equalFoldRight, "", "", true}, + {equalFoldRight, "a", "a", true}, + {equalFoldRight, "", "a", false}, + {equalFoldRight, "a", "", false}, + {equalFoldRight, "a", "A", true}, + {equalFoldRight, "AB", "ab", true}, + {equalFoldRight, "AB", "ac", false}, + {equalFoldRight, "sbkKc", "ſbKKc", true}, + {equalFoldRight, "SbKkc", "ſbKKc", true}, + {equalFoldRight, "SbKkc", "ſbKK", false}, + {equalFoldRight, "e", "é", false}, + {equalFoldRight, "s", "S", true}, + + {simpleLetterEqualFold, "", "", true}, + {simpleLetterEqualFold, "abc", "abc", true}, + {simpleLetterEqualFold, "abc", "ABC", true}, + {simpleLetterEqualFold, "abc", "ABCD", false}, + {simpleLetterEqualFold, "abc", "xxx", false}, + + {asciiEqualFold, "a_B", "A_b", true}, + {asciiEqualFold, "aa@", "aa`", false}, // verify 0x40 and 0x60 aren't case-equivalent +} + +func TestFold(t *testing.T) { + for i, tt := range foldTests { + if got := tt.fn([]byte(tt.s), []byte(tt.t)); got != tt.want { + t.Errorf("%d. %q, %q = %v; want %v", i, tt.s, tt.t, got, tt.want) + } + truth := strings.EqualFold(tt.s, tt.t) + if truth != tt.want { + t.Errorf("strings.EqualFold doesn't agree with case %d", i) + } + } +} + +func TestFoldAgainstUnicode(t *testing.T) { + const bufSize = 5 + buf1 := make([]byte, 0, bufSize) + buf2 := make([]byte, 0, bufSize) + var runes []rune + for i := 0x20; i <= 0x7f; i++ { + runes = append(runes, rune(i)) + } + runes = append(runes, kelvin, smallLongEss) + + funcs := []struct { + name string + fold func(s, t []byte) bool + letter bool // must be ASCII letter + simple bool // must be simple ASCII letter (not 'S' or 'K') + }{ + { + name: "equalFoldRight", + fold: equalFoldRight, + }, + { + name: "asciiEqualFold", + fold: asciiEqualFold, + simple: true, + }, + { + name: "simpleLetterEqualFold", + fold: simpleLetterEqualFold, + simple: true, + letter: true, + }, + } + + for _, ff := range funcs { + for _, r := range runes { + if r >= utf8.RuneSelf { + continue + } + if ff.letter && !isASCIILetter(byte(r)) { + continue + } + if ff.simple && (r == 's' || r == 'S' || r == 'k' || r == 'K') { + continue + } + for _, r2 := range runes { + buf1 := append(buf1[:0], 'x') + buf2 := append(buf2[:0], 'x') + buf1 = buf1[:1+utf8.EncodeRune(buf1[1:bufSize], r)] + buf2 = buf2[:1+utf8.EncodeRune(buf2[1:bufSize], r2)] + buf1 = append(buf1, 'x') + buf2 = append(buf2, 'x') + want := bytes.EqualFold(buf1, buf2) + if got := ff.fold(buf1, buf2); got != want { + t.Errorf("%s(%q, %q) = %v; want %v", ff.name, buf1, buf2, got, want) + } + } + } + } +} + +func isASCIILetter(b byte) bool { + return ('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z') +} diff --git a/vendor/cloud.google.com/go/internal/kokoro/build.sh b/vendor/cloud.google.com/go/internal/kokoro/build.sh new file mode 100755 index 0000000000..869be0d8dc --- /dev/null +++ b/vendor/cloud.google.com/go/internal/kokoro/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Fail on any error +set -eo pipefail + +# Display commands being run +set -x + +# cd to project dir on Kokoro instance +cd git/gocloud + +go version + +# Set $GOPATH +export GOPATH="$HOME/go" +GOCLOUD_HOME=$GOPATH/src/cloud.google.com/go +mkdir -p $GOCLOUD_HOME + +# Move code into $GOPATH and get dependencies +cp -R ./* $GOCLOUD_HOME +cd $GOCLOUD_HOME +go get -v ./... + +# # Don't run integration tests until we can protect against code from +# # untrusted forks reading and storing our service account key. +# cd internal/kokoro +# # Don't print out encryption keys, etc +# set +x +# key=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_key) +# iv=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_iv) +# pass=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_pass) + +# openssl aes-256-cbc -K $key -iv $iv -pass pass:$pass -in kokoro-key.json.enc -out key.json -d +# set -x + +# export GCLOUD_TESTS_GOLANG_KEY="$(pwd)/key.json" +# export GCLOUD_TESTS_GOLANG_PROJECT_ID="dulcet-port-762" +# cd $GOCLOUD_HOME + +# Run tests and tee output to log file, to be pushed to GCS as artifact. +go test -race -v -short ./... 2>&1 | tee $KOKORO_ARTIFACTS_DIR/$KOKORO_GERRIT_CHANGE_NUMBER.txt + +# Make sure README.md is up to date. +make -C internal/readme test diff diff --git a/vendor/cloud.google.com/go/internal/kokoro/kokoro-key.json.enc b/vendor/cloud.google.com/go/internal/kokoro/kokoro-key.json.enc new file mode 100644 index 0000000000..b23885469f Binary files /dev/null and b/vendor/cloud.google.com/go/internal/kokoro/kokoro-key.json.enc differ diff --git a/vendor/cloud.google.com/go/internal/leakcheck/leakcheck.go b/vendor/cloud.google.com/go/internal/leakcheck/leakcheck.go new file mode 100644 index 0000000000..734e070a76 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/leakcheck/leakcheck.go @@ -0,0 +1,116 @@ +// 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 leakcheck contains functions to check leaked goroutines. +// +// Call "defer leakcheck.Check(t)" at the beginning of tests. +// +// This is very closely based on grpc-go's leakcheck. +package leakcheck + +import ( + "runtime" + "sort" + "strings" + "time" +) + +var goroutinesToIgnore = []string{ + "testing.Main(", + "testing.tRunner(", + "testing.(*M).", + "runtime.goexit", + "created by runtime.gc", + "created by runtime/trace.Start", + "interestingGoroutines", + "runtime.MHeap_Scavenger", + "signal.signal_recv", + "sigterm.handler", + "runtime_mcall", + "(*loggingT).flushDaemon", + "goroutine in C code", +} + +// RegisterIgnoreGoroutine appends s into the ignore goroutine list. The +// goroutines whose stack trace contains s will not be identified as leaked +// goroutines. Not thread-safe, only call this function in init(). +func RegisterIgnoreGoroutine(s string) { + goroutinesToIgnore = append(goroutinesToIgnore, s) +} + +func ignore(g string) bool { + sl := strings.SplitN(g, "\n", 2) + if len(sl) != 2 { + return true + } + stack := strings.TrimSpace(sl[1]) + if strings.HasPrefix(stack, "testing.RunTests") { + return true + } + + if stack == "" { + return true + } + + for _, s := range goroutinesToIgnore { + if strings.Contains(stack, s) { + return true + } + } + + return false +} + +// interestingGoroutines returns all goroutines we care about for the purpose of +// leak checking. It excludes testing or runtime ones. +func interestingGoroutines() (gs []string) { + buf := make([]byte, 2<<20) + buf = buf[:runtime.Stack(buf, true)] + for _, g := range strings.Split(string(buf), "\n\n") { + if !ignore(g) { + gs = append(gs, g) + } + } + sort.Strings(gs) + return +} + +// Errorfer is the interface that wraps the Errorf method. It's a subset of +// testing.TB to make it easy to use Check. +type Errorfer interface { + Errorf(format string, args ...interface{}) +} + +func check(efer Errorfer, timeout time.Duration) { + // Loop, waiting for goroutines to shut down. + // Wait up to timeout, but finish as quickly as possible. + deadline := time.Now().Add(timeout) + var leaked []string + for time.Now().Before(deadline) { + if leaked = interestingGoroutines(); len(leaked) == 0 { + return + } + time.Sleep(50 * time.Millisecond) + } + for _, g := range leaked { + efer.Errorf("Leaked goroutine: %v", g) + } +} + +// Check looks at the currently-running goroutines and checks if there are any +// interestring (created by gRPC) goroutines leaked. It waits up to 10 seconds +// in the error cases. +func Check(efer Errorfer) { + check(efer, 10*time.Second) +} diff --git a/vendor/cloud.google.com/go/internal/leakcheck/leakcheck_test.go b/vendor/cloud.google.com/go/internal/leakcheck/leakcheck_test.go new file mode 100644 index 0000000000..2a8f1e6593 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/leakcheck/leakcheck_test.go @@ -0,0 +1,72 @@ +// 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 leakcheck + +import ( + "fmt" + "strings" + "testing" + "time" +) + +type testErrorfer struct { + errorCount int + errors []string +} + +func (e *testErrorfer) Errorf(format string, args ...interface{}) { + e.errors = append(e.errors, fmt.Sprintf(format, args...)) + e.errorCount++ +} + +func TestCheck(t *testing.T) { + const leakCount = 3 + for i := 0; i < leakCount; i++ { + go func() { time.Sleep(2 * time.Second) }() + } + if ig := interestingGoroutines(); len(ig) == 0 { + t.Error("blah") + } + e := &testErrorfer{} + check(e, time.Second) + if e.errorCount != leakCount { + t.Errorf("check found %v leaks, want %v leaks", e.errorCount, leakCount) + t.Logf("leaked goroutines:\n%v", strings.Join(e.errors, "\n")) + } + check(t, 3*time.Second) +} + +func ignoredTestingLeak(d time.Duration) { + time.Sleep(d) +} + +func TestCheckRegisterIgnore(t *testing.T) { + RegisterIgnoreGoroutine("ignoredTestingLeak") + const leakCount = 3 + for i := 0; i < leakCount; i++ { + go func() { time.Sleep(2 * time.Second) }() + } + go func() { ignoredTestingLeak(3 * time.Second) }() + if ig := interestingGoroutines(); len(ig) == 0 { + t.Error("blah") + } + e := &testErrorfer{} + check(e, time.Second) + if e.errorCount != leakCount { + t.Errorf("check found %v leaks, want %v leaks", e.errorCount, leakCount) + t.Logf("leaked goroutines:\n%v", strings.Join(e.errors, "\n")) + } + check(t, 3*time.Second) +} diff --git a/vendor/cloud.google.com/go/internal/optional/optional.go b/vendor/cloud.google.com/go/internal/optional/optional.go new file mode 100644 index 0000000000..72780f764c --- /dev/null +++ b/vendor/cloud.google.com/go/internal/optional/optional.go @@ -0,0 +1,108 @@ +// 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 optional provides versions of primitive types that can +// be nil. These are useful in methods that update some of an API object's +// fields. +package optional + +import ( + "fmt" + "strings" + "time" +) + +type ( + // Bool is either a bool or nil. + Bool interface{} + + // String is either a string or nil. + String interface{} + + // Int is either an int or nil. + Int interface{} + + // Uint is either a uint or nil. + Uint interface{} + + // Float64 is either a float64 or nil. + Float64 interface{} + + // Duration is either a time.Duration or nil. + Duration interface{} +) + +// ToBool returns its argument as a bool. +// It panics if its argument is nil or not a bool. +func ToBool(v Bool) bool { + x, ok := v.(bool) + if !ok { + doPanic("Bool", v) + } + return x +} + +// ToString returns its argument as a string. +// It panics if its argument is nil or not a string. +func ToString(v String) string { + x, ok := v.(string) + if !ok { + doPanic("String", v) + } + return x +} + +// ToInt returns its argument as an int. +// It panics if its argument is nil or not an int. +func ToInt(v Int) int { + x, ok := v.(int) + if !ok { + doPanic("Int", v) + } + return x +} + +// ToUint returns its argument as a uint. +// It panics if its argument is nil or not a uint. +func ToUint(v Uint) uint { + x, ok := v.(uint) + if !ok { + doPanic("Uint", v) + } + return x +} + +// ToFloat64 returns its argument as a float64. +// It panics if its argument is nil or not a float64. +func ToFloat64(v Float64) float64 { + x, ok := v.(float64) + if !ok { + doPanic("Float64", v) + } + return x +} + +// ToDuration returns its argument as a time.Duration. +// It panics if its argument is nil or not a time.Duration. +func ToDuration(v Duration) time.Duration { + x, ok := v.(time.Duration) + if !ok { + doPanic("Duration", v) + } + return x +} + +func doPanic(capType string, v interface{}) { + panic(fmt.Sprintf("optional.%s value should be %s, got %T", capType, strings.ToLower(capType), v)) +} diff --git a/vendor/cloud.google.com/go/internal/optional/optional_test.go b/vendor/cloud.google.com/go/internal/optional/optional_test.go new file mode 100644 index 0000000000..85b83aad2c --- /dev/null +++ b/vendor/cloud.google.com/go/internal/optional/optional_test.go @@ -0,0 +1,64 @@ +// 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 optional + +import "testing" + +func TestConvertSuccess(t *testing.T) { + if got, want := ToBool(false), false; got != want { + t.Errorf("got %v, want %v", got, want) + } + if got, want := ToString(""), ""; got != want { + t.Errorf("got %v, want %v", got, want) + } + if got, want := ToInt(0), 0; got != want { + t.Errorf("got %v, want %v", got, want) + } + if got, want := ToUint(uint(0)), uint(0); got != want { + t.Errorf("got %v, want %v", got, want) + } + if got, want := ToFloat64(0.0), 0.0; got != want { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestConvertFailure(t *testing.T) { + for _, f := range []func(){ + func() { ToBool(nil) }, + func() { ToBool(3) }, + func() { ToString(nil) }, + func() { ToString(3) }, + func() { ToInt(nil) }, + func() { ToInt("") }, + func() { ToUint(nil) }, + func() { ToUint("") }, + func() { ToFloat64(nil) }, + func() { ToFloat64("") }, + } { + if !panics(f) { + t.Error("got no panic, want panic") + } + } +} + +func panics(f func()) (b bool) { + defer func() { + if recover() != nil { + b = true + } + }() + f() + return false +} diff --git a/vendor/cloud.google.com/go/internal/pretty/diff.go b/vendor/cloud.google.com/go/internal/pretty/diff.go new file mode 100644 index 0000000000..2edea743db --- /dev/null +++ b/vendor/cloud.google.com/go/internal/pretty/diff.go @@ -0,0 +1,78 @@ +// 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 pretty + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "syscall" +) + +// Diff compares the pretty-printed representation of two values. The second +// return value reports whether the two values' representations are identical. +// If it is false, the first return value contains the diffs. +// +// The output labels the first value "want" and the second "got". +// +// Diff works by invoking the "diff" command. It will only succeed in +// environments where "diff" is on the shell path. +func Diff(want, got interface{}) (string, bool, error) { + fname1, err := writeToTemp(want) + if err != nil { + return "", false, err + } + defer os.Remove(fname1) + + fname2, err := writeToTemp(got) + if err != nil { + return "", false, err + } + defer os.Remove(fname2) + + cmd := exec.Command("diff", "-u", "--label=want", "--label=got", fname1, fname2) + out, err := cmd.Output() + if err == nil { + return string(out), true, nil + } + eerr, ok := err.(*exec.ExitError) + if !ok { + return "", false, err + } + ws, ok := eerr.Sys().(syscall.WaitStatus) + if !ok { + return "", false, err + } + if ws.ExitStatus() != 1 { + return "", false, err + } + // Exit status of 1 means no error, but diffs were found. + return string(out), false, nil +} + +func writeToTemp(v interface{}) (string, error) { + f, err := ioutil.TempFile("", "prettyDiff") + if err != nil { + return "", err + } + if _, err := fmt.Fprintf(f, "%+v\n", Value(v)); err != nil { + return "", err + } + if err := f.Close(); err != nil { + return "", err + } + return f.Name(), nil +} diff --git a/vendor/cloud.google.com/go/internal/pretty/diff_test.go b/vendor/cloud.google.com/go/internal/pretty/diff_test.go new file mode 100644 index 0000000000..e6ef1790ce --- /dev/null +++ b/vendor/cloud.google.com/go/internal/pretty/diff_test.go @@ -0,0 +1,50 @@ +// 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 pretty + +import "testing" + +func TestDiff(t *testing.T) { + for _, test := range []struct { + v1, v2 interface{} + ok bool + want string + }{ + {5, 5, true, ""}, + {"foo", "foo", true, ""}, + {[]int{1, 2, 3}, []int{1, 0, 3}, false, `--- want ++++ got +@@ -1,5 +1,5 @@ + []int{ + 1, +- 2, ++ 0, + 3, + } +`}, + } { + got, ok, err := Diff(test.v1, test.v2) + if err != nil { + t.Errorf("%v vs. %v: %v", test.v1, test.v2, err) + continue + } + if ok != test.ok { + t.Errorf("%v vs. %v: got %t, want %t", test.v1, test.v2, ok, test.ok) + } + if got != test.want { + t.Errorf("%v vs. %v: got:\n%q\nwant:\n%q", test.v1, test.v2, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/internal/pretty/pretty.go b/vendor/cloud.google.com/go/internal/pretty/pretty.go new file mode 100644 index 0000000000..f55b8f9499 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/pretty/pretty.go @@ -0,0 +1,254 @@ +// 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 pretty implements a simple pretty-printer. It is intended for +// debugging the output of tests. +// +// It follows pointers and produces multi-line output for complex values like +// slices, maps and structs. +package pretty + +import ( + "fmt" + "io" + "reflect" + "sort" + "strings" + "time" +) + +// Indent is the string output at each level of indentation. +var Indent = " " + +// Value returns a value that will print prettily when used as an +// argument for the %v or %s format specifiers. +// With no flags, struct fields and map keys with default values are omitted. +// With the '+' or '#' flags, all values are displayed. +// +// This package does not detect cycles. Attempting to print a Value that +// contains cycles will result in unbounded recursion. +func Value(v interface{}) val { return val{v: v} } + +type val struct{ v interface{} } + +// Format implements the fmt.Formatter interface. +func (v val) Format(s fmt.State, c rune) { + if c == 'v' || c == 's' { + fprint(s, reflect.ValueOf(v.v), state{ + defaults: s.Flag('+') || s.Flag('#'), + }) + } else { + fmt.Fprintf(s, "%%!%c(pretty.Val)", c) + } +} + +type state struct { + level int + prefix, suffix string + defaults bool +} + +const maxLevel = 100 + +var typeOfTime = reflect.TypeOf(time.Time{}) + +func fprint(w io.Writer, v reflect.Value, s state) { + if s.level > maxLevel { + fmt.Fprintln(w, "pretty: max nested depth exceeded") + return + } + indent := strings.Repeat(Indent, s.level) + fmt.Fprintf(w, "%s%s", indent, s.prefix) + if isNil(v) { + fmt.Fprintf(w, "nil%s", s.suffix) + return + } + if v.Type().Kind() == reflect.Interface { + v = v.Elem() + } + if v.Type() == typeOfTime { + fmt.Fprintf(w, "%s%s", v.Interface(), s.suffix) + return + } + for v.Type().Kind() == reflect.Ptr { + fmt.Fprintf(w, "&") + v = v.Elem() + } + switch v.Type().Kind() { + default: + fmt.Fprintf(w, "%s%s", short(v), s.suffix) + + case reflect.Array: + fmt.Fprintf(w, "%s{\n", v.Type()) + for i := 0; i < v.Len(); i++ { + fprint(w, v.Index(i), state{ + level: s.level + 1, + prefix: "", + suffix: ",", + defaults: s.defaults, + }) + fmt.Fprintln(w) + } + fmt.Fprintf(w, "%s}", indent) + + case reflect.Slice: + fmt.Fprintf(w, "%s{", v.Type()) + if v.Len() > 0 { + fmt.Fprintln(w) + for i := 0; i < v.Len(); i++ { + fprint(w, v.Index(i), state{ + level: s.level + 1, + prefix: "", + suffix: ",", + defaults: s.defaults, + }) + fmt.Fprintln(w) + } + } + fmt.Fprintf(w, "%s}%s", indent, s.suffix) + + case reflect.Map: + fmt.Fprintf(w, "%s{", v.Type()) + if v.Len() > 0 { + fmt.Fprintln(w) + keys := v.MapKeys() + maybeSort(keys, v.Type().Key()) + for _, key := range keys { + val := v.MapIndex(key) + if s.defaults || !isDefault(val) { + fprint(w, val, state{ + level: s.level + 1, + prefix: short(key) + ": ", + suffix: ",", + defaults: s.defaults, + }) + fmt.Fprintln(w) + } + } + } + fmt.Fprintf(w, "%s}%s", indent, s.suffix) + + case reflect.Struct: + t := v.Type() + fmt.Fprintf(w, "%s{\n", t) + for i := 0; i < t.NumField(); i++ { + f := v.Field(i) + if s.defaults || !isDefault(f) { + fprint(w, f, state{ + level: s.level + 1, + prefix: t.Field(i).Name + ": ", + suffix: ",", + defaults: s.defaults, + }) + fmt.Fprintln(w) + } + } + fmt.Fprintf(w, "%s}%s", indent, s.suffix) + } +} + +func isNil(v reflect.Value) bool { + if !v.IsValid() { + return true + } + switch v.Type().Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + default: + return false + } +} + +func isDefault(v reflect.Value) bool { + if !v.IsValid() { + return true + } + t := v.Type() + switch t.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + default: + if !v.CanInterface() { + return false + } + return t.Comparable() && v.Interface() == reflect.Zero(t).Interface() + } +} + +// short returns a short, one-line string for v. +func short(v reflect.Value) string { + if !v.IsValid() { + return "nil" + } + if v.Type().Kind() == reflect.String { + return fmt.Sprintf("%q", v) + } + return fmt.Sprintf("%v", v) +} + +func indent(w io.Writer, level int) { + for i := 0; i < level; i++ { + io.WriteString(w, Indent) // ignore errors + } +} + +func maybeSort(vs []reflect.Value, t reflect.Type) { + if less := lessFunc(t); less != nil { + sort.Sort(&sorter{vs, less}) + } +} + +// lessFunc returns a function that implements the "<" operator +// for the given type, or nil if the type doesn't support "<" . +func lessFunc(t reflect.Type) func(v1, v2 interface{}) bool { + switch t.Kind() { + case reflect.String: + return func(v1, v2 interface{}) bool { return v1.(string) < v2.(string) } + case reflect.Int: + return func(v1, v2 interface{}) bool { return v1.(int) < v2.(int) } + case reflect.Int8: + return func(v1, v2 interface{}) bool { return v1.(int8) < v2.(int8) } + case reflect.Int16: + return func(v1, v2 interface{}) bool { return v1.(int16) < v2.(int16) } + case reflect.Int32: + return func(v1, v2 interface{}) bool { return v1.(int32) < v2.(int32) } + case reflect.Int64: + return func(v1, v2 interface{}) bool { return v1.(int64) < v2.(int64) } + case reflect.Uint: + return func(v1, v2 interface{}) bool { return v1.(uint) < v2.(uint) } + case reflect.Uint8: + return func(v1, v2 interface{}) bool { return v1.(uint8) < v2.(uint8) } + case reflect.Uint16: + return func(v1, v2 interface{}) bool { return v1.(uint16) < v2.(uint16) } + case reflect.Uint32: + return func(v1, v2 interface{}) bool { return v1.(uint32) < v2.(uint32) } + case reflect.Uint64: + return func(v1, v2 interface{}) bool { return v1.(uint64) < v2.(uint64) } + case reflect.Float32: + return func(v1, v2 interface{}) bool { return v1.(float32) < v2.(float32) } + case reflect.Float64: + return func(v1, v2 interface{}) bool { return v1.(float64) < v2.(float64) } + default: + return nil + } +} + +type sorter struct { + vs []reflect.Value + less func(v1, v2 interface{}) bool +} + +func (s *sorter) Len() int { return len(s.vs) } +func (s *sorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] } +func (s *sorter) Less(i, j int) bool { return s.less(s.vs[i].Interface(), s.vs[j].Interface()) } diff --git a/vendor/cloud.google.com/go/internal/pretty/pretty_test.go b/vendor/cloud.google.com/go/internal/pretty/pretty_test.go new file mode 100644 index 0000000000..50984f938c --- /dev/null +++ b/vendor/cloud.google.com/go/internal/pretty/pretty_test.go @@ -0,0 +1,105 @@ +// 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 pretty + +import ( + "fmt" + "strings" + "testing" +) + +type S struct { + X int + Y bool + z *string +} + +func TestSprint(t *testing.T) { + Indent = "~" + i := 17 + + for _, test := range []struct { + value interface{} + want string + }{ + // primitives and pointer + {nil, "nil"}, + {3, "3"}, + {9.8, "9.8"}, + {true, "true"}, + {"foo", `"foo"`}, + {&i, "&17"}, + // array and slice + {[3]int{1, 2, 3}, "[3]int{\n~1,\n~2,\n~3,\n}"}, + {[]int{1, 2, 3}, "[]int{\n~1,\n~2,\n~3,\n}"}, + {[]int{}, "[]int{}"}, + {[]string{"foo"}, "[]string{\n~\"foo\",\n}"}, + // map + {map[int]bool{}, "map[int]bool{}"}, + {map[int]bool{1: true, 2: false, 3: true}, + "map[int]bool{\n~1: true,\n~3: true,\n}"}, + // struct + {S{}, "pretty.S{\n}"}, + {S{3, true, ptr("foo")}, + "pretty.S{\n~X: 3,\n~Y: true,\n~z: &\"foo\",\n}"}, + // interface + {[]interface{}{&i}, "[]interface {}{\n~&17,\n}"}, + // nesting + {[]S{{1, false, ptr("a")}, {2, true, ptr("b")}}, + `[]pretty.S{ +~pretty.S{ +~~X: 1, +~~z: &"a", +~}, +~pretty.S{ +~~X: 2, +~~Y: true, +~~z: &"b", +~}, +}`}, + } { + got := fmt.Sprintf("%v", Value(test.value)) + if got != test.want { + t.Errorf("%v: got:\n%q\nwant:\n%q", test.value, got, test.want) + } + } +} + +func TestWithDefaults(t *testing.T) { + Indent = "~" + for _, test := range []struct { + value interface{} + want string + }{ + {map[int]bool{1: true, 2: false, 3: true}, + "map[int]bool{\n~1: true,\n~2: false,\n~3: true,\n}"}, + {S{}, "pretty.S{\n~X: 0,\n~Y: false,\n~z: nil,\n}"}, + } { + got := fmt.Sprintf("%+v", Value(test.value)) + if got != test.want { + t.Errorf("%v: got:\n%q\nwant:\n%q", test.value, got, test.want) + } + } +} + +func TestBadVerb(t *testing.T) { + got := fmt.Sprintf("%d", Value(8)) + want := "%!d(" + if !strings.HasPrefix(got, want) { + t.Errorf("got %q, want prefix %q", got, want) + } +} + +func ptr(s string) *string { return &s } diff --git a/vendor/cloud.google.com/go/internal/protostruct/protostruct.go b/vendor/cloud.google.com/go/internal/protostruct/protostruct.go new file mode 100644 index 0000000000..3f8ebfb5aa --- /dev/null +++ b/vendor/cloud.google.com/go/internal/protostruct/protostruct.go @@ -0,0 +1,56 @@ +// Copyright 2017 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 protostruct supports operations on the protocol buffer Struct message. +package protostruct + +import ( + pb "github.com/golang/protobuf/ptypes/struct" +) + +// DecodeToMap converts a pb.Struct to a map from strings to Go types. +// DecodeToMap panics if s is invalid. +func DecodeToMap(s *pb.Struct) map[string]interface{} { + if s == nil { + return nil + } + m := map[string]interface{}{} + for k, v := range s.Fields { + m[k] = decodeValue(v) + } + return m +} + +func decodeValue(v *pb.Value) interface{} { + switch k := v.Kind.(type) { + case *pb.Value_NullValue: + return nil + case *pb.Value_NumberValue: + return k.NumberValue + case *pb.Value_StringValue: + return k.StringValue + case *pb.Value_BoolValue: + return k.BoolValue + case *pb.Value_StructValue: + return DecodeToMap(k.StructValue) + case *pb.Value_ListValue: + s := make([]interface{}, len(k.ListValue.Values)) + for i, e := range k.ListValue.Values { + s[i] = decodeValue(e) + } + return s + default: + panic("protostruct: unknown kind") + } +} diff --git a/vendor/cloud.google.com/go/internal/protostruct/protostruct_test.go b/vendor/cloud.google.com/go/internal/protostruct/protostruct_test.go new file mode 100644 index 0000000000..f864f7f935 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/protostruct/protostruct_test.go @@ -0,0 +1,58 @@ +// Copyright 2017 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 protostruct supports operations on the protocol buffer Struct message. +package protostruct + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + pb "github.com/golang/protobuf/ptypes/struct" +) + +func TestDecodeToMap(t *testing.T) { + if got := DecodeToMap(nil); !testutil.Equal(got, map[string]interface{}(nil)) { + t.Errorf("DecodeToMap(nil) = %v, want nil", got) + } + nullv := &pb.Value{Kind: &pb.Value_NullValue{}} + stringv := &pb.Value{Kind: &pb.Value_StringValue{"x"}} + boolv := &pb.Value{Kind: &pb.Value_BoolValue{true}} + numberv := &pb.Value{Kind: &pb.Value_NumberValue{2.7}} + in := &pb.Struct{Fields: map[string]*pb.Value{ + "n": nullv, + "s": stringv, + "b": boolv, + "f": numberv, + "l": &pb.Value{Kind: &pb.Value_ListValue{&pb.ListValue{ + Values: []*pb.Value{nullv, stringv, boolv, numberv}, + }}}, + "S": &pb.Value{Kind: &pb.Value_StructValue{&pb.Struct{Fields: map[string]*pb.Value{ + "n1": nullv, + "b1": boolv, + }}}}, + }} + want := map[string]interface{}{ + "n": nil, + "s": "x", + "b": true, + "f": 2.7, + "l": []interface{}{nil, "x", true, 2.7}, + "S": map[string]interface{}{"n1": nil, "b1": true}, + } + got := DecodeToMap(in) + if diff := testutil.Diff(got, want); diff != "" { + t.Error(diff) + } +} diff --git a/vendor/cloud.google.com/go/internal/readme/Makefile b/vendor/cloud.google.com/go/internal/readme/Makefile new file mode 100644 index 0000000000..1c1f4e1d33 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/Makefile @@ -0,0 +1,48 @@ +# Rebuild the README.md file at repo root by inserting code samples +# from compilable go files. + +SHELL=/bin/bash + +GOCLOUD_HOME=$(GOPATH)/src/cloud.google.com/go +README=$(GOCLOUD_HOME)/README.md + +.PHONY: readme test test-good test-bad-go test-bad-md + +readme: + @tmp=$$(mktemp); \ + awk -f snipmd.awk snippets.go $(README) > $$tmp; \ + mv $$tmp $(README) + +diff: + diff $(README) <(awk -f snipmd.awk snippets.go $(README)) + +test: test-good test-bad-go test-bad-md + @echo PASS + +test-good: + @echo testdata/good.md + @cd testdata >& /dev/null; \ + diff -u want.md <(awk -f ../snipmd.awk snips.go good.md) + @echo "testdata/want.md (round trip)" + @cd testdata >& /dev/null; \ + diff -u want.md <(awk -f ../snipmd.awk snips.go want.md) + +test-bad-go: + @for f in testdata/bad-*.go; do \ + echo $$f; \ + if awk -f snipmd.awk $$f >& /dev/null; then \ + echo "$f succeeded, want failure"; \ + exit 1; \ + fi; \ + done + +test-bad-md: + @for f in testdata/bad-*.md; do \ + echo $$f; \ + if awk -f snipmd.awk testdata/snips.go $$f >& /dev/null; then \ + echo "$f succeeded, want failure"; \ + exit 1; \ + fi; \ + done + + diff --git a/vendor/cloud.google.com/go/internal/readme/snipmd.awk b/vendor/cloud.google.com/go/internal/readme/snipmd.awk new file mode 100644 index 0000000000..d694e7575d --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/snipmd.awk @@ -0,0 +1,123 @@ +# Copyright 2017 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. + +# snipmd inserts code snippets from Go source files into a markdown file. +# +# Call with one or more .go files and a .md file: +# +# awk -f snipmd.awk foo.go bar.go template.md +# +# In the Go files, start a snippet with +# //[ NAME +# and end it with +# //] +# +# In the markdown, write +# [snip]:# NAME +# to insert the snippet NAME just below that line. +# If there is already a code block after the [snip]:# line, it will be +# replaced, so a previous output can be used as input. +# +# The following transformations are made to the Go code: +# - The first tab of each line is removed. +# - Trailing blank lines are removed. +# - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...` + + +/^[ \t]*\/\/\[/ { # start snippet in Go file + if (inGo()) { + if ($2 == "") { + die("missing snippet name") + } + curSnip = $2 + next + } +} + +/^[ \t]*\/\/]/ { # end snippet in Go file + if (inGo()) { + if (curSnip != "") { + # Remove all but one trailing newline. + gsub(/\n+$/, "\n", snips[curSnip]) + curSnip = "" + next + } else { + die("//] without corresponding //[") + } + } +} + +ENDFILE { + if (curSnip != "") { + die("unclosed snippet: " curSnip) + } +} + +# Skip code blocks in the input that immediately follow [snip]:# lines, +# because we just inserted the snippet. Supports round-tripping. +/^```go$/,/^```$/ { + if (inMarkdown() && afterSnip) { + next + } +} + +# Matches every line. +{ + if (curSnip != "") { + line = $0 + # Remove initial tab, if any. + if (line ~ /^\t/) { + line = substr(line, 2) + } + # Replace ELLIPSIS. + gsub(/_ = ELLIPSIS/, "...", line) + gsub(/ELLIPSIS/, "...", line) + + snips[curSnip] = snips[curSnip] line "\n" + } else if (inMarkdown()) { + afterSnip = 0 + # Copy .md to output. + print + } +} + +$1 ~ /\[snip\]:#/ { # Snippet marker in .md file. + if (inMarkdown()) { + # We expect '[snip]:#' to be followed by '(NAME)' + if ($2 !~ /\(.*\)/) { + die("bad snip spec: " $0) + } + name = substr($2, 2, length($2)-2) + if (snips[name] == "") { + die("no snippet named " name) + } + printf("```go\n%s```\n", snips[name]) + afterSnip = 1 + } +} + + +function inMarkdown() { + return match(FILENAME, /\.md$/) +} + +function inGo() { + return match(FILENAME, /\.go$/) +} + + +function die(msg) { + printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr" + exit 1 +} diff --git a/vendor/cloud.google.com/go/internal/readme/snippets.go b/vendor/cloud.google.com/go/internal/readme/snippets.go new file mode 100644 index 0000000000..114bd22e56 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/snippets.go @@ -0,0 +1,241 @@ +// Copyright 2017 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. + +// This file holds samples that are embedded into README.md. + +// This file has to compile, but need not execute. +// If it fails to compile, fix it, then run `make` to regenerate README.md. + +package readme + +import ( + "fmt" + "io/ioutil" + "log" + "time" + + "cloud.google.com/go/bigquery" + "cloud.google.com/go/datastore" + "cloud.google.com/go/logging" + "cloud.google.com/go/pubsub" + "cloud.google.com/go/spanner" + "cloud.google.com/go/storage" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var ctx context.Context + +const END = 0 + +func auth() { + //[ auth + client, err := storage.NewClient(ctx) + //] + _ = client + _ = err +} + +func auth2() { + //[ auth-JSON + client, err := storage.NewClient(ctx, option.WithServiceAccountFile("path/to/keyfile.json")) + //] + _ = client + _ = err +} + +func auth3() { + var ELLIPSIS oauth2.TokenSource + //[ auth-ts + tokenSource := ELLIPSIS + client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource)) + //] + _ = client + _ = err +} + +func datastoreSnippets() { + //[ datastore-1 + client, err := datastore.NewClient(ctx, "my-project-id") + if err != nil { + log.Fatal(err) + } + //] + + //[ datastore-2 + type Post struct { + Title string + Body string `datastore:",noindex"` + PublishedAt time.Time + } + keys := []*datastore.Key{ + datastore.NameKey("Post", "post1", nil), + datastore.NameKey("Post", "post2", nil), + } + posts := []*Post{ + {Title: "Post 1", Body: "...", PublishedAt: time.Now()}, + {Title: "Post 2", Body: "...", PublishedAt: time.Now()}, + } + if _, err := client.PutMulti(ctx, keys, posts); err != nil { + log.Fatal(err) + } + //] +} + +func storageSnippets() { + //[ storage-1 + client, err := storage.NewClient(ctx) + if err != nil { + log.Fatal(err) + } + //] + + //[ storage-2 + // Read the object1 from bucket. + rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx) + if err != nil { + log.Fatal(err) + } + defer rc.Close() + body, err := ioutil.ReadAll(rc) + if err != nil { + log.Fatal(err) + } + //] + _ = body +} + +func pubsubSnippets() { + //[ pubsub-1 + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + log.Fatal(err) + } + //] + + const ELLIPSIS = 0 + + //[ pubsub-2 + // Publish "hello world" on topic1. + topic := client.Topic("topic1") + res := topic.Publish(ctx, &pubsub.Message{ + Data: []byte("hello world"), + }) + // The publish happens asynchronously. + // Later, you can get the result from res: + _ = ELLIPSIS + msgID, err := res.Get(ctx) + if err != nil { + log.Fatal(err) + } + + // Use a callback to receive messages via subscription1. + sub := client.Subscription("subscription1") + err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { + fmt.Println(m.Data) + m.Ack() // Acknowledge that we've consumed the message. + }) + if err != nil { + log.Println(err) + } + //] + _ = msgID +} + +func bqSnippets() { + //[ bq-1 + c, err := bigquery.NewClient(ctx, "my-project-ID") + if err != nil { + // TODO: Handle error. + } + //] + + //[ bq-2 + // Construct a query. + q := c.Query(` + SELECT year, SUM(number) + FROM [bigquery-public-data:usa_names.usa_1910_2013] + WHERE name = "William" + GROUP BY year + ORDER BY year +`) + // Execute the query. + it, err := q.Read(ctx) + if err != nil { + // TODO: Handle error. + } + // Iterate through the results. + for { + var values []bigquery.Value + err := it.Next(&values) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(values) + } + //] +} + +func loggingSnippets() { + //[ logging-1 + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + //] + //[ logging-2 + logger := client.Logger("my-log") + logger.Log(logging.Entry{Payload: "something happened!"}) + //] + + //[ logging-3 + err = client.Close() + if err != nil { + // TODO: Handle error. + } + //] +} + +func spannerSnippets() { + //[ spanner-1 + client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D") + if err != nil { + log.Fatal(err) + } + //] + + //[ spanner-2 + // Simple Reads And Writes + _, err = client.Apply(ctx, []*spanner.Mutation{ + spanner.Insert("Users", + []string{"name", "email"}, + []interface{}{"alice", "a@example.com"})}) + if err != nil { + log.Fatal(err) + } + row, err := client.Single().ReadRow(ctx, "Users", + spanner.Key{"alice"}, []string{"email"}) + if err != nil { + log.Fatal(err) + } + //] + _ = row +} diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/bad-no-name.go b/vendor/cloud.google.com/go/internal/readme/testdata/bad-no-name.go new file mode 100644 index 0000000000..33218ea339 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/bad-no-name.go @@ -0,0 +1,23 @@ +// Copyright 2017 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 readme + +import "fmt" + +func f() { + //[ + fmt.Println() + //] +} diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/bad-no-open.go b/vendor/cloud.google.com/go/internal/readme/testdata/bad-no-open.go new file mode 100644 index 0000000000..b9f5619b99 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/bad-no-open.go @@ -0,0 +1,19 @@ +// Copyright 2017 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 readme + +func f() { + //] +} diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/bad-nosnip.md b/vendor/cloud.google.com/go/internal/readme/testdata/bad-nosnip.md new file mode 100644 index 0000000000..e3a1a3c5cc --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/bad-nosnip.md @@ -0,0 +1,2 @@ +[snip]:# (unknown) + diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/bad-spec.md b/vendor/cloud.google.com/go/internal/readme/testdata/bad-spec.md new file mode 100644 index 0000000000..2f46ad853c --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/bad-spec.md @@ -0,0 +1 @@ +[snip]:# missing-parens diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/bad-unclosed.go b/vendor/cloud.google.com/go/internal/readme/testdata/bad-unclosed.go new file mode 100644 index 0000000000..6b9a3da161 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/bad-unclosed.go @@ -0,0 +1,21 @@ +// Copyright 2017 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 readme + +// unclosed snippet + +func f() { + //[ X +} diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/good.md b/vendor/cloud.google.com/go/internal/readme/testdata/good.md new file mode 100644 index 0000000000..c300afea80 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/good.md @@ -0,0 +1,18 @@ +This template is for testing snipmd.awk. + +Put the first snippet here. + +[snip]:# (first) + +And now the second. +[snip]:# (second) + +A top-level snippet. + +[snip]:# (top-level) + +```go +// A code block that is not included. +``` + +And we're done. diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/snips.go b/vendor/cloud.google.com/go/internal/readme/testdata/snips.go new file mode 100644 index 0000000000..e35f791618 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/snips.go @@ -0,0 +1,39 @@ +// Copyright 2017 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 readme + +import ( + "errors" + "fmt" +) + +func f() { + ELLIPSIS := 3 + //[ first + fmt.Println("hello") + x := ELLIPSIS + //] + + //[ second + if x > 2 { + _ = ELLIPSIS + } + //] +} + +//[ top-level +var ErrBad = errors.New("bad") + +//] diff --git a/vendor/cloud.google.com/go/internal/readme/testdata/want.md b/vendor/cloud.google.com/go/internal/readme/testdata/want.md new file mode 100644 index 0000000000..176bd061ab --- /dev/null +++ b/vendor/cloud.google.com/go/internal/readme/testdata/want.md @@ -0,0 +1,30 @@ +This template is for testing snipmd.awk. + +Put the first snippet here. + +[snip]:# (first) +```go +fmt.Println("hello") +x := ... +``` + +And now the second. +[snip]:# (second) +```go +if x > 2 { + ... +} +``` + +A top-level snippet. + +[snip]:# (top-level) +```go +var ErrBad = errors.New("bad") +``` + +```go +// A code block that is not included. +``` + +And we're done. diff --git a/vendor/cloud.google.com/go/internal/retry.go b/vendor/cloud.google.com/go/internal/retry.go new file mode 100644 index 0000000000..e5ee25ac4b --- /dev/null +++ b/vendor/cloud.google.com/go/internal/retry.go @@ -0,0 +1,55 @@ +// 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 internal + +import ( + "time" + + gax "github.com/googleapis/gax-go" + + "golang.org/x/net/context" +) + +// Retry calls the supplied function f repeatedly according to the provided +// backoff parameters. It returns when one of the following occurs: +// When f's first return value is true, Retry immediately returns with f's second +// return value. +// When the provided context is done, Retry returns with an error that +// includes both ctx.Error() and the last error returned by f. +func Retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error)) error { + return retry(ctx, bo, f, gax.Sleep) +} + +func retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error), + sleep func(context.Context, time.Duration) error) error { + var lastErr error + for { + stop, err := f() + if stop { + return err + } + // Remember the last "real" error from f. + if err != nil && err != context.Canceled && err != context.DeadlineExceeded { + lastErr = err + } + p := bo.Pause() + if cerr := sleep(ctx, p); cerr != nil { + if lastErr != nil { + return Annotatef(lastErr, "retry failed with %v; last error", cerr) + } + return cerr + } + } +} diff --git a/vendor/cloud.google.com/go/internal/retry_test.go b/vendor/cloud.google.com/go/internal/retry_test.go new file mode 100644 index 0000000000..b1945f1ae3 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/retry_test.go @@ -0,0 +1,89 @@ +// 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 internal + +import ( + "errors" + "fmt" + "testing" + "time" + + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestRetry(t *testing.T) { + ctx := context.Background() + // Without a context deadline, retry will run until the function + // says not to retry any more. + n := 0 + endRetry := errors.New("end retry") + err := retry(ctx, gax.Backoff{}, + func() (bool, error) { + n++ + if n < 10 { + return false, nil + } + return true, endRetry + }, + func(context.Context, time.Duration) error { return nil }) + if got, want := err, endRetry; got != want { + t.Errorf("got %v, want %v", err, endRetry) + } + if n != 10 { + t.Errorf("n: got %d, want %d", n, 10) + } + + // If the context has a deadline, sleep will return an error + // and end the function. + n = 0 + err = retry(ctx, gax.Backoff{}, + func() (bool, error) { return false, nil }, + func(context.Context, time.Duration) error { + n++ + if n < 10 { + return nil + } + return context.DeadlineExceeded + }) + if err == nil { + t.Error("got nil, want error") + } +} + +func TestRetryPreserveError(t *testing.T) { + // Retry tries to preserve the type and other information from + // the last error returned by the function. + err := retry(context.Background(), gax.Backoff{}, + func() (bool, error) { + return false, status.Error(codes.NotFound, "not found") + }, + func(context.Context, time.Duration) error { + return context.DeadlineExceeded + }) + got, ok := status.FromError(err) + if !ok { + t.Fatalf("got %T, wanted a status", got) + } + if g, w := got.Code(), codes.NotFound; g != w { + t.Errorf("got code %v, want %v", g, w) + } + wantMessage := fmt.Sprintf("retry failed with %v; last error: not found", context.DeadlineExceeded) + if g, w := got.Message(), wantMessage; g != w { + t.Errorf("got message %q, want %q", g, w) + } +} diff --git a/vendor/cloud.google.com/go/internal/snipdoc/README.md b/vendor/cloud.google.com/go/internal/snipdoc/README.md new file mode 100644 index 0000000000..5d284296f6 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/snipdoc/README.md @@ -0,0 +1,29 @@ +# Snipdoc + +Snipdoc is a simple tool for maintaining package documentation that contains +code samples. + +1. Create a subdirectory of your package to hold the following files. "internal" + is a good name. + +2. Write a template file (for example, "doc.template") with the text of your package documentation. The file +should look exactly like you want your doc.go file to look, except for code +snippets. +Instead of embedding a code snippet, write a line consisting solely of + + [NAME] + + for your choice of NAME. + +3. Write a snippets file (for example, "doc-snippets.go") as a valid Go source + file. Begin each snippet you'd like to appear in your package docs with + `//[ NAME` and end it with `//]`. + +4. Construct your doc.go file with the command + ``` + awk -f snipdoc.awk doc-snippets.go doc.template + ``` + The file "sample-makefile" in this directory verifies that the + snippets file compiles and safely constructs a doc.go file. + + diff --git a/vendor/cloud.google.com/go/internal/snipdoc/sample-makefile b/vendor/cloud.google.com/go/internal/snipdoc/sample-makefile new file mode 100644 index 0000000000..6769cb36ea --- /dev/null +++ b/vendor/cloud.google.com/go/internal/snipdoc/sample-makefile @@ -0,0 +1,16 @@ +# Build doc.go from template and snippets. + +SHELL=/bin/bash + +../doc.go: build doc-snippets.go doc.template snipdoc.awk + @tmp=$$(mktemp) && \ + awk -f snipdoc.awk doc-snippets.go doc.template > $$tmp && \ + chmod +w $@ && \ + mv $$tmp $@ && \ + chmod -w $@ + @echo "wrote $@" + +.PHONY: build + +build: + go build doc-snippets.go diff --git a/vendor/cloud.google.com/go/internal/snipdoc/snipdoc.awk b/vendor/cloud.google.com/go/internal/snipdoc/snipdoc.awk new file mode 100644 index 0000000000..98e788ebdf --- /dev/null +++ b/vendor/cloud.google.com/go/internal/snipdoc/snipdoc.awk @@ -0,0 +1,116 @@ +# Copyright 2017 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. + +# snipdoc merges code snippets from Go source files into a template to +# produce another go file (typically doc.go). +# +# Call with one or more .go files and a template file. +# +# awk -f snipmd.awk foo.go bar.go doc.template +# +# In the Go files, start a snippet with +# //[ NAME +# and end it with +# //] +# +# In the template, write +# [NAME] +# on a line by itself to insert the snippet NAME on that line. +# +# The following transformations are made to the Go code: +# - Trailing blank lines are removed. +# - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...` + + +/^[ \t]*\/\/\[/ { # start snippet in Go file + if (inGo()) { + if ($2 == "") { + die("missing snippet name") + } + curSnip = $2 + next + } +} + +/^[ \t]*\/\/]/ { # end snippet in Go file + if (inGo()) { + if (curSnip != "") { + # Remove all trailing newlines. + gsub(/[\t\n]+$/, "", snips[curSnip]) + curSnip = "" + next + } else { + die("//] without corresponding //[") + } + } +} + +ENDFILE { + if (curSnip != "") { + die("unclosed snippet: " curSnip) + } +} + +/^\[.*\]$/ { # Snippet marker in template file. + if (inTemplate()) { + name = substr($1, 2, length($1)-2) + if (snips[name] == "") { + die("no snippet named " name) + } + printf("%s\n", snips[name]) + afterSnip = 1 + next + } +} + +# Matches every line. +{ + if (curSnip != "") { + # If the first line in the snip has no indent, add the indent. + if (snips[curSnip] == "") { + if (index($0, "\t") == 1) { + extraIndent = "" + } else { + extraIndent = "\t" + } + } + + line = $0 + # Replace ELLIPSIS. + gsub(/_ = ELLIPSIS/, "...", line) + gsub(/ELLIPSIS/, "...", line) + + snips[curSnip] = snips[curSnip] extraIndent line "\n" + } else if (inTemplate()) { + afterSnip = 0 + # Copy to output. + print + } +} + + + +function inTemplate() { + return match(FILENAME, /\.template$/) +} + +function inGo() { + return match(FILENAME, /\.go$/) +} + + +function die(msg) { + printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr" + exit 1 +} diff --git a/vendor/cloud.google.com/go/internal/testutil/cmp.go b/vendor/cloud.google.com/go/internal/testutil/cmp.go new file mode 100644 index 0000000000..9dd3d9111e --- /dev/null +++ b/vendor/cloud.google.com/go/internal/testutil/cmp.go @@ -0,0 +1,61 @@ +// Copyright 2017 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 testutil + +import ( + "math" + "math/big" + + "github.com/golang/protobuf/proto" + "github.com/google/go-cmp/cmp" +) + +var ( + alwaysEqual = cmp.Comparer(func(_, _ interface{}) bool { return true }) + + defaultCmpOptions = []cmp.Option{ + // Use proto.Equal for protobufs + cmp.Comparer(proto.Equal), + // Use big.Rat.Cmp for big.Rats + cmp.Comparer(func(x, y *big.Rat) bool { + if x == nil || y == nil { + return x == y + } + return x.Cmp(y) == 0 + }), + // NaNs compare equal + cmp.FilterValues(func(x, y float64) bool { + return math.IsNaN(x) && math.IsNaN(y) + }, alwaysEqual), + cmp.FilterValues(func(x, y float32) bool { + return math.IsNaN(float64(x)) && math.IsNaN(float64(y)) + }, alwaysEqual), + } +) + +// Equal tests two values for equality. +func Equal(x, y interface{}, opts ...cmp.Option) bool { + // Put default options at the end. Order doesn't matter. + opts = append(opts[:len(opts):len(opts)], defaultCmpOptions...) + return cmp.Equal(x, y, opts...) +} + +// Diff reports the differences between two values. +// Diff(x, y) == "" iff Equal(x, y). +func Diff(x, y interface{}, opts ...cmp.Option) string { + // Put default options at the end. Order doesn't matter. + opts = append(opts[:len(opts):len(opts)], defaultCmpOptions...) + return cmp.Diff(x, y, opts...) +} diff --git a/vendor/cloud.google.com/go/internal/testutil/context.go b/vendor/cloud.google.com/go/internal/testutil/context.go new file mode 100644 index 0000000000..136cf48f3a --- /dev/null +++ b/vendor/cloud.google.com/go/internal/testutil/context.go @@ -0,0 +1,106 @@ +// 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 testutil contains helper functions for writing tests. +package testutil + +import ( + "fmt" + "io/ioutil" + "log" + "os" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + "golang.org/x/oauth2/jwt" +) + +const ( + envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID" + envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY" +) + +// ProjID returns the project ID to use in integration tests, or the empty +// string if none is configured. +func ProjID() string { + return os.Getenv(envProjID) +} + +// TokenSource returns the OAuth2 token source to use in integration tests, +// or nil if none is configured. It uses the standard environment variable +// for tests in this repo. +func TokenSource(ctx context.Context, scopes ...string) oauth2.TokenSource { + return TokenSourceEnv(ctx, envPrivateKey, scopes...) +} + +// TokenSourceEnv returns the OAuth2 token source to use in integration tests. or nil +// if none is configured. It tries to get credentials from the filename in the +// environment variable envVar. If the environment variable is unset, TokenSourceEnv +// will try to find 'Application Default Credentials'. Else, TokenSourceEnv will +// return nil. TokenSourceEnv will log.Fatal if the token source is specified but +// missing or invalid. +func TokenSourceEnv(ctx context.Context, envVar string, scopes ...string) oauth2.TokenSource { + key := os.Getenv(envVar) + if key == "" { // Try for application default credentials. + ts, err := google.DefaultTokenSource(ctx, scopes...) + if err != nil { + log.Println("No 'Application Default Credentials' found.") + return nil + } + return ts + } + conf, err := jwtConfigFromFile(key, scopes) + if err != nil { + log.Fatal(err) + } + return conf.TokenSource(ctx) +} + +// JWTConfig reads the JSON private key file whose name is in the default +// environment variable, and returns the jwt.Config it contains. It ignores +// scopes. +// If the environment variable is empty, it returns (nil, nil). +func JWTConfig() (*jwt.Config, error) { + return jwtConfigFromFile(os.Getenv(envPrivateKey), nil) +} + +// jwtConfigFromFile reads the given JSON private key file, and returns the +// jwt.Config it contains. +// If the filename is empty, it returns (nil, nil). +func jwtConfigFromFile(filename string, scopes []string) (*jwt.Config, error) { + if filename == "" { + return nil, nil + } + jsonKey, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("Cannot read the JSON key file, err: %v", err) + } + conf, err := google.JWTConfigFromJSON(jsonKey, scopes...) + if err != nil { + return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err) + } + return conf, nil +} + +// CanReplay reports whether an integration test can be run in replay mode. +// The replay file must exist, and the GCLOUD_TESTS_GOLANG_ENABLE_REPLAY +// environment variable must be non-empty. +func CanReplay(replayFilename string) bool { + if os.Getenv("GCLOUD_TESTS_GOLANG_ENABLE_REPLAY") == "" { + return false + } + _, err := os.Stat(replayFilename) + return err == nil +} diff --git a/vendor/cloud.google.com/go/internal/testutil/go18.go b/vendor/cloud.google.com/go/internal/testutil/go18.go new file mode 100644 index 0000000000..77d4543faa --- /dev/null +++ b/vendor/cloud.google.com/go/internal/testutil/go18.go @@ -0,0 +1,64 @@ +// 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. + +// +build go1.8 + +package testutil + +import ( + "log" + "time" + + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/stats/view" + "go.opencensus.io/trace" +) + +type TestExporter struct { + Spans []*trace.SpanData + Stats chan *view.Data +} + +func NewTestExporter() *TestExporter { + te := &TestExporter{Stats: make(chan *view.Data)} + + view.RegisterExporter(te) + view.SetReportingPeriod(time.Millisecond) + if err := view.Register(ocgrpc.DefaultClientViews...); err != nil { + log.Fatal(err) + } + + trace.RegisterExporter(te) + trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) + + return te +} + +func (te *TestExporter) ExportSpan(s *trace.SpanData) { + te.Spans = append(te.Spans, s) +} + +func (te *TestExporter) ExportView(vd *view.Data) { + if len(vd.Rows) > 0 { + select { + case te.Stats <- vd: + default: + } + } +} + +func (te *TestExporter) Unregister() { + view.UnregisterExporter(te) + trace.UnregisterExporter(te) +} diff --git a/vendor/cloud.google.com/go/internal/testutil/rand.go b/vendor/cloud.google.com/go/internal/testutil/rand.go new file mode 100644 index 0000000000..57d6c1403f --- /dev/null +++ b/vendor/cloud.google.com/go/internal/testutil/rand.go @@ -0,0 +1,44 @@ +// 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 testutil + +import ( + "math/rand" + "sync" + "time" +) + +// NewRand creates a new *rand.Rand seeded with t. The return value is safe for use +// with multiple goroutines. +func NewRand(t time.Time) *rand.Rand { + s := &lockedSource{src: rand.NewSource(t.UnixNano())} + return rand.New(s) +} + +// lockedSource makes a rand.Source safe for use by multiple goroutines. +type lockedSource struct { + mu sync.Mutex + src rand.Source +} + +func (ls *lockedSource) Int63() int64 { + ls.mu.Lock() + defer ls.mu.Unlock() + return ls.src.Int63() +} + +func (ls *lockedSource) Seed(int64) { + panic("shouldn't be calling Seed") +} diff --git a/vendor/cloud.google.com/go/internal/testutil/server.go b/vendor/cloud.google.com/go/internal/testutil/server.go new file mode 100644 index 0000000000..f8f669a01d --- /dev/null +++ b/vendor/cloud.google.com/go/internal/testutil/server.go @@ -0,0 +1,105 @@ +/* +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 testutil + +import ( + "net" + "strconv" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// A Server is an in-process gRPC server, listening on a system-chosen port on +// the local loopback interface. Servers are for testing only and are not +// intended to be used in production code. +// +// To create a server, make a new Server, register your handlers, then call +// Start: +// +// srv, err := NewServer() +// ... +// mypb.RegisterMyServiceServer(srv.Gsrv, &myHandler) +// .... +// srv.Start() +// +// Clients should connect to the server with no security: +// +// conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) +// ... +type Server struct { + Addr string + l net.Listener + Gsrv *grpc.Server +} + +// NewServer creates a new Server. The Server will be listening for gRPC connections +// at the address named by the Addr field, without TLS. +func NewServer(opts ...grpc.ServerOption) (*Server, error) { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + s := &Server{ + Addr: l.Addr().String(), + l: l, + Gsrv: grpc.NewServer(opts...), + } + return s, nil +} + +// Start causes the server to start accepting incoming connections. +// Call Start after registering handlers. +func (s *Server) Start() { + go s.Gsrv.Serve(s.l) +} + +// Close shuts down the server. +func (s *Server) Close() { + s.Gsrv.Stop() + s.l.Close() +} + +// PageBounds converts an incoming page size and token from an RPC request into +// slice bounds and the outgoing next-page token. +// +// PageBounds assumes that the complete, unpaginated list of items exists as a +// single slice. In addition to the page size and token, PageBounds needs the +// length of that slice. +// +// PageBounds's first two return values should be used to construct a sub-slice of +// the complete, unpaginated slice. E.g. if the complete slice is s, then +// s[from:to] is the desired page. Its third return value should be set as the +// NextPageToken field of the RPC response. +func PageBounds(pageSize int, pageToken string, length int) (from, to int, nextPageToken string, err error) { + from, to = 0, length + if pageToken != "" { + from, err = strconv.Atoi(pageToken) + if err != nil { + return 0, 0, "", status.Errorf(codes.InvalidArgument, "bad page token: %v", err) + } + if from >= length { + return length, length, "", nil + } + } + if pageSize > 0 && from+pageSize < length { + to = from + pageSize + nextPageToken = strconv.Itoa(to) + } + return from, to, nextPageToken, nil +} diff --git a/vendor/cloud.google.com/go/internal/testutil/server_test.go b/vendor/cloud.google.com/go/internal/testutil/server_test.go new file mode 100644 index 0000000000..aa88d43e67 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/testutil/server_test.go @@ -0,0 +1,79 @@ +// 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 testutil + +import ( + "testing" + + grpc "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +func TestNewServer(t *testing.T) { + srv, err := NewServer() + if err != nil { + t.Fatal(err) + } + srv.Start() + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + if err != nil { + t.Fatal(err) + } + conn.Close() + srv.Close() +} + +func TestPageBounds(t *testing.T) { + const length = 10 + for _, test := range []struct { + size int + tok string + wantFrom int + wantTo int + wantTok string + }{ + {5, "", + 0, 5, "5"}, + {11, "", + 0, 10, ""}, + {5, "2", + 2, 7, "7"}, + {5, "8", + 8, 10, ""}, + {11, "8", + 8, 10, ""}, + {1, "11", + 10, 10, ""}, + } { + gotFrom, gotTo, gotTok, err := PageBounds(test.size, test.tok, length) + if err != nil { + t.Fatal(err) + } + if got, want := gotFrom, test.wantFrom; got != want { + t.Errorf("%+v: from: got %d, want %d", test, got, want) + } + if got, want := gotTo, test.wantTo; got != want { + t.Errorf("%+v: to: got %d, want %d", test, got, want) + } + if got, want := gotTok, test.wantTok; got != want { + t.Errorf("%+v: got %q, want %q", test, got, want) + } + } + + _, _, _, err := PageBounds(4, "xyz", 5) + if grpc.Code(err) != codes.InvalidArgument { + t.Errorf("want invalid argument, got <%v>", err) + } +} diff --git a/vendor/cloud.google.com/go/internal/trace/go18.go b/vendor/cloud.google.com/go/internal/trace/go18.go new file mode 100644 index 0000000000..b3160f6bd4 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/trace/go18.go @@ -0,0 +1,83 @@ +// 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. + +// +build go1.8 + +package trace + +import ( + "go.opencensus.io/trace" + "golang.org/x/net/context" + "google.golang.org/api/googleapi" + "google.golang.org/genproto/googleapis/rpc/code" + "google.golang.org/grpc/status" +) + +func StartSpan(ctx context.Context, name string) context.Context { + ctx, _ = trace.StartSpan(ctx, name) + return ctx +} + +func EndSpan(ctx context.Context, err error) { + span := trace.FromContext(ctx) + if err != nil { + span.SetStatus(toStatus(err)) + } + span.End() +} + +// ToStatus interrogates an error and converts it to an appropriate +// OpenCensus status. +func toStatus(err error) trace.Status { + if err2, ok := err.(*googleapi.Error); ok { + return trace.Status{Code: httpStatusCodeToOCCode(err2.Code), Message: err2.Message} + } else if s, ok := status.FromError(err); ok { + return trace.Status{Code: int32(s.Code()), Message: s.Message()} + } else { + return trace.Status{Code: int32(code.Code_UNKNOWN), Message: err.Error()} + } +} + +// TODO (deklerk): switch to using OpenCensus function when it becomes available. +// Reference: https://github.com/googleapis/googleapis/blob/26b634d2724ac5dd30ae0b0cbfb01f07f2e4050e/google/rpc/code.proto +func httpStatusCodeToOCCode(httpStatusCode int) int32 { + switch httpStatusCode { + case 200: + return int32(code.Code_OK) + case 499: + return int32(code.Code_CANCELLED) + case 500: + return int32(code.Code_UNKNOWN) // Could also be Code_INTERNAL, Code_DATA_LOSS + case 400: + return int32(code.Code_INVALID_ARGUMENT) // Could also be Code_OUT_OF_RANGE + case 504: + return int32(code.Code_DEADLINE_EXCEEDED) + case 404: + return int32(code.Code_NOT_FOUND) + case 409: + return int32(code.Code_ALREADY_EXISTS) // Could also be Code_ABORTED + case 403: + return int32(code.Code_PERMISSION_DENIED) + case 401: + return int32(code.Code_UNAUTHENTICATED) + case 429: + return int32(code.Code_RESOURCE_EXHAUSTED) + case 501: + return int32(code.Code_UNIMPLEMENTED) + case 503: + return int32(code.Code_UNAVAILABLE) + default: + return int32(code.Code_UNKNOWN) + } +} diff --git a/vendor/cloud.google.com/go/internal/trace/go18_test.go b/vendor/cloud.google.com/go/internal/trace/go18_test.go new file mode 100644 index 0000000000..f907270f3f --- /dev/null +++ b/vendor/cloud.google.com/go/internal/trace/go18_test.go @@ -0,0 +1,55 @@ +// 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. + +// +build go1.8 + +package trace + +import ( + "errors" + "net/http" + "testing" + + "cloud.google.com/go/internal/testutil" + octrace "go.opencensus.io/trace" + "google.golang.org/api/googleapi" + "google.golang.org/genproto/googleapis/rpc/code" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestToStatus(t *testing.T) { + for _, testcase := range []struct { + input error + want octrace.Status + }{ + { + errors.New("some random error"), + octrace.Status{Code: int32(code.Code_UNKNOWN), Message: "some random error"}, + }, + { + &googleapi.Error{Code: http.StatusConflict, Message: "some specific googleapi http error"}, + octrace.Status{Code: int32(code.Code_ALREADY_EXISTS), Message: "some specific googleapi http error"}, + }, + { + status.Error(codes.DataLoss, "some specific grpc error"), + octrace.Status{Code: int32(code.Code_DATA_LOSS), Message: "some specific grpc error"}, + }, + } { + got := toStatus(testcase.input) + if r := testutil.Diff(got, testcase.want); r != "" { + t.Errorf("got -, want +:\n%s", r) + } + } +} diff --git a/vendor/cloud.google.com/go/internal/trace/not_go18.go b/vendor/cloud.google.com/go/internal/trace/not_go18.go new file mode 100644 index 0000000000..c893ed53c0 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/trace/not_go18.go @@ -0,0 +1,30 @@ +// 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. + +// +build !go1.8 + +package trace + +import ( + "golang.org/x/net/context" +) + +// OpenCensus only supports go 1.8 and higher. + +func StartSpan(ctx context.Context, _ string) context.Context { + return ctx +} + +func EndSpan(context.Context, error) { +} diff --git a/vendor/cloud.google.com/go/internal/tracecontext/tracecontext.go b/vendor/cloud.google.com/go/internal/tracecontext/tracecontext.go new file mode 100644 index 0000000000..827ace2e0b --- /dev/null +++ b/vendor/cloud.google.com/go/internal/tracecontext/tracecontext.go @@ -0,0 +1,83 @@ +// Copyright 2017 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 tracecontext provides encoders and decoders for Stackdriver Trace contexts. +package tracecontext + +import "encoding/binary" + +const ( + versionID = 0 + traceIDField = 0 + spanIDField = 1 + optsField = 2 + + traceIDLen = 16 + spanIDLen = 8 + optsLen = 1 + + // Len represents the length of trace context. + Len = 1 + 1 + traceIDLen + 1 + spanIDLen + 1 + optsLen +) + +// Encode encodes trace ID, span ID and options into dst. The number of bytes +// written will be returned. If len(dst) isn't big enough to fit the trace context, +// a negative number is returned. +func Encode(dst []byte, traceID []byte, spanID uint64, opts byte) (n int) { + if len(dst) < Len { + return -1 + } + var offset = 0 + putByte := func(b byte) { dst[offset] = b; offset++ } + putUint64 := func(u uint64) { binary.LittleEndian.PutUint64(dst[offset:], u); offset += 8 } + + putByte(versionID) + putByte(traceIDField) + for _, b := range traceID { + putByte(b) + } + putByte(spanIDField) + putUint64(spanID) + putByte(optsField) + putByte(opts) + + return offset +} + +// Decode decodes the src into a trace ID, span ID and options. If src doesn't +// contain a valid trace context, ok = false is returned. +func Decode(src []byte) (traceID []byte, spanID uint64, opts byte, ok bool) { + if len(src) < Len { + return traceID, spanID, 0, false + } + var offset = 0 + readByte := func() byte { b := src[offset]; offset++; return b } + readUint64 := func() uint64 { v := binary.LittleEndian.Uint64(src[offset:]); offset += 8; return v } + + if readByte() != versionID { + return traceID, spanID, 0, false + } + for offset < len(src) { + switch readByte() { + case traceIDField: + traceID = src[offset : offset+traceIDLen] + offset += traceIDLen + case spanIDField: + spanID = readUint64() + case optsField: + opts = readByte() + } + } + return traceID, spanID, opts, true +} diff --git a/vendor/cloud.google.com/go/internal/tracecontext/tracecontext_test.go b/vendor/cloud.google.com/go/internal/tracecontext/tracecontext_test.go new file mode 100644 index 0000000000..60c27b5540 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/tracecontext/tracecontext_test.go @@ -0,0 +1,136 @@ +// 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 tracecontext + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" +) + +var validData = []byte{0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, 98, 99, 100, 101, 102, 103, 104, 2, 1} + +func TestDecode(t *testing.T) { + tests := []struct { + name string + data []byte + wantTraceID []byte + wantSpanID uint64 + wantOpts byte + wantOk bool + }{ + { + name: "nil data", + data: nil, + wantTraceID: nil, + wantSpanID: 0, + wantOpts: 0, + wantOk: false, + }, + { + name: "short data", + data: []byte{0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77}, + wantTraceID: nil, + wantSpanID: 0, + wantOpts: 0, + wantOk: false, + }, + { + name: "wrong field number", + data: []byte{0, 1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77}, + wantTraceID: nil, + wantSpanID: 0, + wantOpts: 0, + wantOk: false, + }, + { + name: "valid data", + data: validData, + wantTraceID: []byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, + wantSpanID: 0x6867666564636261, + wantOpts: 1, + wantOk: true, + }, + } + for _, tt := range tests { + gotTraceID, gotSpanID, gotOpts, gotOk := Decode(tt.data) + if !testutil.Equal(gotTraceID, tt.wantTraceID) { + t.Errorf("%s: Decode() gotTraceID = %v, want %v", tt.name, gotTraceID, tt.wantTraceID) + } + if gotSpanID != tt.wantSpanID { + t.Errorf("%s: Decode() gotSpanID = %v, want %v", tt.name, gotSpanID, tt.wantSpanID) + } + if gotOpts != tt.wantOpts { + t.Errorf("%s: Decode() gotOpts = %v, want %v", tt.name, gotOpts, tt.wantOpts) + } + if gotOk != tt.wantOk { + t.Errorf("%s: Decode() gotOk = %v, want %v", tt.name, gotOk, tt.wantOk) + } + } +} + +func TestEncode(t *testing.T) { + tests := []struct { + name string + dst []byte + traceID []byte + spanID uint64 + opts byte + wantN int + wantData []byte + }{ + { + name: "short data", + dst: make([]byte, 0), + traceID: []byte("00112233445566"), + spanID: 0x6867666564636261, + opts: 1, + wantN: -1, + wantData: make([]byte, 0), + }, + { + name: "valid data", + dst: make([]byte, Len), + traceID: []byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, + spanID: 0x6867666564636261, + opts: 1, + wantN: Len, + wantData: validData, + }, + } + for _, tt := range tests { + gotN := Encode(tt.dst, tt.traceID, tt.spanID, tt.opts) + if gotN != tt.wantN { + t.Errorf("%s: n = %v, want %v", tt.name, gotN, tt.wantN) + } + if gotData := tt.dst; !testutil.Equal(gotData, tt.wantData) { + t.Errorf("%s: dst = %v, want %v", tt.name, gotData, tt.wantData) + } + } +} + +func BenchmarkDecode(b *testing.B) { + for i := 0; i < b.N; i++ { + Decode(validData) + } +} + +func BenchmarkEncode(b *testing.B) { + for i := 0; i < b.N; i++ { + traceID := make([]byte, 16) + var opts byte + Encode(validData, traceID, 0, opts) + } +} diff --git a/vendor/cloud.google.com/go/internal/uid/uid.go b/vendor/cloud.google.com/go/internal/uid/uid.go new file mode 100644 index 0000000000..036aa91759 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/uid/uid.go @@ -0,0 +1,110 @@ +// Copyright 2017 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 uid supports generating unique IDs. Its chief purpose is to prevent +// multiple test executions from interfering with each other, and to facilitate +// cleanup of old entities that may remain if tests exit early. +package uid + +import ( + "fmt" + "regexp" + "strconv" + "sync" + "time" +) + +// A Space manages a set of unique IDs distinguished by a prefix. +type Space struct { + Prefix string // Prefix of UIDs. Read-only. + Sep rune // Separates UID parts. Read-only. + Time time.Time // Timestamp for UIDs. Read-only. + re *regexp.Regexp + mu sync.Mutex + count int +} + +// Options are optional values for a Space. +type Options struct { + Sep rune // Separates parts of the UID. Defaults to '-'. + Time time.Time // Timestamp for all UIDs made with this space. Defaults to current time. +} + +func NewSpace(prefix string, opts *Options) *Space { + sep := '-' + tm := time.Now().UTC() + if opts != nil { + if opts.Sep != 0 { + sep = opts.Sep + } + if !opts.Time.IsZero() { + tm = opts.Time + } + } + re := fmt.Sprintf(`^%s%[2]c(\d{4})(\d{2})(\d{2})%[2]c(\d+)%[2]c\d+$`, + regexp.QuoteMeta(prefix), sep) + return &Space{ + Prefix: prefix, + Sep: sep, + Time: tm, + re: regexp.MustCompile(re), + } +} + +// New generates a new unique ID. The ID consists of the Space's prefix, a +// timestamp, and a counter value. All unique IDs generated in the same test +// execution will have the same timestamp. +// +// Aside from the characters in the prefix, IDs contain only letters, numbers +// and sep. +func (s *Space) New() string { + s.mu.Lock() + c := s.count + s.count++ + s.mu.Unlock() + // Write the time as a date followed by nanoseconds from midnight of that date. + // That makes it easier to see the approximate time of the ID when it is displayed. + y, m, d := s.Time.Date() + ns := s.Time.Sub(time.Date(y, m, d, 0, 0, 0, 0, time.UTC)) + // Zero-pad the counter for lexical sort order for IDs with the same timestamp. + return fmt.Sprintf("%s%c%04d%02d%02d%c%d%c%04d", + s.Prefix, s.Sep, y, m, d, s.Sep, ns, s.Sep, c) +} + +// Timestamp extracts the timestamp of uid, which must have been generated by +// s. The second return value is true on success, false if there was a problem. +func (s *Space) Timestamp(uid string) (time.Time, bool) { + subs := s.re.FindStringSubmatch(uid) + if subs == nil { + return time.Time{}, false + } + y, err1 := strconv.Atoi(subs[1]) + m, err2 := strconv.Atoi(subs[2]) + d, err3 := strconv.Atoi(subs[3]) + ns, err4 := strconv.Atoi(subs[4]) + if err1 != nil || err2 != nil || err3 != nil || err4 != nil { + return time.Time{}, false + } + return time.Date(y, time.Month(m), d, 0, 0, 0, ns, time.UTC), true +} + +// Older reports whether uid was created by m and has a timestamp older than +// the current time by at least d. +func (s *Space) Older(uid string, d time.Duration) bool { + ts, ok := s.Timestamp(uid) + if !ok { + return false + } + return time.Since(ts) > d +} diff --git a/vendor/cloud.google.com/go/internal/uid/uid_test.go b/vendor/cloud.google.com/go/internal/uid/uid_test.go new file mode 100644 index 0000000000..6bbd4970b8 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/uid/uid_test.go @@ -0,0 +1,70 @@ +// Copyright 2017 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 uid + +import ( + "testing" + "time" +) + +func TestNew(t *testing.T) { + tm := time.Date(2017, 1, 6, 0, 0, 0, 21, time.UTC) + s := NewSpace("prefix", &Options{Time: tm}) + got := s.New() + want := "prefix-20170106-21-0000" + if got != want { + t.Errorf("got %q, want %q", got, want) + } + + s2 := NewSpace("prefix2", &Options{Sep: '_', Time: tm}) + got = s2.New() + want = "prefix2_20170106_21_0000" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestTimestamp(t *testing.T) { + s := NewSpace("unique-ID", nil) + startTime := s.Time + uid := s.New() + got, ok := s.Timestamp(uid) + if !ok { + t.Fatal("got ok = false, want true") + } + if !startTime.Equal(got) { + t.Errorf("got %s, want %s", got, startTime) + } + + got, ok = s.Timestamp("unique-ID-20160308-123-8") + if !ok { + t.Fatal("got false, want true") + } + if want := time.Date(2016, 3, 8, 0, 0, 0, 123, time.UTC); !want.Equal(got) { + t.Errorf("got %s, want %s", got, want) + } + if _, ok = s.Timestamp("invalid-time-1234"); ok { + t.Error("got true, want false") + } +} + +func TestOlder(t *testing.T) { + s := NewSpace("uid", nil) + // A non-matching ID returns false. + id2 := NewSpace("different-prefix", nil).New() + if got, want := s.Older(id2, time.Second), false; got != want { + t.Errorf("got %t, want %t", got, want) + } +} diff --git a/vendor/cloud.google.com/go/internal/version/update_version.sh b/vendor/cloud.google.com/go/internal/version/update_version.sh new file mode 100755 index 0000000000..fecf1f03fd --- /dev/null +++ b/vendor/cloud.google.com/go/internal/version/update_version.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +today=$(date +%Y%m%d) + +sed -i -r -e 's/const Repo = "([0-9]{8})"/const Repo = "'$today'"/' $GOFILE + diff --git a/vendor/cloud.google.com/go/internal/version/version.go b/vendor/cloud.google.com/go/internal/version/version.go new file mode 100644 index 0000000000..220f02c1e1 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/version/version.go @@ -0,0 +1,71 @@ +// 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. + +//go:generate ./update_version.sh + +// Package version contains version information for Google Cloud Client +// Libraries for Go, as reported in request headers. +package version + +import ( + "runtime" + "strings" + "unicode" +) + +// Repo is the current version of the client libraries in this +// repo. It should be a date in YYYYMMDD format. +const Repo = "20180226" + +// Go returns the Go runtime version. The returned string +// has no whitespace. +func Go() string { + return goVersion +} + +var goVersion = goVer(runtime.Version()) + +const develPrefix = "devel +" + +func goVer(s string) string { + if strings.HasPrefix(s, develPrefix) { + s = s[len(develPrefix):] + if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 { + s = s[:p] + } + return s + } + + 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 "" +} + +func notSemverRune(r rune) bool { + return strings.IndexRune("0123456789.", r) < 0 +} diff --git a/vendor/cloud.google.com/go/internal/version/version_test.go b/vendor/cloud.google.com/go/internal/version/version_test.go new file mode 100644 index 0000000000..6253db8fc8 --- /dev/null +++ b/vendor/cloud.google.com/go/internal/version/version_test.go @@ -0,0 +1,35 @@ +// 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 version + +import "testing" + +func TestGoVer(t *testing.T) { + for _, tst := range []struct { + in, want string + }{ + {"go1.8", "1.8.0"}, + {"go1.7.3", "1.7.3"}, + {"go1.8.typealias", "1.8.0-typealias"}, + {"go1.8beta1", "1.8.0-beta1"}, + {"go1.8rc2", "1.8.0-rc2"}, + {"devel +824f981dd4b7 Tue Apr 29 21:41:54 2014 -0400", "824f981dd4b7"}, + {"foo bar zipzap", ""}, + } { + if got := goVer(tst.in); got != tst.want { + t.Errorf("goVer(%q) = %q, want %q", tst.in, got, tst.want) + } + } +} diff --git a/vendor/cloud.google.com/go/issue_template.md b/vendor/cloud.google.com/go/issue_template.md new file mode 100644 index 0000000000..e2ccef3e78 --- /dev/null +++ b/vendor/cloud.google.com/go/issue_template.md @@ -0,0 +1,17 @@ +(delete this for feature requests) + +## Client + +e.g. PubSub + +## Describe Your Environment + +e.g. Alpine Docker on GKE + +## Expected Behavior + +e.g. Messages arrive really fast. + +## Actual Behavior + +e.g. Messages arrive really slowly. \ No newline at end of file diff --git a/vendor/cloud.google.com/go/keys.tar.enc b/vendor/cloud.google.com/go/keys.tar.enc new file mode 100644 index 0000000000..c54408c93a Binary files /dev/null and b/vendor/cloud.google.com/go/keys.tar.enc differ diff --git a/vendor/cloud.google.com/go/kms/apiv1/doc.go b/vendor/cloud.google.com/go/kms/apiv1/doc.go new file mode 100644 index 0000000000..7afa68390e --- /dev/null +++ b/vendor/cloud.google.com/go/kms/apiv1/doc.go @@ -0,0 +1,50 @@ +// 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 kms is an auto-generated package for the +// Google Cloud Key Management Service (KMS) API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Manages encryption for your cloud services the same way you do +// on-premises. +// You can generate, use, rotate, and destroy AES256 encryption keys. +// +// Use the client at cloud.google.com/go/kms in preference to this. +package kms // import "cloud.google.com/go/kms/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/kms/apiv1/key_management_client.go b/vendor/cloud.google.com/go/kms/apiv1/key_management_client.go new file mode 100644 index 0000000000..dc00f48f09 --- /dev/null +++ b/vendor/cloud.google.com/go/kms/apiv1/key_management_client.go @@ -0,0 +1,652 @@ +// 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 kms + +import ( + "fmt" + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// KeyManagementCallOptions contains the retry settings for each method of KeyManagementClient. +type KeyManagementCallOptions struct { + ListKeyRings []gax.CallOption + ListCryptoKeys []gax.CallOption + ListCryptoKeyVersions []gax.CallOption + GetKeyRing []gax.CallOption + GetCryptoKey []gax.CallOption + GetCryptoKeyVersion []gax.CallOption + CreateKeyRing []gax.CallOption + CreateCryptoKey []gax.CallOption + CreateCryptoKeyVersion []gax.CallOption + UpdateCryptoKey []gax.CallOption + UpdateCryptoKeyVersion []gax.CallOption + Encrypt []gax.CallOption + Decrypt []gax.CallOption + UpdateCryptoKeyPrimaryVersion []gax.CallOption + DestroyCryptoKeyVersion []gax.CallOption + RestoreCryptoKeyVersion []gax.CallOption +} + +func defaultKeyManagementClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("cloudkms.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultKeyManagementCallOptions() *KeyManagementCallOptions { + 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 &KeyManagementCallOptions{ + ListKeyRings: retry[[2]string{"default", "idempotent"}], + ListCryptoKeys: retry[[2]string{"default", "idempotent"}], + ListCryptoKeyVersions: retry[[2]string{"default", "idempotent"}], + GetKeyRing: retry[[2]string{"default", "idempotent"}], + GetCryptoKey: retry[[2]string{"default", "idempotent"}], + GetCryptoKeyVersion: retry[[2]string{"default", "idempotent"}], + CreateKeyRing: retry[[2]string{"default", "non_idempotent"}], + CreateCryptoKey: retry[[2]string{"default", "non_idempotent"}], + CreateCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}], + UpdateCryptoKey: retry[[2]string{"default", "non_idempotent"}], + UpdateCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}], + Encrypt: retry[[2]string{"default", "non_idempotent"}], + Decrypt: retry[[2]string{"default", "non_idempotent"}], + UpdateCryptoKeyPrimaryVersion: retry[[2]string{"default", "non_idempotent"}], + DestroyCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}], + RestoreCryptoKeyVersion: retry[[2]string{"default", "non_idempotent"}], + } +} + +// KeyManagementClient is a client for interacting with Google Cloud Key Management Service (KMS) API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type KeyManagementClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + keyManagementClient kmspb.KeyManagementServiceClient + + // The call options for this service. + CallOptions *KeyManagementCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewKeyManagementClient creates a new key management service client. +// +// Google Cloud Key Management Service +// +// Manages cryptographic keys and operations using those keys. Implements a REST +// model with the following objects: +// +// [KeyRing][google.cloud.kms.v1.KeyRing] +// +// [CryptoKey][google.cloud.kms.v1.CryptoKey] +// +// [CryptoKeyVersion][google.cloud.kms.v1.CryptoKeyVersion] +func NewKeyManagementClient(ctx context.Context, opts ...option.ClientOption) (*KeyManagementClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultKeyManagementClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &KeyManagementClient{ + conn: conn, + CallOptions: defaultKeyManagementCallOptions(), + + keyManagementClient: kmspb.NewKeyManagementServiceClient(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *KeyManagementClient) 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 *KeyManagementClient) 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 *KeyManagementClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListKeyRings lists [KeyRings][google.cloud.kms.v1.KeyRing]. +func (c *KeyManagementClient) ListKeyRings(ctx context.Context, req *kmspb.ListKeyRingsRequest, opts ...gax.CallOption) *KeyRingIterator { + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent())) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) + opts = append(c.CallOptions.ListKeyRings[0:len(c.CallOptions.ListKeyRings):len(c.CallOptions.ListKeyRings)], opts...) + it := &KeyRingIterator{} + req = proto.Clone(req).(*kmspb.ListKeyRingsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*kmspb.KeyRing, string, error) { + var resp *kmspb.ListKeyRingsResponse + 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.keyManagementClient.ListKeyRings(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.KeyRings, 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 +} + +// ListCryptoKeys lists [CryptoKeys][google.cloud.kms.v1.CryptoKey]. +func (c *KeyManagementClient) ListCryptoKeys(ctx context.Context, req *kmspb.ListCryptoKeysRequest, opts ...gax.CallOption) *CryptoKeyIterator { + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent())) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) + opts = append(c.CallOptions.ListCryptoKeys[0:len(c.CallOptions.ListCryptoKeys):len(c.CallOptions.ListCryptoKeys)], opts...) + it := &CryptoKeyIterator{} + req = proto.Clone(req).(*kmspb.ListCryptoKeysRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*kmspb.CryptoKey, string, error) { + var resp *kmspb.ListCryptoKeysResponse + 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.keyManagementClient.ListCryptoKeys(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.CryptoKeys, 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 +} + +// ListCryptoKeyVersions lists [CryptoKeyVersions][google.cloud.kms.v1.CryptoKeyVersion]. +func (c *KeyManagementClient) ListCryptoKeyVersions(ctx context.Context, req *kmspb.ListCryptoKeyVersionsRequest, opts ...gax.CallOption) *CryptoKeyVersionIterator { + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent())) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) + opts = append(c.CallOptions.ListCryptoKeyVersions[0:len(c.CallOptions.ListCryptoKeyVersions):len(c.CallOptions.ListCryptoKeyVersions)], opts...) + it := &CryptoKeyVersionIterator{} + req = proto.Clone(req).(*kmspb.ListCryptoKeyVersionsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*kmspb.CryptoKeyVersion, string, error) { + var resp *kmspb.ListCryptoKeyVersionsResponse + 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.keyManagementClient.ListCryptoKeyVersions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.CryptoKeyVersions, 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 +} + +// GetKeyRing returns metadata for a given [KeyRing][google.cloud.kms.v1.KeyRing]. +func (c *KeyManagementClient) GetKeyRing(ctx context.Context, req *kmspb.GetKeyRingRequest, opts ...gax.CallOption) (*kmspb.KeyRing, 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.GetKeyRing[0:len(c.CallOptions.GetKeyRing):len(c.CallOptions.GetKeyRing)], opts...) + var resp *kmspb.KeyRing + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.GetKeyRing(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetCryptoKey returns metadata for a given [CryptoKey][google.cloud.kms.v1.CryptoKey], as well as its +// [primary][google.cloud.kms.v1.CryptoKey.primary] [CryptoKeyVersion][google.cloud.kms.v1.CryptoKeyVersion]. +func (c *KeyManagementClient) GetCryptoKey(ctx context.Context, req *kmspb.GetCryptoKeyRequest, opts ...gax.CallOption) (*kmspb.CryptoKey, 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.GetCryptoKey[0:len(c.CallOptions.GetCryptoKey):len(c.CallOptions.GetCryptoKey)], opts...) + var resp *kmspb.CryptoKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.GetCryptoKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetCryptoKeyVersion returns metadata for a given [CryptoKeyVersion][google.cloud.kms.v1.CryptoKeyVersion]. +func (c *KeyManagementClient) GetCryptoKeyVersion(ctx context.Context, req *kmspb.GetCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, 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.GetCryptoKeyVersion[0:len(c.CallOptions.GetCryptoKeyVersion):len(c.CallOptions.GetCryptoKeyVersion)], opts...) + var resp *kmspb.CryptoKeyVersion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.GetCryptoKeyVersion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateKeyRing create a new [KeyRing][google.cloud.kms.v1.KeyRing] in a given Project and Location. +func (c *KeyManagementClient) CreateKeyRing(ctx context.Context, req *kmspb.CreateKeyRingRequest, opts ...gax.CallOption) (*kmspb.KeyRing, 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.CreateKeyRing[0:len(c.CallOptions.CreateKeyRing):len(c.CallOptions.CreateKeyRing)], opts...) + var resp *kmspb.KeyRing + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.CreateKeyRing(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateCryptoKey create a new [CryptoKey][google.cloud.kms.v1.CryptoKey] within a [KeyRing][google.cloud.kms.v1.KeyRing]. +// +// [CryptoKey.purpose][google.cloud.kms.v1.CryptoKey.purpose] is required. +func (c *KeyManagementClient) CreateCryptoKey(ctx context.Context, req *kmspb.CreateCryptoKeyRequest, opts ...gax.CallOption) (*kmspb.CryptoKey, 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.CreateCryptoKey[0:len(c.CallOptions.CreateCryptoKey):len(c.CallOptions.CreateCryptoKey)], opts...) + var resp *kmspb.CryptoKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.CreateCryptoKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateCryptoKeyVersion create a new [CryptoKeyVersion][google.cloud.kms.v1.CryptoKeyVersion] in a [CryptoKey][google.cloud.kms.v1.CryptoKey]. +// +// The server will assign the next sequential id. If unset, +// [state][google.cloud.kms.v1.CryptoKeyVersion.state] will be set to +// [ENABLED][google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionState.ENABLED]. +func (c *KeyManagementClient) CreateCryptoKeyVersion(ctx context.Context, req *kmspb.CreateCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, 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.CreateCryptoKeyVersion[0:len(c.CallOptions.CreateCryptoKeyVersion):len(c.CallOptions.CreateCryptoKeyVersion)], opts...) + var resp *kmspb.CryptoKeyVersion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.CreateCryptoKeyVersion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateCryptoKey update a [CryptoKey][google.cloud.kms.v1.CryptoKey]. +func (c *KeyManagementClient) UpdateCryptoKey(ctx context.Context, req *kmspb.UpdateCryptoKeyRequest, opts ...gax.CallOption) (*kmspb.CryptoKey, error) { + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "crypto_key.name", req.GetCryptoKey().GetName())) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) + opts = append(c.CallOptions.UpdateCryptoKey[0:len(c.CallOptions.UpdateCryptoKey):len(c.CallOptions.UpdateCryptoKey)], opts...) + var resp *kmspb.CryptoKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.UpdateCryptoKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateCryptoKeyVersion update a [CryptoKeyVersion][google.cloud.kms.v1.CryptoKeyVersion]'s metadata. +// +// [state][google.cloud.kms.v1.CryptoKeyVersion.state] may be changed between +// [ENABLED][google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionState.ENABLED] and +// [DISABLED][google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionState.DISABLED] using this +// method. See [DestroyCryptoKeyVersion][google.cloud.kms.v1.KeyManagementService.DestroyCryptoKeyVersion] and [RestoreCryptoKeyVersion][google.cloud.kms.v1.KeyManagementService.RestoreCryptoKeyVersion] to +// move between other states. +func (c *KeyManagementClient) UpdateCryptoKeyVersion(ctx context.Context, req *kmspb.UpdateCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) { + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "crypto_key_version.name", req.GetCryptoKeyVersion().GetName())) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) + opts = append(c.CallOptions.UpdateCryptoKeyVersion[0:len(c.CallOptions.UpdateCryptoKeyVersion):len(c.CallOptions.UpdateCryptoKeyVersion)], opts...) + var resp *kmspb.CryptoKeyVersion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.UpdateCryptoKeyVersion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Encrypt encrypts data, so that it can only be recovered by a call to [Decrypt][google.cloud.kms.v1.KeyManagementService.Decrypt]. +func (c *KeyManagementClient) Encrypt(ctx context.Context, req *kmspb.EncryptRequest, opts ...gax.CallOption) (*kmspb.EncryptResponse, 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.Encrypt[0:len(c.CallOptions.Encrypt):len(c.CallOptions.Encrypt)], opts...) + var resp *kmspb.EncryptResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.Encrypt(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Decrypt decrypts data that was protected by [Encrypt][google.cloud.kms.v1.KeyManagementService.Encrypt]. +func (c *KeyManagementClient) Decrypt(ctx context.Context, req *kmspb.DecryptRequest, opts ...gax.CallOption) (*kmspb.DecryptResponse, 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.Decrypt[0:len(c.CallOptions.Decrypt):len(c.CallOptions.Decrypt)], opts...) + var resp *kmspb.DecryptResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.Decrypt(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateCryptoKeyPrimaryVersion update the version of a [CryptoKey][google.cloud.kms.v1.CryptoKey] that will be used in [Encrypt][google.cloud.kms.v1.KeyManagementService.Encrypt] +func (c *KeyManagementClient) UpdateCryptoKeyPrimaryVersion(ctx context.Context, req *kmspb.UpdateCryptoKeyPrimaryVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKey, 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.UpdateCryptoKeyPrimaryVersion[0:len(c.CallOptions.UpdateCryptoKeyPrimaryVersion):len(c.CallOptions.UpdateCryptoKeyPrimaryVersion)], opts...) + var resp *kmspb.CryptoKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.UpdateCryptoKeyPrimaryVersion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DestroyCryptoKeyVersion schedule a [CryptoKeyVersion][google.cloud.kms.v1.CryptoKeyVersion] for destruction. +// +// Upon calling this method, [CryptoKeyVersion.state][google.cloud.kms.v1.CryptoKeyVersion.state] will be set to +// [DESTROY_SCHEDULED][google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionState.DESTROY_SCHEDULED] +// and [destroy_time][google.cloud.kms.v1.CryptoKeyVersion.destroy_time] will be set to a time 24 +// hours in the future, at which point the [state][google.cloud.kms.v1.CryptoKeyVersion.state] +// will be changed to +// [DESTROYED][google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionState.DESTROYED], and the key +// material will be irrevocably destroyed. +// +// Before the [destroy_time][google.cloud.kms.v1.CryptoKeyVersion.destroy_time] is reached, +// [RestoreCryptoKeyVersion][google.cloud.kms.v1.KeyManagementService.RestoreCryptoKeyVersion] may be called to reverse the process. +func (c *KeyManagementClient) DestroyCryptoKeyVersion(ctx context.Context, req *kmspb.DestroyCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, 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.DestroyCryptoKeyVersion[0:len(c.CallOptions.DestroyCryptoKeyVersion):len(c.CallOptions.DestroyCryptoKeyVersion)], opts...) + var resp *kmspb.CryptoKeyVersion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.DestroyCryptoKeyVersion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// RestoreCryptoKeyVersion restore a [CryptoKeyVersion][google.cloud.kms.v1.CryptoKeyVersion] in the +// [DESTROY_SCHEDULED][google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionState.DESTROY_SCHEDULED], +// state. +// +// Upon restoration of the CryptoKeyVersion, [state][google.cloud.kms.v1.CryptoKeyVersion.state] +// will be set to [DISABLED][google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionState.DISABLED], +// and [destroy_time][google.cloud.kms.v1.CryptoKeyVersion.destroy_time] will be cleared. +func (c *KeyManagementClient) RestoreCryptoKeyVersion(ctx context.Context, req *kmspb.RestoreCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, 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.RestoreCryptoKeyVersion[0:len(c.CallOptions.RestoreCryptoKeyVersion):len(c.CallOptions.RestoreCryptoKeyVersion)], opts...) + var resp *kmspb.CryptoKeyVersion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.keyManagementClient.RestoreCryptoKeyVersion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CryptoKeyIterator manages a stream of *kmspb.CryptoKey. +type CryptoKeyIterator struct { + items []*kmspb.CryptoKey + 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 []*kmspb.CryptoKey, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *CryptoKeyIterator) 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 *CryptoKeyIterator) Next() (*kmspb.CryptoKey, error) { + var item *kmspb.CryptoKey + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *CryptoKeyIterator) bufLen() int { + return len(it.items) +} + +func (it *CryptoKeyIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// CryptoKeyVersionIterator manages a stream of *kmspb.CryptoKeyVersion. +type CryptoKeyVersionIterator struct { + items []*kmspb.CryptoKeyVersion + 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 []*kmspb.CryptoKeyVersion, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *CryptoKeyVersionIterator) 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 *CryptoKeyVersionIterator) Next() (*kmspb.CryptoKeyVersion, error) { + var item *kmspb.CryptoKeyVersion + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *CryptoKeyVersionIterator) bufLen() int { + return len(it.items) +} + +func (it *CryptoKeyVersionIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// KeyRingIterator manages a stream of *kmspb.KeyRing. +type KeyRingIterator struct { + items []*kmspb.KeyRing + 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 []*kmspb.KeyRing, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *KeyRingIterator) 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 *KeyRingIterator) Next() (*kmspb.KeyRing, error) { + var item *kmspb.KeyRing + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *KeyRingIterator) bufLen() int { + return len(it.items) +} + +func (it *KeyRingIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/kms/apiv1/key_management_client_example_test.go b/vendor/cloud.google.com/go/kms/apiv1/key_management_client_example_test.go new file mode 100644 index 0000000000..9a1c4b2541 --- /dev/null +++ b/vendor/cloud.google.com/go/kms/apiv1/key_management_client_example_test.go @@ -0,0 +1,340 @@ +// 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 kms_test + +import ( + "cloud.google.com/go/kms/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" +) + +func ExampleNewKeyManagementClient() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleKeyManagementClient_ListKeyRings() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.ListKeyRingsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListKeyRings(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleKeyManagementClient_ListCryptoKeys() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.ListCryptoKeysRequest{ + // TODO: Fill request struct fields. + } + it := c.ListCryptoKeys(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleKeyManagementClient_ListCryptoKeyVersions() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.ListCryptoKeyVersionsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListCryptoKeyVersions(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleKeyManagementClient_GetKeyRing() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.GetKeyRingRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetKeyRing(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_GetCryptoKey() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.GetCryptoKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetCryptoKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_GetCryptoKeyVersion() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.GetCryptoKeyVersionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetCryptoKeyVersion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_CreateKeyRing() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.CreateKeyRingRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateKeyRing(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_CreateCryptoKey() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.CreateCryptoKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateCryptoKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_CreateCryptoKeyVersion() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.CreateCryptoKeyVersionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateCryptoKeyVersion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_UpdateCryptoKey() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.UpdateCryptoKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateCryptoKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_UpdateCryptoKeyVersion() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.UpdateCryptoKeyVersionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateCryptoKeyVersion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_Encrypt() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.EncryptRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Encrypt(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_Decrypt() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.DecryptRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Decrypt(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_UpdateCryptoKeyPrimaryVersion() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.UpdateCryptoKeyPrimaryVersionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateCryptoKeyPrimaryVersion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_DestroyCryptoKeyVersion() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.DestroyCryptoKeyVersionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.DestroyCryptoKeyVersion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleKeyManagementClient_RestoreCryptoKeyVersion() { + ctx := context.Background() + c, err := kms.NewKeyManagementClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &kmspb.RestoreCryptoKeyVersionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.RestoreCryptoKeyVersion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/kms/apiv1/mock_test.go b/vendor/cloud.google.com/go/kms/apiv1/mock_test.go new file mode 100644 index 0000000000..af998f8e36 --- /dev/null +++ b/vendor/cloud.google.com/go/kms/apiv1/mock_test.go @@ -0,0 +1,1401 @@ +// 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 kms + +import ( + durationpb "github.com/golang/protobuf/ptypes/duration" + timestamppb "github.com/golang/protobuf/ptypes/timestamp" + kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" + iampb "google.golang.org/genproto/googleapis/iam/v1" + field_maskpb "google.golang.org/genproto/protobuf/field_mask" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockKeyManagementServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + kmspb.KeyManagementServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockKeyManagementServer) ListKeyRings(ctx context.Context, req *kmspb.ListKeyRingsRequest) (*kmspb.ListKeyRingsResponse, 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].(*kmspb.ListKeyRingsResponse), nil +} + +func (s *mockKeyManagementServer) ListCryptoKeys(ctx context.Context, req *kmspb.ListCryptoKeysRequest) (*kmspb.ListCryptoKeysResponse, 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].(*kmspb.ListCryptoKeysResponse), nil +} + +func (s *mockKeyManagementServer) ListCryptoKeyVersions(ctx context.Context, req *kmspb.ListCryptoKeyVersionsRequest) (*kmspb.ListCryptoKeyVersionsResponse, 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].(*kmspb.ListCryptoKeyVersionsResponse), nil +} + +func (s *mockKeyManagementServer) GetKeyRing(ctx context.Context, req *kmspb.GetKeyRingRequest) (*kmspb.KeyRing, 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].(*kmspb.KeyRing), nil +} + +func (s *mockKeyManagementServer) GetCryptoKey(ctx context.Context, req *kmspb.GetCryptoKeyRequest) (*kmspb.CryptoKey, 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].(*kmspb.CryptoKey), nil +} + +func (s *mockKeyManagementServer) GetCryptoKeyVersion(ctx context.Context, req *kmspb.GetCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, 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].(*kmspb.CryptoKeyVersion), nil +} + +func (s *mockKeyManagementServer) CreateKeyRing(ctx context.Context, req *kmspb.CreateKeyRingRequest) (*kmspb.KeyRing, 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].(*kmspb.KeyRing), nil +} + +func (s *mockKeyManagementServer) CreateCryptoKey(ctx context.Context, req *kmspb.CreateCryptoKeyRequest) (*kmspb.CryptoKey, 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].(*kmspb.CryptoKey), nil +} + +func (s *mockKeyManagementServer) CreateCryptoKeyVersion(ctx context.Context, req *kmspb.CreateCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, 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].(*kmspb.CryptoKeyVersion), nil +} + +func (s *mockKeyManagementServer) UpdateCryptoKey(ctx context.Context, req *kmspb.UpdateCryptoKeyRequest) (*kmspb.CryptoKey, 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].(*kmspb.CryptoKey), nil +} + +func (s *mockKeyManagementServer) UpdateCryptoKeyVersion(ctx context.Context, req *kmspb.UpdateCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, 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].(*kmspb.CryptoKeyVersion), nil +} + +func (s *mockKeyManagementServer) Encrypt(ctx context.Context, req *kmspb.EncryptRequest) (*kmspb.EncryptResponse, 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].(*kmspb.EncryptResponse), nil +} + +func (s *mockKeyManagementServer) Decrypt(ctx context.Context, req *kmspb.DecryptRequest) (*kmspb.DecryptResponse, 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].(*kmspb.DecryptResponse), nil +} + +func (s *mockKeyManagementServer) UpdateCryptoKeyPrimaryVersion(ctx context.Context, req *kmspb.UpdateCryptoKeyPrimaryVersionRequest) (*kmspb.CryptoKey, 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].(*kmspb.CryptoKey), nil +} + +func (s *mockKeyManagementServer) DestroyCryptoKeyVersion(ctx context.Context, req *kmspb.DestroyCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, 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].(*kmspb.CryptoKeyVersion), nil +} + +func (s *mockKeyManagementServer) RestoreCryptoKeyVersion(ctx context.Context, req *kmspb.RestoreCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, 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].(*kmspb.CryptoKeyVersion), nil +} + +type mockIamPolicyServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + iampb.IAMPolicyServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockIamPolicyServer) 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 *mockIamPolicyServer) 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 *mockIamPolicyServer) 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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockKeyManagement mockKeyManagementServer + mockIamPolicy mockIamPolicyServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + kmspb.RegisterKeyManagementServiceServer(serv, &mockKeyManagement) + iampb.RegisterIAMPolicyServer(serv, &mockIamPolicy) + + 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 TestKeyManagementServiceListKeyRings(t *testing.T) { + var nextPageToken string = "" + var totalSize int32 = 705419236 + var keyRingsElement *kmspb.KeyRing = &kmspb.KeyRing{} + var keyRings = []*kmspb.KeyRing{keyRingsElement} + var expectedResponse = &kmspb.ListKeyRingsResponse{ + NextPageToken: nextPageToken, + TotalSize: totalSize, + KeyRings: keyRings, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]") + var request = &kmspb.ListKeyRingsRequest{ + Parent: formattedParent, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListKeyRings(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.KeyRings[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 TestKeyManagementServiceListKeyRingsError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]") + var request = &kmspb.ListKeyRingsRequest{ + Parent: formattedParent, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListKeyRings(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 TestKeyManagementServiceListCryptoKeys(t *testing.T) { + var nextPageToken string = "" + var totalSize int32 = 705419236 + var cryptoKeysElement *kmspb.CryptoKey = &kmspb.CryptoKey{} + var cryptoKeys = []*kmspb.CryptoKey{cryptoKeysElement} + var expectedResponse = &kmspb.ListCryptoKeysResponse{ + NextPageToken: nextPageToken, + TotalSize: totalSize, + CryptoKeys: cryptoKeys, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]") + var request = &kmspb.ListCryptoKeysRequest{ + Parent: formattedParent, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListCryptoKeys(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.CryptoKeys[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 TestKeyManagementServiceListCryptoKeysError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]") + var request = &kmspb.ListCryptoKeysRequest{ + Parent: formattedParent, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListCryptoKeys(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 TestKeyManagementServiceListCryptoKeyVersions(t *testing.T) { + var nextPageToken string = "" + var totalSize int32 = 705419236 + var cryptoKeyVersionsElement *kmspb.CryptoKeyVersion = &kmspb.CryptoKeyVersion{} + var cryptoKeyVersions = []*kmspb.CryptoKeyVersion{cryptoKeyVersionsElement} + var expectedResponse = &kmspb.ListCryptoKeyVersionsResponse{ + NextPageToken: nextPageToken, + TotalSize: totalSize, + CryptoKeyVersions: cryptoKeyVersions, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var request = &kmspb.ListCryptoKeyVersionsRequest{ + Parent: formattedParent, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListCryptoKeyVersions(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.CryptoKeyVersions[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 TestKeyManagementServiceListCryptoKeyVersionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var request = &kmspb.ListCryptoKeyVersionsRequest{ + Parent: formattedParent, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListCryptoKeyVersions(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 TestKeyManagementServiceGetKeyRing(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &kmspb.KeyRing{ + Name: name2, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]") + var request = &kmspb.GetKeyRingRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetKeyRing(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceGetKeyRingError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]") + var request = &kmspb.GetKeyRingRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetKeyRing(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 TestKeyManagementServiceGetCryptoKey(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &kmspb.CryptoKey{ + Name: name2, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var request = &kmspb.GetCryptoKeyRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCryptoKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceGetCryptoKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var request = &kmspb.GetCryptoKeyRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCryptoKey(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 TestKeyManagementServiceGetCryptoKeyVersion(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &kmspb.CryptoKeyVersion{ + Name: name2, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]", "[CRYPTO_KEY_VERSION]") + var request = &kmspb.GetCryptoKeyVersionRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCryptoKeyVersion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceGetCryptoKeyVersionError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]", "[CRYPTO_KEY_VERSION]") + var request = &kmspb.GetCryptoKeyVersionRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetCryptoKeyVersion(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 TestKeyManagementServiceCreateKeyRing(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &kmspb.KeyRing{ + Name: name, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]") + var keyRingId string = "keyRingId-2056646742" + var keyRing *kmspb.KeyRing = &kmspb.KeyRing{} + var request = &kmspb.CreateKeyRingRequest{ + Parent: formattedParent, + KeyRingId: keyRingId, + KeyRing: keyRing, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateKeyRing(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceCreateKeyRingError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]") + var keyRingId string = "keyRingId-2056646742" + var keyRing *kmspb.KeyRing = &kmspb.KeyRing{} + var request = &kmspb.CreateKeyRingRequest{ + Parent: formattedParent, + KeyRingId: keyRingId, + KeyRing: keyRing, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateKeyRing(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 TestKeyManagementServiceCreateCryptoKey(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &kmspb.CryptoKey{ + Name: name, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]") + var cryptoKeyId string = "my-app-key" + var purpose kmspb.CryptoKey_CryptoKeyPurpose = kmspb.CryptoKey_ENCRYPT_DECRYPT + var seconds int64 = 2147483647 + var nextRotationTime = ×tamppb.Timestamp{ + Seconds: seconds, + } + var seconds2 int64 = 604800 + var rotationPeriod = &durationpb.Duration{ + Seconds: seconds2, + } + var cryptoKey = &kmspb.CryptoKey{ + Purpose: purpose, + NextRotationTime: nextRotationTime, + RotationSchedule: &kmspb.CryptoKey_RotationPeriod{ + RotationPeriod: rotationPeriod, + }, + } + var request = &kmspb.CreateCryptoKeyRequest{ + Parent: formattedParent, + CryptoKeyId: cryptoKeyId, + CryptoKey: cryptoKey, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateCryptoKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceCreateCryptoKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]") + var cryptoKeyId string = "my-app-key" + var purpose kmspb.CryptoKey_CryptoKeyPurpose = kmspb.CryptoKey_ENCRYPT_DECRYPT + var seconds int64 = 2147483647 + var nextRotationTime = ×tamppb.Timestamp{ + Seconds: seconds, + } + var seconds2 int64 = 604800 + var rotationPeriod = &durationpb.Duration{ + Seconds: seconds2, + } + var cryptoKey = &kmspb.CryptoKey{ + Purpose: purpose, + NextRotationTime: nextRotationTime, + RotationSchedule: &kmspb.CryptoKey_RotationPeriod{ + RotationPeriod: rotationPeriod, + }, + } + var request = &kmspb.CreateCryptoKeyRequest{ + Parent: formattedParent, + CryptoKeyId: cryptoKeyId, + CryptoKey: cryptoKey, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateCryptoKey(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 TestKeyManagementServiceCreateCryptoKeyVersion(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &kmspb.CryptoKeyVersion{ + Name: name, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var cryptoKeyVersion *kmspb.CryptoKeyVersion = &kmspb.CryptoKeyVersion{} + var request = &kmspb.CreateCryptoKeyVersionRequest{ + Parent: formattedParent, + CryptoKeyVersion: cryptoKeyVersion, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateCryptoKeyVersion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceCreateCryptoKeyVersionError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var cryptoKeyVersion *kmspb.CryptoKeyVersion = &kmspb.CryptoKeyVersion{} + var request = &kmspb.CreateCryptoKeyVersionRequest{ + Parent: formattedParent, + CryptoKeyVersion: cryptoKeyVersion, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateCryptoKeyVersion(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 TestKeyManagementServiceUpdateCryptoKey(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &kmspb.CryptoKey{ + Name: name, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var cryptoKey *kmspb.CryptoKey = &kmspb.CryptoKey{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &kmspb.UpdateCryptoKeyRequest{ + CryptoKey: cryptoKey, + UpdateMask: updateMask, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCryptoKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceUpdateCryptoKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var cryptoKey *kmspb.CryptoKey = &kmspb.CryptoKey{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &kmspb.UpdateCryptoKeyRequest{ + CryptoKey: cryptoKey, + UpdateMask: updateMask, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCryptoKey(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 TestKeyManagementServiceUpdateCryptoKeyVersion(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &kmspb.CryptoKeyVersion{ + Name: name, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var cryptoKeyVersion *kmspb.CryptoKeyVersion = &kmspb.CryptoKeyVersion{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &kmspb.UpdateCryptoKeyVersionRequest{ + CryptoKeyVersion: cryptoKeyVersion, + UpdateMask: updateMask, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCryptoKeyVersion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceUpdateCryptoKeyVersionError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var cryptoKeyVersion *kmspb.CryptoKeyVersion = &kmspb.CryptoKeyVersion{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &kmspb.UpdateCryptoKeyVersionRequest{ + CryptoKeyVersion: cryptoKeyVersion, + UpdateMask: updateMask, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCryptoKeyVersion(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 TestKeyManagementServiceEncrypt(t *testing.T) { + var name2 string = "name2-1052831874" + var ciphertext []byte = []byte("-72") + var expectedResponse = &kmspb.EncryptResponse{ + Name: name2, + Ciphertext: ciphertext, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY_PATH]") + var plaintext []byte = []byte("-9") + var request = &kmspb.EncryptRequest{ + Name: formattedName, + Plaintext: plaintext, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Encrypt(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceEncryptError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY_PATH]") + var plaintext []byte = []byte("-9") + var request = &kmspb.EncryptRequest{ + Name: formattedName, + Plaintext: plaintext, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Encrypt(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 TestKeyManagementServiceDecrypt(t *testing.T) { + var plaintext []byte = []byte("-9") + var expectedResponse = &kmspb.DecryptResponse{ + Plaintext: plaintext, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var ciphertext []byte = []byte("-72") + var request = &kmspb.DecryptRequest{ + Name: formattedName, + Ciphertext: ciphertext, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Decrypt(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceDecryptError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var ciphertext []byte = []byte("-72") + var request = &kmspb.DecryptRequest{ + Name: formattedName, + Ciphertext: ciphertext, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Decrypt(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 TestKeyManagementServiceUpdateCryptoKeyPrimaryVersion(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &kmspb.CryptoKey{ + Name: name2, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var cryptoKeyVersionId string = "cryptoKeyVersionId729489152" + var request = &kmspb.UpdateCryptoKeyPrimaryVersionRequest{ + Name: formattedName, + CryptoKeyVersionId: cryptoKeyVersionId, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCryptoKeyPrimaryVersion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceUpdateCryptoKeyPrimaryVersionError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]") + var cryptoKeyVersionId string = "cryptoKeyVersionId729489152" + var request = &kmspb.UpdateCryptoKeyPrimaryVersionRequest{ + Name: formattedName, + CryptoKeyVersionId: cryptoKeyVersionId, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateCryptoKeyPrimaryVersion(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 TestKeyManagementServiceDestroyCryptoKeyVersion(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &kmspb.CryptoKeyVersion{ + Name: name2, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]", "[CRYPTO_KEY_VERSION]") + var request = &kmspb.DestroyCryptoKeyVersionRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DestroyCryptoKeyVersion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceDestroyCryptoKeyVersionError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]", "[CRYPTO_KEY_VERSION]") + var request = &kmspb.DestroyCryptoKeyVersionRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.DestroyCryptoKeyVersion(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 TestKeyManagementServiceRestoreCryptoKeyVersion(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &kmspb.CryptoKeyVersion{ + Name: name2, + } + + mockKeyManagement.err = nil + mockKeyManagement.reqs = nil + + mockKeyManagement.resps = append(mockKeyManagement.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]", "[CRYPTO_KEY_VERSION]") + var request = &kmspb.RestoreCryptoKeyVersionRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RestoreCryptoKeyVersion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockKeyManagement.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 TestKeyManagementServiceRestoreCryptoKeyVersionError(t *testing.T) { + errCode := codes.PermissionDenied + mockKeyManagement.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", "[PROJECT]", "[LOCATION]", "[KEY_RING]", "[CRYPTO_KEY]", "[CRYPTO_KEY_VERSION]") + var request = &kmspb.RestoreCryptoKeyVersionRequest{ + Name: formattedName, + } + + c, err := NewKeyManagementClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.RestoreCryptoKeyVersion(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/vendor/cloud.google.com/go/language/apiv1/AnalyzeSentiment_smoke_test.go b/vendor/cloud.google.com/go/language/apiv1/AnalyzeSentiment_smoke_test.go new file mode 100644 index 0000000000..1233913c38 --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1/AnalyzeSentiment_smoke_test.go @@ -0,0 +1,73 @@ +// 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 language + +import ( + languagepb "google.golang.org/genproto/googleapis/cloud/language/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestLanguageServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var content string = "Hello, world!" + var type_ languagepb.Document_Type = languagepb.Document_PLAIN_TEXT + var document = &languagepb.Document{ + Source: &languagepb.Document_Content{ + Content: content, + }, + Type: type_, + } + var request = &languagepb.AnalyzeSentimentRequest{ + Document: document, + } + + if _, err := c.AnalyzeSentiment(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/language/apiv1/doc.go b/vendor/cloud.google.com/go/language/apiv1/doc.go new file mode 100644 index 0000000000..7685ff3fb5 --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1/doc.go @@ -0,0 +1,47 @@ +// 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 language is an auto-generated package for the +// Google Cloud Natural Language API. + +// +// Google Cloud Natural Language API provides natural language understanding +// technologies to developers. Examples include sentiment analysis, entity +// recognition, and text annotations. +package language // import "cloud.google.com/go/language/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/language/apiv1/language_client.go b/vendor/cloud.google.com/go/language/apiv1/language_client.go new file mode 100644 index 0000000000..40869629bf --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1/language_client.go @@ -0,0 +1,231 @@ +// 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 language + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + languagepb "google.golang.org/genproto/googleapis/cloud/language/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 { + AnalyzeSentiment []gax.CallOption + AnalyzeEntities []gax.CallOption + AnalyzeEntitySentiment []gax.CallOption + AnalyzeSyntax []gax.CallOption + ClassifyText []gax.CallOption + AnnotateText []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("language.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{ + AnalyzeSentiment: retry[[2]string{"default", "idempotent"}], + AnalyzeEntities: retry[[2]string{"default", "idempotent"}], + AnalyzeEntitySentiment: retry[[2]string{"default", "idempotent"}], + AnalyzeSyntax: retry[[2]string{"default", "idempotent"}], + ClassifyText: retry[[2]string{"default", "idempotent"}], + AnnotateText: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud Natural Language 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 languagepb.LanguageServiceClient + + // 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 language service client. +// +// Provides text analysis operations such as sentiment analysis and entity +// recognition. +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: languagepb.NewLanguageServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// AnalyzeSentiment analyzes the sentiment of the provided text. +func (c *Client) AnalyzeSentiment(ctx context.Context, req *languagepb.AnalyzeSentimentRequest, opts ...gax.CallOption) (*languagepb.AnalyzeSentimentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeSentiment[0:len(c.CallOptions.AnalyzeSentiment):len(c.CallOptions.AnalyzeSentiment)], opts...) + var resp *languagepb.AnalyzeSentimentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeSentiment(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnalyzeEntities finds named entities (currently proper names and common nouns) in the text +// along with entity types, salience, mentions for each entity, and +// other properties. +func (c *Client) AnalyzeEntities(ctx context.Context, req *languagepb.AnalyzeEntitiesRequest, opts ...gax.CallOption) (*languagepb.AnalyzeEntitiesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeEntities[0:len(c.CallOptions.AnalyzeEntities):len(c.CallOptions.AnalyzeEntities)], opts...) + var resp *languagepb.AnalyzeEntitiesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeEntities(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnalyzeEntitySentiment finds entities, similar to [AnalyzeEntities][google.cloud.language.v1.LanguageService.AnalyzeEntities] in the text and analyzes +// sentiment associated with each entity and its mentions. +func (c *Client) AnalyzeEntitySentiment(ctx context.Context, req *languagepb.AnalyzeEntitySentimentRequest, opts ...gax.CallOption) (*languagepb.AnalyzeEntitySentimentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeEntitySentiment[0:len(c.CallOptions.AnalyzeEntitySentiment):len(c.CallOptions.AnalyzeEntitySentiment)], opts...) + var resp *languagepb.AnalyzeEntitySentimentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeEntitySentiment(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnalyzeSyntax analyzes the syntax of the text and provides sentence boundaries and +// tokenization along with part of speech tags, dependency trees, and other +// properties. +func (c *Client) AnalyzeSyntax(ctx context.Context, req *languagepb.AnalyzeSyntaxRequest, opts ...gax.CallOption) (*languagepb.AnalyzeSyntaxResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeSyntax[0:len(c.CallOptions.AnalyzeSyntax):len(c.CallOptions.AnalyzeSyntax)], opts...) + var resp *languagepb.AnalyzeSyntaxResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeSyntax(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ClassifyText classifies a document into categories. +func (c *Client) ClassifyText(ctx context.Context, req *languagepb.ClassifyTextRequest, opts ...gax.CallOption) (*languagepb.ClassifyTextResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ClassifyText[0:len(c.CallOptions.ClassifyText):len(c.CallOptions.ClassifyText)], opts...) + var resp *languagepb.ClassifyTextResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ClassifyText(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnnotateText a convenience method that provides all the features that analyzeSentiment, +// analyzeEntities, and analyzeSyntax provide in one call. +func (c *Client) AnnotateText(ctx context.Context, req *languagepb.AnnotateTextRequest, opts ...gax.CallOption) (*languagepb.AnnotateTextResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnnotateText[0:len(c.CallOptions.AnnotateText):len(c.CallOptions.AnnotateText)], opts...) + var resp *languagepb.AnnotateTextResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnnotateText(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/language/apiv1/language_client_example_test.go b/vendor/cloud.google.com/go/language/apiv1/language_client_example_test.go new file mode 100644 index 0000000000..893d2038fb --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1/language_client_example_test.go @@ -0,0 +1,141 @@ +// 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 language_test + +import ( + "cloud.google.com/go/language/apiv1" + "golang.org/x/net/context" + languagepb "google.golang.org/genproto/googleapis/cloud/language/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_AnalyzeSentiment() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeSentimentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeSentiment(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnalyzeEntities() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeEntitiesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeEntities(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnalyzeEntitySentiment() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeEntitySentimentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeEntitySentiment(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnalyzeSyntax() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeSyntaxRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeSyntax(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ClassifyText() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.ClassifyTextRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ClassifyText(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnnotateText() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnnotateTextRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnnotateText(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/language/apiv1/mock_test.go b/vendor/cloud.google.com/go/language/apiv1/mock_test.go new file mode 100644 index 0000000000..c04685825e --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1/mock_test.go @@ -0,0 +1,518 @@ +// 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 language + +import ( + languagepb "google.golang.org/genproto/googleapis/cloud/language/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockLanguageServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + languagepb.LanguageServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockLanguageServer) AnalyzeSentiment(ctx context.Context, req *languagepb.AnalyzeSentimentRequest) (*languagepb.AnalyzeSentimentResponse, 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].(*languagepb.AnalyzeSentimentResponse), nil +} + +func (s *mockLanguageServer) AnalyzeEntities(ctx context.Context, req *languagepb.AnalyzeEntitiesRequest) (*languagepb.AnalyzeEntitiesResponse, 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].(*languagepb.AnalyzeEntitiesResponse), nil +} + +func (s *mockLanguageServer) AnalyzeEntitySentiment(ctx context.Context, req *languagepb.AnalyzeEntitySentimentRequest) (*languagepb.AnalyzeEntitySentimentResponse, 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].(*languagepb.AnalyzeEntitySentimentResponse), nil +} + +func (s *mockLanguageServer) AnalyzeSyntax(ctx context.Context, req *languagepb.AnalyzeSyntaxRequest) (*languagepb.AnalyzeSyntaxResponse, 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].(*languagepb.AnalyzeSyntaxResponse), nil +} + +func (s *mockLanguageServer) ClassifyText(ctx context.Context, req *languagepb.ClassifyTextRequest) (*languagepb.ClassifyTextResponse, 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].(*languagepb.ClassifyTextResponse), nil +} + +func (s *mockLanguageServer) AnnotateText(ctx context.Context, req *languagepb.AnnotateTextRequest) (*languagepb.AnnotateTextResponse, 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].(*languagepb.AnnotateTextResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockLanguage mockLanguageServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + languagepb.RegisterLanguageServiceServer(serv, &mockLanguage) + + 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 TestLanguageServiceAnalyzeSentiment(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeSentimentResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSentiment(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeSentimentError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSentiment(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 TestLanguageServiceAnalyzeEntities(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeEntitiesResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitiesRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntities(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeEntitiesError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitiesRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntities(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 TestLanguageServiceAnalyzeEntitySentiment(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeEntitySentimentResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitySentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntitySentiment(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeEntitySentimentError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitySentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntitySentiment(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 TestLanguageServiceAnalyzeSyntax(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeSyntaxResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSyntaxRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSyntax(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeSyntaxError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSyntaxRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSyntax(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 TestLanguageServiceClassifyText(t *testing.T) { + var expectedResponse *languagepb.ClassifyTextResponse = &languagepb.ClassifyTextResponse{} + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.ClassifyTextRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ClassifyText(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceClassifyTextError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.ClassifyTextRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ClassifyText(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 TestLanguageServiceAnnotateText(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnnotateTextResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var features *languagepb.AnnotateTextRequest_Features = &languagepb.AnnotateTextRequest_Features{} + var request = &languagepb.AnnotateTextRequest{ + Document: document, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnnotateText(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnnotateTextError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var features *languagepb.AnnotateTextRequest_Features = &languagepb.AnnotateTextRequest_Features{} + var request = &languagepb.AnnotateTextRequest{ + Document: document, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnnotateText(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/vendor/cloud.google.com/go/language/apiv1beta2/AnalyzeSentiment_smoke_test.go b/vendor/cloud.google.com/go/language/apiv1beta2/AnalyzeSentiment_smoke_test.go new file mode 100644 index 0000000000..7c2b1bd803 --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1beta2/AnalyzeSentiment_smoke_test.go @@ -0,0 +1,73 @@ +// 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 language + +import ( + languagepb "google.golang.org/genproto/googleapis/cloud/language/v1beta2" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestLanguageServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var content string = "Hello, world!" + var type_ languagepb.Document_Type = languagepb.Document_PLAIN_TEXT + var document = &languagepb.Document{ + Source: &languagepb.Document_Content{ + Content: content, + }, + Type: type_, + } + var request = &languagepb.AnalyzeSentimentRequest{ + Document: document, + } + + if _, err := c.AnalyzeSentiment(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/language/apiv1beta2/doc.go b/vendor/cloud.google.com/go/language/apiv1beta2/doc.go new file mode 100644 index 0000000000..b6802e76e8 --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1beta2/doc.go @@ -0,0 +1,48 @@ +// 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 language is an auto-generated package for the +// Google Cloud Natural Language API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Google Cloud Natural Language API provides natural language understanding +// technologies to developers. Examples include sentiment analysis, entity +// recognition, and text annotations. +package language // import "cloud.google.com/go/language/apiv1beta2" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/language/apiv1beta2/language_client.go b/vendor/cloud.google.com/go/language/apiv1beta2/language_client.go new file mode 100644 index 0000000000..695d6cbc7b --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1beta2/language_client.go @@ -0,0 +1,231 @@ +// 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 language + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + languagepb "google.golang.org/genproto/googleapis/cloud/language/v1beta2" + "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 { + AnalyzeSentiment []gax.CallOption + AnalyzeEntities []gax.CallOption + AnalyzeEntitySentiment []gax.CallOption + AnalyzeSyntax []gax.CallOption + ClassifyText []gax.CallOption + AnnotateText []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("language.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{ + AnalyzeSentiment: retry[[2]string{"default", "idempotent"}], + AnalyzeEntities: retry[[2]string{"default", "idempotent"}], + AnalyzeEntitySentiment: retry[[2]string{"default", "idempotent"}], + AnalyzeSyntax: retry[[2]string{"default", "idempotent"}], + ClassifyText: retry[[2]string{"default", "idempotent"}], + AnnotateText: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud Natural Language 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 languagepb.LanguageServiceClient + + // 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 language service client. +// +// Provides text analysis operations such as sentiment analysis and entity +// recognition. +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: languagepb.NewLanguageServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// AnalyzeSentiment analyzes the sentiment of the provided text. +func (c *Client) AnalyzeSentiment(ctx context.Context, req *languagepb.AnalyzeSentimentRequest, opts ...gax.CallOption) (*languagepb.AnalyzeSentimentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeSentiment[0:len(c.CallOptions.AnalyzeSentiment):len(c.CallOptions.AnalyzeSentiment)], opts...) + var resp *languagepb.AnalyzeSentimentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeSentiment(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnalyzeEntities finds named entities (currently proper names and common nouns) in the text +// along with entity types, salience, mentions for each entity, and +// other properties. +func (c *Client) AnalyzeEntities(ctx context.Context, req *languagepb.AnalyzeEntitiesRequest, opts ...gax.CallOption) (*languagepb.AnalyzeEntitiesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeEntities[0:len(c.CallOptions.AnalyzeEntities):len(c.CallOptions.AnalyzeEntities)], opts...) + var resp *languagepb.AnalyzeEntitiesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeEntities(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnalyzeEntitySentiment finds entities, similar to [AnalyzeEntities][google.cloud.language.v1beta2.LanguageService.AnalyzeEntities] in the text and analyzes +// sentiment associated with each entity and its mentions. +func (c *Client) AnalyzeEntitySentiment(ctx context.Context, req *languagepb.AnalyzeEntitySentimentRequest, opts ...gax.CallOption) (*languagepb.AnalyzeEntitySentimentResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeEntitySentiment[0:len(c.CallOptions.AnalyzeEntitySentiment):len(c.CallOptions.AnalyzeEntitySentiment)], opts...) + var resp *languagepb.AnalyzeEntitySentimentResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeEntitySentiment(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnalyzeSyntax analyzes the syntax of the text and provides sentence boundaries and +// tokenization along with part of speech tags, dependency trees, and other +// properties. +func (c *Client) AnalyzeSyntax(ctx context.Context, req *languagepb.AnalyzeSyntaxRequest, opts ...gax.CallOption) (*languagepb.AnalyzeSyntaxResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnalyzeSyntax[0:len(c.CallOptions.AnalyzeSyntax):len(c.CallOptions.AnalyzeSyntax)], opts...) + var resp *languagepb.AnalyzeSyntaxResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnalyzeSyntax(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ClassifyText classifies a document into categories. +func (c *Client) ClassifyText(ctx context.Context, req *languagepb.ClassifyTextRequest, opts ...gax.CallOption) (*languagepb.ClassifyTextResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ClassifyText[0:len(c.CallOptions.ClassifyText):len(c.CallOptions.ClassifyText)], opts...) + var resp *languagepb.ClassifyTextResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ClassifyText(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AnnotateText a convenience method that provides all syntax, sentiment, entity, and +// classification features in one call. +func (c *Client) AnnotateText(ctx context.Context, req *languagepb.AnnotateTextRequest, opts ...gax.CallOption) (*languagepb.AnnotateTextResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnnotateText[0:len(c.CallOptions.AnnotateText):len(c.CallOptions.AnnotateText)], opts...) + var resp *languagepb.AnnotateTextResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnnotateText(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/language/apiv1beta2/language_client_example_test.go b/vendor/cloud.google.com/go/language/apiv1beta2/language_client_example_test.go new file mode 100644 index 0000000000..d427900f5f --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1beta2/language_client_example_test.go @@ -0,0 +1,141 @@ +// 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 language_test + +import ( + "cloud.google.com/go/language/apiv1beta2" + "golang.org/x/net/context" + languagepb "google.golang.org/genproto/googleapis/cloud/language/v1beta2" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_AnalyzeSentiment() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeSentimentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeSentiment(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnalyzeEntities() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeEntitiesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeEntities(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnalyzeEntitySentiment() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeEntitySentimentRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeEntitySentiment(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnalyzeSyntax() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnalyzeSyntaxRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnalyzeSyntax(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ClassifyText() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.ClassifyTextRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ClassifyText(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AnnotateText() { + ctx := context.Background() + c, err := language.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &languagepb.AnnotateTextRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.AnnotateText(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/language/apiv1beta2/mock_test.go b/vendor/cloud.google.com/go/language/apiv1beta2/mock_test.go new file mode 100644 index 0000000000..c6974ae2d0 --- /dev/null +++ b/vendor/cloud.google.com/go/language/apiv1beta2/mock_test.go @@ -0,0 +1,518 @@ +// 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 language + +import ( + languagepb "google.golang.org/genproto/googleapis/cloud/language/v1beta2" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockLanguageServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + languagepb.LanguageServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockLanguageServer) AnalyzeSentiment(ctx context.Context, req *languagepb.AnalyzeSentimentRequest) (*languagepb.AnalyzeSentimentResponse, 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].(*languagepb.AnalyzeSentimentResponse), nil +} + +func (s *mockLanguageServer) AnalyzeEntities(ctx context.Context, req *languagepb.AnalyzeEntitiesRequest) (*languagepb.AnalyzeEntitiesResponse, 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].(*languagepb.AnalyzeEntitiesResponse), nil +} + +func (s *mockLanguageServer) AnalyzeEntitySentiment(ctx context.Context, req *languagepb.AnalyzeEntitySentimentRequest) (*languagepb.AnalyzeEntitySentimentResponse, 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].(*languagepb.AnalyzeEntitySentimentResponse), nil +} + +func (s *mockLanguageServer) AnalyzeSyntax(ctx context.Context, req *languagepb.AnalyzeSyntaxRequest) (*languagepb.AnalyzeSyntaxResponse, 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].(*languagepb.AnalyzeSyntaxResponse), nil +} + +func (s *mockLanguageServer) ClassifyText(ctx context.Context, req *languagepb.ClassifyTextRequest) (*languagepb.ClassifyTextResponse, 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].(*languagepb.ClassifyTextResponse), nil +} + +func (s *mockLanguageServer) AnnotateText(ctx context.Context, req *languagepb.AnnotateTextRequest) (*languagepb.AnnotateTextResponse, 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].(*languagepb.AnnotateTextResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockLanguage mockLanguageServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + languagepb.RegisterLanguageServiceServer(serv, &mockLanguage) + + 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 TestLanguageServiceAnalyzeSentiment(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeSentimentResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSentiment(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeSentimentError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSentiment(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 TestLanguageServiceAnalyzeEntities(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeEntitiesResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitiesRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntities(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeEntitiesError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitiesRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntities(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 TestLanguageServiceAnalyzeEntitySentiment(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeEntitySentimentResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitySentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntitySentiment(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeEntitySentimentError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeEntitySentimentRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeEntitySentiment(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 TestLanguageServiceAnalyzeSyntax(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnalyzeSyntaxResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSyntaxRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSyntax(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnalyzeSyntaxError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.AnalyzeSyntaxRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnalyzeSyntax(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 TestLanguageServiceClassifyText(t *testing.T) { + var expectedResponse *languagepb.ClassifyTextResponse = &languagepb.ClassifyTextResponse{} + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.ClassifyTextRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ClassifyText(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceClassifyTextError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var request = &languagepb.ClassifyTextRequest{ + Document: document, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ClassifyText(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 TestLanguageServiceAnnotateText(t *testing.T) { + var language string = "language-1613589672" + var expectedResponse = &languagepb.AnnotateTextResponse{ + Language: language, + } + + mockLanguage.err = nil + mockLanguage.reqs = nil + + mockLanguage.resps = append(mockLanguage.resps[:0], expectedResponse) + + var document *languagepb.Document = &languagepb.Document{} + var features *languagepb.AnnotateTextRequest_Features = &languagepb.AnnotateTextRequest_Features{} + var request = &languagepb.AnnotateTextRequest{ + Document: document, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnnotateText(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLanguage.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 TestLanguageServiceAnnotateTextError(t *testing.T) { + errCode := codes.PermissionDenied + mockLanguage.err = gstatus.Error(errCode, "test error") + + var document *languagepb.Document = &languagepb.Document{} + var features *languagepb.AnnotateTextRequest_Features = &languagepb.AnnotateTextRequest_Features{} + var request = &languagepb.AnnotateTextRequest{ + Document: document, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.AnnotateText(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/vendor/cloud.google.com/go/license_test.go b/vendor/cloud.google.com/go/license_test.go new file mode 100644 index 0000000000..a30cad1993 --- /dev/null +++ b/vendor/cloud.google.com/go/license_test.go @@ -0,0 +1,74 @@ +// 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 cloud + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" +) + +var sentinels = []string{ + "Copyright", + "Google", + `Licensed under the Apache License, Version 2.0 (the "License");`, +} + +func TestLicense(t *testing.T) { + t.Parallel() + err := filepath.Walk(".", func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if ext := filepath.Ext(path); ext != ".go" && ext != ".proto" { + return nil + } + if strings.HasSuffix(path, ".pb.go") { + // .pb.go files are generated from the proto files. + // .proto files must have license headers. + return nil + } + if path == "bigtable/cmd/cbt/cbtdoc.go" { + // Automatically generated. + return nil + } + if path == "cmd/go-cloud-debug-agent/internal/debug/elf/elf.go" { + // BSD license, which is compatible, is embedded in the file. + return nil + } + src, err := ioutil.ReadFile(path) + if err != nil { + return nil + } + src = src[:300] // Ensure all of the sentinel values are at the top of the file. + + // Find license + for _, sentinel := range sentinels { + if !bytes.Contains(src, []byte(sentinel)) { + t.Errorf("%v: license header not present. want %q", path, sentinel) + return nil + } + } + + return nil + }) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/README.md b/vendor/cloud.google.com/go/logging/apiv2/README.md new file mode 100644 index 0000000000..d2d9a176e6 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/README.md @@ -0,0 +1,11 @@ +Auto-generated logging v2 clients +================================= + +This package includes auto-generated clients for the logging v2 API. + +Use the handwritten logging client (in the parent directory, +cloud.google.com/go/logging) in preference to this. + +This code is EXPERIMENTAL and subject to CHANGE AT ANY TIME. + + diff --git a/vendor/cloud.google.com/go/logging/apiv2/WriteLogEntries_smoke_test.go b/vendor/cloud.google.com/go/logging/apiv2/WriteLogEntries_smoke_test.go new file mode 100644 index 0000000000..08d675aee6 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/WriteLogEntries_smoke_test.go @@ -0,0 +1,68 @@ +// 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 logging + +import ( + loggingpb "google.golang.org/genproto/googleapis/logging/v2" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestLoggingServiceV2Smoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var entries []*loggingpb.LogEntry = nil + var formattedLogName string = fmt.Sprintf("projects/%s/logs/%s", projectId, "test-"+strconv.FormatInt(time.Now().UnixNano(), 10)+"") + var request = &loggingpb.WriteLogEntriesRequest{ + Entries: entries, + LogName: formattedLogName, + } + + if _, err := c.WriteLogEntries(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/config_client.go b/vendor/cloud.google.com/go/logging/apiv2/config_client.go new file mode 100644 index 0000000000..cc8099c676 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/config_client.go @@ -0,0 +1,428 @@ +// 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 logging + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + loggingpb "google.golang.org/genproto/googleapis/logging/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ConfigCallOptions contains the retry settings for each method of ConfigClient. +type ConfigCallOptions struct { + ListSinks []gax.CallOption + GetSink []gax.CallOption + CreateSink []gax.CallOption + UpdateSink []gax.CallOption + DeleteSink []gax.CallOption + ListExclusions []gax.CallOption + GetExclusion []gax.CallOption + CreateExclusion []gax.CallOption + UpdateExclusion []gax.CallOption + DeleteExclusion []gax.CallOption +} + +func defaultConfigClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("logging.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultConfigCallOptions() *ConfigCallOptions { + retry := map[[2]string][]gax.CallOption{ + {"default", "idempotent"}: { + gax.WithRetry(func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.DeadlineExceeded, + codes.Internal, + codes.Unavailable, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &ConfigCallOptions{ + ListSinks: retry[[2]string{"default", "idempotent"}], + GetSink: retry[[2]string{"default", "idempotent"}], + CreateSink: retry[[2]string{"default", "non_idempotent"}], + UpdateSink: retry[[2]string{"default", "idempotent"}], + DeleteSink: retry[[2]string{"default", "idempotent"}], + ListExclusions: retry[[2]string{"default", "idempotent"}], + GetExclusion: retry[[2]string{"default", "idempotent"}], + CreateExclusion: retry[[2]string{"default", "non_idempotent"}], + UpdateExclusion: retry[[2]string{"default", "non_idempotent"}], + DeleteExclusion: retry[[2]string{"default", "idempotent"}], + } +} + +// ConfigClient is a client for interacting with Stackdriver Logging API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ConfigClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + configClient loggingpb.ConfigServiceV2Client + + // The call options for this service. + CallOptions *ConfigCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewConfigClient creates a new config service v2 client. +// +// Service for configuring sinks used to export log entries outside of +// Stackdriver Logging. +func NewConfigClient(ctx context.Context, opts ...option.ClientOption) (*ConfigClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultConfigClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ConfigClient{ + conn: conn, + CallOptions: defaultConfigCallOptions(), + + configClient: loggingpb.NewConfigServiceV2Client(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ConfigClient) 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 *ConfigClient) 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 *ConfigClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListSinks lists sinks. +func (c *ConfigClient) ListSinks(ctx context.Context, req *loggingpb.ListSinksRequest, opts ...gax.CallOption) *LogSinkIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListSinks[0:len(c.CallOptions.ListSinks):len(c.CallOptions.ListSinks)], opts...) + it := &LogSinkIterator{} + req = proto.Clone(req).(*loggingpb.ListSinksRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogSink, string, error) { + var resp *loggingpb.ListSinksResponse + 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.configClient.ListSinks(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Sinks, 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 +} + +// GetSink gets a sink. +func (c *ConfigClient) GetSink(ctx context.Context, req *loggingpb.GetSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetSink[0:len(c.CallOptions.GetSink):len(c.CallOptions.GetSink)], opts...) + var resp *loggingpb.LogSink + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.configClient.GetSink(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateSink creates a sink that exports specified log entries to a destination. The +// export of newly-ingested log entries begins immediately, unless the sink's +// writer_identity is not permitted to write to the destination. A sink can +// export log entries only from the resource owning the sink. +func (c *ConfigClient) CreateSink(ctx context.Context, req *loggingpb.CreateSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateSink[0:len(c.CallOptions.CreateSink):len(c.CallOptions.CreateSink)], opts...) + var resp *loggingpb.LogSink + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.configClient.CreateSink(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateSink updates a sink. This method replaces the following fields in the existing +// sink with values from the new sink: destination, and filter. +// The updated sink might also have a new writer_identity; see the +// unique_writer_identity field. +func (c *ConfigClient) UpdateSink(ctx context.Context, req *loggingpb.UpdateSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateSink[0:len(c.CallOptions.UpdateSink):len(c.CallOptions.UpdateSink)], opts...) + var resp *loggingpb.LogSink + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.configClient.UpdateSink(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteSink deletes a sink. If the sink has a unique writer_identity, then that +// service account is also deleted. +func (c *ConfigClient) DeleteSink(ctx context.Context, req *loggingpb.DeleteSinkRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteSink[0:len(c.CallOptions.DeleteSink):len(c.CallOptions.DeleteSink)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.configClient.DeleteSink(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListExclusions lists all the exclusions in a parent resource. +func (c *ConfigClient) ListExclusions(ctx context.Context, req *loggingpb.ListExclusionsRequest, opts ...gax.CallOption) *LogExclusionIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListExclusions[0:len(c.CallOptions.ListExclusions):len(c.CallOptions.ListExclusions)], opts...) + it := &LogExclusionIterator{} + req = proto.Clone(req).(*loggingpb.ListExclusionsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogExclusion, string, error) { + var resp *loggingpb.ListExclusionsResponse + 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.configClient.ListExclusions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Exclusions, 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 +} + +// GetExclusion gets the description of an exclusion. +func (c *ConfigClient) GetExclusion(ctx context.Context, req *loggingpb.GetExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetExclusion[0:len(c.CallOptions.GetExclusion):len(c.CallOptions.GetExclusion)], opts...) + var resp *loggingpb.LogExclusion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.configClient.GetExclusion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateExclusion creates a new exclusion in a specified parent resource. +// Only log entries belonging to that resource can be excluded. +// You can have up to 10 exclusions in a resource. +func (c *ConfigClient) CreateExclusion(ctx context.Context, req *loggingpb.CreateExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateExclusion[0:len(c.CallOptions.CreateExclusion):len(c.CallOptions.CreateExclusion)], opts...) + var resp *loggingpb.LogExclusion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.configClient.CreateExclusion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateExclusion changes one or more properties of an existing exclusion. +func (c *ConfigClient) UpdateExclusion(ctx context.Context, req *loggingpb.UpdateExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateExclusion[0:len(c.CallOptions.UpdateExclusion):len(c.CallOptions.UpdateExclusion)], opts...) + var resp *loggingpb.LogExclusion + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.configClient.UpdateExclusion(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteExclusion deletes an exclusion. +func (c *ConfigClient) DeleteExclusion(ctx context.Context, req *loggingpb.DeleteExclusionRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteExclusion[0:len(c.CallOptions.DeleteExclusion):len(c.CallOptions.DeleteExclusion)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.configClient.DeleteExclusion(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// LogExclusionIterator manages a stream of *loggingpb.LogExclusion. +type LogExclusionIterator struct { + items []*loggingpb.LogExclusion + 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 []*loggingpb.LogExclusion, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *LogExclusionIterator) 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 *LogExclusionIterator) Next() (*loggingpb.LogExclusion, error) { + var item *loggingpb.LogExclusion + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *LogExclusionIterator) bufLen() int { + return len(it.items) +} + +func (it *LogExclusionIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// LogSinkIterator manages a stream of *loggingpb.LogSink. +type LogSinkIterator struct { + items []*loggingpb.LogSink + 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 []*loggingpb.LogSink, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *LogSinkIterator) 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 *LogSinkIterator) Next() (*loggingpb.LogSink, error) { + var item *loggingpb.LogSink + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *LogSinkIterator) bufLen() int { + return len(it.items) +} + +func (it *LogSinkIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/config_client_example_test.go b/vendor/cloud.google.com/go/logging/apiv2/config_client_example_test.go new file mode 100644 index 0000000000..a7a36dc49e --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/config_client_example_test.go @@ -0,0 +1,222 @@ +// 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 logging_test + +import ( + "cloud.google.com/go/logging/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + loggingpb "google.golang.org/genproto/googleapis/logging/v2" +) + +func ExampleNewConfigClient() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleConfigClient_ListSinks() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.ListSinksRequest{ + // TODO: Fill request struct fields. + } + it := c.ListSinks(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleConfigClient_GetSink() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.GetSinkRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetSink(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleConfigClient_CreateSink() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.CreateSinkRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateSink(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleConfigClient_UpdateSink() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.UpdateSinkRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateSink(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleConfigClient_DeleteSink() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.DeleteSinkRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteSink(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleConfigClient_ListExclusions() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.ListExclusionsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListExclusions(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleConfigClient_GetExclusion() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.GetExclusionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetExclusion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleConfigClient_CreateExclusion() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.CreateExclusionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateExclusion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleConfigClient_UpdateExclusion() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.UpdateExclusionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateExclusion(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleConfigClient_DeleteExclusion() { + ctx := context.Background() + c, err := logging.NewConfigClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.DeleteExclusionRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteExclusion(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/doc.go b/vendor/cloud.google.com/go/logging/apiv2/doc.go new file mode 100644 index 0000000000..b8087b020f --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/doc.go @@ -0,0 +1,52 @@ +// 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 logging is an auto-generated package for the +// Stackdriver Logging API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Writes log entries and manages your Stackdriver Logging configuration. +// +// Use the client at cloud.google.com/go/logging in preference to this. +package logging // import "cloud.google.com/go/logging/apiv2" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/logging.admin", + "https://www.googleapis.com/auth/logging.read", + "https://www.googleapis.com/auth/logging.write", + } +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/logging_client.go b/vendor/cloud.google.com/go/logging/apiv2/logging_client.go new file mode 100644 index 0000000000..b71268ed39 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/logging_client.go @@ -0,0 +1,410 @@ +// 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 logging + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" + loggingpb "google.golang.org/genproto/googleapis/logging/v2" + "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 { + DeleteLog []gax.CallOption + WriteLogEntries []gax.CallOption + ListLogEntries []gax.CallOption + ListMonitoredResourceDescriptors []gax.CallOption + ListLogs []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("logging.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.Internal, + codes.Unavailable, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &CallOptions{ + DeleteLog: retry[[2]string{"default", "idempotent"}], + WriteLogEntries: retry[[2]string{"default", "non_idempotent"}], + ListLogEntries: retry[[2]string{"default", "idempotent"}], + ListMonitoredResourceDescriptors: retry[[2]string{"default", "idempotent"}], + ListLogs: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Stackdriver Logging 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 loggingpb.LoggingServiceV2Client + + // 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 logging service v2 client. +// +// Service for ingesting and querying logs. +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: loggingpb.NewLoggingServiceV2Client(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// DeleteLog deletes all the log entries in a log. +// The log reappears if it receives new entries. +// Log entries written shortly before the delete operation might not be +// deleted. +func (c *Client) DeleteLog(ctx context.Context, req *loggingpb.DeleteLogRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteLog[0:len(c.CallOptions.DeleteLog):len(c.CallOptions.DeleteLog)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteLog(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// WriteLogEntries writes log entries to Stackdriver Logging. This API method is the +// only way to send log entries to Stackdriver Logging. This method +// is used, directly or indirectly, by the Stackdriver Logging agent +// (fluentd) and all logging libraries configured to use Stackdriver +// Logging. +// A single request may contain log entries for a maximum of 1000 +// different resources (projects, organizations, billing accounts or +// folders) +func (c *Client) WriteLogEntries(ctx context.Context, req *loggingpb.WriteLogEntriesRequest, opts ...gax.CallOption) (*loggingpb.WriteLogEntriesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.WriteLogEntries[0:len(c.CallOptions.WriteLogEntries):len(c.CallOptions.WriteLogEntries)], opts...) + var resp *loggingpb.WriteLogEntriesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.WriteLogEntries(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListLogEntries lists log entries. Use this method to retrieve log entries from +// Stackdriver Logging. For ways to export log entries, see +// Exporting Logs (at /logging/docs/export). +func (c *Client) ListLogEntries(ctx context.Context, req *loggingpb.ListLogEntriesRequest, opts ...gax.CallOption) *LogEntryIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListLogEntries[0:len(c.CallOptions.ListLogEntries):len(c.CallOptions.ListLogEntries)], opts...) + it := &LogEntryIterator{} + req = proto.Clone(req).(*loggingpb.ListLogEntriesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogEntry, string, error) { + var resp *loggingpb.ListLogEntriesResponse + 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.ListLogEntries(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Entries, 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 +} + +// ListMonitoredResourceDescriptors lists the descriptors for monitored resource types used by Stackdriver +// Logging. +func (c *Client) ListMonitoredResourceDescriptors(ctx context.Context, req *loggingpb.ListMonitoredResourceDescriptorsRequest, opts ...gax.CallOption) *MonitoredResourceDescriptorIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListMonitoredResourceDescriptors[0:len(c.CallOptions.ListMonitoredResourceDescriptors):len(c.CallOptions.ListMonitoredResourceDescriptors)], opts...) + it := &MonitoredResourceDescriptorIterator{} + req = proto.Clone(req).(*loggingpb.ListMonitoredResourceDescriptorsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoredrespb.MonitoredResourceDescriptor, string, error) { + var resp *loggingpb.ListMonitoredResourceDescriptorsResponse + 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.ListMonitoredResourceDescriptors(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.ResourceDescriptors, 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 +} + +// ListLogs lists the logs in projects, organizations, folders, or billing accounts. +// Only logs that have entries are listed. +func (c *Client) ListLogs(ctx context.Context, req *loggingpb.ListLogsRequest, opts ...gax.CallOption) *StringIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListLogs[0:len(c.CallOptions.ListLogs):len(c.CallOptions.ListLogs)], opts...) + it := &StringIterator{} + req = proto.Clone(req).(*loggingpb.ListLogsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]string, string, error) { + var resp *loggingpb.ListLogsResponse + 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.ListLogs(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.LogNames, 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 +} + +// LogEntryIterator manages a stream of *loggingpb.LogEntry. +type LogEntryIterator struct { + items []*loggingpb.LogEntry + 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 []*loggingpb.LogEntry, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *LogEntryIterator) 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 *LogEntryIterator) Next() (*loggingpb.LogEntry, error) { + var item *loggingpb.LogEntry + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *LogEntryIterator) bufLen() int { + return len(it.items) +} + +func (it *LogEntryIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// MonitoredResourceDescriptorIterator manages a stream of *monitoredrespb.MonitoredResourceDescriptor. +type MonitoredResourceDescriptorIterator struct { + items []*monitoredrespb.MonitoredResourceDescriptor + 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 []*monitoredrespb.MonitoredResourceDescriptor, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *MonitoredResourceDescriptorIterator) 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 *MonitoredResourceDescriptorIterator) Next() (*monitoredrespb.MonitoredResourceDescriptor, error) { + var item *monitoredrespb.MonitoredResourceDescriptor + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *MonitoredResourceDescriptorIterator) bufLen() int { + return len(it.items) +} + +func (it *MonitoredResourceDescriptorIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// StringIterator manages a stream of string. +type StringIterator struct { + items []string + 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 []string, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *StringIterator) 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 *StringIterator) Next() (string, error) { + var item string + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *StringIterator) bufLen() int { + return len(it.items) +} + +func (it *StringIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/logging_client_example_test.go b/vendor/cloud.google.com/go/logging/apiv2/logging_client_example_test.go new file mode 100644 index 0000000000..232736d25b --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/logging_client_example_test.go @@ -0,0 +1,140 @@ +// 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 logging_test + +import ( + "cloud.google.com/go/logging/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + loggingpb "google.golang.org/genproto/googleapis/logging/v2" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := logging.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_DeleteLog() { + ctx := context.Background() + c, err := logging.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.DeleteLogRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteLog(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_WriteLogEntries() { + ctx := context.Background() + c, err := logging.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.WriteLogEntriesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.WriteLogEntries(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListLogEntries() { + ctx := context.Background() + c, err := logging.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.ListLogEntriesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListLogEntries(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_ListMonitoredResourceDescriptors() { + ctx := context.Background() + c, err := logging.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.ListMonitoredResourceDescriptorsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListMonitoredResourceDescriptors(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_ListLogs() { + ctx := context.Background() + c, err := logging.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.ListLogsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListLogs(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/metrics_client.go b/vendor/cloud.google.com/go/logging/apiv2/metrics_client.go new file mode 100644 index 0000000000..a965dbe324 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/metrics_client.go @@ -0,0 +1,269 @@ +// 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 logging + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + loggingpb "google.golang.org/genproto/googleapis/logging/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// MetricsCallOptions contains the retry settings for each method of MetricsClient. +type MetricsCallOptions struct { + ListLogMetrics []gax.CallOption + GetLogMetric []gax.CallOption + CreateLogMetric []gax.CallOption + UpdateLogMetric []gax.CallOption + DeleteLogMetric []gax.CallOption +} + +func defaultMetricsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("logging.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultMetricsCallOptions() *MetricsCallOptions { + retry := map[[2]string][]gax.CallOption{ + {"default", "idempotent"}: { + gax.WithRetry(func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.DeadlineExceeded, + codes.Internal, + codes.Unavailable, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &MetricsCallOptions{ + ListLogMetrics: retry[[2]string{"default", "idempotent"}], + GetLogMetric: retry[[2]string{"default", "idempotent"}], + CreateLogMetric: retry[[2]string{"default", "non_idempotent"}], + UpdateLogMetric: retry[[2]string{"default", "idempotent"}], + DeleteLogMetric: retry[[2]string{"default", "idempotent"}], + } +} + +// MetricsClient is a client for interacting with Stackdriver Logging API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type MetricsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + metricsClient loggingpb.MetricsServiceV2Client + + // The call options for this service. + CallOptions *MetricsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewMetricsClient creates a new metrics service v2 client. +// +// Service for configuring logs-based metrics. +func NewMetricsClient(ctx context.Context, opts ...option.ClientOption) (*MetricsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultMetricsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &MetricsClient{ + conn: conn, + CallOptions: defaultMetricsCallOptions(), + + metricsClient: loggingpb.NewMetricsServiceV2Client(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *MetricsClient) 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 *MetricsClient) 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 *MetricsClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListLogMetrics lists logs-based metrics. +func (c *MetricsClient) ListLogMetrics(ctx context.Context, req *loggingpb.ListLogMetricsRequest, opts ...gax.CallOption) *LogMetricIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListLogMetrics[0:len(c.CallOptions.ListLogMetrics):len(c.CallOptions.ListLogMetrics)], opts...) + it := &LogMetricIterator{} + req = proto.Clone(req).(*loggingpb.ListLogMetricsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogMetric, string, error) { + var resp *loggingpb.ListLogMetricsResponse + 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.metricsClient.ListLogMetrics(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Metrics, 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 +} + +// GetLogMetric gets a logs-based metric. +func (c *MetricsClient) GetLogMetric(ctx context.Context, req *loggingpb.GetLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetLogMetric[0:len(c.CallOptions.GetLogMetric):len(c.CallOptions.GetLogMetric)], opts...) + var resp *loggingpb.LogMetric + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.metricsClient.GetLogMetric(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateLogMetric creates a logs-based metric. +func (c *MetricsClient) CreateLogMetric(ctx context.Context, req *loggingpb.CreateLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateLogMetric[0:len(c.CallOptions.CreateLogMetric):len(c.CallOptions.CreateLogMetric)], opts...) + var resp *loggingpb.LogMetric + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.metricsClient.CreateLogMetric(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateLogMetric creates or updates a logs-based metric. +func (c *MetricsClient) UpdateLogMetric(ctx context.Context, req *loggingpb.UpdateLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateLogMetric[0:len(c.CallOptions.UpdateLogMetric):len(c.CallOptions.UpdateLogMetric)], opts...) + var resp *loggingpb.LogMetric + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.metricsClient.UpdateLogMetric(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteLogMetric deletes a logs-based metric. +func (c *MetricsClient) DeleteLogMetric(ctx context.Context, req *loggingpb.DeleteLogMetricRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteLogMetric[0:len(c.CallOptions.DeleteLogMetric):len(c.CallOptions.DeleteLogMetric)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.metricsClient.DeleteLogMetric(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// LogMetricIterator manages a stream of *loggingpb.LogMetric. +type LogMetricIterator struct { + items []*loggingpb.LogMetric + 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 []*loggingpb.LogMetric, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *LogMetricIterator) 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 *LogMetricIterator) Next() (*loggingpb.LogMetric, error) { + var item *loggingpb.LogMetric + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *LogMetricIterator) bufLen() int { + return len(it.items) +} + +func (it *LogMetricIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/metrics_client_example_test.go b/vendor/cloud.google.com/go/logging/apiv2/metrics_client_example_test.go new file mode 100644 index 0000000000..d69e1e900a --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/metrics_client_example_test.go @@ -0,0 +1,128 @@ +// 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 logging_test + +import ( + "cloud.google.com/go/logging/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + loggingpb "google.golang.org/genproto/googleapis/logging/v2" +) + +func ExampleNewMetricsClient() { + ctx := context.Background() + c, err := logging.NewMetricsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleMetricsClient_ListLogMetrics() { + ctx := context.Background() + c, err := logging.NewMetricsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.ListLogMetricsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListLogMetrics(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleMetricsClient_GetLogMetric() { + ctx := context.Background() + c, err := logging.NewMetricsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.GetLogMetricRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetLogMetric(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleMetricsClient_CreateLogMetric() { + ctx := context.Background() + c, err := logging.NewMetricsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.CreateLogMetricRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateLogMetric(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleMetricsClient_UpdateLogMetric() { + ctx := context.Background() + c, err := logging.NewMetricsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.UpdateLogMetricRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateLogMetric(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleMetricsClient_DeleteLogMetric() { + ctx := context.Background() + c, err := logging.NewMetricsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &loggingpb.DeleteLogMetricRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteLogMetric(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/mock_test.go b/vendor/cloud.google.com/go/logging/apiv2/mock_test.go new file mode 100644 index 0000000000..4af49981a0 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/mock_test.go @@ -0,0 +1,1677 @@ +// 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 logging + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" + loggingpb "google.golang.org/genproto/googleapis/logging/v2" + field_maskpb "google.golang.org/genproto/protobuf/field_mask" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockLoggingServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + loggingpb.LoggingServiceV2Server + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockLoggingServer) DeleteLog(ctx context.Context, req *loggingpb.DeleteLogRequest) (*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 *mockLoggingServer) WriteLogEntries(ctx context.Context, req *loggingpb.WriteLogEntriesRequest) (*loggingpb.WriteLogEntriesResponse, 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].(*loggingpb.WriteLogEntriesResponse), nil +} + +func (s *mockLoggingServer) ListLogEntries(ctx context.Context, req *loggingpb.ListLogEntriesRequest) (*loggingpb.ListLogEntriesResponse, 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].(*loggingpb.ListLogEntriesResponse), nil +} + +func (s *mockLoggingServer) ListMonitoredResourceDescriptors(ctx context.Context, req *loggingpb.ListMonitoredResourceDescriptorsRequest) (*loggingpb.ListMonitoredResourceDescriptorsResponse, 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].(*loggingpb.ListMonitoredResourceDescriptorsResponse), nil +} + +func (s *mockLoggingServer) ListLogs(ctx context.Context, req *loggingpb.ListLogsRequest) (*loggingpb.ListLogsResponse, 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].(*loggingpb.ListLogsResponse), nil +} + +type mockConfigServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + loggingpb.ConfigServiceV2Server + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockConfigServer) ListSinks(ctx context.Context, req *loggingpb.ListSinksRequest) (*loggingpb.ListSinksResponse, 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].(*loggingpb.ListSinksResponse), nil +} + +func (s *mockConfigServer) GetSink(ctx context.Context, req *loggingpb.GetSinkRequest) (*loggingpb.LogSink, 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].(*loggingpb.LogSink), nil +} + +func (s *mockConfigServer) CreateSink(ctx context.Context, req *loggingpb.CreateSinkRequest) (*loggingpb.LogSink, 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].(*loggingpb.LogSink), nil +} + +func (s *mockConfigServer) UpdateSink(ctx context.Context, req *loggingpb.UpdateSinkRequest) (*loggingpb.LogSink, 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].(*loggingpb.LogSink), nil +} + +func (s *mockConfigServer) DeleteSink(ctx context.Context, req *loggingpb.DeleteSinkRequest) (*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 *mockConfigServer) ListExclusions(ctx context.Context, req *loggingpb.ListExclusionsRequest) (*loggingpb.ListExclusionsResponse, 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].(*loggingpb.ListExclusionsResponse), nil +} + +func (s *mockConfigServer) GetExclusion(ctx context.Context, req *loggingpb.GetExclusionRequest) (*loggingpb.LogExclusion, 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].(*loggingpb.LogExclusion), nil +} + +func (s *mockConfigServer) CreateExclusion(ctx context.Context, req *loggingpb.CreateExclusionRequest) (*loggingpb.LogExclusion, 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].(*loggingpb.LogExclusion), nil +} + +func (s *mockConfigServer) UpdateExclusion(ctx context.Context, req *loggingpb.UpdateExclusionRequest) (*loggingpb.LogExclusion, 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].(*loggingpb.LogExclusion), nil +} + +func (s *mockConfigServer) DeleteExclusion(ctx context.Context, req *loggingpb.DeleteExclusionRequest) (*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 +} + +type mockMetricsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + loggingpb.MetricsServiceV2Server + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockMetricsServer) ListLogMetrics(ctx context.Context, req *loggingpb.ListLogMetricsRequest) (*loggingpb.ListLogMetricsResponse, 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].(*loggingpb.ListLogMetricsResponse), nil +} + +func (s *mockMetricsServer) GetLogMetric(ctx context.Context, req *loggingpb.GetLogMetricRequest) (*loggingpb.LogMetric, 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].(*loggingpb.LogMetric), nil +} + +func (s *mockMetricsServer) CreateLogMetric(ctx context.Context, req *loggingpb.CreateLogMetricRequest) (*loggingpb.LogMetric, 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].(*loggingpb.LogMetric), nil +} + +func (s *mockMetricsServer) UpdateLogMetric(ctx context.Context, req *loggingpb.UpdateLogMetricRequest) (*loggingpb.LogMetric, 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].(*loggingpb.LogMetric), nil +} + +func (s *mockMetricsServer) DeleteLogMetric(ctx context.Context, req *loggingpb.DeleteLogMetricRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockLogging mockLoggingServer + mockConfig mockConfigServer + mockMetrics mockMetricsServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + loggingpb.RegisterLoggingServiceV2Server(serv, &mockLogging) + loggingpb.RegisterConfigServiceV2Server(serv, &mockConfig) + loggingpb.RegisterMetricsServiceV2Server(serv, &mockMetrics) + + 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 TestLoggingServiceV2DeleteLog(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockLogging.err = nil + mockLogging.reqs = nil + + mockLogging.resps = append(mockLogging.resps[:0], expectedResponse) + + var formattedLogName string = fmt.Sprintf("projects/%s/logs/%s", "[PROJECT]", "[LOG]") + var request = &loggingpb.DeleteLogRequest{ + LogName: formattedLogName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteLog(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLogging.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestLoggingServiceV2DeleteLogError(t *testing.T) { + errCode := codes.PermissionDenied + mockLogging.err = gstatus.Error(errCode, "test error") + + var formattedLogName string = fmt.Sprintf("projects/%s/logs/%s", "[PROJECT]", "[LOG]") + var request = &loggingpb.DeleteLogRequest{ + LogName: formattedLogName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteLog(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 TestLoggingServiceV2WriteLogEntries(t *testing.T) { + var expectedResponse *loggingpb.WriteLogEntriesResponse = &loggingpb.WriteLogEntriesResponse{} + + mockLogging.err = nil + mockLogging.reqs = nil + + mockLogging.resps = append(mockLogging.resps[:0], expectedResponse) + + var entries []*loggingpb.LogEntry = nil + var request = &loggingpb.WriteLogEntriesRequest{ + Entries: entries, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.WriteLogEntries(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLogging.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 TestLoggingServiceV2WriteLogEntriesError(t *testing.T) { + errCode := codes.PermissionDenied + mockLogging.err = gstatus.Error(errCode, "test error") + + var entries []*loggingpb.LogEntry = nil + var request = &loggingpb.WriteLogEntriesRequest{ + Entries: entries, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.WriteLogEntries(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 TestLoggingServiceV2ListLogEntries(t *testing.T) { + var nextPageToken string = "" + var entriesElement *loggingpb.LogEntry = &loggingpb.LogEntry{} + var entries = []*loggingpb.LogEntry{entriesElement} + var expectedResponse = &loggingpb.ListLogEntriesResponse{ + NextPageToken: nextPageToken, + Entries: entries, + } + + mockLogging.err = nil + mockLogging.reqs = nil + + mockLogging.resps = append(mockLogging.resps[:0], expectedResponse) + + var formattedResourceNames []string = nil + var request = &loggingpb.ListLogEntriesRequest{ + ResourceNames: formattedResourceNames, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListLogEntries(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLogging.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Entries[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 TestLoggingServiceV2ListLogEntriesError(t *testing.T) { + errCode := codes.PermissionDenied + mockLogging.err = gstatus.Error(errCode, "test error") + + var formattedResourceNames []string = nil + var request = &loggingpb.ListLogEntriesRequest{ + ResourceNames: formattedResourceNames, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListLogEntries(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 TestLoggingServiceV2ListMonitoredResourceDescriptors(t *testing.T) { + var nextPageToken string = "" + var resourceDescriptorsElement *monitoredrespb.MonitoredResourceDescriptor = &monitoredrespb.MonitoredResourceDescriptor{} + var resourceDescriptors = []*monitoredrespb.MonitoredResourceDescriptor{resourceDescriptorsElement} + var expectedResponse = &loggingpb.ListMonitoredResourceDescriptorsResponse{ + NextPageToken: nextPageToken, + ResourceDescriptors: resourceDescriptors, + } + + mockLogging.err = nil + mockLogging.reqs = nil + + mockLogging.resps = append(mockLogging.resps[:0], expectedResponse) + + var request *loggingpb.ListMonitoredResourceDescriptorsRequest = &loggingpb.ListMonitoredResourceDescriptorsRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListMonitoredResourceDescriptors(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLogging.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.ResourceDescriptors[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 TestLoggingServiceV2ListMonitoredResourceDescriptorsError(t *testing.T) { + errCode := codes.PermissionDenied + mockLogging.err = gstatus.Error(errCode, "test error") + + var request *loggingpb.ListMonitoredResourceDescriptorsRequest = &loggingpb.ListMonitoredResourceDescriptorsRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListMonitoredResourceDescriptors(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 TestLoggingServiceV2ListLogs(t *testing.T) { + var nextPageToken string = "" + var logNamesElement string = "logNamesElement-1079688374" + var logNames = []string{logNamesElement} + var expectedResponse = &loggingpb.ListLogsResponse{ + NextPageToken: nextPageToken, + LogNames: logNames, + } + + mockLogging.err = nil + mockLogging.reqs = nil + + mockLogging.resps = append(mockLogging.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListLogsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListLogs(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockLogging.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.LogNames[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 TestLoggingServiceV2ListLogsError(t *testing.T) { + errCode := codes.PermissionDenied + mockLogging.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListLogsRequest{ + Parent: formattedParent, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListLogs(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 TestConfigServiceV2ListSinks(t *testing.T) { + var nextPageToken string = "" + var sinksElement *loggingpb.LogSink = &loggingpb.LogSink{} + var sinks = []*loggingpb.LogSink{sinksElement} + var expectedResponse = &loggingpb.ListSinksResponse{ + NextPageToken: nextPageToken, + Sinks: sinks, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListSinksRequest{ + Parent: formattedParent, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSinks(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Sinks[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 TestConfigServiceV2ListSinksError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListSinksRequest{ + Parent: formattedParent, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSinks(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 TestConfigServiceV2GetSink(t *testing.T) { + var name string = "name3373707" + var destination string = "destination-1429847026" + var filter string = "filter-1274492040" + var writerIdentity string = "writerIdentity775638794" + var includeChildren bool = true + var expectedResponse = &loggingpb.LogSink{ + Name: name, + Destination: destination, + Filter: filter, + WriterIdentity: writerIdentity, + IncludeChildren: includeChildren, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedSinkName string = fmt.Sprintf("projects/%s/sinks/%s", "[PROJECT]", "[SINK]") + var request = &loggingpb.GetSinkRequest{ + SinkName: formattedSinkName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSink(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.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 TestConfigServiceV2GetSinkError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedSinkName string = fmt.Sprintf("projects/%s/sinks/%s", "[PROJECT]", "[SINK]") + var request = &loggingpb.GetSinkRequest{ + SinkName: formattedSinkName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSink(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 TestConfigServiceV2CreateSink(t *testing.T) { + var name string = "name3373707" + var destination string = "destination-1429847026" + var filter string = "filter-1274492040" + var writerIdentity string = "writerIdentity775638794" + var includeChildren bool = true + var expectedResponse = &loggingpb.LogSink{ + Name: name, + Destination: destination, + Filter: filter, + WriterIdentity: writerIdentity, + IncludeChildren: includeChildren, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var sink *loggingpb.LogSink = &loggingpb.LogSink{} + var request = &loggingpb.CreateSinkRequest{ + Parent: formattedParent, + Sink: sink, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSink(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.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 TestConfigServiceV2CreateSinkError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var sink *loggingpb.LogSink = &loggingpb.LogSink{} + var request = &loggingpb.CreateSinkRequest{ + Parent: formattedParent, + Sink: sink, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSink(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 TestConfigServiceV2UpdateSink(t *testing.T) { + var name string = "name3373707" + var destination string = "destination-1429847026" + var filter string = "filter-1274492040" + var writerIdentity string = "writerIdentity775638794" + var includeChildren bool = true + var expectedResponse = &loggingpb.LogSink{ + Name: name, + Destination: destination, + Filter: filter, + WriterIdentity: writerIdentity, + IncludeChildren: includeChildren, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedSinkName string = fmt.Sprintf("projects/%s/sinks/%s", "[PROJECT]", "[SINK]") + var sink *loggingpb.LogSink = &loggingpb.LogSink{} + var request = &loggingpb.UpdateSinkRequest{ + SinkName: formattedSinkName, + Sink: sink, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSink(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.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 TestConfigServiceV2UpdateSinkError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedSinkName string = fmt.Sprintf("projects/%s/sinks/%s", "[PROJECT]", "[SINK]") + var sink *loggingpb.LogSink = &loggingpb.LogSink{} + var request = &loggingpb.UpdateSinkRequest{ + SinkName: formattedSinkName, + Sink: sink, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSink(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 TestConfigServiceV2DeleteSink(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedSinkName string = fmt.Sprintf("projects/%s/sinks/%s", "[PROJECT]", "[SINK]") + var request = &loggingpb.DeleteSinkRequest{ + SinkName: formattedSinkName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSink(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestConfigServiceV2DeleteSinkError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedSinkName string = fmt.Sprintf("projects/%s/sinks/%s", "[PROJECT]", "[SINK]") + var request = &loggingpb.DeleteSinkRequest{ + SinkName: formattedSinkName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSink(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 TestConfigServiceV2ListExclusions(t *testing.T) { + var nextPageToken string = "" + var exclusionsElement *loggingpb.LogExclusion = &loggingpb.LogExclusion{} + var exclusions = []*loggingpb.LogExclusion{exclusionsElement} + var expectedResponse = &loggingpb.ListExclusionsResponse{ + NextPageToken: nextPageToken, + Exclusions: exclusions, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListExclusionsRequest{ + Parent: formattedParent, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListExclusions(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Exclusions[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 TestConfigServiceV2ListExclusionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListExclusionsRequest{ + Parent: formattedParent, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListExclusions(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 TestConfigServiceV2GetExclusion(t *testing.T) { + var name2 string = "name2-1052831874" + var description string = "description-1724546052" + var filter string = "filter-1274492040" + var disabled bool = true + var expectedResponse = &loggingpb.LogExclusion{ + Name: name2, + Description: description, + Filter: filter, + Disabled: disabled, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/exclusions/%s", "[PROJECT]", "[EXCLUSION]") + var request = &loggingpb.GetExclusionRequest{ + Name: formattedName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetExclusion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.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 TestConfigServiceV2GetExclusionError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/exclusions/%s", "[PROJECT]", "[EXCLUSION]") + var request = &loggingpb.GetExclusionRequest{ + Name: formattedName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetExclusion(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 TestConfigServiceV2CreateExclusion(t *testing.T) { + var name string = "name3373707" + var description string = "description-1724546052" + var filter string = "filter-1274492040" + var disabled bool = true + var expectedResponse = &loggingpb.LogExclusion{ + Name: name, + Description: description, + Filter: filter, + Disabled: disabled, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var exclusion *loggingpb.LogExclusion = &loggingpb.LogExclusion{} + var request = &loggingpb.CreateExclusionRequest{ + Parent: formattedParent, + Exclusion: exclusion, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateExclusion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.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 TestConfigServiceV2CreateExclusionError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var exclusion *loggingpb.LogExclusion = &loggingpb.LogExclusion{} + var request = &loggingpb.CreateExclusionRequest{ + Parent: formattedParent, + Exclusion: exclusion, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateExclusion(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 TestConfigServiceV2UpdateExclusion(t *testing.T) { + var name2 string = "name2-1052831874" + var description string = "description-1724546052" + var filter string = "filter-1274492040" + var disabled bool = true + var expectedResponse = &loggingpb.LogExclusion{ + Name: name2, + Description: description, + Filter: filter, + Disabled: disabled, + } + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/exclusions/%s", "[PROJECT]", "[EXCLUSION]") + var exclusion *loggingpb.LogExclusion = &loggingpb.LogExclusion{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &loggingpb.UpdateExclusionRequest{ + Name: formattedName, + Exclusion: exclusion, + UpdateMask: updateMask, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateExclusion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.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 TestConfigServiceV2UpdateExclusionError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/exclusions/%s", "[PROJECT]", "[EXCLUSION]") + var exclusion *loggingpb.LogExclusion = &loggingpb.LogExclusion{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &loggingpb.UpdateExclusionRequest{ + Name: formattedName, + Exclusion: exclusion, + UpdateMask: updateMask, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateExclusion(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 TestConfigServiceV2DeleteExclusion(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockConfig.err = nil + mockConfig.reqs = nil + + mockConfig.resps = append(mockConfig.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/exclusions/%s", "[PROJECT]", "[EXCLUSION]") + var request = &loggingpb.DeleteExclusionRequest{ + Name: formattedName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteExclusion(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockConfig.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestConfigServiceV2DeleteExclusionError(t *testing.T) { + errCode := codes.PermissionDenied + mockConfig.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/exclusions/%s", "[PROJECT]", "[EXCLUSION]") + var request = &loggingpb.DeleteExclusionRequest{ + Name: formattedName, + } + + c, err := NewConfigClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteExclusion(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 TestMetricsServiceV2ListLogMetrics(t *testing.T) { + var nextPageToken string = "" + var metricsElement *loggingpb.LogMetric = &loggingpb.LogMetric{} + var metrics = []*loggingpb.LogMetric{metricsElement} + var expectedResponse = &loggingpb.ListLogMetricsResponse{ + NextPageToken: nextPageToken, + Metrics: metrics, + } + + mockMetrics.err = nil + mockMetrics.reqs = nil + + mockMetrics.resps = append(mockMetrics.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListLogMetricsRequest{ + Parent: formattedParent, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListLogMetrics(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetrics.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Metrics[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 TestMetricsServiceV2ListLogMetricsError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetrics.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &loggingpb.ListLogMetricsRequest{ + Parent: formattedParent, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListLogMetrics(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 TestMetricsServiceV2GetLogMetric(t *testing.T) { + var name string = "name3373707" + var description string = "description-1724546052" + var filter string = "filter-1274492040" + var valueExtractor string = "valueExtractor2047672534" + var expectedResponse = &loggingpb.LogMetric{ + Name: name, + Description: description, + Filter: filter, + ValueExtractor: valueExtractor, + } + + mockMetrics.err = nil + mockMetrics.reqs = nil + + mockMetrics.resps = append(mockMetrics.resps[:0], expectedResponse) + + var formattedMetricName string = fmt.Sprintf("projects/%s/metrics/%s", "[PROJECT]", "[METRIC]") + var request = &loggingpb.GetLogMetricRequest{ + MetricName: formattedMetricName, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetLogMetric(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetrics.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 TestMetricsServiceV2GetLogMetricError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetrics.err = gstatus.Error(errCode, "test error") + + var formattedMetricName string = fmt.Sprintf("projects/%s/metrics/%s", "[PROJECT]", "[METRIC]") + var request = &loggingpb.GetLogMetricRequest{ + MetricName: formattedMetricName, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetLogMetric(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 TestMetricsServiceV2CreateLogMetric(t *testing.T) { + var name string = "name3373707" + var description string = "description-1724546052" + var filter string = "filter-1274492040" + var valueExtractor string = "valueExtractor2047672534" + var expectedResponse = &loggingpb.LogMetric{ + Name: name, + Description: description, + Filter: filter, + ValueExtractor: valueExtractor, + } + + mockMetrics.err = nil + mockMetrics.reqs = nil + + mockMetrics.resps = append(mockMetrics.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var metric *loggingpb.LogMetric = &loggingpb.LogMetric{} + var request = &loggingpb.CreateLogMetricRequest{ + Parent: formattedParent, + Metric: metric, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateLogMetric(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetrics.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 TestMetricsServiceV2CreateLogMetricError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetrics.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var metric *loggingpb.LogMetric = &loggingpb.LogMetric{} + var request = &loggingpb.CreateLogMetricRequest{ + Parent: formattedParent, + Metric: metric, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateLogMetric(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 TestMetricsServiceV2UpdateLogMetric(t *testing.T) { + var name string = "name3373707" + var description string = "description-1724546052" + var filter string = "filter-1274492040" + var valueExtractor string = "valueExtractor2047672534" + var expectedResponse = &loggingpb.LogMetric{ + Name: name, + Description: description, + Filter: filter, + ValueExtractor: valueExtractor, + } + + mockMetrics.err = nil + mockMetrics.reqs = nil + + mockMetrics.resps = append(mockMetrics.resps[:0], expectedResponse) + + var formattedMetricName string = fmt.Sprintf("projects/%s/metrics/%s", "[PROJECT]", "[METRIC]") + var metric *loggingpb.LogMetric = &loggingpb.LogMetric{} + var request = &loggingpb.UpdateLogMetricRequest{ + MetricName: formattedMetricName, + Metric: metric, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateLogMetric(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetrics.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 TestMetricsServiceV2UpdateLogMetricError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetrics.err = gstatus.Error(errCode, "test error") + + var formattedMetricName string = fmt.Sprintf("projects/%s/metrics/%s", "[PROJECT]", "[METRIC]") + var metric *loggingpb.LogMetric = &loggingpb.LogMetric{} + var request = &loggingpb.UpdateLogMetricRequest{ + MetricName: formattedMetricName, + Metric: metric, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateLogMetric(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 TestMetricsServiceV2DeleteLogMetric(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockMetrics.err = nil + mockMetrics.reqs = nil + + mockMetrics.resps = append(mockMetrics.resps[:0], expectedResponse) + + var formattedMetricName string = fmt.Sprintf("projects/%s/metrics/%s", "[PROJECT]", "[METRIC]") + var request = &loggingpb.DeleteLogMetricRequest{ + MetricName: formattedMetricName, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteLogMetric(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetrics.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestMetricsServiceV2DeleteLogMetricError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetrics.err = gstatus.Error(errCode, "test error") + + var formattedMetricName string = fmt.Sprintf("projects/%s/metrics/%s", "[PROJECT]", "[METRIC]") + var request = &loggingpb.DeleteLogMetricRequest{ + MetricName: formattedMetricName, + } + + c, err := NewMetricsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteLogMetric(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) + } +} diff --git a/vendor/cloud.google.com/go/logging/apiv2/path_funcs.go b/vendor/cloud.google.com/go/logging/apiv2/path_funcs.go new file mode 100644 index 0000000000..37bbe9d4f4 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/apiv2/path_funcs.go @@ -0,0 +1,107 @@ +// 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. + +package logging + +// ConfigProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func ConfigProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// ConfigSinkPath returns the path for the sink resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/sinks/%s", project, sink) +// instead. +func ConfigSinkPath(project, sink string) string { + return "" + + "projects/" + + project + + "/sinks/" + + sink + + "" +} + +// ConfigExclusionPath returns the path for the exclusion resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/exclusions/%s", project, exclusion) +// instead. +func ConfigExclusionPath(project, exclusion string) string { + return "" + + "projects/" + + project + + "/exclusions/" + + exclusion + + "" +} + +// ProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func ProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// LogPath returns the path for the log resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/logs/%s", project, log) +// instead. +func LogPath(project, log string) string { + return "" + + "projects/" + + project + + "/logs/" + + log + + "" +} + +// MetricsProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func MetricsProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// MetricsMetricPath returns the path for the metric resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/metrics/%s", project, metric) +// instead. +func MetricsMetricPath(project, metric string) string { + return "" + + "projects/" + + project + + "/metrics/" + + metric + + "" +} diff --git a/vendor/cloud.google.com/go/logging/doc.go b/vendor/cloud.google.com/go/logging/doc.go new file mode 100644 index 0000000000..3326ae78ed --- /dev/null +++ b/vendor/cloud.google.com/go/logging/doc.go @@ -0,0 +1,117 @@ +// 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 logging contains a Stackdriver Logging client suitable for writing logs. +For reading logs, and working with sinks, metrics and monitored resources, +see package cloud.google.com/go/logging/logadmin. + +This client uses Logging API v2. +See https://cloud.google.com/logging/docs/api/v2/ for an introduction to the API. + + +Note: This package is in beta. Some backwards-incompatible changes may occur. + + +Creating a Client + +Use a Client to interact with the Stackdriver Logging API. + + // Create a Client + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + + +Basic Usage + +For most use cases, you'll want to add log entries to a buffer to be periodically +flushed (automatically and asynchronously) to the Stackdriver Logging service. + + // Initialize a logger + lg := client.Logger("my-log") + + // Add entry to log buffer + lg.Log(logging.Entry{Payload: "something happened!"}) + + +Closing your Client + +You should call Client.Close before your program exits to flush any buffered log entries to the Stackdriver Logging service. + + // Close the client when finished. + err = client.Close() + if err != nil { + // TODO: Handle error. + } + + +Synchronous Logging + +For critical errors, you may want to send your log entries immediately. +LogSync is slow and will block until the log entry has been sent, so it is +not recommended for normal use. + + lg.LogSync(ctx, logging.Entry{Payload: "ALERT! Something critical happened!"}) + + +Payloads + +An entry payload can be a string, as in the examples above. It can also be any value +that can be marshaled to a JSON object, like a map[string]interface{} or a struct: + + type MyEntry struct { + Name string + Count int + } + lg.Log(logging.Entry{Payload: MyEntry{Name: "Bob", Count: 3}}) + +If you have a []byte of JSON, wrap it in json.RawMessage: + + j := []byte(`{"Name": "Bob", "Count": 3}`) + lg.Log(logging.Entry{Payload: json.RawMessage(j)}) + + +The Standard Logger Interface + +You may want use a standard log.Logger in your program. + + // stdlg implements log.Logger + stdlg := lg.StandardLogger(logging.Info) + stdlg.Println("some info") + + +Log Levels + +An Entry may have one of a number of severity levels associated with it. + + logging.Entry{ + Payload: "something terrible happened!", + Severity: logging.Critical, + } + + +Viewing Logs + +You can view Stackdriver logs for projects at +https://console.cloud.google.com/logs/viewer. Use the dropdown at the top left. When +running from a Google Cloud Platform VM, select "GCE VM Instance". Otherwise, select +"Google Project" and then the project ID. Logs for organizations, folders and billing +accounts can be viewed on the command line with the "gcloud logging read" command. + + +*/ +package logging // import "cloud.google.com/go/logging" diff --git a/vendor/cloud.google.com/go/logging/examples_test.go b/vendor/cloud.google.com/go/logging/examples_test.go new file mode 100644 index 0000000000..b4bb4522b9 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/examples_test.go @@ -0,0 +1,166 @@ +// 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 logging_test + +import ( + "encoding/json" + "fmt" + "os" + + "cloud.google.com/go/logging" + "golang.org/x/net/context" +) + +func ExampleNewClient() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + // Use client to manage logs, metrics and sinks. + // Close the client when finished. + if err := client.Close(); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_Ping() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + if err := client.Ping(ctx); err != nil { + // TODO: Handle error. + } +} + +// Although Logger.Flush and Client.Close both return errors, they don't tell you +// whether the errors were frequent or significant. For most programs, it doesn't +// matter if there were a few errors while writing logs, although if those few errors +// indicated a bug in your program, you might want to know about them. The best way +// to handle errors is by setting the OnError function. If it runs quickly, it will +// see every error generated during logging. +func ExampleNewClient_errorFunc() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + // Print all errors to stdout, and count them. Multiple calls to the OnError + // function never happen concurrently, so there is no need for locking nErrs, + // provided you don't read it until after the logging client is closed. + var nErrs int + client.OnError = func(e error) { + fmt.Fprintf(os.Stdout, "logging: %v", e) + nErrs++ + } + // Use client to manage logs, metrics and sinks. + // Close the client when finished. + if err := client.Close(); err != nil { + // TODO: Handle error. + } + fmt.Printf("saw %d errors\n", nErrs) +} + +func ExampleClient_Logger() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + lg := client.Logger("my-log") + _ = lg // TODO: use the Logger. +} + +func ExampleLogger_LogSync() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + lg := client.Logger("my-log") + err = lg.LogSync(ctx, logging.Entry{Payload: "red alert"}) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleLogger_Log() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + lg := client.Logger("my-log") + lg.Log(logging.Entry{Payload: "something happened"}) +} + +// An Entry payload can be anything that marshals to a +// JSON object, like a struct. +func ExampleLogger_Log_struct() { + type MyEntry struct { + Name string + Count int + } + + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + lg := client.Logger("my-log") + lg.Log(logging.Entry{Payload: MyEntry{Name: "Bob", Count: 3}}) +} + +// To log a JSON value, wrap it in json.RawMessage. +func ExampleLogger_Log_json() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + lg := client.Logger("my-log") + j := []byte(`{"Name": "Bob", "Count": 3}`) + lg.Log(logging.Entry{Payload: json.RawMessage(j)}) +} + +func ExampleLogger_Flush() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + lg := client.Logger("my-log") + lg.Log(logging.Entry{Payload: "something happened"}) + lg.Flush() +} + +func ExampleLogger_StandardLogger() { + ctx := context.Background() + client, err := logging.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + lg := client.Logger("my-log") + slg := lg.StandardLogger(logging.Info) + slg.Println("an informative message") +} + +func ExampleParseSeverity() { + sev := logging.ParseSeverity("ALERT") + fmt.Println(sev) + // Output: Alert +} diff --git a/vendor/cloud.google.com/go/logging/internal/common.go b/vendor/cloud.google.com/go/logging/internal/common.go new file mode 100644 index 0000000000..74284d66ae --- /dev/null +++ b/vendor/cloud.google.com/go/logging/internal/common.go @@ -0,0 +1,39 @@ +// 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 internal + +import ( + "fmt" + "strings" +) + +const ( + ProdAddr = "logging.googleapis.com:443" + Version = "0.2.0" +) + +func LogPath(parent, logID string) string { + logID = strings.Replace(logID, "/", "%2F", -1) + return fmt.Sprintf("%s/logs/%s", parent, logID) +} + +func LogIDFromPath(parent, path string) string { + start := len(parent) + len("/logs/") + if len(path) < start { + return "" + } + logID := path[start:] + return strings.Replace(logID, "%2F", "/", -1) +} diff --git a/vendor/cloud.google.com/go/logging/internal/testing/equal.go b/vendor/cloud.google.com/go/logging/internal/testing/equal.go new file mode 100644 index 0000000000..2277a4c2e8 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/internal/testing/equal.go @@ -0,0 +1,42 @@ +/* +Copyright 2017 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 testing + +import ( + "fmt" + + "github.com/golang/protobuf/proto" +) + +// Compare two payloads, assuming they are both proto.Messages +// or both strings. +func PayloadEqual(a, b interface{}) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + switch a := a.(type) { + case proto.Message: + return proto.Equal(a, b.(proto.Message)) + case string: + return a == b.(string) + default: + panic(fmt.Sprintf("payloadEqual: unexpected type %T", a)) + } +} diff --git a/vendor/cloud.google.com/go/logging/internal/testing/fake.go b/vendor/cloud.google.com/go/logging/internal/testing/fake.go new file mode 100644 index 0000000000..f5cab0588c --- /dev/null +++ b/vendor/cloud.google.com/go/logging/internal/testing/fake.go @@ -0,0 +1,395 @@ +/* +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 testing provides support for testing the logging client. +package testing + +import ( + "errors" + "fmt" + "regexp" + "sort" + "strings" + "sync" + "time" + + emptypb "github.com/golang/protobuf/ptypes/empty" + tspb "github.com/golang/protobuf/ptypes/timestamp" + + "cloud.google.com/go/internal/testutil" + context "golang.org/x/net/context" + lpb "google.golang.org/genproto/googleapis/api/label" + mrpb "google.golang.org/genproto/googleapis/api/monitoredres" + logpb "google.golang.org/genproto/googleapis/logging/v2" +) + +type loggingHandler struct { + logpb.LoggingServiceV2Server + + mu sync.Mutex + logs map[string][]*logpb.LogEntry // indexed by log name +} + +type configHandler struct { + logpb.ConfigServiceV2Server + + mu sync.Mutex + sinks map[string]*logpb.LogSink // indexed by (full) sink name +} + +type metricHandler struct { + logpb.MetricsServiceV2Server + + mu sync.Mutex + metrics map[string]*logpb.LogMetric // indexed by (full) metric name +} + +// NewServer creates a new in-memory fake server implementing the logging service. +// It returns the address of the server. +func NewServer() (string, error) { + srv, err := testutil.NewServer() + if err != nil { + return "", err + } + logpb.RegisterLoggingServiceV2Server(srv.Gsrv, &loggingHandler{ + logs: make(map[string][]*logpb.LogEntry), + }) + logpb.RegisterConfigServiceV2Server(srv.Gsrv, &configHandler{ + sinks: make(map[string]*logpb.LogSink), + }) + logpb.RegisterMetricsServiceV2Server(srv.Gsrv, &metricHandler{ + metrics: make(map[string]*logpb.LogMetric), + }) + srv.Start() + return srv.Addr, nil +} + +// DeleteLog deletes a log and all its log entries. The log will reappear if it +// receives new entries. +func (h *loggingHandler) DeleteLog(_ context.Context, req *logpb.DeleteLogRequest) (*emptypb.Empty, error) { + // TODO(jba): return NotFound if log isn't there? + h.mu.Lock() + defer h.mu.Unlock() + delete(h.logs, req.LogName) + return &emptypb.Empty{}, nil +} + +// The only IDs that WriteLogEntries will accept. +// Important for testing Ping. +const ( + validProjectID = "PROJECT_ID" + validOrgID = "433637338589" +) + +// WriteLogEntries writes log entries to Stackdriver Logging. All log entries in +// Stackdriver Logging are written by this method. +func (h *loggingHandler) WriteLogEntries(_ context.Context, req *logpb.WriteLogEntriesRequest) (*logpb.WriteLogEntriesResponse, error) { + if !strings.HasPrefix(req.LogName, "projects/"+validProjectID+"/") && !strings.HasPrefix(req.LogName, "organizations/"+validOrgID+"/") { + return nil, fmt.Errorf("bad LogName: %q", req.LogName) + } + // TODO(jba): support insertId? + h.mu.Lock() + defer h.mu.Unlock() + for _, e := range req.Entries { + // Assign timestamp if missing. + if e.Timestamp == nil { + e.Timestamp = &tspb.Timestamp{Seconds: time.Now().Unix(), Nanos: 0} + } + // Fill from common fields in request. + if e.LogName == "" { + e.LogName = req.LogName + } + if e.Resource == nil { + // TODO(jba): use a global one if nil? + e.Resource = req.Resource + } + for k, v := range req.Labels { + if _, ok := e.Labels[k]; !ok { + e.Labels[k] = v + } + } + + // Store by log name. + h.logs[e.LogName] = append(h.logs[e.LogName], e) + } + return &logpb.WriteLogEntriesResponse{}, nil +} + +// ListLogEntries lists log entries. Use this method to retrieve log entries +// from Stackdriver Logging. +// +// This fake implementation ignores project IDs. It does not support full filtering, only +// expressions of the form "logName = NAME". +func (h *loggingHandler) ListLogEntries(_ context.Context, req *logpb.ListLogEntriesRequest) (*logpb.ListLogEntriesResponse, error) { + h.mu.Lock() + defer h.mu.Unlock() + entries, err := h.filterEntries(req.Filter) + if err != nil { + return nil, err + } + if err = sortEntries(entries, req.OrderBy); err != nil { + return nil, err + } + + from, to, nextPageToken, err := testutil.PageBounds(int(req.PageSize), req.PageToken, len(entries)) + if err != nil { + return nil, err + } + return &logpb.ListLogEntriesResponse{ + Entries: entries[from:to], + NextPageToken: nextPageToken, + }, nil +} + +func (h *loggingHandler) filterEntries(filter string) ([]*logpb.LogEntry, error) { + logName, err := parseFilter(filter) + if err != nil { + return nil, err + } + if logName != "" { + return h.logs[logName], nil + } + var entries []*logpb.LogEntry + for _, es := range h.logs { + entries = append(entries, es...) + } + return entries, nil +} + +var filterRegexp = regexp.MustCompile(`^logName\s*=\s*"?([-_/.%\w]+)"?$`) + +// returns the log name, or "" for the empty filter +func parseFilter(filter string) (string, error) { + if filter == "" { + return "", nil + } + subs := filterRegexp.FindStringSubmatch(filter) + if subs == nil { + return "", invalidArgument("bad filter") + } + return subs[1], nil // cannot panic by construction of regexp +} + +func sortEntries(entries []*logpb.LogEntry, orderBy string) error { + switch orderBy { + case "", "timestamp asc": + sort.Sort(byTimestamp(entries)) + return nil + + case "timestamp desc": + sort.Sort(sort.Reverse(byTimestamp(entries))) + return nil + + default: + return invalidArgument("bad order_by") + } +} + +type byTimestamp []*logpb.LogEntry + +func (s byTimestamp) Len() int { return len(s) } +func (s byTimestamp) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byTimestamp) Less(i, j int) bool { + c := compareTimestamps(s[i].Timestamp, s[j].Timestamp) + switch { + case c < 0: + return true + case c > 0: + return false + default: + return s[i].InsertId < s[j].InsertId + } +} + +func compareTimestamps(ts1, ts2 *tspb.Timestamp) int64 { + if ts1.Seconds != ts2.Seconds { + return ts1.Seconds - ts2.Seconds + } + return int64(ts1.Nanos - ts2.Nanos) +} + +// Lists monitored resource descriptors that are used by Stackdriver Logging. +func (h *loggingHandler) ListMonitoredResourceDescriptors(context.Context, *logpb.ListMonitoredResourceDescriptorsRequest) (*logpb.ListMonitoredResourceDescriptorsResponse, error) { + return &logpb.ListMonitoredResourceDescriptorsResponse{ + ResourceDescriptors: []*mrpb.MonitoredResourceDescriptor{ + { + Type: "global", + DisplayName: "Global", + Description: "... a log is not associated with any specific resource.", + Labels: []*lpb.LabelDescriptor{ + {Key: "project_id", Description: "The identifier of the GCP project..."}, + }, + }, + }, + }, nil +} + +// Lists logs. +func (h *loggingHandler) ListLogs(_ context.Context, req *logpb.ListLogsRequest) (*logpb.ListLogsResponse, error) { + // Return fixed, fake response. + logNames := []string{"a", "b", "c"} + from, to, npt, err := testutil.PageBounds(int(req.PageSize), req.PageToken, len(logNames)) + if err != nil { + return nil, err + } + var lns []string + for _, ln := range logNames[from:to] { + lns = append(lns, req.Parent+"/logs/"+ln) + } + return &logpb.ListLogsResponse{ + LogNames: lns, + NextPageToken: npt, + }, nil +} + +// Gets a sink. +func (h *configHandler) GetSink(_ context.Context, req *logpb.GetSinkRequest) (*logpb.LogSink, error) { + h.mu.Lock() + defer h.mu.Unlock() + if s, ok := h.sinks[req.SinkName]; ok { + return s, nil + } + // TODO(jba): use error codes + return nil, fmt.Errorf("sink %q not found", req.SinkName) +} + +// Creates a sink. +func (h *configHandler) CreateSink(_ context.Context, req *logpb.CreateSinkRequest) (*logpb.LogSink, error) { + h.mu.Lock() + defer h.mu.Unlock() + fullName := fmt.Sprintf("%s/sinks/%s", req.Parent, req.Sink.Name) + if _, ok := h.sinks[fullName]; ok { + return nil, fmt.Errorf("sink with name %q already exists", fullName) + } + h.sinks[fullName] = req.Sink + return req.Sink, nil +} + +// Creates or updates a sink. +func (h *configHandler) UpdateSink(_ context.Context, req *logpb.UpdateSinkRequest) (*logpb.LogSink, error) { + h.mu.Lock() + defer h.mu.Unlock() + // Update of a non-existent sink will create it. + h.sinks[req.SinkName] = req.Sink + return req.Sink, nil +} + +// Deletes a sink. +func (h *configHandler) DeleteSink(_ context.Context, req *logpb.DeleteSinkRequest) (*emptypb.Empty, error) { + h.mu.Lock() + defer h.mu.Unlock() + delete(h.sinks, req.SinkName) + return &emptypb.Empty{}, nil +} + +// Lists sinks. This fake implementation ignores the Parent field of +// ListSinksRequest. All sinks are listed, regardless of their project. +func (h *configHandler) ListSinks(_ context.Context, req *logpb.ListSinksRequest) (*logpb.ListSinksResponse, error) { + h.mu.Lock() + var sinks []*logpb.LogSink + for _, s := range h.sinks { + sinks = append(sinks, s) + } + h.mu.Unlock() // safe because no *logpb.LogSink is ever modified + // Since map iteration varies, sort the sinks. + sort.Sort(sinksByName(sinks)) + from, to, nextPageToken, err := testutil.PageBounds(int(req.PageSize), req.PageToken, len(sinks)) + if err != nil { + return nil, err + } + return &logpb.ListSinksResponse{ + Sinks: sinks[from:to], + NextPageToken: nextPageToken, + }, nil +} + +type sinksByName []*logpb.LogSink + +func (s sinksByName) Len() int { return len(s) } +func (s sinksByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s sinksByName) Less(i, j int) bool { return s[i].Name < s[j].Name } + +// Gets a metric. +func (h *metricHandler) GetLogMetric(_ context.Context, req *logpb.GetLogMetricRequest) (*logpb.LogMetric, error) { + h.mu.Lock() + defer h.mu.Unlock() + if s, ok := h.metrics[req.MetricName]; ok { + return s, nil + } + // TODO(jba): use error codes + return nil, fmt.Errorf("metric %q not found", req.MetricName) +} + +// Creates a metric. +func (h *metricHandler) CreateLogMetric(_ context.Context, req *logpb.CreateLogMetricRequest) (*logpb.LogMetric, error) { + h.mu.Lock() + defer h.mu.Unlock() + fullName := fmt.Sprintf("%s/metrics/%s", req.Parent, req.Metric.Name) + if _, ok := h.metrics[fullName]; ok { + return nil, fmt.Errorf("metric with name %q already exists", fullName) + } + h.metrics[fullName] = req.Metric + return req.Metric, nil +} + +// Creates or updates a metric. +func (h *metricHandler) UpdateLogMetric(_ context.Context, req *logpb.UpdateLogMetricRequest) (*logpb.LogMetric, error) { + h.mu.Lock() + defer h.mu.Unlock() + // Update of a non-existent metric will create it. + h.metrics[req.MetricName] = req.Metric + return req.Metric, nil +} + +// Deletes a metric. +func (h *metricHandler) DeleteLogMetric(_ context.Context, req *logpb.DeleteLogMetricRequest) (*emptypb.Empty, error) { + h.mu.Lock() + defer h.mu.Unlock() + delete(h.metrics, req.MetricName) + return &emptypb.Empty{}, nil +} + +// Lists metrics. This fake implementation ignores the Parent field of +// ListMetricsRequest. All metrics are listed, regardless of their project. +func (h *metricHandler) ListLogMetrics(_ context.Context, req *logpb.ListLogMetricsRequest) (*logpb.ListLogMetricsResponse, error) { + h.mu.Lock() + var metrics []*logpb.LogMetric + for _, s := range h.metrics { + metrics = append(metrics, s) + } + h.mu.Unlock() // safe because no *logpb.LogMetric is ever modified + // Since map iteration varies, sort the metrics. + sort.Sort(metricsByName(metrics)) + from, to, nextPageToken, err := testutil.PageBounds(int(req.PageSize), req.PageToken, len(metrics)) + if err != nil { + return nil, err + } + return &logpb.ListLogMetricsResponse{ + Metrics: metrics[from:to], + NextPageToken: nextPageToken, + }, nil +} + +type metricsByName []*logpb.LogMetric + +func (s metricsByName) Len() int { return len(s) } +func (s metricsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s metricsByName) Less(i, j int) bool { return s[i].Name < s[j].Name } + +func invalidArgument(msg string) error { + // TODO(jba): status codes + return errors.New(msg) +} diff --git a/vendor/cloud.google.com/go/logging/internal/testing/fake_test.go b/vendor/cloud.google.com/go/logging/internal/testing/fake_test.go new file mode 100644 index 0000000000..1ef9cf12a9 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/internal/testing/fake_test.go @@ -0,0 +1,122 @@ +/* +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. +*/ + +// This file contains only basic checks. The fake is effectively tested by the +// logging client unit tests. + +package testing + +import ( + "testing" + "time" + + "github.com/golang/protobuf/proto" + tspb "github.com/golang/protobuf/ptypes/timestamp" + logpb "google.golang.org/genproto/googleapis/logging/v2" + grpc "google.golang.org/grpc" +) + +func TestNewServer(t *testing.T) { + // Confirm that we can create and use a working gRPC server. + addr, err := NewServer() + if err != nil { + t.Fatal(err) + } + conn, err := grpc.Dial(addr, grpc.WithInsecure()) + if err != nil { + t.Fatal(err) + } + // Avoid "connection is closing; please retry" message from gRPC. + time.Sleep(300 * time.Millisecond) + conn.Close() +} + +func TestParseFilter(t *testing.T) { + for _, test := range []struct { + filter string + want string + wantErr bool + }{ + {"", "", false}, + {"logName = syslog", "syslog", false}, + {"logname = syslog", "", true}, + {"logName = 'syslog'", "", true}, + {"logName == syslog", "", true}, + } { + got, err := parseFilter(test.filter) + if err != nil { + if !test.wantErr { + t.Errorf("%q: got %v, want no error", test.filter, err) + } + continue + } + if test.wantErr { + t.Errorf("%q: got no error, want one", test.filter) + continue + } + if got != test.want { + t.Errorf("%q: got %q, want %q", test.filter, got, test.want) + } + } +} + +func TestSortEntries(t *testing.T) { + entries := []*logpb.LogEntry{ + /* 0 */ {Timestamp: &tspb.Timestamp{Seconds: 30}}, + /* 1 */ {Timestamp: &tspb.Timestamp{Seconds: 10}}, + /* 2 */ {Timestamp: &tspb.Timestamp{Seconds: 20}, InsertId: "b"}, + /* 3 */ {Timestamp: &tspb.Timestamp{Seconds: 20}, InsertId: "a"}, + /* 4 */ {Timestamp: &tspb.Timestamp{Seconds: 20}, InsertId: "c"}, + } + for _, test := range []struct { + orderBy string + want []int // slice of index into entries; nil == error + }{ + {"", []int{1, 3, 2, 4, 0}}, + {"timestamp asc", []int{1, 3, 2, 4, 0}}, + {"timestamp desc", []int{0, 4, 2, 3, 1}}, + {"something else", nil}, + } { + got := make([]*logpb.LogEntry, len(entries)) + copy(got, entries) + err := sortEntries(got, test.orderBy) + if err != nil { + if test.want != nil { + t.Errorf("%q: got %v, want nil error", test.orderBy, err) + } + continue + } + want := make([]*logpb.LogEntry, len(entries)) + for i, j := range test.want { + want[i] = entries[j] + } + if !logEntriesEqual(got, want) { + t.Errorf("%q: got %v, want %v", test.orderBy, got, want) + } + } +} + +func logEntriesEqual(a, b []*logpb.LogEntry) bool { + if len(a) != len(b) { + return false + } + for i, aa := range a { + if !proto.Equal(aa, b[i]) { + return false + } + } + return true +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/example_entry_iterator_test.go b/vendor/cloud.google.com/go/logging/logadmin/example_entry_iterator_test.go new file mode 100644 index 0000000000..7551b80739 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/example_entry_iterator_test.go @@ -0,0 +1,66 @@ +// 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 logadmin_test + +import ( + "fmt" + "time" + + "cloud.google.com/go/logging/logadmin" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleClient_Entries() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.Entries(ctx, logadmin.Filter(`logName = "projects/my-project/logs/my-log"`)) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleFilter_timestamp() { + // This example demonstrates how to list the last 24 hours of log entries. + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + oneDayAgo := time.Now().Add(-24 * time.Hour) + t := oneDayAgo.Format(time.RFC3339) // Logging API wants timestamps in RFC 3339 format. + it := client.Entries(ctx, logadmin.Filter(fmt.Sprintf(`timestamp > "%s"`, t))) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleEntryIterator_Next() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.Entries(ctx) + for { + entry, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(entry) + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/example_metric_iterator_test.go b/vendor/cloud.google.com/go/logging/logadmin/example_metric_iterator_test.go new file mode 100644 index 0000000000..903fe0f91d --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/example_metric_iterator_test.go @@ -0,0 +1,52 @@ +// 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 logadmin_test + +import ( + "fmt" + + "cloud.google.com/go/logging/logadmin" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleClient_Metrics() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.Metrics(ctx) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleMetricIterator_Next() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.Metrics(ctx) + for { + metric, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(metric) + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/example_paging_test.go b/vendor/cloud.google.com/go/logging/logadmin/example_paging_test.go new file mode 100644 index 0000000000..fed6d214bb --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/example_paging_test.go @@ -0,0 +1,92 @@ +// 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 logadmin_test + +import ( + "bytes" + "flag" + "fmt" + "html/template" + "log" + "net/http" + + "cloud.google.com/go/logging" + "cloud.google.com/go/logging/logadmin" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +var ( + client *logadmin.Client + projectID = flag.String("project-id", "", "ID of the project to use") +) + +func ExampleClient_Entries_pagination() { + // This example demonstrates how to iterate through items a page at a time + // even if each successive page is fetched by a different process. It is a + // complete web server that displays pages of log entries. To run it as a + // standalone program, rename both the package and this function to "main". + ctx := context.Background() + flag.Parse() + if *projectID == "" { + log.Fatal("-project-id missing") + } + var err error + client, err = logadmin.NewClient(ctx, *projectID) + if err != nil { + log.Fatalf("creating logging client: %v", err) + } + + http.HandleFunc("/entries", handleEntries) + log.Print("listening on 8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +var pageTemplate = template.Must(template.New("").Parse(` +
+ {{range .Entries}} + + {{end}} +
{{.}}
+{{if .Next}} + Next Page +{{end}} +`)) + +func handleEntries(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + filter := fmt.Sprintf(`logName = "projects/%s/logs/testlog"`, *projectID) + it := client.Entries(ctx, logadmin.Filter(filter)) + var entries []*logging.Entry + nextTok, err := iterator.NewPager(it, 5, r.URL.Query().Get("pageToken")).NextPage(&entries) + if err != nil { + http.Error(w, fmt.Sprintf("problem getting the next page: %v", err), http.StatusInternalServerError) + return + } + data := struct { + Entries []*logging.Entry + Next string + }{ + entries, + nextTok, + } + var buf bytes.Buffer + if err := pageTemplate.Execute(&buf, data); err != nil { + http.Error(w, fmt.Sprintf("problem executing page template: %v", err), http.StatusInternalServerError) + } + if _, err := buf.WriteTo(w); err != nil { + log.Printf("writing response: %v", err) + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/example_resource_iterator_test.go b/vendor/cloud.google.com/go/logging/logadmin/example_resource_iterator_test.go new file mode 100644 index 0000000000..6481219b03 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/example_resource_iterator_test.go @@ -0,0 +1,52 @@ +// 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 logadmin_test + +import ( + "fmt" + + "cloud.google.com/go/logging/logadmin" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleClient_ResourceDescriptors() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.ResourceDescriptors(ctx) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleResourceDescriptorIterator_Next() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.ResourceDescriptors(ctx) + for { + rdesc, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(rdesc) + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/example_sink_iterator_test.go b/vendor/cloud.google.com/go/logging/logadmin/example_sink_iterator_test.go new file mode 100644 index 0000000000..2a0fe0baec --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/example_sink_iterator_test.go @@ -0,0 +1,52 @@ +// 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 logadmin_test + +import ( + "fmt" + + "cloud.google.com/go/logging/logadmin" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleClient_Sinks() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.Sinks(ctx) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleSinkIterator_Next() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + it := client.Sinks(ctx) + for { + sink, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(sink) + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/examples_test.go b/vendor/cloud.google.com/go/logging/logadmin/examples_test.go new file mode 100644 index 0000000000..249c768b70 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/examples_test.go @@ -0,0 +1,161 @@ +// 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 logadmin_test + +import ( + "fmt" + + "cloud.google.com/go/logging/logadmin" + "golang.org/x/net/context" +) + +func ExampleNewClient() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + // Use client to manage logs, metrics and sinks. + // Close the client when finished. + if err := client.Close(); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_DeleteLog() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + err = client.DeleteLog(ctx, "my-log") + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_CreateMetric() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + err = client.CreateMetric(ctx, &logadmin.Metric{ + ID: "severe-errors", + Description: "entries at ERROR or higher severities", + Filter: "severity >= ERROR", + }) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_DeleteMetric() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + if err := client.DeleteMetric(ctx, "severe-errors"); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_Metric() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + m, err := client.Metric(ctx, "severe-errors") + if err != nil { + // TODO: Handle error. + } + fmt.Println(m) +} + +func ExampleClient_UpdateMetric() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + err = client.UpdateMetric(ctx, &logadmin.Metric{ + ID: "severe-errors", + Description: "entries at high severities", + Filter: "severity > ERROR", + }) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_CreateSink() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + sink, err := client.CreateSink(ctx, &logadmin.Sink{ + ID: "severe-errors-to-gcs", + Destination: "storage.googleapis.com/my-bucket", + Filter: "severity >= ERROR", + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(sink) +} + +func ExampleClient_DeleteSink() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + if err := client.DeleteSink(ctx, "severe-errors-to-gcs"); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_Sink() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + s, err := client.Sink(ctx, "severe-errors-to-gcs") + if err != nil { + // TODO: Handle error. + } + fmt.Println(s) +} + +func ExampleClient_UpdateSink() { + ctx := context.Background() + client, err := logadmin.NewClient(ctx, "my-project") + if err != nil { + // TODO: Handle error. + } + sink, err := client.UpdateSink(ctx, &logadmin.Sink{ + ID: "severe-errors-to-gcs", + Destination: "storage.googleapis.com/my-other-bucket", + Filter: "severity >= ERROR", + }) + if err != nil { + // TODO: Handle error. + } + fmt.Println(sink) +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/logadmin.go b/vendor/cloud.google.com/go/logging/logadmin/logadmin.go new file mode 100644 index 0000000000..3701febe17 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/logadmin.go @@ -0,0 +1,406 @@ +// 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. + +// These features are missing now, but will likely be added: +// - There is no way to specify CallOptions. + +// Package logadmin contains a Stackdriver Logging client that can be used +// for reading logs and working with sinks, metrics and monitored resources. +// For a client that can write logs, see package cloud.google.com/go/logging. +// +// The client uses Logging API v2. +// See https://cloud.google.com/logging/docs/api/v2/ for an introduction to the API. +// +// Note: This package is in beta. Some backwards-incompatible changes may occur. +package logadmin // import "cloud.google.com/go/logging/logadmin" + +import ( + "fmt" + "math" + "net/http" + "net/url" + "strings" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/logging" + vkit "cloud.google.com/go/logging/apiv2" + "cloud.google.com/go/logging/internal" + "github.com/golang/protobuf/ptypes" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + logtypepb "google.golang.org/genproto/googleapis/logging/type" + logpb "google.golang.org/genproto/googleapis/logging/v2" + "google.golang.org/grpc/codes" + + // Import the following so EntryIterator can unmarshal log protos. + _ "google.golang.org/genproto/googleapis/appengine/logging/v1" + _ "google.golang.org/genproto/googleapis/cloud/audit" +) + +// Client is a Logging client. A Client is associated with a single Cloud project. +type Client struct { + lClient *vkit.Client // logging client + sClient *vkit.ConfigClient // sink client + mClient *vkit.MetricsClient // metric client + parent string + closed bool +} + +// NewClient returns a new logging client associated with the provided project ID. +// +// By default NewClient uses AdminScope. To use a different scope, call +// NewClient using a WithScopes option (see https://godoc.org/google.golang.org/api/option#WithScopes). +func NewClient(ctx context.Context, parent string, opts ...option.ClientOption) (*Client, error) { + if !strings.ContainsRune(parent, '/') { + parent = "projects/" + parent + } + opts = append([]option.ClientOption{ + option.WithEndpoint(internal.ProdAddr), + option.WithScopes(logging.AdminScope), + }, opts...) + lc, err := vkit.NewClient(ctx, opts...) + if err != nil { + return nil, err + } + // TODO(jba): pass along any client options that should be provided to all clients. + sc, err := vkit.NewConfigClient(ctx, option.WithGRPCConn(lc.Connection())) + if err != nil { + return nil, err + } + mc, err := vkit.NewMetricsClient(ctx, option.WithGRPCConn(lc.Connection())) + if err != nil { + return nil, err + } + // Retry some non-idempotent methods on INTERNAL, because it happens sometimes + // and in all observed cases the operation did not complete. + retryerOnInternal := func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.Internal, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 1000 * time.Millisecond, + Multiplier: 1.2, + }) + } + mc.CallOptions.CreateLogMetric = []gax.CallOption{gax.WithRetry(retryerOnInternal)} + mc.CallOptions.UpdateLogMetric = []gax.CallOption{gax.WithRetry(retryerOnInternal)} + + lc.SetGoogleClientInfo("gccl", version.Repo) + sc.SetGoogleClientInfo("gccl", version.Repo) + mc.SetGoogleClientInfo("gccl", version.Repo) + client := &Client{ + lClient: lc, + sClient: sc, + mClient: mc, + parent: parent, + } + return client, nil +} + +// Close closes the client. +func (c *Client) Close() error { + if c.closed { + return nil + } + // Return only the first error. Since all clients share an underlying connection, + // Closes after the first always report a "connection is closing" error. + err := c.lClient.Close() + _ = c.sClient.Close() + _ = c.mClient.Close() + c.closed = true + return err +} + +// DeleteLog deletes a log and all its log entries. The log will reappear if it receives new entries. +// logID identifies the log within the project. An example log ID is "syslog". Requires AdminScope. +func (c *Client) DeleteLog(ctx context.Context, logID string) error { + return c.lClient.DeleteLog(ctx, &logpb.DeleteLogRequest{ + LogName: internal.LogPath(c.parent, logID), + }) +} + +func toHTTPRequest(p *logtypepb.HttpRequest) (*logging.HTTPRequest, error) { + if p == nil { + return nil, nil + } + u, err := url.Parse(p.RequestUrl) + if err != nil { + return nil, err + } + var dur time.Duration + if p.Latency != nil { + dur, err = ptypes.Duration(p.Latency) + if err != nil { + return nil, err + } + } + hr := &http.Request{ + Method: p.RequestMethod, + URL: u, + Header: map[string][]string{}, + } + if p.UserAgent != "" { + hr.Header.Set("User-Agent", p.UserAgent) + } + if p.Referer != "" { + hr.Header.Set("Referer", p.Referer) + } + return &logging.HTTPRequest{ + Request: hr, + RequestSize: p.RequestSize, + Status: int(p.Status), + ResponseSize: p.ResponseSize, + Latency: dur, + RemoteIP: p.RemoteIp, + CacheHit: p.CacheHit, + CacheValidatedWithOriginServer: p.CacheValidatedWithOriginServer, + }, nil +} + +// An EntriesOption is an option for listing log entries. +type EntriesOption interface { + set(*logpb.ListLogEntriesRequest) +} + +// ProjectIDs sets the project IDs or project numbers from which to retrieve +// log entries. Examples of a project ID: "my-project-1A", "1234567890". +func ProjectIDs(pids []string) EntriesOption { return projectIDs(pids) } + +type projectIDs []string + +func (p projectIDs) set(r *logpb.ListLogEntriesRequest) { + r.ResourceNames = make([]string, len(p)) + for i, v := range p { + r.ResourceNames[i] = fmt.Sprintf("projects/%s", v) + } +} + +// ResourceNames sets the resource names from which to retrieve +// log entries. Examples: "projects/my-project-1A", "organizations/my-org". +func ResourceNames(rns []string) EntriesOption { return resourceNames(rns) } + +type resourceNames []string + +func (rn resourceNames) set(r *logpb.ListLogEntriesRequest) { + r.ResourceNames = append([]string(nil), rn...) +} + +// Filter sets an advanced logs filter for listing log entries (see +// https://cloud.google.com/logging/docs/view/advanced_filters). The filter is +// compared against all log entries in the projects specified by ProjectIDs. +// Only entries that match the filter are retrieved. An empty filter (the +// default) matches all log entries. +// +// In the filter string, log names must be written in their full form, as +// "projects/PROJECT-ID/logs/LOG-ID". Forward slashes in LOG-ID must be +// replaced by %2F before calling Filter. +// +// Timestamps in the filter string must be written in RFC 3339 format. See the +// timestamp example. +func Filter(f string) EntriesOption { return filter(f) } + +type filter string + +func (f filter) set(r *logpb.ListLogEntriesRequest) { r.Filter = string(f) } + +// NewestFirst causes log entries to be listed from most recent (newest) to +// least recent (oldest). By default, they are listed from oldest to newest. +func NewestFirst() EntriesOption { return newestFirst{} } + +type newestFirst struct{} + +func (newestFirst) set(r *logpb.ListLogEntriesRequest) { r.OrderBy = "timestamp desc" } + +// Entries returns an EntryIterator for iterating over log entries. By default, +// the log entries will be restricted to those from the project passed to +// NewClient. This may be overridden by passing a ProjectIDs option. Requires ReadScope or AdminScope. +func (c *Client) Entries(ctx context.Context, opts ...EntriesOption) *EntryIterator { + it := &EntryIterator{ + it: c.lClient.ListLogEntries(ctx, listLogEntriesRequest(c.parent, opts)), + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +func listLogEntriesRequest(parent string, opts []EntriesOption) *logpb.ListLogEntriesRequest { + req := &logpb.ListLogEntriesRequest{ + ResourceNames: []string{parent}, + } + for _, opt := range opts { + opt.set(req) + } + return req +} + +// An EntryIterator iterates over log entries. +type EntryIterator struct { + it *vkit.LogEntryIterator + pageInfo *iterator.PageInfo + nextFunc func() error + items []*logging.Entry +} + +// PageInfo supports pagination. See https://godoc.org/google.golang.org/api/iterator package for details. +func (it *EntryIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +// Next returns the next result. Its second return value is iterator.Done +// (https://godoc.org/google.golang.org/api/iterator) if there are no more +// results. Once Next returns Done, all subsequent calls will return Done. +func (it *EntryIterator) Next() (*logging.Entry, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *EntryIterator) fetch(pageSize int, pageToken string) (string, error) { + return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error { + item, err := it.it.Next() + if err != nil { + return err + } + e, err := fromLogEntry(item) + if err != nil { + return err + } + it.items = append(it.items, e) + return nil + }) +} + +func trunc32(i int) int32 { + if i > math.MaxInt32 { + i = math.MaxInt32 + } + return int32(i) +} + +var slashUnescaper = strings.NewReplacer("%2F", "/", "%2f", "/") + +func fromLogEntry(le *logpb.LogEntry) (*logging.Entry, error) { + time, err := ptypes.Timestamp(le.Timestamp) + if err != nil { + return nil, err + } + var payload interface{} + switch x := le.Payload.(type) { + case *logpb.LogEntry_TextPayload: + payload = x.TextPayload + + case *logpb.LogEntry_ProtoPayload: + var d ptypes.DynamicAny + if err := ptypes.UnmarshalAny(x.ProtoPayload, &d); err != nil { + return nil, fmt.Errorf("logging: unmarshalling proto payload: %v", err) + } + payload = d.Message + + case *logpb.LogEntry_JsonPayload: + // Leave this as a Struct. + // TODO(jba): convert to map[string]interface{}? + payload = x.JsonPayload + + default: + return nil, fmt.Errorf("logging: unknown payload type: %T", le.Payload) + } + hr, err := toHTTPRequest(le.HttpRequest) + if err != nil { + return nil, err + } + return &logging.Entry{ + Timestamp: time, + Severity: logging.Severity(le.Severity), + Payload: payload, + Labels: le.Labels, + InsertID: le.InsertId, + HTTPRequest: hr, + Operation: le.Operation, + LogName: slashUnescaper.Replace(le.LogName), + Resource: le.Resource, + Trace: le.Trace, + }, nil +} + +// Logs lists the logs owned by the parent resource of the client. +func (c *Client) Logs(ctx context.Context) *LogIterator { + it := &LogIterator{ + parentResource: c.parent, + it: c.lClient.ListLogs(ctx, &logpb.ListLogsRequest{Parent: c.parent}), + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// A LogIterator iterates over logs. +type LogIterator struct { + parentResource string + it *vkit.StringIterator + pageInfo *iterator.PageInfo + nextFunc func() error + items []string +} + +// PageInfo supports pagination. See https://godoc.org/google.golang.org/api/iterator package for details. +func (it *LogIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +// Next returns the next result. Its second return value is iterator.Done +// (https://godoc.org/google.golang.org/api/iterator) if there are no more +// results. Once Next returns Done, all subsequent calls will return Done. +func (it *LogIterator) Next() (string, error) { + if err := it.nextFunc(); err != nil { + return "", err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *LogIterator) fetch(pageSize int, pageToken string) (string, error) { + return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error { + logPath, err := it.it.Next() + if err != nil { + return err + } + logID := internal.LogIDFromPath(it.parentResource, logPath) + it.items = append(it.items, logID) + return nil + }) +} + +// Common fetch code for iterators that are backed by vkit iterators. +func iterFetch(pageSize int, pageToken string, pi *iterator.PageInfo, next func() error) (string, error) { + pi.MaxSize = pageSize + pi.Token = pageToken + // Get one item, which will fill the buffer. + if err := next(); err != nil { + return "", err + } + // Collect the rest of the buffer. + for pi.Remaining() > 0 { + if err := next(); err != nil { + return "", err + } + } + return pi.Token, nil +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/logadmin_test.go b/vendor/cloud.google.com/go/logging/logadmin/logadmin_test.go new file mode 100644 index 0000000000..e883f1f07e --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/logadmin_test.go @@ -0,0 +1,267 @@ +// 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. + +// TODO(jba): test that OnError is getting called appropriately. + +package logadmin + +import ( + "flag" + "log" + "net/http" + "net/url" + "os" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/logging" + ltesting "cloud.google.com/go/logging/internal/testing" + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + durpb "github.com/golang/protobuf/ptypes/duration" + structpb "github.com/golang/protobuf/ptypes/struct" + "github.com/google/go-cmp/cmp/cmpopts" + "golang.org/x/net/context" + "google.golang.org/api/option" + mrpb "google.golang.org/genproto/googleapis/api/monitoredres" + audit "google.golang.org/genproto/googleapis/cloud/audit" + logtypepb "google.golang.org/genproto/googleapis/logging/type" + logpb "google.golang.org/genproto/googleapis/logging/v2" + "google.golang.org/grpc" +) + +var ( + client *Client + testProjectID string +) + +var ( + // If true, this test is using the production service, not a fake. + integrationTest bool + + newClient func(ctx context.Context, projectID string) *Client +) + +func TestMain(m *testing.M) { + flag.Parse() // needed for testing.Short() + ctx := context.Background() + testProjectID = testutil.ProjID() + if testProjectID == "" || testing.Short() { + integrationTest = false + if testProjectID != "" { + log.Print("Integration tests skipped in short mode (using fake instead)") + } + testProjectID = "PROJECT_ID" + addr, err := ltesting.NewServer() + if err != nil { + log.Fatalf("creating fake server: %v", err) + } + newClient = func(ctx context.Context, projectID string) *Client { + conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + log.Fatalf("dialing %q: %v", addr, err) + } + c, err := NewClient(ctx, projectID, option.WithGRPCConn(conn)) + if err != nil { + log.Fatalf("creating client for fake at %q: %v", addr, err) + } + return c + } + } else { + integrationTest = true + ts := testutil.TokenSource(ctx, logging.AdminScope) + if ts == nil { + log.Fatal("The project key must be set. See CONTRIBUTING.md for details") + } + log.Printf("running integration tests with project %s", testProjectID) + newClient = func(ctx context.Context, projectID string) *Client { + c, err := NewClient(ctx, projectID, option.WithTokenSource(ts), + option.WithGRPCDialOption(grpc.WithBlock())) + if err != nil { + log.Fatalf("creating prod client: %v", err) + } + return c + } + } + client = newClient(ctx, testProjectID) + initMetrics(ctx) + cleanup := initSinks(ctx) + exit := m.Run() + cleanup() + client.Close() + os.Exit(exit) +} + +// EntryIterator and DeleteLog are tested in the logging package. + +func TestClientClose(t *testing.T) { + c := newClient(context.Background(), testProjectID) + if err := c.Close(); err != nil { + t.Errorf("want got %v, want nil", err) + } +} + +func TestFromLogEntry(t *testing.T) { + now := time.Now() + res := &mrpb.MonitoredResource{Type: "global"} + ts, err := ptypes.TimestampProto(now) + if err != nil { + t.Fatal(err) + } + logEntry := logpb.LogEntry{ + LogName: "projects/PROJECT_ID/logs/LOG_ID", + Resource: res, + Payload: &logpb.LogEntry_TextPayload{TextPayload: "hello"}, + Timestamp: ts, + Severity: logtypepb.LogSeverity_INFO, + InsertId: "123", + HttpRequest: &logtypepb.HttpRequest{ + RequestMethod: "GET", + RequestUrl: "http:://example.com/path?q=1", + RequestSize: 100, + Status: 200, + ResponseSize: 25, + Latency: &durpb.Duration{Seconds: 100}, + UserAgent: "user-agent", + RemoteIp: "127.0.0.1", + Referer: "referer", + CacheHit: true, + CacheValidatedWithOriginServer: true, + }, + Labels: map[string]string{ + "a": "1", + "b": "two", + "c": "true", + }, + } + u, err := url.Parse("http:://example.com/path?q=1") + if err != nil { + t.Fatal(err) + } + want := &logging.Entry{ + LogName: "projects/PROJECT_ID/logs/LOG_ID", + Resource: res, + Timestamp: now.In(time.UTC), + Severity: logging.Info, + Payload: "hello", + Labels: map[string]string{ + "a": "1", + "b": "two", + "c": "true", + }, + InsertID: "123", + HTTPRequest: &logging.HTTPRequest{ + Request: &http.Request{ + Method: "GET", + URL: u, + Header: map[string][]string{ + "User-Agent": {"user-agent"}, + "Referer": {"referer"}, + }, + }, + RequestSize: 100, + Status: 200, + ResponseSize: 25, + Latency: 100 * time.Second, + RemoteIP: "127.0.0.1", + CacheHit: true, + CacheValidatedWithOriginServer: true, + }, + } + got, err := fromLogEntry(&logEntry) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(got, want, cmpopts.IgnoreUnexported(http.Request{})); diff != "" { + t.Errorf("FullEntry:\n%s", diff) + } + + // Proto payload. + alog := &audit.AuditLog{ + ServiceName: "svc", + MethodName: "method", + ResourceName: "shelves/S/books/B", + } + any, err := ptypes.MarshalAny(alog) + if err != nil { + t.Fatal(err) + } + logEntry = logpb.LogEntry{ + LogName: "projects/PROJECT_ID/logs/LOG_ID", + Resource: res, + Timestamp: ts, + Payload: &logpb.LogEntry_ProtoPayload{ProtoPayload: any}, + } + got, err = fromLogEntry(&logEntry) + if err != nil { + t.Fatal(err) + } + if !ltesting.PayloadEqual(got.Payload, alog) { + t.Errorf("got %+v, want %+v", got.Payload, alog) + } + + // JSON payload. + jstruct := &structpb.Struct{Fields: map[string]*structpb.Value{ + "f": {Kind: &structpb.Value_NumberValue{NumberValue: 3.1}}, + }} + logEntry = logpb.LogEntry{ + LogName: "projects/PROJECT_ID/logs/LOG_ID", + Resource: res, + Timestamp: ts, + Payload: &logpb.LogEntry_JsonPayload{JsonPayload: jstruct}, + } + got, err = fromLogEntry(&logEntry) + if err != nil { + t.Fatal(err) + } + if !ltesting.PayloadEqual(got.Payload, jstruct) { + t.Errorf("got %+v, want %+v", got.Payload, jstruct) + } +} + +func TestListLogEntriesRequest(t *testing.T) { + for _, test := range []struct { + opts []EntriesOption + resourceNames []string + filter string + orderBy string + }{ + // Default is client's project ID, empty filter and orderBy. + {nil, []string{"projects/PROJECT_ID"}, "", ""}, + {[]EntriesOption{NewestFirst(), Filter("f")}, + []string{"projects/PROJECT_ID"}, "f", "timestamp desc"}, + {[]EntriesOption{ProjectIDs([]string{"foo"})}, + []string{"projects/foo"}, "", ""}, + {[]EntriesOption{ResourceNames([]string{"folders/F", "organizations/O"})}, + []string{"folders/F", "organizations/O"}, "", ""}, + {[]EntriesOption{NewestFirst(), Filter("f"), ProjectIDs([]string{"foo"})}, + []string{"projects/foo"}, "f", "timestamp desc"}, + {[]EntriesOption{NewestFirst(), Filter("f"), ProjectIDs([]string{"foo"})}, + []string{"projects/foo"}, "f", "timestamp desc"}, + // If there are repeats, last one wins. + {[]EntriesOption{NewestFirst(), Filter("no"), ProjectIDs([]string{"foo"}), Filter("f")}, + []string{"projects/foo"}, "f", "timestamp desc"}, + } { + got := listLogEntriesRequest("projects/PROJECT_ID", test.opts) + want := &logpb.ListLogEntriesRequest{ + ResourceNames: test.resourceNames, + Filter: test.filter, + OrderBy: test.orderBy, + } + if !proto.Equal(got, want) { + t.Errorf("%v:\ngot %v\nwant %v", test.opts, got, want) + } + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/metrics.go b/vendor/cloud.google.com/go/logging/logadmin/metrics.go new file mode 100644 index 0000000000..e33b145fc1 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/metrics.go @@ -0,0 +1,154 @@ +// 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 logadmin + +import ( + "fmt" + + vkit "cloud.google.com/go/logging/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + logpb "google.golang.org/genproto/googleapis/logging/v2" +) + +// Metric describes a logs-based metric. The value of the metric is the +// number of log entries that match a logs filter. +// +// Metrics are a feature of Stackdriver Monitoring. +// See https://cloud.google.com/monitoring/api/v3/metrics for more about them. +type Metric struct { + // ID is a client-assigned metric identifier. Example: + // "severe_errors". Metric identifiers are limited to 1000 + // characters and can include only the following characters: A-Z, + // a-z, 0-9, and the special characters _-.,+!*',()%/\. The + // forward-slash character (/) denotes a hierarchy of name pieces, + // and it cannot be the first character of the name. + ID string + + // Description describes this metric. It is used in documentation. + Description string + + // Filter is an advanced logs filter (see + // https://cloud.google.com/logging/docs/view/advanced_filters). + // Example: "logName:syslog AND severity>=ERROR". + Filter string +} + +// CreateMetric creates a logs-based metric. +func (c *Client) CreateMetric(ctx context.Context, m *Metric) error { + _, err := c.mClient.CreateLogMetric(ctx, &logpb.CreateLogMetricRequest{ + Parent: c.parent, + Metric: toLogMetric(m), + }) + return err +} + +// DeleteMetric deletes a log-based metric. +// The provided metric ID is the metric identifier. For example, "severe_errors". +func (c *Client) DeleteMetric(ctx context.Context, metricID string) error { + return c.mClient.DeleteLogMetric(ctx, &logpb.DeleteLogMetricRequest{ + MetricName: c.metricPath(metricID), + }) +} + +// Metric gets a logs-based metric. +// The provided metric ID is the metric identifier. For example, "severe_errors". +// Requires ReadScope or AdminScope. +func (c *Client) Metric(ctx context.Context, metricID string) (*Metric, error) { + lm, err := c.mClient.GetLogMetric(ctx, &logpb.GetLogMetricRequest{ + MetricName: c.metricPath(metricID), + }) + if err != nil { + return nil, err + } + return fromLogMetric(lm), nil +} + +// UpdateMetric creates a logs-based metric if it does not exist, or updates an +// existing one. +func (c *Client) UpdateMetric(ctx context.Context, m *Metric) error { + _, err := c.mClient.UpdateLogMetric(ctx, &logpb.UpdateLogMetricRequest{ + MetricName: c.metricPath(m.ID), + Metric: toLogMetric(m), + }) + return err +} + +func (c *Client) metricPath(metricID string) string { + return fmt.Sprintf("%s/metrics/%s", c.parent, metricID) +} + +// Metrics returns a MetricIterator for iterating over all Metrics in the Client's project. +// Requires ReadScope or AdminScope. +func (c *Client) Metrics(ctx context.Context) *MetricIterator { + it := &MetricIterator{ + it: c.mClient.ListLogMetrics(ctx, &logpb.ListLogMetricsRequest{Parent: c.parent}), + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// A MetricIterator iterates over Metrics. +type MetricIterator struct { + it *vkit.LogMetricIterator + pageInfo *iterator.PageInfo + nextFunc func() error + items []*Metric +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *MetricIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +// Next returns the next result. Its second return value is Done if there are +// no more results. Once Next returns Done, all subsequent calls will return +// Done. +func (it *MetricIterator) Next() (*Metric, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *MetricIterator) fetch(pageSize int, pageToken string) (string, error) { + return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error { + item, err := it.it.Next() + if err != nil { + return err + } + it.items = append(it.items, fromLogMetric(item)) + return nil + }) +} + +func toLogMetric(m *Metric) *logpb.LogMetric { + return &logpb.LogMetric{ + Name: m.ID, + Description: m.Description, + Filter: m.Filter, + } +} + +func fromLogMetric(lm *logpb.LogMetric) *Metric { + return &Metric{ + ID: lm.Name, + Description: lm.Description, + Filter: lm.Filter, + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/metrics_test.go b/vendor/cloud.google.com/go/logging/logadmin/metrics_test.go new file mode 100644 index 0000000000..fbd433ab8b --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/metrics_test.go @@ -0,0 +1,155 @@ +// 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 logadmin + +import ( + "log" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/internal/uid" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +var metricIDs = uid.NewSpace("GO-CLIENT-TEST-METRIC", nil) + +// Initializes the tests before they run. +func initMetrics(ctx context.Context) { + // Clean up from aborted tests. + it := client.Metrics(ctx) +loop: + for { + m, err := it.Next() + switch err { + case nil: + if metricIDs.Older(m.ID, 24*time.Hour) { + client.DeleteMetric(ctx, m.ID) + } + case iterator.Done: + break loop + default: + log.Printf("cleanupMetrics: %v", err) + return + } + } +} + +func TestCreateDeleteMetric(t *testing.T) { + ctx := context.Background() + metric := &Metric{ + ID: metricIDs.New(), + Description: "DESC", + Filter: "FILTER", + } + if err := client.CreateMetric(ctx, metric); err != nil { + t.Fatal(err) + } + defer client.DeleteMetric(ctx, metric.ID) + + got, err := client.Metric(ctx, metric.ID) + if err != nil { + t.Fatal(err) + } + if want := metric; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + if err := client.DeleteMetric(ctx, metric.ID); err != nil { + t.Fatal(err) + } + + if _, err := client.Metric(ctx, metric.ID); err == nil { + t.Fatal("got no error, expected one") + } +} + +func TestUpdateMetric(t *testing.T) { + ctx := context.Background() + metric := &Metric{ + ID: metricIDs.New(), + Description: "DESC", + Filter: "FILTER", + } + + // Updating a non-existent metric creates a new one. + if err := client.UpdateMetric(ctx, metric); err != nil { + t.Fatal(err) + } + defer client.DeleteMetric(ctx, metric.ID) + got, err := client.Metric(ctx, metric.ID) + if err != nil { + t.Fatal(err) + } + if want := metric; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + // Updating an existing metric changes it. + metric.Description = "CHANGED" + if err := client.UpdateMetric(ctx, metric); err != nil { + t.Fatal(err) + } + got, err = client.Metric(ctx, metric.ID) + if err != nil { + t.Fatal(err) + } + if want := metric; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +func TestListMetrics(t *testing.T) { + ctx := context.Background() + + var metrics []*Metric + want := map[string]*Metric{} + for i := 0; i < 10; i++ { + m := &Metric{ + ID: metricIDs.New(), + Description: "DESC", + Filter: "FILTER", + } + metrics = append(metrics, m) + want[m.ID] = m + } + for _, m := range metrics { + if err := client.CreateMetric(ctx, m); err != nil { + t.Fatalf("Create(%q): %v", m.ID, err) + } + defer client.DeleteMetric(ctx, m.ID) + } + + got := map[string]*Metric{} + it := client.Metrics(ctx) + for { + m, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + // If tests run simultaneously, we may have more metrics than we + // created. So only check for our own. + if _, ok := want[m.ID]; ok { + got[m.ID] = m + } + } + if !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/resources.go b/vendor/cloud.google.com/go/logging/logadmin/resources.go new file mode 100644 index 0000000000..32548eba3b --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/resources.go @@ -0,0 +1,74 @@ +// 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 logadmin + +import ( + vkit "cloud.google.com/go/logging/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + mrpb "google.golang.org/genproto/googleapis/api/monitoredres" + logpb "google.golang.org/genproto/googleapis/logging/v2" +) + +// ResourceDescriptors returns a ResourceDescriptorIterator +// for iterating over MonitoredResourceDescriptors. Requires ReadScope or AdminScope. +// See https://cloud.google.com/logging/docs/api/v2/#monitored-resources for an explanation of +// monitored resources. +// See https://cloud.google.com/logging/docs/api/v2/resource-list for a list of monitored resources. +func (c *Client) ResourceDescriptors(ctx context.Context) *ResourceDescriptorIterator { + it := &ResourceDescriptorIterator{ + it: c.lClient.ListMonitoredResourceDescriptors(ctx, + &logpb.ListMonitoredResourceDescriptorsRequest{}), + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// ResourceDescriptorIterator is an iterator over MonitoredResourceDescriptors. +type ResourceDescriptorIterator struct { + it *vkit.MonitoredResourceDescriptorIterator + pageInfo *iterator.PageInfo + nextFunc func() error + items []*mrpb.MonitoredResourceDescriptor +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *ResourceDescriptorIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +// Next returns the next result. Its second return value is Done if there are +// no more results. Once Next returns Done, all subsequent calls will return +// Done. +func (it *ResourceDescriptorIterator) Next() (*mrpb.MonitoredResourceDescriptor, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *ResourceDescriptorIterator) fetch(pageSize int, pageToken string) (string, error) { + return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error { + item, err := it.it.Next() + if err != nil { + return err + } + it.items = append(it.items, item) + return nil + }) +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/resources_test.go b/vendor/cloud.google.com/go/logging/logadmin/resources_test.go new file mode 100644 index 0000000000..62f54c38aa --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/resources_test.go @@ -0,0 +1,46 @@ +// 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 logadmin + +import ( + "testing" + + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func TestMonitoredResourceDescriptors(t *testing.T) { + // We can't create MonitoredResourceDescriptors, and there is no guarantee + // about what the service will return. So we just check that the result is + // non-empty. + it := client.ResourceDescriptors(context.Background()) + n := 0 +loop: + for { + _, err := it.Next() + switch err { + case nil: + n++ + case iterator.Done: + break loop + default: + t.Fatal(err) + } + } + if n == 0 { + t.Fatal("Next: got no MetricResourceDescriptors, expected at least one") + } + // TODO(jba) test pagination. +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/sinks.go b/vendor/cloud.google.com/go/logging/logadmin/sinks.go new file mode 100644 index 0000000000..7a9ec3a800 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/sinks.go @@ -0,0 +1,168 @@ +// 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 logadmin + +import ( + "fmt" + + vkit "cloud.google.com/go/logging/apiv2" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + logpb "google.golang.org/genproto/googleapis/logging/v2" +) + +// Sink describes a sink used to export log entries outside Stackdriver +// Logging. Incoming log entries matching a filter are exported to a +// destination (a Cloud Storage bucket, BigQuery dataset or Cloud Pub/Sub +// topic). +// +// For more information, see https://cloud.google.com/logging/docs/export/using_exported_logs. +// (The Sinks in this package are what the documentation refers to as "project sinks".) +type Sink struct { + // ID is a client-assigned sink identifier. Example: + // "my-severe-errors-to-pubsub". + // Sink identifiers are limited to 1000 characters + // and can include only the following characters: A-Z, a-z, + // 0-9, and the special characters "_-.". + ID string + + // Destination is the export destination. See + // https://cloud.google.com/logging/docs/api/tasks/exporting-logs. + // Examples: "storage.googleapis.com/a-bucket", + // "bigquery.googleapis.com/projects/a-project-id/datasets/a-dataset". + Destination string + + // Filter optionally specifies an advanced logs filter (see + // https://cloud.google.com/logging/docs/view/advanced_filters) that + // defines the log entries to be exported. Example: "logName:syslog AND + // severity>=ERROR". If omitted, all entries are returned. + Filter string +} + +// CreateSink creates a Sink. It returns an error if the Sink already exists. +// Requires AdminScope. +func (c *Client) CreateSink(ctx context.Context, sink *Sink) (*Sink, error) { + ls, err := c.sClient.CreateSink(ctx, &logpb.CreateSinkRequest{ + Parent: c.parent, + Sink: toLogSink(sink), + }) + if err != nil { + fmt.Printf("Sink: %+v\n", toLogSink(sink)) + return nil, err + } + return fromLogSink(ls), nil +} + +// DeleteSink deletes a sink. The provided sinkID is the sink's identifier, such as +// "my-severe-errors-to-pubsub". +// Requires AdminScope. +func (c *Client) DeleteSink(ctx context.Context, sinkID string) error { + return c.sClient.DeleteSink(ctx, &logpb.DeleteSinkRequest{ + SinkName: c.sinkPath(sinkID), + }) +} + +// Sink gets a sink. The provided sinkID is the sink's identifier, such as +// "my-severe-errors-to-pubsub". +// Requires ReadScope or AdminScope. +func (c *Client) Sink(ctx context.Context, sinkID string) (*Sink, error) { + ls, err := c.sClient.GetSink(ctx, &logpb.GetSinkRequest{ + SinkName: c.sinkPath(sinkID), + }) + if err != nil { + return nil, err + } + return fromLogSink(ls), nil +} + +// UpdateSink updates an existing Sink. Requires AdminScope. +func (c *Client) UpdateSink(ctx context.Context, sink *Sink) (*Sink, error) { + ls, err := c.sClient.UpdateSink(ctx, &logpb.UpdateSinkRequest{ + SinkName: c.sinkPath(sink.ID), + Sink: toLogSink(sink), + }) + if err != nil { + return nil, err + } + return fromLogSink(ls), err +} + +func (c *Client) sinkPath(sinkID string) string { + return fmt.Sprintf("%s/sinks/%s", c.parent, sinkID) +} + +// Sinks returns a SinkIterator for iterating over all Sinks in the Client's project. +// Requires ReadScope or AdminScope. +func (c *Client) Sinks(ctx context.Context) *SinkIterator { + it := &SinkIterator{ + it: c.sClient.ListSinks(ctx, &logpb.ListSinksRequest{Parent: c.parent}), + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + return it +} + +// A SinkIterator iterates over Sinks. +type SinkIterator struct { + it *vkit.LogSinkIterator + pageInfo *iterator.PageInfo + nextFunc func() error + items []*Sink +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *SinkIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +// Next returns the next result. Its second return value is Done if there are +// no more results. Once Next returns Done, all subsequent calls will return +// Done. +func (it *SinkIterator) Next() (*Sink, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *SinkIterator) fetch(pageSize int, pageToken string) (string, error) { + return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error { + item, err := it.it.Next() + if err != nil { + return err + } + it.items = append(it.items, fromLogSink(item)) + return nil + }) +} + +func toLogSink(s *Sink) *logpb.LogSink { + return &logpb.LogSink{ + Name: s.ID, + Destination: s.Destination, + Filter: s.Filter, + OutputVersionFormat: logpb.LogSink_V2, + } +} + +func fromLogSink(ls *logpb.LogSink) *Sink { + return &Sink{ + ID: ls.Name, + Destination: ls.Destination, + Filter: ls.Filter, + } +} diff --git a/vendor/cloud.google.com/go/logging/logadmin/sinks_test.go b/vendor/cloud.google.com/go/logging/logadmin/sinks_test.go new file mode 100644 index 0000000000..af1583cac3 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logadmin/sinks_test.go @@ -0,0 +1,228 @@ +// 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. + +// TODO(jba): document in CONTRIBUTING.md that service account must be given "Logs Configuration Writer" IAM role for sink tests to pass. +// TODO(jba): [cont] (1) From top left menu, go to IAM & Admin. (2) In Roles dropdown for acct, select Logging > Logs Configuration Writer. (3) Save. +// TODO(jba): Also, cloud-logs@google.com must have Owner permission on the GCS bucket named for the test project. + +package logadmin + +import ( + "log" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/internal/uid" + "cloud.google.com/go/storage" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var sinkIDs = uid.NewSpace("GO-CLIENT-TEST-SINK", nil) + +const testFilter = "" + +var testSinkDestination string + +// Called just before TestMain calls m.Run. +// Returns a cleanup function to be called after the tests finish. +func initSinks(ctx context.Context) func() { + // Create a unique GCS bucket so concurrent tests don't interfere with each other. + bucketIDs := uid.NewSpace(testProjectID+"-log-sink", nil) + testBucket := bucketIDs.New() + testSinkDestination = "storage.googleapis.com/" + testBucket + var storageClient *storage.Client + if integrationTest { + // Create a unique bucket as a sink destination, and give the cloud logging account + // owner right. + ts := testutil.TokenSource(ctx, storage.ScopeFullControl) + var err error + storageClient, err = storage.NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + log.Fatalf("new storage client: %v", err) + } + bucket := storageClient.Bucket(testBucket) + if err := bucket.Create(ctx, testProjectID, nil); err != nil { + log.Fatalf("creating storage bucket %q: %v", testBucket, err) + } + if err := bucket.ACL().Set(ctx, "group-cloud-logs@google.com", storage.RoleOwner); err != nil { + log.Fatalf("setting owner role: %v", err) + } + } + // Clean up from aborted tests. + it := client.Sinks(ctx) + for { + s, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + log.Printf("listing sinks: %v", err) + break + } + if sinkIDs.Older(s.ID, 24*time.Hour) { + client.DeleteSink(ctx, s.ID) // ignore error + } + } + if integrationTest { + for _, bn := range bucketNames(ctx, storageClient) { + if bucketIDs.Older(bn, 24*time.Hour) { + storageClient.Bucket(bn).Delete(ctx) // ignore error + } + } + return func() { + if err := storageClient.Bucket(testBucket).Delete(ctx); err != nil { + log.Printf("deleting %q: %v", testBucket, err) + } + storageClient.Close() + } + } + return func() {} +} + +// Collect the name of all buckets for the test project. +func bucketNames(ctx context.Context, client *storage.Client) []string { + var names []string + it := client.Buckets(ctx, testProjectID) +loop: + for { + b, err := it.Next() + switch err { + case nil: + names = append(names, b.Name) + case iterator.Done: + break loop + default: + log.Printf("listing buckets: %v", err) + break loop + } + } + return names +} + +func TestCreateDeleteSink(t *testing.T) { + ctx := context.Background() + sink := &Sink{ + ID: sinkIDs.New(), + Destination: testSinkDestination, + Filter: testFilter, + } + got, err := client.CreateSink(ctx, sink) + if err != nil { + t.Fatal(err) + } + defer client.DeleteSink(ctx, sink.ID) + if want := sink; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + got, err = client.Sink(ctx, sink.ID) + if err != nil { + t.Fatal(err) + } + if want := sink; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + if err := client.DeleteSink(ctx, sink.ID); err != nil { + t.Fatal(err) + } + + if _, err := client.Sink(ctx, sink.ID); err == nil { + t.Fatal("got no error, expected one") + } +} + +func TestUpdateSink(t *testing.T) { + ctx := context.Background() + sink := &Sink{ + ID: sinkIDs.New(), + Destination: testSinkDestination, + Filter: testFilter, + } + + if _, err := client.CreateSink(ctx, sink); err != nil { + t.Fatal(err) + } + got, err := client.UpdateSink(ctx, sink) + if err != nil { + t.Fatal(err) + } + defer client.DeleteSink(ctx, sink.ID) + if want := sink; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + got, err = client.Sink(ctx, sink.ID) + if err != nil { + t.Fatal(err) + } + if want := sink; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + // Updating an existing sink changes it. + sink.Filter = "" + if _, err := client.UpdateSink(ctx, sink); err != nil { + t.Fatal(err) + } + got, err = client.Sink(ctx, sink.ID) + if err != nil { + t.Fatal(err) + } + if want := sink; !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +func TestListSinks(t *testing.T) { + ctx := context.Background() + var sinks []*Sink + want := map[string]*Sink{} + for i := 0; i < 4; i++ { + s := &Sink{ + ID: sinkIDs.New(), + Destination: testSinkDestination, + Filter: testFilter, + } + sinks = append(sinks, s) + want[s.ID] = s + } + for _, s := range sinks { + if _, err := client.CreateSink(ctx, s); err != nil { + t.Fatalf("Create(%q): %v", s.ID, err) + } + defer client.DeleteSink(ctx, s.ID) + } + + got := map[string]*Sink{} + it := client.Sinks(ctx) + for { + s, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + // If tests run simultaneously, we may have more sinks than we + // created. So only check for our own. + if _, ok := want[s.ID]; ok { + got[s.ID] = s + } + } + if !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} diff --git a/vendor/cloud.google.com/go/logging/logging.go b/vendor/cloud.google.com/go/logging/logging.go new file mode 100644 index 0000000000..52e113495e --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logging.go @@ -0,0 +1,819 @@ +// 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. + +// API/gRPC features intentionally missing from this client: +// - You cannot have the server pick the time of the entry. This client +// always sends a time. +// - There is no way to provide a protocol buffer payload. +// - No support for the "partial success" feature when writing log entries. + +// TODO(jba): test whether forward-slash characters in the log ID must be URL-encoded. +// These features are missing now, but will likely be added: +// - There is no way to specify CallOptions. + +package logging + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "math" + "net/http" + "strconv" + "strings" + "sync" + "time" + + "cloud.google.com/go/compute/metadata" + "cloud.google.com/go/internal/version" + vkit "cloud.google.com/go/logging/apiv2" + "cloud.google.com/go/logging/internal" + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + structpb "github.com/golang/protobuf/ptypes/struct" + tspb "github.com/golang/protobuf/ptypes/timestamp" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/support/bundler" + mrpb "google.golang.org/genproto/googleapis/api/monitoredres" + logtypepb "google.golang.org/genproto/googleapis/logging/type" + logpb "google.golang.org/genproto/googleapis/logging/v2" +) + +const ( + // Scope for reading from the logging service. + ReadScope = "https://www.googleapis.com/auth/logging.read" + + // Scope for writing to the logging service. + WriteScope = "https://www.googleapis.com/auth/logging.write" + + // Scope for administrative actions on the logging service. + AdminScope = "https://www.googleapis.com/auth/logging.admin" +) + +const ( + // defaultErrorCapacity is the capacity of the channel used to deliver + // errors to the OnError function. + defaultErrorCapacity = 10 + + // DefaultDelayThreshold is the default value for the DelayThreshold LoggerOption. + DefaultDelayThreshold = time.Second + + // DefaultEntryCountThreshold is the default value for the EntryCountThreshold LoggerOption. + DefaultEntryCountThreshold = 1000 + + // DefaultEntryByteThreshold is the default value for the EntryByteThreshold LoggerOption. + DefaultEntryByteThreshold = 1 << 20 // 1MiB + + // DefaultBufferedByteLimit is the default value for the BufferedByteLimit LoggerOption. + DefaultBufferedByteLimit = 1 << 30 // 1GiB + + // defaultWriteTimeout is the timeout for the underlying write API calls. As + // write API calls are not idempotent, they are not retried on timeout. This + // timeout is to allow clients to degrade gracefully if underlying logging + // service is temporarily impaired for some reason. + defaultWriteTimeout = 10 * time.Minute +) + +// For testing: +var now = time.Now + +// ErrOverflow signals that the number of buffered entries for a Logger +// exceeds its BufferLimit. +var ErrOverflow = bundler.ErrOverflow + +// ErrOversizedEntry signals that an entry's size exceeds the maximum number of +// bytes that will be sent in a single call to the logging service. +var ErrOversizedEntry = bundler.ErrOversizedItem + +// Client is a Logging client. A Client is associated with a single Cloud project. +type Client struct { + client *vkit.Client // client for the logging service + parent string // e.g. "projects/proj-id" + errc chan error // should be buffered to minimize dropped errors + donec chan struct{} // closed on Client.Close to close Logger bundlers + loggers sync.WaitGroup // so we can wait for loggers to close + closed bool + + mu sync.Mutex + nErrs int // number of errors we saw + lastErr error // last error we saw + + // OnError is called when an error occurs in a call to Log or Flush. The + // error may be due to an invalid Entry, an overflow because BufferLimit + // was reached (in which case the error will be ErrOverflow) or an error + // communicating with the logging service. OnError is called with errors + // from all Loggers. It is never called concurrently. OnError is expected + // to return quickly; if errors occur while OnError is running, some may + // not be reported. The default behavior is to call log.Printf. + // + // This field should be set only once, before any method of Client is called. + OnError func(err error) +} + +// NewClient returns a new logging client associated with the provided parent. +// A parent can take any of the following forms: +// projects/PROJECT_ID +// folders/FOLDER_ID +// billingAccounts/ACCOUNT_ID +// organizations/ORG_ID +// for backwards compatibility, a string with no '/' is also allowed and is interpreted +// as a project ID. +// +// By default NewClient uses WriteScope. To use a different scope, call +// NewClient using a WithScopes option (see https://godoc.org/google.golang.org/api/option#WithScopes). +func NewClient(ctx context.Context, parent string, opts ...option.ClientOption) (*Client, error) { + if !strings.ContainsRune(parent, '/') { + parent = "projects/" + parent + } + opts = append([]option.ClientOption{ + option.WithEndpoint(internal.ProdAddr), + option.WithScopes(WriteScope), + }, opts...) + c, err := vkit.NewClient(ctx, opts...) + if err != nil { + return nil, err + } + c.SetGoogleClientInfo("gccl", version.Repo) + client := &Client{ + client: c, + parent: parent, + errc: make(chan error, defaultErrorCapacity), // create a small buffer for errors + donec: make(chan struct{}), + OnError: func(e error) { log.Printf("logging client: %v", e) }, + } + // Call the user's function synchronously, to make life easier for them. + go func() { + for err := range client.errc { + // This reference to OnError is memory-safe if the user sets OnError before + // calling any client methods. The reference happens before the first read from + // client.errc, which happens before the first write to client.errc, which + // happens before any call, which happens before the user sets OnError. + if fn := client.OnError; fn != nil { + fn(err) + } else { + log.Printf("logging (parent %q): %v", parent, err) + } + } + }() + return client, nil +} + +var unixZeroTimestamp *tspb.Timestamp + +func init() { + var err error + unixZeroTimestamp, err = ptypes.TimestampProto(time.Unix(0, 0)) + if err != nil { + panic(err) + } +} + +// Ping reports whether the client's connection to the logging service and the +// authentication configuration are valid. To accomplish this, Ping writes a +// log entry "ping" to a log named "ping". +func (c *Client) Ping(ctx context.Context) error { + ent := &logpb.LogEntry{ + Payload: &logpb.LogEntry_TextPayload{TextPayload: "ping"}, + Timestamp: unixZeroTimestamp, // Identical timestamps and insert IDs are both + InsertId: "ping", // necessary for the service to dedup these entries. + } + _, err := c.client.WriteLogEntries(ctx, &logpb.WriteLogEntriesRequest{ + LogName: internal.LogPath(c.parent, "ping"), + Resource: monitoredResource(c.parent), + Entries: []*logpb.LogEntry{ent}, + }) + return err +} + +// error puts the error on the client's error channel +// without blocking, and records summary error info. +func (c *Client) error(err error) { + select { + case c.errc <- err: + default: + } + c.mu.Lock() + c.lastErr = err + c.nErrs++ + c.mu.Unlock() +} + +func (c *Client) extractErrorInfo() error { + var err error + c.mu.Lock() + if c.lastErr != nil { + err = fmt.Errorf("saw %d errors; last: %v", c.nErrs, c.lastErr) + c.nErrs = 0 + c.lastErr = nil + } + c.mu.Unlock() + return err +} + +// A Logger is used to write log messages to a single log. It can be configured +// with a log ID, common monitored resource, and a set of common labels. +type Logger struct { + client *Client + logName string // "projects/{projectID}/logs/{logID}" + stdLoggers map[Severity]*log.Logger + bundler *bundler.Bundler + + // Options + commonResource *mrpb.MonitoredResource + commonLabels map[string]string + writeTimeout time.Duration +} + +// A LoggerOption is a configuration option for a Logger. +type LoggerOption interface { + set(*Logger) +} + +// CommonResource sets the monitored resource associated with all log entries +// written from a Logger. If not provided, the resource is automatically +// detected based on the running environment. This value can be overridden +// per-entry by setting an Entry's Resource field. +func CommonResource(r *mrpb.MonitoredResource) LoggerOption { return commonResource{r} } + +type commonResource struct{ *mrpb.MonitoredResource } + +func (r commonResource) set(l *Logger) { l.commonResource = r.MonitoredResource } + +var detectedResource struct { + pb *mrpb.MonitoredResource + once sync.Once +} + +func detectResource() *mrpb.MonitoredResource { + detectedResource.once.Do(func() { + if !metadata.OnGCE() { + return + } + projectID, err := metadata.ProjectID() + if err != nil { + return + } + id, err := metadata.InstanceID() + if err != nil { + return + } + zone, err := metadata.Zone() + if err != nil { + return + } + detectedResource.pb = &mrpb.MonitoredResource{ + Type: "gce_instance", + Labels: map[string]string{ + "project_id": projectID, + "instance_id": id, + "zone": zone, + }, + } + }) + return detectedResource.pb +} + +var resourceInfo = map[string]struct{ rtype, label string }{ + "organizations": {"organization", "organization_id"}, + "folders": {"folder", "folder_id"}, + "projects": {"project", "project_id"}, + "billingAccounts": {"billing_account", "account_id"}, +} + +func monitoredResource(parent string) *mrpb.MonitoredResource { + parts := strings.SplitN(parent, "/", 2) + if len(parts) != 2 { + return globalResource(parent) + } + info, ok := resourceInfo[parts[0]] + if !ok { + return globalResource(parts[1]) + } + return &mrpb.MonitoredResource{ + Type: info.rtype, + Labels: map[string]string{info.label: parts[1]}, + } +} + +func globalResource(projectID string) *mrpb.MonitoredResource { + return &mrpb.MonitoredResource{ + Type: "global", + Labels: map[string]string{ + "project_id": projectID, + }, + } +} + +// CommonLabels are labels that apply to all log entries written from a Logger, +// so that you don't have to repeat them in each log entry's Labels field. If +// any of the log entries contains a (key, value) with the same key that is in +// CommonLabels, then the entry's (key, value) overrides the one in +// CommonLabels. +func CommonLabels(m map[string]string) LoggerOption { return commonLabels(m) } + +type commonLabels map[string]string + +func (c commonLabels) set(l *Logger) { l.commonLabels = c } + +// ConcurrentWriteLimit determines how many goroutines will send log entries to the +// underlying service. The default is 1. Set ConcurrentWriteLimit to a higher value to +// increase throughput. +func ConcurrentWriteLimit(n int) LoggerOption { return concurrentWriteLimit(n) } + +type concurrentWriteLimit int + +func (c concurrentWriteLimit) set(l *Logger) { l.bundler.HandlerLimit = int(c) } + +// DelayThreshold is the maximum amount of time that an entry should remain +// buffered in memory before a call to the logging service is triggered. Larger +// values of DelayThreshold will generally result in fewer calls to the logging +// service, while increasing the risk that log entries will be lost if the +// process crashes. +// The default is DefaultDelayThreshold. +func DelayThreshold(d time.Duration) LoggerOption { return delayThreshold(d) } + +type delayThreshold time.Duration + +func (d delayThreshold) set(l *Logger) { l.bundler.DelayThreshold = time.Duration(d) } + +// EntryCountThreshold is the maximum number of entries that will be buffered +// in memory before a call to the logging service is triggered. Larger values +// will generally result in fewer calls to the logging service, while +// increasing both memory consumption and the risk that log entries will be +// lost if the process crashes. +// The default is DefaultEntryCountThreshold. +func EntryCountThreshold(n int) LoggerOption { return entryCountThreshold(n) } + +type entryCountThreshold int + +func (e entryCountThreshold) set(l *Logger) { l.bundler.BundleCountThreshold = int(e) } + +// EntryByteThreshold is the maximum number of bytes of entries that will be +// buffered in memory before a call to the logging service is triggered. See +// EntryCountThreshold for a discussion of the tradeoffs involved in setting +// this option. +// The default is DefaultEntryByteThreshold. +func EntryByteThreshold(n int) LoggerOption { return entryByteThreshold(n) } + +type entryByteThreshold int + +func (e entryByteThreshold) set(l *Logger) { l.bundler.BundleByteThreshold = int(e) } + +// EntryByteLimit is the maximum number of bytes of entries that will be sent +// in a single call to the logging service. ErrOversizedEntry is returned if an +// entry exceeds EntryByteLimit. This option limits the size of a single RPC +// payload, to account for network or service issues with large RPCs. If +// EntryByteLimit is smaller than EntryByteThreshold, the latter has no effect. +// The default is zero, meaning there is no limit. +func EntryByteLimit(n int) LoggerOption { return entryByteLimit(n) } + +type entryByteLimit int + +func (e entryByteLimit) set(l *Logger) { l.bundler.BundleByteLimit = int(e) } + +// BufferedByteLimit is the maximum number of bytes that the Logger will keep +// in memory before returning ErrOverflow. This option limits the total memory +// consumption of the Logger (but note that each Logger has its own, separate +// limit). It is possible to reach BufferedByteLimit even if it is larger than +// EntryByteThreshold or EntryByteLimit, because calls triggered by the latter +// two options may be enqueued (and hence occupying memory) while new log +// entries are being added. +// The default is DefaultBufferedByteLimit. +func BufferedByteLimit(n int) LoggerOption { return bufferedByteLimit(n) } + +type bufferedByteLimit int + +func (b bufferedByteLimit) set(l *Logger) { l.bundler.BufferedByteLimit = int(b) } + +// Logger returns a Logger that will write entries with the given log ID, such as +// "syslog". A log ID must be less than 512 characters long and can only +// include the following characters: upper and lower case alphanumeric +// characters: [A-Za-z0-9]; and punctuation characters: forward-slash, +// underscore, hyphen, and period. +func (c *Client) Logger(logID string, opts ...LoggerOption) *Logger { + r := detectResource() + if r == nil { + r = monitoredResource(c.parent) + } + l := &Logger{ + client: c, + logName: internal.LogPath(c.parent, logID), + commonResource: r, + } + l.bundler = bundler.NewBundler(&logpb.LogEntry{}, func(entries interface{}) { + l.writeLogEntries(entries.([]*logpb.LogEntry)) + }) + l.bundler.DelayThreshold = DefaultDelayThreshold + l.bundler.BundleCountThreshold = DefaultEntryCountThreshold + l.bundler.BundleByteThreshold = DefaultEntryByteThreshold + l.bundler.BufferedByteLimit = DefaultBufferedByteLimit + for _, opt := range opts { + opt.set(l) + } + l.stdLoggers = map[Severity]*log.Logger{} + for s := range severityName { + l.stdLoggers[s] = log.New(severityWriter{l, s}, "", 0) + } + + c.loggers.Add(1) + // Start a goroutine that cleans up the bundler, its channel + // and the writer goroutines when the client is closed. + go func() { + defer c.loggers.Done() + <-c.donec + l.bundler.Flush() + }() + return l +} + +type severityWriter struct { + l *Logger + s Severity +} + +func (w severityWriter) Write(p []byte) (n int, err error) { + w.l.Log(Entry{ + Severity: w.s, + Payload: string(p), + }) + return len(p), nil +} + +// Close waits for all opened loggers to be flushed and closes the client. +func (c *Client) Close() error { + if c.closed { + return nil + } + close(c.donec) // close Logger bundlers + c.loggers.Wait() // wait for all bundlers to flush and close + // Now there can be no more errors. + close(c.errc) // terminate error goroutine + // Prefer errors arising from logging to the error returned from Close. + err := c.extractErrorInfo() + err2 := c.client.Close() + if err == nil { + err = err2 + } + c.closed = true + return err +} + +// Severity is the severity of the event described in a log entry. These +// guideline severity levels are ordered, with numerically smaller levels +// treated as less severe than numerically larger levels. +type Severity int + +const ( + // Default means the log entry has no assigned severity level. + Default = Severity(logtypepb.LogSeverity_DEFAULT) + // Debug means debug or trace information. + Debug = Severity(logtypepb.LogSeverity_DEBUG) + // Info means routine information, such as ongoing status or performance. + Info = Severity(logtypepb.LogSeverity_INFO) + // Notice means normal but significant events, such as start up, shut down, or configuration. + Notice = Severity(logtypepb.LogSeverity_NOTICE) + // Warning means events that might cause problems. + Warning = Severity(logtypepb.LogSeverity_WARNING) + // Error means events that are likely to cause problems. + Error = Severity(logtypepb.LogSeverity_ERROR) + // Critical means events that cause more severe problems or brief outages. + Critical = Severity(logtypepb.LogSeverity_CRITICAL) + // Alert means a person must take an action immediately. + Alert = Severity(logtypepb.LogSeverity_ALERT) + // Emergency means one or more systems are unusable. + Emergency = Severity(logtypepb.LogSeverity_EMERGENCY) +) + +var severityName = map[Severity]string{ + Default: "Default", + Debug: "Debug", + Info: "Info", + Notice: "Notice", + Warning: "Warning", + Error: "Error", + Critical: "Critical", + Alert: "Alert", + Emergency: "Emergency", +} + +// String converts a severity level to a string. +func (v Severity) String() string { + // same as proto.EnumName + s, ok := severityName[v] + if ok { + return s + } + return strconv.Itoa(int(v)) +} + +// ParseSeverity returns the Severity whose name equals s, ignoring case. It +// returns Default if no Severity matches. +func ParseSeverity(s string) Severity { + sl := strings.ToLower(s) + for sev, name := range severityName { + if strings.ToLower(name) == sl { + return sev + } + } + return Default +} + +// Entry is a log entry. +// See https://cloud.google.com/logging/docs/view/logs_index for more about entries. +type Entry struct { + // Timestamp is the time of the entry. If zero, the current time is used. + Timestamp time.Time + + // Severity is the entry's severity level. + // The zero value is Default. + Severity Severity + + // Payload must be either a string, or something that marshals via the + // encoding/json package to a JSON object (and not any other type of JSON value). + Payload interface{} + + // Labels optionally specifies key/value labels for the log entry. + // The Logger.Log method takes ownership of this map. See Logger.CommonLabels + // for more about labels. + Labels map[string]string + + // InsertID is a unique ID for the log entry. If you provide this field, + // the logging service considers other log entries in the same log with the + // same ID as duplicates which can be removed. If omitted, the logging + // service will generate a unique ID for this log entry. Note that because + // this client retries RPCs automatically, it is possible (though unlikely) + // that an Entry without an InsertID will be written more than once. + InsertID string + + // HTTPRequest optionally specifies metadata about the HTTP request + // associated with this log entry, if applicable. It is optional. + HTTPRequest *HTTPRequest + + // Operation optionally provides information about an operation associated + // with the log entry, if applicable. + Operation *logpb.LogEntryOperation + + // LogName is the full log name, in the form + // "projects/{ProjectID}/logs/{LogID}". It is set by the client when + // reading entries. It is an error to set it when writing entries. + LogName string + + // Resource is the monitored resource associated with the entry. + Resource *mrpb.MonitoredResource + + // Trace is the resource name of the trace associated with the log entry, + // if any. If it contains a relative resource name, the name is assumed to + // be relative to //tracing.googleapis.com. + Trace string + + // Optional. Source code location information associated with the log entry, + // if any. + SourceLocation *logpb.LogEntrySourceLocation +} + +// HTTPRequest contains an http.Request as well as additional +// information about the request and its response. +type HTTPRequest struct { + // Request is the http.Request passed to the handler. + Request *http.Request + + // RequestSize is the size of the HTTP request message in bytes, including + // the request headers and the request body. + RequestSize int64 + + // Status is the response code indicating the status of the response. + // Examples: 200, 404. + Status int + + // ResponseSize is the size of the HTTP response message sent back to the client, in bytes, + // including the response headers and the response body. + ResponseSize int64 + + // Latency is the request processing latency on the server, from the time the request was + // received until the response was sent. + Latency time.Duration + + // LocalIP is the IP address (IPv4 or IPv6) of the origin server that the request + // was sent to. + LocalIP string + + // RemoteIP is the IP address (IPv4 or IPv6) of the client that issued the + // HTTP request. Examples: "192.168.1.1", "FE80::0202:B3FF:FE1E:8329". + RemoteIP string + + // CacheHit reports whether an entity was served from cache (with or without + // validation). + CacheHit bool + + // CacheValidatedWithOriginServer reports whether the response was + // validated with the origin server before being served from cache. This + // field is only meaningful if CacheHit is true. + CacheValidatedWithOriginServer bool +} + +func fromHTTPRequest(r *HTTPRequest) *logtypepb.HttpRequest { + if r == nil { + return nil + } + if r.Request == nil { + panic("HTTPRequest must have a non-nil Request") + } + u := *r.Request.URL + u.Fragment = "" + pb := &logtypepb.HttpRequest{ + RequestMethod: r.Request.Method, + RequestUrl: u.String(), + RequestSize: r.RequestSize, + Status: int32(r.Status), + ResponseSize: r.ResponseSize, + UserAgent: r.Request.UserAgent(), + ServerIp: r.LocalIP, + RemoteIp: r.RemoteIP, // TODO(jba): attempt to parse http.Request.RemoteAddr? + Referer: r.Request.Referer(), + CacheHit: r.CacheHit, + CacheValidatedWithOriginServer: r.CacheValidatedWithOriginServer, + } + if r.Latency != 0 { + pb.Latency = ptypes.DurationProto(r.Latency) + } + return pb +} + +// toProtoStruct converts v, which must marshal into a JSON object, +// into a Google Struct proto. +func toProtoStruct(v interface{}) (*structpb.Struct, error) { + // Fast path: if v is already a *structpb.Struct, nothing to do. + if s, ok := v.(*structpb.Struct); ok { + return s, nil + } + // v is a Go value that supports JSON marshalling. We want a Struct + // protobuf. Some day we may have a more direct way to get there, but right + // now the only way is to marshal the Go value to JSON, unmarshal into a + // map, and then build the Struct proto from the map. + var jb []byte + var err error + if raw, ok := v.(json.RawMessage); ok { // needed for Go 1.7 and below + jb = []byte(raw) + } else { + jb, err = json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("logging: json.Marshal: %v", err) + } + } + var m map[string]interface{} + err = json.Unmarshal(jb, &m) + if err != nil { + return nil, fmt.Errorf("logging: json.Unmarshal: %v", err) + } + return jsonMapToProtoStruct(m), nil +} + +func jsonMapToProtoStruct(m map[string]interface{}) *structpb.Struct { + fields := map[string]*structpb.Value{} + for k, v := range m { + fields[k] = jsonValueToStructValue(v) + } + return &structpb.Struct{Fields: fields} +} + +func jsonValueToStructValue(v interface{}) *structpb.Value { + switch x := v.(type) { + case bool: + return &structpb.Value{Kind: &structpb.Value_BoolValue{BoolValue: x}} + case float64: + return &structpb.Value{Kind: &structpb.Value_NumberValue{NumberValue: x}} + case string: + return &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: x}} + case nil: + return &structpb.Value{Kind: &structpb.Value_NullValue{}} + case map[string]interface{}: + return &structpb.Value{Kind: &structpb.Value_StructValue{StructValue: jsonMapToProtoStruct(x)}} + case []interface{}: + var vals []*structpb.Value + for _, e := range x { + vals = append(vals, jsonValueToStructValue(e)) + } + return &structpb.Value{Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: vals}}} + default: + panic(fmt.Sprintf("bad type %T for JSON value", v)) + } +} + +// LogSync logs the Entry synchronously without any buffering. Because LogSync is slow +// and will block, it is intended primarily for debugging or critical errors. +// Prefer Log for most uses. +// TODO(jba): come up with a better name (LogNow?) or eliminate. +func (l *Logger) LogSync(ctx context.Context, e Entry) error { + ent, err := toLogEntry(e) + if err != nil { + return err + } + _, err = l.client.client.WriteLogEntries(ctx, &logpb.WriteLogEntriesRequest{ + LogName: l.logName, + Resource: l.commonResource, + Labels: l.commonLabels, + Entries: []*logpb.LogEntry{ent}, + }) + return err +} + +// Log buffers the Entry for output to the logging service. It never blocks. +func (l *Logger) Log(e Entry) { + ent, err := toLogEntry(e) + if err != nil { + l.client.error(err) + return + } + if err := l.bundler.Add(ent, proto.Size(ent)); err != nil { + l.client.error(err) + } +} + +// Flush blocks until all currently buffered log entries are sent. +// +// If any errors occurred since the last call to Flush from any Logger, or the +// creation of the client if this is the first call, then Flush returns a non-nil +// error with summary information about the errors. This information is unlikely to +// be actionable. For more accurate error reporting, set Client.OnError. +func (l *Logger) Flush() error { + l.bundler.Flush() + return l.client.extractErrorInfo() +} + +func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) { + req := &logpb.WriteLogEntriesRequest{ + LogName: l.logName, + Resource: l.commonResource, + Labels: l.commonLabels, + Entries: entries, + } + ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout) + defer cancel() + _, err := l.client.client.WriteLogEntries(ctx, req) + if err != nil { + l.client.error(err) + } +} + +// StandardLogger returns a *log.Logger for the provided severity. +// +// This method is cheap. A single log.Logger is pre-allocated for each +// severity level in each Logger. Callers may mutate the returned log.Logger +// (for example by calling SetFlags or SetPrefix). +func (l *Logger) StandardLogger(s Severity) *log.Logger { return l.stdLoggers[s] } + +func trunc32(i int) int32 { + if i > math.MaxInt32 { + i = math.MaxInt32 + } + return int32(i) +} + +func toLogEntry(e Entry) (*logpb.LogEntry, error) { + if e.LogName != "" { + return nil, errors.New("logging: Entry.LogName should be not be set when writing") + } + t := e.Timestamp + if t.IsZero() { + t = now() + } + ts, err := ptypes.TimestampProto(t) + if err != nil { + return nil, err + } + ent := &logpb.LogEntry{ + Timestamp: ts, + Severity: logtypepb.LogSeverity(e.Severity), + InsertId: e.InsertID, + HttpRequest: fromHTTPRequest(e.HTTPRequest), + Operation: e.Operation, + Labels: e.Labels, + Trace: e.Trace, + Resource: e.Resource, + SourceLocation: e.SourceLocation, + } + switch p := e.Payload.(type) { + case string: + ent.Payload = &logpb.LogEntry_TextPayload{TextPayload: p} + default: + s, err := toProtoStruct(p) + if err != nil { + return nil, err + } + ent.Payload = &logpb.LogEntry_JsonPayload{JsonPayload: s} + } + return ent, nil +} diff --git a/vendor/cloud.google.com/go/logging/logging_test.go b/vendor/cloud.google.com/go/logging/logging_test.go new file mode 100644 index 0000000000..7d43886702 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logging_test.go @@ -0,0 +1,632 @@ +// 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. + +// TODO(jba): test that OnError is getting called appropriately. + +package logging_test + +import ( + "flag" + "fmt" + "log" + "math/rand" + "os" + "strings" + "sync" + "testing" + "time" + + gax "github.com/googleapis/gax-go" + + cinternal "cloud.google.com/go/internal" + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/internal/uid" + "cloud.google.com/go/logging" + ltesting "cloud.google.com/go/logging/internal/testing" + "cloud.google.com/go/logging/logadmin" + "golang.org/x/net/context" + "golang.org/x/oauth2" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + mrpb "google.golang.org/genproto/googleapis/api/monitoredres" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const testLogIDPrefix = "GO-LOGGING-CLIENT/TEST-LOG" + +var uids = uid.NewSpace(testLogIDPrefix, nil) + +var ( + client *logging.Client + aclient *logadmin.Client + testProjectID string + testLogID string + testFilter string + errorc chan error + ctx context.Context + + // Adjust the fields of a FullEntry received from the production service + // before comparing it with the expected result. We can't correctly + // compare certain fields, like times or server-generated IDs. + clean func(*logging.Entry) + + // Create a new client with the given project ID. + newClients func(ctx context.Context, projectID string) (*logging.Client, *logadmin.Client) +) + +func testNow() time.Time { + return time.Unix(1000, 0) +} + +// If true, this test is using the production service, not a fake. +var integrationTest bool + +func TestMain(m *testing.M) { + flag.Parse() // needed for testing.Short() + ctx = context.Background() + testProjectID = testutil.ProjID() + errorc = make(chan error, 100) + if testProjectID == "" || testing.Short() { + integrationTest = false + if testProjectID != "" { + log.Print("Integration tests skipped in short mode (using fake instead)") + } + testProjectID = "PROJECT_ID" + clean = func(e *logging.Entry) { + // Remove the insert ID for consistency with the integration test. + e.InsertID = "" + } + + addr, err := ltesting.NewServer() + if err != nil { + log.Fatalf("creating fake server: %v", err) + } + logging.SetNow(testNow) + + newClients = func(ctx context.Context, parent string) (*logging.Client, *logadmin.Client) { + conn, err := grpc.Dial(addr, grpc.WithInsecure()) + if err != nil { + log.Fatalf("dialing %q: %v", addr, err) + } + c, err := logging.NewClient(ctx, parent, option.WithGRPCConn(conn)) + if err != nil { + log.Fatalf("creating client for fake at %q: %v", addr, err) + } + ac, err := logadmin.NewClient(ctx, parent, option.WithGRPCConn(conn)) + if err != nil { + log.Fatalf("creating client for fake at %q: %v", addr, err) + } + return c, ac + } + + } else { + integrationTest = true + clean = func(e *logging.Entry) { + // We cannot compare timestamps, so set them to the test time. + // Also, remove the insert ID added by the service. + e.Timestamp = testNow().UTC() + e.InsertID = "" + } + ts := testutil.TokenSource(ctx, logging.AdminScope) + if ts == nil { + log.Fatal("The project key must be set. See CONTRIBUTING.md for details") + } + log.Printf("running integration tests with project %s", testProjectID) + newClients = func(ctx context.Context, parent string) (*logging.Client, *logadmin.Client) { + c, err := logging.NewClient(ctx, parent, option.WithTokenSource(ts)) + if err != nil { + log.Fatalf("creating prod client: %v", err) + } + ac, err := logadmin.NewClient(ctx, parent, option.WithTokenSource(ts)) + if err != nil { + log.Fatalf("creating prod client: %v", err) + } + return c, ac + } + + } + client, aclient = newClients(ctx, testProjectID) + client.OnError = func(e error) { errorc <- e } + + exit := m.Run() + os.Exit(exit) +} + +func initLogs(ctx context.Context) { + testLogID = uids.New() + testFilter = fmt.Sprintf(`logName = "projects/%s/logs/%s"`, testProjectID, + strings.Replace(testLogID, "/", "%2F", -1)) +} + +// Testing of Logger.Log is done in logadmin_test.go, TestEntries. + +func TestLogSync(t *testing.T) { + initLogs(ctx) // Generate new testLogID + ctx := context.Background() + lg := client.Logger(testLogID) + err := lg.LogSync(ctx, logging.Entry{Payload: "hello"}) + if err != nil { + t.Fatal(err) + } + err = lg.LogSync(ctx, logging.Entry{Payload: "goodbye"}) + if err != nil { + t.Fatal(err) + } + // Allow overriding the MonitoredResource. + err = lg.LogSync(ctx, logging.Entry{Payload: "mr", Resource: &mrpb.MonitoredResource{Type: "global"}}) + if err != nil { + t.Fatal(err) + } + + want := []*logging.Entry{ + entryForTesting("hello"), + entryForTesting("goodbye"), + entryForTesting("mr"), + } + var got []*logging.Entry + ok := waitFor(func() bool { + got, err = allTestLogEntries(ctx) + if err != nil { + t.Log("fetching log entries: ", err) + return false + } + return len(got) == len(want) + }) + if !ok { + t.Fatalf("timed out; got: %d, want: %d\n", len(got), len(want)) + } + if msg, ok := compareEntries(got, want); !ok { + t.Error(msg) + } +} + +func TestLogAndEntries(t *testing.T) { + initLogs(ctx) // Generate new testLogID + ctx := context.Background() + payloads := []string{"p1", "p2", "p3", "p4", "p5"} + lg := client.Logger(testLogID) + for _, p := range payloads { + // Use the insert ID to guarantee iteration order. + lg.Log(logging.Entry{Payload: p, InsertID: p}) + } + lg.Flush() + var want []*logging.Entry + for _, p := range payloads { + want = append(want, entryForTesting(p)) + } + var got []*logging.Entry + ok := waitFor(func() bool { + var err error + got, err = allTestLogEntries(ctx) + if err != nil { + t.Log("fetching log entries: ", err) + return false + } + return len(got) == len(want) + }) + if !ok { + t.Fatalf("timed out; got: %d, want: %d\n", len(got), len(want)) + } + if msg, ok := compareEntries(got, want); !ok { + t.Error(msg) + } +} + +// compareEntries compares most fields list of Entries against expected. compareEntries does not compare: +// - HTTPRequest +// - Operation +// - Resource +// - SourceLocation +func compareEntries(got, want []*logging.Entry) (string, bool) { + if len(got) != len(want) { + return fmt.Sprintf("got %d entries, want %d", len(got), len(want)), false + } + for i := range got { + if !compareEntry(got[i], want[i]) { + return fmt.Sprintf("#%d:\ngot %+v\nwant %+v", i, got[i], want[i]), false + } + } + return "", true +} + +func compareEntry(got, want *logging.Entry) bool { + if got.Timestamp.Unix() != want.Timestamp.Unix() { + return false + } + + if got.Severity != want.Severity { + return false + } + + if !ltesting.PayloadEqual(got.Payload, want.Payload) { + return false + } + if !testutil.Equal(got.Labels, want.Labels) { + return false + } + + if got.InsertID != want.InsertID { + return false + } + + if got.LogName != want.LogName { + return false + } + + return true +} + +func entryForTesting(payload interface{}) *logging.Entry { + return &logging.Entry{ + Timestamp: testNow().UTC(), + Payload: payload, + LogName: "projects/" + testProjectID + "/logs/" + testLogID, + Resource: &mrpb.MonitoredResource{Type: "global", Labels: map[string]string{"project_id": testProjectID}}, + } +} + +func countLogEntries(ctx context.Context, filter string) int { + it := aclient.Entries(ctx, logadmin.Filter(filter)) + n := 0 + for { + _, err := it.Next() + if err == iterator.Done { + return n + } + if err != nil { + log.Fatalf("counting log entries: %v", err) + } + n++ + } +} + +func allTestLogEntries(ctx context.Context) ([]*logging.Entry, error) { + return allEntries(ctx, aclient, testFilter) +} + +func allEntries(ctx context.Context, aclient *logadmin.Client, filter string) ([]*logging.Entry, error) { + var es []*logging.Entry + it := aclient.Entries(ctx, logadmin.Filter(filter)) + for { + e, err := cleanNext(it) + switch err { + case nil: + es = append(es, e) + case iterator.Done: + return es, nil + default: + return nil, err + } + } +} + +func cleanNext(it *logadmin.EntryIterator) (*logging.Entry, error) { + e, err := it.Next() + if err != nil { + return nil, err + } + clean(e) + return e, nil +} + +func TestStandardLogger(t *testing.T) { + initLogs(ctx) // Generate new testLogID + ctx := context.Background() + lg := client.Logger(testLogID) + slg := lg.StandardLogger(logging.Info) + + if slg != lg.StandardLogger(logging.Info) { + t.Error("There should be only one standard logger at each severity.") + } + if slg == lg.StandardLogger(logging.Debug) { + t.Error("There should be a different standard logger for each severity.") + } + + slg.Print("info") + lg.Flush() + var got []*logging.Entry + ok := waitFor(func() bool { + var err error + got, err = allTestLogEntries(ctx) + if err != nil { + t.Log("fetching log entries: ", err) + return false + } + return len(got) == 1 + }) + if !ok { + t.Fatalf("timed out; got: %d, want: %d\n", len(got), 1) + } + if len(got) != 1 { + t.Fatalf("expected non-nil request with one entry; got:\n%+v", got) + } + if got, want := got[0].Payload.(string), "info\n"; got != want { + t.Errorf("payload: got %q, want %q", got, want) + } + if got, want := logging.Severity(got[0].Severity), logging.Info; got != want { + t.Errorf("severity: got %s, want %s", got, want) + } +} + +func TestSeverity(t *testing.T) { + if got, want := logging.Info.String(), "Info"; got != want { + t.Errorf("got %q, want %q", got, want) + } + if got, want := logging.Severity(-99).String(), "-99"; got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestParseSeverity(t *testing.T) { + for _, test := range []struct { + in string + want logging.Severity + }{ + {"", logging.Default}, + {"whatever", logging.Default}, + {"Default", logging.Default}, + {"ERROR", logging.Error}, + {"Error", logging.Error}, + {"error", logging.Error}, + } { + got := logging.ParseSeverity(test.in) + if got != test.want { + t.Errorf("%q: got %s, want %s\n", test.in, got, test.want) + } + } +} + +func TestErrors(t *testing.T) { + initLogs(ctx) // Generate new testLogID + // Drain errors already seen. +loop: + for { + select { + case <-errorc: + default: + break loop + } + } + // Try to log something that can't be JSON-marshalled. + lg := client.Logger(testLogID) + lg.Log(logging.Entry{Payload: func() {}}) + // Expect an error from Flush. + err := lg.Flush() + if err == nil { + t.Fatal("expected error, got nil") + } +} + +type badTokenSource struct{} + +func (badTokenSource) Token() (*oauth2.Token, error) { + return &oauth2.Token{}, nil +} + +func TestPing(t *testing.T) { + // Ping twice, in case the service's InsertID logic messes with the error code. + ctx := context.Background() + // The global client should be valid. + if err := client.Ping(ctx); err != nil { + t.Errorf("project %s: got %v, expected nil", testProjectID, err) + } + if err := client.Ping(ctx); err != nil { + t.Errorf("project %s, #2: got %v, expected nil", testProjectID, err) + } + // nonexistent project + c, a := newClients(ctx, testProjectID+"-BAD") + defer c.Close() + defer a.Close() + if err := c.Ping(ctx); err == nil { + t.Errorf("nonexistent project: want error pinging logging api, got nil") + } + if err := c.Ping(ctx); err == nil { + t.Errorf("nonexistent project, #2: want error pinging logging api, got nil") + } + + // Bad creds. We cannot test this with the fake, since it doesn't do auth. + if integrationTest { + c, err := logging.NewClient(ctx, testProjectID, option.WithTokenSource(badTokenSource{})) + if err != nil { + t.Fatal(err) + } + if err := c.Ping(ctx); err == nil { + t.Errorf("bad creds: want error pinging logging api, got nil") + } + if err := c.Ping(ctx); err == nil { + t.Errorf("bad creds, #2: want error pinging logging api, got nil") + } + if err := c.Close(); err != nil { + t.Fatalf("error closing client: %v", err) + } + } +} + +func TestLogsAndDelete(t *testing.T) { + // This function tests both the Logs and DeleteLog methods. We only try to + // delete those logs that we can observe and that were generated by this + // test. This may not include the logs generated from the current test run, + // because the logging service is only eventually consistent. It's + // therefore possible that on some runs, this test will do nothing. + ctx := context.Background() + it := aclient.Logs(ctx) + nDeleted := 0 + for { + logID, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + if strings.HasPrefix(logID, testLogIDPrefix) { + if err := aclient.DeleteLog(ctx, logID); err != nil { + // Ignore NotFound. Sometimes, amazingly, DeleteLog cannot find + // a log that is returned by Logs. + if status.Code(err) != codes.NotFound { + t.Fatalf("deleting %q: %v", logID, err) + } + } else { + nDeleted++ + } + } + } + t.Logf("deleted %d logs", nDeleted) +} + +func TestNonProjectParent(t *testing.T) { + ctx := context.Background() + initLogs(ctx) + const orgID = "433637338589" // org ID for google.com + parent := "organizations/" + orgID + c, a := newClients(ctx, parent) + defer c.Close() + defer a.Close() + lg := c.Logger(testLogID) + err := lg.LogSync(ctx, logging.Entry{Payload: "hello"}) + if integrationTest { + // We don't have permission to log to the organization. + if got, want := status.Code(err), codes.PermissionDenied; got != want { + t.Errorf("got code %s, want %s", got, want) + } + return + } + // Continue test against fake. + if err != nil { + t.Fatal(err) + } + want := []*logging.Entry{{ + Timestamp: testNow().UTC(), + Payload: "hello", + LogName: parent + "/logs/" + testLogID, + Resource: &mrpb.MonitoredResource{ + Type: "organization", + Labels: map[string]string{"organization_id": orgID}, + }, + }} + var got []*logging.Entry + ok := waitFor(func() bool { + got, err = allEntries(ctx, a, fmt.Sprintf(`logName = "%s/logs/%s"`, parent, + strings.Replace(testLogID, "/", "%2F", -1))) + if err != nil { + t.Log("fetching log entries: ", err) + return false + } + return len(got) == len(want) + }) + if !ok { + t.Fatalf("timed out; got: %d, want: %d\n", len(got), len(want)) + } + if msg, ok := compareEntries(got, want); !ok { + t.Error(msg) + } +} + +// waitFor calls f repeatedly with exponential backoff, blocking until it returns true. +// It returns false after a while (if it times out). +func waitFor(f func() bool) bool { + // TODO(shadams): Find a better way to deflake these tests. + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + err := cinternal.Retry(ctx, + gax.Backoff{Initial: time.Second, Multiplier: 2}, + func() (bool, error) { return f(), nil }) + return err == nil +} + +// Interleave a lot of Log and Flush calls, to induce race conditions. +// Run this test with: +// go test -run LogFlushRace -race -count 100 +func TestLogFlushRace(t *testing.T) { + initLogs(ctx) // Generate new testLogID + lg := client.Logger(testLogID, + logging.ConcurrentWriteLimit(5), // up to 5 concurrent log writes + logging.EntryCountThreshold(100)) // small bundle size to increase interleaving + var wgf, wgl sync.WaitGroup + donec := make(chan struct{}) + for i := 0; i < 10; i++ { + wgl.Add(1) + go func() { + defer wgl.Done() + for j := 0; j < 1e4; j++ { + lg.Log(logging.Entry{Payload: "the payload"}) + } + }() + } + for i := 0; i < 5; i++ { + wgf.Add(1) + go func() { + defer wgf.Done() + for { + select { + case <-donec: + return + case <-time.After(time.Duration(rand.Intn(5)) * time.Millisecond): + lg.Flush() + } + } + }() + } + wgl.Wait() + close(donec) + wgf.Wait() +} + +// Test the throughput of concurrent writers. +// TODO(jba): when 1.8 is out, use sub-benchmarks. +func BenchmarkConcurrentWrites1(b *testing.B) { + benchmarkConcurrentWrites(b, 1) +} + +func BenchmarkConcurrentWrites2(b *testing.B) { + benchmarkConcurrentWrites(b, 2) +} + +func BenchmarkConcurrentWrites4(b *testing.B) { + benchmarkConcurrentWrites(b, 4) +} + +func BenchmarkConcurrentWrites8(b *testing.B) { + benchmarkConcurrentWrites(b, 8) +} + +func BenchmarkConcurrentWrites16(b *testing.B) { + benchmarkConcurrentWrites(b, 16) +} + +func BenchmarkConcurrentWrites32(b *testing.B) { + benchmarkConcurrentWrites(b, 32) +} + +func benchmarkConcurrentWrites(b *testing.B, c int) { + if !integrationTest { + b.Skip("only makes sense when running against production service") + } + b.StopTimer() + lg := client.Logger(testLogID, logging.ConcurrentWriteLimit(c), logging.EntryCountThreshold(1000)) + const ( + nEntries = 1e5 + payload = "the quick brown fox jumps over the lazy dog" + ) + b.SetBytes(int64(nEntries * len(payload))) + b.StartTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < nEntries; j++ { + lg.Log(logging.Entry{Payload: payload}) + } + lg.Flush() + } +} diff --git a/vendor/cloud.google.com/go/logging/logging_unexported_test.go b/vendor/cloud.google.com/go/logging/logging_unexported_test.go new file mode 100644 index 0000000000..4c8cc65119 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/logging_unexported_test.go @@ -0,0 +1,333 @@ +// 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. + +// Tests that require access to unexported names of the logging package. + +package logging + +import ( + "encoding/json" + "net/http" + "net/url" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + + "github.com/golang/protobuf/proto" + durpb "github.com/golang/protobuf/ptypes/duration" + structpb "github.com/golang/protobuf/ptypes/struct" + "google.golang.org/api/support/bundler" + mrpb "google.golang.org/genproto/googleapis/api/monitoredres" + logtypepb "google.golang.org/genproto/googleapis/logging/type" +) + +func TestLoggerCreation(t *testing.T) { + const logID = "testing" + c := &Client{parent: "projects/PROJECT_ID"} + customResource := &mrpb.MonitoredResource{ + Type: "global", + Labels: map[string]string{ + "project_id": "ANOTHER_PROJECT", + }, + } + defaultBundler := &bundler.Bundler{ + DelayThreshold: DefaultDelayThreshold, + BundleCountThreshold: DefaultEntryCountThreshold, + BundleByteThreshold: DefaultEntryByteThreshold, + BundleByteLimit: 0, + BufferedByteLimit: DefaultBufferedByteLimit, + } + for _, test := range []struct { + options []LoggerOption + wantLogger *Logger + defaultResource bool + wantBundler *bundler.Bundler + }{ + { + options: nil, + wantLogger: &Logger{}, + defaultResource: true, + wantBundler: defaultBundler, + }, + { + options: []LoggerOption{ + CommonResource(nil), + CommonLabels(map[string]string{"a": "1"}), + }, + wantLogger: &Logger{ + commonResource: nil, + commonLabels: map[string]string{"a": "1"}, + }, + wantBundler: defaultBundler, + }, + { + options: []LoggerOption{CommonResource(customResource)}, + wantLogger: &Logger{commonResource: customResource}, + wantBundler: defaultBundler, + }, + { + options: []LoggerOption{ + DelayThreshold(time.Minute), + EntryCountThreshold(99), + EntryByteThreshold(17), + EntryByteLimit(18), + BufferedByteLimit(19), + }, + wantLogger: &Logger{}, + defaultResource: true, + wantBundler: &bundler.Bundler{ + DelayThreshold: time.Minute, + BundleCountThreshold: 99, + BundleByteThreshold: 17, + BundleByteLimit: 18, + BufferedByteLimit: 19, + }, + }, + } { + gotLogger := c.Logger(logID, test.options...) + if got, want := gotLogger.commonResource, test.wantLogger.commonResource; !test.defaultResource && !proto.Equal(got, want) { + t.Errorf("%v: resource: got %v, want %v", test.options, got, want) + } + if got, want := gotLogger.commonLabels, test.wantLogger.commonLabels; !testutil.Equal(got, want) { + t.Errorf("%v: commonLabels: got %v, want %v", test.options, got, want) + } + if got, want := gotLogger.bundler.DelayThreshold, test.wantBundler.DelayThreshold; got != want { + t.Errorf("%v: DelayThreshold: got %v, want %v", test.options, got, want) + } + if got, want := gotLogger.bundler.BundleCountThreshold, test.wantBundler.BundleCountThreshold; got != want { + t.Errorf("%v: BundleCountThreshold: got %v, want %v", test.options, got, want) + } + if got, want := gotLogger.bundler.BundleByteThreshold, test.wantBundler.BundleByteThreshold; got != want { + t.Errorf("%v: BundleByteThreshold: got %v, want %v", test.options, got, want) + } + if got, want := gotLogger.bundler.BundleByteLimit, test.wantBundler.BundleByteLimit; got != want { + t.Errorf("%v: BundleByteLimit: got %v, want %v", test.options, got, want) + } + if got, want := gotLogger.bundler.BufferedByteLimit, test.wantBundler.BufferedByteLimit; got != want { + t.Errorf("%v: BufferedByteLimit: got %v, want %v", test.options, got, want) + } + } +} + +func TestToProtoStruct(t *testing.T) { + v := struct { + Foo string `json:"foo"` + Bar int `json:"bar,omitempty"` + Baz []float64 `json:"baz"` + Moo map[string]interface{} `json:"moo"` + }{ + Foo: "foovalue", + Baz: []float64{1.1}, + Moo: map[string]interface{}{ + "a": 1, + "b": "two", + "c": true, + }, + } + + got, err := toProtoStruct(v) + if err != nil { + t.Fatal(err) + } + want := &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "foo": {Kind: &structpb.Value_StringValue{StringValue: v.Foo}}, + "baz": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ + {Kind: &structpb.Value_NumberValue{NumberValue: 1.1}}, + }}}}, + "moo": {Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "a": {Kind: &structpb.Value_NumberValue{NumberValue: 1}}, + "b": {Kind: &structpb.Value_StringValue{StringValue: "two"}}, + "c": {Kind: &structpb.Value_BoolValue{BoolValue: true}}, + }, + }, + }}, + }, + } + if !proto.Equal(got, want) { + t.Errorf("got %+v\nwant %+v", got, want) + } + + // Non-structs should fail to convert. + for v := range []interface{}{3, "foo", []int{1, 2, 3}} { + _, err := toProtoStruct(v) + if err == nil { + t.Errorf("%v: got nil, want error", v) + } + } + + // Test fast path. + got, err = toProtoStruct(want) + if err != nil { + t.Fatal(err) + } + if got != want { + t.Error("got and want should be identical, but are not") + } +} + +func TestToLogEntryPayload(t *testing.T) { + for _, test := range []struct { + in interface{} + wantText string + wantStruct *structpb.Struct + }{ + { + in: "string", + wantText: "string", + }, + { + in: map[string]interface{}{"a": 1, "b": true}, + wantStruct: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "a": {Kind: &structpb.Value_NumberValue{NumberValue: 1}}, + "b": {Kind: &structpb.Value_BoolValue{BoolValue: true}}, + }, + }, + }, + { + in: json.RawMessage([]byte(`{"a": 1, "b": true}`)), + wantStruct: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "a": {Kind: &structpb.Value_NumberValue{NumberValue: 1}}, + "b": {Kind: &structpb.Value_BoolValue{BoolValue: true}}, + }, + }, + }, + } { + e, err := toLogEntry(Entry{Payload: test.in}) + if err != nil { + t.Fatalf("%+v: %v", test.in, err) + } + if test.wantStruct != nil { + got := e.GetJsonPayload() + if !proto.Equal(got, test.wantStruct) { + t.Errorf("%+v: got %s, want %s", test.in, got, test.wantStruct) + } + } else { + got := e.GetTextPayload() + if got != test.wantText { + t.Errorf("%+v: got %s, want %s", test.in, got, test.wantText) + } + } + } +} + +func TestFromHTTPRequest(t *testing.T) { + const testURL = "http:://example.com/path?q=1" + u, err := url.Parse(testURL) + if err != nil { + t.Fatal(err) + } + req := &HTTPRequest{ + Request: &http.Request{ + Method: "GET", + URL: u, + Header: map[string][]string{ + "User-Agent": {"user-agent"}, + "Referer": {"referer"}, + }, + }, + RequestSize: 100, + Status: 200, + ResponseSize: 25, + Latency: 100 * time.Second, + LocalIP: "127.0.0.1", + RemoteIP: "10.0.1.1", + CacheHit: true, + CacheValidatedWithOriginServer: true, + } + got := fromHTTPRequest(req) + want := &logtypepb.HttpRequest{ + RequestMethod: "GET", + RequestUrl: testURL, + RequestSize: 100, + Status: 200, + ResponseSize: 25, + Latency: &durpb.Duration{Seconds: 100}, + UserAgent: "user-agent", + ServerIp: "127.0.0.1", + RemoteIp: "10.0.1.1", + Referer: "referer", + CacheHit: true, + CacheValidatedWithOriginServer: true, + } + if !proto.Equal(got, want) { + t.Errorf("got %+v\nwant %+v", got, want) + } +} + +func TestMonitoredResource(t *testing.T) { + for _, test := range []struct { + parent string + want *mrpb.MonitoredResource + }{ + { + "projects/P", + &mrpb.MonitoredResource{ + Type: "project", + Labels: map[string]string{"project_id": "P"}, + }, + }, + + { + "folders/F", + &mrpb.MonitoredResource{ + Type: "folder", + Labels: map[string]string{"folder_id": "F"}, + }, + }, + { + "billingAccounts/B", + &mrpb.MonitoredResource{ + Type: "billing_account", + Labels: map[string]string{"account_id": "B"}, + }, + }, + { + "organizations/123", + &mrpb.MonitoredResource{ + Type: "organization", + Labels: map[string]string{"organization_id": "123"}, + }, + }, + { + "unknown/X", + &mrpb.MonitoredResource{ + Type: "global", + Labels: map[string]string{"project_id": "X"}, + }, + }, + { + "whatever", + &mrpb.MonitoredResource{ + Type: "global", + Labels: map[string]string{"project_id": "whatever"}, + }, + }, + } { + got := monitoredResource(test.parent) + if !testutil.Equal(got, test.want) { + t.Errorf("%q: got %+v, want %+v", test.parent, got, test.want) + } + } +} + +// Used by the tests in logging_test. +func SetNow(f func() time.Time) { + now = f +} diff --git a/vendor/cloud.google.com/go/longrunning/autogen/doc.go b/vendor/cloud.google.com/go/longrunning/autogen/doc.go new file mode 100644 index 0000000000..edb98b0a05 --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/autogen/doc.go @@ -0,0 +1,45 @@ +// 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 longrunning is an auto-generated package for the +// Google Long Running Operations API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// +// Use the client at cloud.google.com/go/longrunning in preference to this. +package longrunning // import "cloud.google.com/go/longrunning/autogen" + +import ( + "golang.org/x/net/context" + "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{} +} diff --git a/vendor/cloud.google.com/go/longrunning/autogen/from_conn.go b/vendor/cloud.google.com/go/longrunning/autogen/from_conn.go new file mode 100644 index 0000000000..9082b0a43b --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/autogen/from_conn.go @@ -0,0 +1,34 @@ +// Copyright 2017, 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 longrunning + +import ( + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" +) + +// InternalFromConn is for use by the google Cloud Libraries only. +// +// InternalFromConn creates OperationsClient from available connection. +func InternalFromConn(conn *grpc.ClientConn) *OperationsClient { + c := &OperationsClient{ + conn: conn, + CallOptions: defaultOperationsCallOptions(), + + operationsClient: longrunningpb.NewOperationsClient(conn), + } + c.SetGoogleClientInfo() + return c +} diff --git a/vendor/cloud.google.com/go/longrunning/autogen/mock_test.go b/vendor/cloud.google.com/go/longrunning/autogen/mock_test.go new file mode 100644 index 0000000000..4be9a3fe7b --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/autogen/mock_test.go @@ -0,0 +1,381 @@ +// 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 longrunning + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockOperationsServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + longrunningpb.OperationsServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockOperationsServer) ListOperations(ctx context.Context, req *longrunningpb.ListOperationsRequest) (*longrunningpb.ListOperationsResponse, 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.ListOperationsResponse), nil +} + +func (s *mockOperationsServer) GetOperation(ctx context.Context, req *longrunningpb.GetOperationRequest) (*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 *mockOperationsServer) DeleteOperation(ctx context.Context, req *longrunningpb.DeleteOperationRequest) (*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 *mockOperationsServer) CancelOperation(ctx context.Context, req *longrunningpb.CancelOperationRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockOperations mockOperationsServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + longrunningpb.RegisterOperationsServer(serv, &mockOperations) + + 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 TestOperationsGetOperation(t *testing.T) { + var name2 string = "name2-1052831874" + var done bool = true + var expectedResponse = &longrunningpb.Operation{ + Name: name2, + Done: done, + } + + mockOperations.err = nil + mockOperations.reqs = nil + + mockOperations.resps = append(mockOperations.resps[:0], expectedResponse) + + var name string = "name3373707" + var request = &longrunningpb.GetOperationRequest{ + Name: name, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetOperation(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOperations.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 TestOperationsGetOperationError(t *testing.T) { + errCode := codes.PermissionDenied + mockOperations.err = gstatus.Error(errCode, "test error") + + var name string = "name3373707" + var request = &longrunningpb.GetOperationRequest{ + Name: name, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetOperation(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 TestOperationsListOperations(t *testing.T) { + var nextPageToken string = "" + var operationsElement *longrunningpb.Operation = &longrunningpb.Operation{} + var operations = []*longrunningpb.Operation{operationsElement} + var expectedResponse = &longrunningpb.ListOperationsResponse{ + NextPageToken: nextPageToken, + Operations: operations, + } + + mockOperations.err = nil + mockOperations.reqs = nil + + mockOperations.resps = append(mockOperations.resps[:0], expectedResponse) + + var name string = "name3373707" + var filter string = "filter-1274492040" + var request = &longrunningpb.ListOperationsRequest{ + Name: name, + Filter: filter, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListOperations(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOperations.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Operations[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 TestOperationsListOperationsError(t *testing.T) { + errCode := codes.PermissionDenied + mockOperations.err = gstatus.Error(errCode, "test error") + + var name string = "name3373707" + var filter string = "filter-1274492040" + var request = &longrunningpb.ListOperationsRequest{ + Name: name, + Filter: filter, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListOperations(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 TestOperationsCancelOperation(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockOperations.err = nil + mockOperations.reqs = nil + + mockOperations.resps = append(mockOperations.resps[:0], expectedResponse) + + var name string = "name3373707" + var request = &longrunningpb.CancelOperationRequest{ + Name: name, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CancelOperation(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOperations.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestOperationsCancelOperationError(t *testing.T) { + errCode := codes.PermissionDenied + mockOperations.err = gstatus.Error(errCode, "test error") + + var name string = "name3373707" + var request = &longrunningpb.CancelOperationRequest{ + Name: name, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CancelOperation(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 TestOperationsDeleteOperation(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockOperations.err = nil + mockOperations.reqs = nil + + mockOperations.resps = append(mockOperations.resps[:0], expectedResponse) + + var name string = "name3373707" + var request = &longrunningpb.DeleteOperationRequest{ + Name: name, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteOperation(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOperations.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestOperationsDeleteOperationError(t *testing.T) { + errCode := codes.PermissionDenied + mockOperations.err = gstatus.Error(errCode, "test error") + + var name string = "name3373707" + var request = &longrunningpb.DeleteOperationRequest{ + Name: name, + } + + c, err := NewOperationsClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteOperation(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) + } +} diff --git a/vendor/cloud.google.com/go/longrunning/autogen/operations_client.go b/vendor/cloud.google.com/go/longrunning/autogen/operations_client.go new file mode 100644 index 0000000000..eeb012ecc3 --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/autogen/operations_client.go @@ -0,0 +1,272 @@ +// 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 longrunning + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// OperationsCallOptions contains the retry settings for each method of OperationsClient. +type OperationsCallOptions struct { + GetOperation []gax.CallOption + ListOperations []gax.CallOption + CancelOperation []gax.CallOption + DeleteOperation []gax.CallOption +} + +func defaultOperationsClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("longrunning.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultOperationsCallOptions() *OperationsCallOptions { + 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 &OperationsCallOptions{ + GetOperation: retry[[2]string{"default", "idempotent"}], + ListOperations: retry[[2]string{"default", "idempotent"}], + CancelOperation: retry[[2]string{"default", "idempotent"}], + DeleteOperation: retry[[2]string{"default", "idempotent"}], + } +} + +// OperationsClient is a client for interacting with Google Long Running Operations API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type OperationsClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + operationsClient longrunningpb.OperationsClient + + // The call options for this service. + CallOptions *OperationsCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewOperationsClient creates a new operations client. +// +// Manages long-running operations with an API service. +// +// When an API method normally takes long time to complete, it can be designed +// to return [Operation][google.longrunning.Operation] to the client, and the client can use this +// interface to receive the real response asynchronously by polling the +// operation resource, or pass the operation resource to another API (such as +// Google Cloud Pub/Sub API) to receive the response. Any API service that +// returns long-running operations should implement the Operations interface +// so developers can have a consistent client experience. +func NewOperationsClient(ctx context.Context, opts ...option.ClientOption) (*OperationsClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultOperationsClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &OperationsClient{ + conn: conn, + CallOptions: defaultOperationsCallOptions(), + + operationsClient: longrunningpb.NewOperationsClient(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *OperationsClient) 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 *OperationsClient) 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 *OperationsClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// GetOperation gets the latest state of a long-running operation. Clients can use this +// method to poll the operation result at intervals as recommended by the API +// service. +func (c *OperationsClient) GetOperation(ctx context.Context, req *longrunningpb.GetOperationRequest, opts ...gax.CallOption) (*longrunningpb.Operation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetOperation[0:len(c.CallOptions.GetOperation):len(c.CallOptions.GetOperation)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.operationsClient.GetOperation(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListOperations lists operations that match the specified filter in the request. If the +// server doesn't support this method, it returns UNIMPLEMENTED. +// +// NOTE: the name binding below allows API services to override the binding +// to use different resource name schemes, such as users/*/operations. +func (c *OperationsClient) ListOperations(ctx context.Context, req *longrunningpb.ListOperationsRequest, opts ...gax.CallOption) *OperationIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListOperations[0:len(c.CallOptions.ListOperations):len(c.CallOptions.ListOperations)], opts...) + it := &OperationIterator{} + req = proto.Clone(req).(*longrunningpb.ListOperationsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*longrunningpb.Operation, string, error) { + var resp *longrunningpb.ListOperationsResponse + 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.operationsClient.ListOperations(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Operations, 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 +} + +// CancelOperation starts asynchronous cancellation on a long-running operation. The server +// makes a best effort to cancel the operation, but success is not +// guaranteed. If the server doesn't support this method, it returns +// google.rpc.Code.UNIMPLEMENTED. Clients can use +// [Operations.GetOperation][google.longrunning.Operations.GetOperation] or +// other methods to check whether the cancellation succeeded or whether the +// operation completed despite cancellation. On successful cancellation, +// the operation is not deleted; instead, it becomes an operation with +// an [Operation.error][google.longrunning.Operation.error] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, +// corresponding to Code.CANCELLED. +func (c *OperationsClient) CancelOperation(ctx context.Context, req *longrunningpb.CancelOperationRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CancelOperation[0:len(c.CallOptions.CancelOperation):len(c.CallOptions.CancelOperation)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.operationsClient.CancelOperation(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// DeleteOperation deletes a long-running operation. This method indicates that the client is +// no longer interested in the operation result. It does not cancel the +// operation. If the server doesn't support this method, it returns +// google.rpc.Code.UNIMPLEMENTED. +func (c *OperationsClient) DeleteOperation(ctx context.Context, req *longrunningpb.DeleteOperationRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteOperation[0:len(c.CallOptions.DeleteOperation):len(c.CallOptions.DeleteOperation)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.operationsClient.DeleteOperation(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// OperationIterator manages a stream of *longrunningpb.Operation. +type OperationIterator struct { + items []*longrunningpb.Operation + 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 []*longrunningpb.Operation, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *OperationIterator) 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 *OperationIterator) Next() (*longrunningpb.Operation, error) { + var item *longrunningpb.Operation + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *OperationIterator) bufLen() int { + return len(it.items) +} + +func (it *OperationIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/longrunning/autogen/operations_client_example_test.go b/vendor/cloud.google.com/go/longrunning/autogen/operations_client_example_test.go new file mode 100644 index 0000000000..7bde78b334 --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/autogen/operations_client_example_test.go @@ -0,0 +1,108 @@ +// 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 longrunning_test + +import ( + "cloud.google.com/go/longrunning/autogen" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +func ExampleNewOperationsClient() { + ctx := context.Background() + c, err := longrunning.NewOperationsClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleOperationsClient_GetOperation() { + ctx := context.Background() + c, err := longrunning.NewOperationsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &longrunningpb.GetOperationRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetOperation(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleOperationsClient_ListOperations() { + ctx := context.Background() + c, err := longrunning.NewOperationsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &longrunningpb.ListOperationsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListOperations(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleOperationsClient_CancelOperation() { + ctx := context.Background() + c, err := longrunning.NewOperationsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &longrunningpb.CancelOperationRequest{ + // TODO: Fill request struct fields. + } + err = c.CancelOperation(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleOperationsClient_DeleteOperation() { + ctx := context.Background() + c, err := longrunning.NewOperationsClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &longrunningpb.DeleteOperationRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteOperation(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/longrunning/example_test.go b/vendor/cloud.google.com/go/longrunning/example_test.go new file mode 100644 index 0000000000..d81b1d3393 --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/example_test.go @@ -0,0 +1,116 @@ +// 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 longrunning + +import ( + "fmt" + "time" + + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/duration" + "github.com/golang/protobuf/ptypes/timestamp" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/longrunning" +) + +func bestMomentInHistory() (*Operation, error) { + t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2009-11-10 23:00:00 +0000 UTC") + if err != nil { + return nil, err + } + resp, err := ptypes.TimestampProto(t) + if err != nil { + return nil, err + } + respAny, err := ptypes.MarshalAny(resp) + if err != nil { + return nil, err + } + metaAny, err := ptypes.MarshalAny(ptypes.DurationProto(1 * time.Hour)) + return &Operation{ + proto: &pb.Operation{ + Name: "best-moment", + Done: true, + Metadata: metaAny, + Result: &pb.Operation_Response{ + Response: respAny, + }, + }, + }, err +} + +func ExampleOperation_Wait() { + // Complex computation, might take a long time. + op, err := bestMomentInHistory() + if err != nil { + // TODO: Handle err. + } + var ts timestamp.Timestamp + err = op.Wait(context.TODO(), &ts) + if err != nil && !op.Done() { + fmt.Println("failed to fetch operation status", err) + } else if err != nil && op.Done() { + fmt.Println("operation completed with error", err) + } else { + fmt.Println(ptypes.TimestampString(&ts)) + } + // Output: + // 2009-11-10T23:00:00Z +} + +func ExampleOperation_Metadata() { + op, err := bestMomentInHistory() + if err != nil { + // TODO: Handle err. + } + + // The operation might contain metadata. + // In this example, the metadata contains the estimated length of time + // the operation might take to complete. + var meta duration.Duration + if err := op.Metadata(&meta); err != nil { + // TODO: Handle err. + } + d, err := ptypes.Duration(&meta) + if err == ErrNoMetadata { + fmt.Println("no metadata") + } else if err != nil { + // TODO: Handle err. + } else { + fmt.Println(d) + } + // Output: + // 1h0m0s +} + +func ExampleOperation_Cancel() { + op, err := bestMomentInHistory() + if err != nil { + // TODO: Handle err. + } + if err := op.Cancel(context.Background()); err != nil { + // TODO: Handle err. + } +} + +func ExampleOperation_Delete() { + op, err := bestMomentInHistory() + if err != nil { + // TODO: Handle err. + } + if err := op.Delete(context.Background()); err != nil { + // TODO: Handle err. + } +} diff --git a/vendor/cloud.google.com/go/longrunning/longrunning.go b/vendor/cloud.google.com/go/longrunning/longrunning.go new file mode 100644 index 0000000000..ff6e520a7c --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/longrunning.go @@ -0,0 +1,181 @@ +// 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 longrunning supports Long Running Operations for the Google Cloud Libraries. +// See google.golang.org/genproto/googleapis/longrunning for its service definition. +// +// Users of the Google Cloud Libraries will typically not use this package directly. +// Instead they will call functions returning Operations and call their methods. +// +// This package is still experimental and subject to change. +package longrunning // import "cloud.google.com/go/longrunning" + +import ( + "errors" + "fmt" + "time" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/googleapis/gax-go" + "google.golang.org/grpc/status" + + "golang.org/x/net/context" + + autogen "cloud.google.com/go/longrunning/autogen" + pb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc/codes" +) + +// ErrNoMetadata is the error returned by Metadata if the operation contains no metadata. +var ErrNoMetadata = errors.New("operation contains no metadata") + +// Operation represents the result of an API call that may not be ready yet. +type Operation struct { + c operationsClient + proto *pb.Operation +} + +type operationsClient interface { + GetOperation(context.Context, *pb.GetOperationRequest, ...gax.CallOption) (*pb.Operation, error) + CancelOperation(context.Context, *pb.CancelOperationRequest, ...gax.CallOption) error + DeleteOperation(context.Context, *pb.DeleteOperationRequest, ...gax.CallOption) error +} + +// InternalNewOperation is for use by the google Cloud Libraries only. +// +// InternalNewOperation returns an long-running operation, abstracting the raw pb.Operation. +// The conn parameter refers to a server that proto was received from. +func InternalNewOperation(inner *autogen.OperationsClient, proto *pb.Operation) *Operation { + return &Operation{ + c: inner, + proto: proto, + } +} + +// 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 *Operation) Name() string { + return op.proto.Name +} + +// Done reports whether the long-running operation has completed. +func (op *Operation) Done() bool { + return op.proto.Done +} + +// Metadata unmarshals op's metadata into meta. +// If op does not contain any metadata, Metadata returns ErrNoMetadata and meta is unmodified. +func (op *Operation) Metadata(meta proto.Message) error { + if m := op.proto.Metadata; m != nil { + return ptypes.UnmarshalAny(m, meta) + } + return ErrNoMetadata +} + +// Poll fetches the latest state of a long-running operation. +// +// 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; if resp != nil, the response of the operation +// is stored in resp. +func (op *Operation) Poll(ctx context.Context, resp proto.Message, opts ...gax.CallOption) error { + if !op.Done() { + p, err := op.c.GetOperation(ctx, &pb.GetOperationRequest{Name: op.Name()}, opts...) + if err != nil { + return err + } + op.proto = p + } + if !op.Done() { + return nil + } + + switch r := op.proto.Result.(type) { + case *pb.Operation_Error: + // TODO (pongad): r.Details may contain further information + return status.Errorf(codes.Code(r.Error.Code), "%s", r.Error.Message) + case *pb.Operation_Response: + if resp == nil { + return nil + } + return ptypes.UnmarshalAny(r.Response, resp) + default: + return fmt.Errorf("unsupported result type %[1]T: %[1]v", r) + } +} + +// DefaultWaitInterval is the polling interval used by Operation.Wait. +const DefaultWaitInterval = 60 * time.Second + +// Wait is equivalent to WaitWithInterval using DefaultWaitInterval. +func (op *Operation) Wait(ctx context.Context, resp proto.Message, opts ...gax.CallOption) error { + return op.WaitWithInterval(ctx, resp, DefaultWaitInterval, opts...) +} + +// WaitWithInterval blocks until the operation is completed. +// If resp != nil, Wait stores the response in resp. +// WaitWithInterval polls every interval, except initially +// when it polls using exponential backoff. +// +// See documentation of Poll for error-handling information. +func (op *Operation) WaitWithInterval(ctx context.Context, resp proto.Message, interval time.Duration, opts ...gax.CallOption) error { + bo := gax.Backoff{ + Initial: 1 * time.Second, + Max: interval, + } + if bo.Max < bo.Initial { + bo.Max = bo.Initial + } + return op.wait(ctx, resp, &bo, gax.Sleep, opts...) +} + +type sleeper func(context.Context, time.Duration) error + +// wait implements Wait, taking exponentialBackoff and sleeper arguments for testing. +func (op *Operation) wait(ctx context.Context, resp proto.Message, bo *gax.Backoff, sl sleeper, opts ...gax.CallOption) error { + for { + if err := op.Poll(ctx, resp, opts...); err != nil { + return err + } + if op.Done() { + return nil + } + if err := sl(ctx, bo.Pause()); err != nil { + return err + } + } +} + +// Cancel starts asynchronous cancellation on a long-running operation. The server +// makes a best effort to cancel the operation, but success is not +// guaranteed. If the server doesn't support this method, it returns +// grpc.Code(error) == codes.Unimplemented. Clients can use +// Poll or other methods to check whether the cancellation succeeded or whether the +// operation completed despite cancellation. On successful cancellation, +// the operation is not deleted; instead, op.Poll returns an error +// with code Canceled. +func (op *Operation) Cancel(ctx context.Context, opts ...gax.CallOption) error { + return op.c.CancelOperation(ctx, &pb.CancelOperationRequest{Name: op.Name()}, opts...) +} + +// Delete deletes a long-running operation. This method indicates that the client is +// no longer interested in the operation result. It does not cancel the +// operation. If the server doesn't support this method, grpc.Code(error) == codes.Unimplemented. +func (op *Operation) Delete(ctx context.Context, opts ...gax.CallOption) error { + return op.c.DeleteOperation(ctx, &pb.DeleteOperationRequest{Name: op.Name()}, opts...) +} diff --git a/vendor/cloud.google.com/go/longrunning/longrunning_test.go b/vendor/cloud.google.com/go/longrunning/longrunning_test.go new file mode 100644 index 0000000000..eeaa995b7d --- /dev/null +++ b/vendor/cloud.google.com/go/longrunning/longrunning_test.go @@ -0,0 +1,215 @@ +// 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 lro supports Long Running Operations for the Google Cloud Libraries. +// +// This package is still experimental and subject to change. +package longrunning + +import ( + "errors" + "testing" + "time" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/duration" + gax "github.com/googleapis/gax-go" + + "golang.org/x/net/context" + + pb "google.golang.org/genproto/googleapis/longrunning" + status "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +type getterService struct { + operationsClient + + // clock represents the fake current time of the service. + // It is the running sum of the of the duration we have slept. + clock time.Duration + + // getTimes records the the times at which GetOperation is called. + getTimes []time.Duration + + // results are the fake results that GetOperation should return. + results []*pb.Operation +} + +func (s *getterService) GetOperation(context.Context, *pb.GetOperationRequest, ...gax.CallOption) (*pb.Operation, error) { + i := len(s.getTimes) + s.getTimes = append(s.getTimes, s.clock) + if i >= len(s.results) { + return nil, errors.New("unexpected call") + } + return s.results[i], nil +} + +func (s *getterService) sleeper() sleeper { + return func(_ context.Context, d time.Duration) error { + s.clock += d + return nil + } +} + +func TestWait(t *testing.T) { + responseDur := ptypes.DurationProto(42 * time.Second) + responseAny, err := ptypes.MarshalAny(responseDur) + if err != nil { + t.Fatal(err) + } + + s := &getterService{ + results: []*pb.Operation{ + {Name: "foo"}, + {Name: "foo"}, + {Name: "foo"}, + {Name: "foo"}, + {Name: "foo"}, + { + Name: "foo", + Done: true, + Result: &pb.Operation_Response{ + Response: responseAny, + }, + }, + }, + } + op := &Operation{ + c: s, + proto: &pb.Operation{Name: "foo"}, + } + if op.Done() { + t.Fatal("operation should not have completed yet") + } + + var resp duration.Duration + bo := gax.Backoff{ + Initial: 1 * time.Second, + Max: 3 * time.Second, + } + if err := op.wait(context.Background(), &resp, &bo, s.sleeper()); err != nil { + t.Fatal(err) + } + if !proto.Equal(&resp, responseDur) { + t.Errorf("response, got %v, want %v", resp, responseDur) + } + if !op.Done() { + t.Errorf("operation should have completed") + } + + maxWait := []time.Duration{ + 1 * time.Second, + 2 * time.Second, + 3 * time.Second, + 3 * time.Second, + 3 * time.Second, + } + for i := 0; i < len(s.getTimes)-1; i++ { + w := s.getTimes[i+1] - s.getTimes[i] + if mw := maxWait[i]; w > mw { + t.Errorf("backoff, waited %s, max %s", w, mw) + } + } +} + +func TestPollRequestError(t *testing.T) { + const opName = "foo" + + // All calls error. + s := &getterService{} + op := &Operation{ + c: s, + proto: &pb.Operation{Name: opName}, + } + if err := op.Poll(context.Background(), nil); err == nil { + t.Fatalf("Poll should error") + } + if n := op.Name(); n != opName { + t.Errorf("operation name, got %q, want %q", n, opName) + } + if op.Done() { + t.Errorf("operation should not have completed; we failed to fetch state") + } +} + +func TestPollErrorResult(t *testing.T) { + const ( + errCode = codes.NotFound + errMsg = "my error" + ) + op := &Operation{ + proto: &pb.Operation{ + Name: "foo", + Done: true, + Result: &pb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: errMsg, + }, + }, + }, + } + err := op.Poll(context.Background(), nil) + if got := grpc.Code(err); got != errCode { + t.Errorf("error code, want %s, got %s", errCode, got) + } + if got := grpc.ErrorDesc(err); got != errMsg { + t.Errorf("error code, want %s, got %s", errMsg, got) + } + if !op.Done() { + t.Errorf("operation should have completed") + } +} + +type errService struct { + operationsClient + errCancel, errDelete error +} + +func (s *errService) CancelOperation(context.Context, *pb.CancelOperationRequest, ...gax.CallOption) error { + return s.errCancel +} + +func (s *errService) DeleteOperation(context.Context, *pb.DeleteOperationRequest, ...gax.CallOption) error { + return s.errDelete +} + +func TestCancelReturnsError(t *testing.T) { + s := &errService{ + errCancel: errors.New("cancel error"), + } + op := &Operation{ + c: s, + proto: &pb.Operation{Name: "foo"}, + } + if got, want := op.Cancel(context.Background()), s.errCancel; got != want { + t.Errorf("cancel, got error %s, want %s", got, want) + } +} + +func TestDeleteReturnsError(t *testing.T) { + s := &errService{ + errDelete: errors.New("delete error"), + } + op := &Operation{ + c: s, + proto: &pb.Operation{Name: "foo"}, + } + if got, want := op.Delete(context.Background()), s.errDelete; got != want { + t.Errorf("cancel, got error %s, want %s", got, want) + } +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/ListMonitoredResourceDescriptors_smoke_test.go b/vendor/cloud.google.com/go/monitoring/apiv3/ListMonitoredResourceDescriptors_smoke_test.go new file mode 100644 index 0000000000..e0cda4a418 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/ListMonitoredResourceDescriptors_smoke_test.go @@ -0,0 +1,67 @@ +// 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 monitoring + +import ( + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestMetricServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewMetricClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var formattedName string = fmt.Sprintf("projects/%s", projectId) + var request = &monitoringpb.ListMonitoredResourceDescriptorsRequest{ + Name: formattedName, + } + + iter := c.ListMonitoredResourceDescriptors(ctx, request) + if _, err := iter.Next(); err != nil && err != iterator.Done { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/alert_policy_client.go b/vendor/cloud.google.com/go/monitoring/apiv3/alert_policy_client.go new file mode 100644 index 0000000000..f0724b4c4d --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/alert_policy_client.go @@ -0,0 +1,279 @@ +// 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 monitoring + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// AlertPolicyCallOptions contains the retry settings for each method of AlertPolicyClient. +type AlertPolicyCallOptions struct { + ListAlertPolicies []gax.CallOption + GetAlertPolicy []gax.CallOption + CreateAlertPolicy []gax.CallOption + DeleteAlertPolicy []gax.CallOption + UpdateAlertPolicy []gax.CallOption +} + +func defaultAlertPolicyClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("monitoring.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultAlertPolicyCallOptions() *AlertPolicyCallOptions { + 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 &AlertPolicyCallOptions{ + ListAlertPolicies: retry[[2]string{"default", "idempotent"}], + GetAlertPolicy: retry[[2]string{"default", "idempotent"}], + CreateAlertPolicy: retry[[2]string{"default", "non_idempotent"}], + DeleteAlertPolicy: retry[[2]string{"default", "idempotent"}], + UpdateAlertPolicy: retry[[2]string{"default", "non_idempotent"}], + } +} + +// AlertPolicyClient is a client for interacting with Stackdriver Monitoring API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type AlertPolicyClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + alertPolicyClient monitoringpb.AlertPolicyServiceClient + + // The call options for this service. + CallOptions *AlertPolicyCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewAlertPolicyClient creates a new alert policy service client. +// +// The AlertPolicyService API is used to manage (list, create, delete, +// edit) alert policies in Stackdriver Monitoring. An alerting policy is +// a description of the conditions under which some aspect of your +// system is considered to be "unhealthy" and the ways to notify +// people or services about this state. In addition to using this API, alert +// policies can also be managed through +// Stackdriver Monitoring (at https://cloud.google.com/monitoring/docs/), +// which can be reached by clicking the "Monitoring" tab in +// Cloud Console (at https://console.cloud.google.com/). +func NewAlertPolicyClient(ctx context.Context, opts ...option.ClientOption) (*AlertPolicyClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultAlertPolicyClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &AlertPolicyClient{ + conn: conn, + CallOptions: defaultAlertPolicyCallOptions(), + + alertPolicyClient: monitoringpb.NewAlertPolicyServiceClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *AlertPolicyClient) 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 *AlertPolicyClient) 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 *AlertPolicyClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListAlertPolicies lists the existing alerting policies for the project. +func (c *AlertPolicyClient) ListAlertPolicies(ctx context.Context, req *monitoringpb.ListAlertPoliciesRequest, opts ...gax.CallOption) *AlertPolicyIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListAlertPolicies[0:len(c.CallOptions.ListAlertPolicies):len(c.CallOptions.ListAlertPolicies)], opts...) + it := &AlertPolicyIterator{} + req = proto.Clone(req).(*monitoringpb.ListAlertPoliciesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoringpb.AlertPolicy, string, error) { + var resp *monitoringpb.ListAlertPoliciesResponse + 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.alertPolicyClient.ListAlertPolicies(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.AlertPolicies, 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 +} + +// GetAlertPolicy gets a single alerting policy. +func (c *AlertPolicyClient) GetAlertPolicy(ctx context.Context, req *monitoringpb.GetAlertPolicyRequest, opts ...gax.CallOption) (*monitoringpb.AlertPolicy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetAlertPolicy[0:len(c.CallOptions.GetAlertPolicy):len(c.CallOptions.GetAlertPolicy)], opts...) + var resp *monitoringpb.AlertPolicy + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.alertPolicyClient.GetAlertPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateAlertPolicy creates a new alerting policy. +func (c *AlertPolicyClient) CreateAlertPolicy(ctx context.Context, req *monitoringpb.CreateAlertPolicyRequest, opts ...gax.CallOption) (*monitoringpb.AlertPolicy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateAlertPolicy[0:len(c.CallOptions.CreateAlertPolicy):len(c.CallOptions.CreateAlertPolicy)], opts...) + var resp *monitoringpb.AlertPolicy + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.alertPolicyClient.CreateAlertPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteAlertPolicy deletes an alerting policy. +func (c *AlertPolicyClient) DeleteAlertPolicy(ctx context.Context, req *monitoringpb.DeleteAlertPolicyRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteAlertPolicy[0:len(c.CallOptions.DeleteAlertPolicy):len(c.CallOptions.DeleteAlertPolicy)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.alertPolicyClient.DeleteAlertPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// UpdateAlertPolicy updates an alerting policy. You can either replace the entire policy with +// a new one or replace only certain fields in the current alerting policy by +// specifying the fields to be updated via updateMask. Returns the +// updated alerting policy. +func (c *AlertPolicyClient) UpdateAlertPolicy(ctx context.Context, req *monitoringpb.UpdateAlertPolicyRequest, opts ...gax.CallOption) (*monitoringpb.AlertPolicy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateAlertPolicy[0:len(c.CallOptions.UpdateAlertPolicy):len(c.CallOptions.UpdateAlertPolicy)], opts...) + var resp *monitoringpb.AlertPolicy + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.alertPolicyClient.UpdateAlertPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AlertPolicyIterator manages a stream of *monitoringpb.AlertPolicy. +type AlertPolicyIterator struct { + items []*monitoringpb.AlertPolicy + 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 []*monitoringpb.AlertPolicy, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *AlertPolicyIterator) 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 *AlertPolicyIterator) Next() (*monitoringpb.AlertPolicy, error) { + var item *monitoringpb.AlertPolicy + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *AlertPolicyIterator) bufLen() int { + return len(it.items) +} + +func (it *AlertPolicyIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/alert_policy_client_example_test.go b/vendor/cloud.google.com/go/monitoring/apiv3/alert_policy_client_example_test.go new file mode 100644 index 0000000000..4ddc33a78b --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/alert_policy_client_example_test.go @@ -0,0 +1,128 @@ +// 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 monitoring_test + +import ( + "cloud.google.com/go/monitoring/apiv3" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +func ExampleNewAlertPolicyClient() { + ctx := context.Background() + c, err := monitoring.NewAlertPolicyClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleAlertPolicyClient_ListAlertPolicies() { + ctx := context.Background() + c, err := monitoring.NewAlertPolicyClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListAlertPoliciesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListAlertPolicies(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleAlertPolicyClient_GetAlertPolicy() { + ctx := context.Background() + c, err := monitoring.NewAlertPolicyClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.GetAlertPolicyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetAlertPolicy(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleAlertPolicyClient_CreateAlertPolicy() { + ctx := context.Background() + c, err := monitoring.NewAlertPolicyClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.CreateAlertPolicyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateAlertPolicy(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleAlertPolicyClient_DeleteAlertPolicy() { + ctx := context.Background() + c, err := monitoring.NewAlertPolicyClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.DeleteAlertPolicyRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteAlertPolicy(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleAlertPolicyClient_UpdateAlertPolicy() { + ctx := context.Background() + c, err := monitoring.NewAlertPolicyClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.UpdateAlertPolicyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateAlertPolicy(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/doc.go b/vendor/cloud.google.com/go/monitoring/apiv3/doc.go new file mode 100644 index 0000000000..dee265c605 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/doc.go @@ -0,0 +1,51 @@ +// 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 monitoring is an auto-generated package for the +// Stackdriver Monitoring API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Manages your Stackdriver Monitoring data and configurations. Most projects +// must be associated with a Stackdriver account, with a few exceptions as +// noted on the individual method pages. +package monitoring // import "cloud.google.com/go/monitoring/apiv3" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/monitoring", + "https://www.googleapis.com/auth/monitoring.read", + "https://www.googleapis.com/auth/monitoring.write", + } +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/group_client.go b/vendor/cloud.google.com/go/monitoring/apiv3/group_client.go new file mode 100644 index 0000000000..0725ecfb99 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/group_client.go @@ -0,0 +1,362 @@ +// 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 monitoring + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// GroupCallOptions contains the retry settings for each method of GroupClient. +type GroupCallOptions struct { + ListGroups []gax.CallOption + GetGroup []gax.CallOption + CreateGroup []gax.CallOption + UpdateGroup []gax.CallOption + DeleteGroup []gax.CallOption + ListGroupMembers []gax.CallOption +} + +func defaultGroupClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("monitoring.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultGroupCallOptions() *GroupCallOptions { + 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 &GroupCallOptions{ + ListGroups: retry[[2]string{"default", "idempotent"}], + GetGroup: retry[[2]string{"default", "idempotent"}], + CreateGroup: retry[[2]string{"default", "non_idempotent"}], + UpdateGroup: retry[[2]string{"default", "idempotent"}], + DeleteGroup: retry[[2]string{"default", "idempotent"}], + ListGroupMembers: retry[[2]string{"default", "idempotent"}], + } +} + +// GroupClient is a client for interacting with Stackdriver Monitoring API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type GroupClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + groupClient monitoringpb.GroupServiceClient + + // The call options for this service. + CallOptions *GroupCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewGroupClient creates a new group service client. +// +// The Group API lets you inspect and manage your +// groups (at google.monitoring.v3.Group). +// +// A group is a named filter that is used to identify +// a collection of monitored resources. Groups are typically used to +// mirror the physical and/or logical topology of the environment. +// Because group membership is computed dynamically, monitored +// resources that are started in the future are automatically placed +// in matching groups. By using a group to name monitored resources in, +// for example, an alert policy, the target of that alert policy is +// updated automatically as monitored resources are added and removed +// from the infrastructure. +func NewGroupClient(ctx context.Context, opts ...option.ClientOption) (*GroupClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultGroupClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &GroupClient{ + conn: conn, + CallOptions: defaultGroupCallOptions(), + + groupClient: monitoringpb.NewGroupServiceClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *GroupClient) 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 *GroupClient) 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 *GroupClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListGroups lists the existing groups. +func (c *GroupClient) ListGroups(ctx context.Context, req *monitoringpb.ListGroupsRequest, opts ...gax.CallOption) *GroupIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListGroups[0:len(c.CallOptions.ListGroups):len(c.CallOptions.ListGroups)], opts...) + it := &GroupIterator{} + req = proto.Clone(req).(*monitoringpb.ListGroupsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoringpb.Group, string, error) { + var resp *monitoringpb.ListGroupsResponse + 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.groupClient.ListGroups(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Group, 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 +} + +// GetGroup gets a single group. +func (c *GroupClient) GetGroup(ctx context.Context, req *monitoringpb.GetGroupRequest, opts ...gax.CallOption) (*monitoringpb.Group, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetGroup[0:len(c.CallOptions.GetGroup):len(c.CallOptions.GetGroup)], opts...) + var resp *monitoringpb.Group + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.groupClient.GetGroup(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateGroup creates a new group. +func (c *GroupClient) CreateGroup(ctx context.Context, req *monitoringpb.CreateGroupRequest, opts ...gax.CallOption) (*monitoringpb.Group, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateGroup[0:len(c.CallOptions.CreateGroup):len(c.CallOptions.CreateGroup)], opts...) + var resp *monitoringpb.Group + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.groupClient.CreateGroup(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateGroup updates an existing group. +// You can change any group attributes except name. +func (c *GroupClient) UpdateGroup(ctx context.Context, req *monitoringpb.UpdateGroupRequest, opts ...gax.CallOption) (*monitoringpb.Group, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateGroup[0:len(c.CallOptions.UpdateGroup):len(c.CallOptions.UpdateGroup)], opts...) + var resp *monitoringpb.Group + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.groupClient.UpdateGroup(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteGroup deletes an existing group. +func (c *GroupClient) DeleteGroup(ctx context.Context, req *monitoringpb.DeleteGroupRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteGroup[0:len(c.CallOptions.DeleteGroup):len(c.CallOptions.DeleteGroup)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.groupClient.DeleteGroup(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListGroupMembers lists the monitored resources that are members of a group. +func (c *GroupClient) ListGroupMembers(ctx context.Context, req *monitoringpb.ListGroupMembersRequest, opts ...gax.CallOption) *MonitoredResourceIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListGroupMembers[0:len(c.CallOptions.ListGroupMembers):len(c.CallOptions.ListGroupMembers)], opts...) + it := &MonitoredResourceIterator{} + req = proto.Clone(req).(*monitoringpb.ListGroupMembersRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoredrespb.MonitoredResource, string, error) { + var resp *monitoringpb.ListGroupMembersResponse + 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.groupClient.ListGroupMembers(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Members, 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 +} + +// GroupIterator manages a stream of *monitoringpb.Group. +type GroupIterator struct { + items []*monitoringpb.Group + 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 []*monitoringpb.Group, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *GroupIterator) 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 *GroupIterator) Next() (*monitoringpb.Group, error) { + var item *monitoringpb.Group + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *GroupIterator) bufLen() int { + return len(it.items) +} + +func (it *GroupIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// MonitoredResourceIterator manages a stream of *monitoredrespb.MonitoredResource. +type MonitoredResourceIterator struct { + items []*monitoredrespb.MonitoredResource + 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 []*monitoredrespb.MonitoredResource, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *MonitoredResourceIterator) 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 *MonitoredResourceIterator) Next() (*monitoredrespb.MonitoredResource, error) { + var item *monitoredrespb.MonitoredResource + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *MonitoredResourceIterator) bufLen() int { + return len(it.items) +} + +func (it *MonitoredResourceIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/group_client_example_test.go b/vendor/cloud.google.com/go/monitoring/apiv3/group_client_example_test.go new file mode 100644 index 0000000000..eaf8eeeb52 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/group_client_example_test.go @@ -0,0 +1,152 @@ +// 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 monitoring_test + +import ( + "cloud.google.com/go/monitoring/apiv3" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +func ExampleNewGroupClient() { + ctx := context.Background() + c, err := monitoring.NewGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleGroupClient_ListGroups() { + ctx := context.Background() + c, err := monitoring.NewGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListGroupsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListGroups(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleGroupClient_GetGroup() { + ctx := context.Background() + c, err := monitoring.NewGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.GetGroupRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetGroup(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleGroupClient_CreateGroup() { + ctx := context.Background() + c, err := monitoring.NewGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.CreateGroupRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateGroup(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleGroupClient_UpdateGroup() { + ctx := context.Background() + c, err := monitoring.NewGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.UpdateGroupRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateGroup(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleGroupClient_DeleteGroup() { + ctx := context.Background() + c, err := monitoring.NewGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.DeleteGroupRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteGroup(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleGroupClient_ListGroupMembers() { + ctx := context.Background() + c, err := monitoring.NewGroupClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListGroupMembersRequest{ + // TODO: Fill request struct fields. + } + it := c.ListGroupMembers(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/metric_client.go b/vendor/cloud.google.com/go/monitoring/apiv3/metric_client.go new file mode 100644 index 0000000000..7234d6dd40 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/metric_client.go @@ -0,0 +1,453 @@ +// 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 monitoring + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + metricpb "google.golang.org/genproto/googleapis/api/metric" + monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// MetricCallOptions contains the retry settings for each method of MetricClient. +type MetricCallOptions struct { + ListMonitoredResourceDescriptors []gax.CallOption + GetMonitoredResourceDescriptor []gax.CallOption + ListMetricDescriptors []gax.CallOption + GetMetricDescriptor []gax.CallOption + CreateMetricDescriptor []gax.CallOption + DeleteMetricDescriptor []gax.CallOption + ListTimeSeries []gax.CallOption + CreateTimeSeries []gax.CallOption +} + +func defaultMetricClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("monitoring.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultMetricCallOptions() *MetricCallOptions { + 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 &MetricCallOptions{ + ListMonitoredResourceDescriptors: retry[[2]string{"default", "idempotent"}], + GetMonitoredResourceDescriptor: retry[[2]string{"default", "idempotent"}], + ListMetricDescriptors: retry[[2]string{"default", "idempotent"}], + GetMetricDescriptor: retry[[2]string{"default", "idempotent"}], + CreateMetricDescriptor: retry[[2]string{"default", "non_idempotent"}], + DeleteMetricDescriptor: retry[[2]string{"default", "idempotent"}], + ListTimeSeries: retry[[2]string{"default", "idempotent"}], + CreateTimeSeries: retry[[2]string{"default", "non_idempotent"}], + } +} + +// MetricClient is a client for interacting with Stackdriver Monitoring API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type MetricClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + metricClient monitoringpb.MetricServiceClient + + // The call options for this service. + CallOptions *MetricCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewMetricClient creates a new metric service client. +// +// Manages metric descriptors, monitored resource descriptors, and +// time series data. +func NewMetricClient(ctx context.Context, opts ...option.ClientOption) (*MetricClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultMetricClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &MetricClient{ + conn: conn, + CallOptions: defaultMetricCallOptions(), + + metricClient: monitoringpb.NewMetricServiceClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *MetricClient) 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 *MetricClient) 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 *MetricClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListMonitoredResourceDescriptors lists monitored resource descriptors that match a filter. This method does not require a Stackdriver account. +func (c *MetricClient) ListMonitoredResourceDescriptors(ctx context.Context, req *monitoringpb.ListMonitoredResourceDescriptorsRequest, opts ...gax.CallOption) *MonitoredResourceDescriptorIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListMonitoredResourceDescriptors[0:len(c.CallOptions.ListMonitoredResourceDescriptors):len(c.CallOptions.ListMonitoredResourceDescriptors)], opts...) + it := &MonitoredResourceDescriptorIterator{} + req = proto.Clone(req).(*monitoringpb.ListMonitoredResourceDescriptorsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoredrespb.MonitoredResourceDescriptor, string, error) { + var resp *monitoringpb.ListMonitoredResourceDescriptorsResponse + 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.metricClient.ListMonitoredResourceDescriptors(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.ResourceDescriptors, 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 +} + +// GetMonitoredResourceDescriptor gets a single monitored resource descriptor. This method does not require a Stackdriver account. +func (c *MetricClient) GetMonitoredResourceDescriptor(ctx context.Context, req *monitoringpb.GetMonitoredResourceDescriptorRequest, opts ...gax.CallOption) (*monitoredrespb.MonitoredResourceDescriptor, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetMonitoredResourceDescriptor[0:len(c.CallOptions.GetMonitoredResourceDescriptor):len(c.CallOptions.GetMonitoredResourceDescriptor)], opts...) + var resp *monitoredrespb.MonitoredResourceDescriptor + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.metricClient.GetMonitoredResourceDescriptor(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListMetricDescriptors lists metric descriptors that match a filter. This method does not require a Stackdriver account. +func (c *MetricClient) ListMetricDescriptors(ctx context.Context, req *monitoringpb.ListMetricDescriptorsRequest, opts ...gax.CallOption) *MetricDescriptorIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListMetricDescriptors[0:len(c.CallOptions.ListMetricDescriptors):len(c.CallOptions.ListMetricDescriptors)], opts...) + it := &MetricDescriptorIterator{} + req = proto.Clone(req).(*monitoringpb.ListMetricDescriptorsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*metricpb.MetricDescriptor, string, error) { + var resp *monitoringpb.ListMetricDescriptorsResponse + 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.metricClient.ListMetricDescriptors(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.MetricDescriptors, 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 +} + +// GetMetricDescriptor gets a single metric descriptor. This method does not require a Stackdriver account. +func (c *MetricClient) GetMetricDescriptor(ctx context.Context, req *monitoringpb.GetMetricDescriptorRequest, opts ...gax.CallOption) (*metricpb.MetricDescriptor, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetMetricDescriptor[0:len(c.CallOptions.GetMetricDescriptor):len(c.CallOptions.GetMetricDescriptor)], opts...) + var resp *metricpb.MetricDescriptor + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.metricClient.GetMetricDescriptor(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateMetricDescriptor creates a new metric descriptor. +// User-created metric descriptors define +// custom metrics (at /monitoring/custom-metrics). +func (c *MetricClient) CreateMetricDescriptor(ctx context.Context, req *monitoringpb.CreateMetricDescriptorRequest, opts ...gax.CallOption) (*metricpb.MetricDescriptor, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateMetricDescriptor[0:len(c.CallOptions.CreateMetricDescriptor):len(c.CallOptions.CreateMetricDescriptor)], opts...) + var resp *metricpb.MetricDescriptor + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.metricClient.CreateMetricDescriptor(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteMetricDescriptor deletes a metric descriptor. Only user-created +// custom metrics (at /monitoring/custom-metrics) can be deleted. +func (c *MetricClient) DeleteMetricDescriptor(ctx context.Context, req *monitoringpb.DeleteMetricDescriptorRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteMetricDescriptor[0:len(c.CallOptions.DeleteMetricDescriptor):len(c.CallOptions.DeleteMetricDescriptor)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.metricClient.DeleteMetricDescriptor(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListTimeSeries lists time series that match a filter. This method does not require a Stackdriver account. +func (c *MetricClient) ListTimeSeries(ctx context.Context, req *monitoringpb.ListTimeSeriesRequest, opts ...gax.CallOption) *TimeSeriesIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListTimeSeries[0:len(c.CallOptions.ListTimeSeries):len(c.CallOptions.ListTimeSeries)], opts...) + it := &TimeSeriesIterator{} + req = proto.Clone(req).(*monitoringpb.ListTimeSeriesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoringpb.TimeSeries, string, error) { + var resp *monitoringpb.ListTimeSeriesResponse + 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.metricClient.ListTimeSeries(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.TimeSeries, 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 +} + +// CreateTimeSeries creates or adds data to one or more time series. +// The response is empty if all time series in the request were written. +// If any time series could not be written, a corresponding failure message is +// included in the error response. +func (c *MetricClient) CreateTimeSeries(ctx context.Context, req *monitoringpb.CreateTimeSeriesRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateTimeSeries[0:len(c.CallOptions.CreateTimeSeries):len(c.CallOptions.CreateTimeSeries)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.metricClient.CreateTimeSeries(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// MetricDescriptorIterator manages a stream of *metricpb.MetricDescriptor. +type MetricDescriptorIterator struct { + items []*metricpb.MetricDescriptor + 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 []*metricpb.MetricDescriptor, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *MetricDescriptorIterator) 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 *MetricDescriptorIterator) Next() (*metricpb.MetricDescriptor, error) { + var item *metricpb.MetricDescriptor + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *MetricDescriptorIterator) bufLen() int { + return len(it.items) +} + +func (it *MetricDescriptorIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// MonitoredResourceDescriptorIterator manages a stream of *monitoredrespb.MonitoredResourceDescriptor. +type MonitoredResourceDescriptorIterator struct { + items []*monitoredrespb.MonitoredResourceDescriptor + 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 []*monitoredrespb.MonitoredResourceDescriptor, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *MonitoredResourceDescriptorIterator) 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 *MonitoredResourceDescriptorIterator) Next() (*monitoredrespb.MonitoredResourceDescriptor, error) { + var item *monitoredrespb.MonitoredResourceDescriptor + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *MonitoredResourceDescriptorIterator) bufLen() int { + return len(it.items) +} + +func (it *MonitoredResourceDescriptorIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// TimeSeriesIterator manages a stream of *monitoringpb.TimeSeries. +type TimeSeriesIterator struct { + items []*monitoringpb.TimeSeries + 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 []*monitoringpb.TimeSeries, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *TimeSeriesIterator) 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 *TimeSeriesIterator) Next() (*monitoringpb.TimeSeries, error) { + var item *monitoringpb.TimeSeries + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *TimeSeriesIterator) bufLen() int { + return len(it.items) +} + +func (it *TimeSeriesIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/metric_client_example_test.go b/vendor/cloud.google.com/go/monitoring/apiv3/metric_client_example_test.go new file mode 100644 index 0000000000..29de109753 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/metric_client_example_test.go @@ -0,0 +1,192 @@ +// 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 monitoring_test + +import ( + "cloud.google.com/go/monitoring/apiv3" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +func ExampleNewMetricClient() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleMetricClient_ListMonitoredResourceDescriptors() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListMonitoredResourceDescriptorsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListMonitoredResourceDescriptors(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleMetricClient_GetMonitoredResourceDescriptor() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.GetMonitoredResourceDescriptorRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetMonitoredResourceDescriptor(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleMetricClient_ListMetricDescriptors() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListMetricDescriptorsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListMetricDescriptors(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleMetricClient_GetMetricDescriptor() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.GetMetricDescriptorRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetMetricDescriptor(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleMetricClient_CreateMetricDescriptor() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.CreateMetricDescriptorRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateMetricDescriptor(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleMetricClient_DeleteMetricDescriptor() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.DeleteMetricDescriptorRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteMetricDescriptor(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleMetricClient_ListTimeSeries() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListTimeSeriesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListTimeSeries(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleMetricClient_CreateTimeSeries() { + ctx := context.Background() + c, err := monitoring.NewMetricClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.CreateTimeSeriesRequest{ + // TODO: Fill request struct fields. + } + err = c.CreateTimeSeries(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/mock_test.go b/vendor/cloud.google.com/go/monitoring/apiv3/mock_test.go new file mode 100644 index 0000000000..be38e7fcd5 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/mock_test.go @@ -0,0 +1,2636 @@ +// 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 monitoring + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + metricpb "google.golang.org/genproto/googleapis/api/metric" + monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockAlertPolicyServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + monitoringpb.AlertPolicyServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockAlertPolicyServer) ListAlertPolicies(ctx context.Context, req *monitoringpb.ListAlertPoliciesRequest) (*monitoringpb.ListAlertPoliciesResponse, 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].(*monitoringpb.ListAlertPoliciesResponse), nil +} + +func (s *mockAlertPolicyServer) GetAlertPolicy(ctx context.Context, req *monitoringpb.GetAlertPolicyRequest) (*monitoringpb.AlertPolicy, 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].(*monitoringpb.AlertPolicy), nil +} + +func (s *mockAlertPolicyServer) CreateAlertPolicy(ctx context.Context, req *monitoringpb.CreateAlertPolicyRequest) (*monitoringpb.AlertPolicy, 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].(*monitoringpb.AlertPolicy), nil +} + +func (s *mockAlertPolicyServer) DeleteAlertPolicy(ctx context.Context, req *monitoringpb.DeleteAlertPolicyRequest) (*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 *mockAlertPolicyServer) UpdateAlertPolicy(ctx context.Context, req *monitoringpb.UpdateAlertPolicyRequest) (*monitoringpb.AlertPolicy, 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].(*monitoringpb.AlertPolicy), nil +} + +type mockGroupServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + monitoringpb.GroupServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockGroupServer) ListGroups(ctx context.Context, req *monitoringpb.ListGroupsRequest) (*monitoringpb.ListGroupsResponse, 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].(*monitoringpb.ListGroupsResponse), nil +} + +func (s *mockGroupServer) GetGroup(ctx context.Context, req *monitoringpb.GetGroupRequest) (*monitoringpb.Group, 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].(*monitoringpb.Group), nil +} + +func (s *mockGroupServer) CreateGroup(ctx context.Context, req *monitoringpb.CreateGroupRequest) (*monitoringpb.Group, 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].(*monitoringpb.Group), nil +} + +func (s *mockGroupServer) UpdateGroup(ctx context.Context, req *monitoringpb.UpdateGroupRequest) (*monitoringpb.Group, 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].(*monitoringpb.Group), nil +} + +func (s *mockGroupServer) DeleteGroup(ctx context.Context, req *monitoringpb.DeleteGroupRequest) (*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 *mockGroupServer) ListGroupMembers(ctx context.Context, req *monitoringpb.ListGroupMembersRequest) (*monitoringpb.ListGroupMembersResponse, 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].(*monitoringpb.ListGroupMembersResponse), nil +} + +type mockMetricServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + monitoringpb.MetricServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockMetricServer) ListMonitoredResourceDescriptors(ctx context.Context, req *monitoringpb.ListMonitoredResourceDescriptorsRequest) (*monitoringpb.ListMonitoredResourceDescriptorsResponse, 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].(*monitoringpb.ListMonitoredResourceDescriptorsResponse), nil +} + +func (s *mockMetricServer) GetMonitoredResourceDescriptor(ctx context.Context, req *monitoringpb.GetMonitoredResourceDescriptorRequest) (*monitoredrespb.MonitoredResourceDescriptor, 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].(*monitoredrespb.MonitoredResourceDescriptor), nil +} + +func (s *mockMetricServer) ListMetricDescriptors(ctx context.Context, req *monitoringpb.ListMetricDescriptorsRequest) (*monitoringpb.ListMetricDescriptorsResponse, 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].(*monitoringpb.ListMetricDescriptorsResponse), nil +} + +func (s *mockMetricServer) GetMetricDescriptor(ctx context.Context, req *monitoringpb.GetMetricDescriptorRequest) (*metricpb.MetricDescriptor, 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].(*metricpb.MetricDescriptor), nil +} + +func (s *mockMetricServer) CreateMetricDescriptor(ctx context.Context, req *monitoringpb.CreateMetricDescriptorRequest) (*metricpb.MetricDescriptor, 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].(*metricpb.MetricDescriptor), nil +} + +func (s *mockMetricServer) DeleteMetricDescriptor(ctx context.Context, req *monitoringpb.DeleteMetricDescriptorRequest) (*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 *mockMetricServer) ListTimeSeries(ctx context.Context, req *monitoringpb.ListTimeSeriesRequest) (*monitoringpb.ListTimeSeriesResponse, 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].(*monitoringpb.ListTimeSeriesResponse), nil +} + +func (s *mockMetricServer) CreateTimeSeries(ctx context.Context, req *monitoringpb.CreateTimeSeriesRequest) (*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 +} + +type mockNotificationChannelServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + monitoringpb.NotificationChannelServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockNotificationChannelServer) ListNotificationChannelDescriptors(ctx context.Context, req *monitoringpb.ListNotificationChannelDescriptorsRequest) (*monitoringpb.ListNotificationChannelDescriptorsResponse, 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].(*monitoringpb.ListNotificationChannelDescriptorsResponse), nil +} + +func (s *mockNotificationChannelServer) GetNotificationChannelDescriptor(ctx context.Context, req *monitoringpb.GetNotificationChannelDescriptorRequest) (*monitoringpb.NotificationChannelDescriptor, 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].(*monitoringpb.NotificationChannelDescriptor), nil +} + +func (s *mockNotificationChannelServer) ListNotificationChannels(ctx context.Context, req *monitoringpb.ListNotificationChannelsRequest) (*monitoringpb.ListNotificationChannelsResponse, 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].(*monitoringpb.ListNotificationChannelsResponse), nil +} + +func (s *mockNotificationChannelServer) GetNotificationChannel(ctx context.Context, req *monitoringpb.GetNotificationChannelRequest) (*monitoringpb.NotificationChannel, 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].(*monitoringpb.NotificationChannel), nil +} + +func (s *mockNotificationChannelServer) CreateNotificationChannel(ctx context.Context, req *monitoringpb.CreateNotificationChannelRequest) (*monitoringpb.NotificationChannel, 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].(*monitoringpb.NotificationChannel), nil +} + +func (s *mockNotificationChannelServer) UpdateNotificationChannel(ctx context.Context, req *monitoringpb.UpdateNotificationChannelRequest) (*monitoringpb.NotificationChannel, 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].(*monitoringpb.NotificationChannel), nil +} + +func (s *mockNotificationChannelServer) DeleteNotificationChannel(ctx context.Context, req *monitoringpb.DeleteNotificationChannelRequest) (*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 +} + +type mockUptimeCheckServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + monitoringpb.UptimeCheckServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockUptimeCheckServer) ListUptimeCheckConfigs(ctx context.Context, req *monitoringpb.ListUptimeCheckConfigsRequest) (*monitoringpb.ListUptimeCheckConfigsResponse, 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].(*monitoringpb.ListUptimeCheckConfigsResponse), nil +} + +func (s *mockUptimeCheckServer) GetUptimeCheckConfig(ctx context.Context, req *monitoringpb.GetUptimeCheckConfigRequest) (*monitoringpb.UptimeCheckConfig, 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].(*monitoringpb.UptimeCheckConfig), nil +} + +func (s *mockUptimeCheckServer) CreateUptimeCheckConfig(ctx context.Context, req *monitoringpb.CreateUptimeCheckConfigRequest) (*monitoringpb.UptimeCheckConfig, 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].(*monitoringpb.UptimeCheckConfig), nil +} + +func (s *mockUptimeCheckServer) UpdateUptimeCheckConfig(ctx context.Context, req *monitoringpb.UpdateUptimeCheckConfigRequest) (*monitoringpb.UptimeCheckConfig, 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].(*monitoringpb.UptimeCheckConfig), nil +} + +func (s *mockUptimeCheckServer) DeleteUptimeCheckConfig(ctx context.Context, req *monitoringpb.DeleteUptimeCheckConfigRequest) (*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 *mockUptimeCheckServer) ListUptimeCheckIps(ctx context.Context, req *monitoringpb.ListUptimeCheckIpsRequest) (*monitoringpb.ListUptimeCheckIpsResponse, 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].(*monitoringpb.ListUptimeCheckIpsResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockAlertPolicy mockAlertPolicyServer + mockGroup mockGroupServer + mockMetric mockMetricServer + mockNotificationChannel mockNotificationChannelServer + mockUptimeCheck mockUptimeCheckServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + monitoringpb.RegisterAlertPolicyServiceServer(serv, &mockAlertPolicy) + monitoringpb.RegisterGroupServiceServer(serv, &mockGroup) + monitoringpb.RegisterMetricServiceServer(serv, &mockMetric) + monitoringpb.RegisterNotificationChannelServiceServer(serv, &mockNotificationChannel) + monitoringpb.RegisterUptimeCheckServiceServer(serv, &mockUptimeCheck) + + 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 TestAlertPolicyServiceListAlertPolicies(t *testing.T) { + var nextPageToken string = "" + var alertPoliciesElement *monitoringpb.AlertPolicy = &monitoringpb.AlertPolicy{} + var alertPolicies = []*monitoringpb.AlertPolicy{alertPoliciesElement} + var expectedResponse = &monitoringpb.ListAlertPoliciesResponse{ + NextPageToken: nextPageToken, + AlertPolicies: alertPolicies, + } + + mockAlertPolicy.err = nil + mockAlertPolicy.reqs = nil + + mockAlertPolicy.resps = append(mockAlertPolicy.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListAlertPoliciesRequest{ + Name: formattedName, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListAlertPolicies(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAlertPolicy.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.AlertPolicies[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 TestAlertPolicyServiceListAlertPoliciesError(t *testing.T) { + errCode := codes.PermissionDenied + mockAlertPolicy.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListAlertPoliciesRequest{ + Name: formattedName, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListAlertPolicies(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 TestAlertPolicyServiceGetAlertPolicy(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var expectedResponse = &monitoringpb.AlertPolicy{ + Name: name2, + DisplayName: displayName, + } + + mockAlertPolicy.err = nil + mockAlertPolicy.reqs = nil + + mockAlertPolicy.resps = append(mockAlertPolicy.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/alertPolicies/%s", "[PROJECT]", "[ALERT_POLICY]") + var request = &monitoringpb.GetAlertPolicyRequest{ + Name: formattedName, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetAlertPolicy(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAlertPolicy.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 TestAlertPolicyServiceGetAlertPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockAlertPolicy.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/alertPolicies/%s", "[PROJECT]", "[ALERT_POLICY]") + var request = &monitoringpb.GetAlertPolicyRequest{ + Name: formattedName, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetAlertPolicy(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 TestAlertPolicyServiceCreateAlertPolicy(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var expectedResponse = &monitoringpb.AlertPolicy{ + Name: name2, + DisplayName: displayName, + } + + mockAlertPolicy.err = nil + mockAlertPolicy.reqs = nil + + mockAlertPolicy.resps = append(mockAlertPolicy.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var alertPolicy *monitoringpb.AlertPolicy = &monitoringpb.AlertPolicy{} + var request = &monitoringpb.CreateAlertPolicyRequest{ + Name: formattedName, + AlertPolicy: alertPolicy, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateAlertPolicy(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAlertPolicy.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 TestAlertPolicyServiceCreateAlertPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockAlertPolicy.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var alertPolicy *monitoringpb.AlertPolicy = &monitoringpb.AlertPolicy{} + var request = &monitoringpb.CreateAlertPolicyRequest{ + Name: formattedName, + AlertPolicy: alertPolicy, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateAlertPolicy(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 TestAlertPolicyServiceDeleteAlertPolicy(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockAlertPolicy.err = nil + mockAlertPolicy.reqs = nil + + mockAlertPolicy.resps = append(mockAlertPolicy.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/alertPolicies/%s", "[PROJECT]", "[ALERT_POLICY]") + var request = &monitoringpb.DeleteAlertPolicyRequest{ + Name: formattedName, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteAlertPolicy(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAlertPolicy.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestAlertPolicyServiceDeleteAlertPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockAlertPolicy.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/alertPolicies/%s", "[PROJECT]", "[ALERT_POLICY]") + var request = &monitoringpb.DeleteAlertPolicyRequest{ + Name: formattedName, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteAlertPolicy(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 TestAlertPolicyServiceUpdateAlertPolicy(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var expectedResponse = &monitoringpb.AlertPolicy{ + Name: name, + DisplayName: displayName, + } + + mockAlertPolicy.err = nil + mockAlertPolicy.reqs = nil + + mockAlertPolicy.resps = append(mockAlertPolicy.resps[:0], expectedResponse) + + var alertPolicy *monitoringpb.AlertPolicy = &monitoringpb.AlertPolicy{} + var request = &monitoringpb.UpdateAlertPolicyRequest{ + AlertPolicy: alertPolicy, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateAlertPolicy(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockAlertPolicy.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 TestAlertPolicyServiceUpdateAlertPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockAlertPolicy.err = gstatus.Error(errCode, "test error") + + var alertPolicy *monitoringpb.AlertPolicy = &monitoringpb.AlertPolicy{} + var request = &monitoringpb.UpdateAlertPolicyRequest{ + AlertPolicy: alertPolicy, + } + + c, err := NewAlertPolicyClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateAlertPolicy(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 TestGroupServiceListGroups(t *testing.T) { + var nextPageToken string = "" + var groupElement *monitoringpb.Group = &monitoringpb.Group{} + var group = []*monitoringpb.Group{groupElement} + var expectedResponse = &monitoringpb.ListGroupsResponse{ + NextPageToken: nextPageToken, + Group: group, + } + + mockGroup.err = nil + mockGroup.reqs = nil + + mockGroup.resps = append(mockGroup.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListGroupsRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListGroups(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockGroup.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Group[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 TestGroupServiceListGroupsError(t *testing.T) { + errCode := codes.PermissionDenied + mockGroup.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListGroupsRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListGroups(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 TestGroupServiceGetGroup(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var parentName string = "parentName1015022848" + var filter string = "filter-1274492040" + var isCluster bool = false + var expectedResponse = &monitoringpb.Group{ + Name: name2, + DisplayName: displayName, + ParentName: parentName, + Filter: filter, + IsCluster: isCluster, + } + + mockGroup.err = nil + mockGroup.reqs = nil + + mockGroup.resps = append(mockGroup.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &monitoringpb.GetGroupRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetGroup(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockGroup.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 TestGroupServiceGetGroupError(t *testing.T) { + errCode := codes.PermissionDenied + mockGroup.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &monitoringpb.GetGroupRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetGroup(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 TestGroupServiceCreateGroup(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var parentName string = "parentName1015022848" + var filter string = "filter-1274492040" + var isCluster bool = false + var expectedResponse = &monitoringpb.Group{ + Name: name2, + DisplayName: displayName, + ParentName: parentName, + Filter: filter, + IsCluster: isCluster, + } + + mockGroup.err = nil + mockGroup.reqs = nil + + mockGroup.resps = append(mockGroup.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var group *monitoringpb.Group = &monitoringpb.Group{} + var request = &monitoringpb.CreateGroupRequest{ + Name: formattedName, + Group: group, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateGroup(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockGroup.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 TestGroupServiceCreateGroupError(t *testing.T) { + errCode := codes.PermissionDenied + mockGroup.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var group *monitoringpb.Group = &monitoringpb.Group{} + var request = &monitoringpb.CreateGroupRequest{ + Name: formattedName, + Group: group, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateGroup(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 TestGroupServiceUpdateGroup(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var parentName string = "parentName1015022848" + var filter string = "filter-1274492040" + var isCluster bool = false + var expectedResponse = &monitoringpb.Group{ + Name: name, + DisplayName: displayName, + ParentName: parentName, + Filter: filter, + IsCluster: isCluster, + } + + mockGroup.err = nil + mockGroup.reqs = nil + + mockGroup.resps = append(mockGroup.resps[:0], expectedResponse) + + var group *monitoringpb.Group = &monitoringpb.Group{} + var request = &monitoringpb.UpdateGroupRequest{ + Group: group, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateGroup(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockGroup.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 TestGroupServiceUpdateGroupError(t *testing.T) { + errCode := codes.PermissionDenied + mockGroup.err = gstatus.Error(errCode, "test error") + + var group *monitoringpb.Group = &monitoringpb.Group{} + var request = &monitoringpb.UpdateGroupRequest{ + Group: group, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateGroup(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 TestGroupServiceDeleteGroup(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockGroup.err = nil + mockGroup.reqs = nil + + mockGroup.resps = append(mockGroup.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &monitoringpb.DeleteGroupRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteGroup(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockGroup.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestGroupServiceDeleteGroupError(t *testing.T) { + errCode := codes.PermissionDenied + mockGroup.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &monitoringpb.DeleteGroupRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteGroup(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 TestGroupServiceListGroupMembers(t *testing.T) { + var nextPageToken string = "" + var totalSize int32 = 705419236 + var membersElement *monitoredrespb.MonitoredResource = &monitoredrespb.MonitoredResource{} + var members = []*monitoredrespb.MonitoredResource{membersElement} + var expectedResponse = &monitoringpb.ListGroupMembersResponse{ + NextPageToken: nextPageToken, + TotalSize: totalSize, + Members: members, + } + + mockGroup.err = nil + mockGroup.reqs = nil + + mockGroup.resps = append(mockGroup.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &monitoringpb.ListGroupMembersRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListGroupMembers(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockGroup.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Members[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 TestGroupServiceListGroupMembersError(t *testing.T) { + errCode := codes.PermissionDenied + mockGroup.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/groups/%s", "[PROJECT]", "[GROUP]") + var request = &monitoringpb.ListGroupMembersRequest{ + Name: formattedName, + } + + c, err := NewGroupClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListGroupMembers(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 TestMetricServiceListMonitoredResourceDescriptors(t *testing.T) { + var nextPageToken string = "" + var resourceDescriptorsElement *monitoredrespb.MonitoredResourceDescriptor = &monitoredrespb.MonitoredResourceDescriptor{} + var resourceDescriptors = []*monitoredrespb.MonitoredResourceDescriptor{resourceDescriptorsElement} + var expectedResponse = &monitoringpb.ListMonitoredResourceDescriptorsResponse{ + NextPageToken: nextPageToken, + ResourceDescriptors: resourceDescriptors, + } + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListMonitoredResourceDescriptorsRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListMonitoredResourceDescriptors(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.ResourceDescriptors[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 TestMetricServiceListMonitoredResourceDescriptorsError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListMonitoredResourceDescriptorsRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListMonitoredResourceDescriptors(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 TestMetricServiceGetMonitoredResourceDescriptor(t *testing.T) { + var name2 string = "name2-1052831874" + var type_ string = "type3575610" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &monitoredrespb.MonitoredResourceDescriptor{ + Name: name2, + Type: type_, + DisplayName: displayName, + Description: description, + } + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/monitoredResourceDescriptors/%s", "[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]") + var request = &monitoringpb.GetMonitoredResourceDescriptorRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetMonitoredResourceDescriptor(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.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 TestMetricServiceGetMonitoredResourceDescriptorError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/monitoredResourceDescriptors/%s", "[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]") + var request = &monitoringpb.GetMonitoredResourceDescriptorRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetMonitoredResourceDescriptor(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 TestMetricServiceListMetricDescriptors(t *testing.T) { + var nextPageToken string = "" + var metricDescriptorsElement *metricpb.MetricDescriptor = &metricpb.MetricDescriptor{} + var metricDescriptors = []*metricpb.MetricDescriptor{metricDescriptorsElement} + var expectedResponse = &monitoringpb.ListMetricDescriptorsResponse{ + NextPageToken: nextPageToken, + MetricDescriptors: metricDescriptors, + } + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListMetricDescriptorsRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListMetricDescriptors(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.MetricDescriptors[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 TestMetricServiceListMetricDescriptorsError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListMetricDescriptorsRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListMetricDescriptors(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 TestMetricServiceGetMetricDescriptor(t *testing.T) { + var name2 string = "name2-1052831874" + var type_ string = "type3575610" + var unit string = "unit3594628" + var description string = "description-1724546052" + var displayName string = "displayName1615086568" + var expectedResponse = &metricpb.MetricDescriptor{ + Name: name2, + Type: type_, + Unit: unit, + Description: description, + DisplayName: displayName, + } + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/metricDescriptors/%s", "[PROJECT]", "[METRIC_DESCRIPTOR]") + var request = &monitoringpb.GetMetricDescriptorRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetMetricDescriptor(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.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 TestMetricServiceGetMetricDescriptorError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/metricDescriptors/%s", "[PROJECT]", "[METRIC_DESCRIPTOR]") + var request = &monitoringpb.GetMetricDescriptorRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetMetricDescriptor(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 TestMetricServiceCreateMetricDescriptor(t *testing.T) { + var name2 string = "name2-1052831874" + var type_ string = "type3575610" + var unit string = "unit3594628" + var description string = "description-1724546052" + var displayName string = "displayName1615086568" + var expectedResponse = &metricpb.MetricDescriptor{ + Name: name2, + Type: type_, + Unit: unit, + Description: description, + DisplayName: displayName, + } + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var metricDescriptor *metricpb.MetricDescriptor = &metricpb.MetricDescriptor{} + var request = &monitoringpb.CreateMetricDescriptorRequest{ + Name: formattedName, + MetricDescriptor: metricDescriptor, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateMetricDescriptor(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.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 TestMetricServiceCreateMetricDescriptorError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var metricDescriptor *metricpb.MetricDescriptor = &metricpb.MetricDescriptor{} + var request = &monitoringpb.CreateMetricDescriptorRequest{ + Name: formattedName, + MetricDescriptor: metricDescriptor, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateMetricDescriptor(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 TestMetricServiceDeleteMetricDescriptor(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/metricDescriptors/%s", "[PROJECT]", "[METRIC_DESCRIPTOR]") + var request = &monitoringpb.DeleteMetricDescriptorRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteMetricDescriptor(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestMetricServiceDeleteMetricDescriptorError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/metricDescriptors/%s", "[PROJECT]", "[METRIC_DESCRIPTOR]") + var request = &monitoringpb.DeleteMetricDescriptorRequest{ + Name: formattedName, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteMetricDescriptor(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 TestMetricServiceListTimeSeries(t *testing.T) { + var nextPageToken string = "" + var timeSeriesElement *monitoringpb.TimeSeries = &monitoringpb.TimeSeries{} + var timeSeries = []*monitoringpb.TimeSeries{timeSeriesElement} + var expectedResponse = &monitoringpb.ListTimeSeriesResponse{ + NextPageToken: nextPageToken, + TimeSeries: timeSeries, + } + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var filter string = "filter-1274492040" + var interval *monitoringpb.TimeInterval = &monitoringpb.TimeInterval{} + var view monitoringpb.ListTimeSeriesRequest_TimeSeriesView = monitoringpb.ListTimeSeriesRequest_FULL + var request = &monitoringpb.ListTimeSeriesRequest{ + Name: formattedName, + Filter: filter, + Interval: interval, + View: view, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTimeSeries(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.TimeSeries[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 TestMetricServiceListTimeSeriesError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var filter string = "filter-1274492040" + var interval *monitoringpb.TimeInterval = &monitoringpb.TimeInterval{} + var view monitoringpb.ListTimeSeriesRequest_TimeSeriesView = monitoringpb.ListTimeSeriesRequest_FULL + var request = &monitoringpb.ListTimeSeriesRequest{ + Name: formattedName, + Filter: filter, + Interval: interval, + View: view, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTimeSeries(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 TestMetricServiceCreateTimeSeries(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockMetric.err = nil + mockMetric.reqs = nil + + mockMetric.resps = append(mockMetric.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var timeSeries []*monitoringpb.TimeSeries = nil + var request = &monitoringpb.CreateTimeSeriesRequest{ + Name: formattedName, + TimeSeries: timeSeries, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CreateTimeSeries(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockMetric.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestMetricServiceCreateTimeSeriesError(t *testing.T) { + errCode := codes.PermissionDenied + mockMetric.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var timeSeries []*monitoringpb.TimeSeries = nil + var request = &monitoringpb.CreateTimeSeriesRequest{ + Name: formattedName, + TimeSeries: timeSeries, + } + + c, err := NewMetricClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.CreateTimeSeries(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 TestNotificationChannelServiceListNotificationChannelDescriptors(t *testing.T) { + var nextPageToken string = "" + var channelDescriptorsElement *monitoringpb.NotificationChannelDescriptor = &monitoringpb.NotificationChannelDescriptor{} + var channelDescriptors = []*monitoringpb.NotificationChannelDescriptor{channelDescriptorsElement} + var expectedResponse = &monitoringpb.ListNotificationChannelDescriptorsResponse{ + NextPageToken: nextPageToken, + ChannelDescriptors: channelDescriptors, + } + + mockNotificationChannel.err = nil + mockNotificationChannel.reqs = nil + + mockNotificationChannel.resps = append(mockNotificationChannel.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListNotificationChannelDescriptorsRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListNotificationChannelDescriptors(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockNotificationChannel.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.ChannelDescriptors[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 TestNotificationChannelServiceListNotificationChannelDescriptorsError(t *testing.T) { + errCode := codes.PermissionDenied + mockNotificationChannel.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListNotificationChannelDescriptorsRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListNotificationChannelDescriptors(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 TestNotificationChannelServiceGetNotificationChannelDescriptor(t *testing.T) { + var name2 string = "name2-1052831874" + var type_ string = "type3575610" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &monitoringpb.NotificationChannelDescriptor{ + Name: name2, + Type: type_, + DisplayName: displayName, + Description: description, + } + + mockNotificationChannel.err = nil + mockNotificationChannel.reqs = nil + + mockNotificationChannel.resps = append(mockNotificationChannel.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/notificationChannelDescriptors/%s", "[PROJECT]", "[CHANNEL_DESCRIPTOR]") + var request = &monitoringpb.GetNotificationChannelDescriptorRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetNotificationChannelDescriptor(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockNotificationChannel.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 TestNotificationChannelServiceGetNotificationChannelDescriptorError(t *testing.T) { + errCode := codes.PermissionDenied + mockNotificationChannel.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/notificationChannelDescriptors/%s", "[PROJECT]", "[CHANNEL_DESCRIPTOR]") + var request = &monitoringpb.GetNotificationChannelDescriptorRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetNotificationChannelDescriptor(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 TestNotificationChannelServiceListNotificationChannels(t *testing.T) { + var nextPageToken string = "" + var notificationChannelsElement *monitoringpb.NotificationChannel = &monitoringpb.NotificationChannel{} + var notificationChannels = []*monitoringpb.NotificationChannel{notificationChannelsElement} + var expectedResponse = &monitoringpb.ListNotificationChannelsResponse{ + NextPageToken: nextPageToken, + NotificationChannels: notificationChannels, + } + + mockNotificationChannel.err = nil + mockNotificationChannel.reqs = nil + + mockNotificationChannel.resps = append(mockNotificationChannel.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListNotificationChannelsRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListNotificationChannels(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockNotificationChannel.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.NotificationChannels[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 TestNotificationChannelServiceListNotificationChannelsError(t *testing.T) { + errCode := codes.PermissionDenied + mockNotificationChannel.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListNotificationChannelsRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListNotificationChannels(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 TestNotificationChannelServiceGetNotificationChannel(t *testing.T) { + var type_ string = "type3575610" + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &monitoringpb.NotificationChannel{ + Type: type_, + Name: name2, + DisplayName: displayName, + Description: description, + } + + mockNotificationChannel.err = nil + mockNotificationChannel.reqs = nil + + mockNotificationChannel.resps = append(mockNotificationChannel.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/notificationChannels/%s", "[PROJECT]", "[NOTIFICATION_CHANNEL]") + var request = &monitoringpb.GetNotificationChannelRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetNotificationChannel(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockNotificationChannel.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 TestNotificationChannelServiceGetNotificationChannelError(t *testing.T) { + errCode := codes.PermissionDenied + mockNotificationChannel.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/notificationChannels/%s", "[PROJECT]", "[NOTIFICATION_CHANNEL]") + var request = &monitoringpb.GetNotificationChannelRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetNotificationChannel(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 TestNotificationChannelServiceCreateNotificationChannel(t *testing.T) { + var type_ string = "type3575610" + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &monitoringpb.NotificationChannel{ + Type: type_, + Name: name2, + DisplayName: displayName, + Description: description, + } + + mockNotificationChannel.err = nil + mockNotificationChannel.reqs = nil + + mockNotificationChannel.resps = append(mockNotificationChannel.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var notificationChannel *monitoringpb.NotificationChannel = &monitoringpb.NotificationChannel{} + var request = &monitoringpb.CreateNotificationChannelRequest{ + Name: formattedName, + NotificationChannel: notificationChannel, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateNotificationChannel(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockNotificationChannel.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 TestNotificationChannelServiceCreateNotificationChannelError(t *testing.T) { + errCode := codes.PermissionDenied + mockNotificationChannel.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var notificationChannel *monitoringpb.NotificationChannel = &monitoringpb.NotificationChannel{} + var request = &monitoringpb.CreateNotificationChannelRequest{ + Name: formattedName, + NotificationChannel: notificationChannel, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateNotificationChannel(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 TestNotificationChannelServiceUpdateNotificationChannel(t *testing.T) { + var type_ string = "type3575610" + var name string = "name3373707" + var displayName string = "displayName1615086568" + var description string = "description-1724546052" + var expectedResponse = &monitoringpb.NotificationChannel{ + Type: type_, + Name: name, + DisplayName: displayName, + Description: description, + } + + mockNotificationChannel.err = nil + mockNotificationChannel.reqs = nil + + mockNotificationChannel.resps = append(mockNotificationChannel.resps[:0], expectedResponse) + + var notificationChannel *monitoringpb.NotificationChannel = &monitoringpb.NotificationChannel{} + var request = &monitoringpb.UpdateNotificationChannelRequest{ + NotificationChannel: notificationChannel, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateNotificationChannel(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockNotificationChannel.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 TestNotificationChannelServiceUpdateNotificationChannelError(t *testing.T) { + errCode := codes.PermissionDenied + mockNotificationChannel.err = gstatus.Error(errCode, "test error") + + var notificationChannel *monitoringpb.NotificationChannel = &monitoringpb.NotificationChannel{} + var request = &monitoringpb.UpdateNotificationChannelRequest{ + NotificationChannel: notificationChannel, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateNotificationChannel(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 TestNotificationChannelServiceDeleteNotificationChannel(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockNotificationChannel.err = nil + mockNotificationChannel.reqs = nil + + mockNotificationChannel.resps = append(mockNotificationChannel.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/notificationChannels/%s", "[PROJECT]", "[NOTIFICATION_CHANNEL]") + var request = &monitoringpb.DeleteNotificationChannelRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteNotificationChannel(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockNotificationChannel.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestNotificationChannelServiceDeleteNotificationChannelError(t *testing.T) { + errCode := codes.PermissionDenied + mockNotificationChannel.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/notificationChannels/%s", "[PROJECT]", "[NOTIFICATION_CHANNEL]") + var request = &monitoringpb.DeleteNotificationChannelRequest{ + Name: formattedName, + } + + c, err := NewNotificationChannelClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteNotificationChannel(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 TestUptimeCheckServiceListUptimeCheckConfigs(t *testing.T) { + var nextPageToken string = "" + var uptimeCheckConfigsElement *monitoringpb.UptimeCheckConfig = &monitoringpb.UptimeCheckConfig{} + var uptimeCheckConfigs = []*monitoringpb.UptimeCheckConfig{uptimeCheckConfigsElement} + var expectedResponse = &monitoringpb.ListUptimeCheckConfigsResponse{ + NextPageToken: nextPageToken, + UptimeCheckConfigs: uptimeCheckConfigs, + } + + mockUptimeCheck.err = nil + mockUptimeCheck.reqs = nil + + mockUptimeCheck.resps = append(mockUptimeCheck.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListUptimeCheckConfigsRequest{ + Parent: formattedParent, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListUptimeCheckConfigs(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockUptimeCheck.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.UptimeCheckConfigs[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 TestUptimeCheckServiceListUptimeCheckConfigsError(t *testing.T) { + errCode := codes.PermissionDenied + mockUptimeCheck.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &monitoringpb.ListUptimeCheckConfigsRequest{ + Parent: formattedParent, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListUptimeCheckConfigs(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 TestUptimeCheckServiceGetUptimeCheckConfig(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var expectedResponse = &monitoringpb.UptimeCheckConfig{ + Name: name2, + DisplayName: displayName, + } + + mockUptimeCheck.err = nil + mockUptimeCheck.reqs = nil + + mockUptimeCheck.resps = append(mockUptimeCheck.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/uptimeCheckConfigs/%s", "[PROJECT]", "[UPTIME_CHECK_CONFIG]") + var request = &monitoringpb.GetUptimeCheckConfigRequest{ + Name: formattedName, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetUptimeCheckConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockUptimeCheck.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 TestUptimeCheckServiceGetUptimeCheckConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockUptimeCheck.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/uptimeCheckConfigs/%s", "[PROJECT]", "[UPTIME_CHECK_CONFIG]") + var request = &monitoringpb.GetUptimeCheckConfigRequest{ + Name: formattedName, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetUptimeCheckConfig(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 TestUptimeCheckServiceCreateUptimeCheckConfig(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var expectedResponse = &monitoringpb.UptimeCheckConfig{ + Name: name, + DisplayName: displayName, + } + + mockUptimeCheck.err = nil + mockUptimeCheck.reqs = nil + + mockUptimeCheck.resps = append(mockUptimeCheck.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var uptimeCheckConfig *monitoringpb.UptimeCheckConfig = &monitoringpb.UptimeCheckConfig{} + var request = &monitoringpb.CreateUptimeCheckConfigRequest{ + Parent: formattedParent, + UptimeCheckConfig: uptimeCheckConfig, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateUptimeCheckConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockUptimeCheck.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 TestUptimeCheckServiceCreateUptimeCheckConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockUptimeCheck.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var uptimeCheckConfig *monitoringpb.UptimeCheckConfig = &monitoringpb.UptimeCheckConfig{} + var request = &monitoringpb.CreateUptimeCheckConfigRequest{ + Parent: formattedParent, + UptimeCheckConfig: uptimeCheckConfig, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateUptimeCheckConfig(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 TestUptimeCheckServiceUpdateUptimeCheckConfig(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var expectedResponse = &monitoringpb.UptimeCheckConfig{ + Name: name, + DisplayName: displayName, + } + + mockUptimeCheck.err = nil + mockUptimeCheck.reqs = nil + + mockUptimeCheck.resps = append(mockUptimeCheck.resps[:0], expectedResponse) + + var uptimeCheckConfig *monitoringpb.UptimeCheckConfig = &monitoringpb.UptimeCheckConfig{} + var request = &monitoringpb.UpdateUptimeCheckConfigRequest{ + UptimeCheckConfig: uptimeCheckConfig, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateUptimeCheckConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockUptimeCheck.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 TestUptimeCheckServiceUpdateUptimeCheckConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockUptimeCheck.err = gstatus.Error(errCode, "test error") + + var uptimeCheckConfig *monitoringpb.UptimeCheckConfig = &monitoringpb.UptimeCheckConfig{} + var request = &monitoringpb.UpdateUptimeCheckConfigRequest{ + UptimeCheckConfig: uptimeCheckConfig, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateUptimeCheckConfig(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 TestUptimeCheckServiceDeleteUptimeCheckConfig(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockUptimeCheck.err = nil + mockUptimeCheck.reqs = nil + + mockUptimeCheck.resps = append(mockUptimeCheck.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/uptimeCheckConfigs/%s", "[PROJECT]", "[UPTIME_CHECK_CONFIG]") + var request = &monitoringpb.DeleteUptimeCheckConfigRequest{ + Name: formattedName, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteUptimeCheckConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockUptimeCheck.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestUptimeCheckServiceDeleteUptimeCheckConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockUptimeCheck.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/uptimeCheckConfigs/%s", "[PROJECT]", "[UPTIME_CHECK_CONFIG]") + var request = &monitoringpb.DeleteUptimeCheckConfigRequest{ + Name: formattedName, + } + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteUptimeCheckConfig(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 TestUptimeCheckServiceListUptimeCheckIps(t *testing.T) { + var nextPageToken string = "" + var uptimeCheckIpsElement *monitoringpb.UptimeCheckIp = &monitoringpb.UptimeCheckIp{} + var uptimeCheckIps = []*monitoringpb.UptimeCheckIp{uptimeCheckIpsElement} + var expectedResponse = &monitoringpb.ListUptimeCheckIpsResponse{ + NextPageToken: nextPageToken, + UptimeCheckIps: uptimeCheckIps, + } + + mockUptimeCheck.err = nil + mockUptimeCheck.reqs = nil + + mockUptimeCheck.resps = append(mockUptimeCheck.resps[:0], expectedResponse) + + var request *monitoringpb.ListUptimeCheckIpsRequest = &monitoringpb.ListUptimeCheckIpsRequest{} + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListUptimeCheckIps(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockUptimeCheck.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.UptimeCheckIps[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 TestUptimeCheckServiceListUptimeCheckIpsError(t *testing.T) { + errCode := codes.PermissionDenied + mockUptimeCheck.err = gstatus.Error(errCode, "test error") + + var request *monitoringpb.ListUptimeCheckIpsRequest = &monitoringpb.ListUptimeCheckIpsRequest{} + + c, err := NewUptimeCheckClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListUptimeCheckIps(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 +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/notification_channel_client.go b/vendor/cloud.google.com/go/monitoring/apiv3/notification_channel_client.go new file mode 100644 index 0000000000..53bcf6bcb6 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/notification_channel_client.go @@ -0,0 +1,376 @@ +// 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 monitoring + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// NotificationChannelCallOptions contains the retry settings for each method of NotificationChannelClient. +type NotificationChannelCallOptions struct { + ListNotificationChannelDescriptors []gax.CallOption + GetNotificationChannelDescriptor []gax.CallOption + ListNotificationChannels []gax.CallOption + GetNotificationChannel []gax.CallOption + CreateNotificationChannel []gax.CallOption + UpdateNotificationChannel []gax.CallOption + DeleteNotificationChannel []gax.CallOption +} + +func defaultNotificationChannelClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("monitoring.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultNotificationChannelCallOptions() *NotificationChannelCallOptions { + 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 &NotificationChannelCallOptions{ + ListNotificationChannelDescriptors: retry[[2]string{"default", "idempotent"}], + GetNotificationChannelDescriptor: retry[[2]string{"default", "idempotent"}], + ListNotificationChannels: retry[[2]string{"default", "idempotent"}], + GetNotificationChannel: retry[[2]string{"default", "idempotent"}], + CreateNotificationChannel: retry[[2]string{"default", "non_idempotent"}], + UpdateNotificationChannel: retry[[2]string{"default", "non_idempotent"}], + DeleteNotificationChannel: retry[[2]string{"default", "idempotent"}], + } +} + +// NotificationChannelClient is a client for interacting with Stackdriver Monitoring API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type NotificationChannelClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + notificationChannelClient monitoringpb.NotificationChannelServiceClient + + // The call options for this service. + CallOptions *NotificationChannelCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewNotificationChannelClient creates a new notification channel service client. +// +// The Notification Channel API provides access to configuration that +// controls how messages related to incidents are sent. +func NewNotificationChannelClient(ctx context.Context, opts ...option.ClientOption) (*NotificationChannelClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultNotificationChannelClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &NotificationChannelClient{ + conn: conn, + CallOptions: defaultNotificationChannelCallOptions(), + + notificationChannelClient: monitoringpb.NewNotificationChannelServiceClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *NotificationChannelClient) 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 *NotificationChannelClient) 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 *NotificationChannelClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListNotificationChannelDescriptors lists the descriptors for supported channel types. The use of descriptors +// makes it possible for new channel types to be dynamically added. +func (c *NotificationChannelClient) ListNotificationChannelDescriptors(ctx context.Context, req *monitoringpb.ListNotificationChannelDescriptorsRequest, opts ...gax.CallOption) *NotificationChannelDescriptorIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListNotificationChannelDescriptors[0:len(c.CallOptions.ListNotificationChannelDescriptors):len(c.CallOptions.ListNotificationChannelDescriptors)], opts...) + it := &NotificationChannelDescriptorIterator{} + req = proto.Clone(req).(*monitoringpb.ListNotificationChannelDescriptorsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoringpb.NotificationChannelDescriptor, string, error) { + var resp *monitoringpb.ListNotificationChannelDescriptorsResponse + 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.notificationChannelClient.ListNotificationChannelDescriptors(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.ChannelDescriptors, 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 +} + +// GetNotificationChannelDescriptor gets a single channel descriptor. The descriptor indicates which fields +// are expected / permitted for a notification channel of the given type. +func (c *NotificationChannelClient) GetNotificationChannelDescriptor(ctx context.Context, req *monitoringpb.GetNotificationChannelDescriptorRequest, opts ...gax.CallOption) (*monitoringpb.NotificationChannelDescriptor, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetNotificationChannelDescriptor[0:len(c.CallOptions.GetNotificationChannelDescriptor):len(c.CallOptions.GetNotificationChannelDescriptor)], opts...) + var resp *monitoringpb.NotificationChannelDescriptor + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.notificationChannelClient.GetNotificationChannelDescriptor(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListNotificationChannels lists the notification channels that have been created for the project. +func (c *NotificationChannelClient) ListNotificationChannels(ctx context.Context, req *monitoringpb.ListNotificationChannelsRequest, opts ...gax.CallOption) *NotificationChannelIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListNotificationChannels[0:len(c.CallOptions.ListNotificationChannels):len(c.CallOptions.ListNotificationChannels)], opts...) + it := &NotificationChannelIterator{} + req = proto.Clone(req).(*monitoringpb.ListNotificationChannelsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoringpb.NotificationChannel, string, error) { + var resp *monitoringpb.ListNotificationChannelsResponse + 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.notificationChannelClient.ListNotificationChannels(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.NotificationChannels, 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 +} + +// GetNotificationChannel gets a single notification channel. The channel includes the relevant +// configuration details with which the channel was created. However, the +// response may truncate or omit passwords, API keys, or other private key +// matter and thus the response may not be 100% identical to the information +// that was supplied in the call to the create method. +func (c *NotificationChannelClient) GetNotificationChannel(ctx context.Context, req *monitoringpb.GetNotificationChannelRequest, opts ...gax.CallOption) (*monitoringpb.NotificationChannel, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetNotificationChannel[0:len(c.CallOptions.GetNotificationChannel):len(c.CallOptions.GetNotificationChannel)], opts...) + var resp *monitoringpb.NotificationChannel + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.notificationChannelClient.GetNotificationChannel(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateNotificationChannel creates a new notification channel, representing a single notification +// endpoint such as an email address, SMS number, or pagerduty service. +func (c *NotificationChannelClient) CreateNotificationChannel(ctx context.Context, req *monitoringpb.CreateNotificationChannelRequest, opts ...gax.CallOption) (*monitoringpb.NotificationChannel, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateNotificationChannel[0:len(c.CallOptions.CreateNotificationChannel):len(c.CallOptions.CreateNotificationChannel)], opts...) + var resp *monitoringpb.NotificationChannel + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.notificationChannelClient.CreateNotificationChannel(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateNotificationChannel updates a notification channel. Fields not specified in the field mask +// remain unchanged. +func (c *NotificationChannelClient) UpdateNotificationChannel(ctx context.Context, req *monitoringpb.UpdateNotificationChannelRequest, opts ...gax.CallOption) (*monitoringpb.NotificationChannel, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateNotificationChannel[0:len(c.CallOptions.UpdateNotificationChannel):len(c.CallOptions.UpdateNotificationChannel)], opts...) + var resp *monitoringpb.NotificationChannel + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.notificationChannelClient.UpdateNotificationChannel(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteNotificationChannel deletes a notification channel. +func (c *NotificationChannelClient) DeleteNotificationChannel(ctx context.Context, req *monitoringpb.DeleteNotificationChannelRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteNotificationChannel[0:len(c.CallOptions.DeleteNotificationChannel):len(c.CallOptions.DeleteNotificationChannel)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.notificationChannelClient.DeleteNotificationChannel(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// NotificationChannelDescriptorIterator manages a stream of *monitoringpb.NotificationChannelDescriptor. +type NotificationChannelDescriptorIterator struct { + items []*monitoringpb.NotificationChannelDescriptor + 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 []*monitoringpb.NotificationChannelDescriptor, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *NotificationChannelDescriptorIterator) 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 *NotificationChannelDescriptorIterator) Next() (*monitoringpb.NotificationChannelDescriptor, error) { + var item *monitoringpb.NotificationChannelDescriptor + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *NotificationChannelDescriptorIterator) bufLen() int { + return len(it.items) +} + +func (it *NotificationChannelDescriptorIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// NotificationChannelIterator manages a stream of *monitoringpb.NotificationChannel. +type NotificationChannelIterator struct { + items []*monitoringpb.NotificationChannel + 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 []*monitoringpb.NotificationChannel, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *NotificationChannelIterator) 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 *NotificationChannelIterator) Next() (*monitoringpb.NotificationChannel, error) { + var item *monitoringpb.NotificationChannel + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *NotificationChannelIterator) bufLen() int { + return len(it.items) +} + +func (it *NotificationChannelIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/notification_channel_client_example_test.go b/vendor/cloud.google.com/go/monitoring/apiv3/notification_channel_client_example_test.go new file mode 100644 index 0000000000..eab1179032 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/notification_channel_client_example_test.go @@ -0,0 +1,170 @@ +// 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 monitoring_test + +import ( + "cloud.google.com/go/monitoring/apiv3" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +func ExampleNewNotificationChannelClient() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleNotificationChannelClient_ListNotificationChannelDescriptors() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListNotificationChannelDescriptorsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListNotificationChannelDescriptors(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleNotificationChannelClient_GetNotificationChannelDescriptor() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.GetNotificationChannelDescriptorRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetNotificationChannelDescriptor(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleNotificationChannelClient_ListNotificationChannels() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListNotificationChannelsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListNotificationChannels(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleNotificationChannelClient_GetNotificationChannel() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.GetNotificationChannelRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetNotificationChannel(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleNotificationChannelClient_CreateNotificationChannel() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.CreateNotificationChannelRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateNotificationChannel(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleNotificationChannelClient_UpdateNotificationChannel() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.UpdateNotificationChannelRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateNotificationChannel(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleNotificationChannelClient_DeleteNotificationChannel() { + ctx := context.Background() + c, err := monitoring.NewNotificationChannelClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.DeleteNotificationChannelRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteNotificationChannel(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/path_funcs.go b/vendor/cloud.google.com/go/monitoring/apiv3/path_funcs.go new file mode 100644 index 0000000000..b2b514ba52 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/path_funcs.go @@ -0,0 +1,107 @@ +// 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. + +package monitoring + +// GroupProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func GroupProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// GroupGroupPath returns the path for the group resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/groups/%s", project, group) +// instead. +func GroupGroupPath(project, group string) string { + return "" + + "projects/" + + project + + "/groups/" + + group + + "" +} + +// MetricProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func MetricProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// MetricMetricDescriptorPath returns the path for the metric descriptor resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/metricDescriptors/%s", project, metricDescriptor) +// instead. +func MetricMetricDescriptorPath(project, metricDescriptor string) string { + return "" + + "projects/" + + project + + "/metricDescriptors/" + + metricDescriptor + + "" +} + +// MetricMonitoredResourceDescriptorPath returns the path for the monitored resource descriptor resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/monitoredResourceDescriptors/%s", project, monitoredResourceDescriptor) +// instead. +func MetricMonitoredResourceDescriptorPath(project, monitoredResourceDescriptor string) string { + return "" + + "projects/" + + project + + "/monitoredResourceDescriptors/" + + monitoredResourceDescriptor + + "" +} + +// UptimeCheckProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func UptimeCheckProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// UptimeCheckUptimeCheckConfigPath returns the path for the uptime check config resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/uptimeCheckConfigs/%s", project, uptimeCheckConfig) +// instead. +func UptimeCheckUptimeCheckConfigPath(project, uptimeCheckConfig string) string { + return "" + + "projects/" + + project + + "/uptimeCheckConfigs/" + + uptimeCheckConfig + + "" +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/uptime_check_client.go b/vendor/cloud.google.com/go/monitoring/apiv3/uptime_check_client.go new file mode 100644 index 0000000000..edbb1165e1 --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/uptime_check_client.go @@ -0,0 +1,362 @@ +// 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 monitoring + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// UptimeCheckCallOptions contains the retry settings for each method of UptimeCheckClient. +type UptimeCheckCallOptions struct { + ListUptimeCheckConfigs []gax.CallOption + GetUptimeCheckConfig []gax.CallOption + CreateUptimeCheckConfig []gax.CallOption + UpdateUptimeCheckConfig []gax.CallOption + DeleteUptimeCheckConfig []gax.CallOption + ListUptimeCheckIps []gax.CallOption +} + +func defaultUptimeCheckClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("monitoring.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultUptimeCheckCallOptions() *UptimeCheckCallOptions { + 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 &UptimeCheckCallOptions{ + ListUptimeCheckConfigs: retry[[2]string{"default", "idempotent"}], + GetUptimeCheckConfig: retry[[2]string{"default", "idempotent"}], + CreateUptimeCheckConfig: retry[[2]string{"default", "non_idempotent"}], + UpdateUptimeCheckConfig: retry[[2]string{"default", "non_idempotent"}], + DeleteUptimeCheckConfig: retry[[2]string{"default", "idempotent"}], + ListUptimeCheckIps: retry[[2]string{"default", "idempotent"}], + } +} + +// UptimeCheckClient is a client for interacting with Stackdriver Monitoring API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type UptimeCheckClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + uptimeCheckClient monitoringpb.UptimeCheckServiceClient + + // The call options for this service. + CallOptions *UptimeCheckCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewUptimeCheckClient creates a new uptime check service client. +// +// The UptimeCheckService API is used to manage (list, create, delete, edit) +// uptime check configurations in the Stackdriver Monitoring product. An uptime +// check is a piece of configuration that determines which resources and +// services to monitor for availability. These configurations can also be +// configured interactively by navigating to the [Cloud Console] +// (http://console.cloud.google.com), selecting the appropriate project, +// clicking on "Monitoring" on the left-hand side to navigate to Stackdriver, +// and then clicking on "Uptime". +func NewUptimeCheckClient(ctx context.Context, opts ...option.ClientOption) (*UptimeCheckClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultUptimeCheckClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &UptimeCheckClient{ + conn: conn, + CallOptions: defaultUptimeCheckCallOptions(), + + uptimeCheckClient: monitoringpb.NewUptimeCheckServiceClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *UptimeCheckClient) 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 *UptimeCheckClient) 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 *UptimeCheckClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListUptimeCheckConfigs lists the existing valid uptime check configurations for the project, +// leaving out any invalid configurations. +func (c *UptimeCheckClient) ListUptimeCheckConfigs(ctx context.Context, req *monitoringpb.ListUptimeCheckConfigsRequest, opts ...gax.CallOption) *UptimeCheckConfigIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListUptimeCheckConfigs[0:len(c.CallOptions.ListUptimeCheckConfigs):len(c.CallOptions.ListUptimeCheckConfigs)], opts...) + it := &UptimeCheckConfigIterator{} + req = proto.Clone(req).(*monitoringpb.ListUptimeCheckConfigsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoringpb.UptimeCheckConfig, string, error) { + var resp *monitoringpb.ListUptimeCheckConfigsResponse + 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.uptimeCheckClient.ListUptimeCheckConfigs(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.UptimeCheckConfigs, 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 +} + +// GetUptimeCheckConfig gets a single uptime check configuration. +func (c *UptimeCheckClient) GetUptimeCheckConfig(ctx context.Context, req *monitoringpb.GetUptimeCheckConfigRequest, opts ...gax.CallOption) (*monitoringpb.UptimeCheckConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetUptimeCheckConfig[0:len(c.CallOptions.GetUptimeCheckConfig):len(c.CallOptions.GetUptimeCheckConfig)], opts...) + var resp *monitoringpb.UptimeCheckConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.uptimeCheckClient.GetUptimeCheckConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateUptimeCheckConfig creates a new uptime check configuration. +func (c *UptimeCheckClient) CreateUptimeCheckConfig(ctx context.Context, req *monitoringpb.CreateUptimeCheckConfigRequest, opts ...gax.CallOption) (*monitoringpb.UptimeCheckConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateUptimeCheckConfig[0:len(c.CallOptions.CreateUptimeCheckConfig):len(c.CallOptions.CreateUptimeCheckConfig)], opts...) + var resp *monitoringpb.UptimeCheckConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.uptimeCheckClient.CreateUptimeCheckConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateUptimeCheckConfig updates an uptime check configuration. You can either replace the entire +// configuration with a new one or replace only certain fields in the current +// configuration by specifying the fields to be updated via "updateMask". +// Returns the updated configuration. +func (c *UptimeCheckClient) UpdateUptimeCheckConfig(ctx context.Context, req *monitoringpb.UpdateUptimeCheckConfigRequest, opts ...gax.CallOption) (*monitoringpb.UptimeCheckConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateUptimeCheckConfig[0:len(c.CallOptions.UpdateUptimeCheckConfig):len(c.CallOptions.UpdateUptimeCheckConfig)], opts...) + var resp *monitoringpb.UptimeCheckConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.uptimeCheckClient.UpdateUptimeCheckConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteUptimeCheckConfig deletes an uptime check configuration. Note that this method will fail +// if the uptime check configuration is referenced by an alert policy or +// other dependent configs that would be rendered invalid by the deletion. +func (c *UptimeCheckClient) DeleteUptimeCheckConfig(ctx context.Context, req *monitoringpb.DeleteUptimeCheckConfigRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteUptimeCheckConfig[0:len(c.CallOptions.DeleteUptimeCheckConfig):len(c.CallOptions.DeleteUptimeCheckConfig)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.uptimeCheckClient.DeleteUptimeCheckConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListUptimeCheckIps returns the list of IPs that checkers run from +func (c *UptimeCheckClient) ListUptimeCheckIps(ctx context.Context, req *monitoringpb.ListUptimeCheckIpsRequest, opts ...gax.CallOption) *UptimeCheckIpIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListUptimeCheckIps[0:len(c.CallOptions.ListUptimeCheckIps):len(c.CallOptions.ListUptimeCheckIps)], opts...) + it := &UptimeCheckIpIterator{} + req = proto.Clone(req).(*monitoringpb.ListUptimeCheckIpsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoringpb.UptimeCheckIp, string, error) { + var resp *monitoringpb.ListUptimeCheckIpsResponse + 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.uptimeCheckClient.ListUptimeCheckIps(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.UptimeCheckIps, 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 +} + +// UptimeCheckConfigIterator manages a stream of *monitoringpb.UptimeCheckConfig. +type UptimeCheckConfigIterator struct { + items []*monitoringpb.UptimeCheckConfig + 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 []*monitoringpb.UptimeCheckConfig, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *UptimeCheckConfigIterator) 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 *UptimeCheckConfigIterator) Next() (*monitoringpb.UptimeCheckConfig, error) { + var item *monitoringpb.UptimeCheckConfig + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *UptimeCheckConfigIterator) bufLen() int { + return len(it.items) +} + +func (it *UptimeCheckConfigIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// UptimeCheckIpIterator manages a stream of *monitoringpb.UptimeCheckIp. +type UptimeCheckIpIterator struct { + items []*monitoringpb.UptimeCheckIp + 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 []*monitoringpb.UptimeCheckIp, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *UptimeCheckIpIterator) 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 *UptimeCheckIpIterator) Next() (*monitoringpb.UptimeCheckIp, error) { + var item *monitoringpb.UptimeCheckIp + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *UptimeCheckIpIterator) bufLen() int { + return len(it.items) +} + +func (it *UptimeCheckIpIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/monitoring/apiv3/uptime_check_client_example_test.go b/vendor/cloud.google.com/go/monitoring/apiv3/uptime_check_client_example_test.go new file mode 100644 index 0000000000..49ee0d0dec --- /dev/null +++ b/vendor/cloud.google.com/go/monitoring/apiv3/uptime_check_client_example_test.go @@ -0,0 +1,152 @@ +// 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 monitoring_test + +import ( + "cloud.google.com/go/monitoring/apiv3" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +func ExampleNewUptimeCheckClient() { + ctx := context.Background() + c, err := monitoring.NewUptimeCheckClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleUptimeCheckClient_ListUptimeCheckConfigs() { + ctx := context.Background() + c, err := monitoring.NewUptimeCheckClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListUptimeCheckConfigsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListUptimeCheckConfigs(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleUptimeCheckClient_GetUptimeCheckConfig() { + ctx := context.Background() + c, err := monitoring.NewUptimeCheckClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.GetUptimeCheckConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetUptimeCheckConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleUptimeCheckClient_CreateUptimeCheckConfig() { + ctx := context.Background() + c, err := monitoring.NewUptimeCheckClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.CreateUptimeCheckConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateUptimeCheckConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleUptimeCheckClient_UpdateUptimeCheckConfig() { + ctx := context.Background() + c, err := monitoring.NewUptimeCheckClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.UpdateUptimeCheckConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateUptimeCheckConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleUptimeCheckClient_DeleteUptimeCheckConfig() { + ctx := context.Background() + c, err := monitoring.NewUptimeCheckClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.DeleteUptimeCheckConfigRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteUptimeCheckConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleUptimeCheckClient_ListUptimeCheckIps() { + ctx := context.Background() + c, err := monitoring.NewUptimeCheckClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &monitoringpb.ListUptimeCheckIpsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListUptimeCheckIps(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/old-news.md b/vendor/cloud.google.com/go/old-news.md new file mode 100644 index 0000000000..6a8b84d7fb --- /dev/null +++ b/vendor/cloud.google.com/go/old-news.md @@ -0,0 +1,698 @@ +_February 26, 2018_ + +*v0.19.0* + +- bigquery: + - Support customer-managed encryption keys. + +- bigtable: + - Improved emulator support. + - Support GetCluster. + +- datastore: + - Add general mutations. + - Support pointer struct fields. + - Support transaction options. + +- firestore: + - Add Transaction.GetAll. + - Support document cursors. + +- logging: + - Support concurrent RPCs to the service. + - Support per-entry resources. + +- profiler: + - Add config options to disable heap and thread profiling. + - Read the project ID from $GOOGLE_CLOUD_PROJECT when it's set. + +- pubsub: + - BEHAVIOR CHANGE: Release flow control after ack/nack (instead of after the + callback returns). + - Add SubscriptionInProject. + - Add OpenCensus instrumentation for streaming pull. + +- storage: + - Support CORS. + + +_January 18, 2018_ + +*v0.18.0* + +- bigquery: + - Marked stable. + - Schema inference of nullable fields supported. + - Added TimePartitioning to QueryConfig. + +- firestore: Data provided to DocumentRef.Set with a Merge option can contain + Delete sentinels. + +- logging: Clients can accept parent resources other than projects. + +- pubsub: + - pubsub/pstest: A lighweight fake for pubsub. Experimental; feedback welcome. + - Support updating more subscription metadata: AckDeadline, + RetainAckedMessages and RetentionDuration. + +- oslogin/apiv1beta: New client for the Cloud OS Login API. + +- rpcreplay: A package for recording and replaying gRPC traffic. + +- spanner: + - Add a ReadWithOptions that supports a row limit, as well as an index. + - Support query plan and execution statistics. + - Added [OpenCensus](http://opencensus.io) support. + +- storage: Clarify checksum validation for gzipped files (it is not validated + when the file is served uncompressed). + + +_December 11, 2017_ + +*v0.17.0* + +- firestore BREAKING CHANGES: + - Remove UpdateMap and UpdateStruct; rename UpdatePaths to Update. + Change + `docref.UpdateMap(ctx, map[string]interface{}{"a.b", 1})` + to + `docref.Update(ctx, []firestore.Update{{Path: "a.b", Value: 1}})` + + Change + `docref.UpdateStruct(ctx, []string{"Field"}, aStruct)` + to + `docref.Update(ctx, []firestore.Update{{Path: "Field", Value: aStruct.Field}})` + - Rename MergePaths to Merge; require args to be FieldPaths + - A value stored as an integer can be read into a floating-point field, and vice versa. +- bigtable/cmd/cbt: + - Support deleting a column. + - Add regex option for row read. +- spanner: Mark stable. +- storage: + - Add Reader.ContentEncoding method. + - Fix handling of SignedURL headers. +- bigquery: + - If Uploader.Put is called with no rows, it returns nil without making a + call. + - Schema inference supports the "nullable" option in struct tags for + non-required fields. + - TimePartitioning supports "Field". + + +_October 30, 2017_ + +*v0.16.0* + +- Other bigquery changes: + - `JobIterator.Next` returns `*Job`; removed `JobInfo` (BREAKING CHANGE). + - UseStandardSQL is deprecated; set UseLegacySQL to true if you need + Legacy SQL. + - Uploader.Put will generate a random insert ID if you do not provide one. + - Support time partitioning for load jobs. + - Support dry-run queries. + - A `Job` remembers its last retrieved status. + - Support retrieving job configuration. + - Support labels for jobs and tables. + - Support dataset access lists. + - Improve support for external data sources, including data from Bigtable and + Google Sheets, and tables with external data. + - Support updating a table's view configuration. + - Fix uploading civil times with nanoseconds. + +- storage: + - Support PubSub notifications. + - Support Requester Pays buckets. + +- profiler: Support goroutine and mutex profile types. + + +_October 3, 2017_ + +*v0.15.0* + +- firestore: beta release. See the + [announcement](https://firebase.googleblog.com/2017/10/introducing-cloud-firestore.html). + +- errorreporting: The existing package has been redesigned. + +- errors: This package has been removed. Use errorreporting. + + +_September 28, 2017_ + +*v0.14.0* + +- bigquery BREAKING CHANGES: + - Standard SQL is the default for queries and views. + - `Table.Create` takes `TableMetadata` as a second argument, instead of + options. + - `Dataset.Create` takes `DatasetMetadata` as a second argument. + - `DatasetMetadata` field `ID` renamed to `FullID` + - `TableMetadata` field `ID` renamed to `FullID` + +- Other bigquery changes: + - The client will append a random suffix to a provided job ID if you set + `AddJobIDSuffix` to true in a job config. + - Listing jobs is supported. + - Better retry logic. + +- vision, language, speech: clients are now stable + +- monitoring: client is now beta + +- profiler: + - Rename InstanceName to Instance, ZoneName to Zone + - Auto-detect service name and version on AppEngine. + +_September 8, 2017_ + +*v0.13.0* + +- bigquery: UseLegacySQL options for CreateTable and QueryConfig. Use these + options to continue using Legacy SQL after the client switches its default + to Standard SQL. + +- bigquery: Support for updating dataset labels. + +- bigquery: Set DatasetIterator.ProjectID to list datasets in a project other + than the client's. DatasetsInProject is no longer needed and is deprecated. + +- bigtable: Fail ListInstances when any zones fail. + +- spanner: support decoding of slices of basic types (e.g. []string, []int64, + etc.) + +- logging/logadmin: UpdateSink no longer creates a sink if it is missing + (actually a change to the underlying service, not the client) + +- profiler: Service and ServiceVersion replace Target in Config. + +_August 22, 2017_ + +*v0.12.0* + +- pubsub: Subscription.Receive now uses streaming pull. + +- pubsub: add Client.TopicInProject to access topics in a different project + than the client. + +- errors: renamed errorreporting. The errors package will be removed shortly. + +- datastore: improved retry behavior. + +- bigquery: support updates to dataset metadata, with etags. + +- bigquery: add etag support to Table.Update (BREAKING: etag argument added). + +- bigquery: generate all job IDs on the client. + +- storage: support bucket lifecycle configurations. + + +_July 31, 2017_ + +*v0.11.0* + +- Clients for spanner, pubsub and video are now in beta. + +- New client for DLP. + +- spanner: performance and testing improvements. + +- storage: requester-pays buckets are supported. + +- storage, profiler, bigtable, bigquery: bug fixes and other minor improvements. + +- pubsub: bug fixes and other minor improvements + +_June 17, 2017_ + + +*v0.10.0* + +- pubsub: Subscription.ModifyPushConfig replaced with Subscription.Update. + +- pubsub: Subscription.Receive now runs concurrently for higher throughput. + +- vision: cloud.google.com/go/vision is deprecated. Use +cloud.google.com/go/vision/apiv1 instead. + +- translation: now stable. + +- trace: several changes to the surface. See the link below. + +[Code changes required from v0.9.0.](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/MIGRATION.md) + + +_March 17, 2017_ + +Breaking Pubsub changes. +* Publish is now asynchronous +([announcement](https://groups.google.com/d/topic/google-api-go-announce/aaqRDIQ3rvU/discussion)). +* Subscription.Pull replaced by Subscription.Receive, which takes a callback ([announcement](https://groups.google.com/d/topic/google-api-go-announce/8pt6oetAdKc/discussion)). +* Message.Done replaced with Message.Ack and Message.Nack. + +_February 14, 2017_ + +Release of a client library for Spanner. See +the +[blog post](https://cloudplatform.googleblog.com/2017/02/introducing-Cloud-Spanner-a-global-database-service-for-mission-critical-applications.html). + +Note that although the Spanner service is beta, the Go client library is alpha. + +_December 12, 2016_ + +Beta release of BigQuery, DataStore, Logging and Storage. See the +[blog post](https://cloudplatform.googleblog.com/2016/12/announcing-new-google-cloud-client.html). + +Also, BigQuery now supports structs. Read a row directly into a struct with +`RowIterator.Next`, and upload a row directly from a struct with `Uploader.Put`. +You can also use field tags. See the [package documentation][cloud-bigquery-ref] +for details. + +_December 5, 2016_ + +More changes to BigQuery: + +* The `ValueList` type was removed. It is no longer necessary. Instead of + ```go + var v ValueList + ... it.Next(&v) .. + ``` + use + + ```go + var v []Value + ... it.Next(&v) ... + ``` + +* Previously, repeatedly calling `RowIterator.Next` on the same `[]Value` or + `ValueList` would append to the slice. Now each call resets the size to zero first. + +* Schema inference will infer the SQL type BYTES for a struct field of + type []byte. Previously it inferred STRING. + +* The types `uint`, `uint64` and `uintptr` are no longer supported in schema + inference. BigQuery's integer type is INT64, and those types may hold values + that are not correctly represented in a 64-bit signed integer. + +* The SQL types DATE, TIME and DATETIME are now supported. They correspond to + the `Date`, `Time` and `DateTime` types in the new `cloud.google.com/go/civil` + package. + +_November 17, 2016_ + +Change to BigQuery: values from INTEGER columns will now be returned as int64, +not int. This will avoid errors arising from large values on 32-bit systems. + +_November 8, 2016_ + +New datastore feature: datastore now encodes your nested Go structs as Entity values, +instead of a flattened list of the embedded struct's fields. +This means that you may now have twice-nested slices, eg. +```go +type State struct { + Cities []struct{ + Populations []int + } +} +``` + +See [the announcement](https://groups.google.com/forum/#!topic/google-api-go-announce/79jtrdeuJAg) for +more details. + +_November 8, 2016_ + +Breaking changes to datastore: contexts no longer hold namespaces; instead you +must set a key's namespace explicitly. Also, key functions have been changed +and renamed. + +* The WithNamespace function has been removed. To specify a namespace in a Query, use the Query.Namespace method: + ```go + q := datastore.NewQuery("Kind").Namespace("ns") + ``` + +* All the fields of Key are exported. That means you can construct any Key with a struct literal: + ```go + k := &Key{Kind: "Kind", ID: 37, Namespace: "ns"} + ``` + +* As a result of the above, the Key methods Kind, ID, d.Name, Parent, SetParent and Namespace have been removed. + +* `NewIncompleteKey` has been removed, replaced by `IncompleteKey`. Replace + ```go + NewIncompleteKey(ctx, kind, parent) + ``` + with + ```go + IncompleteKey(kind, parent) + ``` + and if you do use namespaces, make sure you set the namespace on the returned key. + +* `NewKey` has been removed, replaced by `NameKey` and `IDKey`. Replace + ```go + NewKey(ctx, kind, name, 0, parent) + NewKey(ctx, kind, "", id, parent) + ``` + with + ```go + NameKey(kind, name, parent) + IDKey(kind, id, parent) + ``` + and if you do use namespaces, make sure you set the namespace on the returned key. + +* The `Done` variable has been removed. Replace `datastore.Done` with `iterator.Done`, from the package `google.golang.org/api/iterator`. + +* The `Client.Close` method will have a return type of error. It will return the result of closing the underlying gRPC connection. + +See [the announcement](https://groups.google.com/forum/#!topic/google-api-go-announce/hqXtM_4Ix-0) for +more details. + +_October 27, 2016_ + +Breaking change to bigquery: `NewGCSReference` is now a function, +not a method on `Client`. + +New bigquery feature: `Table.LoaderFrom` now accepts a `ReaderSource`, enabling +loading data into a table from a file or any `io.Reader`. + +_October 21, 2016_ + +Breaking change to pubsub: removed `pubsub.Done`. + +Use `iterator.Done` instead, where `iterator` is the package +`google.golang.org/api/iterator`. + +_October 19, 2016_ + +Breaking changes to cloud.google.com/go/bigquery: + +* Client.Table and Client.OpenTable have been removed. + Replace + ```go + client.OpenTable("project", "dataset", "table") + ``` + with + ```go + client.DatasetInProject("project", "dataset").Table("table") + ``` + +* Client.CreateTable has been removed. + Replace + ```go + client.CreateTable(ctx, "project", "dataset", "table") + ``` + with + ```go + client.DatasetInProject("project", "dataset").Table("table").Create(ctx) + ``` + +* Dataset.ListTables have been replaced with Dataset.Tables. + Replace + ```go + tables, err := ds.ListTables(ctx) + ``` + with + ```go + it := ds.Tables(ctx) + for { + table, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: use table. + } + ``` + +* Client.Read has been replaced with Job.Read, Table.Read and Query.Read. + Replace + ```go + it, err := client.Read(ctx, job) + ``` + with + ```go + it, err := job.Read(ctx) + ``` + and similarly for reading from tables or queries. + +* The iterator returned from the Read methods is now named RowIterator. Its + behavior is closer to the other iterators in these libraries. It no longer + supports the Schema method; see the next item. + Replace + ```go + for it.Next(ctx) { + var vals ValueList + if err := it.Get(&vals); err != nil { + // TODO: Handle error. + } + // TODO: use vals. + } + if err := it.Err(); err != nil { + // TODO: Handle error. + } + ``` + with + ``` + for { + var vals ValueList + err := it.Next(&vals) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: use vals. + } + ``` + Instead of the `RecordsPerRequest(n)` option, write + ```go + it.PageInfo().MaxSize = n + ``` + Instead of the `StartIndex(i)` option, write + ```go + it.StartIndex = i + ``` + +* ValueLoader.Load now takes a Schema in addition to a slice of Values. + Replace + ```go + func (vl *myValueLoader) Load(v []bigquery.Value) + ``` + with + ```go + func (vl *myValueLoader) Load(v []bigquery.Value, s bigquery.Schema) + ``` + + +* Table.Patch is replace by Table.Update. + Replace + ```go + p := table.Patch() + p.Description("new description") + metadata, err := p.Apply(ctx) + ``` + with + ```go + metadata, err := table.Update(ctx, bigquery.TableMetadataToUpdate{ + Description: "new description", + }) + ``` + +* Client.Copy is replaced by separate methods for each of its four functions. + All options have been replaced by struct fields. + + * To load data from Google Cloud Storage into a table, use Table.LoaderFrom. + + Replace + ```go + client.Copy(ctx, table, gcsRef) + ``` + with + ```go + table.LoaderFrom(gcsRef).Run(ctx) + ``` + Instead of passing options to Copy, set fields on the Loader: + ```go + loader := table.LoaderFrom(gcsRef) + loader.WriteDisposition = bigquery.WriteTruncate + ``` + + * To extract data from a table into Google Cloud Storage, use + Table.ExtractorTo. Set fields on the returned Extractor instead of + passing options. + + Replace + ```go + client.Copy(ctx, gcsRef, table) + ``` + with + ```go + table.ExtractorTo(gcsRef).Run(ctx) + ``` + + * To copy data into a table from one or more other tables, use + Table.CopierFrom. Set fields on the returned Copier instead of passing options. + + Replace + ```go + client.Copy(ctx, dstTable, srcTable) + ``` + with + ```go + dst.Table.CopierFrom(srcTable).Run(ctx) + ``` + + * To start a query job, create a Query and call its Run method. Set fields + on the query instead of passing options. + + Replace + ```go + client.Copy(ctx, table, query) + ``` + with + ```go + query.Run(ctx) + ``` + +* Table.NewUploader has been renamed to Table.Uploader. Instead of options, + configure an Uploader by setting its fields. + Replace + ```go + u := table.NewUploader(bigquery.UploadIgnoreUnknownValues()) + ``` + with + ```go + u := table.NewUploader(bigquery.UploadIgnoreUnknownValues()) + u.IgnoreUnknownValues = true + ``` + +_October 10, 2016_ + +Breaking changes to cloud.google.com/go/storage: + +* AdminClient replaced by methods on Client. + Replace + ```go + adminClient.CreateBucket(ctx, bucketName, attrs) + ``` + with + ```go + client.Bucket(bucketName).Create(ctx, projectID, attrs) + ``` + +* BucketHandle.List replaced by BucketHandle.Objects. + Replace + ```go + for query != nil { + objs, err := bucket.List(d.ctx, query) + if err != nil { ... } + query = objs.Next + for _, obj := range objs.Results { + fmt.Println(obj) + } + } + ``` + with + ```go + iter := bucket.Objects(d.ctx, query) + for { + obj, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { ... } + fmt.Println(obj) + } + ``` + (The `iterator` package is at `google.golang.org/api/iterator`.) + + Replace `Query.Cursor` with `ObjectIterator.PageInfo().Token`. + + Replace `Query.MaxResults` with `ObjectIterator.PageInfo().MaxSize`. + + +* ObjectHandle.CopyTo replaced by ObjectHandle.CopierFrom. + Replace + ```go + attrs, err := src.CopyTo(ctx, dst, nil) + ``` + with + ```go + attrs, err := dst.CopierFrom(src).Run(ctx) + ``` + + Replace + ```go + attrs, err := src.CopyTo(ctx, dst, &storage.ObjectAttrs{ContextType: "text/html"}) + ``` + with + ```go + c := dst.CopierFrom(src) + c.ContextType = "text/html" + attrs, err := c.Run(ctx) + ``` + +* ObjectHandle.ComposeFrom replaced by ObjectHandle.ComposerFrom. + Replace + ```go + attrs, err := dst.ComposeFrom(ctx, []*storage.ObjectHandle{src1, src2}, nil) + ``` + with + ```go + attrs, err := dst.ComposerFrom(src1, src2).Run(ctx) + ``` + +* ObjectHandle.Update's ObjectAttrs argument replaced by ObjectAttrsToUpdate. + Replace + ```go + attrs, err := obj.Update(ctx, &storage.ObjectAttrs{ContextType: "text/html"}) + ``` + with + ```go + attrs, err := obj.Update(ctx, storage.ObjectAttrsToUpdate{ContextType: "text/html"}) + ``` + +* ObjectHandle.WithConditions replaced by ObjectHandle.If. + Replace + ```go + obj.WithConditions(storage.Generation(gen), storage.IfMetaGenerationMatch(mgen)) + ``` + with + ```go + obj.Generation(gen).If(storage.Conditions{MetagenerationMatch: mgen}) + ``` + + Replace + ```go + obj.WithConditions(storage.IfGenerationMatch(0)) + ``` + with + ```go + obj.If(storage.Conditions{DoesNotExist: true}) + ``` + +* `storage.Done` replaced by `iterator.Done` (from package `google.golang.org/api/iterator`). + +_October 6, 2016_ + +Package preview/logging deleted. Use logging instead. + +_September 27, 2016_ + +Logging client replaced with preview version (see below). + +_September 8, 2016_ + +* New clients for some of Google's Machine Learning APIs: Vision, Speech, and +Natural Language. + +* Preview version of a new [Stackdriver Logging][cloud-logging] client in +[`cloud.google.com/go/preview/logging`](https://godoc.org/cloud.google.com/go/preview/logging). +This client uses gRPC as its transport layer, and supports log reading, sinks +and metrics. It will replace the current client at `cloud.google.com/go/logging` shortly. + diff --git a/vendor/cloud.google.com/go/oslogin/apiv1/doc.go b/vendor/cloud.google.com/go/oslogin/apiv1/doc.go new file mode 100644 index 0000000000..d6693c13f9 --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1/doc.go @@ -0,0 +1,49 @@ +// 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 oslogin is an auto-generated package for the +// Google Cloud OS Login API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Manages OS login configuration for Google account users. +package oslogin // import "cloud.google.com/go/oslogin/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/compute.readonly", + } +} diff --git a/vendor/cloud.google.com/go/oslogin/apiv1/mock_test.go b/vendor/cloud.google.com/go/oslogin/apiv1/mock_test.go new file mode 100644 index 0000000000..da0780752b --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1/mock_test.go @@ -0,0 +1,520 @@ +// 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 oslogin + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + commonpb "google.golang.org/genproto/googleapis/cloud/oslogin/common" + osloginpb "google.golang.org/genproto/googleapis/cloud/oslogin/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockOsLoginServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + osloginpb.OsLoginServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockOsLoginServer) DeletePosixAccount(ctx context.Context, req *osloginpb.DeletePosixAccountRequest) (*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 *mockOsLoginServer) DeleteSshPublicKey(ctx context.Context, req *osloginpb.DeleteSshPublicKeyRequest) (*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 *mockOsLoginServer) GetLoginProfile(ctx context.Context, req *osloginpb.GetLoginProfileRequest) (*osloginpb.LoginProfile, 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].(*osloginpb.LoginProfile), nil +} + +func (s *mockOsLoginServer) GetSshPublicKey(ctx context.Context, req *osloginpb.GetSshPublicKeyRequest) (*commonpb.SshPublicKey, 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].(*commonpb.SshPublicKey), nil +} + +func (s *mockOsLoginServer) ImportSshPublicKey(ctx context.Context, req *osloginpb.ImportSshPublicKeyRequest) (*osloginpb.ImportSshPublicKeyResponse, 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].(*osloginpb.ImportSshPublicKeyResponse), nil +} + +func (s *mockOsLoginServer) UpdateSshPublicKey(ctx context.Context, req *osloginpb.UpdateSshPublicKeyRequest) (*commonpb.SshPublicKey, 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].(*commonpb.SshPublicKey), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockOsLogin mockOsLoginServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + osloginpb.RegisterOsLoginServiceServer(serv, &mockOsLogin) + + 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 TestOsLoginServiceDeletePosixAccount(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/projects/%s", "[USER]", "[PROJECT]") + var request = &osloginpb.DeletePosixAccountRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeletePosixAccount(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestOsLoginServiceDeletePosixAccountError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/projects/%s", "[USER]", "[PROJECT]") + var request = &osloginpb.DeletePosixAccountRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeletePosixAccount(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 TestOsLoginServiceDeleteSshPublicKey(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.DeleteSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestOsLoginServiceDeleteSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.DeleteSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSshPublicKey(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 TestOsLoginServiceGetLoginProfile(t *testing.T) { + var name2 string = "name2-1052831874" + var suspended bool = false + var expectedResponse = &osloginpb.LoginProfile{ + Name: name2, + Suspended: suspended, + } + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s", "[USER]") + var request = &osloginpb.GetLoginProfileRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetLoginProfile(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceGetLoginProfileError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s", "[USER]") + var request = &osloginpb.GetLoginProfileRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetLoginProfile(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 TestOsLoginServiceGetSshPublicKey(t *testing.T) { + var key string = "key106079" + var expirationTimeUsec int64 = 2058878882 + var fingerprint string = "fingerprint-1375934236" + var expectedResponse = &commonpb.SshPublicKey{ + Key: key, + ExpirationTimeUsec: expirationTimeUsec, + Fingerprint: fingerprint, + } + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.GetSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceGetSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.GetSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSshPublicKey(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 TestOsLoginServiceImportSshPublicKey(t *testing.T) { + var expectedResponse *osloginpb.ImportSshPublicKeyResponse = &osloginpb.ImportSshPublicKeyResponse{} + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("users/%s", "[USER]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.ImportSshPublicKeyRequest{ + Parent: formattedParent, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ImportSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceImportSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("users/%s", "[USER]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.ImportSshPublicKeyRequest{ + Parent: formattedParent, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ImportSshPublicKey(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 TestOsLoginServiceUpdateSshPublicKey(t *testing.T) { + var key string = "key106079" + var expirationTimeUsec int64 = 2058878882 + var fingerprint string = "fingerprint-1375934236" + var expectedResponse = &commonpb.SshPublicKey{ + Key: key, + ExpirationTimeUsec: expirationTimeUsec, + Fingerprint: fingerprint, + } + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.UpdateSshPublicKeyRequest{ + Name: formattedName, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceUpdateSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.UpdateSshPublicKeyRequest{ + Name: formattedName, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSshPublicKey(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/vendor/cloud.google.com/go/oslogin/apiv1/os_login_client.go b/vendor/cloud.google.com/go/oslogin/apiv1/os_login_client.go new file mode 100644 index 0000000000..b25431b0da --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1/os_login_client.go @@ -0,0 +1,224 @@ +// 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 oslogin + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + commonpb "google.golang.org/genproto/googleapis/cloud/oslogin/common" + osloginpb "google.golang.org/genproto/googleapis/cloud/oslogin/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 { + DeletePosixAccount []gax.CallOption + DeleteSshPublicKey []gax.CallOption + GetLoginProfile []gax.CallOption + GetSshPublicKey []gax.CallOption + ImportSshPublicKey []gax.CallOption + UpdateSshPublicKey []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("oslogin.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{ + DeletePosixAccount: retry[[2]string{"default", "idempotent"}], + DeleteSshPublicKey: retry[[2]string{"default", "idempotent"}], + GetLoginProfile: retry[[2]string{"default", "idempotent"}], + GetSshPublicKey: retry[[2]string{"default", "idempotent"}], + ImportSshPublicKey: retry[[2]string{"default", "idempotent"}], + UpdateSshPublicKey: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud OS Login 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 osloginpb.OsLoginServiceClient + + // 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 os login service client. +// +// Cloud OS Login API +// +// The Cloud OS Login API allows you to manage users and their associated SSH +// public keys for logging into virtual machines on Google Cloud Platform. +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: osloginpb.NewOsLoginServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// DeletePosixAccount deletes a POSIX account. +func (c *Client) DeletePosixAccount(ctx context.Context, req *osloginpb.DeletePosixAccountRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeletePosixAccount[0:len(c.CallOptions.DeletePosixAccount):len(c.CallOptions.DeletePosixAccount)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeletePosixAccount(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// DeleteSshPublicKey deletes an SSH public key. +func (c *Client) DeleteSshPublicKey(ctx context.Context, req *osloginpb.DeleteSshPublicKeyRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteSshPublicKey[0:len(c.CallOptions.DeleteSshPublicKey):len(c.CallOptions.DeleteSshPublicKey)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// GetLoginProfile retrieves the profile information used for logging in to a virtual machine +// on Google Compute Engine. +func (c *Client) GetLoginProfile(ctx context.Context, req *osloginpb.GetLoginProfileRequest, opts ...gax.CallOption) (*osloginpb.LoginProfile, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetLoginProfile[0:len(c.CallOptions.GetLoginProfile):len(c.CallOptions.GetLoginProfile)], opts...) + var resp *osloginpb.LoginProfile + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetLoginProfile(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetSshPublicKey retrieves an SSH public key. +func (c *Client) GetSshPublicKey(ctx context.Context, req *osloginpb.GetSshPublicKeyRequest, opts ...gax.CallOption) (*commonpb.SshPublicKey, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetSshPublicKey[0:len(c.CallOptions.GetSshPublicKey):len(c.CallOptions.GetSshPublicKey)], opts...) + var resp *commonpb.SshPublicKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ImportSshPublicKey adds an SSH public key and returns the profile information. Default POSIX +// account information is set when no username and UID exist as part of the +// login profile. +func (c *Client) ImportSshPublicKey(ctx context.Context, req *osloginpb.ImportSshPublicKeyRequest, opts ...gax.CallOption) (*osloginpb.ImportSshPublicKeyResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ImportSshPublicKey[0:len(c.CallOptions.ImportSshPublicKey):len(c.CallOptions.ImportSshPublicKey)], opts...) + var resp *osloginpb.ImportSshPublicKeyResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ImportSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateSshPublicKey updates an SSH public key and returns the profile information. This method +// supports patch semantics. +func (c *Client) UpdateSshPublicKey(ctx context.Context, req *osloginpb.UpdateSshPublicKeyRequest, opts ...gax.CallOption) (*commonpb.SshPublicKey, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateSshPublicKey[0:len(c.CallOptions.UpdateSshPublicKey):len(c.CallOptions.UpdateSshPublicKey)], opts...) + var resp *commonpb.SshPublicKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.UpdateSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/oslogin/apiv1/os_login_client_example_test.go b/vendor/cloud.google.com/go/oslogin/apiv1/os_login_client_example_test.go new file mode 100644 index 0000000000..ae5da59d9e --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1/os_login_client_example_test.go @@ -0,0 +1,137 @@ +// 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 oslogin_test + +import ( + "cloud.google.com/go/oslogin/apiv1" + "golang.org/x/net/context" + osloginpb "google.golang.org/genproto/googleapis/cloud/oslogin/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_DeletePosixAccount() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.DeletePosixAccountRequest{ + // TODO: Fill request struct fields. + } + err = c.DeletePosixAccount(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_DeleteSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.DeleteSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_GetLoginProfile() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.GetLoginProfileRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetLoginProfile(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_GetSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.GetSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ImportSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.ImportSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ImportSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_UpdateSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.UpdateSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/oslogin/apiv1beta/doc.go b/vendor/cloud.google.com/go/oslogin/apiv1beta/doc.go new file mode 100644 index 0000000000..7d98972a99 --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1beta/doc.go @@ -0,0 +1,49 @@ +// 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 oslogin is an auto-generated package for the +// Google Cloud OS Login API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Manages OS login configuration for Google account users. +package oslogin // import "cloud.google.com/go/oslogin/apiv1beta" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/compute.readonly", + } +} diff --git a/vendor/cloud.google.com/go/oslogin/apiv1beta/mock_test.go b/vendor/cloud.google.com/go/oslogin/apiv1beta/mock_test.go new file mode 100644 index 0000000000..4239356063 --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1beta/mock_test.go @@ -0,0 +1,520 @@ +// 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 oslogin + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + commonpb "google.golang.org/genproto/googleapis/cloud/oslogin/common" + osloginpb "google.golang.org/genproto/googleapis/cloud/oslogin/v1beta" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockOsLoginServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + osloginpb.OsLoginServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockOsLoginServer) DeletePosixAccount(ctx context.Context, req *osloginpb.DeletePosixAccountRequest) (*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 *mockOsLoginServer) DeleteSshPublicKey(ctx context.Context, req *osloginpb.DeleteSshPublicKeyRequest) (*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 *mockOsLoginServer) GetLoginProfile(ctx context.Context, req *osloginpb.GetLoginProfileRequest) (*osloginpb.LoginProfile, 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].(*osloginpb.LoginProfile), nil +} + +func (s *mockOsLoginServer) GetSshPublicKey(ctx context.Context, req *osloginpb.GetSshPublicKeyRequest) (*commonpb.SshPublicKey, 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].(*commonpb.SshPublicKey), nil +} + +func (s *mockOsLoginServer) ImportSshPublicKey(ctx context.Context, req *osloginpb.ImportSshPublicKeyRequest) (*osloginpb.ImportSshPublicKeyResponse, 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].(*osloginpb.ImportSshPublicKeyResponse), nil +} + +func (s *mockOsLoginServer) UpdateSshPublicKey(ctx context.Context, req *osloginpb.UpdateSshPublicKeyRequest) (*commonpb.SshPublicKey, 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].(*commonpb.SshPublicKey), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockOsLogin mockOsLoginServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + osloginpb.RegisterOsLoginServiceServer(serv, &mockOsLogin) + + 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 TestOsLoginServiceDeletePosixAccount(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/projects/%s", "[USER]", "[PROJECT]") + var request = &osloginpb.DeletePosixAccountRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeletePosixAccount(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestOsLoginServiceDeletePosixAccountError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/projects/%s", "[USER]", "[PROJECT]") + var request = &osloginpb.DeletePosixAccountRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeletePosixAccount(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 TestOsLoginServiceDeleteSshPublicKey(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.DeleteSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestOsLoginServiceDeleteSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.DeleteSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSshPublicKey(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 TestOsLoginServiceGetLoginProfile(t *testing.T) { + var name2 string = "name2-1052831874" + var suspended bool = false + var expectedResponse = &osloginpb.LoginProfile{ + Name: name2, + Suspended: suspended, + } + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s", "[USER]") + var request = &osloginpb.GetLoginProfileRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetLoginProfile(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceGetLoginProfileError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s", "[USER]") + var request = &osloginpb.GetLoginProfileRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetLoginProfile(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 TestOsLoginServiceGetSshPublicKey(t *testing.T) { + var key string = "key106079" + var expirationTimeUsec int64 = 2058878882 + var fingerprint string = "fingerprint-1375934236" + var expectedResponse = &commonpb.SshPublicKey{ + Key: key, + ExpirationTimeUsec: expirationTimeUsec, + Fingerprint: fingerprint, + } + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.GetSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceGetSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var request = &osloginpb.GetSshPublicKeyRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSshPublicKey(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 TestOsLoginServiceImportSshPublicKey(t *testing.T) { + var expectedResponse *osloginpb.ImportSshPublicKeyResponse = &osloginpb.ImportSshPublicKeyResponse{} + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("users/%s", "[USER]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.ImportSshPublicKeyRequest{ + Parent: formattedParent, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ImportSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceImportSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("users/%s", "[USER]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.ImportSshPublicKeyRequest{ + Parent: formattedParent, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ImportSshPublicKey(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 TestOsLoginServiceUpdateSshPublicKey(t *testing.T) { + var key string = "key106079" + var expirationTimeUsec int64 = 2058878882 + var fingerprint string = "fingerprint-1375934236" + var expectedResponse = &commonpb.SshPublicKey{ + Key: key, + ExpirationTimeUsec: expirationTimeUsec, + Fingerprint: fingerprint, + } + + mockOsLogin.err = nil + mockOsLogin.reqs = nil + + mockOsLogin.resps = append(mockOsLogin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.UpdateSshPublicKeyRequest{ + Name: formattedName, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSshPublicKey(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockOsLogin.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 TestOsLoginServiceUpdateSshPublicKeyError(t *testing.T) { + errCode := codes.PermissionDenied + mockOsLogin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("users/%s/sshPublicKeys/%s", "[USER]", "[FINGERPRINT]") + var sshPublicKey *commonpb.SshPublicKey = &commonpb.SshPublicKey{} + var request = &osloginpb.UpdateSshPublicKeyRequest{ + Name: formattedName, + SshPublicKey: sshPublicKey, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSshPublicKey(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/vendor/cloud.google.com/go/oslogin/apiv1beta/os_login_client.go b/vendor/cloud.google.com/go/oslogin/apiv1beta/os_login_client.go new file mode 100644 index 0000000000..d921241c34 --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1beta/os_login_client.go @@ -0,0 +1,224 @@ +// 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 oslogin + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + commonpb "google.golang.org/genproto/googleapis/cloud/oslogin/common" + osloginpb "google.golang.org/genproto/googleapis/cloud/oslogin/v1beta" + "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 { + DeletePosixAccount []gax.CallOption + DeleteSshPublicKey []gax.CallOption + GetLoginProfile []gax.CallOption + GetSshPublicKey []gax.CallOption + ImportSshPublicKey []gax.CallOption + UpdateSshPublicKey []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("oslogin.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{ + DeletePosixAccount: retry[[2]string{"default", "idempotent"}], + DeleteSshPublicKey: retry[[2]string{"default", "idempotent"}], + GetLoginProfile: retry[[2]string{"default", "idempotent"}], + GetSshPublicKey: retry[[2]string{"default", "idempotent"}], + ImportSshPublicKey: retry[[2]string{"default", "idempotent"}], + UpdateSshPublicKey: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud OS Login 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 osloginpb.OsLoginServiceClient + + // 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 os login service client. +// +// Cloud OS Login API +// +// The Cloud OS Login API allows you to manage users and their associated SSH +// public keys for logging into virtual machines on Google Cloud Platform. +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: osloginpb.NewOsLoginServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// DeletePosixAccount deletes a POSIX account. +func (c *Client) DeletePosixAccount(ctx context.Context, req *osloginpb.DeletePosixAccountRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeletePosixAccount[0:len(c.CallOptions.DeletePosixAccount):len(c.CallOptions.DeletePosixAccount)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeletePosixAccount(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// DeleteSshPublicKey deletes an SSH public key. +func (c *Client) DeleteSshPublicKey(ctx context.Context, req *osloginpb.DeleteSshPublicKeyRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteSshPublicKey[0:len(c.CallOptions.DeleteSshPublicKey):len(c.CallOptions.DeleteSshPublicKey)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// GetLoginProfile retrieves the profile information used for logging in to a virtual machine +// on Google Compute Engine. +func (c *Client) GetLoginProfile(ctx context.Context, req *osloginpb.GetLoginProfileRequest, opts ...gax.CallOption) (*osloginpb.LoginProfile, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetLoginProfile[0:len(c.CallOptions.GetLoginProfile):len(c.CallOptions.GetLoginProfile)], opts...) + var resp *osloginpb.LoginProfile + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetLoginProfile(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetSshPublicKey retrieves an SSH public key. +func (c *Client) GetSshPublicKey(ctx context.Context, req *osloginpb.GetSshPublicKeyRequest, opts ...gax.CallOption) (*commonpb.SshPublicKey, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetSshPublicKey[0:len(c.CallOptions.GetSshPublicKey):len(c.CallOptions.GetSshPublicKey)], opts...) + var resp *commonpb.SshPublicKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ImportSshPublicKey adds an SSH public key and returns the profile information. Default POSIX +// account information is set when no username and UID exist as part of the +// login profile. +func (c *Client) ImportSshPublicKey(ctx context.Context, req *osloginpb.ImportSshPublicKeyRequest, opts ...gax.CallOption) (*osloginpb.ImportSshPublicKeyResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ImportSshPublicKey[0:len(c.CallOptions.ImportSshPublicKey):len(c.CallOptions.ImportSshPublicKey)], opts...) + var resp *osloginpb.ImportSshPublicKeyResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ImportSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateSshPublicKey updates an SSH public key and returns the profile information. This method +// supports patch semantics. +func (c *Client) UpdateSshPublicKey(ctx context.Context, req *osloginpb.UpdateSshPublicKeyRequest, opts ...gax.CallOption) (*commonpb.SshPublicKey, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateSshPublicKey[0:len(c.CallOptions.UpdateSshPublicKey):len(c.CallOptions.UpdateSshPublicKey)], opts...) + var resp *commonpb.SshPublicKey + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.UpdateSshPublicKey(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/oslogin/apiv1beta/os_login_client_example_test.go b/vendor/cloud.google.com/go/oslogin/apiv1beta/os_login_client_example_test.go new file mode 100644 index 0000000000..991ee601af --- /dev/null +++ b/vendor/cloud.google.com/go/oslogin/apiv1beta/os_login_client_example_test.go @@ -0,0 +1,137 @@ +// 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 oslogin_test + +import ( + "cloud.google.com/go/oslogin/apiv1beta" + "golang.org/x/net/context" + osloginpb "google.golang.org/genproto/googleapis/cloud/oslogin/v1beta" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_DeletePosixAccount() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.DeletePosixAccountRequest{ + // TODO: Fill request struct fields. + } + err = c.DeletePosixAccount(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_DeleteSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.DeleteSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_GetLoginProfile() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.GetLoginProfileRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetLoginProfile(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_GetSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.GetSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ImportSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.ImportSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ImportSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_UpdateSshPublicKey() { + ctx := context.Background() + c, err := oslogin.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &osloginpb.UpdateSshPublicKeyRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateSshPublicKey(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/profiler/busybench/busybench.go b/vendor/cloud.google.com/go/profiler/busybench/busybench.go new file mode 100644 index 0000000000..d032a58e70 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/busybench/busybench.go @@ -0,0 +1,97 @@ +// Copyright 2017 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 main + +import ( + "bytes" + "cloud.google.com/go/profiler" + "compress/gzip" + "flag" + "log" + "math/rand" + "sync" + "time" +) + +var ( + service = flag.String("service", "", "service name") + mutexProfiling = flag.Bool("mutex_profiling", false, "enable mutex profiling") + duration = flag.Int("duration", 600, "duration of the benchmark in seconds") + apiAddr = flag.String("api_address", "", "API address of the profiler (e.g. 'cloudprofiler.googleapis.com:443')") +) + +// busywork continuously generates 1MiB of random data and compresses it +// throwing away the result. +func busywork(mu *sync.Mutex) { + ticker := time.NewTicker(time.Duration(*duration) * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + return + default: + mu.Lock() + busyworkOnce() + mu.Unlock() + } + } +} + +func busyworkOnce() { + data := make([]byte, 1024*1024) + rand.Read(data) + + var b bytes.Buffer + gz := gzip.NewWriter(&b) + if _, err := gz.Write(data); err != nil { + log.Printf("Failed to write to gzip stream: %v", err) + return + } + if err := gz.Flush(); err != nil { + log.Printf("Failed to flush to gzip stream: %v", err) + return + } + if err := gz.Close(); err != nil { + log.Printf("Failed to close gzip stream: %v", err) + } + // Throw away the result. +} + +func main() { + flag.Parse() + defer log.Printf("busybench finished profiling.") + + if *service == "" { + log.Print("Service name must be configured using --service flag.") + return + } + if err := profiler.Start(profiler.Config{Service: *service, + MutexProfiling: *mutexProfiling, + DebugLogging: true, + APIAddr: *apiAddr}); err != nil { + log.Printf("Failed to start the profiler: %v", err) + return + } + mu := new(sync.Mutex) + var wg sync.WaitGroup + wg.Add(5) + for i := 0; i < 5; i++ { + go func() { + defer wg.Done() + busywork(mu) + }() + } + wg.Wait() +} diff --git a/vendor/cloud.google.com/go/profiler/integration-test.sh b/vendor/cloud.google.com/go/profiler/integration-test.sh new file mode 100644 index 0000000000..48cf5faaee --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/integration-test.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +retry() { + for i in {1..3}; do + "${@}" && return 0 + done + return 1 +} + +# Fail on any error. +set -eo pipefail + +# Display commands being run. +set -x + +cd git/gocloud +COMMIT=$(git rev-parse HEAD) + +# Set $GOPATH +export GOPATH="$HOME/go" +GOCLOUD_HOME=$GOPATH/src/cloud.google.com/go +mkdir -p $GOCLOUD_HOME + +# Move code into $GOPATH and get dependencies +cp -R ./* $GOCLOUD_HOME +cd $GOCLOUD_HOME/internal/kokoro +# Don't print out encryption keys, etc +set +x +key=$(cat "$KOKORO_ARTIFACTS_DIR/keystore/72523_encrypted_ba2d6f7723ed_key") +iv=$(cat "$KOKORO_ARTIFACTS_DIR/keystore/72523_encrypted_ba2d6f7723ed_iv") +pass=$(cat "$KOKORO_ARTIFACTS_DIR/keystore/72523_encrypted_ba2d6f7723ed_pass") + +openssl aes-256-cbc -K $key -iv $iv -pass pass:$pass -in kokoro-key.json.enc -out key.json -d +set -x + +export GOOGLE_APPLICATION_CREDENTIALS="$(pwd)/key.json" +export GCLOUD_TESTS_GOLANG_PROJECT_ID="dulcet-port-762" +export GCLOUD_TESTS_GOLANG_ZONE="us-west1-b" +export GCLOUD_TESTS_GOLANG_BUCKET="dulcet-port-762-go-cloud-profiler-test" + +cd $GOCLOUD_HOME/profiler +retry go get -t -tags=integration . +go test -timeout=60m -parallel=5 -tags=integration -run TestAgentIntegration -commit="$COMMIT" diff --git a/vendor/cloud.google.com/go/profiler/integration_test.go b/vendor/cloud.google.com/go/profiler/integration_test.go new file mode 100644 index 0000000000..c99548c0e0 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/integration_test.go @@ -0,0 +1,274 @@ +// Copyright 2017 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 integration,go1.7 + +package profiler + +import ( + "bytes" + "flag" + "fmt" + "os" + "strings" + "testing" + "text/template" + "time" + + "cloud.google.com/go/profiler/proftest" + "golang.org/x/net/context" + "golang.org/x/oauth2/google" + compute "google.golang.org/api/compute/v1" +) + +var ( + commit = flag.String("commit", "", "git commit to test") + runID = strings.Replace(time.Now().Format("2006-01-02-15-04-05.000000-0700"), ".", "-", -1) +) + +const ( + cloudScope = "https://www.googleapis.com/auth/cloud-platform" + benchFinishString = "busybench finished profiling" +) + +const startupTemplate = ` +#! /bin/bash + +( +# Shut down the VM in 5 minutes after this script exits +# to stop accounting the VM for billing and cores quota. +trap "sleep 300 && poweroff" EXIT + +retry() { + for i in {1..3}; do + "${@}" && return 0 + done + return 1 +} + +# Fail on any error. +set -eo pipefail + +# Display commands being run. +set -x + +# Install git +retry apt-get update >/dev/null +retry apt-get -y -q install git >/dev/null + +# Install desired Go version +mkdir -p /tmp/bin +retry curl -sL -o /tmp/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme +chmod +x /tmp/bin/gimme +export PATH=$PATH:/tmp/bin + +retry eval "$(gimme {{.GoVersion}})" + +# Set $GOPATH +export GOPATH="$HOME/go" + +export GOCLOUD_HOME=$GOPATH/src/cloud.google.com/go +mkdir -p $GOCLOUD_HOME + +# Install agent +retry git clone https://code.googlesource.com/gocloud $GOCLOUD_HOME >/dev/null + +cd $GOCLOUD_HOME/profiler/busybench +git reset --hard {{.Commit}} +retry go get >/dev/null + +# Run benchmark with agent +go run busybench.go --service="{{.Service}}" --mutex_profiling="{{.MutexProfiling}}" + +# Write output to serial port 2 with timestamp. +) 2>&1 | while read line; do echo "$(date): ${line}"; done >/dev/ttyS1 +` + +const dockerfileFmt = `FROM golang +RUN git clone https://code.googlesource.com/gocloud /go/src/cloud.google.com/go \ + && cd /go/src/cloud.google.com/go/profiler/busybench && git reset --hard %s \ + && go get && go install +CMD ["busybench", "--service", "%s"] + ` + +type goGCETestCase struct { + proftest.InstanceConfig + name string + goVersion string + mutexProfiling bool + wantProfileTypes []string +} + +func (tc *goGCETestCase) initializeStartupScript(template *template.Template) error { + var buf bytes.Buffer + err := template.Execute(&buf, + struct { + Service string + GoVersion string + Commit string + MutexProfiling bool + }{ + Service: tc.name, + GoVersion: tc.goVersion, + Commit: *commit, + MutexProfiling: tc.mutexProfiling, + }) + if err != nil { + return fmt.Errorf("failed to render startup script for %s: %v", tc.name, err) + } + tc.StartupScript = buf.String() + return nil +} + +func TestAgentIntegration(t *testing.T) { + projectID := os.Getenv("GCLOUD_TESTS_GOLANG_PROJECT_ID") + if projectID == "" { + t.Fatalf("Getenv(GCLOUD_TESTS_GOLANG_PROJECT_ID) got empty string") + } + + zone := os.Getenv("GCLOUD_TESTS_GOLANG_ZONE") + if zone == "" { + t.Fatalf("Getenv(GCLOUD_TESTS_GOLANG_ZONE) got empty string") + } + + if *commit == "" { + t.Fatal("commit flag is not set") + } + + ctx := context.Background() + + client, err := google.DefaultClient(ctx, cloudScope) + if err != nil { + t.Fatalf("failed to get default client: %v", err) + } + + computeService, err := compute.New(client) + if err != nil { + t.Fatalf("failed to initialize compute service: %v", err) + } + + template, err := template.New("startupScript").Parse(startupTemplate) + if err != nil { + t.Fatalf("failed to parse startup script template: %v", err) + } + + tr := proftest.TestRunner{ + Client: client, + } + + gceTr := proftest.GCETestRunner{ + TestRunner: tr, + ComputeService: computeService, + } + + testcases := []goGCETestCase{ + { + InstanceConfig: proftest.InstanceConfig{ + ProjectID: projectID, + Zone: zone, + Name: fmt.Sprintf("profiler-test-go110-%s", runID), + MachineType: "n1-standard-1", + }, + name: fmt.Sprintf("profiler-test-go110-%s-gce", runID), + wantProfileTypes: []string{"CPU", "HEAP", "THREADS", "CONTENTION"}, + goVersion: "1.10", + mutexProfiling: true, + }, + { + InstanceConfig: proftest.InstanceConfig{ + ProjectID: projectID, + Zone: zone, + Name: fmt.Sprintf("profiler-test-go19-%s", runID), + MachineType: "n1-standard-1", + }, + name: fmt.Sprintf("profiler-test-go19-%s-gce", runID), + wantProfileTypes: []string{"CPU", "HEAP", "THREADS", "CONTENTION"}, + goVersion: "1.9", + mutexProfiling: true, + }, + { + InstanceConfig: proftest.InstanceConfig{ + ProjectID: projectID, + Zone: zone, + Name: fmt.Sprintf("profiler-test-go18-%s", runID), + MachineType: "n1-standard-1", + }, + name: fmt.Sprintf("profiler-test-go18-%s-gce", runID), + wantProfileTypes: []string{"CPU", "HEAP", "THREADS", "CONTENTION"}, + goVersion: "1.8", + mutexProfiling: true, + }, + { + InstanceConfig: proftest.InstanceConfig{ + ProjectID: projectID, + Zone: zone, + Name: fmt.Sprintf("profiler-test-go17-%s", runID), + MachineType: "n1-standard-1", + }, + name: fmt.Sprintf("profiler-test-go17-%s-gce", runID), + wantProfileTypes: []string{"CPU", "HEAP", "THREADS"}, + goVersion: "1.7", + }, + { + InstanceConfig: proftest.InstanceConfig{ + ProjectID: projectID, + Zone: zone, + Name: fmt.Sprintf("profiler-test-go16-%s", runID), + MachineType: "n1-standard-1", + }, + name: fmt.Sprintf("profiler-test-go16-%s-gce", runID), + wantProfileTypes: []string{"CPU", "HEAP", "THREADS"}, + goVersion: "1.6", + }, + } + + for _, tc := range testcases { + tc := tc // capture range variable + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if err := tc.initializeStartupScript(template); err != nil { + t.Fatalf("failed to initialize startup script") + } + + if err := gceTr.StartInstance(ctx, &tc.InstanceConfig); err != nil { + t.Fatal(err) + } + defer func() { + if gceTr.DeleteInstance(ctx, &tc.InstanceConfig); err != nil { + t.Fatal(err) + } + }() + + timeoutCtx, cancel := context.WithTimeout(ctx, time.Minute*25) + defer cancel() + if err := gceTr.PollForSerialOutput(timeoutCtx, &tc.InstanceConfig, benchFinishString); err != nil { + t.Fatalf("PollForSerialOutput() got error: %v", err) + } + + timeNow := time.Now() + endTime := timeNow.Format(time.RFC3339) + startTime := timeNow.Add(-1 * time.Hour).Format(time.RFC3339) + for _, pType := range tc.wantProfileTypes { + pr, err := tr.QueryProfiles(tc.ProjectID, tc.name, startTime, endTime, pType) + if err != nil { + t.Errorf("QueryProfiles(%s, %s, %s, %s, %s) got error: %v", tc.ProjectID, tc.name, startTime, endTime, pType, err) + continue + } + if err := pr.HasFunction("busywork"); err != nil { + t.Error(err) + } + } + }) + } +} diff --git a/vendor/cloud.google.com/go/profiler/mocks/mock_profiler_client.go b/vendor/cloud.google.com/go/profiler/mocks/mock_profiler_client.go new file mode 100644 index 0000000000..5ec9f9ddb0 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/mocks/mock_profiler_client.go @@ -0,0 +1,78 @@ +// Copyright 2017 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. + +// Automatically generated by MockGen. DO NOT EDIT! +// Source: google.golang.org/genproto/googleapis/devtools/cloudprofiler/v2 (interfaces: ProfilerServiceClient) + +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + context "golang.org/x/net/context" + v2 "google.golang.org/genproto/googleapis/devtools/cloudprofiler/v2" + grpc "google.golang.org/grpc" +) + +// Mock of ProfilerServiceClient interface +type MockProfilerServiceClient struct { + ctrl *gomock.Controller + recorder *_MockProfilerServiceClientRecorder +} + +// Recorder for MockProfilerServiceClient (not exported) +type _MockProfilerServiceClientRecorder struct { + mock *MockProfilerServiceClient +} + +func NewMockProfilerServiceClient(ctrl *gomock.Controller) *MockProfilerServiceClient { + mock := &MockProfilerServiceClient{ctrl: ctrl} + mock.recorder = &_MockProfilerServiceClientRecorder{mock} + return mock +} + +func (_m *MockProfilerServiceClient) EXPECT() *_MockProfilerServiceClientRecorder { + return _m.recorder +} + +func (_m *MockProfilerServiceClient) CreateProfile(_param0 context.Context, _param1 *v2.CreateProfileRequest, _param2 ...grpc.CallOption) (*v2.Profile, error) { + _s := []interface{}{_param0, _param1} + for _, _x := range _param2 { + _s = append(_s, _x) + } + ret := _m.ctrl.Call(_m, "CreateProfile", _s...) + ret0, _ := ret[0].(*v2.Profile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockProfilerServiceClientRecorder) CreateProfile(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + _s := append([]interface{}{arg0, arg1}, arg2...) + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateProfile", _s...) +} + +func (_m *MockProfilerServiceClient) UpdateProfile(_param0 context.Context, _param1 *v2.UpdateProfileRequest, _param2 ...grpc.CallOption) (*v2.Profile, error) { + _s := []interface{}{_param0, _param1} + for _, _x := range _param2 { + _s = append(_s, _x) + } + ret := _m.ctrl.Call(_m, "UpdateProfile", _s...) + ret0, _ := ret[0].(*v2.Profile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockProfilerServiceClientRecorder) UpdateProfile(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + _s := append([]interface{}{arg0, arg1}, arg2...) + return _mr.mock.ctrl.RecordCall(_mr.mock, "UpdateProfile", _s...) +} diff --git a/vendor/cloud.google.com/go/profiler/mutex.go b/vendor/cloud.google.com/go/profiler/mutex.go new file mode 100644 index 0000000000..93512fa0fc --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/mutex.go @@ -0,0 +1,25 @@ +// Copyright 2017 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 go1.8 + +package profiler + +import "runtime" + +func enableMutexProfiling() bool { + // One percent of mutex contention events are profiled. + runtime.SetMutexProfileFraction(100) + return true +} diff --git a/vendor/cloud.google.com/go/profiler/mutex_go17.go b/vendor/cloud.google.com/go/profiler/mutex_go17.go new file mode 100644 index 0000000000..5b9ae9c6bb --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/mutex_go17.go @@ -0,0 +1,21 @@ +// Copyright 2017 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 !go1.8 + +package profiler + +func enableMutexProfiling() bool { + return false +} diff --git a/vendor/cloud.google.com/go/profiler/profiler.go b/vendor/cloud.google.com/go/profiler/profiler.go new file mode 100644 index 0000000000..2e28488f67 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/profiler.go @@ -0,0 +1,504 @@ +// Copyright 2017 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 profiler is a client for the Stackdriver Profiler service. +// +// This package is still experimental and subject to change. +// +// Usage example: +// +// import "cloud.google.com/go/profiler" +// ... +// if err := profiler.Start(profiler.Config{Service: "my-service"}); err != nil { +// // TODO: Handle error. +// } +// +// Calling Start will start a goroutine to collect profiles and upload to +// the profiler server, at the rhythm specified by the server. +// +// The caller must provide the service string in the config, and may provide +// other information as well. See Config for details. +// +// Profiler has CPU, heap and goroutine profiling enabled by default. Mutex +// profiling can be enabled in the config. Note that goroutine and mutex +// profiles are shown as "threads" and "contention" profiles in the profiler +// UI. +package profiler + +import ( + "bytes" + "errors" + "fmt" + "log" + "os" + "runtime" + "runtime/pprof" + "sync" + "time" + + gcemd "cloud.google.com/go/compute/metadata" + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/google/pprof/profile" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + gtransport "google.golang.org/api/transport/grpc" + pb "google.golang.org/genproto/googleapis/devtools/cloudprofiler/v2" + edpb "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + grpcmd "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +var ( + config Config + startOnce sync.Once + mutexEnabled bool + // The functions below are stubbed to be overrideable for testing. + getProjectID = gcemd.ProjectID + getInstanceName = gcemd.InstanceName + getZone = gcemd.Zone + startCPUProfile = pprof.StartCPUProfile + stopCPUProfile = pprof.StopCPUProfile + writeHeapProfile = pprof.WriteHeapProfile + sleep = gax.Sleep + dialGRPC = gtransport.Dial + onGCE = gcemd.OnGCE +) + +const ( + apiAddress = "cloudprofiler.googleapis.com:443" + xGoogAPIMetadata = "x-goog-api-client" + zoneNameLabel = "zone" + versionLabel = "version" + languageLabel = "language" + instanceLabel = "instance" + scope = "https://www.googleapis.com/auth/monitoring.write" + + initialBackoff = time.Minute + // Ensure the agent will recover within 1 hour. + maxBackoff = time.Hour + backoffMultiplier = 1.3 // Backoff envelope increases by this factor on each retry. + retryInfoMetadata = "google.rpc.retryinfo-bin" +) + +// Config is the profiler configuration. +type Config struct { + // Service must be provided to start the profiler. It specifies the name of + // the service under which the profiled data will be recorded and exposed at + // the Profiler UI for the project. You can specify an arbitrary string, but + // see Deployment.target at + // https://github.com/googleapis/googleapis/blob/master/google/devtools/cloudprofiler/v2/profiler.proto + // for restrictions. If the parameter is not set, the agent will probe + // GAE_SERVICE environment variable which is present in Google App Engine + // environment. + // NOTE: The string should be the same across different replicas of + // your service so that the globally constant profiling rate is + // maintained. Do not put things like PID or unique pod ID in the name. + Service string + + // ServiceVersion is an optional field specifying the version of the + // service. It can be an arbitrary string. Profiler profiles + // once per minute for each version of each service in each zone. + // ServiceVersion defaults to GAE_VERSION environment variable if that is + // set, or to empty string otherwise. + ServiceVersion string + + // DebugLogging enables detailed debug logging from profiler. It + // defaults to false. + DebugLogging bool + + // MutexProfiling enables mutex profiling. It defaults to false. + // Note that mutex profiling is not supported by Go versions older + // than Go 1.8. + MutexProfiling bool + + // When true, collecting the heap profiles is disabled. + NoHeapProfiling bool + + // When true, collecting the goroutine profiles is disabled. + NoGoroutineProfiling bool + + // ProjectID is the Cloud Console project ID to use instead of the one set by + // GOOGLE_CLOUD_PROJECT environment variable or read from the VM metadata + // server. + // + // Set this if you are running the agent in your local environment + // or anywhere else outside of Google Cloud Platform. + ProjectID string + + // APIAddr is the HTTP endpoint to use to connect to the profiler + // agent API. Defaults to the production environment, overridable + // for testing. + APIAddr string + + instance string + zone string +} + +// startError represents the error occurred during the +// initializating and starting of the agent. +var startError error + +// Start starts a goroutine to collect and upload profiles. The +// caller must provide the service string in the config. See +// Config for details. Start should only be called once. Any +// additional calls will be ignored. +func Start(cfg Config, options ...option.ClientOption) error { + startOnce.Do(func() { + startError = start(cfg, options...) + }) + return startError +} + +func start(cfg Config, options ...option.ClientOption) error { + if err := initializeConfig(cfg); err != nil { + debugLog("failed to initialize config: %v", err) + return err + } + if config.MutexProfiling { + if mutexEnabled = enableMutexProfiling(); !mutexEnabled { + return fmt.Errorf("mutex profiling is not supported by %s, requires Go 1.8 or later", runtime.Version()) + } + } + + ctx := context.Background() + + opts := []option.ClientOption{ + option.WithEndpoint(config.APIAddr), + option.WithScopes(scope), + } + opts = append(opts, options...) + + conn, err := dialGRPC(ctx, opts...) + if err != nil { + debugLog("failed to dial GRPC: %v", err) + return err + } + + a := initializeAgent(pb.NewProfilerServiceClient(conn)) + go pollProfilerService(withXGoogHeader(ctx), a) + return nil +} + +func debugLog(format string, e ...interface{}) { + if config.DebugLogging { + log.Printf(format, e...) + } +} + +// agent polls the profiler server for instructions on behalf of a task, +// and collects and uploads profiles as requested. +type agent struct { + client pb.ProfilerServiceClient + deployment *pb.Deployment + profileLabels map[string]string + profileTypes []pb.ProfileType +} + +// abortedBackoffDuration retrieves the retry duration from gRPC trailing +// metadata, which is set by the profiler server. +func abortedBackoffDuration(md grpcmd.MD) (time.Duration, error) { + elem := md[retryInfoMetadata] + if len(elem) <= 0 { + return 0, errors.New("no retry info") + } + + var retryInfo edpb.RetryInfo + if err := proto.Unmarshal([]byte(elem[0]), &retryInfo); err != nil { + return 0, err + } else if time, err := ptypes.Duration(retryInfo.RetryDelay); err != nil { + return 0, err + } else { + if time < 0 { + return 0, errors.New("negative retry duration") + } + return time, nil + } +} + +type retryer struct { + backoff gax.Backoff + md grpcmd.MD +} + +func (r *retryer) Retry(err error) (time.Duration, bool) { + st, _ := status.FromError(err) + if st != nil && st.Code() == codes.Aborted { + dur, err := abortedBackoffDuration(r.md) + if err == nil { + return dur, true + } + debugLog("failed to get backoff duration: %v", err) + } + return r.backoff.Pause(), true +} + +// createProfile talks to the profiler server to create profile. In +// case of error, the goroutine will sleep and retry. Sleep duration may +// be specified by the server. Otherwise it will be an exponentially +// increasing value, bounded by maxBackoff. +func (a *agent) createProfile(ctx context.Context) *pb.Profile { + req := pb.CreateProfileRequest{ + Deployment: a.deployment, + ProfileType: a.profileTypes, + } + + var p *pb.Profile + md := grpcmd.New(map[string]string{}) + + gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + p, err = a.client.CreateProfile(ctx, &req, grpc.Trailer(&md)) + if err != nil { + debugLog("failed to create a profile, will retry: %v", err) + } + return err + }, gax.WithRetry(func() gax.Retryer { + return &retryer{ + backoff: gax.Backoff{ + Initial: initialBackoff, + Max: maxBackoff, + Multiplier: backoffMultiplier, + }, + md: md, + } + })) + + debugLog("successfully created profile %v", p.GetProfileType()) + return p +} + +func (a *agent) profileAndUpload(ctx context.Context, p *pb.Profile) { + var prof bytes.Buffer + pt := p.GetProfileType() + + switch pt { + case pb.ProfileType_CPU: + duration, err := ptypes.Duration(p.Duration) + if err != nil { + debugLog("failed to get profile duration: %v", err) + return + } + if err := startCPUProfile(&prof); err != nil { + debugLog("failed to start CPU profile: %v", err) + return + } + sleep(ctx, duration) + stopCPUProfile() + case pb.ProfileType_HEAP: + if err := writeHeapProfile(&prof); err != nil { + debugLog("failed to write heap profile: %v", err) + return + } + case pb.ProfileType_THREADS: + if err := pprof.Lookup("goroutine").WriteTo(&prof, 0); err != nil { + debugLog("failed to create goroutine profile: %v", err) + return + } + case pb.ProfileType_CONTENTION: + duration, err := ptypes.Duration(p.Duration) + if err != nil { + debugLog("failed to get profile duration: %v", err) + return + } + if err := deltaMutexProfile(ctx, duration, &prof); err != nil { + debugLog("failed to create mutex profile: %v", err) + return + } + default: + debugLog("unexpected profile type: %v", pt) + return + } + + // Starting Go 1.9 the profiles are symbolized by runtime/pprof. + // TODO(jianqiaoli): Remove the symbolization code when we decide to + // stop supporting Go 1.8. + if !shouldAssumeSymbolized && pt != pb.ProfileType_CONTENTION { + if err := parseAndSymbolize(&prof); err != nil { + debugLog("failed to symbolize profile: %v", err) + } + } + + p.ProfileBytes = prof.Bytes() + p.Labels = a.profileLabels + req := pb.UpdateProfileRequest{Profile: p} + + // Upload profile, discard profile in case of error. + debugLog("start uploading profile") + if _, err := a.client.UpdateProfile(ctx, &req); err != nil { + debugLog("failed to upload profile: %v", err) + } +} + +// deltaMutexProfile writes mutex profile changes over a time period specified +// with 'duration' to 'prof'. +func deltaMutexProfile(ctx context.Context, duration time.Duration, prof *bytes.Buffer) error { + if !mutexEnabled { + return errors.New("mutex profiling is not enabled") + } + p0, err := mutexProfile() + if err != nil { + return err + } + sleep(ctx, duration) + p, err := mutexProfile() + if err != nil { + return err + } + + // TODO(jianqiaoli): Remove this check when github.com/google/pprof/issues/242 + // is fixed. + if len(p0.Mapping) > 0 { + p0.Scale(-1) + p, err = profile.Merge([]*profile.Profile{p0, p}) + if err != nil { + return err + } + } + + // The mutex profile is not symbolized by runtime.pprof until + // golang.org/issue/21474 is fixed in go1.10. + symbolize(p) + return p.Write(prof) +} + +func mutexProfile() (*profile.Profile, error) { + p := pprof.Lookup("mutex") + if p == nil { + return nil, errors.New("mutex profiling is not supported") + } + var buf bytes.Buffer + if err := p.WriteTo(&buf, 0); err != nil { + return nil, err + } + return profile.Parse(&buf) +} + +// withXGoogHeader 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 withXGoogHeader(ctx context.Context, keyval ...string) context.Context { + kv := append([]string{"gl-go", version.Go(), "gccl", version.Repo}, keyval...) + kv = append(kv, "gax", gax.Version, "grpc", grpc.Version) + + md, _ := grpcmd.FromOutgoingContext(ctx) + md = md.Copy() + md[xGoogAPIMetadata] = []string{gax.XGoogHeader(kv...)} + return grpcmd.NewOutgoingContext(ctx, md) +} + +func initializeAgent(c pb.ProfilerServiceClient) *agent { + labels := map[string]string{languageLabel: "go"} + if config.zone != "" { + labels[zoneNameLabel] = config.zone + } + if config.ServiceVersion != "" { + labels[versionLabel] = config.ServiceVersion + } + d := &pb.Deployment{ + ProjectId: config.ProjectID, + Target: config.Service, + Labels: labels, + } + + profileLabels := map[string]string{} + + if config.instance != "" { + profileLabels[instanceLabel] = config.instance + } + + profileTypes := []pb.ProfileType{pb.ProfileType_CPU} + if !config.NoHeapProfiling { + profileTypes = append(profileTypes, pb.ProfileType_HEAP) + } + if !config.NoGoroutineProfiling { + profileTypes = append(profileTypes, pb.ProfileType_THREADS) + } + if mutexEnabled { + profileTypes = append(profileTypes, pb.ProfileType_CONTENTION) + } + + return &agent{ + client: c, + deployment: d, + profileLabels: profileLabels, + profileTypes: profileTypes, + } +} + +func initializeConfig(cfg Config) error { + config = cfg + + if config.Service == "" { + config.Service = os.Getenv("GAE_SERVICE") + } + if config.Service == "" { + return errors.New("service name must be configured") + } + + if config.ServiceVersion == "" { + config.ServiceVersion = os.Getenv("GAE_VERSION") + } + + if projectID := os.Getenv("GOOGLE_CLOUD_PROJECT"); config.ProjectID == "" && projectID != "" { + // Cloud Shell and App Engine set this environment variable to the project + // ID, so use it if present. In case of App Engine the project ID is also + // available from the GCE metadata server, but by using the environment + // variable saves one request to the metadata server. The environment + // project ID is only used if no project ID is provided in the + // configuration. + config.ProjectID = projectID + } + if onGCE() { + var err error + if config.ProjectID == "" { + if config.ProjectID, err = getProjectID(); err != nil { + return fmt.Errorf("failed to get the project ID from Compute Engine: %v", err) + } + } + + if config.zone, err = getZone(); err != nil { + return fmt.Errorf("failed to get zone from Compute Engine: %v", err) + } + + if config.instance, err = getInstanceName(); err != nil { + return fmt.Errorf("failed to get instance from Compute Engine: %v", err) + } + + } else { + if config.ProjectID == "" { + return fmt.Errorf("project ID must be specified in the configuration if running outside of GCP") + } + } + + if config.APIAddr == "" { + config.APIAddr = apiAddress + } + return nil +} + +// pollProfilerService starts an endless loop to poll the profiler +// server for instructions, and collects and uploads profiles as +// requested. +func pollProfilerService(ctx context.Context, a *agent) { + debugLog("profiler has started") + for { + p := a.createProfile(ctx) + a.profileAndUpload(ctx, p) + } +} diff --git a/vendor/cloud.google.com/go/profiler/profiler_example_test.go b/vendor/cloud.google.com/go/profiler/profiler_example_test.go new file mode 100644 index 0000000000..c56f1a5758 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/profiler_example_test.go @@ -0,0 +1,25 @@ +// Copyright 2017 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 profiler_test + +import ( + "cloud.google.com/go/profiler" +) + +func ExampleStart() { + if err := profiler.Start(profiler.Config{Service: "my-service", ServiceVersion: "v1"}); err != nil { + //TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/profiler/profiler_test.go b/vendor/cloud.google.com/go/profiler/profiler_test.go new file mode 100644 index 0000000000..b478c88c3e --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/profiler_test.go @@ -0,0 +1,856 @@ +// Copyright 2017 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 profiler + +import ( + "bytes" + "compress/gzip" + "errors" + "fmt" + "io" + "log" + "math/rand" + "os" + "runtime" + "strings" + "sync" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/profiler/mocks" + "github.com/golang/mock/gomock" + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/google/pprof/profile" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + gtransport "google.golang.org/api/transport/grpc" + pb "google.golang.org/genproto/googleapis/devtools/cloudprofiler/v2" + edpb "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc/codes" + grpcmd "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +const ( + testProjectID = "test-project-ID" + testInstance = "test-instance" + testZone = "test-zone" + testService = "test-service" + testSvcVersion = "test-service-version" + testProfileDuration = time.Second * 10 + testServerTimeout = time.Second * 15 +) + +func createTestDeployment() *pb.Deployment { + labels := map[string]string{ + zoneNameLabel: testZone, + versionLabel: testSvcVersion, + } + return &pb.Deployment{ + ProjectId: testProjectID, + Target: testService, + Labels: labels, + } +} + +func createTestAgent(psc pb.ProfilerServiceClient) *agent { + return &agent{ + client: psc, + deployment: createTestDeployment(), + profileLabels: map[string]string{instanceLabel: testInstance}, + profileTypes: []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS}, + } +} + +func createTrailers(dur time.Duration) map[string]string { + b, _ := proto.Marshal(&edpb.RetryInfo{ + RetryDelay: ptypes.DurationProto(dur), + }) + return map[string]string{ + retryInfoMetadata: string(b), + } +} + +func TestCreateProfile(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mpc := mocks.NewMockProfilerServiceClient(ctrl) + a := createTestAgent(mpc) + p := &pb.Profile{Name: "test_profile"} + wantRequest := pb.CreateProfileRequest{ + Deployment: a.deployment, + ProfileType: a.profileTypes, + } + + mpc.EXPECT().CreateProfile(ctx, gomock.Eq(&wantRequest), gomock.Any()).Times(1).Return(p, nil) + + gotP := a.createProfile(ctx) + + if !testutil.Equal(gotP, p) { + t.Errorf("CreateProfile() got wrong profile, got %v, want %v", gotP, p) + } +} + +func TestProfileAndUpload(t *testing.T) { + oldStartCPUProfile, oldStopCPUProfile, oldWriteHeapProfile, oldSleep := startCPUProfile, stopCPUProfile, writeHeapProfile, sleep + defer func() { + startCPUProfile, stopCPUProfile, writeHeapProfile, sleep = oldStartCPUProfile, oldStopCPUProfile, oldWriteHeapProfile, oldSleep + }() + + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + errFunc := func(io.Writer) error { return errors.New("") } + testDuration := time.Second * 5 + tests := []struct { + profileType pb.ProfileType + duration *time.Duration + startCPUProfileFunc func(io.Writer) error + writeHeapProfileFunc func(io.Writer) error + wantBytes []byte + }{ + { + profileType: pb.ProfileType_CPU, + duration: &testDuration, + startCPUProfileFunc: func(w io.Writer) error { + w.Write([]byte{1}) + return nil + }, + writeHeapProfileFunc: errFunc, + wantBytes: []byte{1}, + }, + { + profileType: pb.ProfileType_CPU, + startCPUProfileFunc: errFunc, + writeHeapProfileFunc: errFunc, + }, + { + profileType: pb.ProfileType_CPU, + duration: &testDuration, + startCPUProfileFunc: func(w io.Writer) error { + w.Write([]byte{2}) + return nil + }, + writeHeapProfileFunc: func(w io.Writer) error { + w.Write([]byte{3}) + return nil + }, + wantBytes: []byte{2}, + }, + { + profileType: pb.ProfileType_HEAP, + startCPUProfileFunc: errFunc, + writeHeapProfileFunc: func(w io.Writer) error { + w.Write([]byte{4}) + return nil + }, + wantBytes: []byte{4}, + }, + { + profileType: pb.ProfileType_HEAP, + startCPUProfileFunc: errFunc, + writeHeapProfileFunc: errFunc, + }, + { + profileType: pb.ProfileType_HEAP, + startCPUProfileFunc: func(w io.Writer) error { + w.Write([]byte{5}) + return nil + }, + writeHeapProfileFunc: func(w io.Writer) error { + w.Write([]byte{6}) + return nil + }, + wantBytes: []byte{6}, + }, + { + profileType: pb.ProfileType_PROFILE_TYPE_UNSPECIFIED, + startCPUProfileFunc: func(w io.Writer) error { + w.Write([]byte{7}) + return nil + }, + writeHeapProfileFunc: func(w io.Writer) error { + w.Write([]byte{8}) + return nil + }, + }, + } + + for _, tt := range tests { + mpc := mocks.NewMockProfilerServiceClient(ctrl) + a := createTestAgent(mpc) + startCPUProfile = tt.startCPUProfileFunc + stopCPUProfile = func() {} + writeHeapProfile = tt.writeHeapProfileFunc + var gotSleep *time.Duration + sleep = func(ctx context.Context, d time.Duration) error { + gotSleep = &d + return nil + } + p := &pb.Profile{ProfileType: tt.profileType} + if tt.duration != nil { + p.Duration = ptypes.DurationProto(*tt.duration) + } + if tt.wantBytes != nil { + wantProfile := &pb.Profile{ + ProfileType: p.ProfileType, + Duration: p.Duration, + ProfileBytes: tt.wantBytes, + Labels: a.profileLabels, + } + wantRequest := pb.UpdateProfileRequest{ + Profile: wantProfile, + } + mpc.EXPECT().UpdateProfile(ctx, gomock.Eq(&wantRequest)).Times(1) + } else { + mpc.EXPECT().UpdateProfile(gomock.Any(), gomock.Any()).MaxTimes(0) + } + + a.profileAndUpload(ctx, p) + + if tt.duration == nil { + if gotSleep != nil { + t.Errorf("profileAndUpload(%v) slept for: %v, want no sleep", p, gotSleep) + } + } else { + if gotSleep == nil { + t.Errorf("profileAndUpload(%v) didn't sleep, want sleep for: %v", p, tt.duration) + } else if *gotSleep != *tt.duration { + t.Errorf("profileAndUpload(%v) slept for wrong duration, got: %v, want: %v", p, gotSleep, tt.duration) + } + } + } +} + +func TestRetry(t *testing.T) { + normalDuration := time.Second * 3 + negativeDuration := time.Second * -3 + + tests := []struct { + trailers map[string]string + wantPause *time.Duration + }{ + { + createTrailers(normalDuration), + &normalDuration, + }, + { + createTrailers(negativeDuration), + nil, + }, + { + map[string]string{retryInfoMetadata: "wrong format"}, + nil, + }, + { + map[string]string{}, + nil, + }, + } + + for _, tt := range tests { + md := grpcmd.New(tt.trailers) + r := &retryer{ + backoff: gax.Backoff{ + Initial: initialBackoff, + Max: maxBackoff, + Multiplier: backoffMultiplier, + }, + md: md, + } + + pause, shouldRetry := r.Retry(status.Error(codes.Aborted, "")) + + if !shouldRetry { + t.Error("retryer.Retry() returned shouldRetry false, want true") + } + + if tt.wantPause != nil { + if pause != *tt.wantPause { + t.Errorf("retryer.Retry() returned wrong pause, got: %v, want: %v", pause, tt.wantPause) + } + } else { + if pause > initialBackoff { + t.Errorf("retryer.Retry() returned wrong pause, got: %v, want: < %v", pause, initialBackoff) + } + } + } + + md := grpcmd.New(map[string]string{}) + + r := &retryer{ + backoff: gax.Backoff{ + Initial: initialBackoff, + Max: maxBackoff, + Multiplier: backoffMultiplier, + }, + md: md, + } + for i := 0; i < 100; i++ { + pause, shouldRetry := r.Retry(errors.New("")) + if !shouldRetry { + t.Errorf("retryer.Retry() called %v times, returned shouldRetry false, want true", i) + } + if pause > maxBackoff { + t.Errorf("retryer.Retry() called %v times, returned wrong pause, got: %v, want: < %v", i, pause, maxBackoff) + } + } +} + +func TestWithXGoogHeader(t *testing.T) { + ctx := withXGoogHeader(context.Background()) + md, _ := grpcmd.FromOutgoingContext(ctx) + + if xg := md[xGoogAPIMetadata]; len(xg) == 0 { + t.Errorf("withXGoogHeader() sets empty xGoogHeader") + } else { + if !strings.Contains(xg[0], "gl-go/") { + t.Errorf("withXGoogHeader() got: %v, want gl-go key", xg[0]) + } + if !strings.Contains(xg[0], "gccl/") { + t.Errorf("withXGoogHeader() got: %v, want gccl key", xg[0]) + } + if !strings.Contains(xg[0], "gax/") { + t.Errorf("withXGoogHeader() got: %v, want gax key", xg[0]) + } + if !strings.Contains(xg[0], "grpc/") { + t.Errorf("withXGoogHeader() got: %v, want grpc key", xg[0]) + } + } +} + +func TestInitializeAgent(t *testing.T) { + oldConfig, oldMutexEnabled := config, mutexEnabled + defer func() { + config, mutexEnabled = oldConfig, oldMutexEnabled + }() + + for _, tt := range []struct { + config Config + enableMutex bool + wantProfileTypes []pb.ProfileType + wantDeploymentLabels map[string]string + wantProfileLabels map[string]string + }{ + { + config: Config{ServiceVersion: testSvcVersion, zone: testZone}, + wantProfileTypes: []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS}, + wantDeploymentLabels: map[string]string{zoneNameLabel: testZone, versionLabel: testSvcVersion, languageLabel: "go"}, + wantProfileLabels: map[string]string{}, + }, + { + config: Config{zone: testZone}, + wantProfileTypes: []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS}, + wantDeploymentLabels: map[string]string{zoneNameLabel: testZone, languageLabel: "go"}, + wantProfileLabels: map[string]string{}, + }, + { + config: Config{ServiceVersion: testSvcVersion}, + wantProfileTypes: []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS}, + wantDeploymentLabels: map[string]string{versionLabel: testSvcVersion, languageLabel: "go"}, + wantProfileLabels: map[string]string{}, + }, + { + config: Config{instance: testInstance}, + wantProfileTypes: []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS}, + wantDeploymentLabels: map[string]string{languageLabel: "go"}, + wantProfileLabels: map[string]string{instanceLabel: testInstance}, + }, + { + config: Config{instance: testInstance}, + enableMutex: true, + wantProfileTypes: []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_HEAP, pb.ProfileType_THREADS, pb.ProfileType_CONTENTION}, + wantDeploymentLabels: map[string]string{languageLabel: "go"}, + wantProfileLabels: map[string]string{instanceLabel: testInstance}, + }, + { + config: Config{NoHeapProfiling: true}, + wantProfileTypes: []pb.ProfileType{pb.ProfileType_CPU, pb.ProfileType_THREADS}, + wantDeploymentLabels: map[string]string{languageLabel: "go"}, + wantProfileLabels: map[string]string{}, + }, + { + config: Config{NoHeapProfiling: true, NoGoroutineProfiling: true}, + wantProfileTypes: []pb.ProfileType{pb.ProfileType_CPU}, + wantDeploymentLabels: map[string]string{languageLabel: "go"}, + wantProfileLabels: map[string]string{}, + }, + } { + + config = tt.config + config.ProjectID = testProjectID + config.Service = testService + mutexEnabled = tt.enableMutex + a := initializeAgent(nil) + + wantDeployment := &pb.Deployment{ + ProjectId: testProjectID, + Target: testService, + Labels: tt.wantDeploymentLabels, + } + if !testutil.Equal(a.deployment, wantDeployment) { + t.Errorf("initializeAgent() got deployment: %v, want %v", a.deployment, wantDeployment) + } + if !testutil.Equal(a.profileLabels, tt.wantProfileLabels) { + t.Errorf("initializeAgent() got profile labels: %v, want %v", a.profileLabels, tt.wantProfileLabels) + } + if !testutil.Equal(a.profileTypes, tt.wantProfileTypes) { + t.Errorf("initializeAgent() got profile types: %v, want %v", a.profileTypes, tt.wantProfileTypes) + } + } +} + +func TestInitializeConfig(t *testing.T) { + oldConfig, oldService, oldVersion, oldEnvProjectID, oldGetProjectID, oldGetInstanceName, oldGetZone, oldOnGCE := config, os.Getenv("GAE_SERVICE"), os.Getenv("GAE_VERSION"), os.Getenv("GOOGLE_CLOUD_PROJECT"), getProjectID, getInstanceName, getZone, onGCE + defer func() { + config, getProjectID, getInstanceName, getZone, onGCE = oldConfig, oldGetProjectID, oldGetInstanceName, oldGetZone, oldOnGCE + if err := os.Setenv("GAE_SERVICE", oldService); err != nil { + t.Fatal(err) + } + if err := os.Setenv("GAE_VERSION", oldVersion); err != nil { + t.Fatal(err) + } + if err := os.Setenv("GOOGLE_CLOUD_PROJECT", oldEnvProjectID); err != nil { + t.Fatal(err) + } + }() + const ( + testGAEService = "test-gae-service" + testGAEVersion = "test-gae-version" + testGCEProjectID = "test-gce-project-id" + testEnvProjectID = "test-env-project-id" + ) + for _, tt := range []struct { + desc string + config Config + wantConfig Config + wantErrorString string + onGAE bool + onGCE bool + envProjectID bool + }{ + { + "accepts service name", + Config{Service: testService}, + Config{Service: testService, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance}, + "", + false, + true, + false, + }, + { + "env project overrides GCE project", + Config{Service: testService}, + Config{Service: testService, ProjectID: testEnvProjectID, zone: testZone, instance: testInstance}, + "", + false, + true, + true, + }, + { + "requires service name", + Config{}, + Config{}, + "service name must be configured", + false, + true, + false, + }, + { + "accepts service name from config and service version from GAE", + Config{Service: testService}, + Config{Service: testService, ServiceVersion: testGAEVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance}, + "", + true, + true, + false, + }, + { + "reads both service name and version from GAE env vars", + Config{}, + Config{Service: testGAEService, ServiceVersion: testGAEVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance}, + "", + true, + true, + false, + }, + { + "accepts service version from config", + Config{Service: testService, ServiceVersion: testSvcVersion}, + Config{Service: testService, ServiceVersion: testSvcVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance}, + "", + false, + true, + false, + }, + { + "configured version has priority over GAE-provided version", + Config{Service: testService, ServiceVersion: testSvcVersion}, + Config{Service: testService, ServiceVersion: testSvcVersion, ProjectID: testGCEProjectID, zone: testZone, instance: testInstance}, + "", + true, + true, + false, + }, + { + "configured project ID has priority over metadata-provided project ID", + Config{Service: testService, ProjectID: testProjectID}, + Config{Service: testService, ProjectID: testProjectID, zone: testZone, instance: testInstance}, + "", + false, + true, + false, + }, + { + "configured project ID has priority over environment project ID", + Config{Service: testService, ProjectID: testProjectID}, + Config{Service: testService, ProjectID: testProjectID}, + "", + false, + false, + true, + }, + { + "requires project ID if not on GCE", + Config{Service: testService}, + Config{Service: testService}, + "project ID must be specified in the configuration if running outside of GCP", + false, + false, + false, + }, + } { + t.Logf("Running test: %s", tt.desc) + envService, envVersion := "", "" + if tt.onGAE { + envService, envVersion = testGAEService, testGAEVersion + } + if err := os.Setenv("GAE_SERVICE", envService); err != nil { + t.Fatal(err) + } + if err := os.Setenv("GAE_VERSION", envVersion); err != nil { + t.Fatal(err) + } + if tt.onGCE { + onGCE = func() bool { return true } + getProjectID = func() (string, error) { return testGCEProjectID, nil } + getZone = func() (string, error) { return testZone, nil } + getInstanceName = func() (string, error) { return testInstance, nil } + } else { + onGCE = func() bool { return false } + getProjectID = func() (string, error) { return "", fmt.Errorf("test get project id error") } + getZone = func() (string, error) { return "", fmt.Errorf("test get zone error") } + getInstanceName = func() (string, error) { return "", fmt.Errorf("test get instance error") } + } + envProjectID := "" + if tt.envProjectID { + envProjectID = testEnvProjectID + } + if err := os.Setenv("GOOGLE_CLOUD_PROJECT", envProjectID); err != nil { + t.Fatal(err) + } + + errorString := "" + if err := initializeConfig(tt.config); err != nil { + errorString = err.Error() + } + + if !strings.Contains(errorString, tt.wantErrorString) { + t.Errorf("initializeConfig(%v) got error: %v, want contain %v", tt.config, errorString, tt.wantErrorString) + } + if tt.wantErrorString == "" { + tt.wantConfig.APIAddr = apiAddress + } + if config != tt.wantConfig { + t.Errorf("initializeConfig(%v) got: %v, want %v", tt.config, config, tt.wantConfig) + } + } + + for _, tt := range []struct { + wantErrorString string + getProjectIDError bool + getZoneError bool + getInstanceError bool + }{ + { + wantErrorString: "failed to get the project ID from Compute Engine:", + getProjectIDError: true, + }, + { + wantErrorString: "failed to get zone from Compute Engine:", + getZoneError: true, + }, + { + wantErrorString: "failed to get instance from Compute Engine:", + getInstanceError: true, + }, + } { + onGCE = func() bool { return true } + if tt.getProjectIDError { + getProjectID = func() (string, error) { return "", fmt.Errorf("test get project ID error") } + } else { + getProjectID = func() (string, error) { return testGCEProjectID, nil } + } + + if tt.getZoneError { + getZone = func() (string, error) { return "", fmt.Errorf("test get zone error") } + } else { + getZone = func() (string, error) { return testZone, nil } + } + + if tt.getInstanceError { + getInstanceName = func() (string, error) { return "", fmt.Errorf("test get instance error") } + } else { + getInstanceName = func() (string, error) { return testInstance, nil } + } + errorString := "" + if err := initializeConfig(Config{Service: testService}); err != nil { + errorString = err.Error() + } + + if !strings.Contains(errorString, tt.wantErrorString) { + t.Errorf("initializeConfig() got error: %v, want contain %v", errorString, tt.wantErrorString) + } + } +} + +type fakeProfilerServer struct { + pb.ProfilerServiceServer + count int + gotProfiles map[string][]byte + done chan bool +} + +func (fs *fakeProfilerServer) CreateProfile(ctx context.Context, in *pb.CreateProfileRequest) (*pb.Profile, error) { + fs.count++ + switch fs.count { + case 1: + return &pb.Profile{Name: "testCPU", ProfileType: pb.ProfileType_CPU, Duration: ptypes.DurationProto(testProfileDuration)}, nil + case 2: + return &pb.Profile{Name: "testHeap", ProfileType: pb.ProfileType_HEAP}, nil + default: + select {} + } +} + +func (fs *fakeProfilerServer) UpdateProfile(ctx context.Context, in *pb.UpdateProfileRequest) (*pb.Profile, error) { + switch in.Profile.ProfileType { + case pb.ProfileType_CPU: + fs.gotProfiles["CPU"] = in.Profile.ProfileBytes + case pb.ProfileType_HEAP: + fs.gotProfiles["HEAP"] = in.Profile.ProfileBytes + fs.done <- true + } + + return in.Profile, nil +} + +func profileeLoop(quit chan bool) { + for { + select { + case <-quit: + return + default: + profileeWork() + } + } +} + +func profileeWork() { + data := make([]byte, 1024*1024) + rand.Read(data) + + var b bytes.Buffer + gz := gzip.NewWriter(&b) + if _, err := gz.Write(data); err != nil { + log.Println("failed to write to gzip stream", err) + return + } + if err := gz.Flush(); err != nil { + log.Println("failed to flush to gzip stream", err) + return + } + if err := gz.Close(); err != nil { + log.Println("failed to close gzip stream", err) + } +} + +func validateProfile(rawData []byte, wantFunctionName string) error { + p, err := profile.ParseData(rawData) + if err != nil { + return fmt.Errorf("ParseData failed: %v", err) + } + + if len(p.Sample) == 0 { + return fmt.Errorf("profile contains zero samples: %v", p) + } + + if len(p.Location) == 0 { + return fmt.Errorf("profile contains zero locations: %v", p) + } + + if len(p.Function) == 0 { + return fmt.Errorf("profile contains zero functions: %v", p) + } + + for _, l := range p.Location { + if len(l.Line) > 0 && l.Line[0].Function != nil && strings.Contains(l.Line[0].Function.Name, wantFunctionName) { + return nil + } + } + return fmt.Errorf("wanted function name %s not found in the profile", wantFunctionName) +} + +func TestDeltaMutexProfile(t *testing.T) { + oldMutexEnabled, oldMaxProcs := mutexEnabled, runtime.GOMAXPROCS(10) + defer func() { + mutexEnabled = oldMutexEnabled + runtime.GOMAXPROCS(oldMaxProcs) + }() + if mutexEnabled = enableMutexProfiling(); !mutexEnabled { + t.Skip("Go too old - mutex profiling not supported.") + } + + hog(time.Second, mutexHog) + go func() { + hog(2*time.Second, backgroundHog) + }() + + var prof bytes.Buffer + if err := deltaMutexProfile(context.Background(), time.Second, &prof); err != nil { + t.Fatalf("deltaMutexProfile() got error: %v", err) + } + p, err := profile.Parse(&prof) + if err != nil { + t.Fatalf("profile.Parse() got error: %v", err) + } + + if s := sum(p, "mutexHog"); s != 0 { + t.Errorf("mutexHog found in the delta mutex profile (sum=%d):\n%s", s, p) + } + if s := sum(p, "backgroundHog"); s <= 0 { + t.Errorf("backgroundHog not in the delta mutex profile (sum=%d):\n%s", s, p) + } +} + +// sum returns the sum of all mutex counts from the samples whose +// stacks include the specified function name. +func sum(p *profile.Profile, fname string) int64 { + locIDs := map[*profile.Location]bool{} + for _, loc := range p.Location { + for _, l := range loc.Line { + if strings.Contains(l.Function.Name, fname) { + locIDs[loc] = true + break + } + } + } + var s int64 + for _, sample := range p.Sample { + for _, loc := range sample.Location { + if locIDs[loc] { + s += sample.Value[0] + break + } + } + } + return s +} + +func mutexHog(mu1, mu2 *sync.Mutex, start time.Time, dt time.Duration) { + for time.Since(start) < dt { + mu1.Lock() + runtime.Gosched() + mu2.Lock() + mu1.Unlock() + mu2.Unlock() + } +} + +// backgroundHog is identical to mutexHog. We keep them separate +// in order to distinguish them with function names in the stack trace. +func backgroundHog(mu1, mu2 *sync.Mutex, start time.Time, dt time.Duration) { + for time.Since(start) < dt { + mu1.Lock() + runtime.Gosched() + mu2.Lock() + mu1.Unlock() + mu2.Unlock() + } +} + +func hog(dt time.Duration, hogger func(mu1, mu2 *sync.Mutex, start time.Time, dt time.Duration)) { + start := time.Now() + mu1 := new(sync.Mutex) + mu2 := new(sync.Mutex) + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go func() { + defer wg.Done() + hogger(mu1, mu2, start, dt) + }() + } + wg.Wait() +} + +func TestAgentWithServer(t *testing.T) { + oldDialGRPC, oldConfig := dialGRPC, config + defer func() { + dialGRPC, config = oldDialGRPC, oldConfig + }() + + srv, err := testutil.NewServer() + if err != nil { + t.Fatalf("testutil.NewServer(): %v", err) + } + fakeServer := &fakeProfilerServer{gotProfiles: map[string][]byte{}, done: make(chan bool)} + pb.RegisterProfilerServiceServer(srv.Gsrv, fakeServer) + + srv.Start() + + dialGRPC = gtransport.DialInsecure + if err := Start(Config{ + Service: testService, + ProjectID: testProjectID, + APIAddr: srv.Addr, + instance: testInstance, + zone: testZone, + }); err != nil { + t.Fatalf("Start(): %v", err) + } + + quitProfilee := make(chan bool) + go profileeLoop(quitProfilee) + + select { + case <-fakeServer.done: + case <-time.After(testServerTimeout): + t.Errorf("got timeout after %v, want fake server done", testServerTimeout) + } + quitProfilee <- true + + for _, pType := range []string{"CPU", "HEAP"} { + if profile, ok := fakeServer.gotProfiles[pType]; !ok { + t.Errorf("fakeServer.gotProfiles[%s] got no profile, want profile", pType) + } else if err := validateProfile(profile, "profilee"); err != nil { + t.Errorf("validateProfile(%s) got error: %v", pType, err) + } + } +} diff --git a/vendor/cloud.google.com/go/profiler/proftest/proftest.go b/vendor/cloud.google.com/go/profiler/proftest/proftest.go new file mode 100644 index 0000000000..7f833c49f5 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/proftest/proftest.go @@ -0,0 +1,552 @@ +// 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 proftest contains test helpers for profiler agent integration tests. +// This package is experimental. + +// golang.org/x/build/kubernetes/dialer.go imports "context" package (rather +// than "golang.org/x/net/context") and that does not exist in Go 1.6 or +// earlier. +// +build go1.7 + +package proftest + +import ( + "archive/zip" + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "strings" + "time" + + "github.com/googleapis/gax-go" + + "cloud.google.com/go/storage" + "golang.org/x/build/kubernetes" + k8sapi "golang.org/x/build/kubernetes/api" + "golang.org/x/build/kubernetes/gke" + "golang.org/x/net/context" + cloudbuild "google.golang.org/api/cloudbuild/v1" + compute "google.golang.org/api/compute/v1" + container "google.golang.org/api/container/v1" + "google.golang.org/api/googleapi" +) + +const ( + monitorWriteScope = "https://www.googleapis.com/auth/monitoring.write" + storageReadScope = "https://www.googleapis.com/auth/devstorage.read_only" +) + +// TestRunner has common elements used for testing profiling agents on a range +// of environments. +type TestRunner struct { + Client *http.Client +} + +// GCETestRunner supports testing a profiling agent on GCE. +type GCETestRunner struct { + TestRunner + ComputeService *compute.Service +} + +// GKETestRunner supports testing a profiling agent on GKE. +type GKETestRunner struct { + TestRunner + ContainerService *container.Service + StorageClient *storage.Client + Dockerfile string +} + +// ProfileResponse contains the response produced when querying profile server. +type ProfileResponse struct { + Profile ProfileData `json:"profile"` + NumProfiles int32 `json:"numProfiles"` + Deployments []interface{} `json:"deployments"` +} + +// ProfileData has data of a single profile. +type ProfileData struct { + Samples []int32 `json:"samples"` + SampleMetrics interface{} `json:"sampleMetrics"` + DefaultMetricType string `json:"defaultMetricType"` + TreeNodes interface{} `json:"treeNodes"` + Functions functionArray `json:"functions"` + SourceFiles interface{} `json:"sourceFiles"` +} + +type functionArray struct { + Name []string `json:"name"` + Sourcefile []int32 `json:"sourceFile"` +} + +// InstanceConfig is configuration for starting single GCE instance for +// profiling agent test case. +type InstanceConfig struct { + ProjectID string + Zone string + Name string + StartupScript string + MachineType string +} + +// ClusterConfig is configuration for starting single GKE cluster for profiling +// agent test case. +type ClusterConfig struct { + ProjectID string + Zone string + ClusterName string + PodName string + ImageSourceName string + ImageName string + Bucket string + Dockerfile string +} + +// HasFunction returns nil if the function is present, or, if the function is +// not present, and error providing more details why the function is not +// present. +func (pr *ProfileResponse) HasFunction(functionName string) error { + if pr.NumProfiles == 0 { + return fmt.Errorf("failed to find function name %s in profile: profile response contains zero profiles: %v", functionName, pr) + } + if len(pr.Deployments) == 0 { + return fmt.Errorf("failed to find function name %s in profile: profile response contains zero deployments: %v", functionName, pr) + } + if len(pr.Profile.Functions.Name) == 0 { + return fmt.Errorf("failed to find function name %s in profile: profile does not have function data", functionName) + } + + for _, name := range pr.Profile.Functions.Name { + if strings.Contains(name, functionName) { + return nil + } + } + return fmt.Errorf("failed to find function name %s in profile", functionName) +} + +// StartInstance starts a GCE Instance with name, zone, and projectId specified +// by the inst, and which runs the startup script specified in inst. +func (tr *GCETestRunner) StartInstance(ctx context.Context, inst *InstanceConfig) error { + img, err := tr.ComputeService.Images.GetFromFamily("debian-cloud", "debian-9").Context(ctx).Do() + if err != nil { + return err + } + + op, err := tr.ComputeService.Instances.Insert(inst.ProjectID, inst.Zone, &compute.Instance{ + MachineType: fmt.Sprintf("zones/%s/machineTypes/%s", inst.Zone, inst.MachineType), + Name: inst.Name, + Disks: []*compute.AttachedDisk{{ + AutoDelete: true, // delete the disk when the VM is deleted. + Boot: true, + Type: "PERSISTENT", + Mode: "READ_WRITE", + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: img.SelfLink, + DiskType: fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/zones/%s/diskTypes/pd-standard", inst.ProjectID, inst.Zone), + }, + }}, + NetworkInterfaces: []*compute.NetworkInterface{{ + Network: fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/networks/default", inst.ProjectID), + AccessConfigs: []*compute.AccessConfig{{ + Name: "External NAT", + }}, + }}, + Metadata: &compute.Metadata{ + Items: []*compute.MetadataItems{{ + Key: "startup-script", + Value: googleapi.String(inst.StartupScript), + }}, + }, + ServiceAccounts: []*compute.ServiceAccount{{ + Email: "default", + Scopes: []string{ + monitorWriteScope, + }, + }}, + }).Do() + + if err != nil { + return fmt.Errorf("failed to create instance: %v", err) + } + + // Poll status of the operation to create the instance. + getOpCall := tr.ComputeService.ZoneOperations.Get(inst.ProjectID, inst.Zone, op.Name) + for { + if err := checkOpErrors(op); err != nil { + return fmt.Errorf("failed to create instance: %v", err) + } + if op.Status == "DONE" { + return nil + } + + if err := gax.Sleep(ctx, 5*time.Second); err != nil { + return err + } + + op, err = getOpCall.Do() + if err != nil { + return fmt.Errorf("failed to get operation: %v", err) + } + } +} + +// checkOpErrors returns nil if the operation does not have any errors and an +// error summarizing all errors encountered if the operation has errored. +func checkOpErrors(op *compute.Operation) error { + if op.Error == nil || len(op.Error.Errors) == 0 { + return nil + } + + var errs []string + for _, e := range op.Error.Errors { + if e.Message != "" { + errs = append(errs, e.Message) + } else { + errs = append(errs, e.Code) + } + } + return errors.New(strings.Join(errs, ",")) +} + +// DeleteInstance deletes an instance with project id, name, and zone matched +// by inst. +func (tr *GCETestRunner) DeleteInstance(ctx context.Context, inst *InstanceConfig) error { + if _, err := tr.ComputeService.Instances.Delete(inst.ProjectID, inst.Zone, inst.Name).Context(ctx).Do(); err != nil { + return fmt.Errorf("Instances.Delete(%s) got error: %v", inst.Name, err) + } + return nil +} + +// PollForSerialOutput polls serial port 2 of the GCE instance specified by +// inst and returns when the finishString appears in the serial output +// of the instance, or when the context times out. +func (tr *GCETestRunner) PollForSerialOutput(ctx context.Context, inst *InstanceConfig, finishString string) error { + var output string + defer func() { + log.Printf("Serial port output for %s:\n%s", inst.Name, output) + }() + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(20 * time.Second): + resp, err := tr.ComputeService.Instances.GetSerialPortOutput(inst.ProjectID, inst.Zone, inst.Name).Port(2).Context(ctx).Do() + if err != nil { + // Transient failure. + log.Printf("Transient error getting serial port output from instance %s (will retry): %v", inst.Name, err) + continue + } + if resp.Contents == "" { + log.Printf("Ignoring empty serial port output from instance %s (will retry)", inst.Name) + continue + } + if output = resp.Contents; strings.Contains(output, finishString) { + return nil + } + } + } +} + +// QueryProfiles retrieves profiles of a specific type, from a specific time +// range, associated with a particular service and project. +func (tr *TestRunner) QueryProfiles(projectID, service, startTime, endTime, profileType string) (ProfileResponse, error) { + queryURL := fmt.Sprintf("https://cloudprofiler.googleapis.com/v2/projects/%s/profiles:query", projectID) + const queryJSONFmt = `{"endTime": "%s", "profileType": "%s","startTime": "%s", "target": "%s"}` + + queryRequest := fmt.Sprintf(queryJSONFmt, endTime, profileType, startTime, service) + + resp, err := tr.Client.Post(queryURL, "application/json", strings.NewReader(queryRequest)) + if err != nil { + return ProfileResponse{}, fmt.Errorf("failed to query API: %v", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ProfileResponse{}, fmt.Errorf("failed to read response body: %v", err) + } + + var pr ProfileResponse + if err := json.Unmarshal(body, &pr); err != nil { + return ProfileResponse{}, err + } + + return pr, nil +} + +// createAndPublishDockerImage creates a docker image from source code in a GCS +// bucket and pushes the image to Google Container Registry. +func (tr *GKETestRunner) createAndPublishDockerImage(ctx context.Context, projectID, sourceBucket, sourceObject, ImageName string) error { + cloudbuildService, err := cloudbuild.New(tr.Client) + + build := &cloudbuild.Build{ + Source: &cloudbuild.Source{ + StorageSource: &cloudbuild.StorageSource{ + Bucket: sourceBucket, + Object: sourceObject, + }, + }, + Steps: []*cloudbuild.BuildStep{ + { + Name: "gcr.io/cloud-builders/docker", + Args: []string{"build", "-t", ImageName, "."}, + }, + }, + Images: []string{ImageName}, + } + + op, err := cloudbuildService.Projects.Builds.Create(projectID, build).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to create image: %v", err) + } + opID := op.Name + + // Wait for creating image. + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timed out waiting creating image") + + case <-time.After(10 * time.Second): + op, err := cloudbuildService.Operations.Get(opID).Context(ctx).Do() + if err != nil { + log.Printf("Transient error getting operation (will retry): %v", err) + break + } + if op.Done == true { + log.Printf("Published image %s to Google Container Registry.", ImageName) + return nil + } + } + } +} + +type imageResponse struct { + Manifest map[string]interface{} `json:"manifest"` + Name string `json:"name"` + Tags []string `json:"tags"` +} + +// deleteDockerImage deletes a docker image from Google Container Registry. +func (tr *GKETestRunner) deleteDockerImage(ctx context.Context, ImageName string) []error { + queryImageURL := fmt.Sprintf("https://gcr.io/v2/%s/tags/list", ImageName) + resp, err := tr.Client.Get(queryImageURL) + if err != nil { + return []error{fmt.Errorf("failed to list tags: %v", err)} + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return []error{err} + } + var ir imageResponse + if err := json.Unmarshal(body, &ir); err != nil { + return []error{err} + } + + const deleteImageURLFmt = "https://gcr.io/v2/%s/manifests/%s" + var errs []error + for _, tag := range ir.Tags { + if err := deleteDockerImageResource(tr.Client, fmt.Sprintf(deleteImageURLFmt, ImageName, tag)); err != nil { + errs = append(errs, fmt.Errorf("failed to delete tag %s: %v", tag, err)) + } + } + + for manifest := range ir.Manifest { + if err := deleteDockerImageResource(tr.Client, fmt.Sprintf(deleteImageURLFmt, ImageName, manifest)); err != nil { + errs = append(errs, fmt.Errorf("failed to delete manifest %s: %v", manifest, err)) + } + } + return errs +} + +func deleteDockerImageResource(client *http.Client, url string) error { + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return fmt.Errorf("failed to get request: %v", err) + } + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to delete resource: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("failed to delete resource: status code = %d", resp.StatusCode) + } + return nil +} + +func (tr *GKETestRunner) createCluster(ctx context.Context, client *http.Client, projectID, zone, ClusterName string) error { + request := &container.CreateClusterRequest{Cluster: &container.Cluster{ + Name: ClusterName, + InitialNodeCount: 3, + NodeConfig: &container.NodeConfig{ + OauthScopes: []string{ + storageReadScope, + }, + }, + }} + op, err := tr.ContainerService.Projects.Zones.Clusters.Create(projectID, zone, request).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to create cluster %s: %v", ClusterName, err) + } + opID := op.Name + + // Wait for creating cluster. + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timed out waiting creating cluster") + + case <-time.After(10 * time.Second): + op, err := tr.ContainerService.Projects.Zones.Operations.Get(projectID, zone, opID).Context(ctx).Do() + if err != nil { + log.Printf("Transient error getting operation (will retry): %v", err) + break + } + if op.Status == "DONE" { + log.Printf("Created cluster %s.", ClusterName) + return nil + } + if op.Status == "ABORTING" { + return fmt.Errorf("create cluster operation is aborted") + } + } + } +} + +func (tr *GKETestRunner) deployContainer(ctx context.Context, kubernetesClient *kubernetes.Client, podName, ImageName string) error { + // TODO: Pod restart policy defaults to "Always". Previous logs will disappear + // after restarting. Always restart causes the test not be able to see the + // finish signal. Should probably set the restart policy to "OnFailure" when + // we get the GKE workflow working and testable. + pod := &k8sapi.Pod{ + ObjectMeta: k8sapi.ObjectMeta{ + Name: podName, + }, + Spec: k8sapi.PodSpec{ + Containers: []k8sapi.Container{ + { + Name: "profiler-test", + Image: fmt.Sprintf("gcr.io/%s:latest", ImageName), + }, + }, + }, + } + if _, err := kubernetesClient.RunLongLivedPod(ctx, pod); err != nil { + return fmt.Errorf("failed to run pod %s: %v", podName, err) + } + return nil +} + +// PollPodLog polls the log of the kubernetes client and returns when the +// finishString appears in the log, or when the context times out. +func (tr *GKETestRunner) PollPodLog(ctx context.Context, kubernetesClient *kubernetes.Client, podName, finishString string) error { + var output string + defer func() { + log.Printf("Log for pod %s:\n%s", podName, output) + }() + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timed out waiting profiling finishing on container") + + case <-time.After(20 * time.Second): + var err error + output, err = kubernetesClient.PodLog(ctx, podName) + if err != nil { + // Transient failure. + log.Printf("Transient error getting log (will retry): %v", err) + continue + } + if strings.Contains(output, finishString) { + return nil + } + } + } +} + +// DeleteClusterAndImage deletes cluster and images used to create cluster. +func (tr *GKETestRunner) DeleteClusterAndImage(ctx context.Context, cfg *ClusterConfig) []error { + var errs []error + if err := tr.StorageClient.Bucket(cfg.Bucket).Object(cfg.ImageSourceName).Delete(ctx); err != nil { + errs = append(errs, fmt.Errorf("failed to delete storage client: %v", err)) + } + for _, err := range tr.deleteDockerImage(ctx, cfg.ImageName) { + errs = append(errs, fmt.Errorf("failed to delete docker image: %v", err)) + } + if _, err := tr.ContainerService.Projects.Zones.Clusters.Delete(cfg.ProjectID, cfg.Zone, cfg.ClusterName).Context(ctx).Do(); err != nil { + errs = append(errs, fmt.Errorf("failed to delete cluster %s: %v", cfg.ClusterName, err)) + } + + return errs +} + +// StartAndDeployCluster creates image needed for cluster, then starts and +// deploys to cluster. +func (tr *GKETestRunner) StartAndDeployCluster(ctx context.Context, cfg *ClusterConfig) error { + if err := tr.uploadImageSource(ctx, cfg.Bucket, cfg.ImageSourceName, cfg.Dockerfile); err != nil { + return fmt.Errorf("failed to upload image source: %v", err) + } + + createImageCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) + defer cancel() + if err := tr.createAndPublishDockerImage(createImageCtx, cfg.ProjectID, cfg.Bucket, cfg.ImageSourceName, fmt.Sprintf("gcr.io/%s", cfg.ImageName)); err != nil { + return fmt.Errorf("failed to create and publish docker image %s: %v", cfg.ImageName, err) + } + + kubernetesClient, err := gke.NewClient(ctx, cfg.ClusterName, gke.OptZone(cfg.Zone), gke.OptProject(cfg.ProjectID)) + if err != nil { + return fmt.Errorf("failed to create new GKE client: %v", err) + } + + deployContainerCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) + defer cancel() + if err := tr.deployContainer(deployContainerCtx, kubernetesClient, cfg.PodName, cfg.ImageName); err != nil { + return fmt.Errorf("failed to deploy image %q to pod %q: %v", cfg.PodName, cfg.ImageName, err) + } + return nil +} + +// uploadImageSource uploads source code for building docker image to GCS. +func (tr *GKETestRunner) uploadImageSource(ctx context.Context, bucket, objectName, dockerfile string) error { + zipBuf := new(bytes.Buffer) + z := zip.NewWriter(zipBuf) + f, err := z.Create("Dockerfile") + if err != nil { + return err + } + + if _, err := f.Write([]byte(dockerfile)); err != nil { + return err + } + + if err := z.Close(); err != nil { + return err + } + wc := tr.StorageClient.Bucket(bucket).Object(objectName).NewWriter(ctx) + wc.ContentType = "application/zip" + wc.ACL = []storage.ACLRule{{storage.AllUsers, storage.RoleReader}} + if _, err := wc.Write(zipBuf.Bytes()); err != nil { + return err + } + return wc.Close() +} diff --git a/vendor/cloud.google.com/go/profiler/symbolizer.go b/vendor/cloud.google.com/go/profiler/symbolizer.go new file mode 100644 index 0000000000..0233b60563 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/symbolizer.go @@ -0,0 +1,143 @@ +// Copyright 2017 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 profiler + +import ( + "bytes" + "regexp" + "runtime" + "strings" + + "github.com/google/pprof/profile" +) + +var shouldAssumeSymbolized = isSymbolizedGoVersion(runtime.Version()) + +type function interface { + Name() string + FileLine(pc uintptr) (string, int) +} + +// funcForPC is a wrapper for runtime.FuncForPC. Defined as var for testing. +var funcForPC = func(pc uintptr) function { + if f := runtime.FuncForPC(pc); f != nil { + return f + } + return nil +} + +// parseAndSymbolize parses a profile from a buffer, symbolizes it +// if it's not yet symbolized, and writes the profile back as a +// gzip-compressed marshaled protobuf. +func parseAndSymbolize(data *bytes.Buffer) error { + p, err := profile.ParseData(data.Bytes()) + if err != nil { + return err + } + + // Do nothing if the profile is already symbolized. + if symbolized(p) { + return nil + } + // Clear the profile functions to avoid creating duplicates. + p.Function = nil + symbolize(p) + data.Reset() + return p.Write(data) +} + +// isSymbolizedGoVersion returns true if Go version equals to or is +// higher than Go 1.9. Starting Go 1.9 the profiles are symbolized +// by runtime/pprof. +func isSymbolizedGoVersion(goVersion string) bool { + r, err := regexp.Compile(`go(1\.9|1\.[1-9][0-9]|[2-9]).*`) + if err == nil && r.MatchString(goVersion) { + return true + } + return false +} + +// symbolized checks if all locations have symbolized function +// information. +func symbolized(p *profile.Profile) bool { + for _, l := range p.Location { + if len(l.Line) == 0 || l.Line[0].Function == nil { + return false + } + } + return true +} + +func symbolize(p *profile.Profile) { + fns := profileFunctionMap{} + for _, l := range p.Location { + pc := uintptr(l.Address) + f := funcForPC(pc) + if f == nil { + continue + } + file, lineno := f.FileLine(pc) + l.Line = []profile.Line{ + { + Function: fns.findOrAddFunction(f.Name(), file, p), + Line: int64(lineno), + }, + } + } + // Trim runtime functions. Always hide runtime.goexit. Other runtime + // functions are only hidden for heap profile when they appear at the beginning. + isHeapProfile := p.PeriodType != nil && p.PeriodType.Type == "space" + for _, s := range p.Sample { + show := !isHeapProfile + var i int + for _, l := range s.Location { + if len(l.Line) > 0 && l.Line[0].Function != nil { + name := l.Line[0].Function.Name + if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") { + continue + } + } + show = true + s.Location[i] = l + i++ + } + // If all locations of a sample are trimmed, keep the root location. + if i == 0 && len(s.Location) > 0 { + s.Location[0] = s.Location[len(s.Location)-1] + i = 1 + } + s.Location = s.Location[:i] + } +} + +type profileFunctionMap map[profile.Function]*profile.Function + +func (fns profileFunctionMap) findOrAddFunction(name, filename string, p *profile.Profile) *profile.Function { + f := profile.Function{ + Name: name, + SystemName: name, + Filename: filename, + } + if fp := fns[f]; fp != nil { + return fp + } + fp := new(profile.Function) + fns[f] = fp + + *fp = f + fp.ID = uint64(len(p.Function) + 1) + p.Function = append(p.Function, fp) + return fp +} diff --git a/vendor/cloud.google.com/go/profiler/symbolizer_test.go b/vendor/cloud.google.com/go/profiler/symbolizer_test.go new file mode 100644 index 0000000000..c2fd8d3f79 --- /dev/null +++ b/vendor/cloud.google.com/go/profiler/symbolizer_test.go @@ -0,0 +1,229 @@ +// Copyright 2017 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 profiler + +import ( + "bytes" + "testing" + + "cloud.google.com/go/internal/testutil" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/pprof/profile" +) + +type fakeFunc struct { + name string + file string + lineno int +} + +func (f *fakeFunc) Name() string { + return f.name +} +func (f *fakeFunc) FileLine(_ uintptr) (string, int) { + return f.file, f.lineno +} + +var cmpOpt = cmpopts.IgnoreUnexported(profile.Profile{}, profile.Function{}, + profile.Line{}, profile.Location{}, profile.Sample{}, profile.ValueType{}) + +// TestRuntimeFunctionTrimming tests if symbolize trims runtime functions as intended. +func TestRuntimeFunctionTrimming(t *testing.T) { + fakeFuncMap := map[uintptr]*fakeFunc{ + 0x10: &fakeFunc{"runtime.goexit", "runtime.go", 10}, + 0x20: &fakeFunc{"runtime.other", "runtime.go", 20}, + 0x30: &fakeFunc{"foo", "foo.go", 30}, + 0x40: &fakeFunc{"bar", "bar.go", 40}, + } + backupFuncForPC := funcForPC + funcForPC = func(pc uintptr) function { + return fakeFuncMap[pc] + } + defer func() { + funcForPC = backupFuncForPC + }() + testLoc := []*profile.Location{ + {ID: 1, Address: 0x10}, + {ID: 2, Address: 0x20}, + {ID: 3, Address: 0x30}, + {ID: 4, Address: 0x40}, + } + testProfile := &profile.Profile{ + Sample: []*profile.Sample{ + {Location: []*profile.Location{testLoc[0], testLoc[1], testLoc[3], testLoc[2]}}, + {Location: []*profile.Location{testLoc[1], testLoc[3], testLoc[2]}}, + {Location: []*profile.Location{testLoc[3], testLoc[2], testLoc[1]}}, + {Location: []*profile.Location{testLoc[3], testLoc[2], testLoc[0]}}, + {Location: []*profile.Location{testLoc[0], testLoc[1], testLoc[3], testLoc[0]}}, + {Location: []*profile.Location{testLoc[1], testLoc[0]}}, + }, + Location: testLoc, + } + testProfiles := make([]*profile.Profile, 2) + testProfiles[0] = testProfile.Copy() + testProfiles[1] = testProfile.Copy() + // Test case for CPU profile. + testProfiles[0].PeriodType = &profile.ValueType{Type: "cpu", Unit: "nanoseconds"} + // Test case for heap profile. + testProfiles[1].PeriodType = &profile.ValueType{Type: "space", Unit: "bytes"} + wantFunc := []*profile.Function{ + {ID: 1, Name: "runtime.goexit", SystemName: "runtime.goexit", Filename: "runtime.go"}, + {ID: 2, Name: "runtime.other", SystemName: "runtime.other", Filename: "runtime.go"}, + {ID: 3, Name: "foo", SystemName: "foo", Filename: "foo.go"}, + {ID: 4, Name: "bar", SystemName: "bar", Filename: "bar.go"}, + } + wantLoc := []*profile.Location{ + {ID: 1, Address: 0x10, Line: []profile.Line{{Function: wantFunc[0], Line: 10}}}, + {ID: 2, Address: 0x20, Line: []profile.Line{{Function: wantFunc[1], Line: 20}}}, + {ID: 3, Address: 0x30, Line: []profile.Line{{Function: wantFunc[2], Line: 30}}}, + {ID: 4, Address: 0x40, Line: []profile.Line{{Function: wantFunc[3], Line: 40}}}, + } + wantProfiles := []*profile.Profile{ + { + PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}, + Sample: []*profile.Sample{ + {Location: []*profile.Location{wantLoc[1], wantLoc[3], wantLoc[2]}}, + {Location: []*profile.Location{wantLoc[1], wantLoc[3], wantLoc[2]}}, + {Location: []*profile.Location{wantLoc[3], wantLoc[2], wantLoc[1]}}, + {Location: []*profile.Location{wantLoc[3], wantLoc[2]}}, + {Location: []*profile.Location{wantLoc[1], wantLoc[3]}}, + {Location: []*profile.Location{wantLoc[1]}}, + }, + Location: wantLoc, + Function: wantFunc, + }, + { + PeriodType: &profile.ValueType{Type: "space", Unit: "bytes"}, + Sample: []*profile.Sample{ + {Location: []*profile.Location{wantLoc[3], wantLoc[2]}}, + {Location: []*profile.Location{wantLoc[3], wantLoc[2]}}, + {Location: []*profile.Location{wantLoc[3], wantLoc[2], wantLoc[1]}}, + {Location: []*profile.Location{wantLoc[3], wantLoc[2]}}, + {Location: []*profile.Location{wantLoc[3]}}, + {Location: []*profile.Location{wantLoc[0]}}, + }, + Location: wantLoc, + Function: wantFunc, + }, + } + for i := 0; i < 2; i++ { + symbolize(testProfiles[i]) + if !testutil.Equal(testProfiles[i], wantProfiles[i], cmpOpt) { + t.Errorf("incorrect trimming (testcase = %d): got {%v}, want {%v}", i, testProfiles[i], wantProfiles[i]) + } + } +} + +// TestParseAndSymbolize tests if parseAndSymbolize parses and symbolizes +// profiles as intended. +func TestParseAndSymbolize(t *testing.T) { + fakeFuncMap := map[uintptr]*fakeFunc{ + 0x10: &fakeFunc{"foo", "foo.go", 10}, + 0x20: &fakeFunc{"bar", "bar.go", 20}, + } + backupFuncForPC := funcForPC + funcForPC = func(pc uintptr) function { + return fakeFuncMap[pc] + } + defer func() { + funcForPC = backupFuncForPC + }() + + testLoc := []*profile.Location{ + {ID: 1, Address: 0x10}, + {ID: 2, Address: 0x20}, + } + testProfile := &profile.Profile{ + SampleType: []*profile.ValueType{ + &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}, + }, + PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}, + Sample: []*profile.Sample{ + {Location: []*profile.Location{testLoc[0], testLoc[1]}, Value: []int64{1}}, + {Location: []*profile.Location{testLoc[1]}, Value: []int64{1}}, + }, + Location: testLoc, + } + testProfiles := make([]*profile.Profile, 2) + testProfiles[0] = testProfile.Copy() + testProfiles[1] = testProfile.Copy() + + wantFunc := []*profile.Function{ + {ID: 1, Name: "foo", SystemName: "foo", Filename: "foo.go"}, + {ID: 2, Name: "bar", SystemName: "bar", Filename: "bar.go"}, + } + wantLoc := []*profile.Location{ + {ID: 1, Address: 0x10, Line: []profile.Line{{Function: wantFunc[0], Line: 10}}}, + {ID: 2, Address: 0x20, Line: []profile.Line{{Function: wantFunc[1], Line: 20}}}, + } + wantProfile := &profile.Profile{ + SampleType: []*profile.ValueType{ + &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}, + }, + PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}, + Sample: []*profile.Sample{ + {Location: []*profile.Location{wantLoc[0], wantLoc[1]}, Value: []int64{1}}, + {Location: []*profile.Location{wantLoc[1]}, Value: []int64{1}}, + }, + Location: wantLoc, + Function: wantFunc, + } + + // Profile already symbolized. + testProfiles[1].Location = []*profile.Location{ + {ID: 1, Address: 0x10, Line: []profile.Line{{Function: wantFunc[0], Line: 10}}}, + {ID: 2, Address: 0x20, Line: []profile.Line{{Function: wantFunc[1], Line: 20}}}, + } + testProfiles[1].Function = []*profile.Function{ + {ID: 1, Name: "foo", SystemName: "foo", Filename: "foo.go"}, + {ID: 2, Name: "bar", SystemName: "bar", Filename: "bar.go"}, + } + for i := 0; i < 2; i++ { + var prof bytes.Buffer + testProfiles[i].Write(&prof) + + parseAndSymbolize(&prof) + gotProfile, err := profile.ParseData(prof.Bytes()) + if err != nil { + t.Errorf("parsing symbolized profile (testcase = %d) got err: %v, want no error", i, err) + } + if !testutil.Equal(gotProfile, wantProfile, cmpOpt) { + t.Errorf("incorrect symbolization (testcase = %d): got {%v}, want {%v}", i, gotProfile, wantProfile) + } + } +} + +func TestIsSymbolizedGoVersion(t *testing.T) { + for _, tc := range []struct { + input string + want bool + }{ + {"go1.9beta2", true}, + {"go1.9", true}, + {"go1.9.1", true}, + {"go1.10", true}, + {"go1.10.1", true}, + {"go2.0", true}, + {"go3.1", true}, + {"go1.8", false}, + {"go1.8.1", false}, + {"go1.7", false}, + {"devel ", false}, + } { + if got := isSymbolizedGoVersion(tc.input); got != tc.want { + t.Errorf("isSymbolizedGoVersion(%v) got %v, want %v", tc.input, got, tc.want) + } + } +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/ListTopics_smoke_test.go b/vendor/cloud.google.com/go/pubsub/apiv1/ListTopics_smoke_test.go new file mode 100644 index 0000000000..bece095411 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/ListTopics_smoke_test.go @@ -0,0 +1,67 @@ +// 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 pubsub + +import ( + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestPublisherSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewPublisherClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var formattedProject string = fmt.Sprintf("projects/%s", projectId) + var request = &pubsubpb.ListTopicsRequest{ + Project: formattedProject, + } + + iter := c.ListTopics(ctx, request) + if _, err := iter.Next(); err != nil && err != iterator.Done { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/README.md b/vendor/cloud.google.com/go/pubsub/apiv1/README.md new file mode 100644 index 0000000000..b5967ab9c5 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/README.md @@ -0,0 +1,9 @@ +Auto-generated pubsub v1 clients +================================= + +This package includes auto-generated clients for the pubsub v1 API. + +Use the handwritten client (in the parent directory, +cloud.google.com/go/pubsub) in preference to this. + +This code is EXPERIMENTAL and subject to CHANGE AT ANY TIME. diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/doc.go b/vendor/cloud.google.com/go/pubsub/apiv1/doc.go new file mode 100644 index 0000000000..ccdce27d66 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/doc.go @@ -0,0 +1,50 @@ +// 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 pubsub is an auto-generated package for the +// Google Cloud Pub/Sub API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Provides reliable, many-to-many, asynchronous messaging between +// applications. +// +// Use the client at cloud.google.com/go/pubsub in preference to this. +package pubsub // import "cloud.google.com/go/pubsub/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/pubsub", + } +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/mock_test.go b/vendor/cloud.google.com/go/pubsub/apiv1/mock_test.go new file mode 100644 index 0000000000..6eaad6db8e --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/mock_test.go @@ -0,0 +1,1878 @@ +// 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 pubsub + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + timestamppb "github.com/golang/protobuf/ptypes/timestamp" + iampb "google.golang.org/genproto/googleapis/iam/v1" + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" + field_maskpb "google.golang.org/genproto/protobuf/field_mask" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockPublisherServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + pubsubpb.PublisherServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockPublisherServer) CreateTopic(ctx context.Context, req *pubsubpb.Topic) (*pubsubpb.Topic, 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].(*pubsubpb.Topic), nil +} + +func (s *mockPublisherServer) UpdateTopic(ctx context.Context, req *pubsubpb.UpdateTopicRequest) (*pubsubpb.Topic, 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].(*pubsubpb.Topic), nil +} + +func (s *mockPublisherServer) Publish(ctx context.Context, req *pubsubpb.PublishRequest) (*pubsubpb.PublishResponse, 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].(*pubsubpb.PublishResponse), nil +} + +func (s *mockPublisherServer) GetTopic(ctx context.Context, req *pubsubpb.GetTopicRequest) (*pubsubpb.Topic, 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].(*pubsubpb.Topic), nil +} + +func (s *mockPublisherServer) ListTopics(ctx context.Context, req *pubsubpb.ListTopicsRequest) (*pubsubpb.ListTopicsResponse, 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].(*pubsubpb.ListTopicsResponse), nil +} + +func (s *mockPublisherServer) ListTopicSubscriptions(ctx context.Context, req *pubsubpb.ListTopicSubscriptionsRequest) (*pubsubpb.ListTopicSubscriptionsResponse, 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].(*pubsubpb.ListTopicSubscriptionsResponse), nil +} + +func (s *mockPublisherServer) DeleteTopic(ctx context.Context, req *pubsubpb.DeleteTopicRequest) (*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 +} + +type mockIamPolicyServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + iampb.IAMPolicyServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockIamPolicyServer) 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 *mockIamPolicyServer) 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 *mockIamPolicyServer) 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 +} + +type mockSubscriberServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + pubsubpb.SubscriberServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockSubscriberServer) CreateSubscription(ctx context.Context, req *pubsubpb.Subscription) (*pubsubpb.Subscription, 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].(*pubsubpb.Subscription), nil +} + +func (s *mockSubscriberServer) GetSubscription(ctx context.Context, req *pubsubpb.GetSubscriptionRequest) (*pubsubpb.Subscription, 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].(*pubsubpb.Subscription), nil +} + +func (s *mockSubscriberServer) UpdateSubscription(ctx context.Context, req *pubsubpb.UpdateSubscriptionRequest) (*pubsubpb.Subscription, 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].(*pubsubpb.Subscription), nil +} + +func (s *mockSubscriberServer) ListSubscriptions(ctx context.Context, req *pubsubpb.ListSubscriptionsRequest) (*pubsubpb.ListSubscriptionsResponse, 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].(*pubsubpb.ListSubscriptionsResponse), nil +} + +func (s *mockSubscriberServer) DeleteSubscription(ctx context.Context, req *pubsubpb.DeleteSubscriptionRequest) (*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 *mockSubscriberServer) ModifyAckDeadline(ctx context.Context, req *pubsubpb.ModifyAckDeadlineRequest) (*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 *mockSubscriberServer) Acknowledge(ctx context.Context, req *pubsubpb.AcknowledgeRequest) (*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 *mockSubscriberServer) Pull(ctx context.Context, req *pubsubpb.PullRequest) (*pubsubpb.PullResponse, 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].(*pubsubpb.PullResponse), nil +} + +func (s *mockSubscriberServer) StreamingPull(stream pubsubpb.Subscriber_StreamingPullServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + for { + if req, err := stream.Recv(); err == io.EOF { + break + } else if err != nil { + return err + } else { + s.reqs = append(s.reqs, req) + } + } + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*pubsubpb.StreamingPullResponse)); err != nil { + return err + } + } + return nil +} + +func (s *mockSubscriberServer) ModifyPushConfig(ctx context.Context, req *pubsubpb.ModifyPushConfigRequest) (*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 *mockSubscriberServer) ListSnapshots(ctx context.Context, req *pubsubpb.ListSnapshotsRequest) (*pubsubpb.ListSnapshotsResponse, 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].(*pubsubpb.ListSnapshotsResponse), nil +} + +func (s *mockSubscriberServer) CreateSnapshot(ctx context.Context, req *pubsubpb.CreateSnapshotRequest) (*pubsubpb.Snapshot, 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].(*pubsubpb.Snapshot), nil +} + +func (s *mockSubscriberServer) UpdateSnapshot(ctx context.Context, req *pubsubpb.UpdateSnapshotRequest) (*pubsubpb.Snapshot, 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].(*pubsubpb.Snapshot), nil +} + +func (s *mockSubscriberServer) DeleteSnapshot(ctx context.Context, req *pubsubpb.DeleteSnapshotRequest) (*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 *mockSubscriberServer) Seek(ctx context.Context, req *pubsubpb.SeekRequest) (*pubsubpb.SeekResponse, 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].(*pubsubpb.SeekResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockPublisher mockPublisherServer + mockIamPolicy mockIamPolicyServer + mockSubscriber mockSubscriberServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + pubsubpb.RegisterPublisherServer(serv, &mockPublisher) + iampb.RegisterIAMPolicyServer(serv, &mockIamPolicy) + pubsubpb.RegisterSubscriberServer(serv, &mockSubscriber) + + 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 TestPublisherCreateTopic(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &pubsubpb.Topic{ + Name: name2, + } + + mockPublisher.err = nil + mockPublisher.reqs = nil + + mockPublisher.resps = append(mockPublisher.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.Topic{ + Name: formattedName, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateTopic(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockPublisher.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 TestPublisherCreateTopicError(t *testing.T) { + errCode := codes.PermissionDenied + mockPublisher.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.Topic{ + Name: formattedName, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateTopic(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 TestPublisherUpdateTopic(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &pubsubpb.Topic{ + Name: name, + } + + mockPublisher.err = nil + mockPublisher.reqs = nil + + mockPublisher.resps = append(mockPublisher.resps[:0], expectedResponse) + + var topic *pubsubpb.Topic = &pubsubpb.Topic{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &pubsubpb.UpdateTopicRequest{ + Topic: topic, + UpdateMask: updateMask, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateTopic(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockPublisher.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 TestPublisherUpdateTopicError(t *testing.T) { + errCode := codes.PermissionDenied + mockPublisher.err = gstatus.Error(errCode, "test error") + + var topic *pubsubpb.Topic = &pubsubpb.Topic{} + var updateMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &pubsubpb.UpdateTopicRequest{ + Topic: topic, + UpdateMask: updateMask, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateTopic(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 TestPublisherPublish(t *testing.T) { + var messageIdsElement string = "messageIdsElement-744837059" + var messageIds = []string{messageIdsElement} + var expectedResponse = &pubsubpb.PublishResponse{ + MessageIds: messageIds, + } + + mockPublisher.err = nil + mockPublisher.reqs = nil + + mockPublisher.resps = append(mockPublisher.resps[:0], expectedResponse) + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var data []byte = []byte("-86") + var messagesElement = &pubsubpb.PubsubMessage{ + Data: data, + } + var messages = []*pubsubpb.PubsubMessage{messagesElement} + var request = &pubsubpb.PublishRequest{ + Topic: formattedTopic, + Messages: messages, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Publish(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockPublisher.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 TestPublisherPublishError(t *testing.T) { + errCode := codes.PermissionDenied + mockPublisher.err = gstatus.Error(errCode, "test error") + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var data []byte = []byte("-86") + var messagesElement = &pubsubpb.PubsubMessage{ + Data: data, + } + var messages = []*pubsubpb.PubsubMessage{messagesElement} + var request = &pubsubpb.PublishRequest{ + Topic: formattedTopic, + Messages: messages, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Publish(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 TestPublisherGetTopic(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &pubsubpb.Topic{ + Name: name, + } + + mockPublisher.err = nil + mockPublisher.reqs = nil + + mockPublisher.resps = append(mockPublisher.resps[:0], expectedResponse) + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.GetTopicRequest{ + Topic: formattedTopic, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTopic(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockPublisher.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 TestPublisherGetTopicError(t *testing.T) { + errCode := codes.PermissionDenied + mockPublisher.err = gstatus.Error(errCode, "test error") + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.GetTopicRequest{ + Topic: formattedTopic, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTopic(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 TestPublisherListTopics(t *testing.T) { + var nextPageToken string = "" + var topicsElement *pubsubpb.Topic = &pubsubpb.Topic{} + var topics = []*pubsubpb.Topic{topicsElement} + var expectedResponse = &pubsubpb.ListTopicsResponse{ + NextPageToken: nextPageToken, + Topics: topics, + } + + mockPublisher.err = nil + mockPublisher.reqs = nil + + mockPublisher.resps = append(mockPublisher.resps[:0], expectedResponse) + + var formattedProject string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &pubsubpb.ListTopicsRequest{ + Project: formattedProject, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTopics(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockPublisher.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Topics[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 TestPublisherListTopicsError(t *testing.T) { + errCode := codes.PermissionDenied + mockPublisher.err = gstatus.Error(errCode, "test error") + + var formattedProject string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &pubsubpb.ListTopicsRequest{ + Project: formattedProject, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTopics(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 TestPublisherListTopicSubscriptions(t *testing.T) { + var nextPageToken string = "" + var subscriptionsElement string = "subscriptionsElement1698708147" + var subscriptions = []string{subscriptionsElement} + var expectedResponse = &pubsubpb.ListTopicSubscriptionsResponse{ + NextPageToken: nextPageToken, + Subscriptions: subscriptions, + } + + mockPublisher.err = nil + mockPublisher.reqs = nil + + mockPublisher.resps = append(mockPublisher.resps[:0], expectedResponse) + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.ListTopicSubscriptionsRequest{ + Topic: formattedTopic, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTopicSubscriptions(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockPublisher.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Subscriptions[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 TestPublisherListTopicSubscriptionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockPublisher.err = gstatus.Error(errCode, "test error") + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.ListTopicSubscriptionsRequest{ + Topic: formattedTopic, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTopicSubscriptions(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 TestPublisherDeleteTopic(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockPublisher.err = nil + mockPublisher.reqs = nil + + mockPublisher.resps = append(mockPublisher.resps[:0], expectedResponse) + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.DeleteTopicRequest{ + Topic: formattedTopic, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteTopic(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockPublisher.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestPublisherDeleteTopicError(t *testing.T) { + errCode := codes.PermissionDenied + mockPublisher.err = gstatus.Error(errCode, "test error") + + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.DeleteTopicRequest{ + Topic: formattedTopic, + } + + c, err := NewPublisherClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteTopic(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 TestSubscriberCreateSubscription(t *testing.T) { + var name2 string = "name2-1052831874" + var topic2 string = "topic2-1139259102" + var ackDeadlineSeconds int32 = 2135351438 + var retainAckedMessages bool = false + var expectedResponse = &pubsubpb.Subscription{ + Name: name2, + Topic: topic2, + AckDeadlineSeconds: ackDeadlineSeconds, + RetainAckedMessages: retainAckedMessages, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.Subscription{ + Name: formattedName, + Topic: formattedTopic, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSubscription(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberCreateSubscriptionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var formattedTopic string = fmt.Sprintf("projects/%s/topics/%s", "[PROJECT]", "[TOPIC]") + var request = &pubsubpb.Subscription{ + Name: formattedName, + Topic: formattedTopic, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSubscription(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 TestSubscriberGetSubscription(t *testing.T) { + var name string = "name3373707" + var topic string = "topic110546223" + var ackDeadlineSeconds int32 = 2135351438 + var retainAckedMessages bool = false + var expectedResponse = &pubsubpb.Subscription{ + Name: name, + Topic: topic, + AckDeadlineSeconds: ackDeadlineSeconds, + RetainAckedMessages: retainAckedMessages, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.GetSubscriptionRequest{ + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSubscription(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberGetSubscriptionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.GetSubscriptionRequest{ + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSubscription(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 TestSubscriberUpdateSubscription(t *testing.T) { + var name string = "name3373707" + var topic string = "topic110546223" + var ackDeadlineSeconds2 int32 = 921632575 + var retainAckedMessages bool = false + var expectedResponse = &pubsubpb.Subscription{ + Name: name, + Topic: topic, + AckDeadlineSeconds: ackDeadlineSeconds2, + RetainAckedMessages: retainAckedMessages, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var ackDeadlineSeconds int32 = 42 + var subscription = &pubsubpb.Subscription{ + AckDeadlineSeconds: ackDeadlineSeconds, + } + var pathsElement string = "ack_deadline_seconds" + var paths = []string{pathsElement} + var updateMask = &field_maskpb.FieldMask{ + Paths: paths, + } + var request = &pubsubpb.UpdateSubscriptionRequest{ + Subscription: subscription, + UpdateMask: updateMask, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSubscription(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberUpdateSubscriptionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var ackDeadlineSeconds int32 = 42 + var subscription = &pubsubpb.Subscription{ + AckDeadlineSeconds: ackDeadlineSeconds, + } + var pathsElement string = "ack_deadline_seconds" + var paths = []string{pathsElement} + var updateMask = &field_maskpb.FieldMask{ + Paths: paths, + } + var request = &pubsubpb.UpdateSubscriptionRequest{ + Subscription: subscription, + UpdateMask: updateMask, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSubscription(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 TestSubscriberListSubscriptions(t *testing.T) { + var nextPageToken string = "" + var subscriptionsElement *pubsubpb.Subscription = &pubsubpb.Subscription{} + var subscriptions = []*pubsubpb.Subscription{subscriptionsElement} + var expectedResponse = &pubsubpb.ListSubscriptionsResponse{ + NextPageToken: nextPageToken, + Subscriptions: subscriptions, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedProject string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &pubsubpb.ListSubscriptionsRequest{ + Project: formattedProject, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSubscriptions(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Subscriptions[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 TestSubscriberListSubscriptionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedProject string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &pubsubpb.ListSubscriptionsRequest{ + Project: formattedProject, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSubscriptions(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 TestSubscriberDeleteSubscription(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.DeleteSubscriptionRequest{ + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSubscription(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSubscriberDeleteSubscriptionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.DeleteSubscriptionRequest{ + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSubscription(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 TestSubscriberModifyAckDeadline(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var ackIds []string = nil + var ackDeadlineSeconds int32 = 2135351438 + var request = &pubsubpb.ModifyAckDeadlineRequest{ + Subscription: formattedSubscription, + AckIds: ackIds, + AckDeadlineSeconds: ackDeadlineSeconds, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.ModifyAckDeadline(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSubscriberModifyAckDeadlineError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var ackIds []string = nil + var ackDeadlineSeconds int32 = 2135351438 + var request = &pubsubpb.ModifyAckDeadlineRequest{ + Subscription: formattedSubscription, + AckIds: ackIds, + AckDeadlineSeconds: ackDeadlineSeconds, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.ModifyAckDeadline(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 TestSubscriberAcknowledge(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var ackIds []string = nil + var request = &pubsubpb.AcknowledgeRequest{ + Subscription: formattedSubscription, + AckIds: ackIds, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.Acknowledge(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSubscriberAcknowledgeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var ackIds []string = nil + var request = &pubsubpb.AcknowledgeRequest{ + Subscription: formattedSubscription, + AckIds: ackIds, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.Acknowledge(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 TestSubscriberPull(t *testing.T) { + var expectedResponse *pubsubpb.PullResponse = &pubsubpb.PullResponse{} + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var maxMessages int32 = 496131527 + var request = &pubsubpb.PullRequest{ + Subscription: formattedSubscription, + MaxMessages: maxMessages, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Pull(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberPullError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var maxMessages int32 = 496131527 + var request = &pubsubpb.PullRequest{ + Subscription: formattedSubscription, + MaxMessages: maxMessages, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Pull(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 TestSubscriberStreamingPull(t *testing.T) { + var receivedMessagesElement *pubsubpb.ReceivedMessage = &pubsubpb.ReceivedMessage{} + var receivedMessages = []*pubsubpb.ReceivedMessage{receivedMessagesElement} + var expectedResponse = &pubsubpb.StreamingPullResponse{ + ReceivedMessages: receivedMessages, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var streamAckDeadlineSeconds int32 = 1875467245 + var request = &pubsubpb.StreamingPullRequest{ + Subscription: formattedSubscription, + StreamAckDeadlineSeconds: streamAckDeadlineSeconds, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingPull(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberStreamingPullError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var streamAckDeadlineSeconds int32 = 1875467245 + var request = &pubsubpb.StreamingPullRequest{ + Subscription: formattedSubscription, + StreamAckDeadlineSeconds: streamAckDeadlineSeconds, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingPull(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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 TestSubscriberModifyPushConfig(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var pushConfig *pubsubpb.PushConfig = &pubsubpb.PushConfig{} + var request = &pubsubpb.ModifyPushConfigRequest{ + Subscription: formattedSubscription, + PushConfig: pushConfig, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.ModifyPushConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSubscriberModifyPushConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var pushConfig *pubsubpb.PushConfig = &pubsubpb.PushConfig{} + var request = &pubsubpb.ModifyPushConfigRequest{ + Subscription: formattedSubscription, + PushConfig: pushConfig, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.ModifyPushConfig(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 TestSubscriberListSnapshots(t *testing.T) { + var nextPageToken string = "" + var snapshotsElement *pubsubpb.Snapshot = &pubsubpb.Snapshot{} + var snapshots = []*pubsubpb.Snapshot{snapshotsElement} + var expectedResponse = &pubsubpb.ListSnapshotsResponse{ + NextPageToken: nextPageToken, + Snapshots: snapshots, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedProject string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &pubsubpb.ListSnapshotsRequest{ + Project: formattedProject, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSnapshots(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Snapshots[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 TestSubscriberListSnapshotsError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedProject string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &pubsubpb.ListSnapshotsRequest{ + Project: formattedProject, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSnapshots(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 TestSubscriberCreateSnapshot(t *testing.T) { + var name2 string = "name2-1052831874" + var topic string = "topic110546223" + var expectedResponse = &pubsubpb.Snapshot{ + Name: name2, + Topic: topic, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/snapshots/%s", "[PROJECT]", "[SNAPSHOT]") + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.CreateSnapshotRequest{ + Name: formattedName, + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSnapshot(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberCreateSnapshotError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/snapshots/%s", "[PROJECT]", "[SNAPSHOT]") + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.CreateSnapshotRequest{ + Name: formattedName, + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSnapshot(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 TestSubscriberUpdateSnapshot(t *testing.T) { + var name string = "name3373707" + var topic string = "topic110546223" + var expectedResponse = &pubsubpb.Snapshot{ + Name: name, + Topic: topic, + } + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var seconds int64 = 123456 + var expireTime = ×tamppb.Timestamp{ + Seconds: seconds, + } + var snapshot = &pubsubpb.Snapshot{ + ExpireTime: expireTime, + } + var pathsElement string = "expire_time" + var paths = []string{pathsElement} + var updateMask = &field_maskpb.FieldMask{ + Paths: paths, + } + var request = &pubsubpb.UpdateSnapshotRequest{ + Snapshot: snapshot, + UpdateMask: updateMask, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSnapshot(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberUpdateSnapshotError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var seconds int64 = 123456 + var expireTime = ×tamppb.Timestamp{ + Seconds: seconds, + } + var snapshot = &pubsubpb.Snapshot{ + ExpireTime: expireTime, + } + var pathsElement string = "expire_time" + var paths = []string{pathsElement} + var updateMask = &field_maskpb.FieldMask{ + Paths: paths, + } + var request = &pubsubpb.UpdateSnapshotRequest{ + Snapshot: snapshot, + UpdateMask: updateMask, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.UpdateSnapshot(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 TestSubscriberDeleteSnapshot(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSnapshot string = fmt.Sprintf("projects/%s/snapshots/%s", "[PROJECT]", "[SNAPSHOT]") + var request = &pubsubpb.DeleteSnapshotRequest{ + Snapshot: formattedSnapshot, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSnapshot(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSubscriberDeleteSnapshotError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSnapshot string = fmt.Sprintf("projects/%s/snapshots/%s", "[PROJECT]", "[SNAPSHOT]") + var request = &pubsubpb.DeleteSnapshotRequest{ + Snapshot: formattedSnapshot, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSnapshot(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 TestSubscriberSeek(t *testing.T) { + var expectedResponse *pubsubpb.SeekResponse = &pubsubpb.SeekResponse{} + + mockSubscriber.err = nil + mockSubscriber.reqs = nil + + mockSubscriber.resps = append(mockSubscriber.resps[:0], expectedResponse) + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.SeekRequest{ + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Seek(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSubscriber.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 TestSubscriberSeekError(t *testing.T) { + errCode := codes.PermissionDenied + mockSubscriber.err = gstatus.Error(errCode, "test error") + + var formattedSubscription string = fmt.Sprintf("projects/%s/subscriptions/%s", "[PROJECT]", "[SUBSCRIPTION]") + var request = &pubsubpb.SeekRequest{ + Subscription: formattedSubscription, + } + + c, err := NewSubscriberClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Seek(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/vendor/cloud.google.com/go/pubsub/apiv1/path_funcs.go b/vendor/cloud.google.com/go/pubsub/apiv1/path_funcs.go new file mode 100644 index 0000000000..b9ab4848db --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/path_funcs.go @@ -0,0 +1,95 @@ +// 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. + +package pubsub + +// PublisherProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func PublisherProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// PublisherTopicPath returns the path for the topic resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/topics/%s", project, topic) +// instead. +func PublisherTopicPath(project, topic string) string { + return "" + + "projects/" + + project + + "/topics/" + + topic + + "" +} + +// SubscriberProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func SubscriberProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// SubscriberSnapshotPath returns the path for the snapshot resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/snapshots/%s", project, snapshot) +// instead. +func SubscriberSnapshotPath(project, snapshot string) string { + return "" + + "projects/" + + project + + "/snapshots/" + + snapshot + + "" +} + +// SubscriberSubscriptionPath returns the path for the subscription resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/subscriptions/%s", project, subscription) +// instead. +func SubscriberSubscriptionPath(project, subscription string) string { + return "" + + "projects/" + + project + + "/subscriptions/" + + subscription + + "" +} + +// SubscriberTopicPath returns the path for the topic resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/topics/%s", project, topic) +// instead. +func SubscriberTopicPath(project, topic string) string { + return "" + + "projects/" + + project + + "/topics/" + + topic + + "" +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client.go b/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client.go new file mode 100644 index 0000000000..68cb027c6f --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client.go @@ -0,0 +1,402 @@ +// 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 pubsub + +import ( + "math" + "time" + + "cloud.google.com/go/iam" + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// PublisherCallOptions contains the retry settings for each method of PublisherClient. +type PublisherCallOptions struct { + CreateTopic []gax.CallOption + UpdateTopic []gax.CallOption + Publish []gax.CallOption + GetTopic []gax.CallOption + ListTopics []gax.CallOption + ListTopicSubscriptions []gax.CallOption + DeleteTopic []gax.CallOption +} + +func defaultPublisherClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("pubsub.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultPublisherCallOptions() *PublisherCallOptions { + 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, + }) + }), + }, + {"messaging", "one_plus_delivery"}: { + gax.WithRetry(func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.Aborted, + codes.Canceled, + codes.DeadlineExceeded, + codes.Internal, + codes.ResourceExhausted, + codes.Unavailable, + codes.Unknown, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &PublisherCallOptions{ + CreateTopic: retry[[2]string{"default", "idempotent"}], + UpdateTopic: retry[[2]string{"default", "idempotent"}], + Publish: retry[[2]string{"messaging", "one_plus_delivery"}], + GetTopic: retry[[2]string{"default", "idempotent"}], + ListTopics: retry[[2]string{"default", "idempotent"}], + ListTopicSubscriptions: retry[[2]string{"default", "idempotent"}], + DeleteTopic: retry[[2]string{"default", "idempotent"}], + } +} + +// PublisherClient is a client for interacting with Google Cloud Pub/Sub API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type PublisherClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + publisherClient pubsubpb.PublisherClient + + // The call options for this service. + CallOptions *PublisherCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewPublisherClient creates a new publisher client. +// +// The service that an application uses to manipulate topics, and to send +// messages to a topic. +func NewPublisherClient(ctx context.Context, opts ...option.ClientOption) (*PublisherClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultPublisherClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &PublisherClient{ + conn: conn, + CallOptions: defaultPublisherCallOptions(), + + publisherClient: pubsubpb.NewPublisherClient(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *PublisherClient) 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 *PublisherClient) 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 *PublisherClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +func (c *PublisherClient) SubscriptionIAM(subscription *pubsubpb.Subscription) *iam.Handle { + return iam.InternalNewHandle(c.Connection(), subscription.Name) +} + +func (c *PublisherClient) TopicIAM(topic *pubsubpb.Topic) *iam.Handle { + return iam.InternalNewHandle(c.Connection(), topic.Name) +} + +// CreateTopic creates the given topic with the given name. See the +// resource name rules. +func (c *PublisherClient) CreateTopic(ctx context.Context, req *pubsubpb.Topic, opts ...gax.CallOption) (*pubsubpb.Topic, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateTopic[0:len(c.CallOptions.CreateTopic):len(c.CallOptions.CreateTopic)], opts...) + var resp *pubsubpb.Topic + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.publisherClient.CreateTopic(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateTopic updates an existing topic. Note that certain properties of a +// topic are not modifiable. +func (c *PublisherClient) UpdateTopic(ctx context.Context, req *pubsubpb.UpdateTopicRequest, opts ...gax.CallOption) (*pubsubpb.Topic, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateTopic[0:len(c.CallOptions.UpdateTopic):len(c.CallOptions.UpdateTopic)], opts...) + var resp *pubsubpb.Topic + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.publisherClient.UpdateTopic(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Publish adds one or more messages to the topic. Returns NOT_FOUND if the topic +// does not exist. The message payload must not be empty; it must contain +// either a non-empty data field, or at least one attribute. +func (c *PublisherClient) Publish(ctx context.Context, req *pubsubpb.PublishRequest, opts ...gax.CallOption) (*pubsubpb.PublishResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Publish[0:len(c.CallOptions.Publish):len(c.CallOptions.Publish)], opts...) + var resp *pubsubpb.PublishResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.publisherClient.Publish(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetTopic gets the configuration of a topic. +func (c *PublisherClient) GetTopic(ctx context.Context, req *pubsubpb.GetTopicRequest, opts ...gax.CallOption) (*pubsubpb.Topic, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetTopic[0:len(c.CallOptions.GetTopic):len(c.CallOptions.GetTopic)], opts...) + var resp *pubsubpb.Topic + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.publisherClient.GetTopic(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListTopics lists matching topics. +func (c *PublisherClient) ListTopics(ctx context.Context, req *pubsubpb.ListTopicsRequest, opts ...gax.CallOption) *TopicIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListTopics[0:len(c.CallOptions.ListTopics):len(c.CallOptions.ListTopics)], opts...) + it := &TopicIterator{} + req = proto.Clone(req).(*pubsubpb.ListTopicsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*pubsubpb.Topic, string, error) { + var resp *pubsubpb.ListTopicsResponse + 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.publisherClient.ListTopics(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Topics, 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 +} + +// ListTopicSubscriptions lists the names of the subscriptions on this topic. +func (c *PublisherClient) ListTopicSubscriptions(ctx context.Context, req *pubsubpb.ListTopicSubscriptionsRequest, opts ...gax.CallOption) *StringIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListTopicSubscriptions[0:len(c.CallOptions.ListTopicSubscriptions):len(c.CallOptions.ListTopicSubscriptions)], opts...) + it := &StringIterator{} + req = proto.Clone(req).(*pubsubpb.ListTopicSubscriptionsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]string, string, error) { + var resp *pubsubpb.ListTopicSubscriptionsResponse + 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.publisherClient.ListTopicSubscriptions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Subscriptions, 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 +} + +// DeleteTopic deletes the topic with the given name. Returns NOT_FOUND if the topic +// does not exist. After a topic is deleted, a new topic may be created with +// the same name; this is an entirely new topic with none of the old +// configuration or subscriptions. Existing subscriptions to this topic are +// not deleted, but their topic field is set to _deleted-topic_. +func (c *PublisherClient) DeleteTopic(ctx context.Context, req *pubsubpb.DeleteTopicRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteTopic[0:len(c.CallOptions.DeleteTopic):len(c.CallOptions.DeleteTopic)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.publisherClient.DeleteTopic(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// StringIterator manages a stream of string. +type StringIterator struct { + items []string + 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 []string, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *StringIterator) 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 *StringIterator) Next() (string, error) { + var item string + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *StringIterator) bufLen() int { + return len(it.items) +} + +func (it *StringIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// TopicIterator manages a stream of *pubsubpb.Topic. +type TopicIterator struct { + items []*pubsubpb.Topic + 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 []*pubsubpb.Topic, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *TopicIterator) 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 *TopicIterator) Next() (*pubsubpb.Topic, error) { + var item *pubsubpb.Topic + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *TopicIterator) bufLen() int { + return len(it.items) +} + +func (it *TopicIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client_example_test.go b/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client_example_test.go new file mode 100644 index 0000000000..0f60bca5c1 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client_example_test.go @@ -0,0 +1,204 @@ +// 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 pubsub_test + +import ( + "cloud.google.com/go/pubsub/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +func ExampleNewPublisherClient() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExamplePublisherClient_SubscriptionIAM() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + subscription := &pubsubpb.Subscription{} + h := c.SubscriptionIAM(subscription) + policy, err := h.Policy(ctx) + if err != nil { + // TODO: Handle error. + } + //TODO: Use the IAM policy + _ = policy +} + +func ExamplePublisherClient_TopicIAM() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + topic := &pubsubpb.Topic{} + h := c.TopicIAM(topic) + policy, err := h.Policy(ctx) + if err != nil { + // TODO: Handle error. + } + //TODO: Use the IAM policy + _ = policy +} + +func ExamplePublisherClient_CreateTopic() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.Topic{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateTopic(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExamplePublisherClient_UpdateTopic() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.UpdateTopicRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateTopic(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExamplePublisherClient_Publish() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.PublishRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Publish(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExamplePublisherClient_GetTopic() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.GetTopicRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetTopic(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExamplePublisherClient_ListTopics() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.ListTopicsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListTopics(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExamplePublisherClient_ListTopicSubscriptions() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.ListTopicSubscriptionsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListTopicSubscriptions(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExamplePublisherClient_DeleteTopic() { + ctx := context.Background() + c, err := pubsub.NewPublisherClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.DeleteTopicRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteTopic(ctx, req) + if err != nil { + // TODO: Handle error. + } +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/pubsub_pull_example_test.go b/vendor/cloud.google.com/go/pubsub/apiv1/pubsub_pull_example_test.go new file mode 100644 index 0000000000..df13c3d0e1 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/pubsub_pull_example_test.go @@ -0,0 +1,106 @@ +// 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. + +package pubsub_test + +import ( + "fmt" + "log" + "time" + + "cloud.google.com/go/pubsub/apiv1" + "golang.org/x/net/context" + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +func ExampleSubscriberClient_Pull_lengthyClientProcessing() { + projectID := "some-project" + subscriptionID := "some-subscription" + + ctx := context.Background() + client, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + log.Fatal(err) + } + defer client.Close() + + sub := fmt.Sprintf("projects/%s/subscriptions/%s", projectID, subscriptionID) + // Be sure to tune the MaxMessages parameter per your project's needs, and accordingly + // adjust the ack behavior below to batch acknowledgements. + req := pubsubpb.PullRequest{ + Subscription: sub, + MaxMessages: 1, + } + + fmt.Println("Listening..") + + for { + res, err := client.Pull(ctx, &req) + if err != nil { + log.Fatal(err) + } + + // client.Pull returns an empty list if there are no messages available in the + // backlog. We should skip processing steps when that happens. + if len(res.ReceivedMessages) == 0 { + continue + } + + var recvdAckIDs []string + for _, m := range res.ReceivedMessages { + recvdAckIDs = append(recvdAckIDs, m.AckId) + } + + var done = make(chan struct{}) + var delay = 0 * time.Second // Tick immediately upon reception + var ackDeadline = 10 * time.Second + + // Continuously notify the server that processing is still happening on this batch. + go func() { + for { + select { + case <-ctx.Done(): + return + case <-done: + return + case <-time.After(delay): + err := client.ModifyAckDeadline(ctx, &pubsubpb.ModifyAckDeadlineRequest{ + Subscription: sub, + AckIds: recvdAckIDs, + AckDeadlineSeconds: int32(ackDeadline.Seconds()), + }) + if err != nil { + log.Fatal(err) + } + delay = ackDeadline - 5*time.Second // 5 seconds grace period. + } + } + }() + + for _, m := range res.ReceivedMessages { + // Process the message here, possibly in a goroutine. + log.Printf("Got message: %s", string(m.Message.Data)) + + err := client.Acknowledge(ctx, &pubsubpb.AcknowledgeRequest{ + Subscription: sub, + AckIds: []string{m.AckId}, + }) + if err != nil { + log.Fatal(err) + } + } + + close(done) + } +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client.go b/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client.go new file mode 100644 index 0000000000..420d794e31 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client.go @@ -0,0 +1,607 @@ +// 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 pubsub + +import ( + "math" + "time" + + "cloud.google.com/go/iam" + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// SubscriberCallOptions contains the retry settings for each method of SubscriberClient. +type SubscriberCallOptions struct { + CreateSubscription []gax.CallOption + GetSubscription []gax.CallOption + UpdateSubscription []gax.CallOption + ListSubscriptions []gax.CallOption + DeleteSubscription []gax.CallOption + ModifyAckDeadline []gax.CallOption + Acknowledge []gax.CallOption + Pull []gax.CallOption + StreamingPull []gax.CallOption + ModifyPushConfig []gax.CallOption + ListSnapshots []gax.CallOption + CreateSnapshot []gax.CallOption + UpdateSnapshot []gax.CallOption + DeleteSnapshot []gax.CallOption + Seek []gax.CallOption +} + +func defaultSubscriberClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("pubsub.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultSubscriberCallOptions() *SubscriberCallOptions { + 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, + }) + }), + }, + {"messaging", "pull"}: { + gax.WithRetry(func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.Canceled, + codes.DeadlineExceeded, + codes.Internal, + codes.ResourceExhausted, + codes.Unavailable, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + {"streaming_messaging", "pull"}: { + gax.WithRetry(func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.Canceled, + codes.DeadlineExceeded, + codes.Internal, + codes.ResourceExhausted, + codes.Unavailable, + }, gax.Backoff{ + Initial: 100 * time.Millisecond, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &SubscriberCallOptions{ + CreateSubscription: retry[[2]string{"default", "idempotent"}], + GetSubscription: retry[[2]string{"default", "idempotent"}], + UpdateSubscription: retry[[2]string{"default", "idempotent"}], + ListSubscriptions: retry[[2]string{"default", "idempotent"}], + DeleteSubscription: retry[[2]string{"default", "idempotent"}], + ModifyAckDeadline: retry[[2]string{"default", "non_idempotent"}], + Acknowledge: retry[[2]string{"messaging", "non_idempotent"}], + Pull: retry[[2]string{"messaging", "pull"}], + StreamingPull: retry[[2]string{"streaming_messaging", "pull"}], + ModifyPushConfig: retry[[2]string{"default", "non_idempotent"}], + ListSnapshots: retry[[2]string{"default", "idempotent"}], + CreateSnapshot: retry[[2]string{"default", "idempotent"}], + UpdateSnapshot: retry[[2]string{"default", "idempotent"}], + DeleteSnapshot: retry[[2]string{"default", "idempotent"}], + Seek: retry[[2]string{"default", "non_idempotent"}], + } +} + +// SubscriberClient is a client for interacting with Google Cloud Pub/Sub API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type SubscriberClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + subscriberClient pubsubpb.SubscriberClient + + // The call options for this service. + CallOptions *SubscriberCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewSubscriberClient creates a new subscriber client. +// +// The service that an application uses to manipulate subscriptions and to +// consume messages from a subscription via the Pull method or by +// establishing a bi-directional stream using the StreamingPull method. +func NewSubscriberClient(ctx context.Context, opts ...option.ClientOption) (*SubscriberClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultSubscriberClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &SubscriberClient{ + conn: conn, + CallOptions: defaultSubscriberCallOptions(), + + subscriberClient: pubsubpb.NewSubscriberClient(conn), + } + c.SetGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *SubscriberClient) 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 *SubscriberClient) 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 *SubscriberClient) SetGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +func (c *SubscriberClient) SubscriptionIAM(subscription *pubsubpb.Subscription) *iam.Handle { + return iam.InternalNewHandle(c.Connection(), subscription.Name) +} + +func (c *SubscriberClient) TopicIAM(topic *pubsubpb.Topic) *iam.Handle { + return iam.InternalNewHandle(c.Connection(), topic.Name) +} + +// CreateSubscription creates a subscription to a given topic. See the +// resource name rules. +// If the subscription already exists, returns ALREADY_EXISTS. +// If the corresponding topic doesn't exist, returns NOT_FOUND. +// +// If the name is not provided in the request, the server will assign a random +// name for this subscription on the same project as the topic, conforming +// to the +// resource name format (at https://cloud.google.com/pubsub/docs/overview#names). +// The generated name is populated in the returned Subscription object. +// Note that for REST API requests, you must specify a name in the request. +func (c *SubscriberClient) CreateSubscription(ctx context.Context, req *pubsubpb.Subscription, opts ...gax.CallOption) (*pubsubpb.Subscription, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateSubscription[0:len(c.CallOptions.CreateSubscription):len(c.CallOptions.CreateSubscription)], opts...) + var resp *pubsubpb.Subscription + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.CreateSubscription(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetSubscription gets the configuration details of a subscription. +func (c *SubscriberClient) GetSubscription(ctx context.Context, req *pubsubpb.GetSubscriptionRequest, opts ...gax.CallOption) (*pubsubpb.Subscription, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetSubscription[0:len(c.CallOptions.GetSubscription):len(c.CallOptions.GetSubscription)], opts...) + var resp *pubsubpb.Subscription + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.GetSubscription(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateSubscription updates an existing subscription. Note that certain properties of a +// subscription, such as its topic, are not modifiable. +func (c *SubscriberClient) UpdateSubscription(ctx context.Context, req *pubsubpb.UpdateSubscriptionRequest, opts ...gax.CallOption) (*pubsubpb.Subscription, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateSubscription[0:len(c.CallOptions.UpdateSubscription):len(c.CallOptions.UpdateSubscription)], opts...) + var resp *pubsubpb.Subscription + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.UpdateSubscription(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListSubscriptions lists matching subscriptions. +func (c *SubscriberClient) ListSubscriptions(ctx context.Context, req *pubsubpb.ListSubscriptionsRequest, opts ...gax.CallOption) *SubscriptionIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListSubscriptions[0:len(c.CallOptions.ListSubscriptions):len(c.CallOptions.ListSubscriptions)], opts...) + it := &SubscriptionIterator{} + req = proto.Clone(req).(*pubsubpb.ListSubscriptionsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*pubsubpb.Subscription, string, error) { + var resp *pubsubpb.ListSubscriptionsResponse + 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.subscriberClient.ListSubscriptions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Subscriptions, 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 +} + +// DeleteSubscription deletes an existing subscription. All messages retained in the subscription +// are immediately dropped. Calls to Pull after deletion will return +// NOT_FOUND. After a subscription is deleted, a new one may be created with +// the same name, but the new one has no association with the old +// subscription or its topic unless the same topic is specified. +func (c *SubscriberClient) DeleteSubscription(ctx context.Context, req *pubsubpb.DeleteSubscriptionRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteSubscription[0:len(c.CallOptions.DeleteSubscription):len(c.CallOptions.DeleteSubscription)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.subscriberClient.DeleteSubscription(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ModifyAckDeadline modifies the ack deadline for a specific message. This method is useful +// to indicate that more time is needed to process a message by the +// subscriber, or to make the message available for redelivery if the +// processing was interrupted. Note that this does not modify the +// subscription-level ackDeadlineSeconds used for subsequent messages. +func (c *SubscriberClient) ModifyAckDeadline(ctx context.Context, req *pubsubpb.ModifyAckDeadlineRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ModifyAckDeadline[0:len(c.CallOptions.ModifyAckDeadline):len(c.CallOptions.ModifyAckDeadline)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.subscriberClient.ModifyAckDeadline(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// Acknowledge acknowledges the messages associated with the ack_ids in the +// AcknowledgeRequest. The Pub/Sub system can remove the relevant messages +// from the subscription. +// +// Acknowledging a message whose ack deadline has expired may succeed, +// but such a message may be redelivered later. Acknowledging a message more +// than once will not result in an error. +func (c *SubscriberClient) Acknowledge(ctx context.Context, req *pubsubpb.AcknowledgeRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Acknowledge[0:len(c.CallOptions.Acknowledge):len(c.CallOptions.Acknowledge)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.subscriberClient.Acknowledge(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// Pull pulls messages from the server. Returns an empty list if there are no +// messages available in the backlog. The server may return UNAVAILABLE if +// there are too many concurrent pull requests pending for the given +// subscription. +func (c *SubscriberClient) Pull(ctx context.Context, req *pubsubpb.PullRequest, opts ...gax.CallOption) (*pubsubpb.PullResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Pull[0:len(c.CallOptions.Pull):len(c.CallOptions.Pull)], opts...) + var resp *pubsubpb.PullResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.Pull(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// StreamingPull establishes a stream with the server, which sends messages down to the +// client. The client streams acknowledgements and ack deadline modifications +// back to the server. The server will close the stream and return the status +// on any error. The server may close the stream with status UNAVAILABLE to +// reassign server-side resources, in which case, the client should +// re-establish the stream. Flow control can be achieved by configuring the +// underlying RPC channel. +func (c *SubscriberClient) StreamingPull(ctx context.Context, opts ...gax.CallOption) (pubsubpb.Subscriber_StreamingPullClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.StreamingPull[0:len(c.CallOptions.StreamingPull):len(c.CallOptions.StreamingPull)], opts...) + var resp pubsubpb.Subscriber_StreamingPullClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.StreamingPull(ctx, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ModifyPushConfig modifies the PushConfig for a specified subscription. +// +// This may be used to change a push subscription to a pull one (signified by +// an empty PushConfig) or vice versa, or change the endpoint URL and other +// attributes of a push subscription. Messages will accumulate for delivery +// continuously through the call regardless of changes to the PushConfig. +func (c *SubscriberClient) ModifyPushConfig(ctx context.Context, req *pubsubpb.ModifyPushConfigRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ModifyPushConfig[0:len(c.CallOptions.ModifyPushConfig):len(c.CallOptions.ModifyPushConfig)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.subscriberClient.ModifyPushConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ListSnapshots lists the existing snapshots.

+// ALPHA: This feature is part of an alpha release. This API might be +// changed in backward-incompatible ways and is not recommended for production +// use. It is not subject to any SLA or deprecation policy. +func (c *SubscriberClient) ListSnapshots(ctx context.Context, req *pubsubpb.ListSnapshotsRequest, opts ...gax.CallOption) *SnapshotIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListSnapshots[0:len(c.CallOptions.ListSnapshots):len(c.CallOptions.ListSnapshots)], opts...) + it := &SnapshotIterator{} + req = proto.Clone(req).(*pubsubpb.ListSnapshotsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*pubsubpb.Snapshot, string, error) { + var resp *pubsubpb.ListSnapshotsResponse + 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.subscriberClient.ListSnapshots(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Snapshots, 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 +} + +// CreateSnapshot creates a snapshot from the requested subscription.

+// ALPHA: This feature is part of an alpha release. This API might be +// changed in backward-incompatible ways and is not recommended for production +// use. It is not subject to any SLA or deprecation policy. +// If the snapshot already exists, returns ALREADY_EXISTS. +// If the requested subscription doesn't exist, returns NOT_FOUND. +// If the backlog in the subscription is too old -- and the resulting snapshot +// would expire in less than 1 hour -- then FAILED_PRECONDITION is returned. +// See also the Snapshot.expire_time field. If the name is not provided in +// the request, the server will assign a random +// name for this snapshot on the same project as the subscription, conforming +// to the resource name format (at https://cloud.google.com/pubsub/docs/overview#names). +// The generated +// name is populated in the returned Snapshot object. Note that for REST API +// requests, you must specify a name in the request. +func (c *SubscriberClient) CreateSnapshot(ctx context.Context, req *pubsubpb.CreateSnapshotRequest, opts ...gax.CallOption) (*pubsubpb.Snapshot, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateSnapshot[0:len(c.CallOptions.CreateSnapshot):len(c.CallOptions.CreateSnapshot)], opts...) + var resp *pubsubpb.Snapshot + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.CreateSnapshot(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateSnapshot updates an existing snapshot.

+// ALPHA: This feature is part of an alpha release. This API might be +// changed in backward-incompatible ways and is not recommended for production +// use. It is not subject to any SLA or deprecation policy. +// Note that certain properties of a snapshot are not modifiable. +func (c *SubscriberClient) UpdateSnapshot(ctx context.Context, req *pubsubpb.UpdateSnapshotRequest, opts ...gax.CallOption) (*pubsubpb.Snapshot, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateSnapshot[0:len(c.CallOptions.UpdateSnapshot):len(c.CallOptions.UpdateSnapshot)], opts...) + var resp *pubsubpb.Snapshot + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.UpdateSnapshot(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteSnapshot removes an existing snapshot.

+// ALPHA: This feature is part of an alpha release. This API might be +// changed in backward-incompatible ways and is not recommended for production +// use. It is not subject to any SLA or deprecation policy. +// When the snapshot is deleted, all messages retained in the snapshot +// are immediately dropped. After a snapshot is deleted, a new one may be +// created with the same name, but the new one has no association with the old +// snapshot or its subscription, unless the same subscription is specified. +func (c *SubscriberClient) DeleteSnapshot(ctx context.Context, req *pubsubpb.DeleteSnapshotRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteSnapshot[0:len(c.CallOptions.DeleteSnapshot):len(c.CallOptions.DeleteSnapshot)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.subscriberClient.DeleteSnapshot(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// Seek seeks an existing subscription to a point in time or to a given snapshot, +// whichever is provided in the request.

+// ALPHA: This feature is part of an alpha release. This API might be +// changed in backward-incompatible ways and is not recommended for production +// use. It is not subject to any SLA or deprecation policy. +func (c *SubscriberClient) Seek(ctx context.Context, req *pubsubpb.SeekRequest, opts ...gax.CallOption) (*pubsubpb.SeekResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Seek[0:len(c.CallOptions.Seek):len(c.CallOptions.Seek)], opts...) + var resp *pubsubpb.SeekResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.subscriberClient.Seek(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SnapshotIterator manages a stream of *pubsubpb.Snapshot. +type SnapshotIterator struct { + items []*pubsubpb.Snapshot + 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 []*pubsubpb.Snapshot, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *SnapshotIterator) 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 *SnapshotIterator) Next() (*pubsubpb.Snapshot, error) { + var item *pubsubpb.Snapshot + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *SnapshotIterator) bufLen() int { + return len(it.items) +} + +func (it *SnapshotIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// SubscriptionIterator manages a stream of *pubsubpb.Subscription. +type SubscriptionIterator struct { + items []*pubsubpb.Subscription + 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 []*pubsubpb.Subscription, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *SubscriptionIterator) 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 *SubscriptionIterator) Next() (*pubsubpb.Subscription, error) { + var item *pubsubpb.Subscription + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *SubscriptionIterator) bufLen() int { + return len(it.items) +} + +func (it *SubscriptionIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client_example_test.go b/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client_example_test.go new file mode 100644 index 0000000000..581f2bf6e6 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client_example_test.go @@ -0,0 +1,358 @@ +// 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 pubsub_test + +import ( + "io" + + "cloud.google.com/go/pubsub/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +func ExampleNewSubscriberClient() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleSubscriberClient_SubscriptionIAM() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + subscription := &pubsubpb.Subscription{} + h := c.SubscriptionIAM(subscription) + policy, err := h.Policy(ctx) + if err != nil { + // TODO: Handle error. + } + //TODO: Use the IAM policy + _ = policy +} + +func ExampleSubscriberClient_TopicIAM() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + topic := &pubsubpb.Topic{} + h := c.TopicIAM(topic) + policy, err := h.Policy(ctx) + if err != nil { + // TODO: Handle error. + } + //TODO: Use the IAM policy + _ = policy +} + +func ExampleSubscriberClient_CreateSubscription() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.Subscription{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateSubscription(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSubscriberClient_GetSubscription() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.GetSubscriptionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetSubscription(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSubscriberClient_UpdateSubscription() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.UpdateSubscriptionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateSubscription(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSubscriberClient_ListSubscriptions() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.ListSubscriptionsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListSubscriptions(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleSubscriberClient_DeleteSubscription() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.DeleteSubscriptionRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteSubscription(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleSubscriberClient_ModifyAckDeadline() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.ModifyAckDeadlineRequest{ + // TODO: Fill request struct fields. + } + err = c.ModifyAckDeadline(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleSubscriberClient_Acknowledge() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.AcknowledgeRequest{ + // TODO: Fill request struct fields. + } + err = c.Acknowledge(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleSubscriberClient_Pull() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.PullRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Pull(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSubscriberClient_StreamingPull() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + stream, err := c.StreamingPull(ctx) + if err != nil { + // TODO: Handle error. + } + go func() { + reqs := []*pubsubpb.StreamingPullRequest{ + // TODO: Create requests. + } + for _, req := range reqs { + if err := stream.Send(req); err != nil { + // TODO: Handle error. + } + } + stream.CloseSend() + }() + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleSubscriberClient_ModifyPushConfig() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.ModifyPushConfigRequest{ + // TODO: Fill request struct fields. + } + err = c.ModifyPushConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleSubscriberClient_ListSnapshots() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.ListSnapshotsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListSnapshots(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleSubscriberClient_CreateSnapshot() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.CreateSnapshotRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateSnapshot(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSubscriberClient_UpdateSnapshot() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.UpdateSnapshotRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.UpdateSnapshot(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleSubscriberClient_DeleteSnapshot() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.DeleteSnapshotRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteSnapshot(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleSubscriberClient_Seek() { + ctx := context.Background() + c, err := pubsub.NewSubscriberClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &pubsubpb.SeekRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Seek(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/pubsub/debug.go b/vendor/cloud.google.com/go/pubsub/debug.go new file mode 100644 index 0000000000..977ae577f7 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/debug.go @@ -0,0 +1,72 @@ +// 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. + +// +build psdebug + +package pubsub + +import ( + "sync" + "time" +) + +var ( + dmu sync.Mutex + msgTraces = map[string][]Event{} + ackIDToMsgID = map[string]string{} +) + +type Event struct { + Desc string + At time.Time +} + +func MessageEvents(msgID string) []Event { + dmu.Lock() + defer dmu.Unlock() + return msgTraces[msgID] +} + +func addRecv(msgID, ackID string, t time.Time) { + dmu.Lock() + defer dmu.Unlock() + ackIDToMsgID[ackID] = msgID + addEvent(msgID, "recv", t) +} + +func addAcks(ackIDs []string) { + dmu.Lock() + defer dmu.Unlock() + now := time.Now() + for _, id := range ackIDs { + addEvent(ackIDToMsgID[id], "ack", now) + } +} + +func addModAcks(ackIDs []string, deadlineSecs int32) { + dmu.Lock() + defer dmu.Unlock() + desc := "modack" + if deadlineSecs == 0 { + desc = "nack" + } + now := time.Now() + for _, id := range ackIDs { + addEvent(ackIDToMsgID[id], desc, now) + } +} + +func addEvent(msgID, desc string, t time.Time) { + msgTraces[msgID] = append(msgTraces[msgID], Event{desc, t}) +} diff --git a/vendor/cloud.google.com/go/pubsub/doc.go b/vendor/cloud.google.com/go/pubsub/doc.go new file mode 100644 index 0000000000..d3a502473d --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/doc.go @@ -0,0 +1,123 @@ +// 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 pubsub provides an easy way to publish and receive Google Cloud Pub/Sub +messages, hiding the the details of the underlying server RPCs. Google Cloud +Pub/Sub is a many-to-many, asynchronous messaging system that decouples senders +and receivers. + +More information about Google Cloud Pub/Sub is available at +https://cloud.google.com/pubsub/docs + +See https://godoc.org/cloud.google.com/go for authentication, timeouts, +connection pooling and similar aspects of this package. + + +Publishing + +Google Cloud Pub/Sub messages are published to topics. Topics may be created +using the pubsub package like so: + + topic, err := pubsubClient.CreateTopic(context.Background(), "topic-name") + +Messages may then be published to a topic: + + res := topic.Publish(ctx, &pubsub.Message{Data: []byte("payload")}) + +Publish queues the message for publishing and returns immediately. When enough +messages have accumulated, or enough time has elapsed, the batch of messages is +sent to the Pub/Sub service. + +Publish returns a PublishResult, which behaves like a future: its Get method +blocks until the message has been sent to the service. + +The first time you call Publish on a topic, goroutines are started in the +background. To clean up these goroutines, call Stop: + + topic.Stop() + +Receiving + +To receive messages published to a topic, clients create subscriptions +to the topic. There may be more than one subscription per topic; each message +that is published to the topic will be delivered to all of its subscriptions. + +Subsciptions may be created like so: + + sub, err := pubsubClient.CreateSubscription(context.Background(), "sub-name", + pubsub.SubscriptionConfig{Topic: topic}) + +Messages are then consumed from a subscription via callback. + + err := sub.Receive(context.Background(), func(ctx context.Context, m *Message) { + log.Printf("Got message: %s", m.Data) + m.Ack() + }) + if err != nil { + // Handle error. + } + +The callback is invoked concurrently by multiple goroutines, maximizing +throughput. To terminate a call to Receive, cancel its context. + +Once client code has processed the message, it must call Message.Ack, otherwise +the message will eventually be redelivered. As an optimization, if the client +cannot or doesn't want to process the message, it can call Message.Nack to +speed redelivery. For more information and configuration options, see +"Deadlines" below. + +Note: It is possible for Messages to be redelivered, even if Message.Ack has +been called. Client code must be robust to multiple deliveries of messages. + +Deadlines + +The default pubsub deadlines are suitable for most use cases, but may be +overridden. This section describes the tradeoffs that should be considered +when overriding the defaults. + +Behind the scenes, each message returned by the Pub/Sub server has an +associated lease, known as an "ACK deadline". +Unless a message is acknowledged within the ACK deadline, or the client requests that +the ACK deadline be extended, the message will become elegible for redelivery. +As a convenience, the pubsub package will automatically extend deadlines until +either: + * Message.Ack or Message.Nack is called, or + * the "MaxExtension" period elapses from the time the message is fetched from the server. + +The initial ACK deadline given to each messages defaults to 10 seconds, but may +be overridden during subscription creation. Selecting an ACK deadline is a +tradeoff between message redelivery latency and RPC volume. If the pubsub +package fails to acknowledge or extend a message (e.g. due to unexpected +termination of the process), a shorter ACK deadline will generally result in +faster message redelivery by the Pub/Sub system. However, a short ACK deadline +may also increase the number of deadline extension RPCs that the pubsub package +sends to the server. + +The default max extension period is DefaultReceiveSettings.MaxExtension, and can +be overridden by setting Subscription.ReceiveSettings.MaxExtension. Selecting a +max extension period is a tradeoff between the speed at which client code must +process messages, and the redelivery delay if messages fail to be acknowledged +(e.g. because client code neglects to do so). Using a large MaxExtension +increases the available time for client code to process messages. However, if +the client code neglects to call Message.Ack/Nack, a large MaxExtension will +increase the delay before the message is redelivered. + +Slow Message Processing + +For use cases where message processing exceeds 30 minutes, we recommend using +the base client in a pull model, since long-lived streams are periodically killed +by firewalls. See the example at https://godoc.org/cloud.google.com/go/pubsub/apiv1#example-SubscriberClient-Pull-LengthyClientProcessing +*/ +package pubsub // import "cloud.google.com/go/pubsub" diff --git a/vendor/cloud.google.com/go/pubsub/endtoend_test.go b/vendor/cloud.google.com/go/pubsub/endtoend_test.go new file mode 100644 index 0000000000..9b3da09281 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/endtoend_test.go @@ -0,0 +1,234 @@ +// 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 pubsub + +import ( + "bytes" + "fmt" + "log" + "math/rand" + "os" + "sync" + "testing" + "time" + + "golang.org/x/net/context" + + "cloud.google.com/go/internal/testutil" + "google.golang.org/api/option" +) + +const ( + timeout = time.Minute * 10 + ackDeadline = time.Second * 10 + nMessages = 1e4 + acceptableDupPercentage = .05 + numAcceptableDups = int(nMessages * acceptableDupPercentage / 100) +) + +// Buffer log messages to debug failures. +var logBuf bytes.Buffer + +// The end-to-end pumps many messages into a topic and tests that they are all +// delivered to each subscription for the topic. It also tests that messages +// are not unexpectedly redelivered. +func TestIntegration_EndToEnd(t *testing.T) { + t.Parallel() + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + log.SetOutput(&logBuf) + ctx := context.Background() + ts := testutil.TokenSource(ctx, ScopePubSub, ScopeCloudPlatform) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + now := time.Now() + topicName := fmt.Sprintf("endtoend-%d", now.UnixNano()) + subPrefix := fmt.Sprintf("endtoend-%d", now.UnixNano()) + + client, err := NewClient(ctx, testutil.ProjID(), option.WithTokenSource(ts)) + if err != nil { + t.Fatalf("Creating client error: %v", err) + } + + var topic *Topic + if topic, err = client.CreateTopic(ctx, topicName); err != nil { + t.Fatalf("CreateTopic error: %v", err) + } + defer topic.Delete(ctx) + + // Two subscriptions to the same topic. + var subs [2]*Subscription + for i := 0; i < len(subs); i++ { + subs[i], err = client.CreateSubscription(ctx, fmt.Sprintf("%s-%d", subPrefix, i), SubscriptionConfig{ + Topic: topic, + AckDeadline: ackDeadline, + }) + if err != nil { + t.Fatalf("CreateSub error: %v", err) + } + defer subs[i].Delete(ctx) + } + + err = publish(ctx, topic, nMessages) + topic.Stop() + if err != nil { + t.Fatalf("publish: %v", err) + } + + // recv provides an indication that messages are still arriving. + recv := make(chan struct{}) + // We have two subscriptions to our topic. + // Each subscription will get a copy of each published message. + var wg sync.WaitGroup + cctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + consumers := []*consumer{ + {counts: make(map[string]int), recv: recv, durations: []time.Duration{time.Hour}}, + {counts: make(map[string]int), recv: recv, + durations: []time.Duration{ackDeadline, ackDeadline, ackDeadline / 2, ackDeadline / 2, time.Hour}}, + } + for i, con := range consumers { + con := con + sub := subs[i] + wg.Add(1) + go func() { + defer wg.Done() + con.consume(t, cctx, sub) + }() + } + // Wait for a while after the last message before declaring quiescence. + // We wait a multiple of the ack deadline, for two reasons: + // 1. To detect if messages are redelivered after having their ack + // deadline extended. + // 2. To wait for redelivery of messages that were en route when a Receive + // is canceled. This can take considerably longer than the ack deadline. + quiescenceDur := ackDeadline * 6 + quiescenceTimer := time.NewTimer(quiescenceDur) + +loop: + for { + select { + case <-recv: + // Reset timer so we wait quiescenceDur after the last message. + // See https://godoc.org/time#Timer.Reset for why the Stop + // and channel drain are necessary. + if !quiescenceTimer.Stop() { + <-quiescenceTimer.C + } + quiescenceTimer.Reset(quiescenceDur) + + case <-quiescenceTimer.C: + cancel() + log.Println("quiesced") + break loop + + case <-cctx.Done(): + t.Fatal("timed out") + } + } + wg.Wait() + ok := true + for i, con := range consumers { + var numDups int + var zeroes int + for _, v := range con.counts { + if v == 0 { + zeroes += 1 + } + numDups += v - 1 + } + + if zeroes > 0 { + t.Errorf("Consumer %d: %d messages never arrived", i, zeroes) + ok = false + } else if numDups > numAcceptableDups { + t.Errorf("Consumer %d: Willing to accept %d dups (%f%% duplicated of %d messages), but got %d", i, numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups) + ok = false + } + } + if !ok { + logBuf.WriteTo(os.Stdout) + } +} + +// publish publishes n messages to topic, and returns the published message IDs. +func publish(ctx context.Context, topic *Topic, n int) error { + var rs []*PublishResult + for i := 0; i < n; i++ { + m := &Message{Data: []byte(fmt.Sprintf("msg %d", i))} + rs = append(rs, topic.Publish(ctx, m)) + } + var ids []string + for _, r := range rs { + id, err := r.Get(ctx) + if err != nil { + return err + } + ids = append(ids, id) + } + return nil +} + +// consumer consumes messages according to its configuration. +type consumer struct { + durations []time.Duration + + // A value is sent to recv each time Inc is called. + recv chan struct{} + + mu sync.Mutex + counts map[string]int + total int +} + +// consume reads messages from a subscription, and keeps track of what it receives in mc. +// After consume returns, the caller should wait on wg to ensure that no more updates to mc will be made. +func (c *consumer) consume(t *testing.T, ctx context.Context, sub *Subscription) { + for _, dur := range c.durations { + ctx2, cancel := context.WithTimeout(ctx, dur) + defer cancel() + id := sub.name[len(sub.name)-1:] + log.Printf("%s: start receive", id) + prev := c.total + err := sub.Receive(ctx2, c.process) + log.Printf("%s: end receive; read %d", id, c.total-prev) + if err != nil { + t.Errorf("error from Receive: %v", err) + return + } + select { + case <-ctx.Done(): + return + default: + } + } +} + +// process handles a message and records it in mc. +func (c *consumer) process(_ context.Context, m *Message) { + c.mu.Lock() + c.counts[m.ID] += 1 + c.total++ + c.mu.Unlock() + c.recv <- struct{}{} + // Simulate time taken to process m, while continuing to process more messages. + // Some messages will need to have their ack deadline extended due to this delay. + delay := rand.Intn(int(ackDeadline * 3)) + time.AfterFunc(time.Duration(delay), m.Ack) +} diff --git a/vendor/cloud.google.com/go/pubsub/example_subscription_iterator_test.go b/vendor/cloud.google.com/go/pubsub/example_subscription_iterator_test.go new file mode 100644 index 0000000000..00d85508ef --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/example_subscription_iterator_test.go @@ -0,0 +1,54 @@ +// 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 pubsub_test + +import ( + "fmt" + + "cloud.google.com/go/pubsub" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleClient_Subscriptions() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // List all subscriptions of the project. + it := client.Subscriptions(ctx) + _ = it // TODO: iterate using Next. +} + +func ExampleSubscriptionIterator_Next() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // List all subscriptions of the project. + it := client.Subscriptions(ctx) + for { + sub, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(sub) + } +} diff --git a/vendor/cloud.google.com/go/pubsub/example_test.go b/vendor/cloud.google.com/go/pubsub/example_test.go new file mode 100644 index 0000000000..388a6f5fd6 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/example_test.go @@ -0,0 +1,369 @@ +// 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 pubsub_test + +import ( + "fmt" + "time" + + "cloud.google.com/go/pubsub" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleNewClient() { + ctx := context.Background() + _, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + // See the other examples to learn how to use the Client. +} + +func ExampleClient_CreateTopic() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + // Create a new topic with the given name. + topic, err := client.CreateTopic(ctx, "topicName") + if err != nil { + // TODO: Handle error. + } + + _ = topic // TODO: use the topic. +} + +// Use TopicInProject to refer to a topic that is not in the client's project, such +// as a public topic. +func ExampleClient_TopicInProject() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + topic := client.TopicInProject("topicName", "another-project-id") + _ = topic // TODO: use the topic. +} + +func ExampleClient_CreateSubscription() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + // Create a new topic with the given name. + topic, err := client.CreateTopic(ctx, "topicName") + if err != nil { + // TODO: Handle error. + } + + // Create a new subscription to the previously created topic + // with the given name. + sub, err := client.CreateSubscription(ctx, "subName", pubsub.SubscriptionConfig{ + Topic: topic, + AckDeadline: 10 * time.Second, + }) + if err != nil { + // TODO: Handle error. + } + + _ = sub // TODO: use the subscription. +} + +func ExampleTopic_Delete() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + topic := client.Topic("topicName") + if err := topic.Delete(ctx); err != nil { + // TODO: Handle error. + } +} + +func ExampleTopic_Exists() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + topic := client.Topic("topicName") + ok, err := topic.Exists(ctx) + if err != nil { + // TODO: Handle error. + } + if !ok { + // Topic doesn't exist. + } +} + +func ExampleTopic_Publish() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + topic := client.Topic("topicName") + defer topic.Stop() + var results []*pubsub.PublishResult + r := topic.Publish(ctx, &pubsub.Message{ + Data: []byte("hello world"), + }) + results = append(results, r) + // Do other work ... + for _, r := range results { + id, err := r.Get(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Printf("Published a message with a message ID: %s\n", id) + } +} + +func ExampleTopic_Subscriptions() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + topic := client.Topic("topic-name") + // List all subscriptions of the topic (maybe of multiple projects). + for subs := topic.Subscriptions(ctx); ; { + sub, err := subs.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + _ = sub // TODO: use the subscription. + } +} + +func ExampleSubscription_Delete() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + sub := client.Subscription("subName") + if err := sub.Delete(ctx); err != nil { + // TODO: Handle error. + } +} + +func ExampleSubscription_Exists() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + sub := client.Subscription("subName") + ok, err := sub.Exists(ctx) + if err != nil { + // TODO: Handle error. + } + if !ok { + // Subscription doesn't exist. + } +} + +func ExampleSubscription_Config() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + config, err := sub.Config(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Println(config) +} + +func ExampleSubscription_Receive() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { + // TODO: Handle message. + // NOTE: May be called concurrently; synchronize access to shared memory. + m.Ack() + }) + if err != context.Canceled { + // TODO: Handle error. + } +} + +// This example shows how to configure keepalive so that unacknoweldged messages +// expire quickly, allowing other subscribers to take them. +func ExampleSubscription_Receive_maxExtension() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + // This program is expected to process and acknowledge messages in 30 seconds. If + // not, the Pub/Sub API will assume the message is not acknowledged. + sub.ReceiveSettings.MaxExtension = 30 * time.Second + err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { + // TODO: Handle message. + m.Ack() + }) + if err != context.Canceled { + // TODO: Handle error. + } +} + +// This example shows how to throttle Subscription.Receive, which aims for high +// throughput by default. By limiting the number of messages and/or bytes being +// processed at once, you can bound your program's resource consumption. +func ExampleSubscription_Receive_maxOutstanding() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + sub.ReceiveSettings.MaxOutstandingMessages = 5 + sub.ReceiveSettings.MaxOutstandingBytes = 10e6 + err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { + // TODO: Handle message. + m.Ack() + }) + if err != context.Canceled { + // TODO: Handle error. + } +} + +func ExampleSubscription_Update() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + subConfig, err := sub.Update(ctx, pubsub.SubscriptionConfigToUpdate{ + PushConfig: &pubsub.PushConfig{Endpoint: "https://example.com/push"}, + }) + if err != nil { + // TODO: Handle error. + } + _ = subConfig // TODO: Use SubscriptionConfig. +} + +func ExampleSubscription_CreateSnapshot() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + snapConfig, err := sub.CreateSnapshot(ctx, "snapshotName") + if err != nil { + // TODO: Handle error. + } + _ = snapConfig // TODO: Use SnapshotConfig. +} + +func ExampleSubscription_SeekToSnapshot() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + snap := client.Snapshot("snapshotName") + if err := sub.SeekToSnapshot(ctx, snap); err != nil { + // TODO: Handle error. + } +} + +func ExampleSubscription_SeekToTime() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + sub := client.Subscription("subName") + if err := sub.SeekToTime(ctx, time.Now().Add(-time.Hour)); err != nil { + // TODO: Handle error. + } +} + +func ExampleSnapshot_Delete() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + + snap := client.Snapshot("snapshotName") + if err := snap.Delete(ctx); err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_Snapshots() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // List all snapshots for the project. + iter := client.Snapshots(ctx) + _ = iter // TODO: iterate using Next. +} + +func ExampleSnapshotConfigIterator_Next() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // List all snapshots for the project. + iter := client.Snapshots(ctx) + for { + snapConfig, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + _ = snapConfig // TODO: use the SnapshotConfig. + } +} + +// TODO(jba): write an example for PublishResult.Ready +// TODO(jba): write an example for Subscription.IAM +// TODO(jba): write an example for Topic.IAM +// TODO(jba): write an example for Topic.Stop diff --git a/vendor/cloud.google.com/go/pubsub/example_topic_iterator_test.go b/vendor/cloud.google.com/go/pubsub/example_topic_iterator_test.go new file mode 100644 index 0000000000..3a86083824 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/example_topic_iterator_test.go @@ -0,0 +1,53 @@ +// 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 pubsub_test + +import ( + "fmt" + + "cloud.google.com/go/pubsub" + "golang.org/x/net/context" + "google.golang.org/api/iterator" +) + +func ExampleClient_Topics() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + it := client.Topics(ctx) + _ = it // TODO: iterate using Next. +} + +func ExampleTopicIterator_Next() { + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "project-id") + if err != nil { + // TODO: Handle error. + } + // List all topics. + it := client.Topics(ctx) + for { + t, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(t) + } +} diff --git a/vendor/cloud.google.com/go/pubsub/flow_controller.go b/vendor/cloud.google.com/go/pubsub/flow_controller.go new file mode 100644 index 0000000000..594b7a962f --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/flow_controller.go @@ -0,0 +1,106 @@ +// Copyright 2017 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 pubsub + +import ( + "golang.org/x/net/context" + "golang.org/x/sync/semaphore" +) + +// flowController implements flow control for Subscription.Receive. +type flowController struct { + maxSize int // max total size of messages + semCount, semSize *semaphore.Weighted // enforces max number and size of messages +} + +// newFlowController creates a new flowController that ensures no more than +// maxCount messages or maxSize bytes are outstanding at once. If maxCount or +// maxSize is < 1, then an unlimited number of messages or bytes is permitted, +// respectively. +func newFlowController(maxCount, maxSize int) *flowController { + fc := &flowController{ + maxSize: maxSize, + semCount: nil, + semSize: nil, + } + if maxCount > 0 { + fc.semCount = semaphore.NewWeighted(int64(maxCount)) + } + if maxSize > 0 { + fc.semSize = semaphore.NewWeighted(int64(maxSize)) + } + return fc +} + +// acquire blocks until one message of size bytes can proceed or ctx is done. +// It returns nil in the first case, or ctx.Err() in the second. +// +// acquire allows large messages to proceed by treating a size greater than maxSize +// as if it were equal to maxSize. +func (f *flowController) acquire(ctx context.Context, size int) error { + if f.semCount != nil { + if err := f.semCount.Acquire(ctx, 1); err != nil { + return err + } + } + if f.semSize != nil { + if err := f.semSize.Acquire(ctx, f.bound(size)); err != nil { + if f.semCount != nil { + f.semCount.Release(1) + } + return err + } + } + return nil +} + +// tryAcquire returns false if acquire would block. Otherwise, it behaves like +// acquire and returns true. +// +// tryAcquire allows large messages to proceed by treating a size greater than +// maxSize as if it were equal to maxSize. +func (f *flowController) tryAcquire(size int) bool { + if f.semCount != nil { + if !f.semCount.TryAcquire(1) { + return false + } + } + if f.semSize != nil { + if !f.semSize.TryAcquire(f.bound(size)) { + if f.semCount != nil { + f.semCount.Release(1) + } + return false + } + } + return true +} + +// release notes that one message of size bytes is no longer outstanding. +func (f *flowController) release(size int) { + if f.semCount != nil { + f.semCount.Release(1) + } + if f.semSize != nil { + f.semSize.Release(f.bound(size)) + } +} + +func (f *flowController) bound(size int) int64 { + if size > f.maxSize { + return int64(f.maxSize) + } + return int64(size) +} diff --git a/vendor/cloud.google.com/go/pubsub/flow_controller_test.go b/vendor/cloud.google.com/go/pubsub/flow_controller_test.go new file mode 100644 index 0000000000..1449803c2b --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/flow_controller_test.go @@ -0,0 +1,239 @@ +// Copyright 2017 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 pubsub + +import ( + "errors" + "fmt" + "sync/atomic" + "testing" + "time" + + "golang.org/x/net/context" + "golang.org/x/sync/errgroup" +) + +func TestFlowControllerCancel(t *testing.T) { + // Test canceling a flow controller's context. + t.Parallel() + fc := newFlowController(3, 10) + if err := fc.acquire(context.Background(), 5); err != nil { + t.Fatal(err) + } + // Experiment: a context that times out should always return an error. + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond) + defer cancel() + if err := fc.acquire(ctx, 6); err != context.DeadlineExceeded { + t.Fatalf("got %v, expected DeadlineExceeded", err) + } + // Control: a context that is not done should always return nil. + go func() { + time.Sleep(5 * time.Millisecond) + fc.release(5) + }() + if err := fc.acquire(context.Background(), 6); err != nil { + t.Errorf("got %v, expected nil", err) + } +} + +func TestFlowControllerLargeRequest(t *testing.T) { + // Large requests succeed, consuming the entire allotment. + t.Parallel() + fc := newFlowController(3, 10) + err := fc.acquire(context.Background(), 11) + if err != nil { + t.Fatal(err) + } +} + +func TestFlowControllerNoStarve(t *testing.T) { + // A large request won't starve, because the flowController is + // (best-effort) FIFO. + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + fc := newFlowController(10, 10) + first := make(chan int) + for i := 0; i < 20; i++ { + go func() { + for { + if err := fc.acquire(ctx, 1); err != nil { + if err != context.Canceled { + t.Error(err) + } + return + } + select { + case first <- 1: + default: + } + fc.release(1) + } + }() + } + <-first // Wait until the flowController's state is non-zero. + if err := fc.acquire(ctx, 11); err != nil { + t.Errorf("got %v, want nil", err) + } +} + +func TestFlowControllerSaturation(t *testing.T) { + t.Parallel() + const ( + maxCount = 6 + maxSize = 10 + ) + for _, test := range []struct { + acquireSize int + wantCount, wantSize int64 + }{ + { + // Many small acquires cause the flow controller to reach its max count. + acquireSize: 1, + wantCount: 6, + wantSize: 6, + }, + { + // Five acquires of size 2 will cause the flow controller to reach its max size, + // but not its max count. + acquireSize: 2, + wantCount: 5, + wantSize: 10, + }, + { + // If the requests are the right size (relatively prime to maxSize), + // the flow controller will not saturate on size. (In this case, not on count either.) + acquireSize: 3, + wantCount: 3, + wantSize: 9, + }, + } { + fc := newFlowController(maxCount, maxSize) + // Atomically track flow controller state. + var curCount, curSize int64 + success := errors.New("") + // Time out if wantSize or wantCount is never reached. + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + g, ctx := errgroup.WithContext(ctx) + for i := 0; i < 10; i++ { + g.Go(func() error { + var hitCount, hitSize bool + // Run at least until we hit the expected values, and at least + // for enough iterations to exceed them if the flow controller + // is broken. + for i := 0; i < 100 || !hitCount || !hitSize; i++ { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + if err := fc.acquire(ctx, test.acquireSize); err != nil { + return err + } + c := atomic.AddInt64(&curCount, 1) + if c > test.wantCount { + return fmt.Errorf("count %d exceeds want %d", c, test.wantCount) + } + if c == test.wantCount { + hitCount = true + } + s := atomic.AddInt64(&curSize, int64(test.acquireSize)) + if s > test.wantSize { + return fmt.Errorf("size %d exceeds want %d", s, test.wantSize) + } + if s == test.wantSize { + hitSize = true + } + time.Sleep(5 * time.Millisecond) // Let other goroutines make progress. + if atomic.AddInt64(&curCount, -1) < 0 { + return errors.New("negative count") + } + if atomic.AddInt64(&curSize, -int64(test.acquireSize)) < 0 { + return errors.New("negative size") + } + fc.release(test.acquireSize) + } + return success + }) + } + if err := g.Wait(); err != success { + t.Errorf("%+v: %v", test, err) + continue + } + } +} + +func TestFlowControllerTryAcquire(t *testing.T) { + t.Parallel() + fc := newFlowController(3, 10) + + // Successfully tryAcquire 4 bytes. + if !fc.tryAcquire(4) { + t.Error("got false, wanted true") + } + + // Fail to tryAcquire 7 bytes. + if fc.tryAcquire(7) { + t.Error("got true, wanted false") + } + + // Successfully tryAcquire 6 byte. + if !fc.tryAcquire(6) { + t.Error("got false, wanted true") + } +} + +func TestFlowControllerUnboundedCount(t *testing.T) { + t.Parallel() + ctx := context.Background() + fc := newFlowController(0, 10) + + // Successfully acquire 4 bytes. + if err := fc.acquire(ctx, 4); err != nil { + t.Errorf("got %v, wanted no error", err) + } + + // Successfully tryAcquire 4 bytes. + if !fc.tryAcquire(4) { + t.Error("got false, wanted true") + } + + // Fail to tryAcquire 3 bytes. + if fc.tryAcquire(3) { + t.Error("got true, wanted false") + } +} + +func TestFlowControllerUnboundedBytes(t *testing.T) { + t.Parallel() + ctx := context.Background() + fc := newFlowController(2, 0) + + // Successfully acquire 4GB. + if err := fc.acquire(ctx, 4e9); err != nil { + t.Errorf("got %v, wanted no error", err) + } + + // Successfully tryAcquire 4GB bytes. + if !fc.tryAcquire(4e9) { + t.Error("got false, wanted true") + } + + // Fail to tryAcquire a third message. + if fc.tryAcquire(3) { + t.Error("got true, wanted false") + } +} diff --git a/vendor/cloud.google.com/go/pubsub/go18.go b/vendor/cloud.google.com/go/pubsub/go18.go new file mode 100644 index 0000000000..f7287f430d --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/go18.go @@ -0,0 +1,150 @@ +// 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. + +// +build go1.8 + +package pubsub + +import ( + "log" + "sync" + + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/stats" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +func openCensusOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithGRPCDialOption(grpc.WithStatsHandler(&ocgrpc.ClientHandler{})), + } +} + +var subscriptionKey tag.Key + +func init() { + var err error + if subscriptionKey, err = tag.NewKey("subscription"); err != nil { + log.Fatal("cannot create 'subscription' key") + } +} + +const statsPrefix = "cloud.google.com/go/pubsub/" + +var ( + // PullCount is a measure of the number of messages pulled. + // It is EXPERIMENTAL and subject to change or removal without notice. + PullCount = stats.Int64(statsPrefix+"pull_count", "Number of PubSub messages pulled", stats.UnitDimensionless) + + // AckCount is a measure of the number of messages acked. + // It is EXPERIMENTAL and subject to change or removal without notice. + AckCount = stats.Int64(statsPrefix+"ack_count", "Number of PubSub messages acked", stats.UnitDimensionless) + + // NackCount is a measure of the number of messages nacked. + // It is EXPERIMENTAL and subject to change or removal without notice. + NackCount = stats.Int64(statsPrefix+"nack_count", "Number of PubSub messages nacked", stats.UnitDimensionless) + + // ModAckCount is a measure of the number of messages whose ack-deadline was modified. + // It is EXPERIMENTAL and subject to change or removal without notice. + ModAckCount = stats.Int64(statsPrefix+"mod_ack_count", "Number of ack-deadlines modified", stats.UnitDimensionless) + + // StreamOpenCount is a measure of the number of times a streaming-pull stream was opened. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamOpenCount = stats.Int64(statsPrefix+"stream_open_count", "Number of calls opening a new streaming pull", stats.UnitDimensionless) + + // StreamRetryCount is a measure of the number of times a streaming-pull operation was retried. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamRetryCount = stats.Int64(statsPrefix+"stream_retry_count", "Number of retries of a stream send or receive", stats.UnitDimensionless) + + // StreamRequestCount is a measure of the number of requests sent on a streaming-pull stream. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamRequestCount = stats.Int64(statsPrefix+"stream_request_count", "Number gRPC StreamingPull request messages sent", stats.UnitDimensionless) + + // StreamResponseCount is a measure of the number of responses received on a streaming-pull stream. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamResponseCount = stats.Int64(statsPrefix+"stream_response_count", "Number of gRPC StreamingPull response messages received", stats.UnitDimensionless) + + // PullCountView is a cumulative sum of PullCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + PullCountView *view.View + + // AckCountView is a cumulative sum of AckCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + AckCountView *view.View + + // NackCountView is a cumulative sum of NackCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + NackCountView *view.View + + // ModAckCountView is a cumulative sum of ModAckCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + ModAckCountView *view.View + + // StreamOpenCountView is a cumulative sum of StreamOpenCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamOpenCountView *view.View + + // StreamRetryCountView is a cumulative sum of StreamRetryCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamRetryCountView *view.View + + // StreamRequestCountView is a cumulative sum of StreamRequestCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamRequestCountView *view.View + + // StreamResponseCountView is a cumulative sum of StreamResponseCount. + // It is EXPERIMENTAL and subject to change or removal without notice. + StreamResponseCountView *view.View +) + +func init() { + PullCountView = countView(PullCount) + AckCountView = countView(AckCount) + NackCountView = countView(NackCount) + ModAckCountView = countView(ModAckCount) + StreamOpenCountView = countView(StreamOpenCount) + StreamRetryCountView = countView(StreamRetryCount) + StreamRequestCountView = countView(StreamRequestCount) + StreamResponseCountView = countView(StreamResponseCount) +} + +func countView(m *stats.Int64Measure) *view.View { + return &view.View{ + Name: m.Name(), + Description: m.Description(), + TagKeys: []tag.Key{subscriptionKey}, + Measure: m, + Aggregation: view.Sum(), + } +} + +var logOnce sync.Once + +func withSubscriptionKey(ctx context.Context, subName string) context.Context { + ctx, err := tag.New(ctx, tag.Upsert(subscriptionKey, subName)) + if err != nil { + logOnce.Do(func() { + log.Printf("pubsub: error creating tag map: %v", err) + }) + } + return ctx +} + +func recordStat(ctx context.Context, m *stats.Int64Measure, n int64) { + stats.Record(ctx, m.M(n)) +} diff --git a/vendor/cloud.google.com/go/pubsub/integration_test.go b/vendor/cloud.google.com/go/pubsub/integration_test.go new file mode 100644 index 0000000000..7f63d80c75 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/integration_test.go @@ -0,0 +1,449 @@ +// 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 pubsub + +import ( + "fmt" + "testing" + "time" + + gax "github.com/googleapis/gax-go" + + "golang.org/x/net/context" + + "cloud.google.com/go/iam" + "cloud.google.com/go/internal" + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/internal/uid" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var ( + topicIDs = uid.NewSpace("topic", nil) + subIDs = uid.NewSpace("sub", nil) +) + +// messageData is used to hold the contents of a message so that it can be compared against the contents +// of another message without regard to irrelevant fields. +type messageData struct { + ID string + Data []byte + Attributes map[string]string +} + +func extractMessageData(m *Message) *messageData { + return &messageData{ + ID: m.ID, + Data: m.Data, + Attributes: m.Attributes, + } +} + +func integrationTestClient(t *testing.T, ctx context.Context) *Client { + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + projID := testutil.ProjID() + if projID == "" { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + ts := testutil.TokenSource(ctx, ScopePubSub, ScopeCloudPlatform) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + client, err := NewClient(ctx, projID, option.WithTokenSource(ts)) + if err != nil { + t.Fatalf("Creating client error: %v", err) + } + return client +} + +func TestIntegration_All(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := integrationTestClient(t, ctx) + defer client.Close() + + topic, err := client.CreateTopic(ctx, topicIDs.New()) + if err != nil { + t.Errorf("CreateTopic error: %v", err) + } + defer topic.Stop() + + var sub *Subscription + if sub, err = client.CreateSubscription(ctx, subIDs.New(), SubscriptionConfig{Topic: topic}); err != nil { + t.Errorf("CreateSub error: %v", err) + } + + exists, err := topic.Exists(ctx) + if err != nil { + t.Fatalf("TopicExists error: %v", err) + } + if !exists { + t.Errorf("topic %v should exist, but it doesn't", topic) + } + + exists, err = sub.Exists(ctx) + if err != nil { + t.Fatalf("SubExists error: %v", err) + } + if !exists { + t.Errorf("subscription %s should exist, but it doesn't", sub.ID()) + } + + var msgs []*Message + for i := 0; i < 10; i++ { + text := fmt.Sprintf("a message with an index %d", i) + attrs := make(map[string]string) + attrs["foo"] = "bar" + msgs = append(msgs, &Message{ + Data: []byte(text), + Attributes: attrs, + }) + } + + // Publish the messages. + type pubResult struct { + m *Message + r *PublishResult + } + var rs []pubResult + for _, m := range msgs { + r := topic.Publish(ctx, m) + rs = append(rs, pubResult{m, r}) + } + want := make(map[string]*messageData) + for _, res := range rs { + id, err := res.r.Get(ctx) + if err != nil { + t.Fatal(err) + } + md := extractMessageData(res.m) + md.ID = id + want[md.ID] = md + } + + // Use a timeout to ensure that Pull does not block indefinitely if there are unexpectedly few messages available. + timeoutCtx, _ := context.WithTimeout(ctx, time.Minute) + gotMsgs, err := pullN(timeoutCtx, sub, len(want), func(ctx context.Context, m *Message) { + m.Ack() + }) + if err != nil { + t.Fatalf("Pull: %v", err) + } + got := make(map[string]*messageData) + for _, m := range gotMsgs { + md := extractMessageData(m) + got[md.ID] = md + } + if !testutil.Equal(got, want) { + t.Errorf("messages: got: %v ; want: %v", got, want) + } + + if msg, ok := testIAM(ctx, topic.IAM(), "pubsub.topics.get"); !ok { + t.Errorf("topic IAM: %s", msg) + } + if msg, ok := testIAM(ctx, sub.IAM(), "pubsub.subscriptions.get"); !ok { + t.Errorf("sub IAM: %s", msg) + } + + snap, err := sub.CreateSnapshot(ctx, "") + if err != nil { + t.Fatalf("CreateSnapshot error: %v", err) + } + + timeoutCtx, _ = context.WithTimeout(ctx, time.Minute) + err = internal.Retry(timeoutCtx, gax.Backoff{}, func() (bool, error) { + snapIt := client.Snapshots(timeoutCtx) + for { + s, err := snapIt.Next() + if err == nil && s.name == snap.name { + return true, nil + } + if err == iterator.Done { + return false, fmt.Errorf("cannot find snapshot: %q", snap.name) + } + if err != nil { + return false, err + } + } + }) + if err != nil { + t.Error(err) + } + + err = internal.Retry(timeoutCtx, gax.Backoff{}, func() (bool, error) { + err := sub.SeekToSnapshot(timeoutCtx, snap.Snapshot) + return err == nil, err + }) + if err != nil { + t.Error(err) + } + + err = internal.Retry(timeoutCtx, gax.Backoff{}, func() (bool, error) { + err := sub.SeekToTime(timeoutCtx, time.Now()) + return err == nil, err + }) + if err != nil { + t.Error(err) + } + + err = internal.Retry(timeoutCtx, gax.Backoff{}, func() (bool, error) { + snapHandle := client.Snapshot(snap.ID()) + err := snapHandle.Delete(timeoutCtx) + return err == nil, err + }) + if err != nil { + t.Error(err) + } + + if err := sub.Delete(ctx); err != nil { + t.Errorf("DeleteSub error: %v", err) + } + + if err := topic.Delete(ctx); err != nil { + t.Errorf("DeleteTopic error: %v", err) + } +} + +// IAM tests. +// NOTE: for these to succeed, the test runner identity must have the Pub/Sub Admin or Owner roles. +// To set, visit https://console.developers.google.com, select "IAM & Admin" from the top-left +// menu, choose the account, click the Roles dropdown, and select "Pub/Sub > Pub/Sub Admin". +// TODO(jba): move this to a testing package within cloud.google.com/iam, so we can re-use it. +func testIAM(ctx context.Context, h *iam.Handle, permission string) (msg string, ok bool) { + // Attempting to add an non-existent identity (e.g. "alice@example.com") causes the service + // to return an internal error, so use a real identity. + const member = "domain:google.com" + + var policy *iam.Policy + var err error + + if policy, err = h.Policy(ctx); err != nil { + return fmt.Sprintf("Policy: %v", err), false + } + // The resource is new, so the policy should be empty. + if got := policy.Roles(); len(got) > 0 { + return fmt.Sprintf("initially: got roles %v, want none", got), false + } + // Add a member, set the policy, then check that the member is present. + policy.Add(member, iam.Viewer) + if err := h.SetPolicy(ctx, policy); err != nil { + return fmt.Sprintf("SetPolicy: %v", err), false + } + if policy, err = h.Policy(ctx); err != nil { + return fmt.Sprintf("Policy: %v", err), false + } + if got, want := policy.Members(iam.Viewer), []string{member}; !testutil.Equal(got, want) { + return fmt.Sprintf("after Add: got %v, want %v", got, want), false + } + // Now remove that member, set the policy, and check that it's empty again. + policy.Remove(member, iam.Viewer) + if err := h.SetPolicy(ctx, policy); err != nil { + return fmt.Sprintf("SetPolicy: %v", err), false + } + if policy, err = h.Policy(ctx); err != nil { + return fmt.Sprintf("Policy: %v", err), false + } + if got := policy.Roles(); len(got) > 0 { + return fmt.Sprintf("after Remove: got roles %v, want none", got), false + } + // Call TestPermissions. + // Because this user is an admin, it has all the permissions on the + // resource type. Note: the service fails if we ask for inapplicable + // permissions (e.g. a subscription permission on a topic, or a topic + // create permission on a topic rather than its parent). + wantPerms := []string{permission} + gotPerms, err := h.TestPermissions(ctx, wantPerms) + if err != nil { + return fmt.Sprintf("TestPermissions: %v", err), false + } + if !testutil.Equal(gotPerms, wantPerms) { + return fmt.Sprintf("TestPermissions: got %v, want %v", gotPerms, wantPerms), false + } + return "", true +} + +func TestIntegration_UpdateSubscription(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := integrationTestClient(t, ctx) + defer client.Close() + + topic, err := client.CreateTopic(ctx, topicIDs.New()) + if err != nil { + t.Fatalf("CreateTopic error: %v", err) + } + defer topic.Stop() + defer topic.Delete(ctx) + + var sub *Subscription + if sub, err = client.CreateSubscription(ctx, subIDs.New(), SubscriptionConfig{Topic: topic}); err != nil { + t.Fatalf("CreateSub error: %v", err) + } + defer sub.Delete(ctx) + + got, err := sub.Config(ctx) + if err != nil { + t.Fatal(err) + } + want := SubscriptionConfig{ + Topic: topic, + AckDeadline: 10 * time.Second, + RetainAckedMessages: false, + RetentionDuration: defaultRetentionDuration, + } + if !testutil.Equal(got, want) { + t.Fatalf("\ngot %+v\nwant %+v", got, want) + } + // Add a PushConfig and change other fields. + projID := testutil.ProjID() + pc := PushConfig{ + Endpoint: "https://" + projID + ".appspot.com/_ah/push-handlers/push", + Attributes: map[string]string{"x-goog-version": "v1"}, + } + got, err = sub.Update(ctx, SubscriptionConfigToUpdate{ + PushConfig: &pc, + AckDeadline: 2 * time.Minute, + RetainAckedMessages: true, + RetentionDuration: 2 * time.Hour, + }) + if err != nil { + t.Fatal(err) + } + want = SubscriptionConfig{ + Topic: topic, + PushConfig: pc, + AckDeadline: 2 * time.Minute, + RetainAckedMessages: true, + RetentionDuration: 2 * time.Hour, + } + if !testutil.Equal(got, want) { + t.Fatalf("\ngot %+v\nwant %+v", got, want) + } + // Remove the PushConfig, turning the subscription back into pull mode. + // Change AckDeadline, but nothing else. + pc = PushConfig{} + got, err = sub.Update(ctx, SubscriptionConfigToUpdate{ + PushConfig: &pc, + AckDeadline: 30 * time.Second, + }) + if err != nil { + t.Fatal(err) + } + want.PushConfig = pc + want.AckDeadline = 30 * time.Second + // service issue: PushConfig attributes are not removed. + // TODO(jba): remove when issue resolved. + want.PushConfig.Attributes = map[string]string{"x-goog-version": "v1"} + if !testutil.Equal(got, want) { + t.Fatalf("\ngot %+v\nwant %+v", got, want) + } + // If nothing changes, our client returns an error. + _, err = sub.Update(ctx, SubscriptionConfigToUpdate{}) + if err == nil { + t.Fatal("got nil, wanted error") + } +} + +func TestIntegration_PublicTopic(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := integrationTestClient(t, ctx) + defer client.Close() + + sub, err := client.CreateSubscription(ctx, subIDs.New(), SubscriptionConfig{ + Topic: client.TopicInProject("taxirides-realtime", "pubsub-public-data"), + }) + if err != nil { + t.Fatal(err) + } + defer sub.Delete(ctx) + // Confirm that Receive works. It doesn't matter if we actually get any + // messages. + ctxt, cancel := context.WithTimeout(ctx, 5*time.Second) + err = sub.Receive(ctxt, func(_ context.Context, msg *Message) { + msg.Ack() + cancel() + }) + if err != nil { + t.Fatal(err) + } +} + +func TestIntegration_Errors(t *testing.T) { + // Test various edge conditions. + t.Parallel() + ctx := context.Background() + client := integrationTestClient(t, ctx) + defer client.Close() + + topic, err := client.CreateTopic(ctx, topicIDs.New()) + if err != nil { + t.Fatalf("CreateTopic error: %v", err) + } + defer topic.Stop() + defer topic.Delete(ctx) + + // Out-of-range retention duration. + sub, err := client.CreateSubscription(ctx, subIDs.New(), SubscriptionConfig{ + Topic: topic, + RetentionDuration: 1 * time.Second, + }) + if want := codes.InvalidArgument; grpc.Code(err) != want { + t.Errorf("got <%v>, want %s", err, want) + } + if err == nil { + sub.Delete(ctx) + } + + // Ack deadline less than minimum. + sub, err = client.CreateSubscription(ctx, subIDs.New(), SubscriptionConfig{ + Topic: topic, + AckDeadline: 5 * time.Second, + }) + if want := codes.Unknown; grpc.Code(err) != want { + t.Errorf("got <%v>, want %s", err, want) + } + if err == nil { + sub.Delete(ctx) + } + + // Updating a non-existent subscription. + sub = client.Subscription(subIDs.New()) + _, err = sub.Update(ctx, SubscriptionConfigToUpdate{AckDeadline: 20 * time.Second}) + if want := codes.NotFound; grpc.Code(err) != want { + t.Errorf("got <%v>, want %s", err, want) + } + // Deleting a non-existent subscription. + err = sub.Delete(ctx) + if want := codes.NotFound; grpc.Code(err) != want { + t.Errorf("got <%v>, want %s", err, want) + } + + // Updating out-of-range retention duration. + sub, err = client.CreateSubscription(ctx, subIDs.New(), SubscriptionConfig{Topic: topic}) + if err != nil { + t.Fatal(err) + } + defer sub.Delete(ctx) + _, err = sub.Update(ctx, SubscriptionConfigToUpdate{RetentionDuration: 1000 * time.Hour}) + if want := codes.InvalidArgument; grpc.Code(err) != want { + t.Errorf("got <%v>, want %s", err, want) + } +} diff --git a/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution.go b/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution.go new file mode 100644 index 0000000000..6ac6c61fd5 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution.go @@ -0,0 +1,70 @@ +// Copyright 2017 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 distribution + +import ( + "log" + "math" + "sort" + "sync/atomic" +) + +// D is a distribution. Methods of D can be called concurrently by multiple +// goroutines. +type D struct { + buckets []uint64 +} + +// New creates a new distribution capable of holding values from 0 to n-1. +func New(n int) *D { + return &D{ + buckets: make([]uint64, n), + } +} + +// Record records value v to the distribution. +// To help with distributions with long tails, if v is larger than the maximum value, +// Record records the maximum value instead. +// If v is negative, Record panics. +func (d *D) Record(v int) { + if v < 0 { + log.Panicf("Record: value out of range: %d", v) + } else if v >= len(d.buckets) { + v = len(d.buckets) - 1 + } + atomic.AddUint64(&d.buckets[v], 1) +} + +// Percentile computes the p-th percentile of the distribution where +// p is between 0 and 1. +func (d *D) Percentile(p float64) int { + // NOTE: This implementation uses the nearest-rank method. + // https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method + + if p < 0 || p > 1 { + log.Panicf("Percentile: percentile out of range: %f", p) + } + + bucketSums := make([]uint64, len(d.buckets)) + var sum uint64 + for i := range bucketSums { + sum += atomic.LoadUint64(&d.buckets[i]) + bucketSums[i] = sum + } + + total := bucketSums[len(bucketSums)-1] + target := uint64(math.Ceil(float64(total) * p)) + return sort.Search(len(bucketSums), func(i int) bool { return bucketSums[i] >= target }) +} diff --git a/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution_test.go b/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution_test.go new file mode 100644 index 0000000000..5e9296d27f --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution_test.go @@ -0,0 +1,94 @@ +// Copyright 2017 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 distribution + +import ( + "sync" + "testing" +) + +func TestDistribution(t *testing.T) { + // These tests come from examples in https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method + tests := []struct { + // values in distribution + vals []int + + // percentiles and expected percentile values + pp []float64 + vv []int + }{ + { + vals: []int{15, 20, 35, 40, 50}, + pp: []float64{0.05, 0.3, 0.4, 0.5, 1}, + vv: []int{15, 20, 20, 35, 50}, + }, + { + vals: []int{3, 6, 7, 8, 8, 10, 13, 15, 16, 20}, + pp: []float64{0.25, 0.5, 0.75, 1}, + vv: []int{7, 8, 15, 20}, + }, + { + vals: []int{3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20}, + pp: []float64{0.25, 0.5, 0.75, 1}, + vv: []int{7, 9, 15, 20}, + }, + } + + maxVal := 0 + for _, tst := range tests { + for _, v := range tst.vals { + if maxVal < v { + maxVal = v + } + } + } + + for _, tst := range tests { + d := New(maxVal + 1) + for _, v := range tst.vals { + d.Record(v) + } + for i, p := range tst.pp { + got, want := d.Percentile(p), tst.vv[i] + if got != want { + t.Errorf("d=%v, d.Percentile(%f)=%d, want %d", d, p, got, want) + } + } + } +} + +func TestRace(t *testing.T) { + const N int = 1e3 + const parallel = 2 + + d := New(N) + + var wg sync.WaitGroup + wg.Add(parallel) + for i := 0; i < parallel; i++ { + go func() { + for i := 0; i < N; i++ { + d.Record(i) + } + wg.Done() + }() + } + + for i := 0; i < N; i++ { + if p := d.Percentile(0.5); p > N { + t.Fatalf("d.Percentile(0.5)=%d, expected to be at most %d", p, N) + } + } +} diff --git a/vendor/cloud.google.com/go/pubsub/iterator.go b/vendor/cloud.google.com/go/pubsub/iterator.go new file mode 100644 index 0000000000..54e9e44651 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/iterator.go @@ -0,0 +1,379 @@ +// 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 pubsub + +import ( + "sync" + "time" + + vkit "cloud.google.com/go/pubsub/apiv1" + "cloud.google.com/go/pubsub/internal/distribution" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +// newMessageIterator starts a new streamingMessageIterator. Stop must be called on the messageIterator +// when it is no longer needed. +// subName is the full name of the subscription to pull messages from. +// ctx is the context to use for acking messages and extending message deadlines. +func newMessageIterator(ctx context.Context, subc *vkit.SubscriberClient, subName string, po *pullOptions) *streamingMessageIterator { + ps := newPullStream(ctx, subc.StreamingPull, subName, int32(po.ackDeadline.Seconds())) + return newStreamingMessageIterator(ctx, ps, po, subc, subName) +} + +type streamingMessageIterator struct { + ctx context.Context + po *pullOptions + ps *pullStream + subc *vkit.SubscriberClient + subName string + kaTicker *time.Ticker // keep-alive (deadline extensions) + ackTicker *time.Ticker // message acks + nackTicker *time.Ticker // message nacks (more frequent than acks) + pingTicker *time.Ticker // sends to the stream to keep it open + failed chan struct{} // closed on stream error + stopped chan struct{} // closed when Stop is called + drained chan struct{} // closed when stopped && no more pending messages + wg sync.WaitGroup + + mu sync.Mutex + ackTimeDist *distribution.D + keepAliveDeadlines map[string]time.Time + pendingAcks map[string]bool + pendingNacks map[string]bool + pendingModAcks map[string]bool // ack IDs whose ack deadline is to be modified + err error // error from stream failure +} + +func newStreamingMessageIterator(ctx context.Context, ps *pullStream, po *pullOptions, subc *vkit.SubscriberClient, subName string) *streamingMessageIterator { + // TODO: make kaTicker frequency more configurable. (ackDeadline - 5s) is a + // reasonable default for now, because the minimum ack period is 10s. This + // gives us 5s grace. + keepAlivePeriod := po.ackDeadline - 5*time.Second + kaTicker := time.NewTicker(keepAlivePeriod) + + // Ack promptly so users don't lose work if client crashes. + ackTicker := time.NewTicker(100 * time.Millisecond) + nackTicker := time.NewTicker(100 * time.Millisecond) + pingTicker := time.NewTicker(30 * time.Second) + it := &streamingMessageIterator{ + ctx: ctx, + ps: ps, + po: po, + subc: subc, + subName: subName, + kaTicker: kaTicker, + ackTicker: ackTicker, + nackTicker: nackTicker, + pingTicker: pingTicker, + failed: make(chan struct{}), + stopped: make(chan struct{}), + drained: make(chan struct{}), + ackTimeDist: distribution.New(int(maxAckDeadline/time.Second) + 1), + keepAliveDeadlines: map[string]time.Time{}, + pendingAcks: map[string]bool{}, + pendingNacks: map[string]bool{}, + pendingModAcks: map[string]bool{}, + } + it.wg.Add(1) + go it.sender() + return it +} + +// Subscription.receive will call stop on its messageIterator when finished with it. +// Stop will block until Done has been called on all Messages that have been +// returned by Next, or until the context with which the messageIterator was created +// is cancelled or exceeds its deadline. +func (it *streamingMessageIterator) stop() { + it.mu.Lock() + select { + case <-it.stopped: + default: + close(it.stopped) + } + it.checkDrained() + it.mu.Unlock() + it.wg.Wait() +} + +// checkDrained closes the drained channel if the iterator has been stopped and all +// pending messages have either been n/acked or expired. +// +// Called with the lock held. +func (it *streamingMessageIterator) checkDrained() { + select { + case <-it.drained: + return + default: + } + select { + case <-it.stopped: + if len(it.keepAliveDeadlines) == 0 { + close(it.drained) + } + default: + } +} + +// Called when a message is acked/nacked. +func (it *streamingMessageIterator) done(ackID string, ack bool, receiveTime time.Time) { + it.ackTimeDist.Record(int(time.Since(receiveTime) / time.Second)) + it.mu.Lock() + defer it.mu.Unlock() + delete(it.keepAliveDeadlines, ackID) + if ack { + it.pendingAcks[ackID] = true + } else { + it.pendingNacks[ackID] = true + } + it.checkDrained() +} + +// fail is called when a stream method returns a permanent error. +func (it *streamingMessageIterator) fail(err error) { + it.mu.Lock() + if it.err == nil { + it.err = err + close(it.failed) + } + it.mu.Unlock() +} + +// receive makes a call to the stream's Recv method and returns +// its messages. +func (it *streamingMessageIterator) receive() ([]*Message, error) { + // Stop retrieving messages if the context is done, the stream + // failed, or the iterator's Stop method was called. + select { + case <-it.ctx.Done(): + return nil, it.ctx.Err() + default: + } + it.mu.Lock() + err := it.err + it.mu.Unlock() + if err != nil { + return nil, err + } + // Receive messages from stream. This may block indefinitely. + res, err := it.ps.Recv() + // The pullStream handles retries, so any error here is fatal. + if err != nil { + it.fail(err) + return nil, err + } + msgs, err := convertMessages(res.ReceivedMessages) + if err != nil { + it.fail(err) + return nil, err + } + // We received some messages. Remember them so we can keep them alive. Also, + // do a receipt mod-ack. + maxExt := time.Now().Add(it.po.maxExtension) + ackIDs := map[string]bool{} + it.mu.Lock() + now := time.Now() + for _, m := range msgs { + m.receiveTime = now + addRecv(m.ID, m.ackID, now) + m.doneFunc = it.done + it.keepAliveDeadlines[m.ackID] = maxExt + // The receipt mod-ack uses the subscription's configured ack deadline. Don't + // change the mod-ack if the message is going to be nacked. This is possible + // if there are retries. + if !it.pendingNacks[m.ackID] { + ackIDs[m.ackID] = true + } + } + it.mu.Unlock() + if !it.sendModAck(ackIDs, trunc32(int64(it.po.ackDeadline.Seconds()))) { + return nil, it.err + } + return msgs, nil +} + +// sender runs in a goroutine and handles all sends to the stream. +func (it *streamingMessageIterator) sender() { + defer it.wg.Done() + defer it.kaTicker.Stop() + defer it.ackTicker.Stop() + defer it.nackTicker.Stop() + defer it.pingTicker.Stop() + defer it.ps.CloseSend() + + done := false + for !done { + sendAcks := false + sendNacks := false + sendModAcks := false + sendPing := false + select { + case <-it.ctx.Done(): + // Context canceled or timed out: stop immediately, without + // another RPC. + return + + case <-it.failed: + // Stream failed: nothing to do, so stop immediately. + return + + case <-it.drained: + // All outstanding messages have been marked done: + // nothing left to do except make the final calls. + it.mu.Lock() + sendAcks = (len(it.pendingAcks) > 0) + sendNacks = (len(it.pendingNacks) > 0) + // No point in sending modacks. + done = true + + case <-it.kaTicker.C: + it.mu.Lock() + it.handleKeepAlives() + sendModAcks = (len(it.pendingModAcks) > 0) + + case <-it.nackTicker.C: + it.mu.Lock() + sendNacks = (len(it.pendingNacks) > 0) + + case <-it.ackTicker.C: + it.mu.Lock() + sendAcks = (len(it.pendingAcks) > 0) + + case <-it.pingTicker.C: + it.mu.Lock() + // Ping only if we are processing messages. + sendPing = (len(it.keepAliveDeadlines) > 0) + } + // Lock is held here. + var acks, nacks, modAcks map[string]bool + if sendAcks { + acks = it.pendingAcks + it.pendingAcks = map[string]bool{} + } + if sendNacks { + nacks = it.pendingNacks + it.pendingNacks = map[string]bool{} + } + if sendModAcks { + modAcks = it.pendingModAcks + it.pendingModAcks = map[string]bool{} + } + it.mu.Unlock() + // Make Ack and ModAck RPCs. + if sendAcks { + if !it.sendAck(acks) { + return + } + } + if sendNacks { + // Nack indicated by modifying the deadline to zero. + if !it.sendModAck(nacks, 0) { + return + } + } + if sendModAcks { + if !it.sendModAck(modAcks, trunc32(int64(it.po.ackDeadline.Seconds()))) { + return + } + } + if sendPing { + it.pingStream() + } + } +} + +// handleKeepAlives modifies the pending request to include deadline extensions +// for live messages. It also purges expired messages. +// +// Called with the lock held. +func (it *streamingMessageIterator) handleKeepAlives() { + now := time.Now() + for id, expiry := range it.keepAliveDeadlines { + if expiry.Before(now) { + // This delete will not result in skipping any map items, as implied by + // the spec at https://golang.org/ref/spec#For_statements, "For + // statements with range clause", note 3, and stated explicitly at + // https://groups.google.com/forum/#!msg/golang-nuts/UciASUb03Js/pzSq5iVFAQAJ. + delete(it.keepAliveDeadlines, id) + } else { + // This will not conflict with a nack, because nacking removes the ID from keepAliveDeadlines. + it.pendingModAcks[id] = true + } + } + it.checkDrained() +} + +func (it *streamingMessageIterator) sendAck(m map[string]bool) bool { + return it.sendAckIDRPC(m, func(ids []string) error { + addAcks(ids) + return it.subc.Acknowledge(it.ctx, &pb.AcknowledgeRequest{ + Subscription: it.subName, + AckIds: ids, + }) + }) +} + +func (it *streamingMessageIterator) sendModAck(m map[string]bool, deadlineSecs int32) bool { + return it.sendAckIDRPC(m, func(ids []string) error { + addModAcks(ids, deadlineSecs) + return it.subc.ModifyAckDeadline(it.ctx, &pb.ModifyAckDeadlineRequest{ + Subscription: it.subName, + AckDeadlineSeconds: deadlineSecs, + AckIds: ids, + }) + }) +} + +func (it *streamingMessageIterator) sendAckIDRPC(ackIDSet map[string]bool, call func([]string) error) bool { + ackIDs := make([]string, 0, len(ackIDSet)) + for k := range ackIDSet { + ackIDs = append(ackIDs, k) + } + var toSend []string + for len(ackIDs) > 0 { + toSend, ackIDs = splitRequestIDs(ackIDs, maxPayload) + if err := call(toSend); err != nil { + // The underlying client handles retries, so any error is fatal to the + // iterator. + it.fail(err) + return false + } + } + return true +} + +// Send a message to the stream to keep it open. The stream will close if there's no +// traffic on it for a while. By keeping it open, we delay the start of the +// expiration timer on messages that are buffered by gRPC or elsewhere in the +// network. This matters if it takes a long time to process messages relative to the +// default ack deadline, and if the messages are small enough so that many can fit +// into the buffer. +func (it *streamingMessageIterator) pingStream() { + // Ignore error; if the stream is broken, this doesn't matter anyway. + _ = it.ps.Send(&pb.StreamingPullRequest{}) +} + +func splitRequestIDs(ids []string, maxSize int) (prefix, remainder []string) { + size := reqFixedOverhead + i := 0 + for size < maxSize && i < len(ids) { + size += overheadPerID + len(ids[i]) + i++ + } + if size > maxSize { + i-- + } + return ids[:i], ids[i:] +} diff --git a/vendor/cloud.google.com/go/pubsub/iterator_test.go b/vendor/cloud.google.com/go/pubsub/iterator_test.go new file mode 100644 index 0000000000..24d01f3da5 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/iterator_test.go @@ -0,0 +1,43 @@ +// Copyright 2017 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 pubsub + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" +) + +func TestSplitRequestIDs(t *testing.T) { + t.Parallel() + ids := []string{"aaaa", "bbbb", "cccc", "dddd", "eeee"} + for _, test := range []struct { + ids []string + splitIndex int + }{ + {[]string{}, 0}, + {ids, 2}, + {ids[:2], 2}, + } { + got1, got2 := splitRequestIDs(test.ids, reqFixedOverhead+20) + want1, want2 := test.ids[:test.splitIndex], test.ids[test.splitIndex:] + if !testutil.Equal(got1, want1) { + t.Errorf("%v, 1: got %v, want %v", test, got1, want1) + } + if !testutil.Equal(got2, want2) { + t.Errorf("%v, 2: got %v, want %v", test, got2, want2) + } + } +} diff --git a/vendor/cloud.google.com/go/pubsub/loadtest/benchmark_test.go b/vendor/cloud.google.com/go/pubsub/loadtest/benchmark_test.go new file mode 100644 index 0000000000..dfcecad2d9 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/loadtest/benchmark_test.go @@ -0,0 +1,176 @@ +// Copyright 2017 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 loadtest + +// Performance benchmarks for pubsub. +// Run with +// go test -bench . -cpu 1 + +import ( + "log" + "sync" + "sync/atomic" + "testing" + "time" + + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/pubsub" + gtransport "google.golang.org/api/transport/grpc" + pb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +// These constants are designed to match the "throughput" test in +// https://github.com/GoogleCloudPlatform/pubsub/blob/master/load-test-framework/run.py +// and +// https://github.com/GoogleCloudPlatform/pubsub/blob/master/load-test-framework/src/main/java/com/google/pubsub/clients/experimental/CPSPublisherTask.java + +const ( + nMessages = 1e5 + messageSize = 10000 // size of msg data in bytes + batchSize = 10 + batchDuration = 50 * time.Millisecond + serverDelay = 200 * time.Millisecond + maxOutstandingPublishes = 1600 // max_outstanding_messages in run.py +) + +func BenchmarkPublishThroughput(b *testing.B) { + b.SetBytes(nMessages * messageSize) + client := perfClient(serverDelay, 1, b) + + lts := &PubServer{ID: "xxx"} + lts.init(client, "t", messageSize, batchSize, batchDuration) + b.ResetTimer() + for i := 0; i < b.N; i++ { + runOnce(lts) + } +} + +func runOnce(lts *PubServer) { + nRequests := int64(nMessages / batchSize) + var nPublished int64 + var wg sync.WaitGroup + // The Java loadtest framework is rate-limited to 1 billion Execute calls a + // second (each Execute call corresponding to a publishBatch call here), + // but we can ignore this because of the following. + // The framework runs 10,000 threads, each calling Execute in a loop, but + // we can ignore this too. + // The framework caps the number of outstanding calls to Execute at + // maxOutstandingPublishes. That is what we simulate here. + for i := 0; i < maxOutstandingPublishes; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for atomic.AddInt64(&nRequests, -1) >= 0 { + latencies, err := lts.publishBatch() + if err != nil { + log.Fatalf("publishBatch: %v", err) + } + atomic.AddInt64(&nPublished, int64(len(latencies))) + } + }() + } + wg.Wait() + sent := atomic.LoadInt64(&nPublished) + if sent != nMessages { + log.Fatalf("sent %d messages, expected %d", sent, int(nMessages)) + } +} + +func perfClient(pubDelay time.Duration, nConns int, f interface { + Fatal(...interface{}) +}) *pubsub.Client { + ctx := context.Background() + srv, err := newPerfServer(pubDelay) + if err != nil { + f.Fatal(err) + } + conn, err := gtransport.DialInsecure(ctx, + option.WithEndpoint(srv.Addr), + option.WithGRPCConnectionPool(nConns), + + // TODO(grpc/grpc-go#1388) using connection pool without WithBlock + // can cause RPCs to fail randomly. We can delete this after the issue is fixed. + option.WithGRPCDialOption(grpc.WithBlock())) + if err != nil { + f.Fatal(err) + } + client, err := pubsub.NewClient(ctx, "projectID", option.WithGRPCConn(conn)) + if err != nil { + f.Fatal(err) + } + return client +} + +type perfServer struct { + pb.PublisherServer + pb.SubscriberServer + + Addr string + pubDelay time.Duration + + mu sync.Mutex + activePubs int + maxActivePubs int +} + +func newPerfServer(pubDelay time.Duration) (*perfServer, error) { + srv, err := testutil.NewServer(grpc.MaxMsgSize(pubsub.MaxPublishRequestBytes)) + if err != nil { + return nil, err + } + perf := &perfServer{Addr: srv.Addr, pubDelay: pubDelay} + pb.RegisterPublisherServer(srv.Gsrv, perf) + pb.RegisterSubscriberServer(srv.Gsrv, perf) + srv.Start() + return perf, nil +} + +var doLog = false + +func (p *perfServer) incActivePubs(n int) (int, bool) { + p.mu.Lock() + defer p.mu.Unlock() + p.activePubs += n + newMax := false + if p.activePubs > p.maxActivePubs { + p.maxActivePubs = p.activePubs + newMax = true + } + return p.activePubs, newMax +} + +func (p *perfServer) Publish(ctx context.Context, req *pb.PublishRequest) (*pb.PublishResponse, error) { + a, newMax := p.incActivePubs(1) + defer p.incActivePubs(-1) + if newMax && doLog { + log.Printf("max %d active publish calls", a) + } + if doLog { + log.Printf("%p -> Publish %d", p, len(req.Messages)) + } + res := &pb.PublishResponse{MessageIds: make([]string, len(req.Messages))} + for i := range res.MessageIds { + res.MessageIds[i] = "x" + } + time.Sleep(p.pubDelay) + if doLog { + log.Printf("%p <- Publish %d", p, len(req.Messages)) + } + return res, nil +} diff --git a/vendor/cloud.google.com/go/pubsub/loadtest/cmd/loadtest.go b/vendor/cloud.google.com/go/pubsub/loadtest/cmd/loadtest.go new file mode 100644 index 0000000000..4c08982e23 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/loadtest/cmd/loadtest.go @@ -0,0 +1,54 @@ +// Copyright 2017 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 main + +import ( + "flag" + "fmt" + "log" + "net" + "strconv" + + "math/rand" + + "cloud.google.com/go/pubsub/loadtest" + pb "cloud.google.com/go/pubsub/loadtest/pb" + "google.golang.org/grpc" +) + +func main() { + port := flag.Uint("worker_port", 6000, "port to bind worker to") + role := flag.String("r", "", "role: pub/sub") + flag.Parse() + + var lts pb.LoadtestWorkerServer + switch *role { + case "pub": + lts = &loadtest.PubServer{ID: strconv.Itoa(rand.Int())} + case "sub": + lts = &loadtest.SubServer{} + default: + log.Fatalf("unknown role: %q", *role) + } + + serv := grpc.NewServer() + pb.RegisterLoadtestWorkerServer(serv, lts) + + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + serv.Serve(lis) +} diff --git a/vendor/cloud.google.com/go/pubsub/loadtest/loadtest.go b/vendor/cloud.google.com/go/pubsub/loadtest/loadtest.go new file mode 100644 index 0000000000..380e919e8e --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/loadtest/loadtest.go @@ -0,0 +1,215 @@ +// Copyright 2017 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 loadtest implements load testing for pubsub, +// following the interface defined in https://github.com/GoogleCloudPlatform/pubsub/tree/master/load-test-framework/ . +// +// This package is experimental. +package loadtest + +import ( + "bytes" + "errors" + "log" + "runtime" + "strconv" + "sync" + "sync/atomic" + "time" + + "golang.org/x/net/context" + "golang.org/x/time/rate" + + "github.com/golang/protobuf/ptypes" + + "cloud.google.com/go/pubsub" + pb "cloud.google.com/go/pubsub/loadtest/pb" +) + +type pubServerConfig struct { + topic *pubsub.Topic + msgData []byte + batchSize int32 +} + +type PubServer struct { + ID string + + cfg atomic.Value + seqNum int32 +} + +func (l *PubServer) Start(ctx context.Context, req *pb.StartRequest) (*pb.StartResponse, error) { + log.Println("received start") + c, err := pubsub.NewClient(ctx, req.Project) + if err != nil { + return nil, err + } + dur, err := ptypes.Duration(req.PublishBatchDuration) + if err != nil { + return nil, err + } + l.init(c, req.Topic, req.MessageSize, req.PublishBatchSize, dur) + log.Println("started") + return &pb.StartResponse{}, nil +} + +func (l *PubServer) init(c *pubsub.Client, topicName string, msgSize, batchSize int32, batchDur time.Duration) { + topic := c.Topic(topicName) + topic.PublishSettings = pubsub.PublishSettings{ + DelayThreshold: batchDur, + CountThreshold: 950, + ByteThreshold: 9500000, + } + + l.cfg.Store(pubServerConfig{ + topic: topic, + msgData: bytes.Repeat([]byte{'A'}, int(msgSize)), + batchSize: batchSize, + }) +} + +func (l *PubServer) Execute(ctx context.Context, _ *pb.ExecuteRequest) (*pb.ExecuteResponse, error) { + latencies, err := l.publishBatch() + if err != nil { + log.Printf("error: %v", err) + return nil, err + } + return &pb.ExecuteResponse{Latencies: latencies}, nil +} + +func (l *PubServer) publishBatch() ([]int64, error) { + var cfg pubServerConfig + if c, ok := l.cfg.Load().(pubServerConfig); ok { + cfg = c + } else { + return nil, errors.New("config not loaded") + } + + start := time.Now() + latencies := make([]int64, cfg.batchSize) + startStr := strconv.FormatInt(start.UnixNano()/1e6, 10) + seqNum := atomic.AddInt32(&l.seqNum, cfg.batchSize) - cfg.batchSize + + rs := make([]*pubsub.PublishResult, cfg.batchSize) + for i := int32(0); i < cfg.batchSize; i++ { + rs[i] = cfg.topic.Publish(context.TODO(), &pubsub.Message{ + Data: cfg.msgData, + Attributes: map[string]string{ + "sendTime": startStr, + "clientId": l.ID, + "sequenceNumber": strconv.Itoa(int(seqNum + i)), + }, + }) + } + for i, r := range rs { + _, err := r.Get(context.Background()) + if err != nil { + return nil, err + } + // TODO(jba,pongad): fix latencies + // Later values will be skewed by earlier ones, since we wait for the + // results in order. (On the other hand, it may not matter much, since + // messages are added to bundles in order and bundles get sent more or + // less in order.) If we want more accurate values, we can either start + // a goroutine for each result (similar to the original code using a + // callback), or call reflect.Select with the Ready channels of the + // results. + latencies[i] = time.Since(start).Nanoseconds() / 1e6 + } + return latencies, nil +} + +type SubServer struct { + lim *rate.Limiter + + mu sync.Mutex + idents []*pb.MessageIdentifier + latencies []int64 +} + +func (s *SubServer) Start(ctx context.Context, req *pb.StartRequest) (*pb.StartResponse, error) { + log.Println("received start") + s.lim = rate.NewLimiter(rate.Every(time.Second), 1) + + c, err := pubsub.NewClient(ctx, req.Project) + if err != nil { + return nil, err + } + + // Load test API doesn't define any way to stop right now. + go func() { + sub := c.Subscription(req.GetPubsubOptions().Subscription) + sub.ReceiveSettings.NumGoroutines = 10 * runtime.GOMAXPROCS(0) + err := sub.Receive(context.Background(), s.callback) + log.Fatal(err) + }() + + log.Println("started") + return &pb.StartResponse{}, nil +} + +func (s *SubServer) callback(_ context.Context, m *pubsub.Message) { + id, err := strconv.ParseInt(m.Attributes["clientId"], 10, 64) + if err != nil { + log.Println(err) + m.Nack() + return + } + + seqNum, err := strconv.ParseInt(m.Attributes["sequenceNumber"], 10, 32) + if err != nil { + log.Println(err) + m.Nack() + return + } + + sendTimeMillis, err := strconv.ParseInt(m.Attributes["sendTime"], 10, 64) + if err != nil { + log.Println(err) + m.Nack() + return + } + + latency := time.Now().UnixNano()/1e6 - sendTimeMillis + ident := &pb.MessageIdentifier{ + PublisherClientId: id, + SequenceNumber: int32(seqNum), + } + + s.mu.Lock() + s.idents = append(s.idents, ident) + s.latencies = append(s.latencies, latency) + s.mu.Unlock() + m.Ack() +} + +func (s *SubServer) Execute(ctx context.Context, _ *pb.ExecuteRequest) (*pb.ExecuteResponse, error) { + // Throttle so the load tester doesn't spam us and consume all our CPU. + if err := s.lim.Wait(ctx); err != nil { + return nil, err + } + + s.mu.Lock() + idents := s.idents + s.idents = nil + latencies := s.latencies + s.latencies = nil + s.mu.Unlock() + + return &pb.ExecuteResponse{ + Latencies: latencies, + ReceivedMessages: idents, + }, nil +} diff --git a/vendor/cloud.google.com/go/pubsub/loadtest/pb/loadtest.pb.go b/vendor/cloud.google.com/go/pubsub/loadtest/pb/loadtest.pb.go new file mode 100644 index 0000000000..12ea8f00af --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/loadtest/pb/loadtest.pb.go @@ -0,0 +1,792 @@ +// Code generated by protoc-gen-go. +// source: loadtest.proto +// DO NOT EDIT! + +/* +Package google_pubsub_loadtest is a generated protocol buffer package. + +It is generated from these files: + loadtest.proto + +It has these top-level messages: + StartRequest + StartResponse + PubsubOptions + KafkaOptions + MessageIdentifier + CheckRequest + CheckResponse + ExecuteRequest + ExecuteResponse +*/ +package google_pubsub_loadtest + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/ptypes/duration" +import google_protobuf1 "github.com/golang/protobuf/ptypes/timestamp" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type StartRequest struct { + // The GCP project. This must be set even for Kafka, as we use it to export metrics. + Project string `protobuf:"bytes,1,opt,name=project" json:"project,omitempty"` + // The Pub/Sub or Kafka topic name. + Topic string `protobuf:"bytes,2,opt,name=topic" json:"topic,omitempty"` + // The number of requests that can be made, each second, per client. + RequestRate int32 `protobuf:"varint,3,opt,name=request_rate,json=requestRate" json:"request_rate,omitempty"` + // The size of each user message to publish + MessageSize int32 `protobuf:"varint,4,opt,name=message_size,json=messageSize" json:"message_size,omitempty"` + // The maximum outstanding requests, per client. + MaxOutstandingRequests int32 `protobuf:"varint,5,opt,name=max_outstanding_requests,json=maxOutstandingRequests" json:"max_outstanding_requests,omitempty"` + // The time at which the load test should start. If this is less than the current time, we start immediately. + StartTime *google_protobuf1.Timestamp `protobuf:"bytes,6,opt,name=start_time,json=startTime" json:"start_time,omitempty"` + // The burn-in duration, before which results should not be reported. + BurnInDuration *google_protobuf.Duration `protobuf:"bytes,12,opt,name=burn_in_duration,json=burnInDuration" json:"burn_in_duration,omitempty"` + // The number of user messages of size message_size to publish together. + PublishBatchSize int32 `protobuf:"varint,11,opt,name=publish_batch_size,json=publishBatchSize" json:"publish_batch_size,omitempty"` + // The max duration for coalescing a batch of published messages. + PublishBatchDuration *google_protobuf.Duration `protobuf:"bytes,13,opt,name=publish_batch_duration,json=publishBatchDuration" json:"publish_batch_duration,omitempty"` + // Types that are valid to be assigned to StopConditions: + // *StartRequest_TestDuration + // *StartRequest_NumberOfMessages + StopConditions isStartRequest_StopConditions `protobuf_oneof:"stop_conditions"` + // Types that are valid to be assigned to Options: + // *StartRequest_PubsubOptions + // *StartRequest_KafkaOptions + Options isStartRequest_Options `protobuf_oneof:"options"` +} + +func (m *StartRequest) Reset() { *m = StartRequest{} } +func (m *StartRequest) String() string { return proto.CompactTextString(m) } +func (*StartRequest) ProtoMessage() {} +func (*StartRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type isStartRequest_StopConditions interface { + isStartRequest_StopConditions() +} +type isStartRequest_Options interface { + isStartRequest_Options() +} + +type StartRequest_TestDuration struct { + TestDuration *google_protobuf.Duration `protobuf:"bytes,7,opt,name=test_duration,json=testDuration,oneof"` +} +type StartRequest_NumberOfMessages struct { + NumberOfMessages int32 `protobuf:"varint,8,opt,name=number_of_messages,json=numberOfMessages,oneof"` +} +type StartRequest_PubsubOptions struct { + PubsubOptions *PubsubOptions `protobuf:"bytes,9,opt,name=pubsub_options,json=pubsubOptions,oneof"` +} +type StartRequest_KafkaOptions struct { + KafkaOptions *KafkaOptions `protobuf:"bytes,10,opt,name=kafka_options,json=kafkaOptions,oneof"` +} + +func (*StartRequest_TestDuration) isStartRequest_StopConditions() {} +func (*StartRequest_NumberOfMessages) isStartRequest_StopConditions() {} +func (*StartRequest_PubsubOptions) isStartRequest_Options() {} +func (*StartRequest_KafkaOptions) isStartRequest_Options() {} + +func (m *StartRequest) GetStopConditions() isStartRequest_StopConditions { + if m != nil { + return m.StopConditions + } + return nil +} +func (m *StartRequest) GetOptions() isStartRequest_Options { + if m != nil { + return m.Options + } + return nil +} + +func (m *StartRequest) GetProject() string { + if m != nil { + return m.Project + } + return "" +} + +func (m *StartRequest) GetTopic() string { + if m != nil { + return m.Topic + } + return "" +} + +func (m *StartRequest) GetRequestRate() int32 { + if m != nil { + return m.RequestRate + } + return 0 +} + +func (m *StartRequest) GetMessageSize() int32 { + if m != nil { + return m.MessageSize + } + return 0 +} + +func (m *StartRequest) GetMaxOutstandingRequests() int32 { + if m != nil { + return m.MaxOutstandingRequests + } + return 0 +} + +func (m *StartRequest) GetStartTime() *google_protobuf1.Timestamp { + if m != nil { + return m.StartTime + } + return nil +} + +func (m *StartRequest) GetBurnInDuration() *google_protobuf.Duration { + if m != nil { + return m.BurnInDuration + } + return nil +} + +func (m *StartRequest) GetPublishBatchSize() int32 { + if m != nil { + return m.PublishBatchSize + } + return 0 +} + +func (m *StartRequest) GetPublishBatchDuration() *google_protobuf.Duration { + if m != nil { + return m.PublishBatchDuration + } + return nil +} + +func (m *StartRequest) GetTestDuration() *google_protobuf.Duration { + if x, ok := m.GetStopConditions().(*StartRequest_TestDuration); ok { + return x.TestDuration + } + return nil +} + +func (m *StartRequest) GetNumberOfMessages() int32 { + if x, ok := m.GetStopConditions().(*StartRequest_NumberOfMessages); ok { + return x.NumberOfMessages + } + return 0 +} + +func (m *StartRequest) GetPubsubOptions() *PubsubOptions { + if x, ok := m.GetOptions().(*StartRequest_PubsubOptions); ok { + return x.PubsubOptions + } + return nil +} + +func (m *StartRequest) GetKafkaOptions() *KafkaOptions { + if x, ok := m.GetOptions().(*StartRequest_KafkaOptions); ok { + return x.KafkaOptions + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*StartRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _StartRequest_OneofMarshaler, _StartRequest_OneofUnmarshaler, _StartRequest_OneofSizer, []interface{}{ + (*StartRequest_TestDuration)(nil), + (*StartRequest_NumberOfMessages)(nil), + (*StartRequest_PubsubOptions)(nil), + (*StartRequest_KafkaOptions)(nil), + } +} + +func _StartRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*StartRequest) + // stop_conditions + switch x := m.StopConditions.(type) { + case *StartRequest_TestDuration: + b.EncodeVarint(7<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.TestDuration); err != nil { + return err + } + case *StartRequest_NumberOfMessages: + b.EncodeVarint(8<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.NumberOfMessages)) + case nil: + default: + return fmt.Errorf("StartRequest.StopConditions has unexpected type %T", x) + } + // options + switch x := m.Options.(type) { + case *StartRequest_PubsubOptions: + b.EncodeVarint(9<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.PubsubOptions); err != nil { + return err + } + case *StartRequest_KafkaOptions: + b.EncodeVarint(10<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.KafkaOptions); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("StartRequest.Options has unexpected type %T", x) + } + return nil +} + +func _StartRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*StartRequest) + switch tag { + case 7: // stop_conditions.test_duration + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(google_protobuf.Duration) + err := b.DecodeMessage(msg) + m.StopConditions = &StartRequest_TestDuration{msg} + return true, err + case 8: // stop_conditions.number_of_messages + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.StopConditions = &StartRequest_NumberOfMessages{int32(x)} + return true, err + case 9: // options.pubsub_options + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(PubsubOptions) + err := b.DecodeMessage(msg) + m.Options = &StartRequest_PubsubOptions{msg} + return true, err + case 10: // options.kafka_options + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(KafkaOptions) + err := b.DecodeMessage(msg) + m.Options = &StartRequest_KafkaOptions{msg} + return true, err + default: + return false, nil + } +} + +func _StartRequest_OneofSizer(msg proto.Message) (n int) { + m := msg.(*StartRequest) + // stop_conditions + switch x := m.StopConditions.(type) { + case *StartRequest_TestDuration: + s := proto.Size(x.TestDuration) + n += proto.SizeVarint(7<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *StartRequest_NumberOfMessages: + n += proto.SizeVarint(8<<3 | proto.WireVarint) + n += proto.SizeVarint(uint64(x.NumberOfMessages)) + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + // options + switch x := m.Options.(type) { + case *StartRequest_PubsubOptions: + s := proto.Size(x.PubsubOptions) + n += proto.SizeVarint(9<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *StartRequest_KafkaOptions: + s := proto.Size(x.KafkaOptions) + n += proto.SizeVarint(10<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type StartResponse struct { +} + +func (m *StartResponse) Reset() { *m = StartResponse{} } +func (m *StartResponse) String() string { return proto.CompactTextString(m) } +func (*StartResponse) ProtoMessage() {} +func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type PubsubOptions struct { + // The Cloud Pub/Sub subscription name + Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` + // The maximum number of messages to pull which each request. + MaxMessagesPerPull int32 `protobuf:"varint,2,opt,name=max_messages_per_pull,json=maxMessagesPerPull" json:"max_messages_per_pull,omitempty"` +} + +func (m *PubsubOptions) Reset() { *m = PubsubOptions{} } +func (m *PubsubOptions) String() string { return proto.CompactTextString(m) } +func (*PubsubOptions) ProtoMessage() {} +func (*PubsubOptions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *PubsubOptions) GetSubscription() string { + if m != nil { + return m.Subscription + } + return "" +} + +func (m *PubsubOptions) GetMaxMessagesPerPull() int32 { + if m != nil { + return m.MaxMessagesPerPull + } + return 0 +} + +type KafkaOptions struct { + // The network address of the Kafka broker. + Broker string `protobuf:"bytes,1,opt,name=broker" json:"broker,omitempty"` + // The length of time to poll for. + PollDuration *google_protobuf.Duration `protobuf:"bytes,2,opt,name=poll_duration,json=pollDuration" json:"poll_duration,omitempty"` +} + +func (m *KafkaOptions) Reset() { *m = KafkaOptions{} } +func (m *KafkaOptions) String() string { return proto.CompactTextString(m) } +func (*KafkaOptions) ProtoMessage() {} +func (*KafkaOptions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *KafkaOptions) GetBroker() string { + if m != nil { + return m.Broker + } + return "" +} + +func (m *KafkaOptions) GetPollDuration() *google_protobuf.Duration { + if m != nil { + return m.PollDuration + } + return nil +} + +type MessageIdentifier struct { + // The unique id of the client that published the message. + PublisherClientId int64 `protobuf:"varint,1,opt,name=publisher_client_id,json=publisherClientId" json:"publisher_client_id,omitempty"` + // Sequence number of the published message with the given publish_client_id. + SequenceNumber int32 `protobuf:"varint,2,opt,name=sequence_number,json=sequenceNumber" json:"sequence_number,omitempty"` +} + +func (m *MessageIdentifier) Reset() { *m = MessageIdentifier{} } +func (m *MessageIdentifier) String() string { return proto.CompactTextString(m) } +func (*MessageIdentifier) ProtoMessage() {} +func (*MessageIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *MessageIdentifier) GetPublisherClientId() int64 { + if m != nil { + return m.PublisherClientId + } + return 0 +} + +func (m *MessageIdentifier) GetSequenceNumber() int32 { + if m != nil { + return m.SequenceNumber + } + return 0 +} + +type CheckRequest struct { + // Duplicate messages that should not be reported for throughput and latency. + Duplicates []*MessageIdentifier `protobuf:"bytes,1,rep,name=duplicates" json:"duplicates,omitempty"` +} + +func (m *CheckRequest) Reset() { *m = CheckRequest{} } +func (m *CheckRequest) String() string { return proto.CompactTextString(m) } +func (*CheckRequest) ProtoMessage() {} +func (*CheckRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *CheckRequest) GetDuplicates() []*MessageIdentifier { + if m != nil { + return m.Duplicates + } + return nil +} + +type CheckResponse struct { + // Histogram of latencies, each one a delta from the previous CheckResponse sent. + BucketValues []int64 `protobuf:"varint,1,rep,packed,name=bucket_values,json=bucketValues" json:"bucket_values,omitempty"` + // The duration from the start of the loadtest to its completion or now if is_finished is false. + RunningDuration *google_protobuf.Duration `protobuf:"bytes,2,opt,name=running_duration,json=runningDuration" json:"running_duration,omitempty"` + // True if the load test has finished running. + IsFinished bool `protobuf:"varint,3,opt,name=is_finished,json=isFinished" json:"is_finished,omitempty"` + // MessageIdentifiers of all received messages since the last Check + ReceivedMessages []*MessageIdentifier `protobuf:"bytes,4,rep,name=received_messages,json=receivedMessages" json:"received_messages,omitempty"` +} + +func (m *CheckResponse) Reset() { *m = CheckResponse{} } +func (m *CheckResponse) String() string { return proto.CompactTextString(m) } +func (*CheckResponse) ProtoMessage() {} +func (*CheckResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *CheckResponse) GetBucketValues() []int64 { + if m != nil { + return m.BucketValues + } + return nil +} + +func (m *CheckResponse) GetRunningDuration() *google_protobuf.Duration { + if m != nil { + return m.RunningDuration + } + return nil +} + +func (m *CheckResponse) GetIsFinished() bool { + if m != nil { + return m.IsFinished + } + return false +} + +func (m *CheckResponse) GetReceivedMessages() []*MessageIdentifier { + if m != nil { + return m.ReceivedMessages + } + return nil +} + +type ExecuteRequest struct { +} + +func (m *ExecuteRequest) Reset() { *m = ExecuteRequest{} } +func (m *ExecuteRequest) String() string { return proto.CompactTextString(m) } +func (*ExecuteRequest) ProtoMessage() {} +func (*ExecuteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +type ExecuteResponse struct { + // Latencies of the completed operations + Latencies []int64 `protobuf:"varint,1,rep,packed,name=latencies" json:"latencies,omitempty"` + // MessageIdentifiers of all received messages since the last Execute + ReceivedMessages []*MessageIdentifier `protobuf:"bytes,2,rep,name=received_messages,json=receivedMessages" json:"received_messages,omitempty"` +} + +func (m *ExecuteResponse) Reset() { *m = ExecuteResponse{} } +func (m *ExecuteResponse) String() string { return proto.CompactTextString(m) } +func (*ExecuteResponse) ProtoMessage() {} +func (*ExecuteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *ExecuteResponse) GetLatencies() []int64 { + if m != nil { + return m.Latencies + } + return nil +} + +func (m *ExecuteResponse) GetReceivedMessages() []*MessageIdentifier { + if m != nil { + return m.ReceivedMessages + } + return nil +} + +func init() { + proto.RegisterType((*StartRequest)(nil), "google.pubsub.loadtest.StartRequest") + proto.RegisterType((*StartResponse)(nil), "google.pubsub.loadtest.StartResponse") + proto.RegisterType((*PubsubOptions)(nil), "google.pubsub.loadtest.PubsubOptions") + proto.RegisterType((*KafkaOptions)(nil), "google.pubsub.loadtest.KafkaOptions") + proto.RegisterType((*MessageIdentifier)(nil), "google.pubsub.loadtest.MessageIdentifier") + proto.RegisterType((*CheckRequest)(nil), "google.pubsub.loadtest.CheckRequest") + proto.RegisterType((*CheckResponse)(nil), "google.pubsub.loadtest.CheckResponse") + proto.RegisterType((*ExecuteRequest)(nil), "google.pubsub.loadtest.ExecuteRequest") + proto.RegisterType((*ExecuteResponse)(nil), "google.pubsub.loadtest.ExecuteResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Loadtest service + +type LoadtestClient interface { + // Starts a load test + Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) + // Checks the status of a load test + Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*CheckResponse, error) +} + +type loadtestClient struct { + cc *grpc.ClientConn +} + +func NewLoadtestClient(cc *grpc.ClientConn) LoadtestClient { + return &loadtestClient{cc} +} + +func (c *loadtestClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) { + out := new(StartResponse) + err := grpc.Invoke(ctx, "/google.pubsub.loadtest.Loadtest/Start", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *loadtestClient) Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*CheckResponse, error) { + out := new(CheckResponse) + err := grpc.Invoke(ctx, "/google.pubsub.loadtest.Loadtest/Check", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Loadtest service + +type LoadtestServer interface { + // Starts a load test + Start(context.Context, *StartRequest) (*StartResponse, error) + // Checks the status of a load test + Check(context.Context, *CheckRequest) (*CheckResponse, error) +} + +func RegisterLoadtestServer(s *grpc.Server, srv LoadtestServer) { + s.RegisterService(&_Loadtest_serviceDesc, srv) +} + +func _Loadtest_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoadtestServer).Start(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/google.pubsub.loadtest.Loadtest/Start", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoadtestServer).Start(ctx, req.(*StartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Loadtest_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoadtestServer).Check(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/google.pubsub.loadtest.Loadtest/Check", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoadtestServer).Check(ctx, req.(*CheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Loadtest_serviceDesc = grpc.ServiceDesc{ + ServiceName: "google.pubsub.loadtest.Loadtest", + HandlerType: (*LoadtestServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Start", + Handler: _Loadtest_Start_Handler, + }, + { + MethodName: "Check", + Handler: _Loadtest_Check_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "loadtest.proto", +} + +// Client API for LoadtestWorker service + +type LoadtestWorkerClient interface { + // Starts a worker + Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) + // Executes a command on the worker, returning the latencies of the operations. Since some + // commands consist of multiple operations (i.e. pulls contain many received messages with + // different end to end latencies) a single command can have multiple latencies returned. + Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) +} + +type loadtestWorkerClient struct { + cc *grpc.ClientConn +} + +func NewLoadtestWorkerClient(cc *grpc.ClientConn) LoadtestWorkerClient { + return &loadtestWorkerClient{cc} +} + +func (c *loadtestWorkerClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) { + out := new(StartResponse) + err := grpc.Invoke(ctx, "/google.pubsub.loadtest.LoadtestWorker/Start", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *loadtestWorkerClient) Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*ExecuteResponse, error) { + out := new(ExecuteResponse) + err := grpc.Invoke(ctx, "/google.pubsub.loadtest.LoadtestWorker/Execute", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for LoadtestWorker service + +type LoadtestWorkerServer interface { + // Starts a worker + Start(context.Context, *StartRequest) (*StartResponse, error) + // Executes a command on the worker, returning the latencies of the operations. Since some + // commands consist of multiple operations (i.e. pulls contain many received messages with + // different end to end latencies) a single command can have multiple latencies returned. + Execute(context.Context, *ExecuteRequest) (*ExecuteResponse, error) +} + +func RegisterLoadtestWorkerServer(s *grpc.Server, srv LoadtestWorkerServer) { + s.RegisterService(&_LoadtestWorker_serviceDesc, srv) +} + +func _LoadtestWorker_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoadtestWorkerServer).Start(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/google.pubsub.loadtest.LoadtestWorker/Start", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoadtestWorkerServer).Start(ctx, req.(*StartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _LoadtestWorker_Execute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExecuteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoadtestWorkerServer).Execute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/google.pubsub.loadtest.LoadtestWorker/Execute", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoadtestWorkerServer).Execute(ctx, req.(*ExecuteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _LoadtestWorker_serviceDesc = grpc.ServiceDesc{ + ServiceName: "google.pubsub.loadtest.LoadtestWorker", + HandlerType: (*LoadtestWorkerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Start", + Handler: _LoadtestWorker_Start_Handler, + }, + { + MethodName: "Execute", + Handler: _LoadtestWorker_Execute_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "loadtest.proto", +} + +func init() { proto.RegisterFile("loadtest.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 847 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdd, 0x6e, 0xdc, 0x44, + 0x14, 0xae, 0x93, 0x6e, 0x92, 0x3d, 0x6b, 0xef, 0x6e, 0x86, 0x12, 0x99, 0x15, 0xd0, 0x60, 0x28, + 0x0d, 0x12, 0x72, 0x45, 0xb8, 0x81, 0x1b, 0x84, 0x92, 0x82, 0x12, 0x15, 0x9a, 0xc8, 0x8d, 0x8a, + 0xe0, 0x66, 0x34, 0xb6, 0x67, 0x93, 0x61, 0xed, 0x19, 0x33, 0x3f, 0x55, 0xd4, 0x17, 0xe0, 0x8d, + 0x78, 0x00, 0x1e, 0x87, 0x5b, 0x5e, 0x00, 0xcd, 0x78, 0xbc, 0x3f, 0x6d, 0x57, 0x0b, 0x42, 0xbd, + 0x3c, 0xdf, 0xf9, 0xce, 0x37, 0xe7, 0xd7, 0x86, 0x61, 0x25, 0x48, 0xa9, 0xa9, 0xd2, 0x69, 0x23, + 0x85, 0x16, 0xe8, 0xe0, 0x5a, 0x88, 0xeb, 0x8a, 0xa6, 0x8d, 0xc9, 0x95, 0xc9, 0xd3, 0xce, 0x3b, + 0xf9, 0xb0, 0xc5, 0x1f, 0x39, 0x56, 0x6e, 0xa6, 0x8f, 0x4a, 0x23, 0x89, 0x66, 0x82, 0xb7, 0x71, + 0x93, 0xfb, 0xaf, 0xfa, 0x35, 0xab, 0xa9, 0xd2, 0xa4, 0x6e, 0x5a, 0x42, 0xf2, 0x57, 0x0f, 0xc2, + 0x67, 0x9a, 0x48, 0x9d, 0xd1, 0xdf, 0x0c, 0x55, 0x1a, 0xc5, 0xb0, 0xdb, 0x48, 0xf1, 0x2b, 0x2d, + 0x74, 0x1c, 0x1c, 0x06, 0x47, 0xfd, 0xac, 0x33, 0xd1, 0x3d, 0xe8, 0x69, 0xd1, 0xb0, 0x22, 0xde, + 0x72, 0x78, 0x6b, 0xa0, 0x8f, 0x20, 0x94, 0x6d, 0x28, 0x96, 0x44, 0xd3, 0x78, 0xfb, 0x30, 0x38, + 0xea, 0x65, 0x03, 0x8f, 0x65, 0x44, 0x53, 0x4b, 0xa9, 0xa9, 0x52, 0xe4, 0x9a, 0x62, 0xc5, 0x5e, + 0xd2, 0xf8, 0x6e, 0x4b, 0xf1, 0xd8, 0x33, 0xf6, 0x92, 0xa2, 0xaf, 0x20, 0xae, 0xc9, 0x2d, 0x16, + 0x46, 0x2b, 0x4d, 0x78, 0xc9, 0xf8, 0x35, 0xf6, 0x0a, 0x2a, 0xee, 0x39, 0xfa, 0x41, 0x4d, 0x6e, + 0x2f, 0x16, 0x6e, 0x9f, 0xae, 0x42, 0x5f, 0x03, 0x28, 0x9b, 0x3f, 0xb6, 0x95, 0xc5, 0x3b, 0x87, + 0xc1, 0xd1, 0xe0, 0x78, 0x92, 0x76, 0xed, 0xf2, 0x65, 0xa7, 0x57, 0x5d, 0xd9, 0x59, 0xdf, 0xb1, + 0xad, 0x8d, 0x4e, 0x61, 0x9c, 0x1b, 0xc9, 0x31, 0xe3, 0xb8, 0x6b, 0x5b, 0x1c, 0x3a, 0x81, 0xf7, + 0x5e, 0x13, 0x78, 0xec, 0x09, 0xd9, 0xd0, 0x86, 0x9c, 0xf3, 0xce, 0x46, 0x9f, 0x03, 0x6a, 0x4c, + 0x5e, 0x31, 0x75, 0x83, 0x73, 0xa2, 0x8b, 0x9b, 0xb6, 0xc4, 0x81, 0xcb, 0x79, 0xec, 0x3d, 0x27, + 0xd6, 0xe1, 0xea, 0xbc, 0x80, 0x83, 0x55, 0xf6, 0xfc, 0xe1, 0x68, 0xd3, 0xc3, 0xf7, 0x96, 0xc5, + 0xe6, 0xcf, 0x7f, 0x0b, 0x91, 0x5d, 0x84, 0x85, 0xce, 0xee, 0x06, 0x9d, 0xb3, 0x3b, 0x59, 0x68, + 0x23, 0xe6, 0x0a, 0x29, 0x20, 0x6e, 0xea, 0x9c, 0x4a, 0x2c, 0xa6, 0xd8, 0xcf, 0x44, 0xc5, 0x7b, + 0xb6, 0x80, 0xb3, 0x3b, 0xd9, 0xb8, 0xf5, 0x5d, 0x4c, 0x7f, 0xf4, 0x1e, 0xf4, 0x14, 0x86, 0xed, + 0x16, 0x62, 0xd1, 0x58, 0x01, 0x15, 0xf7, 0xdd, 0x93, 0x0f, 0xd2, 0x37, 0xef, 0x68, 0x7a, 0xe9, + 0xec, 0x8b, 0x96, 0x7c, 0x16, 0x64, 0x51, 0xb3, 0x0c, 0xa0, 0x27, 0x10, 0xcd, 0xc8, 0x74, 0x46, + 0xe6, 0x72, 0xe0, 0xe4, 0x3e, 0x59, 0x27, 0xf7, 0xc4, 0x92, 0x17, 0x6a, 0xe1, 0x6c, 0xc9, 0x3e, + 0xd9, 0x87, 0x91, 0xd2, 0xa2, 0xc1, 0x85, 0xe0, 0x25, 0x6b, 0xa1, 0x3e, 0xec, 0x7a, 0xe5, 0x64, + 0x04, 0x91, 0xdf, 0x75, 0xd5, 0x08, 0xae, 0x68, 0x32, 0x85, 0x68, 0x25, 0x3b, 0x94, 0x40, 0xa8, + 0x4c, 0xae, 0x0a, 0xc9, 0x1c, 0xe0, 0x4f, 0x60, 0x05, 0x43, 0x5f, 0xc0, 0xbb, 0x76, 0x57, 0xbb, + 0x56, 0xe1, 0x86, 0x4a, 0xdc, 0x98, 0xaa, 0x72, 0x77, 0xd1, 0xcb, 0x50, 0x4d, 0x6e, 0xbb, 0x66, + 0x5d, 0x52, 0x79, 0x69, 0xaa, 0x2a, 0x99, 0x42, 0xb8, 0x9c, 0x36, 0x3a, 0x80, 0x9d, 0x5c, 0x8a, + 0x19, 0x95, 0xfe, 0x01, 0x6f, 0xa1, 0x6f, 0x20, 0x6a, 0x44, 0x55, 0x2d, 0xa6, 0xb9, 0xb5, 0x69, + 0x2b, 0x42, 0xcb, 0xef, 0xac, 0xa4, 0x82, 0x7d, 0xff, 0xf4, 0x79, 0x49, 0xb9, 0x66, 0x53, 0x46, + 0x25, 0x4a, 0xe1, 0x1d, 0xbf, 0x3a, 0x54, 0xe2, 0xa2, 0x62, 0x94, 0x6b, 0xcc, 0x4a, 0xf7, 0xf2, + 0x76, 0xb6, 0x3f, 0x77, 0x9d, 0x3a, 0xcf, 0x79, 0x89, 0x1e, 0xc2, 0x48, 0xd9, 0xeb, 0xe2, 0x05, + 0xc5, 0xed, 0xf4, 0x7d, 0x65, 0xc3, 0x0e, 0x7e, 0xea, 0xd0, 0xe4, 0x67, 0x08, 0x4f, 0x6f, 0x68, + 0x31, 0xeb, 0x3e, 0x1d, 0xe7, 0x00, 0xa5, 0x69, 0x2a, 0x56, 0x10, 0x4d, 0x55, 0x1c, 0x1c, 0x6e, + 0x1f, 0x0d, 0x8e, 0x3f, 0x5b, 0x37, 0xc6, 0xd7, 0xf2, 0xcc, 0x96, 0x82, 0x93, 0xbf, 0x03, 0x88, + 0xbc, 0x76, 0x3b, 0x2a, 0xf4, 0x31, 0x44, 0xb9, 0x29, 0x66, 0x54, 0xe3, 0x17, 0xa4, 0x32, 0x5e, + 0x7f, 0x3b, 0x0b, 0x5b, 0xf0, 0xb9, 0xc3, 0xd0, 0x63, 0x18, 0x4b, 0xc3, 0xb9, 0xfd, 0x7c, 0xfc, + 0xfb, 0x16, 0x8e, 0x7c, 0xc8, 0xfc, 0x22, 0xee, 0xc3, 0x80, 0x29, 0x3c, 0x65, 0xdc, 0xf6, 0xa5, + 0x74, 0x5f, 0xb4, 0xbd, 0x0c, 0x98, 0xfa, 0xde, 0x23, 0xe8, 0x39, 0xec, 0x4b, 0x5a, 0x50, 0xf6, + 0x82, 0x96, 0x8b, 0x8b, 0xb9, 0xfb, 0x5f, 0xeb, 0x1d, 0x77, 0x1a, 0xdd, 0xb6, 0x24, 0x63, 0x18, + 0x7e, 0x77, 0x4b, 0x0b, 0xa3, 0xa9, 0x6f, 0x69, 0xf2, 0x7b, 0x00, 0xa3, 0x39, 0xe4, 0x3b, 0xf1, + 0x3e, 0xf4, 0x2b, 0xa2, 0x29, 0x2f, 0xd8, 0xbc, 0x0b, 0x0b, 0xe0, 0xcd, 0xb9, 0x6d, 0xfd, 0xef, + 0xdc, 0x8e, 0xff, 0x08, 0x60, 0xef, 0x07, 0x1f, 0x80, 0xae, 0xa0, 0xe7, 0x0e, 0x09, 0xad, 0xbd, + 0xd2, 0xe5, 0x7f, 0xca, 0xe4, 0xc1, 0x06, 0x96, 0x2f, 0xec, 0x0a, 0x7a, 0x6e, 0xe6, 0xeb, 0x55, + 0x97, 0xd7, 0x6d, 0xbd, 0xea, 0xca, 0xe2, 0x1c, 0xff, 0x19, 0xc0, 0xb0, 0x4b, 0xfc, 0x27, 0x21, + 0xed, 0x99, 0xbd, 0x9d, 0xf4, 0x7f, 0x81, 0x5d, 0x3f, 0x2a, 0xf4, 0xe9, 0xba, 0x88, 0xd5, 0xf1, + 0x4e, 0x1e, 0x6e, 0xe4, 0xb5, 0xda, 0x27, 0x29, 0x7c, 0x50, 0x88, 0xfa, 0x15, 0xf6, 0xb4, 0x62, + 0x45, 0x5a, 0x88, 0xba, 0x16, 0xfc, 0x24, 0xea, 0x4a, 0xbc, 0x74, 0xfb, 0xbd, 0xe3, 0xd6, 0xfc, + 0xcb, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xc4, 0xfc, 0xdc, 0x27, 0x48, 0x08, 0x00, 0x00, +} diff --git a/vendor/cloud.google.com/go/pubsub/message.go b/vendor/cloud.google.com/go/pubsub/message.go new file mode 100644 index 0000000000..c4b16e95f1 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/message.go @@ -0,0 +1,100 @@ +// 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 pubsub + +import ( + "time" + + "github.com/golang/protobuf/ptypes" + pb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +// Message represents a Pub/Sub message. +type Message struct { + // ID identifies this message. + // This ID is assigned by the server and is populated for Messages obtained from a subscription. + // This field is read-only. + ID string + + // Data is the actual data in the message. + Data []byte + + // Attributes represents the key-value pairs the current message + // is labelled with. + Attributes map[string]string + + // ackID is the identifier to acknowledge this message. + ackID string + + // The time at which the message was published. + // This is populated by the server for Messages obtained from a subscription. + // This field is read-only. + PublishTime time.Time + + // receiveTime is the time the message was received by the client. + receiveTime time.Time + + // size is the approximate size of the message's data and attributes. + size int + + calledDone bool + + // The done method of the iterator that created this Message. + doneFunc func(string, bool, time.Time) +} + +func toMessage(resp *pb.ReceivedMessage) (*Message, error) { + if resp.Message == nil { + return &Message{ackID: resp.AckId}, nil + } + + pubTime, err := ptypes.Timestamp(resp.Message.PublishTime) + if err != nil { + return nil, err + } + return &Message{ + ackID: resp.AckId, + Data: resp.Message.Data, + Attributes: resp.Message.Attributes, + ID: resp.Message.MessageId, + PublishTime: pubTime, + }, nil +} + +// Ack indicates successful processing of a Message passed to the Subscriber.Receive callback. +// It should not be called on any other Message value. +// If message acknowledgement fails, the Message will be redelivered. +// Client code must call Ack or Nack when finished for each received Message. +// Calls to Ack or Nack have no effect after the first call. +func (m *Message) Ack() { + m.done(true) +} + +// Nack indicates that the client will not or cannot process a Message passed to the Subscriber.Receive callback. +// It should not be called on any other Message value. +// Nack will result in the Message being redelivered more quickly than if it were allowed to expire. +// Client code must call Ack or Nack when finished for each received Message. +// Calls to Ack or Nack have no effect after the first call. +func (m *Message) Nack() { + m.done(false) +} + +func (m *Message) done(ack bool) { + if m.calledDone { + return + } + m.calledDone = true + m.doneFunc(m.ackID, ack, m.receiveTime) +} diff --git a/vendor/cloud.google.com/go/pubsub/mock_test.go b/vendor/cloud.google.com/go/pubsub/mock_test.go new file mode 100644 index 0000000000..d31b3f4b34 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/mock_test.go @@ -0,0 +1,159 @@ +// Copyright 2017 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 pubsub + +// This file provides a mock in-memory pubsub server for streaming pull testing. + +import ( + "io" + "sync" + "time" + + "cloud.google.com/go/internal/testutil" + emptypb "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +type mockServer struct { + pb.SubscriberServer + + Addr string + + mu sync.Mutex + Acked map[string]bool // acked message IDs + Deadlines map[string]int32 // deadlines by message ID + pullResponses []*pullResponse + wg sync.WaitGroup + sub *pb.Subscription +} + +type pullResponse struct { + msgs []*pb.ReceivedMessage + err error +} + +func newMockServer() (*mockServer, error) { + srv, err := testutil.NewServer() + if err != nil { + return nil, err + } + mock := &mockServer{ + Addr: srv.Addr, + Acked: map[string]bool{}, + Deadlines: map[string]int32{}, + sub: &pb.Subscription{ + AckDeadlineSeconds: 10, + PushConfig: &pb.PushConfig{}, + }, + } + pb.RegisterSubscriberServer(srv.Gsrv, mock) + srv.Start() + return mock, nil +} + +// Each call to addStreamingPullMessages results in one StreamingPullResponse. +func (s *mockServer) addStreamingPullMessages(msgs []*pb.ReceivedMessage) { + s.pullResponses = append(s.pullResponses, &pullResponse{msgs, nil}) +} + +func (s *mockServer) addStreamingPullError(err error) { + s.pullResponses = append(s.pullResponses, &pullResponse{nil, err}) +} + +func (s *mockServer) wait() { + s.wg.Wait() +} + +func (s *mockServer) StreamingPull(stream pb.Subscriber_StreamingPullServer) error { + s.wg.Add(1) + defer s.wg.Done() + errc := make(chan error, 1) + s.wg.Add(1) + go func() { + defer s.wg.Done() + for { + req, err := stream.Recv() + if err != nil { + errc <- err + return + } + s.mu.Lock() + for _, id := range req.AckIds { + s.Acked[id] = true + } + for i, id := range req.ModifyDeadlineAckIds { + s.Deadlines[id] = req.ModifyDeadlineSeconds[i] + } + s.mu.Unlock() + } + }() + // Send responses. + for { + s.mu.Lock() + if len(s.pullResponses) == 0 { + s.mu.Unlock() + // Nothing to send, so wait for the client to shut down the stream. + err := <-errc // a real error, or at least EOF + if err == io.EOF { + return nil + } + return err + } + pr := s.pullResponses[0] + s.pullResponses = s.pullResponses[1:] + s.mu.Unlock() + if pr.err != nil { + // Add a slight delay to ensure the server receives any + // messages en route from the client before shutting down the stream. + // This reduces flakiness of tests involving retry. + time.Sleep(200 * time.Millisecond) + } + if pr.err == io.EOF { + return nil + } + if pr.err != nil { + return pr.err + } + // Return any error from Recv. + select { + case err := <-errc: + return err + default: + } + res := &pb.StreamingPullResponse{ReceivedMessages: pr.msgs} + if err := stream.Send(res); err != nil { + return err + } + } +} + +func (s *mockServer) Acknowledge(ctx context.Context, req *pb.AcknowledgeRequest) (*emptypb.Empty, error) { + for _, id := range req.AckIds { + s.Acked[id] = true + } + return &emptypb.Empty{}, nil +} + +func (s *mockServer) ModifyAckDeadline(ctx context.Context, req *pb.ModifyAckDeadlineRequest) (*emptypb.Empty, error) { + for _, id := range req.AckIds { + s.Deadlines[id] = req.AckDeadlineSeconds + } + return &emptypb.Empty{}, nil +} + +func (s *mockServer) GetSubscription(ctx context.Context, req *pb.GetSubscriptionRequest) (*pb.Subscription, error) { + return s.sub, nil +} diff --git a/vendor/cloud.google.com/go/pubsub/nodebug.go b/vendor/cloud.google.com/go/pubsub/nodebug.go new file mode 100644 index 0000000000..774a74a58d --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/nodebug.go @@ -0,0 +1,25 @@ +// 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. + +// +build !psdebug + +package pubsub + +import "time" + +func addRecv(string, string, time.Time) {} + +func addAcks([]string) {} + +func addModAcks([]string, int32) {} diff --git a/vendor/cloud.google.com/go/pubsub/not_go18.go b/vendor/cloud.google.com/go/pubsub/not_go18.go new file mode 100644 index 0000000000..435b3dd35f --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/not_go18.go @@ -0,0 +1,54 @@ +// 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. + +// +build !go1.8 + +package pubsub + +import ( + "golang.org/x/net/context" + "google.golang.org/api/option" +) + +// OpenCensus only supports go 1.8 and higher. + +func openCensusOptions() []option.ClientOption { return nil } + +func withSubscriptionKey(ctx context.Context, _ string) context.Context { + return ctx +} + +type dummy struct{} + +var ( + // Not supported below Go 1.8. + PullCount dummy + // Not supported below Go 1.8. + AckCount dummy + // Not supported below Go 1.8. + NackCount dummy + // Not supported below Go 1.8. + ModAckCount dummy + // Not supported below Go 1.8. + StreamOpenCount dummy + // Not supported below Go 1.8. + StreamRetryCount dummy + // Not supported below Go 1.8. + StreamRequestCount dummy + // Not supported below Go 1.8. + StreamResponseCount dummy +) + +func recordStat(context.Context, dummy, int64) { +} diff --git a/vendor/cloud.google.com/go/pubsub/pstest/examples_test.go b/vendor/cloud.google.com/go/pubsub/pstest/examples_test.go new file mode 100644 index 0000000000..21caf0edd2 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/pstest/examples_test.go @@ -0,0 +1,41 @@ +// 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 pstest_test + +import ( + "cloud.google.com/go/pubsub" + "cloud.google.com/go/pubsub/pstest" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +func ExampleNewServer() { + ctx := context.Background() + // Start a fake server running locally. + srv := pstest.NewServer() + // Connect to the server without using TLS. + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + if err != nil { + // TODO: Handle error. + } + // Use the connection when creating a pubsub client. + client, err := pubsub.NewClient(ctx, "project", option.WithGRPCConn(conn)) + if err != nil { + // TODO: Handle error. + } + defer client.Close() + _ = client // TODO: Use the client. +} diff --git a/vendor/cloud.google.com/go/pubsub/pstest/fake.go b/vendor/cloud.google.com/go/pubsub/pstest/fake.go new file mode 100644 index 0000000000..e1aac6d11c --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/pstest/fake.go @@ -0,0 +1,798 @@ +// Copyright 2017 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 pstest provides a fake Cloud PubSub service for testing. It implements a +// simplified form of the service, suitable for unit tests. It may behave +// differently from the actual service in ways in which the service is +// non-deterministic or unspecified: timing, delivery order, etc. +// +// This package is EXPERIMENTAL and is subject to change without notice. +// +// See the example for usage. +package pstest + +import ( + "fmt" + "io" + "path" + "sort" + "strings" + "sync" + "sync/atomic" + "time" + + "cloud.google.com/go/internal/testutil" + "github.com/golang/protobuf/ptypes" + durpb "github.com/golang/protobuf/ptypes/duration" + emptypb "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// For testing. Note that even though changes to the now variable are atomic, a call +// to the stored function can race with a change to that function. This could be a +// problem if tests are run in parallel, or even if concurrent parts of the same test +// change the value of the variable. +var now atomic.Value + +func init() { + now.Store(time.Now) +} + +func timeNow() time.Time { + return now.Load().(func() time.Time)() +} + +type Server struct { + Addr string // The address that the server is listening on. + gServer gServer +} + +type gServer struct { + pb.PublisherServer + pb.SubscriberServer + + mu sync.Mutex + topics map[string]*topic + subs map[string]*subscription + msgs []*Message // all messages ever published + msgsByID map[string]*Message + wg sync.WaitGroup + nextID int + streamTimeout time.Duration +} + +// NewServer creates a new fake server running in the current process. +func NewServer() *Server { + srv, err := testutil.NewServer() + if err != nil { + panic(fmt.Sprintf("pstest.NewServer: %v", err)) + } + s := &Server{ + Addr: srv.Addr, + gServer: gServer{ + topics: map[string]*topic{}, + subs: map[string]*subscription{}, + msgsByID: map[string]*Message{}, + }, + } + pb.RegisterPublisherServer(srv.Gsrv, &s.gServer) + pb.RegisterSubscriberServer(srv.Gsrv, &s.gServer) + srv.Start() + return s +} + +// Publish behaves as if the Publish RPC was called with a message with the given +// data and attrs. It returns the ID of the message. +// The topic will be created if it doesn't exist. +// +// Publish panics if there is an error, which is appropriate for testing. +func (s *Server) Publish(topic string, data []byte, attrs map[string]string) string { + const topicPattern = "projects/*/topics/*" + ok, err := path.Match(topicPattern, topic) + if err != nil { + panic(err) + } + if !ok { + panic(fmt.Sprintf("topic name must be of the form %q", topicPattern)) + } + _, _ = s.gServer.CreateTopic(nil, &pb.Topic{Name: topic}) + req := &pb.PublishRequest{ + Topic: topic, + Messages: []*pb.PubsubMessage{{Data: data, Attributes: attrs}}, + } + res, err := s.gServer.Publish(nil, req) + if err != nil { + panic(fmt.Sprintf("pstest.Server.Publish: %v", err)) + } + return res.MessageIds[0] +} + +// SetStreamTimeout sets the amount of time a stream will be active before it shuts +// itself down. This mimics the real service's behavior of closing streams after 30 +// minutes. If SetStreamTimeout is never called or is passed zero, streams never shut +// down. +func (s *Server) SetStreamTimeout(d time.Duration) { + s.gServer.mu.Lock() + defer s.gServer.mu.Unlock() + s.gServer.streamTimeout = d +} + +// A Message is a message that was published to the server. +type Message struct { + ID string + Data []byte + Attributes map[string]string + PublishTime time.Time + Deliveries int // number of times delivery of the message was attempted + Acks int // number of acks received from clients + + // protected by server mutex + deliveries int + acks int +} + +// Messages returns information about all messages ever published. +func (s *Server) Messages() []*Message { + s.gServer.mu.Lock() + defer s.gServer.mu.Unlock() + + var msgs []*Message + for _, m := range s.gServer.msgs { + m.Deliveries = m.deliveries + m.Acks = m.acks + msgs = append(msgs, m) + } + return msgs +} + +// Message returns the message with the given ID, or nil if no message +// with that ID was published. +func (s *Server) Message(id string) *Message { + s.gServer.mu.Lock() + defer s.gServer.mu.Unlock() + + m := s.gServer.msgsByID[id] + if m != nil { + m.Deliveries = m.deliveries + m.Acks = m.acks + } + return m +} + +// Wait blocks until all server activity has completed. +func (s *Server) Wait() { + s.gServer.wg.Wait() +} + +func (s *gServer) CreateTopic(_ context.Context, t *pb.Topic) (*pb.Topic, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if s.topics[t.Name] != nil { + return nil, status.Errorf(codes.AlreadyExists, "topic %q", t.Name) + } + top := newTopic(t) + s.topics[t.Name] = top + return top.proto, nil +} + +func (s *gServer) GetTopic(_ context.Context, req *pb.GetTopicRequest) (*pb.Topic, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if t := s.topics[req.Topic]; t != nil { + return t.proto, nil + } + return nil, status.Errorf(codes.NotFound, "topic %q", req.Topic) +} + +func (s *gServer) UpdateTopic(_ context.Context, req *pb.UpdateTopicRequest) (*pb.Topic, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} + +func (s *gServer) ListTopics(_ context.Context, req *pb.ListTopicsRequest) (*pb.ListTopicsResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + + var names []string + for n := range s.topics { + if strings.HasPrefix(n, req.Project) { + names = append(names, n) + } + } + sort.Strings(names) + from, to, nextToken, err := testutil.PageBounds(int(req.PageSize), req.PageToken, len(names)) + if err != nil { + return nil, err + } + res := &pb.ListTopicsResponse{NextPageToken: nextToken} + for i := from; i < to; i++ { + res.Topics = append(res.Topics, s.topics[names[i]].proto) + } + return res, nil +} + +func (s *gServer) ListTopicSubscriptions(_ context.Context, req *pb.ListTopicSubscriptionsRequest) (*pb.ListTopicSubscriptionsResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + + var names []string + for name, sub := range s.subs { + if sub.topic.proto.Name == req.Topic { + names = append(names, name) + } + } + sort.Strings(names) + from, to, nextToken, err := testutil.PageBounds(int(req.PageSize), req.PageToken, len(names)) + if err != nil { + return nil, err + } + return &pb.ListTopicSubscriptionsResponse{ + Subscriptions: names[from:to], + NextPageToken: nextToken, + }, nil +} + +func (s *gServer) DeleteTopic(_ context.Context, req *pb.DeleteTopicRequest) (*emptypb.Empty, error) { + s.mu.Lock() + defer s.mu.Unlock() + + t := s.topics[req.Topic] + if t == nil { + return nil, status.Errorf(codes.NotFound, "topic %q", req.Topic) + } + t.stop() + delete(s.topics, req.Topic) + return &emptypb.Empty{}, nil +} + +func (s *gServer) CreateSubscription(_ context.Context, ps *pb.Subscription) (*pb.Subscription, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if ps.Name == "" { + return nil, status.Errorf(codes.InvalidArgument, "missing name") + } + if s.subs[ps.Name] != nil { + return nil, status.Errorf(codes.AlreadyExists, "subscription %q", ps.Name) + } + if ps.Topic == "" { + return nil, status.Errorf(codes.InvalidArgument, "missing topic") + } + top := s.topics[ps.Topic] + if top == nil { + return nil, status.Errorf(codes.NotFound, "topic %q", ps.Topic) + } + if err := checkAckDeadline(ps.AckDeadlineSeconds); err != nil { + return nil, err + } + if ps.MessageRetentionDuration == nil { + ps.MessageRetentionDuration = defaultMessageRetentionDuration + } + if err := checkMRD(ps.MessageRetentionDuration); err != nil { + return nil, err + } + if ps.PushConfig == nil { + ps.PushConfig = &pb.PushConfig{} + } + + sub := newSubscription(top, &s.mu, ps) + top.subs[ps.Name] = sub + s.subs[ps.Name] = sub + sub.start(&s.wg) + return ps, nil +} + +// Can be set for testing. +var minAckDeadlineSecs int32 = 10 + +func checkAckDeadline(ads int32) error { + if ads < minAckDeadlineSecs || ads > 600 { + // PubSub service returns Unknown. + return status.Errorf(codes.Unknown, "bad ack_deadline_seconds: %d", ads) + } + return nil +} + +const ( + minMessageRetentionDuration = 10 * time.Minute + maxMessageRetentionDuration = 168 * time.Hour +) + +var defaultMessageRetentionDuration = ptypes.DurationProto(maxMessageRetentionDuration) + +func checkMRD(pmrd *durpb.Duration) error { + mrd, err := ptypes.Duration(pmrd) + if err != nil || mrd < minMessageRetentionDuration || mrd > maxMessageRetentionDuration { + return status.Errorf(codes.InvalidArgument, "bad message_retention_duration %+v", pmrd) + } + return nil +} + +func (s *gServer) GetSubscription(_ context.Context, req *pb.GetSubscriptionRequest) (*pb.Subscription, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if sub := s.subs[req.Subscription]; sub != nil { + return sub.proto, nil + } + return nil, status.Errorf(codes.NotFound, "subscription %q", req.Subscription) +} + +func (s *gServer) UpdateSubscription(_ context.Context, req *pb.UpdateSubscriptionRequest) (*pb.Subscription, error) { + s.mu.Lock() + defer s.mu.Unlock() + + sub := s.subs[req.Subscription.Name] + if sub == nil { + return nil, status.Errorf(codes.NotFound, "subscription %q", req.Subscription.Name) + } + + for _, path := range req.UpdateMask.Paths { + switch path { + case "push_config": + sub.proto.PushConfig = req.Subscription.PushConfig + + case "ack_deadline_seconds": + a := req.Subscription.AckDeadlineSeconds + if err := checkAckDeadline(a); err != nil { + return nil, err + } + sub.proto.AckDeadlineSeconds = a + + case "retain_acked_messages": + sub.proto.RetainAckedMessages = req.Subscription.RetainAckedMessages + + case "message_retention_duration": + if err := checkMRD(req.Subscription.MessageRetentionDuration); err != nil { + return nil, err + } + sub.proto.MessageRetentionDuration = req.Subscription.MessageRetentionDuration + + // TODO(jba): labels + default: + return nil, status.Errorf(codes.InvalidArgument, "unknown field name %q", path) + } + } + return sub.proto, nil +} + +func (s *gServer) ListSubscriptions(_ context.Context, req *pb.ListSubscriptionsRequest) (*pb.ListSubscriptionsResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + + var names []string + for name := range s.subs { + if strings.HasPrefix(name, req.Project) { + names = append(names, name) + } + } + sort.Strings(names) + from, to, nextToken, err := testutil.PageBounds(int(req.PageSize), req.PageToken, len(names)) + if err != nil { + return nil, err + } + res := &pb.ListSubscriptionsResponse{NextPageToken: nextToken} + for i := from; i < to; i++ { + res.Subscriptions = append(res.Subscriptions, s.subs[names[i]].proto) + } + return res, nil +} + +func (s *gServer) DeleteSubscription(_ context.Context, req *pb.DeleteSubscriptionRequest) (*emptypb.Empty, error) { + s.mu.Lock() + defer s.mu.Unlock() + + sub := s.subs[req.Subscription] + if sub == nil { + return nil, status.Errorf(codes.NotFound, "subscription %q", req.Subscription) + } + sub.stop() + delete(s.subs, req.Subscription) + sub.topic.deleteSub(sub) + return &emptypb.Empty{}, nil +} + +func (s *gServer) Publish(_ context.Context, req *pb.PublishRequest) (*pb.PublishResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if req.Topic == "" { + return nil, status.Errorf(codes.InvalidArgument, "missing topic") + } + top := s.topics[req.Topic] + if top == nil { + return nil, status.Errorf(codes.NotFound, "topic %q", req.Topic) + } + var ids []string + for _, pm := range req.Messages { + id := fmt.Sprintf("m%d", s.nextID) + s.nextID++ + pm.MessageId = id + pubTime := timeNow() + tsPubTime, err := ptypes.TimestampProto(pubTime) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + pm.PublishTime = tsPubTime + m := &Message{ + ID: id, + Data: pm.Data, + Attributes: pm.Attributes, + PublishTime: pubTime, + } + top.publish(pm, m) + ids = append(ids, id) + s.msgs = append(s.msgs, m) + s.msgsByID[id] = m + } + return &pb.PublishResponse{MessageIds: ids}, nil +} + +type topic struct { + proto *pb.Topic + subs map[string]*subscription +} + +func newTopic(pt *pb.Topic) *topic { + return &topic{ + proto: pt, + subs: map[string]*subscription{}, + } +} + +func (t *topic) stop() { + for _, sub := range t.subs { + sub.proto.Topic = "_deleted-topic_" + sub.stop() + } +} + +func (t *topic) deleteSub(sub *subscription) { + delete(t.subs, sub.proto.Name) +} + +func (t *topic) publish(pm *pb.PubsubMessage, m *Message) { + for _, s := range t.subs { + s.msgs[pm.MessageId] = &message{ + publishTime: m.PublishTime, + proto: &pb.ReceivedMessage{ + AckId: pm.MessageId, + Message: pm, + }, + deliveries: &m.deliveries, + acks: &m.acks, + streamIndex: -1, + } + } +} + +type subscription struct { + topic *topic + mu *sync.Mutex + proto *pb.Subscription + ackTimeout time.Duration + msgs map[string]*message // unacked messages by message ID + streams []*stream + done chan struct{} +} + +func newSubscription(t *topic, mu *sync.Mutex, ps *pb.Subscription) *subscription { + at := time.Duration(ps.AckDeadlineSeconds) * time.Second + if at == 0 { + at = 10 * time.Second + } + return &subscription{ + topic: t, + mu: mu, + proto: ps, + ackTimeout: at, + msgs: map[string]*message{}, + done: make(chan struct{}), + } +} + +func (s *subscription) start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + defer wg.Done() + for { + select { + case <-s.done: + return + case <-time.After(10 * time.Millisecond): + s.deliver() + } + } + }() +} + +func (s *subscription) stop() { + close(s.done) +} + +func (s *gServer) Acknowledge(_ context.Context, req *pb.AcknowledgeRequest) (*emptypb.Empty, error) { + s.mu.Lock() + defer s.mu.Unlock() + if req.Subscription == "" { + return nil, status.Errorf(codes.InvalidArgument, "missing subscription") + } + sub := s.subs[req.Subscription] + for _, id := range req.AckIds { + sub.ack(id) + } + return &emptypb.Empty{}, nil +} + +func (s *gServer) ModifyAckDeadline(_ context.Context, req *pb.ModifyAckDeadlineRequest) (*emptypb.Empty, error) { + s.mu.Lock() + defer s.mu.Unlock() + if req.Subscription == "" { + return nil, status.Errorf(codes.InvalidArgument, "missing subscription") + } + sub := s.subs[req.Subscription] + dur := secsToDur(req.AckDeadlineSeconds) + for _, id := range req.AckIds { + sub.modifyAckDeadline(id, dur) + } + return &emptypb.Empty{}, nil +} + +func (s *gServer) StreamingPull(sps pb.Subscriber_StreamingPullServer) error { + // Receive initial message configuring the pull. + req, err := sps.Recv() + if err != nil { + return err + } + if req.Subscription == "" { + return status.Errorf(codes.InvalidArgument, "missing subscription") + } + s.mu.Lock() + sub := s.subs[req.Subscription] + s.mu.Unlock() + if sub == nil { + return status.Errorf(codes.NotFound, "subscription %s", req.Subscription) + } + // Create a new stream to handle the pull. + st := sub.newStream(sps, s.streamTimeout) + err = st.pull(&s.wg) + sub.deleteStream(st) + return err +} + +var retentionDuration = 10 * time.Minute + +func (s *subscription) deliver() { + s.mu.Lock() + defer s.mu.Unlock() + + tNow := timeNow() + for id, m := range s.msgs { + // Mark a message as re-deliverable if its ack deadline has expired. + if m.outstanding() && tNow.After(m.ackDeadline) { + m.makeAvailable() + } + pubTime, err := ptypes.Timestamp(m.proto.Message.PublishTime) + if err != nil { + panic(err) + } + // Remove messages that have been undelivered for a long time. + if !m.outstanding() && tNow.Sub(pubTime) > retentionDuration { + delete(s.msgs, id) + } + } + // Try to deliver each remaining message. + curIndex := 0 + for _, m := range s.msgs { + if m.outstanding() { + continue + } + // If the message was never delivered before, start with the stream at + // curIndex. If it was delivered before, start with the stream after the one + // that owned it. + if m.streamIndex < 0 { + delIndex, ok := s.deliverMessage(m, curIndex, tNow) + if !ok { + break + } + curIndex = delIndex + 1 + m.streamIndex = curIndex + } else { + delIndex, ok := s.deliverMessage(m, m.streamIndex, tNow) + if !ok { + break + } + m.streamIndex = delIndex + } + } +} + +// deliverMessage attempts to deliver m to the stream at index i. If it can't, it +// tries streams i+1, i+2, ..., wrapping around. It returns the index of the stream +// it delivered the message to, or 0, false if it didn't deliver the message because +// there are no active streams. +func (s *subscription) deliverMessage(m *message, i int, tNow time.Time) (int, bool) { + for len(s.streams) > 0 { + if i >= len(s.streams) { + i = 0 + } + st := s.streams[i] + select { + case <-st.done: + s.streams = deleteStreamAt(s.streams, i) + + case st.msgc <- m.proto: + (*m.deliveries)++ + m.ackDeadline = tNow.Add(st.ackTimeout) + return i, true + } + } + return 0, false +} + +func (s *subscription) newStream(gs pb.Subscriber_StreamingPullServer, timeout time.Duration) *stream { + st := &stream{ + sub: s, + done: make(chan struct{}), + msgc: make(chan *pb.ReceivedMessage), + gstream: gs, + ackTimeout: s.ackTimeout, + timeout: timeout, + } + s.mu.Lock() + s.streams = append(s.streams, st) + s.mu.Unlock() + return st +} + +func (s *subscription) deleteStream(st *stream) { + s.mu.Lock() + defer s.mu.Unlock() + var i int + for i = 0; i < len(s.streams); i++ { + if s.streams[i] == st { + break + } + } + if i < len(s.streams) { + s.streams = deleteStreamAt(s.streams, i) + } +} +func deleteStreamAt(s []*stream, i int) []*stream { + // Preserve order for round-robin delivery. + return append(s[:i], s[i+1:]...) +} + +type message struct { + proto *pb.ReceivedMessage + publishTime time.Time + ackDeadline time.Time + deliveries *int + acks *int + streamIndex int // index of stream that currently owns msg, for round-robin delivery +} + +// A message is outstanding if it is owned by some stream. +func (m *message) outstanding() bool { + return !m.ackDeadline.IsZero() +} + +func (m *message) makeAvailable() { + m.ackDeadline = time.Time{} +} + +type stream struct { + sub *subscription + done chan struct{} // closed when the stream is finished + msgc chan *pb.ReceivedMessage + gstream pb.Subscriber_StreamingPullServer + ackTimeout time.Duration + timeout time.Duration +} + +// pull manages the StreamingPull interaction for the life of the stream. +func (st *stream) pull(wg *sync.WaitGroup) error { + errc := make(chan error, 2) + wg.Add(2) + go func() { + defer wg.Done() + errc <- st.sendLoop() + }() + go func() { + defer wg.Done() + errc <- st.recvLoop() + }() + var tchan <-chan time.Time + if st.timeout > 0 { + tchan = time.After(st.timeout) + } + // Wait until one of the goroutines returns an error, or we time out. + var err error + select { + case err = <-errc: + if err == io.EOF { + err = nil + } + case <-tchan: + } + close(st.done) // stop the other goroutine + return err +} + +func (st *stream) sendLoop() error { + for { + select { + case <-st.done: + return nil + case rm := <-st.msgc: + res := &pb.StreamingPullResponse{ReceivedMessages: []*pb.ReceivedMessage{rm}} + if err := st.gstream.Send(res); err != nil { + return err + } + } + } +} + +func (st *stream) recvLoop() error { + for { + req, err := st.gstream.Recv() + if err != nil { + return err + } + st.sub.handleStreamingPullRequest(st, req) + } +} + +func (s *subscription) handleStreamingPullRequest(st *stream, req *pb.StreamingPullRequest) { + // Lock the entire server. + s.mu.Lock() + defer s.mu.Unlock() + + for _, ackID := range req.AckIds { + s.ack(ackID) + } + for i, id := range req.ModifyDeadlineAckIds { + s.modifyAckDeadline(id, secsToDur(req.ModifyDeadlineSeconds[i])) + } + if req.StreamAckDeadlineSeconds > 0 { + st.ackTimeout = secsToDur(req.StreamAckDeadlineSeconds) + } +} + +func (s *subscription) ack(id string) { + m := s.msgs[id] + if m != nil { + (*m.acks)++ + delete(s.msgs, id) + } +} + +func (s *subscription) modifyAckDeadline(id string, d time.Duration) { + m := s.msgs[id] + if m == nil { // already acked: ignore. + return + } + if d == 0 { // nack + m.makeAvailable() + } else { // extend the deadline by d + m.ackDeadline = timeNow().Add(d) + } +} + +func secsToDur(secs int32) time.Duration { + return time.Duration(secs) * time.Second +} diff --git a/vendor/cloud.google.com/go/pubsub/pstest/fake_test.go b/vendor/cloud.google.com/go/pubsub/pstest/fake_test.go new file mode 100644 index 0000000000..f592ae059b --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/pstest/fake_test.go @@ -0,0 +1,513 @@ +// Copyright 2017 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 pstest + +import ( + "fmt" + "io" + "testing" + "time" + + "github.com/golang/protobuf/ptypes" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc" +) + +func TestTopics(t *testing.T) { + pclient, _, server := newFake(t) + ctx := context.Background() + var topics []*pb.Topic + for i := 1; i < 3; i++ { + topics = append(topics, mustCreateTopic(t, pclient, &pb.Topic{ + Name: fmt.Sprintf("projects/P/topics/T%d", i), + Labels: map[string]string{"num": fmt.Sprintf("%d", i)}, + })) + } + if got, want := len(server.gServer.topics), len(topics); got != want { + t.Fatalf("got %d topics, want %d", got, want) + } + for _, top := range topics { + got, err := pclient.GetTopic(ctx, &pb.GetTopicRequest{Topic: top.Name}) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, top) { + t.Errorf("\ngot %+v\nwant %+v", got, top) + } + } + + res, err := pclient.ListTopics(ctx, &pb.ListTopicsRequest{Project: "projects/P"}) + if err != nil { + t.Fatal(err) + } + if got, want := res.Topics, topics; !testutil.Equal(got, want) { + t.Errorf("\ngot %+v\nwant %+v", got, want) + } + + for _, top := range topics { + if _, err := pclient.DeleteTopic(ctx, &pb.DeleteTopicRequest{Topic: top.Name}); err != nil { + t.Fatal(err) + } + } + if got, want := len(server.gServer.topics), 0; got != want { + t.Fatalf("got %d topics, want %d", got, want) + } +} + +func TestSubscriptions(t *testing.T) { + pclient, sclient, server := newFake(t) + ctx := context.Background() + topic := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + var subs []*pb.Subscription + for i := 0; i < 3; i++ { + subs = append(subs, mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: fmt.Sprintf("projects/P/subscriptions/S%d", i), + Topic: topic.Name, + AckDeadlineSeconds: int32(10 * (i + 1)), + })) + } + + if got, want := len(server.gServer.subs), len(subs); got != want { + t.Fatalf("got %d subscriptions, want %d", got, want) + } + for _, s := range subs { + got, err := sclient.GetSubscription(ctx, &pb.GetSubscriptionRequest{Subscription: s.Name}) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, s) { + t.Errorf("\ngot %+v\nwant %+v", got, s) + } + } + + res, err := sclient.ListSubscriptions(ctx, &pb.ListSubscriptionsRequest{Project: "projects/P"}) + if err != nil { + t.Fatal(err) + } + if got, want := res.Subscriptions, subs; !testutil.Equal(got, want) { + t.Errorf("\ngot %+v\nwant %+v", got, want) + } + + res2, err := pclient.ListTopicSubscriptions(ctx, &pb.ListTopicSubscriptionsRequest{Topic: topic.Name}) + if err != nil { + t.Fatal(err) + } + if got, want := len(res2.Subscriptions), len(subs); got != want { + t.Fatalf("got %d subs, want %d", got, want) + } + for i, got := range res2.Subscriptions { + want := subs[i].Name + if !testutil.Equal(got, want) { + t.Errorf("\ngot %+v\nwant %+v", got, want) + } + } + + for _, s := range subs { + if _, err := sclient.DeleteSubscription(ctx, &pb.DeleteSubscriptionRequest{Subscription: s.Name}); err != nil { + t.Fatal(err) + } + } + if got, want := len(server.gServer.subs), 0; got != want { + t.Fatalf("got %d subscriptions, want %d", got, want) + } +} + +func TestPublish(t *testing.T) { + s := NewServer() + var ids []string + for i := 0; i < 3; i++ { + ids = append(ids, s.Publish("projects/p/topics/t", []byte("hello"), nil)) + } + s.Wait() + ms := s.Messages() + if got, want := len(ms), len(ids); got != want { + t.Errorf("got %d messages, want %d", got, want) + } + for i, id := range ids { + if got, want := ms[i].ID, id; got != want { + t.Errorf("got %s, want %s", got, want) + } + } + + m := s.Message(ids[1]) + if m == nil { + t.Error("got nil, want a message") + } +} + +// Note: this sets the fake's "now" time, so it is senstive to concurrent changes to "now". +func publish(t *testing.T, pclient pb.PublisherClient, topic *pb.Topic, messages []*pb.PubsubMessage) map[string]*pb.PubsubMessage { + pubTime := time.Now() + now.Store(func() time.Time { return pubTime }) + defer func() { now.Store(time.Now) }() + + res, err := pclient.Publish(context.Background(), &pb.PublishRequest{ + Topic: topic.Name, + Messages: messages, + }) + if err != nil { + t.Fatal(err) + } + tsPubTime, err := ptypes.TimestampProto(pubTime) + if err != nil { + t.Fatal(err) + } + want := map[string]*pb.PubsubMessage{} + for i, id := range res.MessageIds { + want[id] = &pb.PubsubMessage{ + Data: messages[i].Data, + Attributes: messages[i].Attributes, + MessageId: id, + PublishTime: tsPubTime, + } + } + return want +} + +func TestStreamingPull(t *testing.T) { + // A simple test of streaming pull. + pclient, sclient, _ := newFake(t) + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S", + Topic: top.Name, + AckDeadlineSeconds: 10, + }) + + want := publish(t, pclient, top, []*pb.PubsubMessage{ + {Data: []byte("d1")}, + {Data: []byte("d2")}, + {Data: []byte("d3")}, + }) + got := pubsubMessages(pullN(t, len(want), sclient, sub)) + if diff := testutil.Diff(got, want); diff != "" { + t.Error(diff) + } +} + +func TestStreamingPullAck(t *testing.T) { + // Ack each message as it arrives. Make sure we don't see dups. + minAckDeadlineSecs = 1 + pclient, sclient, _ := newFake(t) + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S", + Topic: top.Name, + AckDeadlineSeconds: 1, + }) + + _ = publish(t, pclient, top, []*pb.PubsubMessage{ + {Data: []byte("d1")}, + {Data: []byte("d2")}, + {Data: []byte("d3")}, + }) + + got := map[string]bool{} + spc := mustStartPull(t, sclient, sub) + time.AfterFunc(time.Duration(3*minAckDeadlineSecs)*time.Second, func() { + if err := spc.CloseSend(); err != nil { + t.Errorf("CloseSend: %v", err) + } + }) + + for { + res, err := spc.Recv() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + req := &pb.StreamingPullRequest{} + for _, m := range res.ReceivedMessages { + if got[m.Message.MessageId] { + t.Fatal("duplicate message") + } + got[m.Message.MessageId] = true + req.AckIds = append(req.AckIds, m.AckId) + } + if err := spc.Send(req); err != nil { + t.Fatal(err) + } + } +} + +func TestAcknowledge(t *testing.T) { + ctx := context.Background() + pclient, sclient, srv := newFake(t) + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S", + Topic: top.Name, + AckDeadlineSeconds: 10, + }) + + publish(t, pclient, top, []*pb.PubsubMessage{ + {Data: []byte("d1")}, + {Data: []byte("d2")}, + {Data: []byte("d3")}, + }) + msgs := pullN(t, 3, sclient, sub) + var ackIDs []string + for _, m := range msgs { + ackIDs = append(ackIDs, m.AckId) + } + if _, err := sclient.Acknowledge(ctx, &pb.AcknowledgeRequest{ + Subscription: sub.Name, + AckIds: ackIDs, + }); err != nil { + t.Fatal(err) + } + smsgs := srv.Messages() + if got, want := len(smsgs), 3; got != want { + t.Fatalf("got %d messages, want %d", got, want) + } + for _, sm := range smsgs { + if sm.Acks != 1 { + t.Errorf("message %s: got %d acks, want 1", sm.ID, sm.Acks) + } + } +} + +func TestModAck(t *testing.T) { + ctx := context.Background() + pclient, sclient, _ := newFake(t) + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S", + Topic: top.Name, + AckDeadlineSeconds: 10, + }) + + publish(t, pclient, top, []*pb.PubsubMessage{ + {Data: []byte("d1")}, + {Data: []byte("d2")}, + {Data: []byte("d3")}, + }) + msgs := pullN(t, 3, sclient, sub) + var ackIDs []string + for _, m := range msgs { + ackIDs = append(ackIDs, m.AckId) + } + if _, err := sclient.ModifyAckDeadline(ctx, &pb.ModifyAckDeadlineRequest{ + Subscription: sub.Name, + AckIds: ackIDs, + AckDeadlineSeconds: 0, + }); err != nil { + t.Fatal(err) + } + // Having nacked all three messages, we should see them again. + msgs = pullN(t, 3, sclient, sub) + if got, want := len(msgs), 3; got != want { + t.Errorf("got %d messages, want %d", got, want) + } +} + +func TestAckDeadline(t *testing.T) { + // Messages should be resent after they expire. + pclient, sclient, _ := newFake(t) + minAckDeadlineSecs = 2 + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S", + Topic: top.Name, + AckDeadlineSeconds: minAckDeadlineSecs, + }) + + _ = publish(t, pclient, top, []*pb.PubsubMessage{ + {Data: []byte("d1")}, + {Data: []byte("d2")}, + {Data: []byte("d3")}, + }) + + got := map[string]int{} + spc := mustStartPull(t, sclient, sub) + // In 5 seconds the ack deadline will expire twice, so we should see each message + // exactly three times. + time.AfterFunc(5*time.Second, func() { + if err := spc.CloseSend(); err != nil { + t.Errorf("CloseSend: %v", err) + } + }) + for { + res, err := spc.Recv() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + for _, m := range res.ReceivedMessages { + got[m.Message.MessageId]++ + } + } + for id, n := range got { + if n != 3 { + t.Errorf("message %s: saw %d times, want 3", id, n) + } + } +} + +func TestMultiSubs(t *testing.T) { + // Each subscription gets every message. + pclient, sclient, _ := newFake(t) + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub1 := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S1", + Topic: top.Name, + AckDeadlineSeconds: 10, + }) + sub2 := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S2", + Topic: top.Name, + AckDeadlineSeconds: 10, + }) + + want := publish(t, pclient, top, []*pb.PubsubMessage{ + {Data: []byte("d1")}, + {Data: []byte("d2")}, + {Data: []byte("d3")}, + }) + got1 := pubsubMessages(pullN(t, len(want), sclient, sub1)) + got2 := pubsubMessages(pullN(t, len(want), sclient, sub2)) + if diff := testutil.Diff(got1, want); diff != "" { + t.Error(diff) + } + if diff := testutil.Diff(got2, want); diff != "" { + t.Error(diff) + } +} + +func TestMultiStreams(t *testing.T) { + // Messages are handed out to the streams of a subscription in round-robin order. + pclient, sclient, _ := newFake(t) + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S", + Topic: top.Name, + AckDeadlineSeconds: 10, + }) + want := publish(t, pclient, top, []*pb.PubsubMessage{ + {Data: []byte("d1")}, + {Data: []byte("d2")}, + {Data: []byte("d3")}, + {Data: []byte("d4")}, + }) + streams := []pb.Subscriber_StreamingPullClient{ + mustStartPull(t, sclient, sub), + mustStartPull(t, sclient, sub), + } + got := map[string]*pb.PubsubMessage{} + for i := 0; i < 2; i++ { + for _, st := range streams { + res, err := st.Recv() + if err != nil { + t.Fatal(err) + } + m := res.ReceivedMessages[0] + got[m.Message.MessageId] = m.Message + } + } + if diff := testutil.Diff(got, want); diff != "" { + t.Error(diff) + } +} + +func TestStreamingPullTimeout(t *testing.T) { + pclient, sclient, srv := newFake(t) + timeout := 200 * time.Millisecond + srv.SetStreamTimeout(timeout) + top := mustCreateTopic(t, pclient, &pb.Topic{Name: "projects/P/topics/T"}) + sub := mustCreateSubscription(t, sclient, &pb.Subscription{ + Name: "projects/P/subscriptions/S", + Topic: top.Name, + AckDeadlineSeconds: 10, + }) + stream := mustStartPull(t, sclient, sub) + time.Sleep(2 * timeout) + _, err := stream.Recv() + if err != io.EOF { + t.Errorf("got %v, want io.EOF", err) + } +} + +func mustStartPull(t *testing.T, sc pb.SubscriberClient, sub *pb.Subscription) pb.Subscriber_StreamingPullClient { + spc, err := sc.StreamingPull(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := spc.Send(&pb.StreamingPullRequest{Subscription: sub.Name}); err != nil { + t.Fatal(err) + } + return spc +} + +func pullN(t *testing.T, n int, sc pb.SubscriberClient, sub *pb.Subscription) map[string]*pb.ReceivedMessage { + spc := mustStartPull(t, sc, sub) + got := map[string]*pb.ReceivedMessage{} + for i := 0; i < n; i++ { + res, err := spc.Recv() + if err != nil { + t.Fatal(err) + } + for _, m := range res.ReceivedMessages { + got[m.Message.MessageId] = m + } + } + if err := spc.CloseSend(); err != nil { + t.Fatal(err) + } + res, err := spc.Recv() + if err != io.EOF { + t.Fatalf("Recv returned <%v> instead of EOF; res = %v", err, res) + } + return got +} + +func pubsubMessages(rms map[string]*pb.ReceivedMessage) map[string]*pb.PubsubMessage { + ms := map[string]*pb.PubsubMessage{} + for k, rm := range rms { + ms[k] = rm.Message + } + return ms +} + +func mustCreateTopic(t *testing.T, pc pb.PublisherClient, topic *pb.Topic) *pb.Topic { + top, err := pc.CreateTopic(context.Background(), topic) + if err != nil { + t.Fatal(err) + } + return top +} + +func mustCreateSubscription(t *testing.T, sc pb.SubscriberClient, sub *pb.Subscription) *pb.Subscription { + sub, err := sc.CreateSubscription(context.Background(), sub) + if err != nil { + t.Fatal(err) + } + return sub +} + +func newFake(t *testing.T) (pb.PublisherClient, pb.SubscriberClient, *Server) { + srv := NewServer() + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + if err != nil { + t.Fatal(err) + } + return pb.NewPublisherClient(conn), pb.NewSubscriberClient(conn), srv +} diff --git a/vendor/cloud.google.com/go/pubsub/pstest_test.go b/vendor/cloud.google.com/go/pubsub/pstest_test.go new file mode 100644 index 0000000000..888f0f91e2 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/pstest_test.go @@ -0,0 +1,77 @@ +// 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 pubsub_test + +import ( + "strconv" + "sync" + "testing" + + "golang.org/x/net/context" + + "cloud.google.com/go/pubsub" + "cloud.google.com/go/pubsub/pstest" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +func TestPSTest(t *testing.T) { + t.Parallel() + ctx := context.Background() + srv := pstest.NewServer() + + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + if err != nil { + panic(err) + } + + client, err := pubsub.NewClient(ctx, "some-project", option.WithGRPCConn(conn)) + if err != nil { + panic(err) + } + defer client.Close() + + topic, err := client.CreateTopic(ctx, "test-topic") + if err != nil { + panic(err) + } + + sub, err := client.CreateSubscription(ctx, "sub-name", pubsub.SubscriptionConfig{Topic: topic}) + if err != nil { + panic(err) + } + + go func() { + for i := 0; i < 10; i++ { + srv.Publish("projects/some-project/topics/test-topic", []byte(strconv.Itoa(i)), nil) + } + }() + + ctx, cancel := context.WithCancel(ctx) + var mu sync.Mutex + count := 0 + err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { + mu.Lock() + count++ + if count >= 10 { + cancel() + } + mu.Unlock() + m.Ack() + }) + if err != nil { + panic(err) + } +} diff --git a/vendor/cloud.google.com/go/pubsub/pubsub.go b/vendor/cloud.google.com/go/pubsub/pubsub.go new file mode 100644 index 0000000000..c2e58ed6a3 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/pubsub.go @@ -0,0 +1,113 @@ +// 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 pubsub // import "cloud.google.com/go/pubsub" + +import ( + "fmt" + "os" + "runtime" + "time" + + "cloud.google.com/go/internal/version" + vkit "cloud.google.com/go/pubsub/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" +) + +const ( + // ScopePubSub grants permissions to view and manage Pub/Sub + // topics and subscriptions. + ScopePubSub = "https://www.googleapis.com/auth/pubsub" + + // ScopeCloudPlatform grants permissions to view and manage your data + // across Google Cloud Platform services. + ScopeCloudPlatform = "https://www.googleapis.com/auth/cloud-platform" +) + +const ( + prodAddr = "https://pubsub.googleapis.com/" + minAckDeadline = 10 * time.Second + maxAckDeadline = 10 * time.Minute +) + +// Client is a Google Pub/Sub client scoped to a single project. +// +// Clients should be reused rather than being created as needed. +// A Client may be shared by multiple goroutines. +type Client struct { + projectID string + pubc *vkit.PublisherClient + subc *vkit.SubscriberClient +} + +// NewClient creates a new PubSub client. +func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (c *Client, err error) { + var o []option.ClientOption + // Environment variables for gcloud emulator: + // https://cloud.google.com/sdk/gcloud/reference/beta/emulators/pubsub/ + if addr := os.Getenv("PUBSUB_EMULATOR_HOST"); addr != "" { + conn, err := grpc.Dial(addr, grpc.WithInsecure()) + if err != nil { + return nil, fmt.Errorf("grpc.Dial: %v", err) + } + o = []option.ClientOption{option.WithGRPCConn(conn)} + } else { + o = []option.ClientOption{ + // Create multiple connections to increase throughput. + option.WithGRPCConnectionPool(runtime.GOMAXPROCS(0)), + option.WithGRPCDialOption(grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 5 * time.Minute, + })), + } + o = append(o, openCensusOptions()...) + } + o = append(o, opts...) + pubc, err := vkit.NewPublisherClient(ctx, o...) + if err != nil { + return nil, fmt.Errorf("pubsub: %v", err) + } + subc, err := vkit.NewSubscriberClient(ctx, option.WithGRPCConn(pubc.Connection())) + if err != nil { + // Should never happen, since we are passing in the connection. + // If it does, we cannot close, because the user may have passed in their + // own connection originally. + return nil, fmt.Errorf("pubsub: %v", err) + } + pubc.SetGoogleClientInfo("gccl", version.Repo) + subc.SetGoogleClientInfo("gccl", version.Repo) + return &Client{ + projectID: projectID, + pubc: pubc, + subc: subc, + }, nil +} + +// Close releases any resources held by the client, +// such as memory and goroutines. +// +// If the client is available for the lifetime of the program, then Close need not be +// called at exit. +func (c *Client) Close() error { + // Return the first error, because the first call closes the connection. + err := c.pubc.Close() + _ = c.subc.Close() + return err +} + +func (c *Client) fullyQualifiedProjectName() string { + return fmt.Sprintf("projects/%s", c.projectID) +} diff --git a/vendor/cloud.google.com/go/pubsub/pullstream.go b/vendor/cloud.google.com/go/pubsub/pullstream.go new file mode 100644 index 0000000000..670ae76300 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/pullstream.go @@ -0,0 +1,188 @@ +// 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 pubsub + +import ( + "io" + "sync" + "time" + + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc" +) + +// A pullStream supports the methods of a StreamingPullClient, but re-opens +// the stream on a retryable error. +type pullStream struct { + ctx context.Context + open func() (pb.Subscriber_StreamingPullClient, error) + + mu sync.Mutex + spc *pb.Subscriber_StreamingPullClient + err error // permanent error +} + +// for testing +type streamingPullFunc func(context.Context, ...gax.CallOption) (pb.Subscriber_StreamingPullClient, error) + +func newPullStream(ctx context.Context, streamingPull streamingPullFunc, subName string, ackDeadlineSecs int32) *pullStream { + ctx = withSubscriptionKey(ctx, subName) + return &pullStream{ + ctx: ctx, + open: func() (pb.Subscriber_StreamingPullClient, error) { + spc, err := streamingPull(ctx, gax.WithGRPCOptions(grpc.MaxCallRecvMsgSize(maxSendRecvBytes))) + if err == nil { + recordStat(ctx, StreamRequestCount, 1) + err = spc.Send(&pb.StreamingPullRequest{ + Subscription: subName, + StreamAckDeadlineSeconds: ackDeadlineSecs, + }) + } + if err != nil { + return nil, err + } + return spc, nil + }, + } +} + +// get returns either a valid *StreamingPullClient (SPC), or a permanent error. +// If the argument is nil, this is the first call for an RPC, and the current +// SPC will be returned (or a new one will be opened). Otherwise, this call is a +// request to re-open the stream because of a retryable error, and the argument +// is a pointer to the SPC that returned the error. +func (s *pullStream) get(spc *pb.Subscriber_StreamingPullClient) (*pb.Subscriber_StreamingPullClient, error) { + s.mu.Lock() + defer s.mu.Unlock() + // A stored error is permanent. + if s.err != nil { + return nil, s.err + } + // If the context is done, so are we. + select { + case <-s.ctx.Done(): + s.err = s.ctx.Err() + return nil, s.err + default: + } + // TODO(jba): We can use the following instead of the above after we drop support for 1.8: + // s.err = s.ctx.Err() + // if s.err != nil { + // return nil, s.err + // } + + // If the current and argument SPCs differ, return the current one. This subsumes two cases: + // 1. We have an SPC and the caller is getting the stream for the first time. + // 2. The caller wants to retry, but they have an older SPC; we've already retried. + if spc != s.spc { + return s.spc, nil + } + // Either this is the very first call on this stream (s.spc == nil), or we have a valid + // retry request. Either way, open a new stream. + // The lock is held here for a long time, but it doesn't matter because no callers could get + // anything done anyway. + s.spc = new(pb.Subscriber_StreamingPullClient) + *s.spc, s.err = s.openWithRetry() // Any error from openWithRetry is permanent. + return s.spc, s.err +} + +func (s *pullStream) openWithRetry() (pb.Subscriber_StreamingPullClient, error) { + var bo gax.Backoff + for { + recordStat(s.ctx, StreamOpenCount, 1) + spc, err := s.open() + if err != nil && isRetryable(err) { + recordStat(s.ctx, StreamRetryCount, 1) + if err := gax.Sleep(s.ctx, bo.Pause()); err != nil { + return nil, err + } + continue + } + return spc, err + } +} + +func (s *pullStream) call(f func(pb.Subscriber_StreamingPullClient) error) error { + var ( + spc *pb.Subscriber_StreamingPullClient + err error + bo gax.Backoff + ) + for { + spc, err = s.get(spc) + if err != nil { + return err + } + start := time.Now() + err = f(*spc) + if err != nil { + if isRetryable(err) { + recordStat(s.ctx, StreamRetryCount, 1) + if time.Since(start) < 30*time.Second { // don't sleep if we've been blocked for a while + if err := gax.Sleep(s.ctx, bo.Pause()); err != nil { + return err + } + } + continue + } + s.mu.Lock() + s.err = err + s.mu.Unlock() + } + return err + } +} + +func (s *pullStream) Send(req *pb.StreamingPullRequest) error { + return s.call(func(spc pb.Subscriber_StreamingPullClient) error { + recordStat(s.ctx, AckCount, int64(len(req.AckIds))) + zeroes := 0 + for _, mds := range req.ModifyDeadlineSeconds { + if mds == 0 { + zeroes++ + } + } + recordStat(s.ctx, NackCount, int64(zeroes)) + recordStat(s.ctx, ModAckCount, int64(len(req.ModifyDeadlineSeconds)-zeroes)) + recordStat(s.ctx, StreamRequestCount, 1) + return spc.Send(req) + }) +} + +func (s *pullStream) Recv() (*pb.StreamingPullResponse, error) { + var res *pb.StreamingPullResponse + err := s.call(func(spc pb.Subscriber_StreamingPullClient) error { + var err error + recordStat(s.ctx, StreamResponseCount, 1) + res, err = spc.Recv() + if err == nil { + recordStat(s.ctx, PullCount, int64(len(res.ReceivedMessages))) + } + return err + }) + return res, err +} + +func (s *pullStream) CloseSend() error { + err := s.call(func(spc pb.Subscriber_StreamingPullClient) error { + return spc.CloseSend() + }) + s.mu.Lock() + s.err = io.EOF // should not be retried + s.mu.Unlock() + return err +} diff --git a/vendor/cloud.google.com/go/pubsub/pullstream_test.go b/vendor/cloud.google.com/go/pubsub/pullstream_test.go new file mode 100644 index 0000000000..4c5b2a414c --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/pullstream_test.go @@ -0,0 +1,78 @@ +// 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 pubsub + +import ( + "testing" + + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestPullStreamGet(t *testing.T) { + // Test that we retry on the initial Send call from pullstream.get. We don't do this + // test with the server in fake_test.go because there's no clear way to get Send + // to fail from the server. + t.Parallel() + for _, test := range []struct { + desc string + errors []error + wantCode codes.Code + }{ + { + desc: "nil error", + errors: []error{nil}, + wantCode: codes.OK, + }, + { + desc: "non-retryable error", + errors: []error{status.Errorf(codes.InvalidArgument, "")}, + wantCode: codes.InvalidArgument, + }, + { + desc: "retryable errors", + errors: []error{ + status.Errorf(codes.Unavailable, "first"), + status.Errorf(codes.Unavailable, "second"), + nil, + }, + wantCode: codes.OK, + }, + } { + streamingPull := func(context.Context, ...gax.CallOption) (pb.Subscriber_StreamingPullClient, error) { + if len(test.errors) == 0 { + panic("out of errors") + } + err := test.errors[0] + test.errors = test.errors[1:] + return &testStreamingPullClient{sendError: err}, nil + } + ps := newPullStream(context.Background(), streamingPull, "", 0) + _, err := ps.get(nil) + if got := status.Code(err); got != test.wantCode { + t.Errorf("%s: got %s, want %s", test.desc, got, test.wantCode) + } + } +} + +type testStreamingPullClient struct { + pb.Subscriber_StreamingPullClient + sendError error +} + +func (c *testStreamingPullClient) Send(*pb.StreamingPullRequest) error { return c.sendError } diff --git a/vendor/cloud.google.com/go/pubsub/service.go b/vendor/cloud.google.com/go/pubsub/service.go new file mode 100644 index 0000000000..b1bf1f8664 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/service.go @@ -0,0 +1,78 @@ +// 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 pubsub + +import ( + "fmt" + "math" + "strings" + + pb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// maxPayload is the maximum number of bytes to devote to actual ids in +// acknowledgement or modifyAckDeadline requests. A serialized +// AcknowledgeRequest proto has a small constant overhead, plus the size of the +// subscription name, plus 3 bytes per ID (a tag byte and two size bytes). A +// ModifyAckDeadlineRequest has an additional few bytes for the deadline. We +// don't know the subscription name here, so we just assume the size exclusive +// of ids is 100 bytes. +// +// With gRPC there is no way for the client to know the server's max message size (it is +// configurable on the server). We know from experience that it +// it 512K. +const ( + maxPayload = 512 * 1024 + reqFixedOverhead = 100 + overheadPerID = 3 + maxSendRecvBytes = 20 * 1024 * 1024 // 20M +) + +func convertMessages(rms []*pb.ReceivedMessage) ([]*Message, error) { + msgs := make([]*Message, 0, len(rms)) + for i, m := range rms { + msg, err := toMessage(m) + if err != nil { + return nil, fmt.Errorf("pubsub: cannot decode the retrieved message at index: %d, message: %+v", i, m) + } + msgs = append(msgs, msg) + } + return msgs, nil +} + +func trunc32(i int64) int32 { + if i > math.MaxInt32 { + i = math.MaxInt32 + } + return int32(i) +} + +// Logic from https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/google-cloud-clients/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/v1/StatusUtil.java +func isRetryable(err error) bool { + s, ok := status.FromError(err) + if !ok { // includes io.EOF, normal stream close, which causes us to reopen + return true + } + switch s.Code() { + case codes.DeadlineExceeded, codes.Internal, codes.Canceled, codes.ResourceExhausted: + return true + case codes.Unavailable: + return !strings.Contains(s.Message(), "Server shutdownNow invoked") + default: + return false + } +} diff --git a/vendor/cloud.google.com/go/pubsub/snapshot.go b/vendor/cloud.google.com/go/pubsub/snapshot.go new file mode 100644 index 0000000000..8b7366cdba --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/snapshot.go @@ -0,0 +1,160 @@ +// Copyright 2017 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 pubsub + +import ( + "fmt" + "strings" + "time" + + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/pubsub/v1" +) + +// Snapshot is a reference to a PubSub snapshot. +type Snapshot struct { + c *Client + + // The fully qualified identifier for the snapshot, in the format "projects//snapshots/" + name string +} + +// ID returns the unique identifier of the snapshot within its project. +func (s *Snapshot) ID() string { + slash := strings.LastIndex(s.name, "/") + if slash == -1 { + // name is not a fully-qualified name. + panic("bad snapshot name") + } + return s.name[slash+1:] +} + +// SnapshotConfig contains the details of a Snapshot. +type SnapshotConfig struct { + *Snapshot + Topic *Topic + Expiration time.Time +} + +// Snapshot creates a reference to a snapshot. +func (c *Client) Snapshot(id string) *Snapshot { + return &Snapshot{ + c: c, + name: fmt.Sprintf("projects/%s/snapshots/%s", c.projectID, id), + } +} + +// Snapshots returns an iterator which returns snapshots for this project. +func (c *Client) Snapshots(ctx context.Context) *SnapshotConfigIterator { + it := c.subc.ListSnapshots(ctx, &pb.ListSnapshotsRequest{ + Project: c.fullyQualifiedProjectName(), + }) + next := func() (*SnapshotConfig, error) { + snap, err := it.Next() + if err != nil { + return nil, err + } + return toSnapshotConfig(snap, c) + } + return &SnapshotConfigIterator{next: next} +} + +// SnapshotConfigIterator is an iterator that returns a series of snapshots. +type SnapshotConfigIterator struct { + next func() (*SnapshotConfig, error) +} + +// Next returns the next SnapshotConfig. Its second return value is iterator.Done if there are no more results. +// Once Next returns iterator.Done, all subsequent calls will return iterator.Done. +func (snaps *SnapshotConfigIterator) Next() (*SnapshotConfig, error) { + return snaps.next() +} + +// Delete deletes a snapshot. +func (snap *Snapshot) Delete(ctx context.Context) error { + return snap.c.subc.DeleteSnapshot(ctx, &pb.DeleteSnapshotRequest{Snapshot: snap.name}) +} + +// SeekToTime seeks the subscription to a point in time. +// +// Messages retained in the subscription that were published before this +// time are marked as acknowledged, and messages retained in the +// subscription that were published after this time are marked as +// unacknowledged. Note that this operation affects only those messages +// retained in the subscription (configured by SnapshotConfig). For example, +// if `time` corresponds to a point before the message retention +// window (or to a point before the system's notion of the subscription +// creation time), only retained messages will be marked as unacknowledged, +// and already-expunged messages will not be restored. +func (s *Subscription) SeekToTime(ctx context.Context, t time.Time) error { + ts, err := ptypes.TimestampProto(t) + if err != nil { + return err + } + _, err = s.c.subc.Seek(ctx, &pb.SeekRequest{ + Subscription: s.name, + Target: &pb.SeekRequest_Time{ts}, + }) + return err +} + +// CreateSnapshot creates a new snapshot from this subscription. +// The snapshot will be for the topic this subscription is subscribed to. +// If the name is empty string, a unique name is assigned. +// +// The created snapshot is guaranteed to retain: +// (a) The existing backlog on the subscription. More precisely, this is +// defined as the messages in the subscription's backlog that are +// unacknowledged when Snapshot returns without error. +// (b) Any messages published to the subscription's topic following +// Snapshot returning without error. +func (s *Subscription) CreateSnapshot(ctx context.Context, name string) (*SnapshotConfig, error) { + if name != "" { + name = fmt.Sprintf("projects/%s/snapshots/%s", strings.Split(s.name, "/")[1], name) + } + snap, err := s.c.subc.CreateSnapshot(ctx, &pb.CreateSnapshotRequest{ + Name: name, + Subscription: s.name, + }) + if err != nil { + return nil, err + } + return toSnapshotConfig(snap, s.c) +} + +// SeekToSnapshot seeks the subscription to a snapshot. +// +// The snapshot need not be created from this subscription, +// but it must be for the topic this subscription is subscribed to. +func (s *Subscription) SeekToSnapshot(ctx context.Context, snap *Snapshot) error { + _, err := s.c.subc.Seek(ctx, &pb.SeekRequest{ + Subscription: s.name, + Target: &pb.SeekRequest_Snapshot{snap.name}, + }) + return err +} + +func toSnapshotConfig(snap *pb.Snapshot, c *Client) (*SnapshotConfig, error) { + exp, err := ptypes.Timestamp(snap.ExpireTime) + if err != nil { + return nil, err + } + return &SnapshotConfig{ + Snapshot: &Snapshot{c: c, name: snap.Name}, + Topic: newTopic(c, snap.Topic), + Expiration: exp, + }, nil +} diff --git a/vendor/cloud.google.com/go/pubsub/streaming_pull_test.go b/vendor/cloud.google.com/go/pubsub/streaming_pull_test.go new file mode 100644 index 0000000000..7d0a207e41 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/streaming_pull_test.go @@ -0,0 +1,316 @@ +// Copyright 2017 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 pubsub + +// TODO(jba): test keepalive +// TODO(jba): test that expired messages are not kept alive +// TODO(jba): test that when all messages expire, Stop returns. + +import ( + "io" + "strconv" + "sync" + "sync/atomic" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "google.golang.org/grpc/status" + + tspb "github.com/golang/protobuf/ptypes/timestamp" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "golang.org/x/net/context" + "google.golang.org/api/option" + pb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var ( + timestamp = &tspb.Timestamp{} + testMessages = []*pb.ReceivedMessage{ + {AckId: "0", Message: &pb.PubsubMessage{Data: []byte{1}, PublishTime: timestamp}}, + {AckId: "1", Message: &pb.PubsubMessage{Data: []byte{2}, PublishTime: timestamp}}, + {AckId: "2", Message: &pb.PubsubMessage{Data: []byte{3}, PublishTime: timestamp}}, + } +) + +func TestStreamingPullBasic(t *testing.T) { + client, server := newMock(t) + server.addStreamingPullMessages(testMessages) + testStreamingPullIteration(t, client, server, testMessages) +} + +func TestStreamingPullMultipleFetches(t *testing.T) { + client, server := newMock(t) + server.addStreamingPullMessages(testMessages[:1]) + server.addStreamingPullMessages(testMessages[1:]) + testStreamingPullIteration(t, client, server, testMessages) +} + +func testStreamingPullIteration(t *testing.T, client *Client, server *mockServer, msgs []*pb.ReceivedMessage) { + sub := client.Subscription("S") + gotMsgs, err := pullN(context.Background(), sub, len(msgs), func(_ context.Context, m *Message) { + id, err := strconv.Atoi(m.ackID) + if err != nil { + panic(err) + } + // ack evens, nack odds + if id%2 == 0 { + m.Ack() + } else { + m.Nack() + } + }) + if err != nil { + t.Fatalf("Pull: %v", err) + } + gotMap := map[string]*Message{} + for _, m := range gotMsgs { + gotMap[m.ackID] = m + } + for i, msg := range msgs { + want, err := toMessage(msg) + if err != nil { + t.Fatal(err) + } + want.calledDone = true + got := gotMap[want.ackID] + if got == nil { + t.Errorf("%d: no message for ackID %q", i, want.ackID) + continue + } + if !testutil.Equal(got, want, cmp.AllowUnexported(Message{}), cmpopts.IgnoreTypes(time.Time{}, func(string, bool, time.Time) {})) { + t.Errorf("%d: got\n%#v\nwant\n%#v", i, got, want) + } + } + server.wait() + for i := 0; i < len(msgs); i++ { + id := msgs[i].AckId + if i%2 == 0 { + if !server.Acked[id] { + t.Errorf("msg %q should have been acked but wasn't", id) + } + } else { + if dl, ok := server.Deadlines[id]; !ok || dl != 0 { + t.Errorf("msg %q should have been nacked but wasn't", id) + } + } + } +} + +func TestStreamingPullError(t *testing.T) { + // If an RPC to the service returns a non-retryable error, Pull should + // return after all callbacks return, without waiting for messages to be + // acked. + client, server := newMock(t) + server.addStreamingPullMessages(testMessages[:1]) + server.addStreamingPullError(status.Errorf(codes.Unknown, "")) + sub := client.Subscription("S") + // Use only one goroutine, since the fake server is configured to + // return only one error. + sub.ReceiveSettings.NumGoroutines = 1 + callbackDone := make(chan struct{}) + ctx, _ := context.WithTimeout(context.Background(), time.Second) + err := sub.Receive(ctx, func(ctx context.Context, m *Message) { + defer close(callbackDone) + select { + case <-ctx.Done(): + return + } + }) + select { + case <-callbackDone: + default: + t.Fatal("Receive returned but callback was not done") + } + if want := codes.Unknown; grpc.Code(err) != want { + t.Fatalf("got <%v>, want code %v", err, want) + } +} + +func TestStreamingPullCancel(t *testing.T) { + // If Receive's context is canceled, it should return after all callbacks + // return and all messages have been acked. + client, server := newMock(t) + server.addStreamingPullMessages(testMessages) + sub := client.Subscription("S") + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + var n int32 + err := sub.Receive(ctx, func(ctx2 context.Context, m *Message) { + atomic.AddInt32(&n, 1) + defer atomic.AddInt32(&n, -1) + cancel() + m.Ack() + }) + if got := atomic.LoadInt32(&n); got != 0 { + t.Errorf("Receive returned with %d callbacks still running", got) + } + if err != nil { + t.Fatalf("Receive got <%v>, want nil", err) + } +} + +func TestStreamingPullRetry(t *testing.T) { + // Check that we retry on io.EOF or Unavailable. + t.Parallel() + client, server := newMock(t) + server.addStreamingPullMessages(testMessages[:1]) + server.addStreamingPullError(io.EOF) + server.addStreamingPullError(io.EOF) + server.addStreamingPullMessages(testMessages[1:2]) + server.addStreamingPullError(status.Errorf(codes.Unavailable, "")) + server.addStreamingPullError(status.Errorf(codes.Unavailable, "")) + server.addStreamingPullMessages(testMessages[2:]) + + testStreamingPullIteration(t, client, server, testMessages) +} + +func TestStreamingPullOneActive(t *testing.T) { + // Only one call to Pull can be active at a time. + client, srv := newMock(t) + srv.addStreamingPullMessages(testMessages[:1]) + sub := client.Subscription("S") + ctx, cancel := context.WithCancel(context.Background()) + err := sub.Receive(ctx, func(ctx context.Context, m *Message) { + m.Ack() + err := sub.Receive(ctx, func(context.Context, *Message) {}) + if err != errReceiveInProgress { + t.Errorf("got <%v>, want <%v>", err, errReceiveInProgress) + } + cancel() + }) + if err != nil { + t.Fatalf("got <%v>, want nil", err) + } +} + +func TestStreamingPullConcurrent(t *testing.T) { + newMsg := func(i int) *pb.ReceivedMessage { + return &pb.ReceivedMessage{ + AckId: strconv.Itoa(i), + Message: &pb.PubsubMessage{Data: []byte{byte(i)}, PublishTime: timestamp}, + } + } + + // Multiple goroutines should be able to read from the same iterator. + client, server := newMock(t) + // Add a lot of messages, a few at a time, to make sure both threads get a chance. + nMessages := 100 + for i := 0; i < nMessages; i += 2 { + server.addStreamingPullMessages([]*pb.ReceivedMessage{newMsg(i), newMsg(i + 1)}) + } + sub := client.Subscription("S") + ctx, _ := context.WithTimeout(context.Background(), time.Second) + gotMsgs, err := pullN(ctx, sub, nMessages, func(ctx context.Context, m *Message) { + m.Ack() + }) + if err != nil { + t.Fatalf("Receive: %v", err) + } + seen := map[string]bool{} + for _, gm := range gotMsgs { + if seen[gm.ackID] { + t.Fatalf("duplicate ID %q", gm.ackID) + } + seen[gm.ackID] = true + } + if len(seen) != nMessages { + t.Fatalf("got %d messages, want %d", len(seen), nMessages) + } +} + +func TestStreamingPullFlowControl(t *testing.T) { + // Callback invocations should not occur if flow control limits are exceeded. + client, server := newMock(t) + server.addStreamingPullMessages(testMessages) + sub := client.Subscription("S") + sub.ReceiveSettings.MaxOutstandingMessages = 2 + ctx, cancel := context.WithCancel(context.Background()) + activec := make(chan int) + waitc := make(chan int) + errc := make(chan error) + go func() { + errc <- sub.Receive(ctx, func(_ context.Context, m *Message) { + activec <- 1 + <-waitc + m.Ack() + }) + }() + // Here, two callbacks are active. Receive should be blocked in the flow + // control acquire method on the third message. + <-activec + <-activec + select { + case <-activec: + t.Fatal("third callback in progress") + case <-time.After(100 * time.Millisecond): + } + cancel() + // Receive still has not returned, because both callbacks are still blocked on waitc. + select { + case err := <-errc: + t.Fatalf("Receive returned early with error %v", err) + case <-time.After(100 * time.Millisecond): + } + // Let both callbacks proceed. + waitc <- 1 + waitc <- 1 + // The third callback will never run, because acquire returned a non-nil + // error, causing Receive to return. So now Receive should end. + if err := <-errc; err != nil { + t.Fatalf("got %v from Receive, want nil", err) + } +} + +func newMock(t *testing.T) (*Client, *mockServer) { + srv, err := newMockServer() + if err != nil { + t.Fatal(err) + } + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + if err != nil { + t.Fatal(err) + } + client, err := NewClient(context.Background(), "P", option.WithGRPCConn(conn)) + if err != nil { + t.Fatal(err) + } + return client, srv +} + +// pullN calls sub.Receive until at least n messages are received. +func pullN(ctx context.Context, sub *Subscription, n int, f func(context.Context, *Message)) ([]*Message, error) { + var ( + mu sync.Mutex + msgs []*Message + ) + cctx, cancel := context.WithCancel(ctx) + err := sub.Receive(cctx, func(ctx context.Context, m *Message) { + mu.Lock() + msgs = append(msgs, m) + nSeen := len(msgs) + mu.Unlock() + f(ctx, m) + if nSeen >= n { + cancel() + } + }) + if err != nil { + return nil, err + } + return msgs, nil +} diff --git a/vendor/cloud.google.com/go/pubsub/subscription.go b/vendor/cloud.google.com/go/pubsub/subscription.go new file mode 100644 index 0000000000..574e45ede0 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/subscription.go @@ -0,0 +1,522 @@ +// 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 pubsub + +import ( + "errors" + "fmt" + "io" + "strings" + "sync" + "time" + + "cloud.google.com/go/iam" + "cloud.google.com/go/internal/optional" + "github.com/golang/protobuf/ptypes" + durpb "github.com/golang/protobuf/ptypes/duration" + "golang.org/x/net/context" + "golang.org/x/sync/errgroup" + pb "google.golang.org/genproto/googleapis/pubsub/v1" + fmpb "google.golang.org/genproto/protobuf/field_mask" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// Subscription is a reference to a PubSub subscription. +type Subscription struct { + c *Client + + // The fully qualified identifier for the subscription, in the format "projects//subscriptions/" + name string + + // Settings for pulling messages. Configure these before calling Receive. + ReceiveSettings ReceiveSettings + + mu sync.Mutex + receiveActive bool +} + +// Subscription creates a reference to a subscription. +func (c *Client) Subscription(id string) *Subscription { + return c.SubscriptionInProject(id, c.projectID) +} + +// SubscriptionInProject creates a reference to a subscription in a given project. +func (c *Client) SubscriptionInProject(id, projectID string) *Subscription { + return &Subscription{ + c: c, + name: fmt.Sprintf("projects/%s/subscriptions/%s", projectID, id), + } +} + +// String returns the globally unique printable name of the subscription. +func (s *Subscription) String() string { + return s.name +} + +// ID returns the unique identifier of the subscription within its project. +func (s *Subscription) ID() string { + slash := strings.LastIndex(s.name, "/") + if slash == -1 { + // name is not a fully-qualified name. + panic("bad subscription name") + } + return s.name[slash+1:] +} + +// Subscriptions returns an iterator which returns all of the subscriptions for the client's project. +func (c *Client) Subscriptions(ctx context.Context) *SubscriptionIterator { + it := c.subc.ListSubscriptions(ctx, &pb.ListSubscriptionsRequest{ + Project: c.fullyQualifiedProjectName(), + }) + return &SubscriptionIterator{ + c: c, + next: func() (string, error) { + sub, err := it.Next() + if err != nil { + return "", err + } + return sub.Name, nil + }, + } +} + +// SubscriptionIterator is an iterator that returns a series of subscriptions. +type SubscriptionIterator struct { + c *Client + next func() (string, error) +} + +// Next returns the next subscription. If there are no more subscriptions, iterator.Done will be returned. +func (subs *SubscriptionIterator) Next() (*Subscription, error) { + subName, err := subs.next() + if err != nil { + return nil, err + } + return &Subscription{c: subs.c, name: subName}, nil +} + +// PushConfig contains configuration for subscriptions that operate in push mode. +type PushConfig struct { + // A URL locating the endpoint to which messages should be pushed. + Endpoint string + + // Endpoint configuration attributes. See https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions#pushconfig for more details. + Attributes map[string]string +} + +func (pc *PushConfig) toProto() *pb.PushConfig { + return &pb.PushConfig{ + Attributes: pc.Attributes, + PushEndpoint: pc.Endpoint, + } +} + +// Subscription config contains the configuration of a subscription. +type SubscriptionConfig struct { + Topic *Topic + PushConfig PushConfig + + // The default maximum time after a subscriber receives a message before + // the subscriber should acknowledge the message. Note: messages which are + // obtained via Subscription.Receive need not be acknowledged within this + // deadline, as the deadline will be automatically extended. + AckDeadline time.Duration + + // Whether to retain acknowledged messages. If true, acknowledged messages + // will not be expunged until they fall out of the RetentionDuration window. + RetainAckedMessages bool + + // How long to retain messages in backlog, from the time of publish. If + // RetainAckedMessages is true, this duration affects the retention of + // acknowledged messages, otherwise only unacknowledged messages are retained. + // Defaults to 7 days. Cannot be longer than 7 days or shorter than 10 minutes. + RetentionDuration time.Duration +} + +func (cfg *SubscriptionConfig) toProto(name string) *pb.Subscription { + var pbPushConfig *pb.PushConfig + if cfg.PushConfig.Endpoint != "" || len(cfg.PushConfig.Attributes) != 0 { + pbPushConfig = &pb.PushConfig{ + Attributes: cfg.PushConfig.Attributes, + PushEndpoint: cfg.PushConfig.Endpoint, + } + } + var retentionDuration *durpb.Duration + if cfg.RetentionDuration != 0 { + retentionDuration = ptypes.DurationProto(cfg.RetentionDuration) + } + return &pb.Subscription{ + Name: name, + Topic: cfg.Topic.name, + PushConfig: pbPushConfig, + AckDeadlineSeconds: trunc32(int64(cfg.AckDeadline.Seconds())), + RetainAckedMessages: cfg.RetainAckedMessages, + MessageRetentionDuration: retentionDuration, + } +} + +func protoToSubscriptionConfig(pbSub *pb.Subscription, c *Client) (SubscriptionConfig, error) { + rd := time.Hour * 24 * 7 + var err error + if pbSub.MessageRetentionDuration != nil { + rd, err = ptypes.Duration(pbSub.MessageRetentionDuration) + if err != nil { + return SubscriptionConfig{}, err + } + } + return SubscriptionConfig{ + Topic: newTopic(c, pbSub.Topic), + AckDeadline: time.Second * time.Duration(pbSub.AckDeadlineSeconds), + PushConfig: PushConfig{ + Endpoint: pbSub.PushConfig.PushEndpoint, + Attributes: pbSub.PushConfig.Attributes, + }, + RetainAckedMessages: pbSub.RetainAckedMessages, + RetentionDuration: rd, + }, nil +} + +// ReceiveSettings configure the Receive method. +// A zero ReceiveSettings will result in values equivalent to DefaultReceiveSettings. +type ReceiveSettings struct { + // MaxExtension is the maximum period for which the Subscription should + // automatically extend the ack deadline for each message. + // + // The Subscription will automatically extend the ack deadline of all + // fetched Messages for the duration specified. Automatic deadline + // extension may be disabled by specifying a duration less than 0. + // + // Connections may be terminated if they last longer than 30m, which + // effectively makes that the ceiling for this value. For longer message + // processing, see the example at https://godoc.org/cloud.google.com/go/pubsub/apiv1#example_SubscriberClient_Pull_lengthyClientProcessing + MaxExtension time.Duration + + // MaxOutstandingMessages is the maximum number of unprocessed messages + // (unacknowledged but not yet expired). If MaxOutstandingMessages is 0, it + // will be treated as if it were DefaultReceiveSettings.MaxOutstandingMessages. + // If the value is negative, then there will be no limit on the number of + // unprocessed messages. + MaxOutstandingMessages int + + // MaxOutstandingBytes is the maximum size of unprocessed messages + // (unacknowledged but not yet expired). If MaxOutstandingBytes is 0, it will + // be treated as if it were DefaultReceiveSettings.MaxOutstandingBytes. If + // the value is negative, then there will be no limit on the number of bytes + // for unprocessed messages. + MaxOutstandingBytes int + + // NumGoroutines is the number of goroutines Receive will spawn to pull + // messages concurrently. If NumGoroutines is less than 1, it will be treated + // as if it were DefaultReceiveSettings.NumGoroutines. + // + // NumGoroutines does not limit the number of messages that can be processed + // concurrently. Even with one goroutine, many messages might be processed at + // once, because that goroutine may continually receive messages and invoke the + // function passed to Receive on them. To limit the number of messages being + // processed concurrently, set MaxOutstandingMessages. + NumGoroutines int +} + +// DefaultReceiveSettings holds the default values for ReceiveSettings. +var DefaultReceiveSettings = ReceiveSettings{ + MaxExtension: 10 * time.Minute, + MaxOutstandingMessages: 1000, + MaxOutstandingBytes: 1e9, // 1G + NumGoroutines: 1, +} + +// Delete deletes the subscription. +func (s *Subscription) Delete(ctx context.Context) error { + return s.c.subc.DeleteSubscription(ctx, &pb.DeleteSubscriptionRequest{Subscription: s.name}) +} + +// Exists reports whether the subscription exists on the server. +func (s *Subscription) Exists(ctx context.Context) (bool, error) { + _, err := s.c.subc.GetSubscription(ctx, &pb.GetSubscriptionRequest{Subscription: s.name}) + if err == nil { + return true, nil + } + if grpc.Code(err) == codes.NotFound { + return false, nil + } + return false, err +} + +// Config fetches the current configuration for the subscription. +func (s *Subscription) Config(ctx context.Context) (SubscriptionConfig, error) { + pbSub, err := s.c.subc.GetSubscription(ctx, &pb.GetSubscriptionRequest{Subscription: s.name}) + if err != nil { + return SubscriptionConfig{}, err + } + cfg, err := protoToSubscriptionConfig(pbSub, s.c) + if err != nil { + return SubscriptionConfig{}, err + } + return cfg, nil +} + +// SubscriptionConfigToUpdate describes how to update a subscription. +type SubscriptionConfigToUpdate struct { + // If non-nil, the push config is changed. + PushConfig *PushConfig + + // If non-zero, the ack deadline is changed. + AckDeadline time.Duration + + // If set, RetainAckedMessages is changed. + RetainAckedMessages optional.Bool + + // If non-zero, RetentionDuration is changed. + RetentionDuration time.Duration +} + +// Update changes an existing subscription according to the fields set in cfg. +// It returns the new SubscriptionConfig. +// +// Update returns an error if no fields were modified. +func (s *Subscription) Update(ctx context.Context, cfg SubscriptionConfigToUpdate) (SubscriptionConfig, error) { + req := s.updateRequest(&cfg) + if len(req.UpdateMask.Paths) == 0 { + return SubscriptionConfig{}, errors.New("pubsub: UpdateSubscription call with nothing to update") + } + rpsub, err := s.c.subc.UpdateSubscription(ctx, req) + if err != nil { + return SubscriptionConfig{}, err + } + return protoToSubscriptionConfig(rpsub, s.c) +} + +func (s *Subscription) updateRequest(cfg *SubscriptionConfigToUpdate) *pb.UpdateSubscriptionRequest { + psub := &pb.Subscription{Name: s.name} + var paths []string + if cfg.PushConfig != nil { + psub.PushConfig = cfg.PushConfig.toProto() + paths = append(paths, "push_config") + } + if cfg.AckDeadline != 0 { + psub.AckDeadlineSeconds = trunc32(int64(cfg.AckDeadline.Seconds())) + paths = append(paths, "ack_deadline_seconds") + } + if cfg.RetainAckedMessages != nil { + psub.RetainAckedMessages = optional.ToBool(cfg.RetainAckedMessages) + paths = append(paths, "retain_acked_messages") + } + if cfg.RetentionDuration != 0 { + psub.MessageRetentionDuration = ptypes.DurationProto(cfg.RetentionDuration) + paths = append(paths, "message_retention_duration") + } + return &pb.UpdateSubscriptionRequest{ + Subscription: psub, + UpdateMask: &fmpb.FieldMask{Paths: paths}, + } +} + +func (s *Subscription) IAM() *iam.Handle { + return iam.InternalNewHandle(s.c.subc.Connection(), s.name) +} + +// CreateSubscription creates a new subscription on a topic. +// +// id is the name of the subscription to create. It must start with a letter, +// and contain only letters ([A-Za-z]), numbers ([0-9]), dashes (-), +// underscores (_), periods (.), tildes (~), plus (+) or percent signs (%). It +// must be between 3 and 255 characters in length, and must not start with +// "goog". +// +// cfg.Topic is the topic from which the subscription should receive messages. It +// need not belong to the same project as the subscription. This field is required. +// +// cfg.AckDeadline is the maximum time after a subscriber receives a message before +// the subscriber should acknowledge the message. It must be between 10 and 600 +// seconds (inclusive), and is rounded down to the nearest second. If the +// provided ackDeadline is 0, then the default value of 10 seconds is used. +// Note: messages which are obtained via Subscription.Receive need not be +// acknowledged within this deadline, as the deadline will be automatically +// extended. +// +// cfg.PushConfig may be set to configure this subscription for push delivery. +// +// If the subscription already exists an error will be returned. +func (c *Client) CreateSubscription(ctx context.Context, id string, cfg SubscriptionConfig) (*Subscription, error) { + if cfg.Topic == nil { + return nil, errors.New("pubsub: require non-nil Topic") + } + if cfg.AckDeadline == 0 { + cfg.AckDeadline = 10 * time.Second + } + if d := cfg.AckDeadline; d < 10*time.Second || d > 600*time.Second { + return nil, fmt.Errorf("ack deadline must be between 10 and 600 seconds; got: %v", d) + } + + sub := c.Subscription(id) + _, err := c.subc.CreateSubscription(ctx, cfg.toProto(sub.name)) + if err != nil { + return nil, err + } + return sub, nil +} + +var errReceiveInProgress = errors.New("pubsub: Receive already in progress for this subscription") + +// Receive calls f with the outstanding messages from the subscription. +// It blocks until ctx is done, or the service returns a non-retryable error. +// +// The standard way to terminate a Receive is to cancel its context: +// +// cctx, cancel := context.WithCancel(ctx) +// err := sub.Receive(cctx, callback) +// // Call cancel from callback, or another goroutine. +// +// If the service returns a non-retryable error, Receive returns that error after +// all of the outstanding calls to f have returned. If ctx is done, Receive +// returns nil after all of the outstanding calls to f have returned and +// all messages have been acknowledged or have expired. +// +// Receive calls f concurrently from multiple goroutines. It is encouraged to +// process messages synchronously in f, even if that processing is relatively +// time-consuming; Receive will spawn new goroutines for incoming messages, +// limited by MaxOutstandingMessages and MaxOutstandingBytes in ReceiveSettings. +// +// The context passed to f will be canceled when ctx is Done or there is a +// fatal service error. +// +// Receive will automatically extend the ack deadline of all fetched Messages for the +// period specified by s.ReceiveSettings.MaxExtension. +// +// Each Subscription may have only one invocation of Receive active at a time. +func (s *Subscription) Receive(ctx context.Context, f func(context.Context, *Message)) error { + s.mu.Lock() + if s.receiveActive { + s.mu.Unlock() + return errReceiveInProgress + } + s.receiveActive = true + s.mu.Unlock() + defer func() { s.mu.Lock(); s.receiveActive = false; s.mu.Unlock() }() + + config, err := s.Config(ctx) + if err != nil { + if grpc.Code(err) == codes.Canceled { + return nil + } + return err + } + maxCount := s.ReceiveSettings.MaxOutstandingMessages + if maxCount == 0 { + maxCount = DefaultReceiveSettings.MaxOutstandingMessages + } + maxBytes := s.ReceiveSettings.MaxOutstandingBytes + if maxBytes == 0 { + maxBytes = DefaultReceiveSettings.MaxOutstandingBytes + } + maxExt := s.ReceiveSettings.MaxExtension + if maxExt == 0 { + maxExt = DefaultReceiveSettings.MaxExtension + } else if maxExt < 0 { + // If MaxExtension is negative, disable automatic extension. + maxExt = 0 + } + numGoroutines := s.ReceiveSettings.NumGoroutines + if numGoroutines < 1 { + numGoroutines = DefaultReceiveSettings.NumGoroutines + } + // TODO(jba): add tests that verify that ReceiveSettings are correctly processed. + po := &pullOptions{ + maxExtension: maxExt, + maxPrefetch: trunc32(int64(maxCount)), + ackDeadline: config.AckDeadline, + } + fc := newFlowController(maxCount, maxBytes) + + // Wait for all goroutines started by Receive to return, so instead of an + // obscure goroutine leak we have an obvious blocked call to Receive. + group, gctx := errgroup.WithContext(ctx) + for i := 0; i < numGoroutines; i++ { + group.Go(func() error { + return s.receive(gctx, po, fc, f) + }) + } + return group.Wait() +} + +func (s *Subscription) receive(ctx context.Context, po *pullOptions, fc *flowController, f func(context.Context, *Message)) error { + // Cancel a sub-context when we return, to kick the context-aware callbacks + // and the goroutine below. + ctx2, cancel := context.WithCancel(ctx) + // Call stop when Receive's context is done. + // Stop will block until all outstanding messages have been acknowledged + // or there was a fatal service error. + // The iterator does not use the context passed to Receive. If it did, canceling + // that context would immediately stop the iterator without waiting for unacked + // messages. + iter := newMessageIterator(context.Background(), s.c.subc, s.name, po) + + // We cannot use errgroup from Receive here. Receive might already be calling group.Wait, + // and group.Wait cannot be called concurrently with group.Go. We give each receive() its + // own WaitGroup instead. + // Since wg.Add is only called from the main goroutine, wg.Wait is guaranteed + // to be called after all Adds. + var wg sync.WaitGroup + wg.Add(1) + go func() { + <-ctx2.Done() + iter.stop() + wg.Done() + }() + defer wg.Wait() + + defer cancel() + for { + msgs, err := iter.receive() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + for i, msg := range msgs { + msg := msg + // TODO(jba): call acquire closer to when the message is allocated. + if err := fc.acquire(ctx, len(msg.Data)); err != nil { + // TODO(jba): test that these "orphaned" messages are nacked immediately when ctx is done. + for _, m := range msgs[i:] { + m.Nack() + } + return nil + } + old := msg.doneFunc + msgLen := len(msg.Data) + msg.doneFunc = func(ackID string, ack bool, receiveTime time.Time) { + defer fc.release(msgLen) + old(ackID, ack, receiveTime) + } + wg.Add(1) + go func() { + defer wg.Done() + f(ctx2, msg) + }() + } + } +} + +// TODO(jba): remove when we delete messageIterator. +type pullOptions struct { + maxExtension time.Duration + maxPrefetch int32 + // ackDeadline is the default ack deadline for the subscription. Not + // configurable. + ackDeadline time.Duration +} diff --git a/vendor/cloud.google.com/go/pubsub/subscription_test.go b/vendor/cloud.google.com/go/pubsub/subscription_test.go new file mode 100644 index 0000000000..abe036d798 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/subscription_test.go @@ -0,0 +1,196 @@ +// 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 pubsub + +import ( + "fmt" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/pubsub/pstest" + + "golang.org/x/net/context" + + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +// All returns the remaining subscriptions from this iterator. +func slurpSubs(it *SubscriptionIterator) ([]*Subscription, error) { + var subs []*Subscription + for { + switch sub, err := it.Next(); err { + case nil: + subs = append(subs, sub) + case iterator.Done: + return subs, nil + default: + return nil, err + } + } +} + +func TestSubscriptionID(t *testing.T) { + const id = "id" + c := &Client{projectID: "projid"} + s := c.Subscription(id) + if got, want := s.ID(), id; got != want { + t.Errorf("Subscription.ID() = %q; want %q", got, want) + } +} + +func TestListProjectSubscriptions(t *testing.T) { + ctx := context.Background() + c, _ := newFake(t) + topic := mustCreateTopic(t, c, "t") + var want []string + for i := 1; i <= 2; i++ { + id := fmt.Sprintf("s%d", i) + want = append(want, id) + _, err := c.CreateSubscription(ctx, id, SubscriptionConfig{Topic: topic}) + if err != nil { + t.Fatal(err) + } + } + subs, err := slurpSubs(c.Subscriptions(ctx)) + if err != nil { + t.Fatal(err) + } + + got := getSubIDs(subs) + if !testutil.Equal(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func getSubIDs(subs []*Subscription) []string { + var names []string + for _, sub := range subs { + names = append(names, sub.ID()) + } + return names +} + +func TestListTopicSubscriptions(t *testing.T) { + ctx := context.Background() + c, _ := newFake(t) + topics := []*Topic{ + mustCreateTopic(t, c, "t0"), + mustCreateTopic(t, c, "t1"), + } + wants := make([][]string, 2) + for i := 0; i < 5; i++ { + id := fmt.Sprintf("s%d", i) + sub, err := c.CreateSubscription(ctx, id, SubscriptionConfig{Topic: topics[i%2]}) + if err != nil { + t.Fatal(err) + } + wants[i%2] = append(wants[i%2], sub.ID()) + } + + for i, topic := range topics { + subs, err := slurpSubs(topic.Subscriptions(ctx)) + if err != nil { + t.Fatal(err) + } + got := getSubIDs(subs) + if !testutil.Equal(got, wants[i]) { + t.Errorf("#%d: got %v, want %v", i, got, wants[i]) + } + } +} + +const defaultRetentionDuration = 168 * time.Hour + +func TestUpdateSubscription(t *testing.T) { + ctx := context.Background() + client, _ := newFake(t) + defer client.Close() + + topic := mustCreateTopic(t, client, "t") + sub, err := client.CreateSubscription(ctx, "s", SubscriptionConfig{Topic: topic}) + if err != nil { + t.Fatal(err) + } + cfg, err := sub.Config(ctx) + if err != nil { + t.Fatal(err) + } + want := SubscriptionConfig{ + Topic: topic, + AckDeadline: 10 * time.Second, + RetainAckedMessages: false, + RetentionDuration: defaultRetentionDuration, + } + if !testutil.Equal(cfg, want) { + t.Fatalf("\ngot %+v\nwant %+v", cfg, want) + } + + got, err := sub.Update(ctx, SubscriptionConfigToUpdate{ + AckDeadline: 20 * time.Second, + RetainAckedMessages: true, + }) + if err != nil { + t.Fatal(err) + } + want = SubscriptionConfig{ + Topic: topic, + AckDeadline: 20 * time.Second, + RetainAckedMessages: true, + RetentionDuration: defaultRetentionDuration, + } + if !testutil.Equal(got, want) { + t.Fatalf("\ngot %+v\nwant %+v", got, want) + } + + got, err = sub.Update(ctx, SubscriptionConfigToUpdate{RetentionDuration: 2 * time.Hour}) + if err != nil { + t.Fatal(err) + } + want.RetentionDuration = 2 * time.Hour + if !testutil.Equal(got, want) { + t.Fatalf("\ngot %+v\nwant %+v", got, want) + } + + _, err = sub.Update(ctx, SubscriptionConfigToUpdate{}) + if err == nil { + t.Fatal("got nil, want error") + } +} + +func (t1 *Topic) Equal(t2 *Topic) bool { + if t1 == nil && t2 == nil { + return true + } + if t1 == nil || t2 == nil { + return false + } + return t1.c == t2.c && t1.name == t2.name +} + +func newFake(t *testing.T) (*Client, *pstest.Server) { + ctx := context.Background() + srv := pstest.NewServer() + client, err := NewClient(ctx, "P", + option.WithEndpoint(srv.Addr), + option.WithoutAuthentication(), + option.WithGRPCDialOption(grpc.WithInsecure())) + if err != nil { + t.Fatal(err) + } + return client, srv +} diff --git a/vendor/cloud.google.com/go/pubsub/timeout_test.go b/vendor/cloud.google.com/go/pubsub/timeout_test.go new file mode 100644 index 0000000000..83a0089a49 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/timeout_test.go @@ -0,0 +1,88 @@ +// 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 pubsub + +import ( + "log" + "sync/atomic" + "testing" + "time" + + "golang.org/x/net/context" + + "cloud.google.com/go/pubsub/pstest" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +// Using the fake PubSub server in the pstest package, verify that streaming +// pull resumes if the server stream times out. +func TestStreamTimeout(t *testing.T) { + t.Parallel() + log.SetFlags(log.Lmicroseconds) + ctx := context.Background() + srv := pstest.NewServer() + srv.SetStreamTimeout(2 * time.Second) + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure()) + if err != nil { + t.Fatal(err) + } + + client, err := NewClient(ctx, "P", option.WithGRPCConn(conn)) + if err != nil { + t.Fatal(err) + } + defer client.Close() + topic, err := client.CreateTopic(ctx, "T") + if err != nil { + t.Fatal(err) + } + sub, err := client.CreateSubscription(ctx, "sub", SubscriptionConfig{Topic: topic, AckDeadline: 10 * time.Second}) + if err != nil { + t.Fatal(err) + } + const nPublish = 8 + rctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + errc := make(chan error) + var nSeen int64 + go func() { + errc <- sub.Receive(rctx, func(ctx context.Context, m *Message) { + m.Ack() + n := atomic.AddInt64(&nSeen, 1) + if n >= nPublish { + cancel() + } + }) + }() + + for i := 0; i < nPublish; i++ { + pr := topic.Publish(ctx, &Message{Data: []byte("msg")}) + _, err := pr.Get(ctx) + if err != nil { + t.Fatal(err) + } + time.Sleep(250 * time.Millisecond) + } + + err = <-errc + if err := sub.Delete(ctx); err != nil { + t.Fatal(err) + } + n := atomic.LoadInt64(&nSeen) + if n < nPublish { + t.Errorf("got %d messages, want %d", n, nPublish) + } +} diff --git a/vendor/cloud.google.com/go/pubsub/topic.go b/vendor/cloud.google.com/go/pubsub/topic.go new file mode 100644 index 0000000000..9ec04f9092 --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/topic.go @@ -0,0 +1,377 @@ +// 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 pubsub + +import ( + "errors" + "fmt" + "runtime" + "strings" + "sync" + "time" + + "cloud.google.com/go/iam" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/support/bundler" + pb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +const ( + // The maximum number of messages that can be in a single publish request, as + // determined by the PubSub service. + MaxPublishRequestCount = 1000 + + // The maximum size of a single publish request in bytes, as determined by the PubSub service. + MaxPublishRequestBytes = 1e7 + + maxInt = int(^uint(0) >> 1) +) + +// ErrOversizedMessage indicates that a message's size exceeds MaxPublishRequestBytes. +var ErrOversizedMessage = bundler.ErrOversizedItem + +// Topic is a reference to a PubSub topic. +// +// The methods of Topic are safe for use by multiple goroutines. +type Topic struct { + c *Client + // The fully qualified identifier for the topic, in the format "projects//topics/" + name string + + // Settings for publishing messages. All changes must be made before the + // first call to Publish. The default is DefaultPublishSettings. + PublishSettings PublishSettings + + mu sync.RWMutex + stopped bool + bundler *bundler.Bundler + + wg sync.WaitGroup +} + +// PublishSettings control the bundling of published messages. +type PublishSettings struct { + + // Publish a non-empty batch after this delay has passed. + DelayThreshold time.Duration + + // Publish a batch when it has this many messages. The maximum is + // MaxPublishRequestCount. + CountThreshold int + + // Publish a batch when its size in bytes reaches this value. + ByteThreshold int + + // The number of goroutines that invoke the Publish RPC concurrently. + // Defaults to a multiple of GOMAXPROCS. + NumGoroutines int + + // The maximum time that the client will attempt to publish a bundle of messages. + Timeout time.Duration +} + +// DefaultPublishSettings holds the default values for topics' PublishSettings. +var DefaultPublishSettings = PublishSettings{ + DelayThreshold: 1 * time.Millisecond, + CountThreshold: 100, + ByteThreshold: 1e6, + Timeout: 60 * time.Second, +} + +// CreateTopic creates a new topic. +// The specified topic ID must start with a letter, and contain only letters +// ([A-Za-z]), numbers ([0-9]), dashes (-), underscores (_), periods (.), +// tildes (~), plus (+) or percent signs (%). It must be between 3 and 255 +// characters in length, and must not start with "goog". +// If the topic already exists an error will be returned. +func (c *Client) CreateTopic(ctx context.Context, id string) (*Topic, error) { + t := c.Topic(id) + _, err := c.pubc.CreateTopic(ctx, &pb.Topic{Name: t.name}) + if err != nil { + return nil, err + } + return t, nil +} + +// Topic creates a reference to a topic in the client's project. +// +// If a Topic's Publish method is called, it has background goroutines +// associated with it. Clean them up by calling Topic.Stop. +// +// Avoid creating many Topic instances if you use them to publish. +func (c *Client) Topic(id string) *Topic { + return c.TopicInProject(id, c.projectID) +} + +// TopicInProject creates a reference to a topic in the given project. +// +// If a Topic's Publish method is called, it has background goroutines +// associated with it. Clean them up by calling Topic.Stop. +// +// Avoid creating many Topic instances if you use them to publish. +func (c *Client) TopicInProject(id, projectID string) *Topic { + return newTopic(c, fmt.Sprintf("projects/%s/topics/%s", projectID, id)) +} + +func newTopic(c *Client, name string) *Topic { + return &Topic{ + c: c, + name: name, + PublishSettings: DefaultPublishSettings, + } +} + +// Topics returns an iterator which returns all of the topics for the client's project. +func (c *Client) Topics(ctx context.Context) *TopicIterator { + it := c.pubc.ListTopics(ctx, &pb.ListTopicsRequest{Project: c.fullyQualifiedProjectName()}) + return &TopicIterator{ + c: c, + next: func() (string, error) { + topic, err := it.Next() + if err != nil { + return "", err + } + return topic.Name, nil + }, + } +} + +// TopicIterator is an iterator that returns a series of topics. +type TopicIterator struct { + c *Client + next func() (string, error) +} + +// Next returns the next topic. If there are no more topics, iterator.Done will be returned. +func (tps *TopicIterator) Next() (*Topic, error) { + topicName, err := tps.next() + if err != nil { + return nil, err + } + return newTopic(tps.c, topicName), nil +} + +// ID returns the unique idenfier of the topic within its project. +func (t *Topic) ID() string { + slash := strings.LastIndex(t.name, "/") + if slash == -1 { + // name is not a fully-qualified name. + panic("bad topic name") + } + return t.name[slash+1:] +} + +// String returns the printable globally unique name for the topic. +func (t *Topic) String() string { + return t.name +} + +// Delete deletes the topic. +func (t *Topic) Delete(ctx context.Context) error { + return t.c.pubc.DeleteTopic(ctx, &pb.DeleteTopicRequest{Topic: t.name}) +} + +// Exists reports whether the topic exists on the server. +func (t *Topic) Exists(ctx context.Context) (bool, error) { + if t.name == "_deleted-topic_" { + return false, nil + } + _, err := t.c.pubc.GetTopic(ctx, &pb.GetTopicRequest{Topic: t.name}) + if err == nil { + return true, nil + } + if grpc.Code(err) == codes.NotFound { + return false, nil + } + return false, err +} + +func (t *Topic) IAM() *iam.Handle { + return iam.InternalNewHandle(t.c.pubc.Connection(), t.name) +} + +// Subscriptions returns an iterator which returns the subscriptions for this topic. +// +// Some of the returned subscriptions may belong to a project other than t. +func (t *Topic) Subscriptions(ctx context.Context) *SubscriptionIterator { + it := t.c.pubc.ListTopicSubscriptions(ctx, &pb.ListTopicSubscriptionsRequest{ + Topic: t.name, + }) + return &SubscriptionIterator{ + c: t.c, + next: it.Next, + } +} + +var errTopicStopped = errors.New("pubsub: Stop has been called for this topic") + +// Publish publishes msg to the topic asynchronously. Messages are batched and +// sent according to the topic's PublishSettings. Publish never blocks. +// +// Publish returns a non-nil PublishResult which will be ready when the +// message has been sent (or has failed to be sent) to the server. +// +// Publish creates goroutines for batching and sending messages. These goroutines +// need to be stopped by calling t.Stop(). Once stopped, future calls to Publish +// will immediately return a PublishResult with an error. +func (t *Topic) Publish(ctx context.Context, msg *Message) *PublishResult { + // TODO(jba): if this turns out to take significant time, try to approximate it. + // Or, convert the messages to protos in Publish, instead of in the service. + msg.size = proto.Size(&pb.PubsubMessage{ + Data: msg.Data, + Attributes: msg.Attributes, + }) + r := &PublishResult{ready: make(chan struct{})} + t.initBundler() + t.mu.RLock() + defer t.mu.RUnlock() + // TODO(aboulhosn) [from bcmills] consider changing the semantics of bundler to perform this logic so we don't have to do it here + if t.stopped { + r.set("", errTopicStopped) + return r + } + + // TODO(jba) [from bcmills] consider using a shared channel per bundle + // (requires Bundler API changes; would reduce allocations) + // The call to Add should never return an error because the bundler's + // BufferedByteLimit is set to maxInt; we do not perform any flow + // control in the client. + err := t.bundler.Add(&bundledMessage{msg, r}, msg.size) + if err != nil { + r.set("", err) + } + return r +} + +// Send all remaining published messages and stop goroutines created for handling +// publishing. Returns once all outstanding messages have been sent or have +// failed to be sent. +func (t *Topic) Stop() { + t.mu.Lock() + noop := t.stopped || t.bundler == nil + t.stopped = true + t.mu.Unlock() + if noop { + return + } + t.bundler.Flush() +} + +// A PublishResult holds the result from a call to Publish. +type PublishResult struct { + ready chan struct{} + serverID string + err error +} + +// Ready returns a channel that is closed when the result is ready. +// When the Ready channel is closed, Get is guaranteed not to block. +func (r *PublishResult) Ready() <-chan struct{} { return r.ready } + +// Get returns the server-generated message ID and/or error result of a Publish call. +// Get blocks until the Publish call completes or the context is done. +func (r *PublishResult) Get(ctx context.Context) (serverID string, err error) { + // If the result is already ready, return it even if the context is done. + select { + case <-r.Ready(): + return r.serverID, r.err + default: + } + select { + case <-ctx.Done(): + return "", ctx.Err() + case <-r.Ready(): + return r.serverID, r.err + } +} + +func (r *PublishResult) set(sid string, err error) { + r.serverID = sid + r.err = err + close(r.ready) +} + +type bundledMessage struct { + msg *Message + res *PublishResult +} + +func (t *Topic) initBundler() { + t.mu.RLock() + noop := t.stopped || t.bundler != nil + t.mu.RUnlock() + if noop { + return + } + t.mu.Lock() + defer t.mu.Unlock() + // Must re-check, since we released the lock. + if t.stopped || t.bundler != nil { + return + } + + timeout := t.PublishSettings.Timeout + t.bundler = bundler.NewBundler(&bundledMessage{}, func(items interface{}) { + // TODO(jba): use a context detached from the one passed to NewClient. + ctx := context.TODO() + if timeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } + t.publishMessageBundle(ctx, items.([]*bundledMessage)) + }) + t.bundler.DelayThreshold = t.PublishSettings.DelayThreshold + t.bundler.BundleCountThreshold = t.PublishSettings.CountThreshold + if t.bundler.BundleCountThreshold > MaxPublishRequestCount { + t.bundler.BundleCountThreshold = MaxPublishRequestCount + } + t.bundler.BundleByteThreshold = t.PublishSettings.ByteThreshold + t.bundler.BufferedByteLimit = maxInt + t.bundler.BundleByteLimit = MaxPublishRequestBytes + // Unless overridden, allow many goroutines per CPU to call the Publish RPC concurrently. + // The default value was determined via extensive load testing (see the loadtest subdirectory). + if t.PublishSettings.NumGoroutines > 0 { + t.bundler.HandlerLimit = t.PublishSettings.NumGoroutines + } else { + t.bundler.HandlerLimit = 25 * runtime.GOMAXPROCS(0) + } +} + +func (t *Topic) publishMessageBundle(ctx context.Context, bms []*bundledMessage) { + pbMsgs := make([]*pb.PubsubMessage, len(bms)) + for i, bm := range bms { + pbMsgs[i] = &pb.PubsubMessage{ + Data: bm.msg.Data, + Attributes: bm.msg.Attributes, + } + bm.msg = nil // release bm.msg for GC + } + res, err := t.c.pubc.Publish(ctx, &pb.PublishRequest{ + Topic: t.name, + Messages: pbMsgs, + }, gax.WithGRPCOptions(grpc.MaxCallSendMsgSize(maxSendRecvBytes))) + for i, bm := range bms { + if err != nil { + bm.res.set("", err) + } else { + bm.res.set(res.MessageIds[i], nil) + } + } +} diff --git a/vendor/cloud.google.com/go/pubsub/topic_test.go b/vendor/cloud.google.com/go/pubsub/topic_test.go new file mode 100644 index 0000000000..4d726e7d7f --- /dev/null +++ b/vendor/cloud.google.com/go/pubsub/topic_test.go @@ -0,0 +1,142 @@ +// 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 pubsub + +import ( + "fmt" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "google.golang.org/grpc/status" + + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +func checkTopicListing(t *testing.T, c *Client, want []string) { + topics, err := slurpTopics(c.Topics(context.Background())) + if err != nil { + t.Fatalf("error listing topics: %v", err) + } + var got []string + for _, topic := range topics { + got = append(got, topic.ID()) + } + if !testutil.Equal(got, want) { + t.Errorf("topic list: got: %v, want: %v", got, want) + } +} + +// All returns the remaining topics from this iterator. +func slurpTopics(it *TopicIterator) ([]*Topic, error) { + var topics []*Topic + for { + switch topic, err := it.Next(); err { + case nil: + topics = append(topics, topic) + case iterator.Done: + return topics, nil + default: + return nil, err + } + } +} + +func TestTopicID(t *testing.T) { + const id = "id" + c, _ := newFake(t) + s := c.Topic(id) + if got, want := s.ID(), id; got != want { + t.Errorf("Token.ID() = %q; want %q", got, want) + } +} + +func TestListTopics(t *testing.T) { + c, _ := newFake(t) + var ids []string + for i := 1; i <= 4; i++ { + id := fmt.Sprintf("t%d", i) + ids = append(ids, id) + mustCreateTopic(t, c, id) + } + checkTopicListing(t, c, ids) +} + +func TestListCompletelyEmptyTopics(t *testing.T) { + c, _ := newFake(t) + checkTopicListing(t, c, nil) +} + +func TestStopPublishOrder(t *testing.T) { + // Check that Stop doesn't panic if called before Publish. + // Also that Publish after Stop returns the right error. + ctx := context.Background() + c := &Client{projectID: "projid"} + topic := c.Topic("t") + topic.Stop() + r := topic.Publish(ctx, &Message{}) + _, err := r.Get(ctx) + if err != errTopicStopped { + t.Errorf("got %v, want errTopicStopped", err) + } +} + +func TestPublishTimeout(t *testing.T) { + ctx := context.Background() + serv, err := testutil.NewServer() + pubsubpb.RegisterPublisherServer(serv.Gsrv, &alwaysFailPublish{}) + conn, err := grpc.Dial(serv.Addr, grpc.WithInsecure()) + if err != nil { + t.Fatal(err) + } + c, err := NewClient(ctx, "projectID", option.WithGRPCConn(conn)) + if err != nil { + t.Fatal(err) + } + topic := c.Topic("t") + topic.PublishSettings.Timeout = 3 * time.Second + r := topic.Publish(ctx, &Message{}) + defer topic.Stop() + select { + case <-r.Ready(): + _, err = r.Get(ctx) + if err != context.DeadlineExceeded { + t.Fatalf("got %v, want context.DeadlineExceeded", err) + } + case <-time.After(2 * topic.PublishSettings.Timeout): + t.Fatal("timed out") + } +} + +type alwaysFailPublish struct { + pubsubpb.PublisherServer +} + +func (s *alwaysFailPublish) Publish(ctx context.Context, req *pubsubpb.PublishRequest) (*pubsubpb.PublishResponse, error) { + return nil, status.Errorf(codes.Unavailable, "try again") +} + +func mustCreateTopic(t *testing.T, c *Client, id string) *Topic { + topic, err := c.CreateTopic(context.Background(), id) + if err != nil { + t.Fatal(err) + } + return topic +} diff --git a/vendor/cloud.google.com/go/redis/apiv1beta1/cloud_redis_client.go b/vendor/cloud.google.com/go/redis/apiv1beta1/cloud_redis_client.go new file mode 100644 index 0000000000..8085b112d6 --- /dev/null +++ b/vendor/cloud.google.com/go/redis/apiv1beta1/cloud_redis_client.go @@ -0,0 +1,521 @@ +// 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 redis + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/proto" + anypb "github.com/golang/protobuf/ptypes/any" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + redispb "google.golang.org/genproto/googleapis/cloud/redis/v1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +// CloudRedisCallOptions contains the retry settings for each method of CloudRedisClient. +type CloudRedisCallOptions struct { + ListInstances []gax.CallOption + GetInstance []gax.CallOption + CreateInstance []gax.CallOption + UpdateInstance []gax.CallOption + DeleteInstance []gax.CallOption +} + +func defaultCloudRedisClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("redis.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultCloudRedisCallOptions() *CloudRedisCallOptions { + retry := map[[2]string][]gax.CallOption{} + return &CloudRedisCallOptions{ + ListInstances: retry[[2]string{"default", "non_idempotent"}], + GetInstance: retry[[2]string{"default", "non_idempotent"}], + CreateInstance: retry[[2]string{"default", "non_idempotent"}], + UpdateInstance: retry[[2]string{"default", "non_idempotent"}], + DeleteInstance: retry[[2]string{"default", "non_idempotent"}], + } +} + +// CloudRedisClient is a client for interacting with Google Cloud Memorystore for Redis API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type CloudRedisClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + cloudRedisClient redispb.CloudRedisClient + + // 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 *CloudRedisCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewCloudRedisClient creates a new cloud redis client. +// +// Configures and manages Cloud Memorystore for Redis instances +// +// Google Cloud Memorystore for Redis v1beta1 +// +// The redis.googleapis.com service implements the Google Cloud Memorystore +// for Redis API and defines the following resource model for managing Redis +// instances: +// +// The service works with a collection of cloud projects, named: /projects/* +// +// Each project has a collection of available locations, named: /locations/* +// +// Each location has a collection of Redis instances, named: /instances/* +// +// As such, Redis instances are resources of the form: +// /projects/{project_id}/locations/{location_id}/instances/{instance_id} +// +// Note that location_id must be refering to a GCP region; for example: +// +// projects/redpepper-1290/locations/us-central1/instances/my-redis +func NewCloudRedisClient(ctx context.Context, opts ...option.ClientOption) (*CloudRedisClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultCloudRedisClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &CloudRedisClient{ + conn: conn, + CallOptions: defaultCloudRedisCallOptions(), + + cloudRedisClient: redispb.NewCloudRedisClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *CloudRedisClient) 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 *CloudRedisClient) 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 *CloudRedisClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListInstances lists all Redis instances owned by a project in either the specified +// location (region) or all locations. +// +// The location should have the following format: +// +// projects/{project_id}/locations/{location_id} +// +// If location_id is specified as - (wildcard), then all regions +// available to the project are queried, and the results are aggregated. +func (c *CloudRedisClient) ListInstances(ctx context.Context, req *redispb.ListInstancesRequest, opts ...gax.CallOption) *InstanceIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListInstances[0:len(c.CallOptions.ListInstances):len(c.CallOptions.ListInstances)], opts...) + it := &InstanceIterator{} + req = proto.Clone(req).(*redispb.ListInstancesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*redispb.Instance, string, error) { + var resp *redispb.ListInstancesResponse + 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.cloudRedisClient.ListInstances(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Instances, 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 +} + +// GetInstance gets the details of a specific Redis instance. +func (c *CloudRedisClient) GetInstance(ctx context.Context, req *redispb.GetInstanceRequest, opts ...gax.CallOption) (*redispb.Instance, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetInstance[0:len(c.CallOptions.GetInstance):len(c.CallOptions.GetInstance)], opts...) + var resp *redispb.Instance + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.cloudRedisClient.GetInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateInstance creates a Redis instance based on the specified tier and memory size. +// +// By default, the instance is peered to the project's +// default network (at /compute/docs/networks-and-firewalls#networks). +// +// The creation is executed asynchronously and callers may check the returned +// operation to track its progress. Once the operation is completed the Redis +// instance will be fully functional. Completed longrunning.Operation will +// contain the new instance object in the response field. +// +// The returned operation is automatically deleted after a few hours, so there +// is no need to call DeleteOperation. +func (c *CloudRedisClient) CreateInstance(ctx context.Context, req *redispb.CreateInstanceRequest, opts ...gax.CallOption) (*CreateInstanceOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateInstance[0:len(c.CallOptions.CreateInstance):len(c.CallOptions.CreateInstance)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.cloudRedisClient.CreateInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &CreateInstanceOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// UpdateInstance updates the metadata and configuration of a specific Redis instance. +// +// Completed longrunning.Operation will contain the new instance object +// in the response field. The returned operation is automatically deleted +// after a few hours, so there is no need to call DeleteOperation. +func (c *CloudRedisClient) UpdateInstance(ctx context.Context, req *redispb.UpdateInstanceRequest, opts ...gax.CallOption) (*UpdateInstanceOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateInstance[0:len(c.CallOptions.UpdateInstance):len(c.CallOptions.UpdateInstance)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.cloudRedisClient.UpdateInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &UpdateInstanceOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// DeleteInstance deletes a specific Redis instance. Instance stops serving and data is +// deleted. +func (c *CloudRedisClient) DeleteInstance(ctx context.Context, req *redispb.DeleteInstanceRequest, opts ...gax.CallOption) (*DeleteInstanceOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteInstance[0:len(c.CallOptions.DeleteInstance):len(c.CallOptions.DeleteInstance)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.cloudRedisClient.DeleteInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &DeleteInstanceOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// InstanceIterator manages a stream of *redispb.Instance. +type InstanceIterator struct { + items []*redispb.Instance + 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 []*redispb.Instance, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *InstanceIterator) 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 *InstanceIterator) Next() (*redispb.Instance, error) { + var item *redispb.Instance + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *InstanceIterator) bufLen() int { + return len(it.items) +} + +func (it *InstanceIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// CreateInstanceOperation manages a long-running operation from CreateInstance. +type CreateInstanceOperation struct { + lro *longrunning.Operation +} + +// CreateInstanceOperation returns a new CreateInstanceOperation from a given name. +// The name must be that of a previously created CreateInstanceOperation, possibly from a different process. +func (c *CloudRedisClient) CreateInstanceOperation(name string) *CreateInstanceOperation { + return &CreateInstanceOperation{ + 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 *CreateInstanceOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*redispb.Instance, error) { + var resp redispb.Instance + if err := op.lro.WaitWithInterval(ctx, &resp, 360000*time.Millisecond, 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 *CreateInstanceOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*redispb.Instance, error) { + var resp redispb.Instance + 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 *CreateInstanceOperation) Metadata() (*anypb.Any, error) { + var meta anypb.Any + 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 *CreateInstanceOperation) 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 *CreateInstanceOperation) Name() string { + return op.lro.Name() +} + +// DeleteInstanceOperation manages a long-running operation from DeleteInstance. +type DeleteInstanceOperation struct { + lro *longrunning.Operation +} + +// DeleteInstanceOperation returns a new DeleteInstanceOperation from a given name. +// The name must be that of a previously created DeleteInstanceOperation, possibly from a different process. +func (c *CloudRedisClient) DeleteInstanceOperation(name string) *DeleteInstanceOperation { + return &DeleteInstanceOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *DeleteInstanceOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 360000*time.Millisecond, opts...) +} + +// 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. +func (op *DeleteInstanceOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *DeleteInstanceOperation) Metadata() (*anypb.Any, error) { + var meta anypb.Any + 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 *DeleteInstanceOperation) 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 *DeleteInstanceOperation) Name() string { + return op.lro.Name() +} + +// UpdateInstanceOperation manages a long-running operation from UpdateInstance. +type UpdateInstanceOperation struct { + lro *longrunning.Operation +} + +// UpdateInstanceOperation returns a new UpdateInstanceOperation from a given name. +// The name must be that of a previously created UpdateInstanceOperation, possibly from a different process. +func (c *CloudRedisClient) UpdateInstanceOperation(name string) *UpdateInstanceOperation { + return &UpdateInstanceOperation{ + 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 *UpdateInstanceOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*redispb.Instance, error) { + var resp redispb.Instance + if err := op.lro.WaitWithInterval(ctx, &resp, 360000*time.Millisecond, 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 *UpdateInstanceOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*redispb.Instance, error) { + var resp redispb.Instance + 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 *UpdateInstanceOperation) Metadata() (*anypb.Any, error) { + var meta anypb.Any + 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 *UpdateInstanceOperation) 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 *UpdateInstanceOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/redis/apiv1beta1/cloud_redis_client_example_test.go b/vendor/cloud.google.com/go/redis/apiv1beta1/cloud_redis_client_example_test.go new file mode 100644 index 0000000000..6d2eeb1bf9 --- /dev/null +++ b/vendor/cloud.google.com/go/redis/apiv1beta1/cloud_redis_client_example_test.go @@ -0,0 +1,141 @@ +// 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 redis_test + +import ( + "cloud.google.com/go/redis/apiv1beta1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + redispb "google.golang.org/genproto/googleapis/cloud/redis/v1beta1" +) + +func ExampleNewCloudRedisClient() { + ctx := context.Background() + c, err := redis.NewCloudRedisClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleCloudRedisClient_ListInstances() { + ctx := context.Background() + c, err := redis.NewCloudRedisClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &redispb.ListInstancesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListInstances(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleCloudRedisClient_GetInstance() { + ctx := context.Background() + c, err := redis.NewCloudRedisClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &redispb.GetInstanceRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleCloudRedisClient_CreateInstance() { + ctx := context.Background() + c, err := redis.NewCloudRedisClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &redispb.CreateInstanceRequest{ + // TODO: Fill request struct fields. + } + op, err := c.CreateInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleCloudRedisClient_UpdateInstance() { + ctx := context.Background() + c, err := redis.NewCloudRedisClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &redispb.UpdateInstanceRequest{ + // TODO: Fill request struct fields. + } + op, err := c.UpdateInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleCloudRedisClient_DeleteInstance() { + ctx := context.Background() + c, err := redis.NewCloudRedisClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &redispb.DeleteInstanceRequest{ + // TODO: Fill request struct fields. + } + op, err := c.DeleteInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} diff --git a/vendor/cloud.google.com/go/redis/apiv1beta1/doc.go b/vendor/cloud.google.com/go/redis/apiv1beta1/doc.go new file mode 100644 index 0000000000..e392a6dd2e --- /dev/null +++ b/vendor/cloud.google.com/go/redis/apiv1beta1/doc.go @@ -0,0 +1,48 @@ +// 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 redis is an auto-generated package for the +// Google Cloud Memorystore for Redis API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// The Google Cloud Memorystore for Redis API is used for creating and +// managing +// Redis instances on the Google Cloud Platform. +package redis // import "cloud.google.com/go/redis/apiv1beta1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/redis/apiv1beta1/mock_test.go b/vendor/cloud.google.com/go/redis/apiv1beta1/mock_test.go new file mode 100644 index 0000000000..3acf4402ca --- /dev/null +++ b/vendor/cloud.google.com/go/redis/apiv1beta1/mock_test.go @@ -0,0 +1,641 @@ +// 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 redis + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + redispb "google.golang.org/genproto/googleapis/cloud/redis/v1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + field_maskpb "google.golang.org/genproto/protobuf/field_mask" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockCloudRedisServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + redispb.CloudRedisServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockCloudRedisServer) ListInstances(ctx context.Context, req *redispb.ListInstancesRequest) (*redispb.ListInstancesResponse, 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].(*redispb.ListInstancesResponse), nil +} + +func (s *mockCloudRedisServer) GetInstance(ctx context.Context, req *redispb.GetInstanceRequest) (*redispb.Instance, 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].(*redispb.Instance), nil +} + +func (s *mockCloudRedisServer) CreateInstance(ctx context.Context, req *redispb.CreateInstanceRequest) (*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 *mockCloudRedisServer) UpdateInstance(ctx context.Context, req *redispb.UpdateInstanceRequest) (*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 *mockCloudRedisServer) DeleteInstance(ctx context.Context, req *redispb.DeleteInstanceRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockCloudRedis mockCloudRedisServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + redispb.RegisterCloudRedisServer(serv, &mockCloudRedis) + + 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 TestCloudRedisListInstances(t *testing.T) { + var nextPageToken string = "" + var instancesElement *redispb.Instance = &redispb.Instance{} + var instances = []*redispb.Instance{instancesElement} + var expectedResponse = &redispb.ListInstancesResponse{ + NextPageToken: nextPageToken, + Instances: instances, + } + + mockCloudRedis.err = nil + mockCloudRedis.reqs = nil + + mockCloudRedis.resps = append(mockCloudRedis.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]") + var request = &redispb.ListInstancesRequest{ + Parent: formattedParent, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInstances(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockCloudRedis.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Instances[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 TestCloudRedisListInstancesError(t *testing.T) { + errCode := codes.PermissionDenied + mockCloudRedis.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]") + var request = &redispb.ListInstancesRequest{ + Parent: formattedParent, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInstances(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 TestCloudRedisGetInstance(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var locationId string = "locationId552319461" + var alternativeLocationId string = "alternativeLocationId-718920621" + var redisVersion string = "redisVersion-685310444" + var reservedIpRange string = "reservedIpRange-1082940580" + var host string = "host3208616" + var port int32 = 3446913 + var currentLocationId string = "currentLocationId1312712735" + var statusMessage string = "statusMessage-239442758" + var memorySizeGb int32 = 34199707 + var authorizedNetwork string = "authorizedNetwork-1733809270" + var expectedResponse = &redispb.Instance{ + Name: name2, + DisplayName: displayName, + LocationId: locationId, + AlternativeLocationId: alternativeLocationId, + RedisVersion: redisVersion, + ReservedIpRange: reservedIpRange, + Host: host, + Port: port, + CurrentLocationId: currentLocationId, + StatusMessage: statusMessage, + MemorySizeGb: memorySizeGb, + AuthorizedNetwork: authorizedNetwork, + } + + mockCloudRedis.err = nil + mockCloudRedis.reqs = nil + + mockCloudRedis.resps = append(mockCloudRedis.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/instances/%s", "[PROJECT]", "[LOCATION]", "[INSTANCE]") + var request = &redispb.GetInstanceRequest{ + Name: formattedName, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInstance(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockCloudRedis.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 TestCloudRedisGetInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockCloudRedis.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/instances/%s", "[PROJECT]", "[LOCATION]", "[INSTANCE]") + var request = &redispb.GetInstanceRequest{ + Name: formattedName, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInstance(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 TestCloudRedisCreateInstance(t *testing.T) { + var name string = "name3373707" + var displayName string = "displayName1615086568" + var locationId string = "locationId552319461" + var alternativeLocationId string = "alternativeLocationId-718920621" + var redisVersion string = "redisVersion-685310444" + var reservedIpRange string = "reservedIpRange-1082940580" + var host string = "host3208616" + var port int32 = 3446913 + var currentLocationId string = "currentLocationId1312712735" + var statusMessage string = "statusMessage-239442758" + var memorySizeGb2 int32 = 1493816946 + var authorizedNetwork string = "authorizedNetwork-1733809270" + var expectedResponse = &redispb.Instance{ + Name: name, + DisplayName: displayName, + LocationId: locationId, + AlternativeLocationId: alternativeLocationId, + RedisVersion: redisVersion, + ReservedIpRange: reservedIpRange, + Host: host, + Port: port, + CurrentLocationId: currentLocationId, + StatusMessage: statusMessage, + MemorySizeGb: memorySizeGb2, + AuthorizedNetwork: authorizedNetwork, + } + + mockCloudRedis.err = nil + mockCloudRedis.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockCloudRedis.resps = append(mockCloudRedis.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]") + var instanceId string = "test_instance" + var tier redispb.Instance_Tier = redispb.Instance_BASIC + var memorySizeGb int32 = 1 + var instance = &redispb.Instance{ + Tier: tier, + MemorySizeGb: memorySizeGb, + } + var request = &redispb.CreateInstanceRequest{ + Parent: formattedParent, + InstanceId: instanceId, + Instance: instance, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateInstance(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, mockCloudRedis.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 TestCloudRedisCreateInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockCloudRedis.err = nil + mockCloudRedis.resps = append(mockCloudRedis.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/locations/%s", "[PROJECT]", "[LOCATION]") + var instanceId string = "test_instance" + var tier redispb.Instance_Tier = redispb.Instance_BASIC + var memorySizeGb int32 = 1 + var instance = &redispb.Instance{ + Tier: tier, + MemorySizeGb: memorySizeGb, + } + var request = &redispb.CreateInstanceRequest{ + Parent: formattedParent, + InstanceId: instanceId, + Instance: instance, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateInstance(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 TestCloudRedisUpdateInstance(t *testing.T) { + var name string = "name3373707" + var displayName2 string = "displayName21615000987" + var locationId string = "locationId552319461" + var alternativeLocationId string = "alternativeLocationId-718920621" + var redisVersion string = "redisVersion-685310444" + var reservedIpRange string = "reservedIpRange-1082940580" + var host string = "host3208616" + var port int32 = 3446913 + var currentLocationId string = "currentLocationId1312712735" + var statusMessage string = "statusMessage-239442758" + var memorySizeGb2 int32 = 1493816946 + var authorizedNetwork string = "authorizedNetwork-1733809270" + var expectedResponse = &redispb.Instance{ + Name: name, + DisplayName: displayName2, + LocationId: locationId, + AlternativeLocationId: alternativeLocationId, + RedisVersion: redisVersion, + ReservedIpRange: reservedIpRange, + Host: host, + Port: port, + CurrentLocationId: currentLocationId, + StatusMessage: statusMessage, + MemorySizeGb: memorySizeGb2, + AuthorizedNetwork: authorizedNetwork, + } + + mockCloudRedis.err = nil + mockCloudRedis.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockCloudRedis.resps = append(mockCloudRedis.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var pathsElement string = "display_name" + var pathsElement2 string = "memory_size_gb" + var paths = []string{pathsElement, pathsElement2} + var updateMask = &field_maskpb.FieldMask{ + Paths: paths, + } + var displayName string = "UpdatedDisplayName" + var memorySizeGb int32 = 4 + var instance = &redispb.Instance{ + DisplayName: displayName, + MemorySizeGb: memorySizeGb, + } + var request = &redispb.UpdateInstanceRequest{ + UpdateMask: updateMask, + Instance: instance, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateInstance(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, mockCloudRedis.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 TestCloudRedisUpdateInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockCloudRedis.err = nil + mockCloudRedis.resps = append(mockCloudRedis.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var pathsElement string = "display_name" + var pathsElement2 string = "memory_size_gb" + var paths = []string{pathsElement, pathsElement2} + var updateMask = &field_maskpb.FieldMask{ + Paths: paths, + } + var displayName string = "UpdatedDisplayName" + var memorySizeGb int32 = 4 + var instance = &redispb.Instance{ + DisplayName: displayName, + MemorySizeGb: memorySizeGb, + } + var request = &redispb.UpdateInstanceRequest{ + UpdateMask: updateMask, + Instance: instance, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateInstance(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 TestCloudRedisDeleteInstance(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockCloudRedis.err = nil + mockCloudRedis.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockCloudRedis.resps = append(mockCloudRedis.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/instances/%s", "[PROJECT]", "[LOCATION]", "[INSTANCE]") + var request = &redispb.DeleteInstanceRequest{ + Name: formattedName, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.DeleteInstance(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockCloudRedis.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestCloudRedisDeleteInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockCloudRedis.err = nil + mockCloudRedis.resps = append(mockCloudRedis.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var formattedName string = fmt.Sprintf("projects/%s/locations/%s/instances/%s", "[PROJECT]", "[LOCATION]", "[INSTANCE]") + var request = &redispb.DeleteInstanceRequest{ + Name: formattedName, + } + + c, err := NewCloudRedisClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.DeleteInstance(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} diff --git a/vendor/cloud.google.com/go/regen-gapic.sh b/vendor/cloud.google.com/go/regen-gapic.sh new file mode 100755 index 0000000000..0c23ea7066 --- /dev/null +++ b/vendor/cloud.google.com/go/regen-gapic.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# This script generates all GAPIC clients in this repo. +# One-time setup: +# cd path/to/googleapis # https://github.com/googleapis/googleapis +# virtualenv env +# . env/bin/activate +# pip install googleapis-artman +# deactivate +# +# Regenerate: +# cd path/to/googleapis +# . env/bin/activate +# $GOPATH/src/cloud.google.com/go/regen-gapic.sh +# deactivate +# +# Being in googleapis directory is important; +# that's where we find YAML files and where artman puts the "artman-genfiles" directory. +# +# NOTE: This script does not generate the "raw" gRPC client found in google.golang.org/genproto. +# To do that, use the regen.sh script in the genproto repo instead. + +set -ex + +APIS=( +google/iam/artman_iam_admin.yaml +google/cloud/bigquery/datatransfer/artman_bigquerydatatransfer.yaml +google/cloud/dataproc/artman_dataproc_v1.yaml +google/cloud/dialogflow/artman_dialogflow_v2.yaml +google/cloud/kms/artman_cloudkms.yaml +google/cloud/language/artman_language_v1.yaml +google/cloud/language/artman_language_v1beta2.yaml +google/cloud/oslogin/artman_oslogin_v1.yaml +google/cloud/oslogin/artman_oslogin_v1beta.yaml +google/cloud/redis/artman_redis_v1beta1.yaml +google/cloud/speech/artman_speech_v1.yaml +google/cloud/speech/artman_speech_v1beta1.yaml +google/cloud/speech/artman_speech_v1p1beta1.yaml +google/cloud/tasks/artman_cloudtasks.yaml +google/cloud/texttospeech/artman_texttospeech_v1.yaml +google/cloud/videointelligence/artman_videointelligence_v1.yaml +google/cloud/videointelligence/artman_videointelligence_v1beta1.yaml +google/cloud/videointelligence/artman_videointelligence_v1beta2.yaml +google/cloud/vision/artman_vision_v1.yaml +google/cloud/vision/artman_vision_v1p1beta1.yaml +google/container/artman_container.yaml +google/devtools/artman_clouddebugger.yaml +google/devtools/clouderrorreporting/artman_errorreporting.yaml +google/devtools/cloudtrace/artman_cloudtrace_v1.yaml +google/devtools/cloudtrace/artman_cloudtrace_v2.yaml +google/firestore/artman_firestore.yaml +google/logging/artman_logging.yaml +google/longrunning/artman_longrunning.yaml +google/monitoring/artman_monitoring.yaml +google/privacy/dlp/artman_dlp_v2.yaml +google/pubsub/artman_pubsub.yaml +google/spanner/admin/database/artman_spanner_admin_database.yaml +google/spanner/admin/instance/artman_spanner_admin_instance.yaml +google/spanner/artman_spanner.yaml +) + +for api in "${APIS[@]}"; do + rm -rf artman-genfiles/* + artman --config "$api" generate go_gapic + cp -r artman-genfiles/gapi-*/cloud.google.com/go/* $GOPATH/src/cloud.google.com/go/ +done + +#go list cloud.google.com/go/... | grep apiv | xargs go test + +#go test -short cloud.google.com/go/... + +#echo "googleapis version: $(git rev-parse HEAD)" diff --git a/vendor/cloud.google.com/go/rpcreplay/Makefile b/vendor/cloud.google.com/go/rpcreplay/Makefile new file mode 100644 index 0000000000..cb05a7b715 --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/Makefile @@ -0,0 +1,32 @@ +# Copyright 2017 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. + +# Makefile for building Go files from protos. + +# Change these to match your environment. +PROTOC=$(HOME)/bin/protoc +PROTOC_GO_PLUGIN_DIR=$(GOPATH)/bin +PROTOBUF_REPO=$(HOME)/git-repos/protobuf + +gen-protos: sync-protobuf + for d in proto/*; do \ + PATH=$(PATH):$(PROTOC_GO_PLUGIN_DIR) \ + $(PROTOC) --go_out=plugins=grpc:$$d \ + -I $$d -I $(PROTOBUF_REPO)/src $$d/*.proto; \ + done + + +sync-protobuf: + cd $(PROTOBUF_REPO); git pull + diff --git a/vendor/cloud.google.com/go/rpcreplay/doc.go b/vendor/cloud.google.com/go/rpcreplay/doc.go new file mode 100644 index 0000000000..90455555f2 --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/doc.go @@ -0,0 +1,145 @@ +// Copyright 2017 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 rpcreplay supports the capture and replay of gRPC calls. Its main goal is +to improve testing. Once you capture the calls of a test that runs against a real +service, you have an "automatic mock" that can be replayed against the same test, +yielding a unit test that is fast and flake-free. + +This package is EXPERIMENTAL and subject to change without notice. + + +Recording + +To record a sequence of gRPC calls to a file, create a Recorder and pass its +DialOptions to grpc.Dial: + + rec, err := rpcreplay.NewRecorder("service.replay", nil) + if err != nil { ... } + defer func() { + if err := rec.Close(); err != nil { ... } + }() + conn, err := grpc.Dial(serverAddress, rec.DialOptions()...) + +It is essential to close the Recorder when the interaction is finished. + +There is also a NewRecorderWriter function for capturing to an arbitrary +io.Writer. + + +Replaying + +Replaying a captured file looks almost identical: create a Replayer and use +its DialOptions. (Since we're reading the file and not writing it, we don't +have to be as careful about the error returned from Close). + + rep, err := rpcreplay.NewReplayer("service.replay") + if err != nil { ... } + defer rep.Close() + conn, err := grpc.Dial(serverAddress, rep.DialOptions()...) + + +Initial State + +A test might use random or time-sensitive values, for instance to create unique +resources for isolation from other tests. The test therefore has initial values, such +as the current time, or a random seed, that differ from run to run. You must record +this initial state and re-establish it on replay. + +To record the initial state, serialize it into a []byte and pass it as the second +argument to NewRecorder: + + timeNow := time.Now() + b, err := timeNow.MarshalBinary() + if err != nil { ... } + rec, err := rpcreplay.NewRecorder("service.replay", b) + +On replay, get the bytes from Replayer.Initial: + + rep, err := rpcreplay.NewReplayer("service.replay") + if err != nil { ... } + defer rep.Close() + err = timeNow.UnmarshalBinary(rep.Initial()) + if err != nil { ... } + + +Callbacks that modify what is saved and matched from the replay file + +Recorders and replayers have support for running callbacks before messages are written/read +from the replay file. A Recorder has a BeforeFunc that can modify a request or response +before it is written to the replay file. The actual RPCs sent to the service during recording +remain unaltered; only what is saved in the replay file can be changed.A Replayer has a +BeforeFunc that can modify a request before it is sent for matching. + +Example uses for these callbacks include customized logging, or scrubbing data +before RPCs are written to the replay file. If requests are modified by the callbacks during recording, +it is important to perform the same modifications to the requests when replaying, or RPC +matching on replay will fail. + +A common way to analyze and modify the various messages is to use a type switch. + + // Assume these types implement proto.Message. + type Greeting struct { + line string + } + + type Farewell struct { + line string + } + + func sayings(method string, msg proto.Message) error { + switch m := msg.(type) { + case Greeting: + msg.line = "Hi!" + return nil + case Farewell: + msg.line = "Bye bye!" + return nil + default: + return fmt.Errorf("unknown message type") + } + } + +Nondeterminism + +A nondeterministic program may invoke RPCs in a different order each time +it is run. The order in which RPCs are called during recording may differ +from the order during replay. + +The replayer matches incoming to recorded requests by method name and request +contents, so nondeterminism is only a concern for identical requests that result +in different responses. A nondeterministic program whose behavior differs +depending on the order of such RPCs probably has a race condition: since both the +recorded sequence of RPCs and the sequence during replay are valid orderings, the +program should behave the same under both. + + +Other Replayer Differences + +Besides the differences in replay mentioned above, other differences may cause issues +for some programs. We list them here. + +The Replayer delivers a response to an RPC immediately, without waiting for other +incoming RPCs. This can violate causality. For example, in a Pub/Sub program where +one goroutine publishes and another subscribes, during replay the Subscribe call may +finish before the Publish call begins. + +For streaming RPCs, the Replayer delivers the result of Send and Recv calls in +the order they were recorded. No attempt is made to match message contents. + +At present, this package does not record or replay stream headers and trailers, or +the result of the CloseSend method. +*/ +package rpcreplay // import "cloud.google.com/go/rpcreplay" diff --git a/vendor/cloud.google.com/go/rpcreplay/example_test.go b/vendor/cloud.google.com/go/rpcreplay/example_test.go new file mode 100644 index 0000000000..912db2065b --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/example_test.go @@ -0,0 +1,47 @@ +// Copyright 2017 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 rpcreplay_test + +var serverAddress string + +// func Example_NewRecorder() { +// rec, err := rpcreplay.NewRecorder("service.replay", nil) +// if err != nil { +// // TODO: Handle error. +// } +// defer func() { +// if err := rec.Close(); err != nil { +// // TODO: Handle error. +// } +// }() +// conn, err := grpc.Dial(serverAddress, rec.DialOptions()...) +// if err != nil { +// // TODO: Handle error. +// } +// _ = conn // TODO: use connection +// } + +// func Example_NewReplayer() { +// rep, err := rpcreplay.NewReplayer("service.replay") +// if err != nil { +// // TODO: Handle error. +// } +// defer rep.Close() +// conn, err := grpc.Dial(serverAddress, rep.DialOptions()...) +// if err != nil { +// // TODO: Handle error. +// } +// _ = conn // TODO: use connection +// } diff --git a/vendor/cloud.google.com/go/rpcreplay/fake_test.go b/vendor/cloud.google.com/go/rpcreplay/fake_test.go new file mode 100644 index 0000000000..bed9e1c791 --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/fake_test.go @@ -0,0 +1,122 @@ +// Copyright 2017 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 rpcreplay + +import ( + "io" + "log" + "net" + + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + pb "cloud.google.com/go/rpcreplay/proto/intstore" +) + +// intStoreServer is an in-memory implementation of IntStore. +type intStoreServer struct { + pb.IntStoreServer + + Addr string + l net.Listener + gsrv *grpc.Server + + items map[string]int32 +} + +func newIntStoreServer() *intStoreServer { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + log.Fatal(err) + } + s := &intStoreServer{ + Addr: l.Addr().String(), + l: l, + gsrv: grpc.NewServer(), + } + pb.RegisterIntStoreServer(s.gsrv, s) + go s.gsrv.Serve(s.l) + return s +} + +func (s *intStoreServer) stop() { + s.gsrv.Stop() + s.l.Close() +} + +func (s *intStoreServer) Set(_ context.Context, item *pb.Item) (*pb.SetResponse, error) { + old := s.setItem(item) + return &pb.SetResponse{PrevValue: old}, nil +} + +func (s *intStoreServer) setItem(item *pb.Item) int32 { + if s.items == nil { + s.items = map[string]int32{} + } + old := s.items[item.Name] + s.items[item.Name] = item.Value + return old +} + +func (s *intStoreServer) Get(_ context.Context, req *pb.GetRequest) (*pb.Item, error) { + val, ok := s.items[req.Name] + if !ok { + return nil, status.Errorf(codes.NotFound, "%q", req.Name) + } + return &pb.Item{Name: req.Name, Value: val}, nil +} + +func (s *intStoreServer) ListItems(_ *pb.ListItemsRequest, ss pb.IntStore_ListItemsServer) error { + for name, val := range s.items { + if err := ss.Send(&pb.Item{Name: name, Value: val}); err != nil { + return err + } + } + return nil +} + +func (s *intStoreServer) SetStream(ss pb.IntStore_SetStreamServer) error { + n := 0 + for { + item, err := ss.Recv() + if err == io.EOF { + break + } + if err != nil { + return err + } + s.setItem(item) + n++ + } + return ss.SendAndClose(&pb.Summary{Count: int32(n)}) +} + +func (s *intStoreServer) StreamChat(ss pb.IntStore_StreamChatServer) error { + for { + item, err := ss.Recv() + if err == io.EOF { + break + } + if err != nil { + return err + } + if err := ss.Send(item); err != nil { + return err + } + } + return nil +} diff --git a/vendor/cloud.google.com/go/rpcreplay/proto/intstore/intstore.pb.go b/vendor/cloud.google.com/go/rpcreplay/proto/intstore/intstore.pb.go new file mode 100644 index 0000000000..657ac2f8d5 --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/proto/intstore/intstore.pb.go @@ -0,0 +1,454 @@ +// Code generated by protoc-gen-go. +// source: intstore.proto +// DO NOT EDIT! + +/* +Package intstore is a generated protocol buffer package. + +It is generated from these files: + intstore.proto + +It has these top-level messages: + Item + SetResponse + GetRequest + Summary + ListItemsRequest +*/ +package intstore + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Item struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Value int32 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"` +} + +func (m *Item) Reset() { *m = Item{} } +func (m *Item) String() string { return proto.CompactTextString(m) } +func (*Item) ProtoMessage() {} +func (*Item) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Item) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Item) GetValue() int32 { + if m != nil { + return m.Value + } + return 0 +} + +type SetResponse struct { + PrevValue int32 `protobuf:"varint,1,opt,name=prev_value,json=prevValue" json:"prev_value,omitempty"` +} + +func (m *SetResponse) Reset() { *m = SetResponse{} } +func (m *SetResponse) String() string { return proto.CompactTextString(m) } +func (*SetResponse) ProtoMessage() {} +func (*SetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *SetResponse) GetPrevValue() int32 { + if m != nil { + return m.PrevValue + } + return 0 +} + +type GetRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *GetRequest) Reset() { *m = GetRequest{} } +func (m *GetRequest) String() string { return proto.CompactTextString(m) } +func (*GetRequest) ProtoMessage() {} +func (*GetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *GetRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type Summary struct { + Count int32 `protobuf:"varint,1,opt,name=count" json:"count,omitempty"` +} + +func (m *Summary) Reset() { *m = Summary{} } +func (m *Summary) String() string { return proto.CompactTextString(m) } +func (*Summary) ProtoMessage() {} +func (*Summary) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *Summary) GetCount() int32 { + if m != nil { + return m.Count + } + return 0 +} + +type ListItemsRequest struct { +} + +func (m *ListItemsRequest) Reset() { *m = ListItemsRequest{} } +func (m *ListItemsRequest) String() string { return proto.CompactTextString(m) } +func (*ListItemsRequest) ProtoMessage() {} +func (*ListItemsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func init() { + proto.RegisterType((*Item)(nil), "intstore.Item") + proto.RegisterType((*SetResponse)(nil), "intstore.SetResponse") + proto.RegisterType((*GetRequest)(nil), "intstore.GetRequest") + proto.RegisterType((*Summary)(nil), "intstore.Summary") + proto.RegisterType((*ListItemsRequest)(nil), "intstore.ListItemsRequest") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for IntStore service + +type IntStoreClient interface { + Set(ctx context.Context, in *Item, opts ...grpc.CallOption) (*SetResponse, error) + Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*Item, error) + // A server-to-client streaming RPC. + ListItems(ctx context.Context, in *ListItemsRequest, opts ...grpc.CallOption) (IntStore_ListItemsClient, error) + // A client-to-server streaming RPC. + SetStream(ctx context.Context, opts ...grpc.CallOption) (IntStore_SetStreamClient, error) + // A Bidirectional streaming RPC. + StreamChat(ctx context.Context, opts ...grpc.CallOption) (IntStore_StreamChatClient, error) +} + +type intStoreClient struct { + cc *grpc.ClientConn +} + +func NewIntStoreClient(cc *grpc.ClientConn) IntStoreClient { + return &intStoreClient{cc} +} + +func (c *intStoreClient) Set(ctx context.Context, in *Item, opts ...grpc.CallOption) (*SetResponse, error) { + out := new(SetResponse) + err := grpc.Invoke(ctx, "/intstore.IntStore/Set", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *intStoreClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*Item, error) { + out := new(Item) + err := grpc.Invoke(ctx, "/intstore.IntStore/Get", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *intStoreClient) ListItems(ctx context.Context, in *ListItemsRequest, opts ...grpc.CallOption) (IntStore_ListItemsClient, error) { + stream, err := grpc.NewClientStream(ctx, &_IntStore_serviceDesc.Streams[0], c.cc, "/intstore.IntStore/ListItems", opts...) + if err != nil { + return nil, err + } + x := &intStoreListItemsClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type IntStore_ListItemsClient interface { + Recv() (*Item, error) + grpc.ClientStream +} + +type intStoreListItemsClient struct { + grpc.ClientStream +} + +func (x *intStoreListItemsClient) Recv() (*Item, error) { + m := new(Item) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *intStoreClient) SetStream(ctx context.Context, opts ...grpc.CallOption) (IntStore_SetStreamClient, error) { + stream, err := grpc.NewClientStream(ctx, &_IntStore_serviceDesc.Streams[1], c.cc, "/intstore.IntStore/SetStream", opts...) + if err != nil { + return nil, err + } + x := &intStoreSetStreamClient{stream} + return x, nil +} + +type IntStore_SetStreamClient interface { + Send(*Item) error + CloseAndRecv() (*Summary, error) + grpc.ClientStream +} + +type intStoreSetStreamClient struct { + grpc.ClientStream +} + +func (x *intStoreSetStreamClient) Send(m *Item) error { + return x.ClientStream.SendMsg(m) +} + +func (x *intStoreSetStreamClient) CloseAndRecv() (*Summary, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(Summary) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *intStoreClient) StreamChat(ctx context.Context, opts ...grpc.CallOption) (IntStore_StreamChatClient, error) { + stream, err := grpc.NewClientStream(ctx, &_IntStore_serviceDesc.Streams[2], c.cc, "/intstore.IntStore/StreamChat", opts...) + if err != nil { + return nil, err + } + x := &intStoreStreamChatClient{stream} + return x, nil +} + +type IntStore_StreamChatClient interface { + Send(*Item) error + Recv() (*Item, error) + grpc.ClientStream +} + +type intStoreStreamChatClient struct { + grpc.ClientStream +} + +func (x *intStoreStreamChatClient) Send(m *Item) error { + return x.ClientStream.SendMsg(m) +} + +func (x *intStoreStreamChatClient) Recv() (*Item, error) { + m := new(Item) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// Server API for IntStore service + +type IntStoreServer interface { + Set(context.Context, *Item) (*SetResponse, error) + Get(context.Context, *GetRequest) (*Item, error) + // A server-to-client streaming RPC. + ListItems(*ListItemsRequest, IntStore_ListItemsServer) error + // A client-to-server streaming RPC. + SetStream(IntStore_SetStreamServer) error + // A Bidirectional streaming RPC. + StreamChat(IntStore_StreamChatServer) error +} + +func RegisterIntStoreServer(s *grpc.Server, srv IntStoreServer) { + s.RegisterService(&_IntStore_serviceDesc, srv) +} + +func _IntStore_Set_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Item) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IntStoreServer).Set(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/intstore.IntStore/Set", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IntStoreServer).Set(ctx, req.(*Item)) + } + return interceptor(ctx, in, info, handler) +} + +func _IntStore_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IntStoreServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/intstore.IntStore/Get", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IntStoreServer).Get(ctx, req.(*GetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _IntStore_ListItems_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListItemsRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(IntStoreServer).ListItems(m, &intStoreListItemsServer{stream}) +} + +type IntStore_ListItemsServer interface { + Send(*Item) error + grpc.ServerStream +} + +type intStoreListItemsServer struct { + grpc.ServerStream +} + +func (x *intStoreListItemsServer) Send(m *Item) error { + return x.ServerStream.SendMsg(m) +} + +func _IntStore_SetStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(IntStoreServer).SetStream(&intStoreSetStreamServer{stream}) +} + +type IntStore_SetStreamServer interface { + SendAndClose(*Summary) error + Recv() (*Item, error) + grpc.ServerStream +} + +type intStoreSetStreamServer struct { + grpc.ServerStream +} + +func (x *intStoreSetStreamServer) SendAndClose(m *Summary) error { + return x.ServerStream.SendMsg(m) +} + +func (x *intStoreSetStreamServer) Recv() (*Item, error) { + m := new(Item) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _IntStore_StreamChat_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(IntStoreServer).StreamChat(&intStoreStreamChatServer{stream}) +} + +type IntStore_StreamChatServer interface { + Send(*Item) error + Recv() (*Item, error) + grpc.ServerStream +} + +type intStoreStreamChatServer struct { + grpc.ServerStream +} + +func (x *intStoreStreamChatServer) Send(m *Item) error { + return x.ServerStream.SendMsg(m) +} + +func (x *intStoreStreamChatServer) Recv() (*Item, error) { + m := new(Item) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _IntStore_serviceDesc = grpc.ServiceDesc{ + ServiceName: "intstore.IntStore", + HandlerType: (*IntStoreServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Set", + Handler: _IntStore_Set_Handler, + }, + { + MethodName: "Get", + Handler: _IntStore_Get_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "ListItems", + Handler: _IntStore_ListItems_Handler, + ServerStreams: true, + }, + { + StreamName: "SetStream", + Handler: _IntStore_SetStream_Handler, + ClientStreams: true, + }, + { + StreamName: "StreamChat", + Handler: _IntStore_StreamChat_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "intstore.proto", +} + +func init() { proto.RegisterFile("intstore.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 273 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x91, 0x4f, 0x4b, 0xc3, 0x40, + 0x10, 0xc5, 0xb3, 0xfd, 0xa3, 0xcd, 0x08, 0x45, 0x87, 0x0a, 0x25, 0x20, 0x86, 0x3d, 0xe5, 0xa0, + 0x21, 0xd4, 0xa3, 0x47, 0x0f, 0xa5, 0xe0, 0x29, 0x0b, 0x5e, 0x25, 0xca, 0x80, 0x05, 0xb3, 0x1b, + 0x77, 0x27, 0x05, 0xbf, 0x84, 0x9f, 0x59, 0x36, 0x5b, 0x9b, 0xd2, 0x78, 0xdb, 0xb7, 0xf3, 0x66, + 0xde, 0x6f, 0x76, 0x61, 0xbe, 0xd5, 0xec, 0xd8, 0x58, 0xca, 0x1b, 0x6b, 0xd8, 0xe0, 0xec, 0x4f, + 0xcb, 0x02, 0x26, 0x1b, 0xa6, 0x1a, 0x11, 0x26, 0xba, 0xaa, 0x69, 0x29, 0x52, 0x91, 0xc5, 0x65, + 0x77, 0xc6, 0x05, 0x4c, 0x77, 0xd5, 0x67, 0x4b, 0xcb, 0x51, 0x2a, 0xb2, 0x69, 0x19, 0x84, 0xbc, + 0x83, 0x0b, 0x45, 0x5c, 0x92, 0x6b, 0x8c, 0x76, 0x84, 0x37, 0x00, 0x8d, 0xa5, 0xdd, 0x6b, 0x70, + 0x8a, 0xce, 0x19, 0xfb, 0x9b, 0x97, 0xce, 0x9d, 0x02, 0xac, 0xbd, 0xfb, 0xab, 0x25, 0xc7, 0xff, + 0xa5, 0xc8, 0x5b, 0x38, 0x57, 0x6d, 0x5d, 0x57, 0xf6, 0xdb, 0x07, 0xbe, 0x9b, 0x56, 0xf3, 0x7e, + 0x4c, 0x10, 0x12, 0xe1, 0xf2, 0x79, 0xeb, 0xd8, 0x63, 0xba, 0xfd, 0xa0, 0xd5, 0xcf, 0x08, 0x66, + 0x1b, 0xcd, 0xca, 0xef, 0x80, 0x39, 0x8c, 0x15, 0x31, 0xce, 0xf3, 0xc3, 0x96, 0xde, 0x9b, 0x5c, + 0xf7, 0xfa, 0x08, 0x58, 0x46, 0x78, 0x0f, 0xe3, 0x35, 0x31, 0x2e, 0xfa, 0x7a, 0x8f, 0x98, 0x9c, + 0x4c, 0x91, 0x11, 0x3e, 0x42, 0x7c, 0xc8, 0xc7, 0xa4, 0x2f, 0x9f, 0x42, 0x0d, 0x5b, 0x0b, 0x81, + 0x2b, 0x88, 0x15, 0xb1, 0x62, 0x4b, 0x55, 0x3d, 0x20, 0xbc, 0x3a, 0x22, 0x0c, 0x4f, 0x20, 0xa3, + 0xcc, 0xf7, 0x40, 0x68, 0x78, 0xfa, 0xa8, 0x86, 0x6b, 0x0d, 0x52, 0x32, 0x51, 0x88, 0xb7, 0xb3, + 0xee, 0x63, 0x1f, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x22, 0x28, 0xa0, 0x49, 0xea, 0x01, 0x00, + 0x00, +} diff --git a/vendor/cloud.google.com/go/rpcreplay/proto/intstore/intstore.proto b/vendor/cloud.google.com/go/rpcreplay/proto/intstore/intstore.proto new file mode 100644 index 0000000000..b3f6643190 --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/proto/intstore/intstore.proto @@ -0,0 +1,54 @@ +// Copyright 2017 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. + +// IntStore is a service for testing the rpcreplay package. +// It is a simple key-value store for integers. + +syntax = "proto3"; + +package intstore; + +service IntStore { + rpc Set(Item) returns (SetResponse) {} + + rpc Get(GetRequest) returns (Item) {} + + // A server-to-client streaming RPC. + rpc ListItems(ListItemsRequest) returns (stream Item) {} + + // A client-to-server streaming RPC. + rpc SetStream(stream Item) returns (Summary) {} + + // A Bidirectional streaming RPC. + rpc StreamChat(stream Item) returns (stream Item) {} +} + +message Item { + string name = 1; + int32 value = 2; +} + +message SetResponse { + int32 prev_value = 1; +} + +message GetRequest { + string name = 1; +} + +message Summary { + int32 count = 1; +} + +message ListItemsRequest {} diff --git a/vendor/cloud.google.com/go/rpcreplay/proto/rpcreplay/rpcreplay.pb.go b/vendor/cloud.google.com/go/rpcreplay/proto/rpcreplay/rpcreplay.pb.go new file mode 100644 index 0000000000..8e76a3951d --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/proto/rpcreplay/rpcreplay.pb.go @@ -0,0 +1,170 @@ +// Code generated by protoc-gen-go. +// source: rpcreplay.proto +// DO NOT EDIT! + +/* +Package rpcreplay is a generated protocol buffer package. + +It is generated from these files: + rpcreplay.proto + +It has these top-level messages: + Entry +*/ +package rpcreplay + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/ptypes/any" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Entry_Kind int32 + +const ( + Entry_TYPE_UNSPECIFIED Entry_Kind = 0 + // A unary request. + // method: the full name of the method + // message: the request proto + // is_error: false + // ref_index: 0 + Entry_REQUEST Entry_Kind = 1 + // A unary response. + // method: the full name of the method + // message: + // if is_error: a google.rpc.Status proto + // else: the response proto + // ref_index: index in the sequence of Entries of matching request (1-based) + Entry_RESPONSE Entry_Kind = 2 + // A method that creates a stream. + // method: the full name of the method + // message: + // if is_error: a google.rpc.Status proto + // else: nil + // ref_index: 0 + Entry_CREATE_STREAM Entry_Kind = 3 + // A call to Send on the client returned by a stream-creating method. + // method: unset + // message: the proto being sent + // is_error: false + // ref_index: index of matching CREATE_STREAM entry (1-based) + Entry_SEND Entry_Kind = 4 + // A call to Recv on the client returned by a stream-creating method. + // method: unset + // message: + // if is_error: a google.rpc.Status proto, or nil on EOF + // else: the received message + // ref_index: index of matching CREATE_STREAM entry + Entry_RECV Entry_Kind = 5 +) + +var Entry_Kind_name = map[int32]string{ + 0: "TYPE_UNSPECIFIED", + 1: "REQUEST", + 2: "RESPONSE", + 3: "CREATE_STREAM", + 4: "SEND", + 5: "RECV", +} +var Entry_Kind_value = map[string]int32{ + "TYPE_UNSPECIFIED": 0, + "REQUEST": 1, + "RESPONSE": 2, + "CREATE_STREAM": 3, + "SEND": 4, + "RECV": 5, +} + +func (x Entry_Kind) String() string { + return proto.EnumName(Entry_Kind_name, int32(x)) +} +func (Entry_Kind) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } + +// An Entry represents a single RPC activity, typically a request or response. +type Entry struct { + Kind Entry_Kind `protobuf:"varint,1,opt,name=kind,enum=rpcreplay.Entry_Kind" json:"kind,omitempty"` + Method string `protobuf:"bytes,2,opt,name=method" json:"method,omitempty"` + Message *google_protobuf.Any `protobuf:"bytes,3,opt,name=message" json:"message,omitempty"` + IsError bool `protobuf:"varint,4,opt,name=is_error,json=isError" json:"is_error,omitempty"` + RefIndex int32 `protobuf:"varint,5,opt,name=ref_index,json=refIndex" json:"ref_index,omitempty"` +} + +func (m *Entry) Reset() { *m = Entry{} } +func (m *Entry) String() string { return proto.CompactTextString(m) } +func (*Entry) ProtoMessage() {} +func (*Entry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Entry) GetKind() Entry_Kind { + if m != nil { + return m.Kind + } + return Entry_TYPE_UNSPECIFIED +} + +func (m *Entry) GetMethod() string { + if m != nil { + return m.Method + } + return "" +} + +func (m *Entry) GetMessage() *google_protobuf.Any { + if m != nil { + return m.Message + } + return nil +} + +func (m *Entry) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +func (m *Entry) GetRefIndex() int32 { + if m != nil { + return m.RefIndex + } + return 0 +} + +func init() { + proto.RegisterType((*Entry)(nil), "rpcreplay.Entry") + proto.RegisterEnum("rpcreplay.Entry_Kind", Entry_Kind_name, Entry_Kind_value) +} + +func init() { proto.RegisterFile("rpcreplay.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 289 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x44, 0x8e, 0xdf, 0x4e, 0xc2, 0x30, + 0x14, 0xc6, 0x2d, 0x6c, 0x30, 0x0e, 0xfe, 0xa9, 0x0d, 0x9a, 0xa1, 0x37, 0x0b, 0x57, 0xf3, 0xa6, + 0x24, 0xf8, 0x04, 0x04, 0x8e, 0x09, 0x31, 0x22, 0xb6, 0xc3, 0xc4, 0x1b, 0x17, 0x70, 0x05, 0x17, + 0xa1, 0x25, 0xdd, 0x4c, 0xdc, 0x6b, 0xf8, 0xc4, 0x66, 0x13, 0xf4, 0xae, 0xbf, 0x7e, 0xbf, 0x9c, + 0xef, 0x83, 0x33, 0xbb, 0x7b, 0xb3, 0x6a, 0xb7, 0x59, 0x14, 0x7c, 0x67, 0x4d, 0x6e, 0x58, 0xeb, + 0xef, 0xe3, 0xaa, 0xbb, 0x36, 0x66, 0xbd, 0x51, 0xfd, 0x2a, 0x58, 0x7e, 0xae, 0xfa, 0x0b, 0xbd, + 0xb7, 0x7a, 0xdf, 0x35, 0x70, 0x51, 0xe7, 0xb6, 0x60, 0x37, 0xe0, 0x7c, 0xa4, 0x3a, 0xf1, 0x49, + 0x40, 0xc2, 0xd3, 0xc1, 0x05, 0xff, 0xbf, 0x57, 0xe5, 0xfc, 0x3e, 0xd5, 0x89, 0xa8, 0x14, 0x76, + 0x09, 0x8d, 0xad, 0xca, 0xdf, 0x4d, 0xe2, 0xd7, 0x02, 0x12, 0xb6, 0xc4, 0x9e, 0x18, 0x87, 0xe6, + 0x56, 0x65, 0xd9, 0x62, 0xad, 0xfc, 0x7a, 0x40, 0xc2, 0xf6, 0xa0, 0xc3, 0x7f, 0x9b, 0xf9, 0xa1, + 0x99, 0x0f, 0x75, 0x21, 0x0e, 0x12, 0xeb, 0x82, 0x97, 0x66, 0xb1, 0xb2, 0xd6, 0x58, 0xdf, 0x09, + 0x48, 0xe8, 0x89, 0x66, 0x9a, 0x61, 0x89, 0xec, 0x1a, 0x5a, 0x56, 0xad, 0xe2, 0x54, 0x27, 0xea, + 0xcb, 0x77, 0x03, 0x12, 0xba, 0xc2, 0xb3, 0x6a, 0x35, 0x29, 0xb9, 0xf7, 0x0a, 0x4e, 0xb9, 0x86, + 0x75, 0x80, 0x46, 0x2f, 0x33, 0x8c, 0xe7, 0x53, 0x39, 0xc3, 0xd1, 0xe4, 0x6e, 0x82, 0x63, 0x7a, + 0xc4, 0xda, 0xd0, 0x14, 0xf8, 0x34, 0x47, 0x19, 0x51, 0xc2, 0x8e, 0xc1, 0x13, 0x28, 0x67, 0x8f, + 0x53, 0x89, 0xb4, 0xc6, 0xce, 0xe1, 0x64, 0x24, 0x70, 0x18, 0x61, 0x2c, 0x23, 0x81, 0xc3, 0x07, + 0x5a, 0x67, 0x1e, 0x38, 0x12, 0xa7, 0x63, 0xea, 0x94, 0x2f, 0x81, 0xa3, 0x67, 0xea, 0x2e, 0x1b, + 0xd5, 0xdc, 0xdb, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe7, 0x9b, 0x9d, 0x4f, 0x54, 0x01, 0x00, + 0x00, +} diff --git a/vendor/cloud.google.com/go/rpcreplay/proto/rpcreplay/rpcreplay.proto b/vendor/cloud.google.com/go/rpcreplay/proto/rpcreplay/rpcreplay.proto new file mode 100644 index 0000000000..8abbff92fa --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/proto/rpcreplay/rpcreplay.proto @@ -0,0 +1,71 @@ +// Copyright 2017 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. + +syntax = "proto3"; + +package rpcreplay; + +import "google/protobuf/any.proto"; + +// An Entry represents a single RPC activity, typically a request or response. +message Entry { + enum Kind { + TYPE_UNSPECIFIED = 0; + + // A unary request. + // method: the full name of the method + // message: the request proto + // is_error: false + // ref_index: 0 + REQUEST = 1; + + // A unary response. + // method: the full name of the method + // message: + // if is_error: a google.rpc.Status proto + // else: the response proto + // ref_index: index in the sequence of Entries of matching request (1-based) + RESPONSE = 2; + + // A method that creates a stream. + // method: the full name of the method + // message: + // if is_error: a google.rpc.Status proto + // else: nil + // ref_index: 0 + CREATE_STREAM = 3; + + // A call to Send on the client returned by a stream-creating method. + // method: unset + // message: the proto being sent + // is_error: false + // ref_index: index of matching CREATE_STREAM entry (1-based) + SEND = 4; // message sent on stream + + // A call to Recv on the client returned by a stream-creating method. + // method: unset + // message: + // if is_error: a google.rpc.Status proto, or nil on EOF + // else: the received message + // ref_index: index of matching CREATE_STREAM entry + RECV = 5; // message received from stream + } + + Kind kind = 1; + string method = 2; // method name + google.protobuf.Any message = 3; // request, response or error status + bool is_error = 4; // was response an error? + int32 ref_index = 5; // for RESPONSE, index of matching request; + // for SEND/RECV, index of CREATE_STREAM +} diff --git a/vendor/cloud.google.com/go/rpcreplay/rpcreplay.go b/vendor/cloud.google.com/go/rpcreplay/rpcreplay.go new file mode 100644 index 0000000000..dc84252ee7 --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/rpcreplay.go @@ -0,0 +1,718 @@ +// Copyright 2017 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 rpcreplay + +import ( + "bufio" + "encoding/binary" + "errors" + "fmt" + "io" + "log" + "os" + "sync" + + "golang.org/x/net/context" + + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + + pb "cloud.google.com/go/rpcreplay/proto/rpcreplay" + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/any" + spb "google.golang.org/genproto/googleapis/rpc/status" +) + +// A Recorder records RPCs for later playback. +type Recorder struct { + mu sync.Mutex + w *bufio.Writer + f *os.File + next int + err error + // BeforeFunc defines a function that can inspect and modify requests and responses + // written to the replay file. It does not modify messages sent to the service. + // It is run once before a request is written to the replay file, and once before a response + // is written to the replay file. + // The function is called with the method name and the message that triggered the callback. + // If the function returns an error, the error will be returned to the client. + // This is only executed for unary RPCs; streaming RPCs are not supported. + BeforeFunc func(string, proto.Message) error +} + +// NewRecorder creates a recorder that writes to filename. The file will +// also store the initial bytes for retrieval during replay. +// +// You must call Close on the Recorder to ensure that all data is written. +func NewRecorder(filename string, initial []byte) (*Recorder, error) { + f, err := os.Create(filename) + if err != nil { + return nil, err + } + rec, err := NewRecorderWriter(f, initial) + if err != nil { + _ = f.Close() + return nil, err + } + rec.f = f + return rec, nil +} + +// NewRecorderWriter creates a recorder that writes to w. The initial +// bytes will also be written to w for retrieval during replay. +// +// You must call Close on the Recorder to ensure that all data is written. +func NewRecorderWriter(w io.Writer, initial []byte) (*Recorder, error) { + bw := bufio.NewWriter(w) + if err := writeHeader(bw, initial); err != nil { + return nil, err + } + return &Recorder{w: bw, next: 1}, nil +} + +// DialOptions returns the options that must be passed to grpc.Dial +// to enable recording. +func (r *Recorder) DialOptions() []grpc.DialOption { + return []grpc.DialOption{ + grpc.WithUnaryInterceptor(r.interceptUnary), + grpc.WithStreamInterceptor(r.interceptStream), + } +} + +// Close saves any unwritten information. +func (r *Recorder) Close() error { + r.mu.Lock() + defer r.mu.Unlock() + if r.err != nil { + return r.err + } + err := r.w.Flush() + if r.f != nil { + if err2 := r.f.Close(); err == nil { + err = err2 + } + } + return err +} + +// Intercepts all unary (non-stream) RPCs. +func (r *Recorder) interceptUnary(ctx context.Context, method string, req, res interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + ereq := &entry{ + kind: pb.Entry_REQUEST, + method: method, + msg: message{msg: proto.Clone(req.(proto.Message))}, + } + + if r.BeforeFunc != nil { + if err := r.BeforeFunc(method, ereq.msg.msg); err != nil { + return err + } + } + refIndex, err := r.writeEntry(ereq) + if err != nil { + return err + } + ierr := invoker(ctx, method, req, res, cc, opts...) + eres := &entry{ + kind: pb.Entry_RESPONSE, + refIndex: refIndex, + } + // If the error is not a gRPC status, then something more + // serious is wrong. More significantly, we have no way + // of serializing an arbitrary error. So just return it + // without recording the response. + if _, ok := status.FromError(ierr); !ok { + r.mu.Lock() + r.err = fmt.Errorf("saw non-status error in %s response: %v (%T)", method, ierr, ierr) + r.mu.Unlock() + return ierr + } + eres.msg.set(proto.Clone(res.(proto.Message)), ierr) + if r.BeforeFunc != nil { + if err := r.BeforeFunc(method, eres.msg.msg); err != nil { + return err + } + } + if _, err := r.writeEntry(eres); err != nil { + return err + } + return ierr +} + +func (r *Recorder) writeEntry(e *entry) (int, error) { + r.mu.Lock() + defer r.mu.Unlock() + if r.err != nil { + return 0, r.err + } + err := writeEntry(r.w, e) + if err != nil { + r.err = err + return 0, err + } + n := r.next + r.next++ + return n, nil +} + +func (r *Recorder) interceptStream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + cstream, serr := streamer(ctx, desc, cc, method, opts...) + e := &entry{ + kind: pb.Entry_CREATE_STREAM, + method: method, + } + e.msg.set(nil, serr) + refIndex, err := r.writeEntry(e) + if err != nil { + return nil, err + } + return &recClientStream{ + ctx: ctx, + rec: r, + cstream: cstream, + refIndex: refIndex, + }, serr +} + +// A recClientStream implements the gprc.ClientStream interface. +// It behaves exactly like the default ClientStream, but also +// records all messages sent and received. +type recClientStream struct { + ctx context.Context + rec *Recorder + cstream grpc.ClientStream + refIndex int +} + +func (rcs *recClientStream) Context() context.Context { return rcs.ctx } + +func (rcs *recClientStream) SendMsg(m interface{}) error { + serr := rcs.cstream.SendMsg(m) + e := &entry{ + kind: pb.Entry_SEND, + refIndex: rcs.refIndex, + } + e.msg.set(m, serr) + if _, err := rcs.rec.writeEntry(e); err != nil { + return err + } + return serr +} + +func (rcs *recClientStream) RecvMsg(m interface{}) error { + serr := rcs.cstream.RecvMsg(m) + e := &entry{ + kind: pb.Entry_RECV, + refIndex: rcs.refIndex, + } + e.msg.set(m, serr) + if _, err := rcs.rec.writeEntry(e); err != nil { + return err + } + return serr +} + +func (rcs *recClientStream) Header() (metadata.MD, error) { + // TODO(jba): record. + return rcs.cstream.Header() +} + +func (rcs *recClientStream) Trailer() metadata.MD { + // TODO(jba): record. + return rcs.cstream.Trailer() +} + +func (rcs *recClientStream) CloseSend() error { + // TODO(jba): record. + return rcs.cstream.CloseSend() +} + +// A Replayer replays a set of RPCs saved by a Recorder. +type Replayer struct { + initial []byte // initial state + log func(format string, v ...interface{}) // for debugging + + mu sync.Mutex + calls []*call + streams []*stream + // BeforeFunc defines a function that can inspect and modify requests before they + // are matched for responses from the replay file. + // The function is called with the method name and the message that triggered the callback. + // If the function returns an error, the error will be returned to the client. + // This is only executed for unary RPCs; streaming RPCs are not supported. + BeforeFunc func(string, proto.Message) error +} + +// A call represents a unary RPC, with a request and response (or error). +type call struct { + method string + request proto.Message + response message +} + +// A stream represents a gRPC stream, with an initial create-stream call, followed by +// zero or more sends and/or receives. +type stream struct { + method string + createIndex int + createErr error // error from create call + sends []message + recvs []message +} + +// NewReplayer creates a Replayer that reads from filename. +func NewReplayer(filename string) (*Replayer, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + return NewReplayerReader(f) +} + +// NewReplayerReader creates a Replayer that reads from r. +func NewReplayerReader(r io.Reader) (*Replayer, error) { + rep := &Replayer{ + log: func(string, ...interface{}) {}, + } + if err := rep.read(r); err != nil { + return nil, err + } + return rep, nil +} + +// read reads the stream of recorded entries. +// It matches requests with responses, with each pair grouped +// into a call struct. +func (rep *Replayer) read(r io.Reader) error { + r = bufio.NewReader(r) + bytes, err := readHeader(r) + if err != nil { + return err + } + rep.initial = bytes + + callsByIndex := map[int]*call{} + streamsByIndex := map[int]*stream{} + for i := 1; ; i++ { + e, err := readEntry(r) + if err != nil { + return err + } + if e == nil { + break + } + switch e.kind { + case pb.Entry_REQUEST: + callsByIndex[i] = &call{ + method: e.method, + request: e.msg.msg, + } + + case pb.Entry_RESPONSE: + call := callsByIndex[e.refIndex] + if call == nil { + return fmt.Errorf("replayer: no request for response #%d", i) + } + delete(callsByIndex, e.refIndex) + call.response = e.msg + rep.calls = append(rep.calls, call) + + case pb.Entry_CREATE_STREAM: + s := &stream{method: e.method, createIndex: i} + s.createErr = e.msg.err + streamsByIndex[i] = s + rep.streams = append(rep.streams, s) + + case pb.Entry_SEND: + s := streamsByIndex[e.refIndex] + if s == nil { + return fmt.Errorf("replayer: no stream for send #%d", i) + } + s.sends = append(s.sends, e.msg) + + case pb.Entry_RECV: + s := streamsByIndex[e.refIndex] + if s == nil { + return fmt.Errorf("replayer: no stream for recv #%d", i) + } + s.recvs = append(s.recvs, e.msg) + + default: + return fmt.Errorf("replayer: unknown kind %s", e.kind) + } + } + if len(callsByIndex) > 0 { + return fmt.Errorf("replayer: %d unmatched requests", len(callsByIndex)) + } + return nil +} + +// DialOptions returns the options that must be passed to grpc.Dial +// to enable replaying. +func (r *Replayer) DialOptions() []grpc.DialOption { + return []grpc.DialOption{ + // On replay, we make no RPCs, which means the connection may be closed + // before the normally async Dial completes. Making the Dial synchronous + // fixes that. + grpc.WithBlock(), + grpc.WithUnaryInterceptor(r.interceptUnary), + grpc.WithStreamInterceptor(r.interceptStream), + } +} + +// Initial returns the initial state saved by the Recorder. +func (r *Replayer) Initial() []byte { return r.initial } + +// SetLogFunc sets a function to be used for debug logging. The function +// should be safe to be called from multiple goroutines. +func (r *Replayer) SetLogFunc(f func(format string, v ...interface{})) { + r.log = f +} + +// Close closes the Replayer. +func (r *Replayer) Close() error { + return nil +} + +func (r *Replayer) interceptUnary(_ context.Context, method string, req, res interface{}, _ *grpc.ClientConn, _ grpc.UnaryInvoker, _ ...grpc.CallOption) error { + mreq := req.(proto.Message) + if r.BeforeFunc != nil { + if err := r.BeforeFunc(method, mreq); err != nil { + return err + } + } + r.log("request %s (%s)", method, req) + call := r.extractCall(method, mreq) + if call == nil { + return fmt.Errorf("replayer: request not found: %s", mreq) + } + r.log("returning %v", call.response) + if call.response.err != nil { + return call.response.err + } + proto.Merge(res.(proto.Message), call.response.msg) // copy msg into res + return nil +} + +func (r *Replayer) interceptStream(ctx context.Context, _ *grpc.StreamDesc, _ *grpc.ClientConn, method string, _ grpc.Streamer, _ ...grpc.CallOption) (grpc.ClientStream, error) { + r.log("create-stream %s", method) + str := r.extractStream(method) + if str == nil { + return nil, fmt.Errorf("replayer: stream not found for method %s", method) + } + if str.createErr != nil { + return nil, str.createErr + } + return &repClientStream{ctx: ctx, str: str}, nil +} + +type repClientStream struct { + ctx context.Context + str *stream +} + +func (rcs *repClientStream) Context() context.Context { return rcs.ctx } + +func (rcs *repClientStream) SendMsg(m interface{}) error { + if len(rcs.str.sends) == 0 { + return fmt.Errorf("replayer: no more sends for stream %s, created at index %d", + rcs.str.method, rcs.str.createIndex) + } + // TODO(jba): Do not assume that the sends happen in the same order on replay. + msg := rcs.str.sends[0] + rcs.str.sends = rcs.str.sends[1:] + return msg.err +} + +func (rcs *repClientStream) RecvMsg(m interface{}) error { + if len(rcs.str.recvs) == 0 { + return fmt.Errorf("replayer: no more receives for stream %s, created at index %d", + rcs.str.method, rcs.str.createIndex) + } + msg := rcs.str.recvs[0] + rcs.str.recvs = rcs.str.recvs[1:] + if msg.err != nil { + return msg.err + } + proto.Merge(m.(proto.Message), msg.msg) // copy msg into m + return nil +} + +func (rcs *repClientStream) Header() (metadata.MD, error) { + log.Printf("replay: stream metadata not supported") + return nil, nil +} + +func (rcs *repClientStream) Trailer() metadata.MD { + log.Printf("replay: stream metadata not supported") + return nil +} + +func (rcs *repClientStream) CloseSend() error { + return nil +} + +// extractCall finds the first call in the list with the same method +// and request. It returns nil if it can't find such a call. +func (r *Replayer) extractCall(method string, req proto.Message) *call { + r.mu.Lock() + defer r.mu.Unlock() + for i, call := range r.calls { + if call == nil { + continue + } + if method == call.method && proto.Equal(req, call.request) { + r.calls[i] = nil // nil out this call so we don't reuse it + return call + } + } + return nil +} + +func (r *Replayer) extractStream(method string) *stream { + r.mu.Lock() + defer r.mu.Unlock() + for i, stream := range r.streams { + if stream == nil { + continue + } + if method == stream.method { + r.streams[i] = nil + return stream + } + } + return nil +} + +// Fprint reads the entries from filename and writes them to w in human-readable form. +// It is intended for debugging. +func Fprint(w io.Writer, filename string) error { + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + return FprintReader(w, f) +} + +// FprintReader reads the entries from r and writes them to w in human-readable form. +// It is intended for debugging. +func FprintReader(w io.Writer, r io.Reader) error { + initial, err := readHeader(r) + if err != nil { + return err + } + fmt.Fprintf(w, "initial state: %q\n", string(initial)) + for i := 1; ; i++ { + e, err := readEntry(r) + if err != nil { + return err + } + if e == nil { + return nil + } + + s := "message" + if e.msg.err != nil { + s = "error" + } + fmt.Fprintf(w, "#%d: kind: %s, method: %s, ref index: %d, %s:\n", + i, e.kind, e.method, e.refIndex, s) + if e.msg.err == nil { + if err := proto.MarshalText(w, e.msg.msg); err != nil { + return err + } + } else { + fmt.Fprintf(w, "%v\n", e.msg.err) + } + } +} + +// An entry holds one gRPC action (request, response, etc.). +type entry struct { + kind pb.Entry_Kind + method string + msg message + refIndex int // index of corresponding request or create-stream +} + +func (e1 *entry) equal(e2 *entry) bool { + if e1 == nil && e2 == nil { + return true + } + if e1 == nil || e2 == nil { + return false + } + return e1.kind == e2.kind && + e1.method == e2.method && + proto.Equal(e1.msg.msg, e2.msg.msg) && + errEqual(e1.msg.err, e2.msg.err) && + e1.refIndex == e2.refIndex +} + +func errEqual(e1, e2 error) bool { + if e1 == e2 { + return true + } + s1, ok1 := status.FromError(e1) + s2, ok2 := status.FromError(e2) + if !ok1 || !ok2 { + return false + } + return proto.Equal(s1.Proto(), s2.Proto()) +} + +// message holds either a single proto.Message or an error. +type message struct { + msg proto.Message + err error +} + +func (m *message) set(msg interface{}, err error) { + m.err = err + if err != io.EOF && msg != nil { + m.msg = msg.(proto.Message) + } +} + +// File format: +// header +// sequence of Entry protos +// +// Header format: +// magic string +// a record containing the bytes of the initial state + +const magic = "RPCReplay" + +func writeHeader(w io.Writer, initial []byte) error { + if _, err := io.WriteString(w, magic); err != nil { + return err + } + return writeRecord(w, initial) +} + +func readHeader(r io.Reader) ([]byte, error) { + var buf [len(magic)]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { + if err == io.EOF { + err = errors.New("rpcreplay: empty replay file") + } + return nil, err + } + if string(buf[:]) != magic { + return nil, errors.New("rpcreplay: not a replay file (does not begin with magic string)") + } + bytes, err := readRecord(r) + if err == io.EOF { + err = errors.New("rpcreplay: missing initial state") + } + return bytes, err +} + +func writeEntry(w io.Writer, e *entry) error { + var m proto.Message + if e.msg.err != nil && e.msg.err != io.EOF { + s, ok := status.FromError(e.msg.err) + if !ok { + return fmt.Errorf("rpcreplay: error %v is not a Status", e.msg.err) + } + m = s.Proto() + } else { + m = e.msg.msg + } + var a *any.Any + var err error + if m != nil { + a, err = ptypes.MarshalAny(m) + if err != nil { + return err + } + } + pe := &pb.Entry{ + Kind: e.kind, + Method: e.method, + Message: a, + IsError: e.msg.err != nil, + RefIndex: int32(e.refIndex), + } + bytes, err := proto.Marshal(pe) + if err != nil { + return err + } + return writeRecord(w, bytes) +} + +func readEntry(r io.Reader) (*entry, error) { + buf, err := readRecord(r) + if err == io.EOF { + return nil, nil + } + if err != nil { + return nil, err + } + var pe pb.Entry + if err := proto.Unmarshal(buf, &pe); err != nil { + return nil, err + } + var msg message + if pe.Message != nil { + var any ptypes.DynamicAny + if err := ptypes.UnmarshalAny(pe.Message, &any); err != nil { + return nil, err + } + if pe.IsError { + msg.err = status.ErrorProto(any.Message.(*spb.Status)) + } else { + msg.msg = any.Message + } + } else if pe.IsError { + msg.err = io.EOF + } else if pe.Kind != pb.Entry_CREATE_STREAM { + return nil, errors.New("rpcreplay: entry with nil message and false is_error") + } + return &entry{ + kind: pe.Kind, + method: pe.Method, + msg: msg, + refIndex: int(pe.RefIndex), + }, nil +} + +// A record consists of an unsigned 32-bit little-endian length L followed by L +// bytes. + +func writeRecord(w io.Writer, data []byte) error { + if err := binary.Write(w, binary.LittleEndian, uint32(len(data))); err != nil { + return err + } + _, err := w.Write(data) + return err +} + +func readRecord(r io.Reader) ([]byte, error) { + var size uint32 + if err := binary.Read(r, binary.LittleEndian, &size); err != nil { + return nil, err + } + buf := make([]byte, size) + if _, err := io.ReadFull(r, buf); err != nil { + return nil, err + } + return buf, nil +} diff --git a/vendor/cloud.google.com/go/rpcreplay/rpcreplay_test.go b/vendor/cloud.google.com/go/rpcreplay/rpcreplay_test.go new file mode 100644 index 0000000000..ad67925bdf --- /dev/null +++ b/vendor/cloud.google.com/go/rpcreplay/rpcreplay_test.go @@ -0,0 +1,564 @@ +// Copyright 2017 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 rpcreplay + +import ( + "bytes" + "errors" + "io" + "strings" + "testing" + + "cloud.google.com/go/internal/testutil" + ipb "cloud.google.com/go/rpcreplay/proto/intstore" + rpb "cloud.google.com/go/rpcreplay/proto/rpcreplay" + "github.com/golang/protobuf/proto" + "github.com/google/go-cmp/cmp" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestRecordIO(t *testing.T) { + buf := &bytes.Buffer{} + want := []byte{1, 2, 3} + if err := writeRecord(buf, want); err != nil { + t.Fatal(err) + } + got, err := readRecord(buf) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestHeaderIO(t *testing.T) { + buf := &bytes.Buffer{} + want := []byte{1, 2, 3} + if err := writeHeader(buf, want); err != nil { + t.Fatal(err) + } + got, err := readHeader(buf) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(got, want) { + t.Errorf("got %v, want %v", got, want) + } + + // readHeader errors + for _, contents := range []string{"", "badmagic", "gRPCReplay"} { + if _, err := readHeader(bytes.NewBufferString(contents)); err == nil { + t.Errorf("%q: got nil, want error", contents) + } + } +} + +func TestEntryIO(t *testing.T) { + for i, want := range []*entry{ + { + kind: rpb.Entry_REQUEST, + method: "method", + msg: message{msg: &rpb.Entry{}}, + refIndex: 7, + }, + { + kind: rpb.Entry_RESPONSE, + method: "method", + msg: message{err: status.Error(codes.NotFound, "not found")}, + refIndex: 8, + }, + { + kind: rpb.Entry_RECV, + method: "method", + msg: message{err: io.EOF}, + refIndex: 3, + }, + } { + buf := &bytes.Buffer{} + if err := writeEntry(buf, want); err != nil { + t.Fatal(err) + } + got, err := readEntry(buf) + if err != nil { + t.Fatal(err) + } + if !got.equal(want) { + t.Errorf("#%d: got %v, want %v", i, got, want) + } + } +} + +var initialState = []byte{1, 2, 3} + +func TestRecord(t *testing.T) { + srv := newIntStoreServer() + defer srv.stop() + buf := record(t, srv) + + gotIstate, err := readHeader(buf) + if err != nil { + t.Fatal(err) + } + if !testutil.Equal(gotIstate, initialState) { + t.Fatalf("got %v, want %v", gotIstate, initialState) + } + item := &ipb.Item{Name: "a", Value: 1} + wantEntries := []*entry{ + // Set + { + kind: rpb.Entry_REQUEST, + method: "/intstore.IntStore/Set", + msg: message{msg: item}, + }, + { + kind: rpb.Entry_RESPONSE, + msg: message{msg: &ipb.SetResponse{PrevValue: 0}}, + refIndex: 1, + }, + // Get + { + kind: rpb.Entry_REQUEST, + method: "/intstore.IntStore/Get", + msg: message{msg: &ipb.GetRequest{Name: "a"}}, + }, + { + kind: rpb.Entry_RESPONSE, + msg: message{msg: item}, + refIndex: 3, + }, + { + kind: rpb.Entry_REQUEST, + method: "/intstore.IntStore/Get", + msg: message{msg: &ipb.GetRequest{Name: "x"}}, + }, + { + kind: rpb.Entry_RESPONSE, + msg: message{err: status.Error(codes.NotFound, `"x"`)}, + refIndex: 5, + }, + // ListItems + { // entry #7 + kind: rpb.Entry_CREATE_STREAM, + method: "/intstore.IntStore/ListItems", + }, + { + kind: rpb.Entry_SEND, + msg: message{msg: &ipb.ListItemsRequest{}}, + refIndex: 7, + }, + { + kind: rpb.Entry_RECV, + msg: message{msg: item}, + refIndex: 7, + }, + { + kind: rpb.Entry_RECV, + msg: message{err: io.EOF}, + refIndex: 7, + }, + // SetStream + { // entry #11 + kind: rpb.Entry_CREATE_STREAM, + method: "/intstore.IntStore/SetStream", + }, + { + kind: rpb.Entry_SEND, + msg: message{msg: &ipb.Item{Name: "b", Value: 2}}, + refIndex: 11, + }, + { + kind: rpb.Entry_SEND, + msg: message{msg: &ipb.Item{Name: "c", Value: 3}}, + refIndex: 11, + }, + { + kind: rpb.Entry_RECV, + msg: message{msg: &ipb.Summary{Count: 2}}, + refIndex: 11, + }, + + // StreamChat + { // entry #15 + kind: rpb.Entry_CREATE_STREAM, + method: "/intstore.IntStore/StreamChat", + }, + { + kind: rpb.Entry_SEND, + msg: message{msg: &ipb.Item{Name: "d", Value: 4}}, + refIndex: 15, + }, + { + kind: rpb.Entry_RECV, + msg: message{msg: &ipb.Item{Name: "d", Value: 4}}, + refIndex: 15, + }, + { + kind: rpb.Entry_SEND, + msg: message{msg: &ipb.Item{Name: "e", Value: 5}}, + refIndex: 15, + }, + { + kind: rpb.Entry_RECV, + msg: message{msg: &ipb.Item{Name: "e", Value: 5}}, + refIndex: 15, + }, + { + kind: rpb.Entry_RECV, + msg: message{err: io.EOF}, + refIndex: 15, + }, + } + for i, w := range wantEntries { + g, err := readEntry(buf) + if err != nil { + t.Fatalf("#%d: %v", i+1, err) + } + if !g.equal(w) { + t.Errorf("#%d:\ngot %+v\nwant %+v", i+1, g, w) + } + } + g, err := readEntry(buf) + if err != nil { + t.Fatal(err) + } + if g != nil { + t.Errorf("\ngot %+v\nwant nil", g) + } +} + +func TestReplay(t *testing.T) { + srv := newIntStoreServer() + defer srv.stop() + + buf := record(t, srv) + rep, err := NewReplayerReader(buf) + if err != nil { + t.Fatal(err) + } + if got, want := rep.Initial(), initialState; !testutil.Equal(got, want) { + t.Fatalf("got %v, want %v", got, want) + } + // Replay the test. + testService(t, srv.Addr, rep.DialOptions()) +} + +func record(t *testing.T, srv *intStoreServer) *bytes.Buffer { + buf := &bytes.Buffer{} + rec, err := NewRecorderWriter(buf, initialState) + if err != nil { + t.Fatal(err) + } + testService(t, srv.Addr, rec.DialOptions()) + if err := rec.Close(); err != nil { + t.Fatal(err) + } + return buf +} + +func testService(t *testing.T, addr string, opts []grpc.DialOption) { + conn, err := grpc.Dial(addr, + append([]grpc.DialOption{grpc.WithInsecure()}, opts...)...) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + client := ipb.NewIntStoreClient(conn) + ctx := context.Background() + item := &ipb.Item{Name: "a", Value: 1} + res, err := client.Set(ctx, item) + if err != nil { + t.Fatal(err) + } + if res.PrevValue != 0 { + t.Errorf("got %d, want 0", res.PrevValue) + } + got, err := client.Get(ctx, &ipb.GetRequest{Name: "a"}) + if err != nil { + t.Fatal(err) + } + if !proto.Equal(got, item) { + t.Errorf("got %v, want %v", got, item) + } + _, err = client.Get(ctx, &ipb.GetRequest{Name: "x"}) + if err == nil { + t.Fatal("got nil, want error") + } + if _, ok := status.FromError(err); !ok { + t.Errorf("got error type %T, want a grpc/status.Status", err) + } + + wantItems := []*ipb.Item{item} + lic, err := client.ListItems(ctx, &ipb.ListItemsRequest{}) + if err != nil { + t.Fatal(err) + } + for i := 0; ; i++ { + item, err := lic.Recv() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + if i >= len(wantItems) || !proto.Equal(item, wantItems[i]) { + t.Fatalf("%d: bad item", i) + } + } + + ssc, err := client.SetStream(ctx) + if err != nil { + t.Fatal(err) + } + + must := func(err error) { + if err != nil { + t.Fatal(err) + } + } + + for i, name := range []string{"b", "c"} { + must(ssc.Send(&ipb.Item{Name: name, Value: int32(i + 2)})) + } + summary, err := ssc.CloseAndRecv() + if err != nil { + t.Fatal(err) + } + if got, want := summary.Count, int32(2); got != want { + t.Fatalf("got %d, want %d", got, want) + } + + chatc, err := client.StreamChat(ctx) + if err != nil { + t.Fatal(err) + } + for i, name := range []string{"d", "e"} { + item := &ipb.Item{Name: name, Value: int32(i + 4)} + must(chatc.Send(item)) + got, err := chatc.Recv() + if err != nil { + t.Fatal(err) + } + if !proto.Equal(got, item) { + t.Errorf("got %v, want %v", got, item) + } + } + must(chatc.CloseSend()) + if _, err := chatc.Recv(); err != io.EOF { + t.Fatalf("got %v, want EOF", err) + } +} + +func TestRecorderBeforeFunc(t *testing.T) { + var tests = []struct { + name string + msg, wantRespMsg, wantEntryMsg *ipb.Item + f func(string, proto.Message) error + wantErr bool + }{ + { + name: "BeforeFunc should modify messages saved, but not alter what is sent/received to/from services", + msg: &ipb.Item{Name: "foo", Value: 1}, + wantEntryMsg: &ipb.Item{Name: "bar", Value: 2}, + wantRespMsg: &ipb.Item{Name: "foo", Value: 1}, + f: func(method string, m proto.Message) error { + // This callback only runs when Set is called. + if !strings.HasSuffix(method, "Set") { + return nil + } + if _, ok := m.(*ipb.Item); !ok { + return nil + } + + item := m.(*ipb.Item) + item.Name = "bar" + item.Value = 2 + return nil + }, + }, + { + name: "BeforeFunc should not be able to alter returned responses", + msg: &ipb.Item{Name: "foo", Value: 1}, + wantRespMsg: &ipb.Item{Name: "foo", Value: 1}, + f: func(method string, m proto.Message) error { + // This callback only runs when Get is called. + if !strings.HasSuffix(method, "Get") { + return nil + } + if _, ok := m.(*ipb.Item); !ok { + return nil + } + + item := m.(*ipb.Item) + item.Value = 2 + return nil + }, + }, + { + name: "Errors should cause the RPC send to fail", + msg: &ipb.Item{}, + f: func(_ string, _ proto.Message) error { + return errors.New("err") + }, + wantErr: true, + }, + } + + for _, tc := range tests { + // Wrap test cases in a func so defers execute correctly. + func() { + srv := newIntStoreServer() + defer srv.stop() + + var b bytes.Buffer + r, err := NewRecorderWriter(&b, nil) + if err != nil { + t.Error(err) + return + } + r.BeforeFunc = tc.f + ctx := context.Background() + conn, err := grpc.DialContext(ctx, srv.Addr, append([]grpc.DialOption{grpc.WithInsecure()}, r.DialOptions()...)...) + if err != nil { + t.Error(err) + return + } + defer conn.Close() + + client := ipb.NewIntStoreClient(conn) + _, err = client.Set(ctx, tc.msg) + switch { + case err != nil && !tc.wantErr: + t.Error(err) + return + case err == nil && tc.wantErr: + t.Errorf("got nil; want error") + return + case err != nil: + // Error found as expected, don't check Get(). + return + } + + if tc.wantRespMsg != nil { + got, err := client.Get(ctx, &ipb.GetRequest{Name: tc.msg.GetName()}) + if err != nil { + t.Error(err) + return + } + if !cmp.Equal(got, tc.wantRespMsg) { + t.Errorf("got %+v; want %+v", got, tc.wantRespMsg) + } + } + + r.Close() + + if tc.wantEntryMsg != nil { + _, _ = readHeader(&b) + e, err := readEntry(&b) + if err != nil { + t.Error(err) + return + } + got := e.msg.msg.(*ipb.Item) + if !cmp.Equal(got, tc.wantEntryMsg) { + t.Errorf("got %v; want %v", got, tc.wantEntryMsg) + } + } + }() + } +} + +func TestReplayerBeforeFunc(t *testing.T) { + var tests = []struct { + name string + msg, reqMsg *ipb.Item + f func(string, proto.Message) error + wantErr bool + }{ + { + name: "BeforeFunc should modify messages sent before they are passed to the replayer", + msg: &ipb.Item{Name: "foo", Value: 1}, + reqMsg: &ipb.Item{Name: "bar", Value: 1}, + f: func(method string, m proto.Message) error { + item := m.(*ipb.Item) + item.Name = "foo" + return nil + }, + }, + { + name: "Errors should cause the RPC send to fail", + msg: &ipb.Item{}, + f: func(_ string, _ proto.Message) error { + return errors.New("err") + }, + wantErr: true, + }, + } + + for _, tc := range tests { + // Wrap test cases in a func so defers execute correctly. + func() { + srv := newIntStoreServer() + defer srv.stop() + + var b bytes.Buffer + rec, err := NewRecorderWriter(&b, nil) + if err != nil { + t.Error(err) + return + } + ctx := context.Background() + conn, err := grpc.DialContext(ctx, srv.Addr, append([]grpc.DialOption{grpc.WithInsecure()}, rec.DialOptions()...)...) + if err != nil { + t.Error(err) + return + } + defer conn.Close() + + client := ipb.NewIntStoreClient(conn) + _, err = client.Set(ctx, tc.msg) + if err != nil { + t.Error(err) + return + } + rec.Close() + + rep, err := NewReplayerReader(&b) + if err != nil { + t.Error(err) + return + } + rep.BeforeFunc = tc.f + conn, err = grpc.DialContext(ctx, srv.Addr, append([]grpc.DialOption{grpc.WithInsecure()}, rep.DialOptions()...)...) + if err != nil { + t.Error(err) + return + } + defer conn.Close() + + client = ipb.NewIntStoreClient(conn) + _, err = client.Set(ctx, tc.reqMsg) + switch { + case err != nil && !tc.wantErr: + t.Error(err) + case err == nil && tc.wantErr: + t.Errorf("got nil; want error") + } + }() + } +} diff --git a/vendor/cloud.google.com/go/run-tests.sh b/vendor/cloud.google.com/go/run-tests.sh new file mode 100755 index 0000000000..a643e61c47 --- /dev/null +++ b/vendor/cloud.google.com/go/run-tests.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +# Selectively run tests for this repo, based on what has changed +# in a commit. Runs short tests for the whole repo, and full tests +# for changed directories. + +set -e + +prefix=cloud.google.com/go + +dryrun=false +if [[ $1 == "-n" ]]; then + dryrun=true + shift +fi + +if [[ $1 == "" ]]; then + echo >&2 "usage: $0 [-n] COMMIT" + exit 1 +fi + +# Files or directories that cause all tests to run if modified. +declare -A run_all +run_all=([.travis.yml]=1 [run-tests.sh]=1) + +function run { + if $dryrun; then + echo $* + else + (set -x; $*) + fi +} + + +# Find all the packages that have changed in this commit. +declare -A changed_packages + +for f in $(git diff-tree --no-commit-id --name-only -r $1); do + if [[ ${run_all[$f]} == 1 ]]; then + # This change requires a full test. Do it and exit. + run go test -race -v $prefix/... + exit + fi + # Map, e.g., "spanner/client.go" to "$prefix/spanner". + d=$(dirname $f) + if [[ $d == "." ]]; then + pkg=$prefix + else + pkg=$prefix/$d + fi + changed_packages[$pkg]=1 +done + +echo "changed packages: ${!changed_packages[*]}" + + +# Reports whether its argument, a package name, depends (recursively) +# on a changed package. +function depends_on_changed_package { + # According to go list, a package does not depend on itself, so + # we test that separately. + if [[ ${changed_packages[$1]} == 1 ]]; then + return 0 + fi + for dep in $(go list -f '{{range .Deps}}{{.}} {{end}}' $1); do + if [[ ${changed_packages[$dep]} == 1 ]]; then + return 0 + fi + done + return 1 +} + +# Collect the packages into two separate lists. (It is faster to call "go test" on a +# list of packages than to individually "go test" each one.) + +shorts= +fulls= +for pkg in $(go list $prefix/...); do # for each package in the repo + if depends_on_changed_package $pkg; then # if it depends on a changed package + fulls="$fulls $pkg" # run the full test + else # otherwise + shorts="$shorts $pkg" # run the short test + fi +done +run go test -race -v -short $shorts +if [[ $fulls != "" ]]; then + run go test -race -v $fulls +fi diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client.go new file mode 100644 index 0000000000..2c6e6a1005 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client.go @@ -0,0 +1,521 @@ +// 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 database + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + iampb "google.golang.org/genproto/googleapis/iam/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// DatabaseAdminCallOptions contains the retry settings for each method of DatabaseAdminClient. +type DatabaseAdminCallOptions struct { + ListDatabases []gax.CallOption + CreateDatabase []gax.CallOption + GetDatabase []gax.CallOption + UpdateDatabaseDdl []gax.CallOption + DropDatabase []gax.CallOption + GetDatabaseDdl []gax.CallOption + SetIamPolicy []gax.CallOption + GetIamPolicy []gax.CallOption + TestIamPermissions []gax.CallOption +} + +func defaultDatabaseAdminClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("spanner.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultDatabaseAdminCallOptions() *DatabaseAdminCallOptions { + 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: 1000 * time.Millisecond, + Max: 32000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &DatabaseAdminCallOptions{ + ListDatabases: retry[[2]string{"default", "idempotent"}], + CreateDatabase: retry[[2]string{"default", "non_idempotent"}], + GetDatabase: retry[[2]string{"default", "idempotent"}], + UpdateDatabaseDdl: retry[[2]string{"default", "idempotent"}], + DropDatabase: retry[[2]string{"default", "idempotent"}], + GetDatabaseDdl: retry[[2]string{"default", "idempotent"}], + SetIamPolicy: retry[[2]string{"default", "non_idempotent"}], + GetIamPolicy: retry[[2]string{"default", "idempotent"}], + TestIamPermissions: retry[[2]string{"default", "non_idempotent"}], + } +} + +// DatabaseAdminClient is a client for interacting with Cloud Spanner Database Admin API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type DatabaseAdminClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + databaseAdminClient databasepb.DatabaseAdminClient + + // 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 *DatabaseAdminCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewDatabaseAdminClient creates a new database admin client. +// +// Cloud Spanner Database Admin API +// +// The Cloud Spanner Database Admin API can be used to create, drop, and +// list databases. It also enables updating the schema of pre-existing +// databases. +func NewDatabaseAdminClient(ctx context.Context, opts ...option.ClientOption) (*DatabaseAdminClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultDatabaseAdminClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &DatabaseAdminClient{ + conn: conn, + CallOptions: defaultDatabaseAdminCallOptions(), + + databaseAdminClient: databasepb.NewDatabaseAdminClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *DatabaseAdminClient) 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 *DatabaseAdminClient) 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 *DatabaseAdminClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListDatabases lists Cloud Spanner databases. +func (c *DatabaseAdminClient) ListDatabases(ctx context.Context, req *databasepb.ListDatabasesRequest, opts ...gax.CallOption) *DatabaseIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListDatabases[0:len(c.CallOptions.ListDatabases):len(c.CallOptions.ListDatabases)], opts...) + it := &DatabaseIterator{} + req = proto.Clone(req).(*databasepb.ListDatabasesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*databasepb.Database, string, error) { + var resp *databasepb.ListDatabasesResponse + 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.databaseAdminClient.ListDatabases(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Databases, 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 +} + +// CreateDatabase creates a new Cloud Spanner database and starts to prepare it for serving. +// The returned [long-running operation][google.longrunning.Operation] will +// have a name of the format /operations/ and +// can be used to track preparation of the database. The +// [metadata][google.longrunning.Operation.metadata] field type is +// [CreateDatabaseMetadata][google.spanner.admin.database.v1.CreateDatabaseMetadata]. The +// [response][google.longrunning.Operation.response] field type is +// [Database][google.spanner.admin.database.v1.Database], if successful. +func (c *DatabaseAdminClient) CreateDatabase(ctx context.Context, req *databasepb.CreateDatabaseRequest, opts ...gax.CallOption) (*CreateDatabaseOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateDatabase[0:len(c.CallOptions.CreateDatabase):len(c.CallOptions.CreateDatabase)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.databaseAdminClient.CreateDatabase(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &CreateDatabaseOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// GetDatabase gets the state of a Cloud Spanner database. +func (c *DatabaseAdminClient) GetDatabase(ctx context.Context, req *databasepb.GetDatabaseRequest, opts ...gax.CallOption) (*databasepb.Database, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetDatabase[0:len(c.CallOptions.GetDatabase):len(c.CallOptions.GetDatabase)], opts...) + var resp *databasepb.Database + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.databaseAdminClient.GetDatabase(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// UpdateDatabaseDdl updates the schema of a Cloud Spanner database by +// creating/altering/dropping tables, columns, indexes, etc. The returned +// [long-running operation][google.longrunning.Operation] will have a name of +// the format /operations/ and can be used to +// track execution of the schema change(s). The +// [metadata][google.longrunning.Operation.metadata] field type is +// [UpdateDatabaseDdlMetadata][google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata]. The operation has no response. +func (c *DatabaseAdminClient) UpdateDatabaseDdl(ctx context.Context, req *databasepb.UpdateDatabaseDdlRequest, opts ...gax.CallOption) (*UpdateDatabaseDdlOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateDatabaseDdl[0:len(c.CallOptions.UpdateDatabaseDdl):len(c.CallOptions.UpdateDatabaseDdl)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.databaseAdminClient.UpdateDatabaseDdl(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &UpdateDatabaseDdlOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// DropDatabase drops (aka deletes) a Cloud Spanner database. +func (c *DatabaseAdminClient) DropDatabase(ctx context.Context, req *databasepb.DropDatabaseRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DropDatabase[0:len(c.CallOptions.DropDatabase):len(c.CallOptions.DropDatabase)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.databaseAdminClient.DropDatabase(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// GetDatabaseDdl returns the schema of a Cloud Spanner database as a list of formatted +// DDL statements. This method does not show pending schema updates, those may +// be queried using the [Operations][google.longrunning.Operations] API. +func (c *DatabaseAdminClient) GetDatabaseDdl(ctx context.Context, req *databasepb.GetDatabaseDdlRequest, opts ...gax.CallOption) (*databasepb.GetDatabaseDdlResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetDatabaseDdl[0:len(c.CallOptions.GetDatabaseDdl):len(c.CallOptions.GetDatabaseDdl)], opts...) + var resp *databasepb.GetDatabaseDdlResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.databaseAdminClient.GetDatabaseDdl(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SetIamPolicy sets the access control policy on a database resource. Replaces any +// existing policy. +// +// Authorization requires spanner.databases.setIamPolicy permission on +// [resource][google.iam.v1.SetIamPolicyRequest.resource]. +func (c *DatabaseAdminClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.databaseAdminClient.SetIamPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetIamPolicy gets the access control policy for a database resource. Returns an empty +// policy if a database exists but does not have a policy set. +// +// Authorization requires spanner.databases.getIamPolicy permission on +// [resource][google.iam.v1.GetIamPolicyRequest.resource]. +func (c *DatabaseAdminClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.databaseAdminClient.GetIamPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// TestIamPermissions returns permissions that the caller has on the specified database resource. +// +// Attempting this RPC on a non-existent Cloud Spanner database will result in +// a NOT_FOUND error if the user has spanner.databases.list permission on +// the containing Cloud Spanner instance. Otherwise returns an empty set of +// permissions. +func (c *DatabaseAdminClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.databaseAdminClient.TestIamPermissions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// DatabaseIterator manages a stream of *databasepb.Database. +type DatabaseIterator struct { + items []*databasepb.Database + 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 []*databasepb.Database, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *DatabaseIterator) 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 *DatabaseIterator) Next() (*databasepb.Database, error) { + var item *databasepb.Database + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *DatabaseIterator) bufLen() int { + return len(it.items) +} + +func (it *DatabaseIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// CreateDatabaseOperation manages a long-running operation from CreateDatabase. +type CreateDatabaseOperation struct { + lro *longrunning.Operation +} + +// CreateDatabaseOperation returns a new CreateDatabaseOperation from a given name. +// The name must be that of a previously created CreateDatabaseOperation, possibly from a different process. +func (c *DatabaseAdminClient) CreateDatabaseOperation(name string) *CreateDatabaseOperation { + return &CreateDatabaseOperation{ + 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 *CreateDatabaseOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*databasepb.Database, error) { + var resp databasepb.Database + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *CreateDatabaseOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*databasepb.Database, error) { + var resp databasepb.Database + 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 *CreateDatabaseOperation) Metadata() (*databasepb.CreateDatabaseMetadata, error) { + var meta databasepb.CreateDatabaseMetadata + 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 *CreateDatabaseOperation) 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 *CreateDatabaseOperation) Name() string { + return op.lro.Name() +} + +// UpdateDatabaseDdlOperation manages a long-running operation from UpdateDatabaseDdl. +type UpdateDatabaseDdlOperation struct { + lro *longrunning.Operation +} + +// UpdateDatabaseDdlOperation returns a new UpdateDatabaseDdlOperation from a given name. +// The name must be that of a previously created UpdateDatabaseDdlOperation, possibly from a different process. +func (c *DatabaseAdminClient) UpdateDatabaseDdlOperation(name string) *UpdateDatabaseDdlOperation { + return &UpdateDatabaseDdlOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), + } +} + +// Wait blocks until the long-running operation is completed, returning any error encountered. +// +// See documentation of Poll for error-handling information. +func (op *UpdateDatabaseDdlOperation) Wait(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.WaitWithInterval(ctx, nil, 45000*time.Millisecond, opts...) +} + +// 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. +func (op *UpdateDatabaseDdlOperation) Poll(ctx context.Context, opts ...gax.CallOption) error { + return op.lro.Poll(ctx, nil, opts...) +} + +// 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 *UpdateDatabaseDdlOperation) Metadata() (*databasepb.UpdateDatabaseDdlMetadata, error) { + var meta databasepb.UpdateDatabaseDdlMetadata + 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 *UpdateDatabaseDdlOperation) 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 *UpdateDatabaseDdlOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client_example_test.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client_example_test.go new file mode 100644 index 0000000000..be6dc82516 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client_example_test.go @@ -0,0 +1,207 @@ +// 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 database_test + +import ( + "cloud.google.com/go/spanner/admin/database/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + iampb "google.golang.org/genproto/googleapis/iam/v1" + databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1" +) + +func ExampleNewDatabaseAdminClient() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleDatabaseAdminClient_ListDatabases() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &databasepb.ListDatabasesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListDatabases(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleDatabaseAdminClient_CreateDatabase() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &databasepb.CreateDatabaseRequest{ + // TODO: Fill request struct fields. + } + op, err := c.CreateDatabase(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleDatabaseAdminClient_GetDatabase() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &databasepb.GetDatabaseRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetDatabase(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleDatabaseAdminClient_UpdateDatabaseDdl() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &databasepb.UpdateDatabaseDdlRequest{ + // TODO: Fill request struct fields. + } + op, err := c.UpdateDatabaseDdl(ctx, req) + if err != nil { + // TODO: Handle error. + } + + err = op.Wait(ctx) + // TODO: Handle error. +} + +func ExampleDatabaseAdminClient_DropDatabase() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &databasepb.DropDatabaseRequest{ + // TODO: Fill request struct fields. + } + err = c.DropDatabase(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleDatabaseAdminClient_GetDatabaseDdl() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &databasepb.GetDatabaseDdlRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetDatabaseDdl(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleDatabaseAdminClient_SetIamPolicy() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(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 ExampleDatabaseAdminClient_GetIamPolicy() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(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 ExampleDatabaseAdminClient_TestIamPermissions() { + ctx := context.Background() + c, err := database.NewDatabaseAdminClient(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 +} diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/doc.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/doc.go new file mode 100644 index 0000000000..5706f706cb --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/doc.go @@ -0,0 +1,46 @@ +// 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 database is an auto-generated package for the +// Cloud Spanner Database Admin API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +package database // import "cloud.google.com/go/spanner/admin/database/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/spanner.admin", + } +} diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/mock_test.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/mock_test.go new file mode 100644 index 0000000000..5c08210dcf --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/mock_test.go @@ -0,0 +1,798 @@ +// 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 database + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + iampb "google.golang.org/genproto/googleapis/iam/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockDatabaseAdminServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + databasepb.DatabaseAdminServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockDatabaseAdminServer) ListDatabases(ctx context.Context, req *databasepb.ListDatabasesRequest) (*databasepb.ListDatabasesResponse, 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].(*databasepb.ListDatabasesResponse), nil +} + +func (s *mockDatabaseAdminServer) CreateDatabase(ctx context.Context, req *databasepb.CreateDatabaseRequest) (*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 *mockDatabaseAdminServer) GetDatabase(ctx context.Context, req *databasepb.GetDatabaseRequest) (*databasepb.Database, 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].(*databasepb.Database), nil +} + +func (s *mockDatabaseAdminServer) UpdateDatabaseDdl(ctx context.Context, req *databasepb.UpdateDatabaseDdlRequest) (*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 *mockDatabaseAdminServer) DropDatabase(ctx context.Context, req *databasepb.DropDatabaseRequest) (*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 *mockDatabaseAdminServer) GetDatabaseDdl(ctx context.Context, req *databasepb.GetDatabaseDdlRequest) (*databasepb.GetDatabaseDdlResponse, 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].(*databasepb.GetDatabaseDdlResponse), nil +} + +func (s *mockDatabaseAdminServer) 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 *mockDatabaseAdminServer) 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 *mockDatabaseAdminServer) 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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockDatabaseAdmin mockDatabaseAdminServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + databasepb.RegisterDatabaseAdminServer(serv, &mockDatabaseAdmin) + + 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 TestDatabaseAdminListDatabases(t *testing.T) { + var nextPageToken string = "" + var databasesElement *databasepb.Database = &databasepb.Database{} + var databases = []*databasepb.Database{databasesElement} + var expectedResponse = &databasepb.ListDatabasesResponse{ + NextPageToken: nextPageToken, + Databases: databases, + } + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &databasepb.ListDatabasesRequest{ + Parent: formattedParent, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDatabases(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Databases[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 TestDatabaseAdminListDatabasesError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &databasepb.ListDatabasesRequest{ + Parent: formattedParent, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListDatabases(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 TestDatabaseAdminCreateDatabase(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &databasepb.Database{ + Name: name, + } + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var createStatement string = "createStatement552974828" + var request = &databasepb.CreateDatabaseRequest{ + Parent: formattedParent, + CreateStatement: createStatement, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateDatabase(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, mockDatabaseAdmin.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 TestDatabaseAdminCreateDatabaseError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.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/instances/%s", "[PROJECT]", "[INSTANCE]") + var createStatement string = "createStatement552974828" + var request = &databasepb.CreateDatabaseRequest{ + Parent: formattedParent, + CreateStatement: createStatement, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateDatabase(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 TestDatabaseAdminGetDatabase(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &databasepb.Database{ + Name: name2, + } + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &databasepb.GetDatabaseRequest{ + Name: formattedName, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDatabase(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDatabaseAdmin.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 TestDatabaseAdminGetDatabaseError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &databasepb.GetDatabaseRequest{ + Name: formattedName, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDatabase(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 TestDatabaseAdminUpdateDatabaseDdl(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var statements []string = nil + var request = &databasepb.UpdateDatabaseDdlRequest{ + Database: formattedDatabase, + Statements: statements, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateDatabaseDdl(context.Background(), request) + if err != nil { + t.Fatal(err) + } + err = respLRO.Wait(context.Background()) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDatabaseAdminUpdateDatabaseDdlError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var statements []string = nil + var request = &databasepb.UpdateDatabaseDdlRequest{ + Database: formattedDatabase, + Statements: statements, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateDatabaseDdl(context.Background(), request) + if err != nil { + t.Fatal(err) + } + 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) + } +} +func TestDatabaseAdminDropDatabase(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &databasepb.DropDatabaseRequest{ + Database: formattedDatabase, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DropDatabase(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestDatabaseAdminDropDatabaseError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &databasepb.DropDatabaseRequest{ + Database: formattedDatabase, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DropDatabase(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 TestDatabaseAdminGetDatabaseDdl(t *testing.T) { + var expectedResponse *databasepb.GetDatabaseDdlResponse = &databasepb.GetDatabaseDdlResponse{} + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &databasepb.GetDatabaseDdlRequest{ + Database: formattedDatabase, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDatabaseDdl(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockDatabaseAdmin.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 TestDatabaseAdminGetDatabaseDdlError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &databasepb.GetDatabaseDdlRequest{ + Database: formattedDatabase, + } + + c, err := NewDatabaseAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetDatabaseDdl(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 TestDatabaseAdminSetIamPolicy(t *testing.T) { + var version int32 = 351608024 + var etag []byte = []byte("21") + var expectedResponse = &iampb.Policy{ + Version: version, + Etag: etag, + } + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var policy *iampb.Policy = &iampb.Policy{} + var request = &iampb.SetIamPolicyRequest{ + Resource: formattedResource, + Policy: policy, + } + + c, err := NewDatabaseAdminClient(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, mockDatabaseAdmin.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 TestDatabaseAdminSetIamPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var policy *iampb.Policy = &iampb.Policy{} + var request = &iampb.SetIamPolicyRequest{ + Resource: formattedResource, + Policy: policy, + } + + c, err := NewDatabaseAdminClient(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 TestDatabaseAdminGetIamPolicy(t *testing.T) { + var version int32 = 351608024 + var etag []byte = []byte("21") + var expectedResponse = &iampb.Policy{ + Version: version, + Etag: etag, + } + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &iampb.GetIamPolicyRequest{ + Resource: formattedResource, + } + + c, err := NewDatabaseAdminClient(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, mockDatabaseAdmin.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 TestDatabaseAdminGetIamPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &iampb.GetIamPolicyRequest{ + Resource: formattedResource, + } + + c, err := NewDatabaseAdminClient(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 TestDatabaseAdminTestIamPermissions(t *testing.T) { + var expectedResponse *iampb.TestIamPermissionsResponse = &iampb.TestIamPermissionsResponse{} + + mockDatabaseAdmin.err = nil + mockDatabaseAdmin.reqs = nil + + mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var permissions []string = nil + var request = &iampb.TestIamPermissionsRequest{ + Resource: formattedResource, + Permissions: permissions, + } + + c, err := NewDatabaseAdminClient(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, mockDatabaseAdmin.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 TestDatabaseAdminTestIamPermissionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockDatabaseAdmin.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var permissions []string = nil + var request = &iampb.TestIamPermissionsRequest{ + Resource: formattedResource, + Permissions: permissions, + } + + c, err := NewDatabaseAdminClient(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 +} diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/path_funcs.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/path_funcs.go new file mode 100644 index 0000000000..5490f57c48 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/path_funcs.go @@ -0,0 +1,45 @@ +// 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. + +package database + +// DatabaseAdminInstancePath returns the path for the instance resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/instances/%s", project, instance) +// instead. +func DatabaseAdminInstancePath(project, instance string) string { + return "" + + "projects/" + + project + + "/instances/" + + instance + + "" +} + +// DatabaseAdminDatabasePath returns the path for the database resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, database) +// instead. +func DatabaseAdminDatabasePath(project, instance, database string) string { + return "" + + "projects/" + + project + + "/instances/" + + instance + + "/databases/" + + database + + "" +} diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/doc.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/doc.go new file mode 100644 index 0000000000..574f289b14 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/doc.go @@ -0,0 +1,46 @@ +// 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 instance is an auto-generated package for the +// Cloud Spanner Instance Admin API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +package instance // import "cloud.google.com/go/spanner/admin/instance/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/spanner.admin", + } +} diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client.go new file mode 100644 index 0000000000..57cabfdc21 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client.go @@ -0,0 +1,707 @@ +// 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 instance + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + iampb "google.golang.org/genproto/googleapis/iam/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + instancepb "google.golang.org/genproto/googleapis/spanner/admin/instance/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// InstanceAdminCallOptions contains the retry settings for each method of InstanceAdminClient. +type InstanceAdminCallOptions struct { + ListInstanceConfigs []gax.CallOption + GetInstanceConfig []gax.CallOption + ListInstances []gax.CallOption + GetInstance []gax.CallOption + CreateInstance []gax.CallOption + UpdateInstance []gax.CallOption + DeleteInstance []gax.CallOption + SetIamPolicy []gax.CallOption + GetIamPolicy []gax.CallOption + TestIamPermissions []gax.CallOption +} + +func defaultInstanceAdminClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("spanner.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultInstanceAdminCallOptions() *InstanceAdminCallOptions { + 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: 1000 * time.Millisecond, + Max: 32000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &InstanceAdminCallOptions{ + ListInstanceConfigs: retry[[2]string{"default", "idempotent"}], + GetInstanceConfig: retry[[2]string{"default", "idempotent"}], + ListInstances: retry[[2]string{"default", "idempotent"}], + GetInstance: retry[[2]string{"default", "idempotent"}], + CreateInstance: retry[[2]string{"default", "non_idempotent"}], + UpdateInstance: retry[[2]string{"default", "non_idempotent"}], + DeleteInstance: retry[[2]string{"default", "idempotent"}], + SetIamPolicy: retry[[2]string{"default", "non_idempotent"}], + GetIamPolicy: retry[[2]string{"default", "idempotent"}], + TestIamPermissions: retry[[2]string{"default", "non_idempotent"}], + } +} + +// InstanceAdminClient is a client for interacting with Cloud Spanner Instance Admin API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type InstanceAdminClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + instanceAdminClient instancepb.InstanceAdminClient + + // 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 *InstanceAdminCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewInstanceAdminClient creates a new instance admin client. +// +// Cloud Spanner Instance Admin API +// +// The Cloud Spanner Instance Admin API can be used to create, delete, +// modify and list instances. Instances are dedicated Cloud Spanner serving +// and storage resources to be used by Cloud Spanner databases. +// +// Each instance has a "configuration", which dictates where the +// serving resources for the Cloud Spanner instance are located (e.g., +// US-central, Europe). Configurations are created by Google based on +// resource availability. +// +// Cloud Spanner billing is based on the instances that exist and their +// sizes. After an instance exists, there are no additional +// per-database or per-operation charges for use of the instance +// (though there may be additional network bandwidth charges). +// Instances offer isolation: problems with databases in one instance +// will not affect other instances. However, within an instance +// databases can affect each other. For example, if one database in an +// instance receives a lot of requests and consumes most of the +// instance resources, fewer resources are available for other +// databases in that instance, and their performance may suffer. +func NewInstanceAdminClient(ctx context.Context, opts ...option.ClientOption) (*InstanceAdminClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultInstanceAdminClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &InstanceAdminClient{ + conn: conn, + CallOptions: defaultInstanceAdminCallOptions(), + + instanceAdminClient: instancepb.NewInstanceAdminClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *InstanceAdminClient) 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 *InstanceAdminClient) 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 *InstanceAdminClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListInstanceConfigs lists the supported instance configurations for a given project. +func (c *InstanceAdminClient) ListInstanceConfigs(ctx context.Context, req *instancepb.ListInstanceConfigsRequest, opts ...gax.CallOption) *InstanceConfigIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListInstanceConfigs[0:len(c.CallOptions.ListInstanceConfigs):len(c.CallOptions.ListInstanceConfigs)], opts...) + it := &InstanceConfigIterator{} + req = proto.Clone(req).(*instancepb.ListInstanceConfigsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*instancepb.InstanceConfig, string, error) { + var resp *instancepb.ListInstanceConfigsResponse + 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.instanceAdminClient.ListInstanceConfigs(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.InstanceConfigs, 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 +} + +// GetInstanceConfig gets information about a particular instance configuration. +func (c *InstanceAdminClient) GetInstanceConfig(ctx context.Context, req *instancepb.GetInstanceConfigRequest, opts ...gax.CallOption) (*instancepb.InstanceConfig, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetInstanceConfig[0:len(c.CallOptions.GetInstanceConfig):len(c.CallOptions.GetInstanceConfig)], opts...) + var resp *instancepb.InstanceConfig + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.instanceAdminClient.GetInstanceConfig(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListInstances lists all instances in the given project. +func (c *InstanceAdminClient) ListInstances(ctx context.Context, req *instancepb.ListInstancesRequest, opts ...gax.CallOption) *InstanceIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListInstances[0:len(c.CallOptions.ListInstances):len(c.CallOptions.ListInstances)], opts...) + it := &InstanceIterator{} + req = proto.Clone(req).(*instancepb.ListInstancesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*instancepb.Instance, string, error) { + var resp *instancepb.ListInstancesResponse + 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.instanceAdminClient.ListInstances(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Instances, 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 +} + +// GetInstance gets information about a particular instance. +func (c *InstanceAdminClient) GetInstance(ctx context.Context, req *instancepb.GetInstanceRequest, opts ...gax.CallOption) (*instancepb.Instance, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetInstance[0:len(c.CallOptions.GetInstance):len(c.CallOptions.GetInstance)], opts...) + var resp *instancepb.Instance + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.instanceAdminClient.GetInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// CreateInstance creates an instance and begins preparing it to begin serving. The +// returned [long-running operation][google.longrunning.Operation] +// can be used to track the progress of preparing the new +// instance. The instance name is assigned by the caller. If the +// named instance already exists, CreateInstance returns +// ALREADY_EXISTS. +// +// Immediately upon completion of this request: +// +// The instance is readable via the API, with all requested attributes +// but no allocated resources. Its state is CREATING. +// +// Until completion of the returned operation: +// +// Cancelling the operation renders the instance immediately unreadable +// via the API. +// +// The instance can be deleted. +// +// All other attempts to modify the instance are rejected. +// +// Upon completion of the returned operation: +// +// Billing for all successfully-allocated resources begins (some types +// may have lower than the requested levels). +// +// Databases can be created in the instance. +// +// The instance's allocated resource levels are readable via the API. +// +// The instance's state becomes READY. +// +// The returned [long-running operation][google.longrunning.Operation] will +// have a name of the format /operations/ and +// can be used to track creation of the instance. The +// [metadata][google.longrunning.Operation.metadata] field type is +// [CreateInstanceMetadata][google.spanner.admin.instance.v1.CreateInstanceMetadata]. +// The [response][google.longrunning.Operation.response] field type is +// [Instance][google.spanner.admin.instance.v1.Instance], if successful. +func (c *InstanceAdminClient) CreateInstance(ctx context.Context, req *instancepb.CreateInstanceRequest, opts ...gax.CallOption) (*CreateInstanceOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateInstance[0:len(c.CallOptions.CreateInstance):len(c.CallOptions.CreateInstance)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.instanceAdminClient.CreateInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &CreateInstanceOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// UpdateInstance updates an instance, and begins allocating or releasing resources +// as requested. The returned [long-running +// operation][google.longrunning.Operation] can be used to track the +// progress of updating the instance. If the named instance does not +// exist, returns NOT_FOUND. +// +// Immediately upon completion of this request: +// +// For resource types for which a decrease in the instance's allocation +// has been requested, billing is based on the newly-requested level. +// +// Until completion of the returned operation: +// +// Cancelling the operation sets its metadata's +// [cancel_time][google.spanner.admin.instance.v1.UpdateInstanceMetadata.cancel_time], and begins +// restoring resources to their pre-request values. The operation +// is guaranteed to succeed at undoing all resource changes, +// after which point it terminates with a CANCELLED status. +// +// All other attempts to modify the instance are rejected. +// +// Reading the instance via the API continues to give the pre-request +// resource levels. +// +// Upon completion of the returned operation: +// +// Billing begins for all successfully-allocated resources (some types +// may have lower than the requested levels). +// +// All newly-reserved resources are available for serving the instance's +// tables. +// +// The instance's new resource levels are readable via the API. +// +// The returned [long-running operation][google.longrunning.Operation] will +// have a name of the format /operations/ and +// can be used to track the instance modification. The +// [metadata][google.longrunning.Operation.metadata] field type is +// [UpdateInstanceMetadata][google.spanner.admin.instance.v1.UpdateInstanceMetadata]. +// The [response][google.longrunning.Operation.response] field type is +// [Instance][google.spanner.admin.instance.v1.Instance], if successful. +// +// Authorization requires spanner.instances.update permission on +// resource [name][google.spanner.admin.instance.v1.Instance.name]. +func (c *InstanceAdminClient) UpdateInstance(ctx context.Context, req *instancepb.UpdateInstanceRequest, opts ...gax.CallOption) (*UpdateInstanceOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.UpdateInstance[0:len(c.CallOptions.UpdateInstance):len(c.CallOptions.UpdateInstance)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.instanceAdminClient.UpdateInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &UpdateInstanceOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// DeleteInstance deletes an instance. +// +// Immediately upon completion of the request: +// +// Billing ceases for all of the instance's reserved resources. +// +// Soon afterward: +// +// The instance and all of its databases immediately and +// irrevocably disappear from the API. All data in the databases +// is permanently deleted. +func (c *InstanceAdminClient) DeleteInstance(ctx context.Context, req *instancepb.DeleteInstanceRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteInstance[0:len(c.CallOptions.DeleteInstance):len(c.CallOptions.DeleteInstance)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.instanceAdminClient.DeleteInstance(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// SetIamPolicy sets the access control policy on an instance resource. Replaces any +// existing policy. +// +// Authorization requires spanner.instances.setIamPolicy on +// [resource][google.iam.v1.SetIamPolicyRequest.resource]. +func (c *InstanceAdminClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.instanceAdminClient.SetIamPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetIamPolicy gets the access control policy for an instance resource. Returns an empty +// policy if an instance exists but does not have a policy set. +// +// Authorization requires spanner.instances.getIamPolicy on +// [resource][google.iam.v1.GetIamPolicyRequest.resource]. +func (c *InstanceAdminClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.instanceAdminClient.GetIamPolicy(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// TestIamPermissions returns permissions that the caller has on the specified instance resource. +// +// Attempting this RPC on a non-existent Cloud Spanner instance resource will +// result in a NOT_FOUND error if the user has spanner.instances.list +// permission on the containing Google Cloud Project. Otherwise returns an +// empty set of permissions. +func (c *InstanceAdminClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + 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.instanceAdminClient.TestIamPermissions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// InstanceConfigIterator manages a stream of *instancepb.InstanceConfig. +type InstanceConfigIterator struct { + items []*instancepb.InstanceConfig + 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 []*instancepb.InstanceConfig, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *InstanceConfigIterator) 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 *InstanceConfigIterator) Next() (*instancepb.InstanceConfig, error) { + var item *instancepb.InstanceConfig + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *InstanceConfigIterator) bufLen() int { + return len(it.items) +} + +func (it *InstanceConfigIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// InstanceIterator manages a stream of *instancepb.Instance. +type InstanceIterator struct { + items []*instancepb.Instance + 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 []*instancepb.Instance, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *InstanceIterator) 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 *InstanceIterator) Next() (*instancepb.Instance, error) { + var item *instancepb.Instance + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *InstanceIterator) bufLen() int { + return len(it.items) +} + +func (it *InstanceIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} + +// CreateInstanceOperation manages a long-running operation from CreateInstance. +type CreateInstanceOperation struct { + lro *longrunning.Operation +} + +// CreateInstanceOperation returns a new CreateInstanceOperation from a given name. +// The name must be that of a previously created CreateInstanceOperation, possibly from a different process. +func (c *InstanceAdminClient) CreateInstanceOperation(name string) *CreateInstanceOperation { + return &CreateInstanceOperation{ + 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 *CreateInstanceOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) { + var resp instancepb.Instance + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *CreateInstanceOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) { + var resp instancepb.Instance + 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 *CreateInstanceOperation) Metadata() (*instancepb.CreateInstanceMetadata, error) { + var meta instancepb.CreateInstanceMetadata + 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 *CreateInstanceOperation) 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 *CreateInstanceOperation) Name() string { + return op.lro.Name() +} + +// UpdateInstanceOperation manages a long-running operation from UpdateInstance. +type UpdateInstanceOperation struct { + lro *longrunning.Operation +} + +// UpdateInstanceOperation returns a new UpdateInstanceOperation from a given name. +// The name must be that of a previously created UpdateInstanceOperation, possibly from a different process. +func (c *InstanceAdminClient) UpdateInstanceOperation(name string) *UpdateInstanceOperation { + return &UpdateInstanceOperation{ + 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 *UpdateInstanceOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) { + var resp instancepb.Instance + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *UpdateInstanceOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) { + var resp instancepb.Instance + 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 *UpdateInstanceOperation) Metadata() (*instancepb.UpdateInstanceMetadata, error) { + var meta instancepb.UpdateInstanceMetadata + 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 *UpdateInstanceOperation) 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 *UpdateInstanceOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client_example_test.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client_example_test.go new file mode 100644 index 0000000000..894fcaec6e --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client_example_test.go @@ -0,0 +1,235 @@ +// 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 instance_test + +import ( + "cloud.google.com/go/spanner/admin/instance/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + iampb "google.golang.org/genproto/googleapis/iam/v1" + instancepb "google.golang.org/genproto/googleapis/spanner/admin/instance/v1" +) + +func ExampleNewInstanceAdminClient() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleInstanceAdminClient_ListInstanceConfigs() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &instancepb.ListInstanceConfigsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListInstanceConfigs(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleInstanceAdminClient_GetInstanceConfig() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &instancepb.GetInstanceConfigRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetInstanceConfig(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleInstanceAdminClient_ListInstances() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &instancepb.ListInstancesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListInstances(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleInstanceAdminClient_GetInstance() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &instancepb.GetInstanceRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleInstanceAdminClient_CreateInstance() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &instancepb.CreateInstanceRequest{ + // TODO: Fill request struct fields. + } + op, err := c.CreateInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleInstanceAdminClient_UpdateInstance() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &instancepb.UpdateInstanceRequest{ + // TODO: Fill request struct fields. + } + op, err := c.UpdateInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleInstanceAdminClient_DeleteInstance() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &instancepb.DeleteInstanceRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteInstance(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleInstanceAdminClient_SetIamPolicy() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(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 ExampleInstanceAdminClient_GetIamPolicy() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(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 ExampleInstanceAdminClient_TestIamPermissions() { + ctx := context.Background() + c, err := instance.NewInstanceAdminClient(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 +} diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/mock_test.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/mock_test.go new file mode 100644 index 0000000000..7b0e1ad891 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/mock_test.go @@ -0,0 +1,917 @@ +// 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 instance + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + iampb "google.golang.org/genproto/googleapis/iam/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + instancepb "google.golang.org/genproto/googleapis/spanner/admin/instance/v1" + field_maskpb "google.golang.org/genproto/protobuf/field_mask" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockInstanceAdminServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + instancepb.InstanceAdminServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockInstanceAdminServer) ListInstanceConfigs(ctx context.Context, req *instancepb.ListInstanceConfigsRequest) (*instancepb.ListInstanceConfigsResponse, 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].(*instancepb.ListInstanceConfigsResponse), nil +} + +func (s *mockInstanceAdminServer) GetInstanceConfig(ctx context.Context, req *instancepb.GetInstanceConfigRequest) (*instancepb.InstanceConfig, 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].(*instancepb.InstanceConfig), nil +} + +func (s *mockInstanceAdminServer) ListInstances(ctx context.Context, req *instancepb.ListInstancesRequest) (*instancepb.ListInstancesResponse, 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].(*instancepb.ListInstancesResponse), nil +} + +func (s *mockInstanceAdminServer) GetInstance(ctx context.Context, req *instancepb.GetInstanceRequest) (*instancepb.Instance, 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].(*instancepb.Instance), nil +} + +func (s *mockInstanceAdminServer) CreateInstance(ctx context.Context, req *instancepb.CreateInstanceRequest) (*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 *mockInstanceAdminServer) UpdateInstance(ctx context.Context, req *instancepb.UpdateInstanceRequest) (*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 *mockInstanceAdminServer) DeleteInstance(ctx context.Context, req *instancepb.DeleteInstanceRequest) (*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 *mockInstanceAdminServer) 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 *mockInstanceAdminServer) 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 *mockInstanceAdminServer) 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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockInstanceAdmin mockInstanceAdminServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + instancepb.RegisterInstanceAdminServer(serv, &mockInstanceAdmin) + + 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 TestInstanceAdminListInstanceConfigs(t *testing.T) { + var nextPageToken string = "" + var instanceConfigsElement *instancepb.InstanceConfig = &instancepb.InstanceConfig{} + var instanceConfigs = []*instancepb.InstanceConfig{instanceConfigsElement} + var expectedResponse = &instancepb.ListInstanceConfigsResponse{ + NextPageToken: nextPageToken, + InstanceConfigs: instanceConfigs, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &instancepb.ListInstanceConfigsRequest{ + Parent: formattedParent, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInstanceConfigs(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.InstanceConfigs[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 TestInstanceAdminListInstanceConfigsError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &instancepb.ListInstanceConfigsRequest{ + Parent: formattedParent, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInstanceConfigs(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 TestInstanceAdminGetInstanceConfig(t *testing.T) { + var name2 string = "name2-1052831874" + var displayName string = "displayName1615086568" + var expectedResponse = &instancepb.InstanceConfig{ + Name: name2, + DisplayName: displayName, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/instanceConfigs/%s", "[PROJECT]", "[INSTANCE_CONFIG]") + var request = &instancepb.GetInstanceConfigRequest{ + Name: formattedName, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInstanceConfig(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockInstanceAdmin.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 TestInstanceAdminGetInstanceConfigError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/instanceConfigs/%s", "[PROJECT]", "[INSTANCE_CONFIG]") + var request = &instancepb.GetInstanceConfigRequest{ + Name: formattedName, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInstanceConfig(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 TestInstanceAdminListInstances(t *testing.T) { + var nextPageToken string = "" + var instancesElement *instancepb.Instance = &instancepb.Instance{} + var instances = []*instancepb.Instance{instancesElement} + var expectedResponse = &instancepb.ListInstancesResponse{ + NextPageToken: nextPageToken, + Instances: instances, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &instancepb.ListInstancesRequest{ + Parent: formattedParent, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInstances(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Instances[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 TestInstanceAdminListInstancesError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var request = &instancepb.ListInstancesRequest{ + Parent: formattedParent, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListInstances(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 TestInstanceAdminGetInstance(t *testing.T) { + var name2 string = "name2-1052831874" + var config string = "config-1354792126" + var displayName string = "displayName1615086568" + var nodeCount int32 = 1539922066 + var expectedResponse = &instancepb.Instance{ + Name: name2, + Config: config, + DisplayName: displayName, + NodeCount: nodeCount, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &instancepb.GetInstanceRequest{ + Name: formattedName, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInstance(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockInstanceAdmin.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 TestInstanceAdminGetInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &instancepb.GetInstanceRequest{ + Name: formattedName, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetInstance(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 TestInstanceAdminCreateInstance(t *testing.T) { + var name string = "name3373707" + var config string = "config-1354792126" + var displayName string = "displayName1615086568" + var nodeCount int32 = 1539922066 + var expectedResponse = &instancepb.Instance{ + Name: name, + Config: config, + DisplayName: displayName, + NodeCount: nodeCount, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") + var instanceId string = "instanceId-2101995259" + var instance *instancepb.Instance = &instancepb.Instance{} + var request = &instancepb.CreateInstanceRequest{ + Parent: formattedParent, + InstanceId: instanceId, + Instance: instance, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateInstance(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, mockInstanceAdmin.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 TestInstanceAdminCreateInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = nil + mockInstanceAdmin.resps = append(mockInstanceAdmin.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 instanceId string = "instanceId-2101995259" + var instance *instancepb.Instance = &instancepb.Instance{} + var request = &instancepb.CreateInstanceRequest{ + Parent: formattedParent, + InstanceId: instanceId, + Instance: instance, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.CreateInstance(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 TestInstanceAdminUpdateInstance(t *testing.T) { + var name string = "name3373707" + var config string = "config-1354792126" + var displayName string = "displayName1615086568" + var nodeCount int32 = 1539922066 + var expectedResponse = &instancepb.Instance{ + Name: name, + Config: config, + DisplayName: displayName, + NodeCount: nodeCount, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var instance *instancepb.Instance = &instancepb.Instance{} + var fieldMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &instancepb.UpdateInstanceRequest{ + Instance: instance, + FieldMask: fieldMask, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateInstance(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, mockInstanceAdmin.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 TestInstanceAdminUpdateInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = nil + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var instance *instancepb.Instance = &instancepb.Instance{} + var fieldMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{} + var request = &instancepb.UpdateInstanceRequest{ + Instance: instance, + FieldMask: fieldMask, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.UpdateInstance(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 TestInstanceAdminDeleteInstance(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &instancepb.DeleteInstanceRequest{ + Name: formattedName, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteInstance(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestInstanceAdminDeleteInstanceError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &instancepb.DeleteInstanceRequest{ + Name: formattedName, + } + + c, err := NewInstanceAdminClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteInstance(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 TestInstanceAdminSetIamPolicy(t *testing.T) { + var version int32 = 351608024 + var etag []byte = []byte("21") + var expectedResponse = &iampb.Policy{ + Version: version, + Etag: etag, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var policy *iampb.Policy = &iampb.Policy{} + var request = &iampb.SetIamPolicyRequest{ + Resource: formattedResource, + Policy: policy, + } + + c, err := NewInstanceAdminClient(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, mockInstanceAdmin.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 TestInstanceAdminSetIamPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var policy *iampb.Policy = &iampb.Policy{} + var request = &iampb.SetIamPolicyRequest{ + Resource: formattedResource, + Policy: policy, + } + + c, err := NewInstanceAdminClient(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 TestInstanceAdminGetIamPolicy(t *testing.T) { + var version int32 = 351608024 + var etag []byte = []byte("21") + var expectedResponse = &iampb.Policy{ + Version: version, + Etag: etag, + } + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &iampb.GetIamPolicyRequest{ + Resource: formattedResource, + } + + c, err := NewInstanceAdminClient(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, mockInstanceAdmin.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 TestInstanceAdminGetIamPolicyError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var request = &iampb.GetIamPolicyRequest{ + Resource: formattedResource, + } + + c, err := NewInstanceAdminClient(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 TestInstanceAdminTestIamPermissions(t *testing.T) { + var expectedResponse *iampb.TestIamPermissionsResponse = &iampb.TestIamPermissionsResponse{} + + mockInstanceAdmin.err = nil + mockInstanceAdmin.reqs = nil + + mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse) + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var permissions []string = nil + var request = &iampb.TestIamPermissionsRequest{ + Resource: formattedResource, + Permissions: permissions, + } + + c, err := NewInstanceAdminClient(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, mockInstanceAdmin.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 TestInstanceAdminTestIamPermissionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockInstanceAdmin.err = gstatus.Error(errCode, "test error") + + var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + var permissions []string = nil + var request = &iampb.TestIamPermissionsRequest{ + Resource: formattedResource, + Permissions: permissions, + } + + c, err := NewInstanceAdminClient(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 +} diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/path_funcs.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/path_funcs.go new file mode 100644 index 0000000000..a2255805f7 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/path_funcs.go @@ -0,0 +1,55 @@ +// 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. + +package instance + +// InstanceAdminProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func InstanceAdminProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// InstanceAdminInstanceConfigPath returns the path for the instance config resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/instanceConfigs/%s", project, instanceConfig) +// instead. +func InstanceAdminInstanceConfigPath(project, instanceConfig string) string { + return "" + + "projects/" + + project + + "/instanceConfigs/" + + instanceConfig + + "" +} + +// InstanceAdminInstancePath returns the path for the instance resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/instances/%s", project, instance) +// instead. +func InstanceAdminInstancePath(project, instance string) string { + return "" + + "projects/" + + project + + "/instances/" + + instance + + "" +} diff --git a/vendor/cloud.google.com/go/spanner/apiv1/doc.go b/vendor/cloud.google.com/go/spanner/apiv1/doc.go new file mode 100644 index 0000000000..61525ddf2e --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/apiv1/doc.go @@ -0,0 +1,50 @@ +// 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 spanner is an auto-generated package for the +// Cloud Spanner API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Cloud Spanner is a managed, mission-critical, globally consistent and +// scalable relational database service. +// +// Use the client at cloud.google.com/go/spanner in preference to this. +package spanner // import "cloud.google.com/go/spanner/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/spanner.data", + } +} diff --git a/vendor/cloud.google.com/go/spanner/apiv1/mock_test.go b/vendor/cloud.google.com/go/spanner/apiv1/mock_test.go new file mode 100644 index 0000000000..3315384fa5 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/apiv1/mock_test.go @@ -0,0 +1,1085 @@ +// 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 spanner + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + spannerpb "google.golang.org/genproto/googleapis/spanner/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockSpannerServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + spannerpb.SpannerServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockSpannerServer) CreateSession(ctx context.Context, req *spannerpb.CreateSessionRequest) (*spannerpb.Session, 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].(*spannerpb.Session), nil +} + +func (s *mockSpannerServer) GetSession(ctx context.Context, req *spannerpb.GetSessionRequest) (*spannerpb.Session, 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].(*spannerpb.Session), nil +} + +func (s *mockSpannerServer) ListSessions(ctx context.Context, req *spannerpb.ListSessionsRequest) (*spannerpb.ListSessionsResponse, 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].(*spannerpb.ListSessionsResponse), nil +} + +func (s *mockSpannerServer) DeleteSession(ctx context.Context, req *spannerpb.DeleteSessionRequest) (*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 *mockSpannerServer) ExecuteSql(ctx context.Context, req *spannerpb.ExecuteSqlRequest) (*spannerpb.ResultSet, 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].(*spannerpb.ResultSet), nil +} + +func (s *mockSpannerServer) ExecuteStreamingSql(req *spannerpb.ExecuteSqlRequest, stream spannerpb.Spanner_ExecuteStreamingSqlServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + s.reqs = append(s.reqs, req) + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*spannerpb.PartialResultSet)); err != nil { + return err + } + } + return nil +} + +func (s *mockSpannerServer) Read(ctx context.Context, req *spannerpb.ReadRequest) (*spannerpb.ResultSet, 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].(*spannerpb.ResultSet), nil +} + +func (s *mockSpannerServer) StreamingRead(req *spannerpb.ReadRequest, stream spannerpb.Spanner_StreamingReadServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + s.reqs = append(s.reqs, req) + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*spannerpb.PartialResultSet)); err != nil { + return err + } + } + return nil +} + +func (s *mockSpannerServer) BeginTransaction(ctx context.Context, req *spannerpb.BeginTransactionRequest) (*spannerpb.Transaction, 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].(*spannerpb.Transaction), nil +} + +func (s *mockSpannerServer) Commit(ctx context.Context, req *spannerpb.CommitRequest) (*spannerpb.CommitResponse, 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].(*spannerpb.CommitResponse), nil +} + +func (s *mockSpannerServer) Rollback(ctx context.Context, req *spannerpb.RollbackRequest) (*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 *mockSpannerServer) PartitionQuery(ctx context.Context, req *spannerpb.PartitionQueryRequest) (*spannerpb.PartitionResponse, 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].(*spannerpb.PartitionResponse), nil +} + +func (s *mockSpannerServer) PartitionRead(ctx context.Context, req *spannerpb.PartitionReadRequest) (*spannerpb.PartitionResponse, 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].(*spannerpb.PartitionResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockSpanner mockSpannerServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + spannerpb.RegisterSpannerServer(serv, &mockSpanner) + + 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 TestSpannerCreateSession(t *testing.T) { + var name string = "name3373707" + var expectedResponse = &spannerpb.Session{ + Name: name, + } + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &spannerpb.CreateSessionRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSession(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerCreateSessionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &spannerpb.CreateSessionRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSession(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 TestSpannerGetSession(t *testing.T) { + var name2 string = "name2-1052831874" + var expectedResponse = &spannerpb.Session{ + Name: name2, + } + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var request = &spannerpb.GetSessionRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSession(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerGetSessionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var request = &spannerpb.GetSessionRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetSession(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 TestSpannerListSessions(t *testing.T) { + var nextPageToken string = "" + var sessionsElement *spannerpb.Session = &spannerpb.Session{} + var sessions = []*spannerpb.Session{sessionsElement} + var expectedResponse = &spannerpb.ListSessionsResponse{ + NextPageToken: nextPageToken, + Sessions: sessions, + } + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &spannerpb.ListSessionsRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSessions(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Sessions[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 TestSpannerListSessionsError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + var request = &spannerpb.ListSessionsRequest{ + Database: formattedDatabase, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListSessions(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 TestSpannerDeleteSession(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var request = &spannerpb.DeleteSessionRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSession(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSpannerDeleteSessionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var request = &spannerpb.DeleteSessionRequest{ + Name: formattedName, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.DeleteSession(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 TestSpannerExecuteSql(t *testing.T) { + var expectedResponse *spannerpb.ResultSet = &spannerpb.ResultSet{} + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var sql string = "sql114126" + var request = &spannerpb.ExecuteSqlRequest{ + Session: formattedSession, + Sql: sql, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ExecuteSql(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerExecuteSqlError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var sql string = "sql114126" + var request = &spannerpb.ExecuteSqlRequest{ + Session: formattedSession, + Sql: sql, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ExecuteSql(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 TestSpannerExecuteStreamingSql(t *testing.T) { + var chunkedValue bool = true + var resumeToken []byte = []byte("103") + var expectedResponse = &spannerpb.PartialResultSet{ + ChunkedValue: chunkedValue, + ResumeToken: resumeToken, + } + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var sql string = "sql114126" + var request = &spannerpb.ExecuteSqlRequest{ + Session: formattedSession, + Sql: sql, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.ExecuteStreamingSql(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerExecuteStreamingSqlError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var sql string = "sql114126" + var request = &spannerpb.ExecuteSqlRequest{ + Session: formattedSession, + Sql: sql, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.ExecuteStreamingSql(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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 TestSpannerRead(t *testing.T) { + var expectedResponse *spannerpb.ResultSet = &spannerpb.ResultSet{} + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var table string = "table110115790" + var columns []string = nil + var keySet *spannerpb.KeySet = &spannerpb.KeySet{} + var request = &spannerpb.ReadRequest{ + Session: formattedSession, + Table: table, + Columns: columns, + KeySet: keySet, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Read(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerReadError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var table string = "table110115790" + var columns []string = nil + var keySet *spannerpb.KeySet = &spannerpb.KeySet{} + var request = &spannerpb.ReadRequest{ + Session: formattedSession, + Table: table, + Columns: columns, + KeySet: keySet, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Read(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 TestSpannerStreamingRead(t *testing.T) { + var chunkedValue bool = true + var resumeToken []byte = []byte("103") + var expectedResponse = &spannerpb.PartialResultSet{ + ChunkedValue: chunkedValue, + ResumeToken: resumeToken, + } + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var table string = "table110115790" + var columns []string = nil + var keySet *spannerpb.KeySet = &spannerpb.KeySet{} + var request = &spannerpb.ReadRequest{ + Session: formattedSession, + Table: table, + Columns: columns, + KeySet: keySet, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRead(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerStreamingReadError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var table string = "table110115790" + var columns []string = nil + var keySet *spannerpb.KeySet = &spannerpb.KeySet{} + var request = &spannerpb.ReadRequest{ + Session: formattedSession, + Table: table, + Columns: columns, + KeySet: keySet, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRead(context.Background(), request) + if err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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 TestSpannerBeginTransaction(t *testing.T) { + var id []byte = []byte("27") + var expectedResponse = &spannerpb.Transaction{ + Id: id, + } + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var options *spannerpb.TransactionOptions = &spannerpb.TransactionOptions{} + var request = &spannerpb.BeginTransactionRequest{ + Session: formattedSession, + Options: options, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BeginTransaction(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerBeginTransactionError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var options *spannerpb.TransactionOptions = &spannerpb.TransactionOptions{} + var request = &spannerpb.BeginTransactionRequest{ + Session: formattedSession, + Options: options, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BeginTransaction(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 TestSpannerCommit(t *testing.T) { + var expectedResponse *spannerpb.CommitResponse = &spannerpb.CommitResponse{} + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var mutations []*spannerpb.Mutation = nil + var request = &spannerpb.CommitRequest{ + Session: formattedSession, + Mutations: mutations, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Commit(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerCommitError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var mutations []*spannerpb.Mutation = nil + var request = &spannerpb.CommitRequest{ + Session: formattedSession, + Mutations: mutations, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Commit(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 TestSpannerRollback(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var transactionId []byte = []byte("28") + var request = &spannerpb.RollbackRequest{ + Session: formattedSession, + TransactionId: transactionId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.Rollback(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestSpannerRollbackError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var transactionId []byte = []byte("28") + var request = &spannerpb.RollbackRequest{ + Session: formattedSession, + TransactionId: transactionId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.Rollback(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 TestSpannerPartitionQuery(t *testing.T) { + var expectedResponse *spannerpb.PartitionResponse = &spannerpb.PartitionResponse{} + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var sql string = "sql114126" + var request = &spannerpb.PartitionQueryRequest{ + Session: formattedSession, + Sql: sql, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.PartitionQuery(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerPartitionQueryError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var sql string = "sql114126" + var request = &spannerpb.PartitionQueryRequest{ + Session: formattedSession, + Sql: sql, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.PartitionQuery(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 TestSpannerPartitionRead(t *testing.T) { + var expectedResponse *spannerpb.PartitionResponse = &spannerpb.PartitionResponse{} + + mockSpanner.err = nil + mockSpanner.reqs = nil + + mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse) + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var table string = "table110115790" + var keySet *spannerpb.KeySet = &spannerpb.KeySet{} + var request = &spannerpb.PartitionReadRequest{ + Session: formattedSession, + Table: table, + KeySet: keySet, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.PartitionRead(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpanner.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 TestSpannerPartitionReadError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpanner.err = gstatus.Error(errCode, "test error") + + var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]") + var table string = "table110115790" + var keySet *spannerpb.KeySet = &spannerpb.KeySet{} + var request = &spannerpb.PartitionReadRequest{ + Session: formattedSession, + Table: table, + KeySet: keySet, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.PartitionRead(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/vendor/cloud.google.com/go/spanner/apiv1/path_funcs.go b/vendor/cloud.google.com/go/spanner/apiv1/path_funcs.go new file mode 100644 index 0000000000..19f30adb39 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/apiv1/path_funcs.go @@ -0,0 +1,49 @@ +// 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. + +package spanner + +// DatabasePath returns the path for the database resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, database) +// instead. +func DatabasePath(project, instance, database string) string { + return "" + + "projects/" + + project + + "/instances/" + + instance + + "/databases/" + + database + + "" +} + +// SessionPath returns the path for the session resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", project, instance, database, session) +// instead. +func SessionPath(project, instance, database, session string) string { + return "" + + "projects/" + + project + + "/instances/" + + instance + + "/databases/" + + database + + "/sessions/" + + session + + "" +} diff --git a/vendor/cloud.google.com/go/spanner/apiv1/spanner_client.go b/vendor/cloud.google.com/go/spanner/apiv1/spanner_client.go new file mode 100644 index 0000000000..4827f5a758 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/apiv1/spanner_client.go @@ -0,0 +1,503 @@ +// 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 spanner + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + spannerpb "google.golang.org/genproto/googleapis/spanner/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 { + CreateSession []gax.CallOption + GetSession []gax.CallOption + ListSessions []gax.CallOption + DeleteSession []gax.CallOption + ExecuteSql []gax.CallOption + ExecuteStreamingSql []gax.CallOption + Read []gax.CallOption + StreamingRead []gax.CallOption + BeginTransaction []gax.CallOption + Commit []gax.CallOption + Rollback []gax.CallOption + PartitionQuery []gax.CallOption + PartitionRead []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("spanner.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: 1000 * time.Millisecond, + Max: 32000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + {"long_running", "long_running"}: { + gax.WithRetry(func() gax.Retryer { + return gax.OnCodes([]codes.Code{ + codes.Unavailable, + }, gax.Backoff{ + Initial: 1000 * time.Millisecond, + Max: 32000 * time.Millisecond, + Multiplier: 1.3, + }) + }), + }, + } + return &CallOptions{ + CreateSession: retry[[2]string{"default", "idempotent"}], + GetSession: retry[[2]string{"default", "idempotent"}], + ListSessions: retry[[2]string{"default", "idempotent"}], + DeleteSession: retry[[2]string{"default", "idempotent"}], + ExecuteSql: retry[[2]string{"default", "idempotent"}], + ExecuteStreamingSql: retry[[2]string{"default", "non_idempotent"}], + Read: retry[[2]string{"default", "idempotent"}], + StreamingRead: retry[[2]string{"default", "non_idempotent"}], + BeginTransaction: retry[[2]string{"default", "idempotent"}], + Commit: retry[[2]string{"long_running", "long_running"}], + Rollback: retry[[2]string{"default", "idempotent"}], + PartitionQuery: retry[[2]string{"default", "idempotent"}], + PartitionRead: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Cloud Spanner 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 spannerpb.SpannerClient + + // 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 spanner client. +// +// Cloud Spanner API +// +// The Cloud Spanner API can be used to manage sessions and execute +// transactions on data stored in Cloud Spanner databases. +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: spannerpb.NewSpannerClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// CreateSession creates a new session. A session can be used to perform +// transactions that read and/or modify data in a Cloud Spanner database. +// Sessions are meant to be reused for many consecutive +// transactions. +// +// Sessions can only execute one transaction at a time. To execute +// multiple concurrent read-write/write-only transactions, create +// multiple sessions. Note that standalone reads and queries use a +// transaction internally, and count toward the one transaction +// limit. +// +// Cloud Spanner limits the number of sessions that can exist at any given +// time; thus, it is a good idea to delete idle and/or unneeded sessions. +// Aside from explicit deletes, Cloud Spanner can delete sessions for which no +// operations are sent for more than an hour. If a session is deleted, +// requests to it return NOT_FOUND. +// +// Idle sessions can be kept alive by sending a trivial SQL query +// periodically, e.g., "SELECT 1". +func (c *Client) CreateSession(ctx context.Context, req *spannerpb.CreateSessionRequest, opts ...gax.CallOption) (*spannerpb.Session, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateSession[0:len(c.CallOptions.CreateSession):len(c.CallOptions.CreateSession)], opts...) + var resp *spannerpb.Session + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateSession(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetSession gets a session. Returns NOT_FOUND if the session does not exist. +// This is mainly useful for determining whether a session is still +// alive. +func (c *Client) GetSession(ctx context.Context, req *spannerpb.GetSessionRequest, opts ...gax.CallOption) (*spannerpb.Session, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetSession[0:len(c.CallOptions.GetSession):len(c.CallOptions.GetSession)], opts...) + var resp *spannerpb.Session + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetSession(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListSessions lists all sessions in a given database. +func (c *Client) ListSessions(ctx context.Context, req *spannerpb.ListSessionsRequest, opts ...gax.CallOption) *SessionIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListSessions[0:len(c.CallOptions.ListSessions):len(c.CallOptions.ListSessions)], opts...) + it := &SessionIterator{} + req = proto.Clone(req).(*spannerpb.ListSessionsRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*spannerpb.Session, string, error) { + var resp *spannerpb.ListSessionsResponse + 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.ListSessions(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Sessions, 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 +} + +// DeleteSession ends a session, releasing server resources associated with it. +func (c *Client) DeleteSession(ctx context.Context, req *spannerpb.DeleteSessionRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.DeleteSession[0:len(c.CallOptions.DeleteSession):len(c.CallOptions.DeleteSession)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.DeleteSession(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// ExecuteSql executes an SQL query, returning all rows in a single reply. This +// method cannot be used to return a result set larger than 10 MiB; +// if the query yields more data than that, the query fails with +// a FAILED_PRECONDITION error. +// +// Queries inside read-write transactions might return ABORTED. If +// this occurs, the application should restart the transaction from +// the beginning. See [Transaction][google.spanner.v1.Transaction] for more details. +// +// Larger result sets can be fetched in streaming fashion by calling +// [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] instead. +func (c *Client) ExecuteSql(ctx context.Context, req *spannerpb.ExecuteSqlRequest, opts ...gax.CallOption) (*spannerpb.ResultSet, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ExecuteSql[0:len(c.CallOptions.ExecuteSql):len(c.CallOptions.ExecuteSql)], opts...) + var resp *spannerpb.ResultSet + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ExecuteSql(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ExecuteStreamingSql like [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], except returns the result +// set as a stream. Unlike [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], there +// is no limit on the size of the returned result set. However, no +// individual row in the result set can exceed 100 MiB, and no +// column value can exceed 10 MiB. +func (c *Client) ExecuteStreamingSql(ctx context.Context, req *spannerpb.ExecuteSqlRequest, opts ...gax.CallOption) (spannerpb.Spanner_ExecuteStreamingSqlClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ExecuteStreamingSql[0:len(c.CallOptions.ExecuteStreamingSql):len(c.CallOptions.ExecuteStreamingSql)], opts...) + var resp spannerpb.Spanner_ExecuteStreamingSqlClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ExecuteStreamingSql(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Read reads rows from the database using key lookups and scans, as a +// simple key/value style alternative to +// [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql]. This method cannot be used to +// return a result set larger than 10 MiB; if the read matches more +// data than that, the read fails with a FAILED_PRECONDITION +// error. +// +// Reads inside read-write transactions might return ABORTED. If +// this occurs, the application should restart the transaction from +// the beginning. See [Transaction][google.spanner.v1.Transaction] for more details. +// +// Larger result sets can be yielded in streaming fashion by calling +// [StreamingRead][google.spanner.v1.Spanner.StreamingRead] instead. +func (c *Client) Read(ctx context.Context, req *spannerpb.ReadRequest, opts ...gax.CallOption) (*spannerpb.ResultSet, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Read[0:len(c.CallOptions.Read):len(c.CallOptions.Read)], opts...) + var resp *spannerpb.ResultSet + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.Read(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// StreamingRead like [Read][google.spanner.v1.Spanner.Read], except returns the result set as a +// stream. Unlike [Read][google.spanner.v1.Spanner.Read], there is no limit on the +// size of the returned result set. However, no individual row in +// the result set can exceed 100 MiB, and no column value can exceed +// 10 MiB. +func (c *Client) StreamingRead(ctx context.Context, req *spannerpb.ReadRequest, opts ...gax.CallOption) (spannerpb.Spanner_StreamingReadClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.StreamingRead[0:len(c.CallOptions.StreamingRead):len(c.CallOptions.StreamingRead)], opts...) + var resp spannerpb.Spanner_StreamingReadClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.StreamingRead(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// BeginTransaction begins a new transaction. This step can often be skipped: +// [Read][google.spanner.v1.Spanner.Read], [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and +// [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a +// side-effect. +func (c *Client) BeginTransaction(ctx context.Context, req *spannerpb.BeginTransactionRequest, opts ...gax.CallOption) (*spannerpb.Transaction, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BeginTransaction[0:len(c.CallOptions.BeginTransaction):len(c.CallOptions.BeginTransaction)], opts...) + var resp *spannerpb.Transaction + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.BeginTransaction(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Commit commits a transaction. The request includes the mutations to be +// applied to rows in the database. +// +// Commit might return an ABORTED error. This can occur at any time; +// commonly, the cause is conflicts with concurrent +// transactions. However, it can also happen for a variety of other +// reasons. If Commit returns ABORTED, the caller should re-attempt +// the transaction from the beginning, re-using the same session. +func (c *Client) Commit(ctx context.Context, req *spannerpb.CommitRequest, opts ...gax.CallOption) (*spannerpb.CommitResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Commit[0:len(c.CallOptions.Commit):len(c.CallOptions.Commit)], opts...) + var resp *spannerpb.CommitResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.Commit(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// Rollback rolls back a transaction, releasing any locks it holds. It is a good +// idea to call this for any transaction that includes one or more +// [Read][google.spanner.v1.Spanner.Read] or [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] requests and +// ultimately decides not to commit. +// +// Rollback returns OK if it successfully aborts the transaction, the +// transaction was already aborted, or the transaction is not +// found. Rollback never returns ABORTED. +func (c *Client) Rollback(ctx context.Context, req *spannerpb.RollbackRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Rollback[0:len(c.CallOptions.Rollback):len(c.CallOptions.Rollback)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.Rollback(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// PartitionQuery creates a set of partition tokens that can be used to execute a query +// operation in parallel. Each of the returned partition tokens can be used +// by [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] to specify a subset +// of the query result to read. The same session and read-only transaction +// must be used by the PartitionQueryRequest used to create the +// partition tokens and the ExecuteSqlRequests that use the partition tokens. +// Partition tokens become invalid when the session used to create them +// is deleted or begins a new transaction. +func (c *Client) PartitionQuery(ctx context.Context, req *spannerpb.PartitionQueryRequest, opts ...gax.CallOption) (*spannerpb.PartitionResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.PartitionQuery[0:len(c.CallOptions.PartitionQuery):len(c.CallOptions.PartitionQuery)], opts...) + var resp *spannerpb.PartitionResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.PartitionQuery(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// PartitionRead creates a set of partition tokens that can be used to execute a read +// operation in parallel. Each of the returned partition tokens can be used +// by [StreamingRead][google.spanner.v1.Spanner.StreamingRead] to specify a subset of the read +// result to read. The same session and read-only transaction must be used by +// the PartitionReadRequest used to create the partition tokens and the +// ReadRequests that use the partition tokens. +// Partition tokens become invalid when the session used to create them +// is deleted or begins a new transaction. +func (c *Client) PartitionRead(ctx context.Context, req *spannerpb.PartitionReadRequest, opts ...gax.CallOption) (*spannerpb.PartitionResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.PartitionRead[0:len(c.CallOptions.PartitionRead):len(c.CallOptions.PartitionRead)], opts...) + var resp *spannerpb.PartitionResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.PartitionRead(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SessionIterator manages a stream of *spannerpb.Session. +type SessionIterator struct { + items []*spannerpb.Session + 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 []*spannerpb.Session, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *SessionIterator) 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 *SessionIterator) Next() (*spannerpb.Session, error) { + var item *spannerpb.Session + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *SessionIterator) bufLen() int { + return len(it.items) +} + +func (it *SessionIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/spanner/apiv1/spanner_client_example_test.go b/vendor/cloud.google.com/go/spanner/apiv1/spanner_client_example_test.go new file mode 100644 index 0000000000..6806af9de6 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/apiv1/spanner_client_example_test.go @@ -0,0 +1,290 @@ +// 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 spanner_test + +import ( + "io" + + "cloud.google.com/go/spanner/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + spannerpb "google.golang.org/genproto/googleapis/spanner/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_CreateSession() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.CreateSessionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateSession(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_GetSession() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.GetSessionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetSession(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListSessions() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.ListSessionsRequest{ + // TODO: Fill request struct fields. + } + it := c.ListSessions(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_DeleteSession() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.DeleteSessionRequest{ + // TODO: Fill request struct fields. + } + err = c.DeleteSession(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_ExecuteSql() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.ExecuteSqlRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ExecuteSql(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ExecuteStreamingSql() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.ExecuteSqlRequest{ + // TODO: Fill request struct fields. + } + stream, err := c.ExecuteStreamingSql(ctx, req) + if err != nil { + // TODO: Handle error. + } + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_Read() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.ReadRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Read(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_StreamingRead() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.ReadRequest{ + // TODO: Fill request struct fields. + } + stream, err := c.StreamingRead(ctx, req) + if err != nil { + // TODO: Handle error. + } + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} + +func ExampleClient_BeginTransaction() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.BeginTransactionRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.BeginTransaction(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_Commit() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.CommitRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Commit(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_Rollback() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.RollbackRequest{ + // TODO: Fill request struct fields. + } + err = c.Rollback(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_PartitionQuery() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.PartitionQueryRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.PartitionQuery(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_PartitionRead() { + ctx := context.Background() + c, err := spanner.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &spannerpb.PartitionReadRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.PartitionRead(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/spanner/appengine.go b/vendor/cloud.google.com/go/spanner/appengine.go new file mode 100644 index 0000000000..c7ce1b4dfb --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/appengine.go @@ -0,0 +1,20 @@ +// Copyright 2017 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 appengine + +package spanner + +// numChannels is the default value for NumChannels of client +const numChannels = 1 diff --git a/vendor/cloud.google.com/go/spanner/backoff.go b/vendor/cloud.google.com/go/spanner/backoff.go new file mode 100644 index 0000000000..b2e13e9e4d --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/backoff.go @@ -0,0 +1,58 @@ +/* +Copyright 2017 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 spanner + +import ( + "math/rand" + "time" +) + +const ( + // minBackoff is the minimum backoff used by default. + minBackoff = 1 * time.Second + // maxBackoff is the maximum backoff used by default. + maxBackoff = 32 * time.Second + // jitter is the jitter factor. + jitter = 0.4 + // rate is the rate of exponential increase in the backoff. + rate = 1.3 +) + +var defaultBackoff = exponentialBackoff{minBackoff, maxBackoff} + +type exponentialBackoff struct { + min, max time.Duration +} + +// delay calculates the delay that should happen at n-th +// exponential backoff in a series. +func (b exponentialBackoff) delay(retries int) time.Duration { + min, max := float64(b.min), float64(b.max) + delay := min + for delay < max && retries > 0 { + delay *= rate + retries-- + } + if delay > max { + delay = max + } + delay -= delay * jitter * rand.Float64() + if delay < min { + delay = min + } + return time.Duration(delay) +} diff --git a/vendor/cloud.google.com/go/spanner/backoff_test.go b/vendor/cloud.google.com/go/spanner/backoff_test.go new file mode 100644 index 0000000000..179be2dec1 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/backoff_test.go @@ -0,0 +1,62 @@ +/* +Copyright 2017 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 spanner + +import ( + "math" + "time" + + "testing" +) + +// Test if exponential backoff helper can produce correct series of +// retry delays. +func TestBackoff(t *testing.T) { + b := exponentialBackoff{minBackoff, maxBackoff} + tests := []struct { + retries int + min time.Duration + max time.Duration + }{ + { + retries: 0, + min: minBackoff, + max: minBackoff, + }, + { + retries: 1, + min: minBackoff, + max: time.Duration(rate * float64(minBackoff)), + }, + { + retries: 3, + min: time.Duration(math.Pow(rate, 3) * (1 - jitter) * float64(minBackoff)), + max: time.Duration(math.Pow(rate, 3) * float64(minBackoff)), + }, + { + retries: 1000, + min: time.Duration((1 - jitter) * float64(maxBackoff)), + max: maxBackoff, + }, + } + for _, test := range tests { + got := b.delay(test.retries) + if float64(got) < float64(test.min) || float64(got) > float64(test.max) { + t.Errorf("delay(%v) = %v, want in range [%v, %v]", test.retries, got, test.min, test.max) + } + } +} diff --git a/vendor/cloud.google.com/go/spanner/batch.go b/vendor/cloud.google.com/go/spanner/batch.go new file mode 100644 index 0000000000..18ce8ff186 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/batch.go @@ -0,0 +1,345 @@ +/* +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 spanner + +import ( + "bytes" + "encoding/gob" + "log" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// BatchReadOnlyTransaction is a ReadOnlyTransaction that allows for exporting +// arbitrarily large amounts of data from Cloud Spanner databases. +// BatchReadOnlyTransaction partitions a read/query request. Read/query request +// can then be executed independently over each partition while observing the +// same snapshot of the database. BatchReadOnlyTransaction can also be shared +// across multiple clients by passing around the BatchReadOnlyTransactionID and +// then recreating the transaction using Client.BatchReadOnlyTransactionFromID. +// +// Note: if a client is used only to run partitions, you can +// create it using a ClientConfig with both MinOpened and MaxIdle set to +// zero to avoid creating unnecessary sessions. You can also avoid excess +// gRPC channels by setting ClientConfig.NumChannels to the number of +// concurrently active BatchReadOnlyTransactions you expect to have. +type BatchReadOnlyTransaction struct { + ReadOnlyTransaction + ID BatchReadOnlyTransactionID +} + +// BatchReadOnlyTransactionID is a unique identifier for a +// BatchReadOnlyTransaction. It can be used to re-create a +// BatchReadOnlyTransaction on a different machine or process by calling +// Client.BatchReadOnlyTransactionFromID. +type BatchReadOnlyTransactionID struct { + // unique ID for the transaction. + tid transactionID + // sid is the id of the Cloud Spanner session used for this transaction. + sid string + // rts is the read timestamp of this transaction. + rts time.Time +} + +// Partition defines a segment of data to be read in a batch read or query. A +// partition can be serialized and processed across several different machines +// or processes. +type Partition struct { + pt []byte + qreq *sppb.ExecuteSqlRequest + rreq *sppb.ReadRequest +} + +// PartitionOptions specifies options for a PartitionQueryRequest and +// PartitionReadRequest. See +// https://godoc.org/google.golang.org/genproto/googleapis/spanner/v1#PartitionOptions +// for more details. +type PartitionOptions struct { + // The desired data size for each partition generated. + PartitionBytes int64 + // The desired maximum number of partitions to return. + MaxPartitions int64 +} + +// toProto converts a spanner.PartitionOptions into a sppb.PartitionOptions +func (opt PartitionOptions) toProto() *sppb.PartitionOptions { + return &sppb.PartitionOptions{ + PartitionSizeBytes: opt.PartitionBytes, + MaxPartitions: opt.MaxPartitions, + } +} + +// PartitionRead returns a list of Partitions that can be used to read rows from +// the database. These partitions can be executed across multiple processes, +// even across different machines. The partition size and count hints can be +// configured using PartitionOptions. +func (t *BatchReadOnlyTransaction) PartitionRead(ctx context.Context, table string, keys KeySet, columns []string, opt PartitionOptions) ([]*Partition, error) { + return t.PartitionReadUsingIndex(ctx, table, "", keys, columns, opt) +} + +// PartitionReadUsingIndex returns a list of Partitions that can be used to read +// rows from the database using an index. +func (t *BatchReadOnlyTransaction) PartitionReadUsingIndex(ctx context.Context, table, index string, keys KeySet, columns []string, opt PartitionOptions) ([]*Partition, error) { + sh, ts, err := t.acquire(ctx) + if err != nil { + return nil, err + } + sid, client := sh.getID(), sh.getClient() + var ( + kset *sppb.KeySet + resp *sppb.PartitionResponse + partitions []*Partition + ) + kset, err = keys.keySetProto() + // request Partitions + if err != nil { + return nil, err + } + resp, err = client.PartitionRead(ctx, &sppb.PartitionReadRequest{ + Session: sid, + Transaction: ts, + Table: table, + Index: index, + Columns: columns, + KeySet: kset, + PartitionOptions: opt.toProto(), + }) + // prepare ReadRequest + req := &sppb.ReadRequest{ + Session: sid, + Transaction: ts, + Table: table, + Index: index, + Columns: columns, + KeySet: kset, + } + // generate Partitions + for _, p := range resp.GetPartitions() { + partitions = append(partitions, &Partition{ + pt: p.PartitionToken, + rreq: req, + }) + } + return partitions, err +} + +// PartitionQuery returns a list of Partitions that can be used to execute a query against the database. +func (t *BatchReadOnlyTransaction) PartitionQuery(ctx context.Context, statement Statement, opt PartitionOptions) ([]*Partition, error) { + sh, ts, err := t.acquire(ctx) + if err != nil { + return nil, err + } + sid, client := sh.getID(), sh.getClient() + var ( + resp *sppb.PartitionResponse + partitions []*Partition + ) + // request Partitions + req := &sppb.PartitionQueryRequest{ + Session: sid, + Transaction: ts, + Sql: statement.SQL, + PartitionOptions: opt.toProto(), + } + if err := statement.bindParams(req); err != nil { + return nil, err + } + resp, err = client.PartitionQuery(ctx, req) + // prepare ExecuteSqlRequest + r := &sppb.ExecuteSqlRequest{ + Session: sid, + Transaction: ts, + Sql: statement.SQL, + } + if err := statement.bindParams(r); err != nil { + return nil, err + } + // generate Partitions + for _, p := range resp.GetPartitions() { + partitions = append(partitions, &Partition{ + pt: p.PartitionToken, + qreq: r, + }) + } + return partitions, err +} + +// release implements txReadEnv.release, noop. +func (t *BatchReadOnlyTransaction) release(err error) { +} + +// setTimestamp implements txReadEnv.setTimestamp, noop. +// read timestamp is ready on txn initialization, avoid contending writing to it with future partitions. +func (t *BatchReadOnlyTransaction) setTimestamp(ts time.Time) { +} + +// Close marks the txn as closed. +func (t *BatchReadOnlyTransaction) Close() { + t.mu.Lock() + defer t.mu.Unlock() + t.state = txClosed +} + +// Cleanup cleans up all the resources used by this transaction and makes +// it unusable. Once this method is invoked, the transaction is no longer +// usable anywhere, including other clients/processes with which this +// transaction was shared. +// +// Calling Cleanup is optional, but recommended. If Cleanup is not called, the +// transaction's resources will be freed when the session expires on the backend and +// is deleted. For more information about recycled sessions, see +// https://cloud.google.com/spanner/docs/sessions. +func (t *BatchReadOnlyTransaction) Cleanup(ctx context.Context) { + t.Close() + t.mu.Lock() + defer t.mu.Unlock() + sh := t.sh + if sh == nil { + return + } + t.sh = nil + sid, client := sh.getID(), sh.getClient() + err := runRetryable(ctx, func(ctx context.Context) error { + _, e := client.DeleteSession(ctx, &sppb.DeleteSessionRequest{Name: sid}) + return e + }) + if err != nil { + log.Printf("Failed to delete session %v. Error: %v", sid, err) + } +} + +// Execute runs a single Partition obtained from PartitionRead or PartitionQuery. +func (t *BatchReadOnlyTransaction) Execute(ctx context.Context, p *Partition) *RowIterator { + var ( + sh *sessionHandle + err error + rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error) + ) + if sh, _, err = t.acquire(ctx); err != nil { + return &RowIterator{err: err} + } + client := sh.getClient() + if client == nil { + // Might happen if transaction is closed in the middle of a API call. + return &RowIterator{err: errSessionClosed(sh)} + } + // read or query partition + if p.rreq != nil { + p.rreq.PartitionToken = p.pt + rpc = func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) { + p.rreq.ResumeToken = resumeToken + return client.StreamingRead(ctx, p.rreq) + } + } else { + p.qreq.PartitionToken = p.pt + rpc = func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) { + p.qreq.ResumeToken = resumeToken + return client.ExecuteStreamingSql(ctx, p.qreq) + } + } + return stream( + contextWithOutgoingMetadata(ctx, sh.getMetadata()), + rpc, + t.setTimestamp, + t.release) +} + +// MarshalBinary implements BinaryMarshaler. +func (tid BatchReadOnlyTransactionID) MarshalBinary() (data []byte, err error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + if err := enc.Encode(tid.tid); err != nil { + return nil, err + } + if err := enc.Encode(tid.sid); err != nil { + return nil, err + } + if err := enc.Encode(tid.rts); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// UnmarshalBinary implements BinaryUnmarshaler. +func (tid *BatchReadOnlyTransactionID) UnmarshalBinary(data []byte) error { + dec := gob.NewDecoder(bytes.NewReader(data)) + if err := dec.Decode(&tid.tid); err != nil { + return err + } + if err := dec.Decode(&tid.sid); err != nil { + return err + } + return dec.Decode(&tid.rts) +} + +// MarshalBinary implements BinaryMarshaler. +func (p Partition) MarshalBinary() (data []byte, err error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + if err := enc.Encode(p.pt); err != nil { + return nil, err + } + var isReadPartition bool + var req proto.Message + if p.rreq != nil { + isReadPartition = true + req = p.rreq + } else { + isReadPartition = false + req = p.qreq + } + if err := enc.Encode(isReadPartition); err != nil { + return nil, err + } + if data, err = proto.Marshal(req); err != nil { + return nil, err + } + if err := enc.Encode(data); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// UnmarshalBinary implements BinaryUnmarshaler. +func (p *Partition) UnmarshalBinary(data []byte) error { + var ( + isReadPartition bool + d []byte + err error + ) + dec := gob.NewDecoder(bytes.NewReader(data)) + if err := dec.Decode(&p.pt); err != nil { + return err + } + if err := dec.Decode(&isReadPartition); err != nil { + return err + } + if err := dec.Decode(&d); err != nil { + return err + } + if isReadPartition { + p.rreq = &sppb.ReadRequest{} + err = proto.Unmarshal(d, p.rreq) + } else { + p.qreq = &sppb.ExecuteSqlRequest{} + err = proto.Unmarshal(d, p.qreq) + } + return err +} diff --git a/vendor/cloud.google.com/go/spanner/batch_test.go b/vendor/cloud.google.com/go/spanner/batch_test.go new file mode 100644 index 0000000000..fbae25cb8a --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/batch_test.go @@ -0,0 +1,73 @@ +/* +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 spanner + +import ( + "testing" + "time" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +func TestPartitionRoundTrip(t *testing.T) { + t.Parallel() + for i, want := range []Partition{ + {rreq: &sppb.ReadRequest{Table: "t"}}, + {qreq: &sppb.ExecuteSqlRequest{Sql: "sql"}}, + } { + got := serdesPartition(t, i, &want) + if !testEqual(got, want) { + t.Errorf("got: %#v\nwant:%#v", got, want) + } + } +} + +func TestBROTIDRoundTrip(t *testing.T) { + t.Parallel() + tm := time.Now() + want := BatchReadOnlyTransactionID{ + tid: []byte("tid"), + sid: "sid", + rts: tm, + } + data, err := want.MarshalBinary() + if err != nil { + t.Fatal(err) + } + var got BatchReadOnlyTransactionID + if err := got.UnmarshalBinary(data); err != nil { + t.Fatal(err) + } + if !testEqual(got, want) { + t.Errorf("got: %#v\nwant:%#v", got, want) + } +} + +// serdesPartition is a helper that serialize a Partition then deserialize it +func serdesPartition(t *testing.T, i int, p1 *Partition) (p2 Partition) { + var ( + data []byte + err error + ) + if data, err = p1.MarshalBinary(); err != nil { + t.Fatalf("#%d: encoding failed %v", i, err) + } + if err = p2.UnmarshalBinary(data); err != nil { + t.Fatalf("#%d: decoding failed %v", i, err) + } + return p2 +} diff --git a/vendor/cloud.google.com/go/spanner/client.go b/vendor/cloud.google.com/go/spanner/client.go new file mode 100644 index 0000000000..3759bc4f63 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/client.go @@ -0,0 +1,453 @@ +/* +Copyright 2017 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 spanner + +import ( + "fmt" + "log" + "regexp" + "sync/atomic" + "time" + + "cloud.google.com/go/internal/version" + "golang.org/x/net/context" + "google.golang.org/api/option" + gtransport "google.golang.org/api/transport/grpc" + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +const ( + endpoint = "spanner.googleapis.com:443" + + // resourcePrefixHeader is the name of the metadata header used to indicate + // the resource being operated on. + resourcePrefixHeader = "google-cloud-resource-prefix" + // xGoogHeaderKey is the name of the metadata header used to indicate client + // information. + xGoogHeaderKey = "x-goog-api-client" +) + +const ( + // Scope is the scope for Cloud Spanner Data API. + Scope = "https://www.googleapis.com/auth/spanner.data" + + // AdminScope is the scope for Cloud Spanner Admin APIs. + AdminScope = "https://www.googleapis.com/auth/spanner.admin" +) + +var ( + validDBPattern = regexp.MustCompile("^projects/[^/]+/instances/[^/]+/databases/[^/]+$") + xGoogHeaderVal = fmt.Sprintf("gl-go/%s gccl/%s grpc/%s", version.Go(), version.Repo, grpc.Version) +) + +func validDatabaseName(db string) error { + if matched := validDBPattern.MatchString(db); !matched { + return fmt.Errorf("database name %q should conform to pattern %q", + db, validDBPattern.String()) + } + return nil +} + +// Client is a client for reading and writing data to a Cloud Spanner database. A +// client is safe to use concurrently, except for its Close method. +type Client struct { + // rr must be accessed through atomic operations. + rr uint32 + conns []*grpc.ClientConn + clients []sppb.SpannerClient + database string + // Metadata to be sent with each request. + md metadata.MD + idleSessions *sessionPool + // sessionLabels for the sessions created by this client. + sessionLabels map[string]string +} + +// ClientConfig has configurations for the client. +type ClientConfig struct { + // NumChannels is the number of gRPC channels. + // If zero, a reasonable default is used based on the execution environment. + NumChannels int + co []option.ClientOption + // SessionPoolConfig is the configuration for session pool. + SessionPoolConfig + // SessionLabels for the sessions created by this client. + // See https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#session for more info. + SessionLabels map[string]string +} + +// errDial returns error for dialing to Cloud Spanner. +func errDial(ci int, err error) error { + e := toSpannerError(err).(*Error) + e.decorate(fmt.Sprintf("dialing fails for channel[%v]", ci)) + return e +} + +func contextWithOutgoingMetadata(ctx context.Context, md metadata.MD) context.Context { + existing, ok := metadata.FromOutgoingContext(ctx) + if ok { + md = metadata.Join(existing, md) + } + return metadata.NewOutgoingContext(ctx, md) +} + +// NewClient creates a client to a database. A valid database name has the +// form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID. It uses a default +// configuration. +func NewClient(ctx context.Context, database string, opts ...option.ClientOption) (*Client, error) { + return NewClientWithConfig(ctx, database, ClientConfig{}, opts...) +} + +// NewClientWithConfig creates a client to a database. A valid database name has the +// form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID. +func NewClientWithConfig(ctx context.Context, database string, config ClientConfig, opts ...option.ClientOption) (c *Client, err error) { + ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.NewClient") + defer func() { traceEndSpan(ctx, err) }() + + // Validate database path. + if err := validDatabaseName(database); err != nil { + return nil, err + } + c = &Client{ + database: database, + md: metadata.Pairs( + resourcePrefixHeader, database, + xGoogHeaderKey, xGoogHeaderVal), + } + // Make a copy of labels. + c.sessionLabels = make(map[string]string) + for k, v := range config.SessionLabels { + c.sessionLabels[k] = v + } + // gRPC options + allOpts := []option.ClientOption{ + option.WithEndpoint(endpoint), + option.WithScopes(Scope), + option.WithGRPCDialOption( + grpc.WithDefaultCallOptions( + grpc.MaxCallSendMsgSize(100<<20), + grpc.MaxCallRecvMsgSize(100<<20), + ), + ), + } + allOpts = append(allOpts, opts...) + // Prepare gRPC channels. + if config.NumChannels == 0 { + config.NumChannels = numChannels + } + // Default configs for session pool. + if config.MaxOpened == 0 { + config.MaxOpened = uint64(config.NumChannels * 100) + } + if config.MaxBurst == 0 { + config.MaxBurst = 10 + } + for i := 0; i < config.NumChannels; i++ { + conn, err := gtransport.Dial(ctx, allOpts...) + if err != nil { + return nil, errDial(i, err) + } + c.conns = append(c.conns, conn) + c.clients = append(c.clients, sppb.NewSpannerClient(conn)) + } + // Prepare session pool. + config.SessionPoolConfig.getRPCClient = func() (sppb.SpannerClient, error) { + // TODO: support more loadbalancing options. + return c.rrNext(), nil + } + config.SessionPoolConfig.sessionLabels = c.sessionLabels + sp, err := newSessionPool(database, config.SessionPoolConfig, c.md) + if err != nil { + c.Close() + return nil, err + } + c.idleSessions = sp + return c, nil +} + +// rrNext returns the next available Cloud Spanner RPC client in a round-robin manner. +func (c *Client) rrNext() sppb.SpannerClient { + return c.clients[atomic.AddUint32(&c.rr, 1)%uint32(len(c.clients))] +} + +// Close closes the client. +func (c *Client) Close() { + if c.idleSessions != nil { + c.idleSessions.close() + } + for _, conn := range c.conns { + conn.Close() + } +} + +// Single provides a read-only snapshot transaction optimized for the case +// where only a single read or query is needed. This is more efficient than +// using ReadOnlyTransaction() for a single read or query. +// +// Single will use a strong TimestampBound by default. Use +// ReadOnlyTransaction.WithTimestampBound to specify a different +// TimestampBound. A non-strong bound can be used to reduce latency, or +// "time-travel" to prior versions of the database, see the documentation of +// TimestampBound for details. +func (c *Client) Single() *ReadOnlyTransaction { + t := &ReadOnlyTransaction{singleUse: true, sp: c.idleSessions} + t.txReadOnly.txReadEnv = t + return t +} + +// ReadOnlyTransaction returns a ReadOnlyTransaction that can be used for +// multiple reads from the database. You must call Close() when the +// ReadOnlyTransaction is no longer needed to release resources on the server. +// +// ReadOnlyTransaction will use a strong TimestampBound by default. Use +// ReadOnlyTransaction.WithTimestampBound to specify a different +// TimestampBound. A non-strong bound can be used to reduce latency, or +// "time-travel" to prior versions of the database, see the documentation of +// TimestampBound for details. +func (c *Client) ReadOnlyTransaction() *ReadOnlyTransaction { + t := &ReadOnlyTransaction{ + singleUse: false, + sp: c.idleSessions, + txReadyOrClosed: make(chan struct{}), + } + t.txReadOnly.txReadEnv = t + return t +} + +// BatchReadOnlyTransaction returns a BatchReadOnlyTransaction that can be used +// for partitioned reads or queries from a snapshot of the database. This is +// useful in batch processing pipelines where one wants to divide the work of +// reading from the database across multiple machines. +// +// Note: This transaction does not use the underlying session pool but creates a +// new session each time, and the session is reused across clients. +// +// You should call Close() after the txn is no longer needed on local +// client, and call Cleanup() when the txn is finished for all clients, to free +// the session. +func (c *Client) BatchReadOnlyTransaction(ctx context.Context, tb TimestampBound) (*BatchReadOnlyTransaction, error) { + var ( + tx transactionID + rts time.Time + s *session + sh *sessionHandle + err error + ) + defer func() { + if err != nil && sh != nil { + e := runRetryable(ctx, func(ctx context.Context) error { + _, e := s.client.DeleteSession(ctx, &sppb.DeleteSessionRequest{Name: s.getID()}) + return e + }) + if e != nil { + log.Printf("Failed to delete session %v. Error: %v", s.getID(), e) + } + } + }() + // create session + sc := c.rrNext() + err = runRetryable(ctx, func(ctx context.Context) error { + sid, e := sc.CreateSession(ctx, &sppb.CreateSessionRequest{Database: c.database, Session: &sppb.Session{Labels: c.sessionLabels}}) + if e != nil { + return e + } + // If no error, construct the new session. + s = &session{valid: true, client: sc, id: sid.Name, createTime: time.Now(), md: c.md} + return nil + }) + if err != nil { + return nil, err + } + sh = &sessionHandle{session: s} + // begin transaction + err = runRetryable(contextWithOutgoingMetadata(ctx, sh.getMetadata()), func(ctx context.Context) error { + res, e := sh.getClient().BeginTransaction(ctx, &sppb.BeginTransactionRequest{ + Session: sh.getID(), + Options: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadOnly_{ + ReadOnly: buildTransactionOptionsReadOnly(tb, true), + }, + }, + }) + if e != nil { + return e + } + tx = res.Id + if res.ReadTimestamp != nil { + rts = time.Unix(res.ReadTimestamp.Seconds, int64(res.ReadTimestamp.Nanos)) + } + return nil + }) + if err != nil { + return nil, err + } + + t := &BatchReadOnlyTransaction{ + ReadOnlyTransaction: ReadOnlyTransaction{ + tx: tx, + txReadyOrClosed: make(chan struct{}), + state: txActive, + sh: sh, + rts: rts, + }, + ID: BatchReadOnlyTransactionID{ + tid: tx, + sid: sh.getID(), + rts: rts, + }, + } + t.txReadOnly.txReadEnv = t + return t, nil +} + +// BatchReadOnlyTransactionFromID reconstruct a BatchReadOnlyTransaction from BatchReadOnlyTransactionID +func (c *Client) BatchReadOnlyTransactionFromID(tid BatchReadOnlyTransactionID) *BatchReadOnlyTransaction { + sc := c.rrNext() + s := &session{valid: true, client: sc, id: tid.sid, createTime: time.Now(), md: c.md} + sh := &sessionHandle{session: s} + + t := &BatchReadOnlyTransaction{ + ReadOnlyTransaction: ReadOnlyTransaction{ + tx: tid.tid, + txReadyOrClosed: make(chan struct{}), + state: txActive, + sh: sh, + rts: tid.rts, + }, + ID: tid, + } + t.txReadOnly.txReadEnv = t + return t +} + +type transactionInProgressKey struct{} + +func checkNestedTxn(ctx context.Context) error { + if ctx.Value(transactionInProgressKey{}) != nil { + return spannerErrorf(codes.FailedPrecondition, "Cloud Spanner does not support nested transactions") + } + return nil +} + +// ReadWriteTransaction executes a read-write transaction, with retries as +// necessary. +// +// The function f will be called one or more times. It must not maintain +// any state between calls. +// +// If the transaction cannot be committed or if f returns an IsAborted error, +// ReadWriteTransaction will call f again. It will continue to call f until the +// transaction can be committed or the Context times out or is cancelled. If f +// returns an error other than IsAborted, ReadWriteTransaction will abort the +// transaction and return the error. +// +// To limit the number of retries, set a deadline on the Context rather than +// using a fixed limit on the number of attempts. ReadWriteTransaction will +// retry as needed until that deadline is met. +func (c *Client) ReadWriteTransaction(ctx context.Context, f func(context.Context, *ReadWriteTransaction) error) (commitTimestamp time.Time, err error) { + ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.ReadWriteTransaction") + defer func() { traceEndSpan(ctx, err) }() + if err := checkNestedTxn(ctx); err != nil { + return time.Time{}, err + } + var ( + ts time.Time + sh *sessionHandle + ) + err = runRetryableNoWrap(ctx, func(ctx context.Context) error { + var ( + err error + t *ReadWriteTransaction + ) + if sh == nil || sh.getID() == "" || sh.getClient() == nil { + // Session handle hasn't been allocated or has been destroyed. + sh, err = c.idleSessions.takeWriteSession(ctx) + if err != nil { + // If session retrieval fails, just fail the transaction. + return err + } + t = &ReadWriteTransaction{ + sh: sh, + tx: sh.getTransactionID(), + } + } else { + t = &ReadWriteTransaction{ + sh: sh, + } + } + t.txReadOnly.txReadEnv = t + tracePrintf(ctx, map[string]interface{}{"transactionID": string(sh.getTransactionID())}, + "Starting transaction attempt") + if err = t.begin(ctx); err != nil { + // Mask error from begin operation as retryable error. + return errRetry(err) + } + ts, err = t.runInTransaction(ctx, f) + return err + }) + if sh != nil { + sh.recycle() + } + return ts, err +} + +// applyOption controls the behavior of Client.Apply. +type applyOption struct { + // If atLeastOnce == true, Client.Apply will execute the mutations on Cloud Spanner at least once. + atLeastOnce bool +} + +// An ApplyOption is an optional argument to Apply. +type ApplyOption func(*applyOption) + +// ApplyAtLeastOnce returns an ApplyOption that removes replay protection. +// +// With this option, Apply may attempt to apply mutations more than once; if +// the mutations are not idempotent, this may lead to a failure being reported +// when the mutation was applied more than once. For example, an insert may +// fail with ALREADY_EXISTS even though the row did not exist before Apply was +// called. For this reason, most users of the library will prefer not to use +// this option. However, ApplyAtLeastOnce requires only a single RPC, whereas +// Apply's default replay protection may require an additional RPC. So this +// option may be appropriate for latency sensitive and/or high throughput blind +// writing. +func ApplyAtLeastOnce() ApplyOption { + return func(ao *applyOption) { + ao.atLeastOnce = true + } +} + +// Apply applies a list of mutations atomically to the database. +func (c *Client) Apply(ctx context.Context, ms []*Mutation, opts ...ApplyOption) (commitTimestamp time.Time, err error) { + ao := &applyOption{} + for _, opt := range opts { + opt(ao) + } + if !ao.atLeastOnce { + return c.ReadWriteTransaction(ctx, func(ctx context.Context, t *ReadWriteTransaction) error { + return t.BufferWrite(ms) + }) + } + + ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.Apply") + defer func() { traceEndSpan(ctx, err) }() + t := &writeOnlyTransaction{c.idleSessions} + return t.applyAtLeastOnce(ctx, ms...) +} diff --git a/vendor/cloud.google.com/go/spanner/client_test.go b/vendor/cloud.google.com/go/spanner/client_test.go new file mode 100644 index 0000000000..54c52631f0 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/client_test.go @@ -0,0 +1,50 @@ +/* +Copyright 2017 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 spanner + +import ( + "strings" + "testing" +) + +// Test validDatabaseName() +func TestValidDatabaseName(t *testing.T) { + validDbURI := "projects/spanner-cloud-test/instances/foo/databases/foodb" + invalidDbUris := []string{ + // Completely wrong DB URI. + "foobarDB", + // Project ID contains "/". + "projects/spanner-cloud/test/instances/foo/databases/foodb", + // No instance ID. + "projects/spanner-cloud-test/instances//databases/foodb", + } + if err := validDatabaseName(validDbURI); err != nil { + t.Errorf("validateDatabaseName(%q) = %v, want nil", validDbURI, err) + } + for _, d := range invalidDbUris { + if err, wantErr := validDatabaseName(d), "should conform to pattern"; !strings.Contains(err.Error(), wantErr) { + t.Errorf("validateDatabaseName(%q) = %q, want error pattern %q", validDbURI, err, wantErr) + } + } +} + +func TestReadOnlyTransactionClose(t *testing.T) { + // Closing a ReadOnlyTransaction shouldn't panic. + c := &Client{} + tx := c.ReadOnlyTransaction() + tx.Close() +} diff --git a/vendor/cloud.google.com/go/spanner/doc.go b/vendor/cloud.google.com/go/spanner/doc.go new file mode 100644 index 0000000000..8f0303bb37 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/doc.go @@ -0,0 +1,315 @@ +/* +Copyright 2017 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 spanner provides a client for reading and writing to Cloud Spanner +databases. See the packages under admin for clients that operate on databases +and instances. + +Note: This package is in beta. Some backwards-incompatible changes may occur. + +See https://cloud.google.com/spanner/docs/getting-started/go/ for an introduction +to Cloud Spanner and additional help on using this API. + +See https://godoc.org/cloud.google.com/go for authentication, timeouts, +connection pooling and similar aspects of this package. + + +Creating a Client + +To start working with this package, create a client that refers to the database +of interest: + + ctx := context.Background() + client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D") + if err != nil { + // TODO: Handle error. + } + defer client.Close() + +Remember to close the client after use to free up the sessions in the session +pool. + + +Simple Reads and Writes + +Two Client methods, Apply and Single, work well for simple reads and writes. As +a quick introduction, here we write a new row to the database and read it back: + + _, err := client.Apply(ctx, []*spanner.Mutation{ + spanner.Insert("Users", + []string{"name", "email"}, + []interface{}{"alice", "a@example.com"})}) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Users", + spanner.Key{"alice"}, []string{"email"}) + if err != nil { + // TODO: Handle error. + } + +All the methods used above are discussed in more detail below. + + +Keys + +Every Cloud Spanner row has a unique key, composed of one or more columns. +Construct keys with a literal of type Key: + + key1 := spanner.Key{"alice"} + + +KeyRanges + +The keys of a Cloud Spanner table are ordered. You can specify ranges of keys +using the KeyRange type: + + kr1 := spanner.KeyRange{Start: key1, End: key2} + +By default, a KeyRange includes its start key but not its end key. Use +the Kind field to specify other boundary conditions: + + // include both keys + kr2 := spanner.KeyRange{Start: key1, End: key2, Kind: spanner.ClosedClosed} + + +KeySets + +A KeySet represents a set of keys. A single Key or KeyRange can act as a KeySet. Use +the KeySets function to build the union of several KeySets: + + ks1 := spanner.KeySets(key1, key2, kr1, kr2) + +AllKeys returns a KeySet that refers to all the keys in a table: + + ks2 := spanner.AllKeys() + + +Transactions + +All Cloud Spanner reads and writes occur inside transactions. There are two +types of transactions, read-only and read-write. Read-only transactions cannot +change the database, do not acquire locks, and may access either the current +database state or states in the past. Read-write transactions can read the +database before writing to it, and always apply to the most recent database +state. + + +Single Reads + +The simplest and fastest transaction is a ReadOnlyTransaction that supports a +single read operation. Use Client.Single to create such a transaction. You can +chain the call to Single with a call to a Read method. + +When you only want one row whose key you know, use ReadRow. Provide the table +name, key, and the columns you want to read: + + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) + +Read multiple rows with the Read method. It takes a table name, KeySet, and list +of columns: + + iter := client.Single().Read(ctx, "Accounts", keyset1, columns) + +Read returns a RowIterator. You can call the Do method on the iterator and pass +a callback: + + err := iter.Do(func(row *Row) error { + // TODO: use row + return nil + }) + +RowIterator also follows the standard pattern for the Google +Cloud Client Libraries: + + defer iter.Stop() + for { + row, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: use row + } + +Always call Stop when you finish using an iterator this way, whether or not you +iterate to the end. (Failing to call Stop could lead you to exhaust the +database's session quota.) + +To read rows with an index, use ReadUsingIndex. + +Statements + +The most general form of reading uses SQL statements. Construct a Statement +with NewStatement, setting any parameters using the Statement's Params map: + + stmt := spanner.NewStatement("SELECT First, Last FROM SINGERS WHERE Last >= @start") + stmt.Params["start"] = "Dylan" + +You can also construct a Statement directly with a struct literal, providing +your own map of parameters. + +Use the Query method to run the statement and obtain an iterator: + + iter := client.Single().Query(ctx, stmt) + + +Rows + +Once you have a Row, via an iterator or a call to ReadRow, you can extract +column values in several ways. Pass in a pointer to a Go variable of the +appropriate type when you extract a value. + +You can extract by column position or name: + + err := row.Column(0, &name) + err = row.ColumnByName("balance", &balance) + +You can extract all the columns at once: + + err = row.Columns(&name, &balance) + +Or you can define a Go struct that corresponds to your columns, and extract +into that: + + var s struct { Name string; Balance int64 } + err = row.ToStruct(&s) + + +For Cloud Spanner columns that may contain NULL, use one of the NullXXX types, +like NullString: + + var ns spanner.NullString + if err := row.Column(0, &ns); err != nil { + // TODO: Handle error. + } + if ns.Valid { + fmt.Println(ns.StringVal) + } else { + fmt.Println("column is NULL") + } + + +Multiple Reads + +To perform more than one read in a transaction, use ReadOnlyTransaction: + + txn := client.ReadOnlyTransaction() + defer txn.Close() + iter := txn.Query(ctx, stmt1) + // ... + iter = txn.Query(ctx, stmt2) + // ... + +You must call Close when you are done with the transaction. + + +Timestamps and Timestamp Bounds + +Cloud Spanner read-only transactions conceptually perform all their reads at a +single moment in time, called the transaction's read timestamp. Once a read has +started, you can call ReadOnlyTransaction's Timestamp method to obtain the read +timestamp. + +By default, a transaction will pick the most recent time (a time where all +previously committed transactions are visible) for its reads. This provides the +freshest data, but may involve some delay. You can often get a quicker response +if you are willing to tolerate "stale" data. You can control the read timestamp +selected by a transaction by calling the WithTimestampBound method on the +transaction before using it. For example, to perform a query on data that is at +most one minute stale, use + + client.Single(). + WithTimestampBound(spanner.MaxStaleness(1*time.Minute)). + Query(ctx, stmt) + +See the documentation of TimestampBound for more details. + + +Mutations + +To write values to a Cloud Spanner database, construct a Mutation. The spanner +package has functions for inserting, updating and deleting rows. Except for the +Delete methods, which take a Key or KeyRange, each mutation-building function +comes in three varieties. + +One takes lists of columns and values along with the table name: + + m1 := spanner.Insert("Users", + []string{"name", "email"}, + []interface{}{"alice", "a@example.com"}) + +One takes a map from column names to values: + + m2 := spanner.InsertMap("Users", map[string]interface{}{ + "name": "alice", + "email": "a@example.com", + }) + +And the third accepts a struct value, and determines the columns from the +struct field names: + + type User struct { Name, Email string } + u := User{Name: "alice", Email: "a@example.com"} + m3, err := spanner.InsertStruct("Users", u) + + +Writes + +To apply a list of mutations to the database, use Apply: + + _, err := client.Apply(ctx, []*spanner.Mutation{m1, m2, m3}) + +If you need to read before writing in a single transaction, use a +ReadWriteTransaction. ReadWriteTransactions may abort and need to be retried. +You pass in a function to ReadWriteTransaction, and the client will handle the +retries automatically. Use the transaction's BufferWrite method to buffer +mutations, which will all be executed at the end of the transaction: + + _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { + var balance int64 + row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) + if err != nil { + // This function will be called again if this is an IsAborted error. + return err + } + if err := row.Column(0, &balance); err != nil { + return err + } + + if balance <= 10 { + return errors.New("insufficient funds in account") + } + balance -= 10 + m := spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance}) + txn.BufferWrite([]*spanner.Mutation{m}) + + // The buffered mutation will be committed. If the commit + // fails with an IsAborted error, this function will be called + // again. + return nil + }) + +Tracing + +This client has been instrumented to use OpenCensus tracing (http://opencensus.io). +To enable tracing, see "Enabling Tracing for a Program" at +https://godoc.org/go.opencensus.io/trace. OpenCensus tracing requires Go 1.8 or higher. +*/ +package spanner // import "cloud.google.com/go/spanner" diff --git a/vendor/cloud.google.com/go/spanner/errors.go b/vendor/cloud.google.com/go/spanner/errors.go new file mode 100644 index 0000000000..b940a7c599 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/errors.go @@ -0,0 +1,115 @@ +/* +Copyright 2017 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 spanner + +import ( + "fmt" + + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// Error is the structured error returned by Cloud Spanner client. +type Error struct { + // Code is the canonical error code for describing the nature of a + // particular error. + Code codes.Code + // Desc explains more details of the error. + Desc string + // trailers are the trailers returned in the response, if any. + trailers metadata.MD +} + +// Error implements error.Error. +func (e *Error) Error() string { + if e == nil { + return fmt.Sprintf("spanner: OK") + } + return fmt.Sprintf("spanner: code = %q, desc = %q", e.Code, e.Desc) +} + +// decorate decorates an existing spanner.Error with more information. +func (e *Error) decorate(info string) { + e.Desc = fmt.Sprintf("%v, %v", info, e.Desc) +} + +// spannerErrorf generates a *spanner.Error with the given error code and +// description. +func spannerErrorf(ec codes.Code, format string, args ...interface{}) error { + return &Error{ + Code: ec, + Desc: fmt.Sprintf(format, args...), + } +} + +// toSpannerError converts general Go error to *spanner.Error. +func toSpannerError(err error) error { + return toSpannerErrorWithMetadata(err, nil) +} + +// toSpannerErrorWithMetadata converts general Go error and grpc trailers to *spanner.Error. +// Note: modifies original error if trailers aren't nil +func toSpannerErrorWithMetadata(err error, trailers metadata.MD) error { + if err == nil { + return nil + } + if se, ok := err.(*Error); ok { + if trailers != nil { + se.trailers = metadata.Join(se.trailers, trailers) + } + return se + } + switch { + case err == context.DeadlineExceeded: + return &Error{codes.DeadlineExceeded, err.Error(), trailers} + case err == context.Canceled: + return &Error{codes.Canceled, err.Error(), trailers} + case grpc.Code(err) == codes.Unknown: + return &Error{codes.Unknown, err.Error(), trailers} + default: + return &Error{grpc.Code(err), grpc.ErrorDesc(err), trailers} + } +} + +// ErrCode extracts the canonical error code from a Go error. +func ErrCode(err error) codes.Code { + se, ok := toSpannerError(err).(*Error) + if !ok { + return codes.Unknown + } + return se.Code +} + +// ErrDesc extracts the Cloud Spanner error description from a Go error. +func ErrDesc(err error) string { + se, ok := toSpannerError(err).(*Error) + if !ok { + return err.Error() + } + return se.Desc +} + +// errTrailers extracts the grpc trailers if present from a Go error. +func errTrailers(err error) metadata.MD { + se, ok := err.(*Error) + if !ok { + return nil + } + return se.trailers +} diff --git a/vendor/cloud.google.com/go/spanner/errors_test.go b/vendor/cloud.google.com/go/spanner/errors_test.go new file mode 100644 index 0000000000..7626b9151a --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/errors_test.go @@ -0,0 +1,44 @@ +/* +Copyright 2017 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 spanner + +import ( + "errors" + "testing" + + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestToSpannerError(t *testing.T) { + for _, test := range []struct { + err error + wantCode codes.Code + }{ + {errors.New("wha?"), codes.Unknown}, + {context.Canceled, codes.Canceled}, + {context.DeadlineExceeded, codes.DeadlineExceeded}, + {status.Errorf(codes.ResourceExhausted, "so tired"), codes.ResourceExhausted}, + {spannerErrorf(codes.InvalidArgument, "bad"), codes.InvalidArgument}, + } { + err := toSpannerError(test.err) + if got, want := err.(*Error).Code, test.wantCode; got != want { + t.Errorf("%v: got %s, want %s", test.err, got, want) + } + } +} diff --git a/vendor/cloud.google.com/go/spanner/examples_test.go b/vendor/cloud.google.com/go/spanner/examples_test.go new file mode 100644 index 0000000000..3cd83ce4c0 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/examples_test.go @@ -0,0 +1,668 @@ +/* +Copyright 2017 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 spanner_test + +import ( + "errors" + "fmt" + "sync" + "time" + + "cloud.google.com/go/spanner" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + const myDB = "projects/my-project/instances/my-instance/database/my-db" + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + _ = client // TODO: Use client. +} + +const myDB = "projects/my-project/instances/my-instance/database/my-db" + +func ExampleNewClientWithConfig() { + ctx := context.Background() + const myDB = "projects/my-project/instances/my-instance/database/my-db" + client, err := spanner.NewClientWithConfig(ctx, myDB, spanner.ClientConfig{ + NumChannels: 10, + }) + if err != nil { + // TODO: Handle error. + } + _ = client // TODO: Use client. + client.Close() // Close client when done. +} + +func ExampleClient_Single() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) + _ = iter // TODO: iterate using Next or Do. +} + +func ExampleClient_ReadOnlyTransaction() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + t := client.ReadOnlyTransaction() + defer t.Close() + // TODO: Read with t using Read, ReadRow, ReadUsingIndex, or Query. +} + +func ExampleClient_ReadWriteTransaction() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { + var balance int64 + row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) + if err != nil { + // This function will be called again if this is an + // IsAborted error. + return err + } + if err := row.Column(0, &balance); err != nil { + return err + } + + if balance <= 10 { + return errors.New("insufficient funds in account") + } + balance -= 10 + m := spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance}) + return txn.BufferWrite([]*spanner.Mutation{m}) + // The buffered mutation will be committed. If the commit + // fails with an IsAborted error, this function will be called + // again. + }) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleUpdate() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { + row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) + if err != nil { + return err + } + var balance int64 + if err := row.Column(0, &balance); err != nil { + return err + } + return txn.BufferWrite([]*spanner.Mutation{ + spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance + 10}), + }) + }) + if err != nil { + // TODO: Handle error. + } +} + +// This example is the same as the one for Update, except for the use of UpdateMap. +func ExampleUpdateMap() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { + row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) + if err != nil { + return err + } + var balance int64 + if err := row.Column(0, &balance); err != nil { + return err + } + return txn.BufferWrite([]*spanner.Mutation{ + spanner.UpdateMap("Accounts", map[string]interface{}{ + "user": "alice", + "balance": balance + 10, + }), + }) + }) + if err != nil { + // TODO: Handle error. + } +} + +// This example is the same as the one for Update, except for the use of UpdateStruct. +func ExampleUpdateStruct() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + type account struct { + User string `spanner:"user"` + Balance int64 `spanner:"balance"` + } + _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { + row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) + if err != nil { + return err + } + var balance int64 + if err := row.Column(0, &balance); err != nil { + return err + } + m, err := spanner.UpdateStruct("Accounts", account{ + User: "alice", + Balance: balance + 10, + }) + if err != nil { + return err + } + return txn.BufferWrite([]*spanner.Mutation{m}) + }) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_Apply() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + m := spanner.Update("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"}) + _, err = client.Apply(ctx, []*spanner.Mutation{m}) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleInsert() { + m := spanner.Insert("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"}) + _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. +} + +func ExampleInsertMap() { + m := spanner.InsertMap("Users", map[string]interface{}{ + "name": "alice", + "email": "a@example.com", + }) + _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. +} + +func ExampleInsertStruct() { + type User struct { + Name, Email string + } + u := User{Name: "alice", Email: "a@example.com"} + m, err := spanner.InsertStruct("Users", u) + if err != nil { + // TODO: Handle error. + } + _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. +} + +func ExampleDelete() { + m := spanner.Delete("Users", spanner.Key{"alice"}) + _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. +} + +func ExampleDelete_keyRange() { + m := spanner.Delete("Users", spanner.KeyRange{ + Start: spanner.Key{"alice"}, + End: spanner.Key{"bob"}, + Kind: spanner.ClosedClosed, + }) + _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. +} + +func ExampleRowIterator_Next() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) + defer iter.Stop() + for { + row, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + var firstName string + if err := row.Column(0, &firstName); err != nil { + // TODO: Handle error. + } + fmt.Println(firstName) + } +} + +func ExampleRowIterator_Do() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) + err = iter.Do(func(r *spanner.Row) error { + var firstName string + if err := r.Column(0, &firstName); err != nil { + return err + } + fmt.Println(firstName) + return nil + }) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleRow_Size() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) + if err != nil { + // TODO: Handle error. + } + fmt.Println(row.Size()) // size is 2 +} + +func ExampleRow_ColumnName() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) + if err != nil { + // TODO: Handle error. + } + fmt.Println(row.ColumnName(1)) // prints "balance" +} + +func ExampleRow_ColumnIndex() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) + if err != nil { + // TODO: Handle error. + } + index, err := row.ColumnIndex("balance") + if err != nil { + // TODO: Handle error. + } + fmt.Println(index) +} + +func ExampleRow_ColumnNames() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) + if err != nil { + // TODO: Handle error. + } + fmt.Println(row.ColumnNames()) +} + +func ExampleRow_ColumnByName() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) + if err != nil { + // TODO: Handle error. + } + var balance int64 + if err := row.ColumnByName("balance", &balance); err != nil { + // TODO: Handle error. + } + fmt.Println(balance) +} + +func ExampleRow_Columns() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) + if err != nil { + // TODO: Handle error. + } + var name string + var balance int64 + if err := row.Columns(&name, &balance); err != nil { + // TODO: Handle error. + } + fmt.Println(name, balance) +} + +func ExampleRow_ToStruct() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) + if err != nil { + // TODO: Handle error. + } + + type Account struct { + Name string + Balance int64 + } + + var acct Account + if err := row.ToStruct(&acct); err != nil { + // TODO: Handle error. + } + fmt.Println(acct) +} + +func ExampleReadOnlyTransaction_Read() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + iter := client.Single().Read(ctx, "Users", + spanner.KeySets(spanner.Key{"alice"}, spanner.Key{"bob"}), + []string{"name", "email"}) + _ = iter // TODO: iterate using Next or Do. +} + +func ExampleReadOnlyTransaction_ReadUsingIndex() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + iter := client.Single().ReadUsingIndex(ctx, "Users", + "UsersByEmail", + spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}), + []string{"name", "email"}) + _ = iter // TODO: iterate using Next or Do. +} + +func ExampleReadOnlyTransaction_ReadWithOptions() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + // Use an index, and limit to 100 rows at most. + iter := client.Single().ReadWithOptions(ctx, "Users", + spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}), + []string{"name", "email"}, &spanner.ReadOptions{ + Index: "UsersByEmail", + Limit: 100, + }) + _ = iter // TODO: iterate using Next or Do. +} + +func ExampleReadOnlyTransaction_ReadRow() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + row, err := client.Single().ReadRow(ctx, "Users", spanner.Key{"alice"}, + []string{"name", "email"}) + if err != nil { + // TODO: Handle error. + } + _ = row // TODO: use row +} + +func ExampleReadOnlyTransaction_Query() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) + _ = iter // TODO: iterate using Next or Do. +} + +func ExampleNewStatement() { + stmt := spanner.NewStatement("SELECT FirstName, LastName FROM SINGERS WHERE LastName >= @start") + stmt.Params["start"] = "Dylan" + // TODO: Use stmt in Query. +} + +func ExampleNewStatement_structLiteral() { + stmt := spanner.Statement{ + SQL: `SELECT FirstName, LastName FROM SINGERS WHERE LastName = ("Lea", "Martin")`, + } + _ = stmt // TODO: Use stmt in Query. +} + +func ExampleStructParam() { + stmt := spanner.Statement{ + SQL: "SELECT * FROM SINGERS WHERE (FirstName, LastName) = @singerinfo", + Params: map[string]interface{}{ + "singerinfo": struct { + FirstName string + LastName string + }{"Bob", "Dylan"}, + }, + } + _ = stmt // TODO: Use stmt in Query. +} + +func ExampleArrayOfStructParam() { + stmt := spanner.Statement{ + SQL: "SELECT * FROM SINGERS WHERE (FirstName, LastName) IN UNNEST(@singerinfo)", + Params: map[string]interface{}{ + "singerinfo": []struct { + FirstName string + LastName string + }{ + {"Ringo", "Starr"}, + {"John", "Lennon"}, + }, + }, + } + _ = stmt // TODO: Use stmt in Query. +} + +func ExampleReadOnlyTransaction_Timestamp() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + txn := client.Single() + row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"}, + []string{"name", "email"}) + if err != nil { + // TODO: Handle error. + } + readTimestamp, err := txn.Timestamp() + if err != nil { + // TODO: Handle error. + } + fmt.Println("read happened at", readTimestamp) + _ = row // TODO: use row +} + +func ExampleReadOnlyTransaction_WithTimestampBound() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + txn := client.Single().WithTimestampBound(spanner.MaxStaleness(30 * time.Second)) + row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"}, []string{"name", "email"}) + if err != nil { + // TODO: Handle error. + } + _ = row // TODO: use row + readTimestamp, err := txn.Timestamp() + if err != nil { + // TODO: Handle error. + } + fmt.Println("read happened at", readTimestamp) +} + +func ExampleGenericColumnValue_Decode() { + // In real applications, rows can be retrieved by methods like client.Single().ReadRow(). + row, err := spanner.NewRow([]string{"intCol", "strCol"}, []interface{}{42, "my-text"}) + if err != nil { + // TODO: Handle error. + } + for i := 0; i < row.Size(); i++ { + var col spanner.GenericColumnValue + if err := row.Column(i, &col); err != nil { + // TODO: Handle error. + } + switch col.Type.Code { + case sppb.TypeCode_INT64: + var v int64 + if err := col.Decode(&v); err != nil { + // TODO: Handle error. + } + fmt.Println("int", v) + case sppb.TypeCode_STRING: + var v string + if err := col.Decode(&v); err != nil { + // TODO: Handle error. + } + fmt.Println("string", v) + } + } + // Output: + // int 42 + // string my-text +} + +func ExampleClient_BatchReadOnlyTransaction() { + ctx := context.Background() + var ( + client *spanner.Client + txn *spanner.BatchReadOnlyTransaction + err error + ) + if client, err = spanner.NewClient(ctx, myDB); err != nil { + // TODO: Handle error. + } + defer client.Close() + if txn, err = client.BatchReadOnlyTransaction(ctx, spanner.StrongRead()); err != nil { + // TODO: Handle error. + } + defer txn.Close() + + // Singer represents the elements in a row from the Singers table. + type Singer struct { + SingerID int64 + FirstName string + LastName string + SingerInfo []byte + } + stmt := spanner.Statement{SQL: "SELECT * FROM Singers;"} + partitions, err := txn.PartitionQuery(ctx, stmt, spanner.PartitionOptions{}) + if err != nil { + // TODO: Handle error. + } + // Note: here we use multiple goroutines, but you should use separate processes/machines. + wg := sync.WaitGroup{} + for i, p := range partitions { + wg.Add(1) + go func(i int, p *spanner.Partition) { + defer wg.Done() + iter := txn.Execute(ctx, p) + defer iter.Stop() + for { + row, err := iter.Next() + if err == iterator.Done { + break + } else if err != nil { + // TODO: Handle error. + } + var s Singer + if err := row.ToStruct(&s); err != nil { + // TODO: Handle error. + } + _ = s // TODO: Process the row. + } + }(i, p) + } + wg.Wait() +} + +func ExampleCommitTimestamp() { + ctx := context.Background() + client, err := spanner.NewClient(ctx, myDB) + if err != nil { + // TODO: Handle error. + } + + type account struct { + User string + Creation spanner.NullTime // time.Time can also be used if column is NOT NULL + } + + a := account{User: "Joe", Creation: spanner.NullTime{spanner.CommitTimestamp, true}} + m, err := spanner.InsertStruct("Accounts", a) + if err != nil { + // TODO: Handle error. + } + _, err = client.Apply(ctx, []*spanner.Mutation{m}, spanner.ApplyAtLeastOnce()) + if err != nil { + // TODO: Handle error. + } + + if r, e := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"Joe"}, []string{"User", "Creation"}); e != nil { + // TODO: Handle error. + } else { + var got account + if err := r.ToStruct(&got); err != nil { + // TODO: Handle error. + } + _ = got // TODO: Process row. + } +} diff --git a/vendor/cloud.google.com/go/spanner/go17.go b/vendor/cloud.google.com/go/spanner/go17.go new file mode 100644 index 0000000000..f42419f71b --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/go17.go @@ -0,0 +1,23 @@ +// 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. + +// +build go1.7 + +package spanner + +import "reflect" + +func structTagLookup(tag reflect.StructTag, key string) (string, bool) { + return tag.Lookup(key) +} diff --git a/vendor/cloud.google.com/go/spanner/go17_test.go b/vendor/cloud.google.com/go/spanner/go17_test.go new file mode 100644 index 0000000000..d1f6bfbe1b --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/go17_test.go @@ -0,0 +1,466 @@ +// 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. + +// +build go1.7 + +package spanner + +import ( + "reflect" + "testing" + "time" + + "cloud.google.com/go/civil" + proto "github.com/golang/protobuf/proto" + proto3 "github.com/golang/protobuf/ptypes/struct" + "golang.org/x/net/context" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +func TestEncodeStructValueDynamicStructs(t *testing.T) { + dynStructType := reflect.StructOf([]reflect.StructField{ + {Name: "A", Type: reflect.TypeOf(0), Tag: `spanner:"a"`}, + {Name: "B", Type: reflect.TypeOf(""), Tag: `spanner:"b"`}, + }) + dynNullableStructType := reflect.PtrTo(dynStructType) + dynStructArrType := reflect.SliceOf(dynStructType) + dynNullableStructArrType := reflect.SliceOf(dynNullableStructType) + + dynStructValue := reflect.New(dynStructType) + dynStructValue.Elem().Field(0).SetInt(10) + dynStructValue.Elem().Field(1).SetString("abc") + + dynStructArrValue := reflect.MakeSlice(dynNullableStructArrType, 2, 2) + dynStructArrValue.Index(0).Set(reflect.Zero(dynNullableStructType)) + dynStructArrValue.Index(1).Set(dynStructValue) + + structProtoType := structType( + mkField("a", intType()), + mkField("b", stringType())) + + arrProtoType := listType(structProtoType) + + for _, test := range []encodeTest{ + { + "Dynanic non-NULL struct value.", + dynStructValue.Elem().Interface(), + listProto(intProto(10), stringProto("abc")), + structProtoType, + }, + { + "Dynanic NULL struct value.", + reflect.Zero(dynNullableStructType).Interface(), + nullProto(), + structProtoType, + }, + { + "Empty array of dynamic structs.", + reflect.MakeSlice(dynStructArrType, 0, 0).Interface(), + listProto([]*proto3.Value{}...), + arrProtoType, + }, + { + "NULL array of non-NULL-able dynamic structs.", + reflect.Zero(dynStructArrType).Interface(), + nullProto(), + arrProtoType, + }, + { + "NULL array of NULL-able(nil) dynamic structs.", + reflect.Zero(dynNullableStructArrType).Interface(), + nullProto(), + arrProtoType, + }, + { + "Array containing NULL(nil) dynamic-typed struct elements.", + dynStructArrValue.Interface(), + listProto( + nullProto(), + listProto(intProto(10), stringProto("abc"))), + arrProtoType, + }, + } { + encodeStructValue(test, t) + } +} + +func TestEncodeStructValueEmptyStruct(t *testing.T) { + emptyListValue := listProto([]*proto3.Value{}...) + emptyStructType := structType([]*sppb.StructType_Field{}...) + emptyStruct := struct{}{} + nullEmptyStruct := (*struct{})(nil) + + dynamicEmptyStructType := reflect.StructOf(make([]reflect.StructField, 0, 0)) + dynamicStructArrType := reflect.SliceOf(reflect.PtrTo((dynamicEmptyStructType))) + + dynamicEmptyStruct := reflect.New(dynamicEmptyStructType) + dynamicNullEmptyStruct := reflect.Zero(reflect.PtrTo(dynamicEmptyStructType)) + + dynamicStructArrValue := reflect.MakeSlice(dynamicStructArrType, 2, 2) + dynamicStructArrValue.Index(0).Set(dynamicNullEmptyStruct) + dynamicStructArrValue.Index(1).Set(dynamicEmptyStruct) + + for _, test := range []encodeTest{ + { + "Go empty struct.", + emptyStruct, + emptyListValue, + emptyStructType, + }, + { + "Dynamic empty struct.", + dynamicEmptyStruct.Interface(), + emptyListValue, + emptyStructType, + }, + { + "Go NULL empty struct.", + nullEmptyStruct, + nullProto(), + emptyStructType, + }, + { + "Dynamic NULL empty struct.", + dynamicNullEmptyStruct.Interface(), + nullProto(), + emptyStructType, + }, + { + "Non-empty array of dynamic NULL and non-NULL empty structs.", + dynamicStructArrValue.Interface(), + listProto(nullProto(), emptyListValue), + listType(emptyStructType), + }, + { + "Non-empty array of nullable empty structs.", + []*struct{}{nullEmptyStruct, &emptyStruct}, + listProto(nullProto(), emptyListValue), + listType(emptyStructType), + }, + { + "Empty array of empty struct.", + []struct{}{}, + emptyListValue, + listType(emptyStructType), + }, + { + "Null array of empty structs.", + []struct{}(nil), + nullProto(), + listType(emptyStructType), + }, + } { + encodeStructValue(test, t) + } +} + +func TestEncodeStructValueMixedStructTypes(t *testing.T) { + type staticStruct struct { + F int `spanner:"fStatic"` + } + s1 := staticStruct{10} + s2 := (*staticStruct)(nil) + + var f float64 + dynStructType := reflect.StructOf([]reflect.StructField{ + {Name: "A", Type: reflect.TypeOf(f), Tag: `spanner:"fDynamic"`}, + }) + s3 := reflect.New(dynStructType) + s3.Elem().Field(0).SetFloat(3.14) + + for _, test := range []encodeTest{ + { + "'struct' with static and dynamic *struct, []*struct, []struct fields", + struct { + A []staticStruct + B []*staticStruct + C interface{} + }{ + []staticStruct{s1, s1}, + []*staticStruct{&s1, s2}, + s3.Interface(), + }, + listProto( + listProto(listProto(intProto(10)), listProto(intProto(10))), + listProto(listProto(intProto(10)), nullProto()), + listProto(floatProto(3.14))), + structType( + mkField("A", listType(structType(mkField("fStatic", intType())))), + mkField("B", listType(structType(mkField("fStatic", intType())))), + mkField("C", structType(mkField("fDynamic", floatType())))), + }, + } { + encodeStructValue(test, t) + } +} + +func TestBindParamsDynamic(t *testing.T) { + // Verify Statement.bindParams generates correct values and types. + st := Statement{ + SQL: "SELECT id from t_foo WHERE col = @var", + Params: map[string]interface{}{"var": nil}, + } + want := &sppb.ExecuteSqlRequest{ + Params: &proto3.Struct{ + Fields: map[string]*proto3.Value{"var": nil}, + }, + ParamTypes: map[string]*sppb.Type{"var": nil}, + } + var ( + t1, _ = time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z") + // Boundaries + t2, _ = time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00.000000000Z") + ) + dynamicStructType := reflect.StructOf([]reflect.StructField{ + {Name: "A", Type: reflect.TypeOf(t1), Tag: `spanner:"field"`}, + {Name: "B", Type: reflect.TypeOf(3.14), Tag: `spanner:""`}, + }) + dynamicStructArrType := reflect.SliceOf(reflect.PtrTo(dynamicStructType)) + dynamicEmptyStructType := reflect.StructOf(make([]reflect.StructField, 0, 0)) + + dynamicStructTypeProto := structType( + mkField("field", timeType()), + mkField("", floatType())) + + s3 := reflect.New(dynamicStructType) + s3.Elem().Field(0).Set(reflect.ValueOf(t1)) + s3.Elem().Field(1).SetFloat(1.4) + + s4 := reflect.New(dynamicStructType) + s4.Elem().Field(0).Set(reflect.ValueOf(t2)) + s4.Elem().Field(1).SetFloat(-13.3) + + dynamicStructArrayVal := reflect.MakeSlice(dynamicStructArrType, 2, 2) + dynamicStructArrayVal.Index(0).Set(s3) + dynamicStructArrayVal.Index(1).Set(s4) + + for i, test := range []struct { + val interface{} + wantField *proto3.Value + wantType *sppb.Type + }{ + { + s3.Interface(), + listProto(timeProto(t1), floatProto(1.4)), + structType( + mkField("field", timeType()), + mkField("", floatType())), + }, + { + reflect.Zero(reflect.PtrTo(dynamicEmptyStructType)).Interface(), + nullProto(), + structType([]*sppb.StructType_Field{}...), + }, + { + dynamicStructArrayVal.Interface(), + listProto( + listProto(timeProto(t1), floatProto(1.4)), + listProto(timeProto(t2), floatProto(-13.3))), + listType(dynamicStructTypeProto), + }, + { + []*struct { + F1 time.Time `spanner:"field"` + F2 float64 `spanner:""` + }{ + nil, + {t1, 1.4}, + }, + listProto( + nullProto(), + listProto(timeProto(t1), floatProto(1.4))), + listType(dynamicStructTypeProto), + }, + } { + st.Params["var"] = test.val + want.Params.Fields["var"] = test.wantField + want.ParamTypes["var"] = test.wantType + got := &sppb.ExecuteSqlRequest{} + if err := st.bindParams(got); err != nil || !proto.Equal(got, want) { + // handle NaN + if test.wantType.Code == floatType().Code && proto.MarshalTextString(got) == proto.MarshalTextString(want) { + continue + } + t.Errorf("#%d: bind result: \n(%v, %v)\nwant\n(%v, %v)\n", i, got, err, want, nil) + } + } +} + +func TestStructParametersBind(t *testing.T) { + t.Parallel() + ctx := context.Background() + client, _, tearDown := prepare(ctx, t, nil) + defer tearDown() + + type tRow []interface{} + type tRows []struct{ trow tRow } + + type allFields struct { + Stringf string + Intf int + Boolf bool + Floatf float64 + Bytef []byte + Timef time.Time + Datef civil.Date + } + allColumns := []string{ + "Stringf", + "Intf", + "Boolf", + "Floatf", + "Bytef", + "Timef", + "Datef", + } + s1 := allFields{"abc", 300, false, 3.45, []byte("foo"), t1, d1} + s2 := allFields{"def", -300, false, -3.45, []byte("bar"), t2, d2} + + dynamicStructType := reflect.StructOf([]reflect.StructField{ + {Name: "A", Type: reflect.TypeOf(t1), Tag: `spanner:"ff1"`}, + }) + s3 := reflect.New(dynamicStructType) + s3.Elem().Field(0).Set(reflect.ValueOf(t1)) + + for i, test := range []struct { + param interface{} + sql string + cols []string + trows tRows + }{ + // Struct value. + { + s1, + "SELECT" + + " @p.Stringf," + + " @p.Intf," + + " @p.Boolf," + + " @p.Floatf," + + " @p.Bytef," + + " @p.Timef," + + " @p.Datef", + allColumns, + tRows{ + {tRow{"abc", 300, false, 3.45, []byte("foo"), t1, d1}}, + }, + }, + // Array of struct value. + { + []allFields{s1, s2}, + "SELECT * FROM UNNEST(@p)", + allColumns, + tRows{ + {tRow{"abc", 300, false, 3.45, []byte("foo"), t1, d1}}, + {tRow{"def", -300, false, -3.45, []byte("bar"), t2, d2}}, + }, + }, + // Null struct. + { + (*allFields)(nil), + "SELECT @p IS NULL", + []string{""}, + tRows{ + {tRow{true}}, + }, + }, + // Null Array of struct. + { + []allFields(nil), + "SELECT @p IS NULL", + []string{""}, + tRows{ + {tRow{true}}, + }, + }, + // Empty struct. + { + struct{}{}, + "SELECT @p IS NULL ", + []string{""}, + tRows{ + {tRow{false}}, + }, + }, + // Empty array of struct. + { + []allFields{}, + "SELECT * FROM UNNEST(@p) ", + allColumns, + tRows{}, + }, + // Struct with duplicate fields. + { + struct { + A int `spanner:"field"` + B int `spanner:"field"` + }{10, 20}, + "SELECT * FROM UNNEST([@p]) ", + []string{"field", "field"}, + tRows{ + {tRow{10, 20}}, + }, + }, + // Struct with unnamed fields. + { + struct { + A string `spanner:""` + }{"hello"}, + "SELECT * FROM UNNEST([@p]) ", + []string{""}, + tRows{ + {tRow{"hello"}}, + }, + }, + // Mixed struct. + { + struct { + DynamicStructField interface{} `spanner:"f1"` + ArrayStructField []*allFields `spanner:"f2"` + }{ + DynamicStructField: s3.Interface(), + ArrayStructField: []*allFields{nil}, + }, + "SELECT @p.f1.ff1, ARRAY_LENGTH(@p.f2), @p.f2[OFFSET(0)] IS NULL ", + []string{"ff1", "", ""}, + tRows{ + {tRow{t1, 1, true}}, + }, + }, + } { + iter := client.Single().Query(ctx, Statement{ + SQL: test.sql, + Params: map[string]interface{}{"p": test.param}, + }) + var gotRows []*Row + err := iter.Do(func(r *Row) error { + gotRows = append(gotRows, r) + return nil + }) + if err != nil { + t.Errorf("Failed to execute test case %d, error: %v", i, err) + } + + var wantRows []*Row + for j, row := range test.trows { + r, err := NewRow(test.cols, row.trow) + if err != nil { + t.Errorf("Invalid row %d in test case %d", j, i) + } + wantRows = append(wantRows, r) + } + if !testEqual(gotRows, wantRows) { + t.Errorf("%d: Want result %v, got result %v", i, wantRows, gotRows) + } + } +} diff --git a/vendor/cloud.google.com/go/spanner/go18.go b/vendor/cloud.google.com/go/spanner/go18.go new file mode 100644 index 0000000000..a31e9b9ad5 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/go18.go @@ -0,0 +1,59 @@ +// Copyright 2017 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 go1.8 + +package spanner + +import ( + "fmt" + + "go.opencensus.io/trace" + "golang.org/x/net/context" +) + +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 { + // TODO(jba): Add error code to the status. + 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...) +} diff --git a/vendor/cloud.google.com/go/spanner/integration_test.go b/vendor/cloud.google.com/go/spanner/integration_test.go new file mode 100644 index 0000000000..fddceb56fa --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/integration_test.go @@ -0,0 +1,1925 @@ +/* +Copyright 2017 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 spanner + +import ( + "errors" + "flag" + "fmt" + "log" + "math" + "os" + "reflect" + "strings" + "sync" + "testing" + "time" + + "cloud.google.com/go/civil" + "cloud.google.com/go/internal/testutil" + database "cloud.google.com/go/spanner/admin/database/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/grpc/codes" + + adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1" +) + +var ( + // testProjectID specifies the project used for testing. + // It can be changed by setting environment variable GCLOUD_TESTS_GOLANG_PROJECT_ID. + testProjectID = testutil.ProjID() + // testInstanceID specifies the Cloud Spanner instance used for testing. + testInstanceID = "go-integration-test" + + // admin is a spanner.DatabaseAdminClient. + admin *database.DatabaseAdminClient +) + +var ( + singerDBStatements = []string{ + `CREATE TABLE Singers ( + SingerId INT64 NOT NULL, + FirstName STRING(1024), + LastName STRING(1024), + SingerInfo BYTES(MAX) + ) PRIMARY KEY (SingerId)`, + `CREATE INDEX SingerByName ON Singers(FirstName, LastName)`, + `CREATE TABLE Accounts ( + AccountId INT64 NOT NULL, + Nickname STRING(100), + Balance INT64 NOT NULL, + ) PRIMARY KEY (AccountId)`, + `CREATE INDEX AccountByNickname ON Accounts(Nickname) STORING (Balance)`, + `CREATE TABLE Types ( + RowID INT64 NOT NULL, + String STRING(MAX), + StringArray ARRAY, + Bytes BYTES(MAX), + BytesArray ARRAY, + Int64a INT64, + Int64Array ARRAY, + Bool BOOL, + BoolArray ARRAY, + Float64 FLOAT64, + Float64Array ARRAY, + Date DATE, + DateArray ARRAY, + Timestamp TIMESTAMP, + TimestampArray ARRAY, + ) PRIMARY KEY (RowID)`, + } + + readDBStatements = []string{ + `CREATE TABLE TestTable ( + Key STRING(MAX) NOT NULL, + StringValue STRING(MAX) + ) PRIMARY KEY (Key)`, + `CREATE INDEX TestTableByValue ON TestTable(StringValue)`, + `CREATE INDEX TestTableByValueDesc ON TestTable(StringValue DESC)`, + } + + simpleDBStatements = []string{ + `CREATE TABLE test ( + a STRING(1024), + b STRING(1024), + ) PRIMARY KEY (a)`, + } + simpleDBTableColumns = []string{"a", "b"} + + ctsDBStatements = []string{ + `CREATE TABLE TestTable ( + Key STRING(MAX) NOT NULL, + Ts TIMESTAMP OPTIONS (allow_commit_timestamp = true), + ) PRIMARY KEY (Key)`, + } +) + +const ( + str1 = "alice" + str2 = "a@example.com" +) + +type testTableRow struct{ Key, StringValue string } + +func TestMain(m *testing.M) { + initIntegrationTest() + os.Exit(m.Run()) +} + +func initIntegrationTest() { + flag.Parse() // needed for testing.Short() + if testing.Short() { + return + } + if testProjectID == "" { + log.Print("Integration tests skipped: GCLOUD_TESTS_GOLANG_PROJECT_ID is missing") + return + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, AdminScope, Scope) + if ts == nil { + log.Printf("Integration test skipped: cannot get service account credential from environment variable %v", "GCLOUD_TESTS_GOLANG_KEY") + return + } + var err error + // Create Admin client and Data client. + admin, err = database.NewDatabaseAdminClient(ctx, option.WithTokenSource(ts), option.WithEndpoint(endpoint)) + if err != nil { + log.Fatalf("cannot create admin client: %v", err) + } +} + +var ( + mu sync.Mutex + count int + now = time.Now() +) + +// prepare initializes Cloud Spanner testing DB and clients. +func prepare(ctx context.Context, t *testing.T, statements []string) (client *Client, dbPath string, tearDown func()) { + if admin == nil { + t.Skip("Integration tests skipped") + } + // Construct a unique test DB name. + mu.Lock() + dbName := fmt.Sprintf("gotest_%d_%d", now.UnixNano(), count) + count++ + mu.Unlock() + + dbPath = fmt.Sprintf("projects/%v/instances/%v/databases/%v", testProjectID, testInstanceID, dbName) + // Create database and tables. + op, err := admin.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{ + Parent: fmt.Sprintf("projects/%v/instances/%v", testProjectID, testInstanceID), + CreateStatement: "CREATE DATABASE " + dbName, + ExtraStatements: statements, + }) + if err != nil { + t.Fatalf("cannot create testing DB %v: %v", dbPath, err) + } + if _, err := op.Wait(ctx); err != nil { + t.Fatalf("cannot create testing DB %v: %v", dbPath, err) + } + client, err = NewClientWithConfig(ctx, dbPath, ClientConfig{ + SessionPoolConfig: SessionPoolConfig{WriteSessions: 0.2}, + }, option.WithTokenSource(testutil.TokenSource(ctx, Scope)), option.WithEndpoint(endpoint)) + if err != nil { + t.Fatalf("cannot create data client on DB %v: %v", dbPath, err) + } + return client, dbPath, func() { + client.Close() + if err := admin.DropDatabase(ctx, &adminpb.DropDatabaseRequest{Database: dbPath}); err != nil { + t.Logf("failed to drop database %s (error %v), might need a manual removal", + dbPath, err) + } + } +} + +// Test SingleUse transaction. +func TestSingleUse(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + // Set up testing environment. + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + writes := []struct { + row []interface{} + ts time.Time + }{ + {row: []interface{}{1, "Marc", "Foo"}}, + {row: []interface{}{2, "Tars", "Bar"}}, + {row: []interface{}{3, "Alpha", "Beta"}}, + {row: []interface{}{4, "Last", "End"}}, + } + // Try to write four rows through the Apply API. + for i, w := range writes { + var err error + m := InsertOrUpdate("Singers", + []string{"SingerId", "FirstName", "LastName"}, + w.row) + if writes[i].ts, err = client.Apply(ctx, []*Mutation{m}, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + } + + // For testing timestamp bound staleness. + <-time.After(time.Second) + + // Test reading rows with different timestamp bounds. + for i, test := range []struct { + want [][]interface{} + tb TimestampBound + checkTs func(time.Time) error + }{ + { + // strong + [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}}, + StrongRead(), + func(ts time.Time) error { + // writes[3] is the last write, all subsequent strong read should have a timestamp larger than that. + if ts.Before(writes[3].ts) { + return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts) + } + return nil + }, + }, + { + // min_read_timestamp + [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}}, + MinReadTimestamp(writes[3].ts), + func(ts time.Time) error { + if ts.Before(writes[3].ts) { + return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts) + } + return nil + }, + }, + { + // max_staleness + [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}}, + MaxStaleness(time.Second), + func(ts time.Time) error { + if ts.Before(writes[3].ts) { + return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts) + } + return nil + }, + }, + { + // read_timestamp + [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}}, + ReadTimestamp(writes[2].ts), + func(ts time.Time) error { + if ts != writes[2].ts { + return fmt.Errorf("read got timestamp %v, want %v", ts, writes[2].ts) + } + return nil + }, + }, + { + // exact_staleness + nil, + // Specify a staleness which should be already before this test because + // context timeout is set to be 10s. + ExactStaleness(11 * time.Second), + func(ts time.Time) error { + if ts.After(writes[0].ts) { + return fmt.Errorf("read got timestamp %v, want it to be no earlier than %v", ts, writes[0].ts) + } + return nil + }, + }, + } { + // SingleUse.Query + su := client.Single().WithTimestampBound(test.tb) + got, err := readAll(su.Query( + ctx, + Statement{ + "SELECT SingerId, FirstName, LastName FROM Singers WHERE SingerId IN (@id1, @id3, @id4)", + map[string]interface{}{"id1": int64(1), "id3": int64(3), "id4": int64(4)}, + })) + if err != nil { + t.Errorf("%d: SingleUse.Query returns error %v, want nil", i, err) + } + if !testEqual(got, test.want) { + t.Errorf("%d: got unexpected result from SingleUse.Query: %v, want %v", i, got, test.want) + } + rts, err := su.Timestamp() + if err != nil { + t.Errorf("%d: SingleUse.Query doesn't return a timestamp, error: %v", i, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: SingleUse.Query doesn't return expected timestamp: %v", i, err) + } + // SingleUse.Read + su = client.Single().WithTimestampBound(test.tb) + got, err = readAll(su.Read(ctx, "Singers", KeySets(Key{1}, Key{3}, Key{4}), []string{"SingerId", "FirstName", "LastName"})) + if err != nil { + t.Errorf("%d: SingleUse.Read returns error %v, want nil", i, err) + } + if !testEqual(got, test.want) { + t.Errorf("%d: got unexpected result from SingleUse.Read: %v, want %v", i, got, test.want) + } + rts, err = su.Timestamp() + if err != nil { + t.Errorf("%d: SingleUse.Read doesn't return a timestamp, error: %v", i, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: SingleUse.Read doesn't return expected timestamp: %v", i, err) + } + // SingleUse.ReadRow + got = nil + for _, k := range []Key{{1}, {3}, {4}} { + su = client.Single().WithTimestampBound(test.tb) + r, err := su.ReadRow(ctx, "Singers", k, []string{"SingerId", "FirstName", "LastName"}) + if err != nil { + continue + } + v, err := rowToValues(r) + if err != nil { + continue + } + got = append(got, v) + rts, err = su.Timestamp() + if err != nil { + t.Errorf("%d: SingleUse.ReadRow(%v) doesn't return a timestamp, error: %v", i, k, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: SingleUse.ReadRow(%v) doesn't return expected timestamp: %v", i, k, err) + } + } + if !testEqual(got, test.want) { + t.Errorf("%d: got unexpected results from SingleUse.ReadRow: %v, want %v", i, got, test.want) + } + // SingleUse.ReadUsingIndex + su = client.Single().WithTimestampBound(test.tb) + got, err = readAll(su.ReadUsingIndex(ctx, "Singers", "SingerByName", KeySets(Key{"Marc", "Foo"}, Key{"Alpha", "Beta"}, Key{"Last", "End"}), []string{"SingerId", "FirstName", "LastName"})) + if err != nil { + t.Errorf("%d: SingleUse.ReadUsingIndex returns error %v, want nil", i, err) + } + // The results from ReadUsingIndex is sorted by the index rather than primary key. + if len(got) != len(test.want) { + t.Errorf("%d: got unexpected result from SingleUse.ReadUsingIndex: %v, want %v", i, got, test.want) + } + for j, g := range got { + if j > 0 { + prev := got[j-1][1].(string) + got[j-1][2].(string) + curr := got[j][1].(string) + got[j][2].(string) + if strings.Compare(prev, curr) > 0 { + t.Errorf("%d: SingleUse.ReadUsingIndex fails to order rows by index keys, %v should be after %v", i, got[j-1], got[j]) + } + } + found := false + for _, w := range test.want { + if testEqual(g, w) { + found = true + } + } + if !found { + t.Errorf("%d: got unexpected result from SingleUse.ReadUsingIndex: %v, want %v", i, got, test.want) + break + } + } + rts, err = su.Timestamp() + if err != nil { + t.Errorf("%d: SingleUse.ReadUsingIndex doesn't return a timestamp, error: %v", i, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: SingleUse.ReadUsingIndex doesn't return expected timestamp: %v", i, err) + } + } + + // Reading with limit. + su := client.Single() + const limit = 1 + gotRows, err := readAll(su.ReadWithOptions(ctx, "Singers", KeySets(Key{1}, Key{3}, Key{4}), + []string{"SingerId", "FirstName", "LastName"}, &ReadOptions{Limit: limit})) + if err != nil { + t.Errorf("SingleUse.ReadWithOptions returns error %v, want nil", err) + } + if got, want := len(gotRows), limit; got != want { + t.Errorf("got %d, want %d", got, want) + } + +} + +// Test ReadOnlyTransaction. The testsuite is mostly like SingleUse, except it +// also tests for a single timestamp across multiple reads. +func TestReadOnlyTransaction(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + // Set up testing environment. + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + writes := []struct { + row []interface{} + ts time.Time + }{ + {row: []interface{}{1, "Marc", "Foo"}}, + {row: []interface{}{2, "Tars", "Bar"}}, + {row: []interface{}{3, "Alpha", "Beta"}}, + {row: []interface{}{4, "Last", "End"}}, + } + // Try to write four rows through the Apply API. + for i, w := range writes { + var err error + m := InsertOrUpdate("Singers", + []string{"SingerId", "FirstName", "LastName"}, + w.row) + if writes[i].ts, err = client.Apply(ctx, []*Mutation{m}, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + } + + // For testing timestamp bound staleness. + <-time.After(time.Second) + + // Test reading rows with different timestamp bounds. + for i, test := range []struct { + want [][]interface{} + tb TimestampBound + checkTs func(time.Time) error + }{ + // Note: min_read_timestamp and max_staleness are not supported by ReadOnlyTransaction. See + // API document for more details. + { + // strong + [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}}, + StrongRead(), + func(ts time.Time) error { + if ts.Before(writes[3].ts) { + return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts) + } + return nil + }, + }, + { + // read_timestamp + [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}}, + ReadTimestamp(writes[2].ts), + func(ts time.Time) error { + if ts != writes[2].ts { + return fmt.Errorf("read got timestamp %v, expect %v", ts, writes[2].ts) + } + return nil + }, + }, + { + // exact_staleness + nil, + // Specify a staleness which should be already before this test because + // context timeout is set to be 10s. + ExactStaleness(11 * time.Second), + func(ts time.Time) error { + if ts.After(writes[0].ts) { + return fmt.Errorf("read got timestamp %v, want it to be no earlier than %v", ts, writes[0].ts) + } + return nil + }, + }, + } { + // ReadOnlyTransaction.Query + ro := client.ReadOnlyTransaction().WithTimestampBound(test.tb) + got, err := readAll(ro.Query( + ctx, + Statement{ + "SELECT SingerId, FirstName, LastName FROM Singers WHERE SingerId IN (@id1, @id3, @id4)", + map[string]interface{}{"id1": int64(1), "id3": int64(3), "id4": int64(4)}, + })) + if err != nil { + t.Errorf("%d: ReadOnlyTransaction.Query returns error %v, want nil", i, err) + } + if !testEqual(got, test.want) { + t.Errorf("%d: got unexpected result from ReadOnlyTransaction.Query: %v, want %v", i, got, test.want) + } + rts, err := ro.Timestamp() + if err != nil { + t.Errorf("%d: ReadOnlyTransaction.Query doesn't return a timestamp, error: %v", i, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: ReadOnlyTransaction.Query doesn't return expected timestamp: %v", i, err) + } + roTs := rts + // ReadOnlyTransaction.Read + got, err = readAll(ro.Read(ctx, "Singers", KeySets(Key{1}, Key{3}, Key{4}), []string{"SingerId", "FirstName", "LastName"})) + if err != nil { + t.Errorf("%d: ReadOnlyTransaction.Read returns error %v, want nil", i, err) + } + if !testEqual(got, test.want) { + t.Errorf("%d: got unexpected result from ReadOnlyTransaction.Read: %v, want %v", i, got, test.want) + } + rts, err = ro.Timestamp() + if err != nil { + t.Errorf("%d: ReadOnlyTransaction.Read doesn't return a timestamp, error: %v", i, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: ReadOnlyTransaction.Read doesn't return expected timestamp: %v", i, err) + } + if roTs != rts { + t.Errorf("%d: got two read timestamps: %v, %v, want ReadOnlyTransaction to return always the same read timestamp", i, roTs, rts) + } + // ReadOnlyTransaction.ReadRow + got = nil + for _, k := range []Key{{1}, {3}, {4}} { + r, err := ro.ReadRow(ctx, "Singers", k, []string{"SingerId", "FirstName", "LastName"}) + if err != nil { + continue + } + v, err := rowToValues(r) + if err != nil { + continue + } + got = append(got, v) + rts, err = ro.Timestamp() + if err != nil { + t.Errorf("%d: ReadOnlyTransaction.ReadRow(%v) doesn't return a timestamp, error: %v", i, k, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: ReadOnlyTransaction.ReadRow(%v) doesn't return expected timestamp: %v", i, k, err) + } + if roTs != rts { + t.Errorf("%d: got two read timestamps: %v, %v, want ReadOnlyTransaction to return always the same read timestamp", i, roTs, rts) + } + } + if !testEqual(got, test.want) { + t.Errorf("%d: got unexpected results from ReadOnlyTransaction.ReadRow: %v, want %v", i, got, test.want) + } + // SingleUse.ReadUsingIndex + got, err = readAll(ro.ReadUsingIndex(ctx, "Singers", "SingerByName", KeySets(Key{"Marc", "Foo"}, Key{"Alpha", "Beta"}, Key{"Last", "End"}), []string{"SingerId", "FirstName", "LastName"})) + if err != nil { + t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex returns error %v, want nil", i, err) + } + // The results from ReadUsingIndex is sorted by the index rather than primary key. + if len(got) != len(test.want) { + t.Errorf("%d: got unexpected result from ReadOnlyTransaction.ReadUsingIndex: %v, want %v", i, got, test.want) + } + for j, g := range got { + if j > 0 { + prev := got[j-1][1].(string) + got[j-1][2].(string) + curr := got[j][1].(string) + got[j][2].(string) + if strings.Compare(prev, curr) > 0 { + t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex fails to order rows by index keys, %v should be after %v", i, got[j-1], got[j]) + } + } + found := false + for _, w := range test.want { + if testEqual(g, w) { + found = true + } + } + if !found { + t.Errorf("%d: got unexpected result from ReadOnlyTransaction.ReadUsingIndex: %v, want %v", i, got, test.want) + break + } + } + rts, err = ro.Timestamp() + if err != nil { + t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex doesn't return a timestamp, error: %v", i, err) + } + if err := test.checkTs(rts); err != nil { + t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex doesn't return expected timestamp: %v", i, err) + } + if roTs != rts { + t.Errorf("%d: got two read timestamps: %v, %v, want ReadOnlyTransaction to return always the same read timestamp", i, roTs, rts) + } + ro.Close() + } +} + +// Test ReadOnlyTransaction with different timestamp bound when there's an update at the same time. +func TestUpdateDuringRead(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + for i, tb := range []TimestampBound{ + StrongRead(), + ReadTimestamp(time.Now().Add(-time.Minute * 30)), // version GC is 1 hour + ExactStaleness(time.Minute * 30), + } { + ro := client.ReadOnlyTransaction().WithTimestampBound(tb) + _, err := ro.ReadRow(ctx, "Singers", Key{i}, []string{"SingerId"}) + if ErrCode(err) != codes.NotFound { + t.Errorf("%d: ReadOnlyTransaction.ReadRow before write returns error: %v, want NotFound", i, err) + } + + m := InsertOrUpdate("Singers", []string{"SingerId"}, []interface{}{i}) + if _, err := client.Apply(ctx, []*Mutation{m}, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + + _, err = ro.ReadRow(ctx, "Singers", Key{i}, []string{"SingerId"}) + if ErrCode(err) != codes.NotFound { + t.Errorf("%d: ReadOnlyTransaction.ReadRow after write returns error: %v, want NotFound", i, err) + } + } +} + +// Test ReadWriteTransaction. +func TestReadWriteTransaction(t *testing.T) { + t.Parallel() + // Give a longer deadline because of transaction backoffs. + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + // Set up two accounts + accounts := []*Mutation{ + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(1), "Foo", int64(50)}), + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(2), "Bar", int64(1)}), + } + if _, err := client.Apply(ctx, accounts, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + wg := sync.WaitGroup{} + + readBalance := func(iter *RowIterator) (int64, error) { + defer iter.Stop() + var bal int64 + for { + row, err := iter.Next() + if err == iterator.Done { + return bal, nil + } + if err != nil { + return 0, err + } + if err := row.Column(0, &bal); err != nil { + return 0, err + } + } + } + + for i := 0; i < 20; i++ { + wg.Add(1) + go func(iter int) { + defer wg.Done() + _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + // Query Foo's balance and Bar's balance. + bf, e := readBalance(tx.Query(ctx, + Statement{"SELECT Balance FROM Accounts WHERE AccountId = @id", map[string]interface{}{"id": int64(1)}})) + if e != nil { + return e + } + bb, e := readBalance(tx.Read(ctx, "Accounts", KeySets(Key{int64(2)}), []string{"Balance"})) + if e != nil { + return e + } + if bf <= 0 { + return nil + } + bf-- + bb++ + return tx.BufferWrite([]*Mutation{ + Update("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(1), bf}), + Update("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(2), bb}), + }) + }) + if err != nil { + t.Fatalf("%d: failed to execute transaction: %v", iter, err) + } + }(i) + } + // Because of context timeout, all goroutines will eventually return. + wg.Wait() + _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + var bf, bb int64 + r, e := tx.ReadRow(ctx, "Accounts", Key{int64(1)}, []string{"Balance"}) + if e != nil { + return e + } + if ce := r.Column(0, &bf); ce != nil { + return ce + } + bb, e = readBalance(tx.ReadUsingIndex(ctx, "Accounts", "AccountByNickname", KeySets(Key{"Bar"}), []string{"Balance"})) + if e != nil { + return e + } + if bf != 30 || bb != 21 { + t.Errorf("Foo's balance is now %v and Bar's balance is now %v, want %v and %v", bf, bb, 30, 21) + } + return nil + }) + if err != nil { + t.Errorf("failed to check balances: %v", err) + } +} + +const ( + testTable = "TestTable" + testTableIndex = "TestTableByValue" +) + +var testTableColumns = []string{"Key", "StringValue"} + +func TestReads(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + // Set up testing environment. + client, _, tearDown := prepare(ctx, t, readDBStatements) + defer tearDown() + + // Includes k0..k14. Strings sort lexically, eg "k1" < "k10" < "k2". + var ms []*Mutation + for i := 0; i < 15; i++ { + ms = append(ms, InsertOrUpdate(testTable, + testTableColumns, + []interface{}{fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i)})) + } + // Don't use ApplyAtLeastOnce, so we can test the other code path. + if _, err := client.Apply(ctx, ms); err != nil { + t.Fatal(err) + } + + // Empty read. + rows, err := readAllTestTable(client.Single().Read(ctx, testTable, + KeyRange{Start: Key{"k99"}, End: Key{"z"}}, testTableColumns)) + if err != nil { + t.Fatal(err) + } + if got, want := len(rows), 0; got != want { + t.Errorf("got %d, want %d", got, want) + } + + // Index empty read. + rows, err = readAllTestTable(client.Single().ReadUsingIndex(ctx, testTable, testTableIndex, + KeyRange{Start: Key{"v99"}, End: Key{"z"}}, testTableColumns)) + if err != nil { + t.Fatal(err) + } + if got, want := len(rows), 0; got != want { + t.Errorf("got %d, want %d", got, want) + } + + // Point read. + row, err := client.Single().ReadRow(ctx, testTable, Key{"k1"}, testTableColumns) + if err != nil { + t.Fatal(err) + } + var got testTableRow + if err := row.ToStruct(&got); err != nil { + t.Fatal(err) + } + if want := (testTableRow{"k1", "v1"}); got != want { + t.Errorf("got %v, want %v", got, want) + } + + // Point read not found. + _, err = client.Single().ReadRow(ctx, testTable, Key{"k999"}, testTableColumns) + if ErrCode(err) != codes.NotFound { + t.Fatalf("got %v, want NotFound", err) + } + + // No index point read not found, because Go does not have ReadRowUsingIndex. + + rangeReads(ctx, t, client) + indexRangeReads(ctx, t, client) +} + +func rangeReads(ctx context.Context, t *testing.T, client *Client) { + checkRange := func(ks KeySet, wantNums ...int) { + if msg, ok := compareRows(client.Single().Read(ctx, testTable, ks, testTableColumns), wantNums); !ok { + t.Errorf("key set %+v: %s", ks, msg) + } + } + + checkRange(Key{"k1"}, 1) + checkRange(KeyRange{Key{"k3"}, Key{"k5"}, ClosedOpen}, 3, 4) + checkRange(KeyRange{Key{"k3"}, Key{"k5"}, ClosedClosed}, 3, 4, 5) + checkRange(KeyRange{Key{"k3"}, Key{"k5"}, OpenClosed}, 4, 5) + checkRange(KeyRange{Key{"k3"}, Key{"k5"}, OpenOpen}, 4) + + // Partial key specification. + checkRange(KeyRange{Key{"k7"}, Key{}, ClosedClosed}, 7, 8, 9) + checkRange(KeyRange{Key{"k7"}, Key{}, OpenClosed}, 8, 9) + checkRange(KeyRange{Key{}, Key{"k11"}, ClosedOpen}, 0, 1, 10) + checkRange(KeyRange{Key{}, Key{"k11"}, ClosedClosed}, 0, 1, 10, 11) + + // The following produce empty ranges. + // TODO(jba): Consider a multi-part key to illustrate partial key behavior. + // checkRange(KeyRange{Key{"k7"}, Key{}, ClosedOpen}) + // checkRange(KeyRange{Key{"k7"}, Key{}, OpenOpen}) + // checkRange(KeyRange{Key{}, Key{"k11"}, OpenOpen}) + // checkRange(KeyRange{Key{}, Key{"k11"}, OpenClosed}) + + // Prefix is component-wise, not string prefix. + checkRange(Key{"k1"}.AsPrefix(), 1) + checkRange(KeyRange{Key{"k1"}, Key{"k2"}, ClosedOpen}, 1, 10, 11, 12, 13, 14) + + checkRange(AllKeys(), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) +} + +func indexRangeReads(ctx context.Context, t *testing.T, client *Client) { + checkRange := func(ks KeySet, wantNums ...int) { + if msg, ok := compareRows(client.Single().ReadUsingIndex(ctx, testTable, testTableIndex, ks, testTableColumns), + wantNums); !ok { + t.Errorf("key set %+v: %s", ks, msg) + } + } + + checkRange(Key{"v1"}, 1) + checkRange(KeyRange{Key{"v3"}, Key{"v5"}, ClosedOpen}, 3, 4) + checkRange(KeyRange{Key{"v3"}, Key{"v5"}, ClosedClosed}, 3, 4, 5) + checkRange(KeyRange{Key{"v3"}, Key{"v5"}, OpenClosed}, 4, 5) + checkRange(KeyRange{Key{"v3"}, Key{"v5"}, OpenOpen}, 4) + + // // Partial key specification. + checkRange(KeyRange{Key{"v7"}, Key{}, ClosedClosed}, 7, 8, 9) + checkRange(KeyRange{Key{"v7"}, Key{}, OpenClosed}, 8, 9) + checkRange(KeyRange{Key{}, Key{"v11"}, ClosedOpen}, 0, 1, 10) + checkRange(KeyRange{Key{}, Key{"v11"}, ClosedClosed}, 0, 1, 10, 11) + + // // The following produce empty ranges. + // checkRange(KeyRange{Key{"v7"}, Key{}, ClosedOpen}) + // checkRange(KeyRange{Key{"v7"}, Key{}, OpenOpen}) + // checkRange(KeyRange{Key{}, Key{"v11"}, OpenOpen}) + // checkRange(KeyRange{Key{}, Key{"v11"}, OpenClosed}) + + // // Prefix is component-wise, not string prefix. + checkRange(Key{"v1"}.AsPrefix(), 1) + checkRange(KeyRange{Key{"v1"}, Key{"v2"}, ClosedOpen}, 1, 10, 11, 12, 13, 14) + checkRange(AllKeys(), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) + + // Read from an index with DESC ordering. + wantNums := []int{14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + if msg, ok := compareRows(client.Single().ReadUsingIndex(ctx, testTable, "TestTableByValueDesc", AllKeys(), testTableColumns), + wantNums); !ok { + t.Errorf("desc: %s", msg) + } +} + +func compareRows(iter *RowIterator, wantNums []int) (string, bool) { + rows, err := readAllTestTable(iter) + if err != nil { + return err.Error(), false + } + want := map[string]string{} + for _, n := range wantNums { + want[fmt.Sprintf("k%d", n)] = fmt.Sprintf("v%d", n) + } + got := map[string]string{} + for _, r := range rows { + got[r.Key] = r.StringValue + } + if !testEqual(got, want) { + return fmt.Sprintf("got %v, want %v", got, want), false + } + return "", true +} + +func TestEarlyTimestamp(t *testing.T) { + t.Parallel() + // Test that we can get the timestamp from a read-only transaction as + // soon as we have read at least one row. + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + // Set up testing environment. + client, _, tearDown := prepare(ctx, t, readDBStatements) + defer tearDown() + + var ms []*Mutation + for i := 0; i < 3; i++ { + ms = append(ms, InsertOrUpdate(testTable, + testTableColumns, + []interface{}{fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i)})) + } + if _, err := client.Apply(ctx, ms, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + + txn := client.Single() + iter := txn.Read(ctx, testTable, AllKeys(), testTableColumns) + defer iter.Stop() + // In single-use transaction, we should get an error before reading anything. + if _, err := txn.Timestamp(); err == nil { + t.Error("wanted error, got nil") + } + // After reading one row, the timestamp should be available. + _, err := iter.Next() + if err != nil { + t.Fatal(err) + } + if _, err := txn.Timestamp(); err != nil { + t.Errorf("got %v, want nil", err) + } + + txn = client.ReadOnlyTransaction() + defer txn.Close() + iter = txn.Read(ctx, testTable, AllKeys(), testTableColumns) + defer iter.Stop() + // In an ordinary read-only transaction, the timestamp should be + // available immediately. + if _, err := txn.Timestamp(); err != nil { + t.Errorf("got %v, want nil", err) + } +} + +func TestNestedTransaction(t *testing.T) { + t.Parallel() + // You cannot use a transaction from inside a read-write transaction. + ctx := context.Background() + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + _, err := client.ReadWriteTransaction(ctx, + func(context.Context, *ReadWriteTransaction) error { return nil }) + if ErrCode(err) != codes.FailedPrecondition { + t.Fatalf("got %v, want FailedPrecondition", err) + } + _, err = client.Single().ReadRow(ctx, "Singers", Key{1}, []string{"SingerId"}) + if ErrCode(err) != codes.FailedPrecondition { + t.Fatalf("got %v, want FailedPrecondition", err) + } + rot := client.ReadOnlyTransaction() + defer rot.Close() + _, err = rot.ReadRow(ctx, "Singers", Key{1}, []string{"SingerId"}) + if ErrCode(err) != codes.FailedPrecondition { + t.Fatalf("got %v, want FailedPrecondition", err) + } + return nil + }) + if err != nil { + t.Fatal(err) + } +} + +// Test client recovery on database recreation. +func TestDbRemovalRecovery(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + client, dbPath, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + // Drop the testing database. + if err := admin.DropDatabase(ctx, &adminpb.DropDatabaseRequest{Database: dbPath}); err != nil { + t.Fatalf("failed to drop testing database %v: %v", dbPath, err) + } + + // Now, send the query. + iter := client.Single().Query(ctx, Statement{SQL: "SELECT SingerId FROM Singers"}) + defer iter.Stop() + if _, err := iter.Next(); err == nil { + t.Errorf("client sends query to removed database successfully, want it to fail") + } + + // Recreate database and table. + dbName := dbPath[strings.LastIndex(dbPath, "/")+1:] + op, err := admin.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{ + Parent: fmt.Sprintf("projects/%v/instances/%v", testProjectID, testInstanceID), + CreateStatement: "CREATE DATABASE " + dbName, + ExtraStatements: []string{ + `CREATE TABLE Singers ( + SingerId INT64 NOT NULL, + FirstName STRING(1024), + LastName STRING(1024), + SingerInfo BYTES(MAX) + ) PRIMARY KEY (SingerId)`, + }, + }) + if err != nil { + t.Fatalf("cannot recreate testing DB %v: %v", dbPath, err) + } + if _, err := op.Wait(ctx); err != nil { + t.Fatalf("cannot recreate testing DB %v: %v", dbPath, err) + } + + // Now, send the query again. + iter = client.Single().Query(ctx, Statement{SQL: "SELECT SingerId FROM Singers"}) + defer iter.Stop() + _, err = iter.Next() + if err != nil && err != iterator.Done { + t.Errorf("failed to send query to database %v: %v", dbPath, err) + } +} + +// Test encoding/decoding non-struct Cloud Spanner types. +func TestBasicTypes(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + t1, _ := time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z") + // Boundaries + t2, _ := time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00.000000000Z") + t3, _ := time.Parse(time.RFC3339Nano, "9999-12-31T23:59:59.999999999Z") + d1, _ := civil.ParseDate("2016-11-15") + // Boundaries + d2, _ := civil.ParseDate("0001-01-01") + d3, _ := civil.ParseDate("9999-12-31") + + tests := []struct { + col string + val interface{} + want interface{} + }{ + {col: "String", val: ""}, + {col: "String", val: "", want: NullString{"", true}}, + {col: "String", val: "foo"}, + {col: "String", val: "foo", want: NullString{"foo", true}}, + {col: "String", val: NullString{"bar", true}, want: "bar"}, + {col: "String", val: NullString{"bar", false}, want: NullString{"", false}}, + {col: "StringArray", val: []string(nil), want: []NullString(nil)}, + {col: "StringArray", val: []string{}, want: []NullString{}}, + {col: "StringArray", val: []string{"foo", "bar"}, want: []NullString{{"foo", true}, {"bar", true}}}, + {col: "StringArray", val: []NullString(nil)}, + {col: "StringArray", val: []NullString{}}, + {col: "StringArray", val: []NullString{{"foo", true}, {}}}, + {col: "Bytes", val: []byte{}}, + {col: "Bytes", val: []byte{1, 2, 3}}, + {col: "Bytes", val: []byte(nil)}, + {col: "BytesArray", val: [][]byte(nil)}, + {col: "BytesArray", val: [][]byte{}}, + {col: "BytesArray", val: [][]byte{{1}, {2, 3}}}, + {col: "Int64a", val: 0, want: int64(0)}, + {col: "Int64a", val: -1, want: int64(-1)}, + {col: "Int64a", val: 2, want: int64(2)}, + {col: "Int64a", val: int64(3)}, + {col: "Int64a", val: 4, want: NullInt64{4, true}}, + {col: "Int64a", val: NullInt64{5, true}, want: int64(5)}, + {col: "Int64a", val: NullInt64{6, true}, want: int64(6)}, + {col: "Int64a", val: NullInt64{7, false}, want: NullInt64{0, false}}, + {col: "Int64Array", val: []int(nil), want: []NullInt64(nil)}, + {col: "Int64Array", val: []int{}, want: []NullInt64{}}, + {col: "Int64Array", val: []int{1, 2}, want: []NullInt64{{1, true}, {2, true}}}, + {col: "Int64Array", val: []int64(nil), want: []NullInt64(nil)}, + {col: "Int64Array", val: []int64{}, want: []NullInt64{}}, + {col: "Int64Array", val: []int64{1, 2}, want: []NullInt64{{1, true}, {2, true}}}, + {col: "Int64Array", val: []NullInt64(nil)}, + {col: "Int64Array", val: []NullInt64{}}, + {col: "Int64Array", val: []NullInt64{{1, true}, {}}}, + {col: "Bool", val: false}, + {col: "Bool", val: true}, + {col: "Bool", val: false, want: NullBool{false, true}}, + {col: "Bool", val: true, want: NullBool{true, true}}, + {col: "Bool", val: NullBool{true, true}}, + {col: "Bool", val: NullBool{false, false}}, + {col: "BoolArray", val: []bool(nil), want: []NullBool(nil)}, + {col: "BoolArray", val: []bool{}, want: []NullBool{}}, + {col: "BoolArray", val: []bool{true, false}, want: []NullBool{{true, true}, {false, true}}}, + {col: "BoolArray", val: []NullBool(nil)}, + {col: "BoolArray", val: []NullBool{}}, + {col: "BoolArray", val: []NullBool{{false, true}, {true, true}, {}}}, + {col: "Float64", val: 0.0}, + {col: "Float64", val: 3.14}, + {col: "Float64", val: math.NaN()}, + {col: "Float64", val: math.Inf(1)}, + {col: "Float64", val: math.Inf(-1)}, + {col: "Float64", val: 2.78, want: NullFloat64{2.78, true}}, + {col: "Float64", val: NullFloat64{2.71, true}, want: 2.71}, + {col: "Float64", val: NullFloat64{1.41, true}, want: NullFloat64{1.41, true}}, + {col: "Float64", val: NullFloat64{0, false}}, + {col: "Float64Array", val: []float64(nil), want: []NullFloat64(nil)}, + {col: "Float64Array", val: []float64{}, want: []NullFloat64{}}, + {col: "Float64Array", val: []float64{2.72, 3.14, math.Inf(1)}, want: []NullFloat64{{2.72, true}, {3.14, true}, {math.Inf(1), true}}}, + {col: "Float64Array", val: []NullFloat64(nil)}, + {col: "Float64Array", val: []NullFloat64{}}, + {col: "Float64Array", val: []NullFloat64{{2.72, true}, {math.Inf(1), true}, {}}}, + {col: "Date", val: d1}, + {col: "Date", val: d1, want: NullDate{d1, true}}, + {col: "Date", val: NullDate{d1, true}}, + {col: "Date", val: NullDate{d1, true}, want: d1}, + {col: "Date", val: NullDate{civil.Date{}, false}}, + {col: "DateArray", val: []civil.Date(nil), want: []NullDate(nil)}, + {col: "DateArray", val: []civil.Date{}, want: []NullDate{}}, + {col: "DateArray", val: []civil.Date{d1, d2, d3}, want: []NullDate{{d1, true}, {d2, true}, {d3, true}}}, + {col: "Timestamp", val: t1}, + {col: "Timestamp", val: t1, want: NullTime{t1, true}}, + {col: "Timestamp", val: NullTime{t1, true}}, + {col: "Timestamp", val: NullTime{t1, true}, want: t1}, + {col: "Timestamp", val: NullTime{}}, + {col: "TimestampArray", val: []time.Time(nil), want: []NullTime(nil)}, + {col: "TimestampArray", val: []time.Time{}, want: []NullTime{}}, + {col: "TimestampArray", val: []time.Time{t1, t2, t3}, want: []NullTime{{t1, true}, {t2, true}, {t3, true}}}, + } + + // Write rows into table first. + var muts []*Mutation + for i, test := range tests { + muts = append(muts, InsertOrUpdate("Types", []string{"RowID", test.col}, []interface{}{i, test.val})) + } + if _, err := client.Apply(ctx, muts, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + + for i, test := range tests { + row, err := client.Single().ReadRow(ctx, "Types", []interface{}{i}, []string{test.col}) + if err != nil { + t.Fatalf("Unable to fetch row %v: %v", i, err) + } + // Create new instance of type of test.want. + want := test.want + if want == nil { + want = test.val + } + gotp := reflect.New(reflect.TypeOf(want)) + if err := row.Column(0, gotp.Interface()); err != nil { + t.Errorf("%d: col:%v val:%#v, %v", i, test.col, test.val, err) + continue + } + got := reflect.Indirect(gotp).Interface() + + // One of the test cases is checking NaN handling. Given + // NaN!=NaN, we can't use reflect to test for it. + if isNaN(got) && isNaN(want) { + continue + } + + // Check non-NaN cases. + if !testEqual(got, want) { + t.Errorf("%d: col:%v val:%#v, got %#v, want %#v", i, test.col, test.val, got, want) + continue + } + } +} + +// Test decoding Cloud Spanner STRUCT type. +func TestStructTypes(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + tests := []struct { + q Statement + want func(r *Row) error + }{ + { + q: Statement{SQL: `SELECT ARRAY(SELECT STRUCT(1, 2))`}, + want: func(r *Row) error { + // Test STRUCT ARRAY decoding to []NullRow. + var rows []NullRow + if err := r.Column(0, &rows); err != nil { + return err + } + if len(rows) != 1 { + return fmt.Errorf("len(rows) = %d; want 1", len(rows)) + } + if !rows[0].Valid { + return fmt.Errorf("rows[0] is NULL") + } + var i, j int64 + if err := rows[0].Row.Columns(&i, &j); err != nil { + return err + } + if i != 1 || j != 2 { + return fmt.Errorf("got (%d,%d), want (1,2)", i, j) + } + return nil + }, + }, + { + q: Statement{SQL: `SELECT ARRAY(SELECT STRUCT(1 as foo, 2 as bar)) as col1`}, + want: func(r *Row) error { + // Test Row.ToStruct. + s := struct { + Col1 []*struct { + Foo int64 `spanner:"foo"` + Bar int64 `spanner:"bar"` + } `spanner:"col1"` + }{} + if err := r.ToStruct(&s); err != nil { + return err + } + want := struct { + Col1 []*struct { + Foo int64 `spanner:"foo"` + Bar int64 `spanner:"bar"` + } `spanner:"col1"` + }{ + Col1: []*struct { + Foo int64 `spanner:"foo"` + Bar int64 `spanner:"bar"` + }{ + { + Foo: 1, + Bar: 2, + }, + }, + } + if !testEqual(want, s) { + return fmt.Errorf("unexpected decoding result: %v, want %v", s, want) + } + return nil + }, + }, + } + for i, test := range tests { + iter := client.Single().Query(ctx, test.q) + defer iter.Stop() + row, err := iter.Next() + if err != nil { + t.Errorf("%d: %v", i, err) + continue + } + if err := test.want(row); err != nil { + t.Errorf("%d: %v", i, err) + continue + } + } +} + +func TestStructParametersUnsupported(t *testing.T) { + t.Parallel() + ctx := context.Background() + client, _, tearDown := prepare(ctx, t, nil) + defer tearDown() + + for _, test := range []struct { + param interface{} + wantCode codes.Code + wantMsgPart string + }{ + { + struct { + Field int + }{10}, + codes.Unimplemented, + "Unsupported query shape: " + + "A struct value cannot be returned as a column value. " + + "Rewrite the query to flatten the struct fields in the result.", + }, + { + []struct { + Field int + }{{10}, {20}}, + codes.Unimplemented, + "Unsupported query shape: " + + "This query can return a null-valued array of struct, " + + "which is not supported by Spanner.", + }, + } { + iter := client.Single().Query(ctx, Statement{ + SQL: "SELECT @p", + Params: map[string]interface{}{"p": test.param}, + }) + _, err := iter.Next() + iter.Stop() + if msg, ok := matchError(err, test.wantCode, test.wantMsgPart); !ok { + t.Fatal(msg) + } + } +} + +// Test queries of the form "SELECT expr". +func TestQueryExpressions(t *testing.T) { + t.Parallel() + ctx := context.Background() + client, _, tearDown := prepare(ctx, t, nil) + defer tearDown() + + newRow := func(vals []interface{}) *Row { + row, err := NewRow(make([]string, len(vals)), vals) + if err != nil { + t.Fatal(err) + } + return row + } + + tests := []struct { + expr string + want interface{} + }{ + {"1", int64(1)}, + {"[1, 2, 3]", []NullInt64{{1, true}, {2, true}, {3, true}}}, + {"[1, NULL, 3]", []NullInt64{{1, true}, {0, false}, {3, true}}}, + {"IEEE_DIVIDE(1, 0)", math.Inf(1)}, + {"IEEE_DIVIDE(-1, 0)", math.Inf(-1)}, + {"IEEE_DIVIDE(0, 0)", math.NaN()}, + // TODO(jba): add IEEE_DIVIDE(0, 0) to the following array when we have a better equality predicate. + {"[IEEE_DIVIDE(1, 0), IEEE_DIVIDE(-1, 0)]", []NullFloat64{{math.Inf(1), true}, {math.Inf(-1), true}}}, + {"ARRAY(SELECT AS STRUCT * FROM (SELECT 'a', 1) WHERE 0 = 1)", []NullRow{}}, + {"ARRAY(SELECT STRUCT(1, 2))", []NullRow{{Row: *newRow([]interface{}{1, 2}), Valid: true}}}, + } + for _, test := range tests { + iter := client.Single().Query(ctx, Statement{SQL: "SELECT " + test.expr}) + defer iter.Stop() + row, err := iter.Next() + if err != nil { + t.Errorf("%q: %v", test.expr, err) + continue + } + // Create new instance of type of test.want. + gotp := reflect.New(reflect.TypeOf(test.want)) + if err := row.Column(0, gotp.Interface()); err != nil { + t.Errorf("%q: Column returned error %v", test.expr, err) + continue + } + got := reflect.Indirect(gotp).Interface() + // TODO(jba): remove isNaN special case when we have a better equality predicate. + if isNaN(got) && isNaN(test.want) { + continue + } + if !testEqual(got, test.want) { + t.Errorf("%q\n got %#v\nwant %#v", test.expr, got, test.want) + } + } +} + +func TestQueryStats(t *testing.T) { + t.Parallel() + ctx := context.Background() + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + accounts := []*Mutation{ + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(1), "Foo", int64(50)}), + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(2), "Bar", int64(1)}), + } + if _, err := client.Apply(ctx, accounts, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + const sql = "SELECT Balance FROM Accounts" + + qp, err := client.Single().AnalyzeQuery(ctx, Statement{sql, nil}) + if err != nil { + t.Fatal(err) + } + if len(qp.PlanNodes) == 0 { + t.Error("got zero plan nodes, expected at least one") + } + + iter := client.Single().QueryWithStats(ctx, Statement{sql, nil}) + defer iter.Stop() + for { + _, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + } + if iter.QueryPlan == nil { + t.Error("got nil QueryPlan, expected one") + } + if iter.QueryStats == nil { + t.Error("got nil QueryStats, expected some") + } +} + +func isNaN(x interface{}) bool { + f, ok := x.(float64) + if !ok { + return false + } + return math.IsNaN(f) +} + +func TestInvalidDatabase(t *testing.T) { + t.Parallel() + if testing.Short() { + t.Skip("Integration tests skipped in short mode") + } + if testProjectID == "" { + t.Skip("Integration tests skipped: GCLOUD_TESTS_GOLANG_PROJECT_ID is missing") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, Scope) + if ts == nil { + t.Skip("Integration test skipped: cannot get service account credential from environment variable GCLOUD_TESTS_GOLANG_KEY") + } + db := fmt.Sprintf("projects/%v/instances/%v/databases/invalid", testProjectID, testInstanceID) + c, err := NewClient(ctx, db, option.WithTokenSource(ts)) + // Client creation should succeed even if the database is invalid. + if err != nil { + t.Fatal(err) + } + _, err = c.Single().ReadRow(ctx, "TestTable", Key{1}, []string{"col1"}) + if msg, ok := matchError(err, codes.NotFound, ""); !ok { + t.Fatal(msg) + } +} + +func TestReadErrors(t *testing.T) { + t.Parallel() + ctx := context.Background() + client, _, tearDown := prepare(ctx, t, readDBStatements) + defer tearDown() + + // Read over invalid table fails + _, err := client.Single().ReadRow(ctx, "badTable", Key{1}, []string{"StringValue"}) + if msg, ok := matchError(err, codes.NotFound, "badTable"); !ok { + t.Error(msg) + } + // Read over invalid column fails + _, err = client.Single().ReadRow(ctx, "TestTable", Key{1}, []string{"badcol"}) + if msg, ok := matchError(err, codes.NotFound, "badcol"); !ok { + t.Error(msg) + } + + // Invalid query fails + iter := client.Single().Query(ctx, Statement{SQL: "SELECT Apples AND Oranges"}) + defer iter.Stop() + _, err = iter.Next() + if msg, ok := matchError(err, codes.InvalidArgument, "unrecognized name"); !ok { + t.Error(msg) + } + + // Read should fail on cancellation. + cctx, cancel := context.WithCancel(ctx) + cancel() + _, err = client.Single().ReadRow(cctx, "TestTable", Key{1}, []string{"StringValue"}) + if msg, ok := matchError(err, codes.Canceled, ""); !ok { + t.Error(msg) + } + // Read should fail if deadline exceeded. + dctx, _ := context.WithTimeout(ctx, time.Nanosecond) + <-dctx.Done() + _, err = client.Single().ReadRow(dctx, "TestTable", Key{1}, []string{"StringValue"}) + if msg, ok := matchError(err, codes.DeadlineExceeded, ""); !ok { + t.Error(msg) + } +} + +func matchError(got error, wantCode codes.Code, wantMsgPart string) (string, bool) { + if ErrCode(got) != wantCode || !strings.Contains(strings.ToLower(ErrDesc(got)), strings.ToLower(wantMsgPart)) { + return fmt.Sprintf("got error <%v>\n"+`want `, got, wantCode, wantMsgPart), false + } + return "", true +} + +func rowToValues(r *Row) ([]interface{}, error) { + var x int64 + var y, z string + if err := r.Column(0, &x); err != nil { + return nil, err + } + if err := r.Column(1, &y); err != nil { + return nil, err + } + if err := r.Column(2, &z); err != nil { + return nil, err + } + return []interface{}{x, y, z}, nil +} + +func readAll(iter *RowIterator) ([][]interface{}, error) { + defer iter.Stop() + var vals [][]interface{} + for { + row, err := iter.Next() + if err == iterator.Done { + return vals, nil + } + if err != nil { + return nil, err + } + v, err := rowToValues(row) + if err != nil { + return nil, err + } + vals = append(vals, v) + } +} + +func readAllTestTable(iter *RowIterator) ([]testTableRow, error) { + defer iter.Stop() + var vals []testTableRow + for { + row, err := iter.Next() + if err == iterator.Done { + return vals, nil + } + if err != nil { + return nil, err + } + var ttr testTableRow + if err := row.ToStruct(&ttr); err != nil { + return nil, err + } + vals = append(vals, ttr) + } +} + +// Test TransactionRunner. Test that transactions are aborted and retried as expected. +func TestTransactionRunner(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + client, _, tearDown := prepare(ctx, t, singerDBStatements) + defer tearDown() + + // Test 1: User error should abort the transaction. + _, _ = client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + tx.BufferWrite([]*Mutation{ + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(1), "Foo", int64(50)})}) + return errors.New("user error") + }) + // Empty read. + rows, err := readAllTestTable(client.Single().Read(ctx, "Accounts", Key{1}, []string{"AccountId", "Nickname", "Balance"})) + if err != nil { + t.Fatal(err) + } + if got, want := len(rows), 0; got != want { + t.Errorf("Empty read, got %d, want %d.", got, want) + } + + // Test 2: Expect abort and retry. + // We run two ReadWriteTransactions concurrently and make txn1 abort txn2 by committing writes to the column txn2 have read, + // and expect the following read to abort and txn2 retries. + + // Set up two accounts + accounts := []*Mutation{ + Insert("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(1), int64(0)}), + Insert("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(2), int64(1)}), + } + if _, err := client.Apply(ctx, accounts, ApplyAtLeastOnce()); err != nil { + t.Fatal(err) + } + + var ( + cTxn1Start = make(chan struct{}) + cTxn1Commit = make(chan struct{}) + cTxn2Start = make(chan struct{}) + wg sync.WaitGroup + ) + + // read balance, check error if we don't expect abort. + readBalance := func(tx interface { + ReadRow(ctx context.Context, table string, key Key, columns []string) (*Row, error) + }, key int64, expectAbort bool) (int64, error) { + var b int64 + r, e := tx.ReadRow(ctx, "Accounts", Key{int64(key)}, []string{"Balance"}) + if e != nil { + if expectAbort && !isAbortErr(e) { + t.Errorf("ReadRow got %v, want Abort error.", e) + } + return b, e + } + if ce := r.Column(0, &b); ce != nil { + return b, ce + } + return b, nil + } + + wg.Add(2) + // Txn 1 + go func() { + defer wg.Done() + var once sync.Once + _, e := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + b, e := readBalance(tx, 1, false) + if e != nil { + return e + } + // txn 1 can abort, in that case we skip closing the channel on retry. + once.Do(func() { close(cTxn1Start) }) + e = tx.BufferWrite([]*Mutation{ + Update("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(1), int64(b + 1)})}) + if e != nil { + return e + } + // Wait for second transaction. + <-cTxn2Start + return nil + }) + close(cTxn1Commit) + if e != nil { + t.Errorf("Transaction 1 commit, got %v, want nil.", e) + } + }() + // Txn 2 + go func() { + // Wait until txn 1 starts. + <-cTxn1Start + defer wg.Done() + var ( + once sync.Once + b1 int64 + b2 int64 + e error + ) + _, e = client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + if b1, e = readBalance(tx, 1, false); e != nil { + return e + } + // Skip closing channel on retry. + once.Do(func() { close(cTxn2Start) }) + // Wait until txn 1 successfully commits. + <-cTxn1Commit + // Txn1 has committed and written a balance to the account. + // Now this transaction (txn2) reads and re-writes the balance. + // The first time through, it will abort because it overlaps with txn1. + // Then it will retry after txn1 commits, and succeed. + if b2, e = readBalance(tx, 2, true); e != nil { + return e + } + return tx.BufferWrite([]*Mutation{ + Update("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(2), int64(b1 + b2)})}) + }) + if e != nil { + t.Errorf("Transaction 2 commit, got %v, want nil.", e) + } + }() + wg.Wait() + // Check that both transactions' effects are visible. + for i := int64(1); i <= int64(2); i++ { + if b, e := readBalance(client.Single(), i, false); e != nil { + t.Fatalf("ReadBalance for key %d error %v.", i, e) + } else if b != i { + t.Errorf("Balance for key %d, got %d, want %d.", i, b, i) + } + } +} + +// createClient creates Cloud Spanner data client. +func createClient(ctx context.Context, dbPath string) (client *Client, err error) { + client, err = NewClientWithConfig(ctx, dbPath, ClientConfig{ + SessionPoolConfig: SessionPoolConfig{WriteSessions: 0.2}, + }, option.WithTokenSource(testutil.TokenSource(ctx, Scope)), option.WithEndpoint(endpoint)) + if err != nil { + return nil, fmt.Errorf("cannot create data client on DB %v: %v", dbPath, err) + } + return client, nil +} + +// populate prepares the database with some data. +func populate(ctx context.Context, client *Client) error { + // Populate data + var err error + m := InsertMap("test", map[string]interface{}{ + "a": str1, + "b": str2, + }) + _, err = client.Apply(ctx, []*Mutation{m}) + return err +} + +// Test PartitionQuery of BatchReadOnlyTransaction, create partitions then +// serialize and deserialize both transaction and partition to be used in +// execution on another client, and compare results. +func TestBatchQuery(t *testing.T) { + t.Parallel() + // Set up testing environment. + var ( + client2 *Client + err error + ) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + client, dbPath, tearDown := prepare(ctx, t, simpleDBStatements) + defer tearDown() + if err = populate(ctx, client); err != nil { + t.Fatal(err) + } + if client2, err = createClient(ctx, dbPath); err != nil { + t.Fatal(err) + } + defer client2.Close() + + // PartitionQuery + var ( + txn *BatchReadOnlyTransaction + partitions []*Partition + stmt = Statement{SQL: "SELECT * FROM test;"} + ) + + if txn, err = client.BatchReadOnlyTransaction(ctx, StrongRead()); err != nil { + t.Fatal(err) + } + defer txn.Cleanup(ctx) + if partitions, err = txn.PartitionQuery(ctx, stmt, PartitionOptions{0, 3}); err != nil { + t.Fatal(err) + } + + // Reconstruct BatchReadOnlyTransactionID and execute partitions + var ( + tid2 BatchReadOnlyTransactionID + data []byte + gotResult bool // if we get matching result from two separate txns + ) + if data, err = txn.ID.MarshalBinary(); err != nil { + t.Fatalf("encoding failed %v", err) + } + if err = tid2.UnmarshalBinary(data); err != nil { + t.Fatalf("decoding failed %v", err) + } + txn2 := client2.BatchReadOnlyTransactionFromID(tid2) + + // Execute Partitions and compare results + for i, p := range partitions { + iter := txn.Execute(ctx, p) + defer iter.Stop() + p2 := serdesPartition(t, i, p) + iter2 := txn2.Execute(ctx, &p2) + defer iter2.Stop() + + row1, err1 := iter.Next() + row2, err2 := iter2.Next() + if err1 != err2 { + t.Fatalf("execution failed for different reasons: %v, %v", err1, err2) + continue + } + if !testEqual(row1, row2) { + t.Fatalf("execution returned different values: %v, %v", row1, row2) + continue + } + if row1 == nil { + continue + } + var a, b string + if err = row1.Columns(&a, &b); err != nil { + t.Fatalf("failed to parse row %v", err) + continue + } + if a == str1 && b == str2 { + gotResult = true + } + } + if !gotResult { + t.Fatalf("execution didn't return expected values") + } +} + +// Test PartitionRead of BatchReadOnlyTransaction, similar to TestBatchQuery +func TestBatchRead(t *testing.T) { + t.Parallel() + // Set up testing environment. + var ( + client2 *Client + err error + ) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + client, dbPath, tearDown := prepare(ctx, t, simpleDBStatements) + defer tearDown() + if err = populate(ctx, client); err != nil { + t.Fatal(err) + } + if client2, err = createClient(ctx, dbPath); err != nil { + t.Fatal(err) + } + defer client2.Close() + + // PartitionRead + var ( + txn *BatchReadOnlyTransaction + partitions []*Partition + ) + + if txn, err = client.BatchReadOnlyTransaction(ctx, StrongRead()); err != nil { + t.Fatal(err) + } + defer txn.Cleanup(ctx) + if partitions, err = txn.PartitionRead(ctx, "test", AllKeys(), simpleDBTableColumns, PartitionOptions{0, 3}); err != nil { + t.Fatal(err) + } + + // Reconstruct BatchReadOnlyTransactionID and execute partitions + var ( + tid2 BatchReadOnlyTransactionID + data []byte + gotResult bool // if we get matching result from two separate txns + ) + if data, err = txn.ID.MarshalBinary(); err != nil { + t.Fatalf("encoding failed %v", err) + } + if err = tid2.UnmarshalBinary(data); err != nil { + t.Fatalf("decoding failed %v", err) + } + txn2 := client2.BatchReadOnlyTransactionFromID(tid2) + + // Execute Partitions and compare results + for i, p := range partitions { + iter := txn.Execute(ctx, p) + defer iter.Stop() + p2 := serdesPartition(t, i, p) + iter2 := txn2.Execute(ctx, &p2) + defer iter2.Stop() + + row1, err1 := iter.Next() + row2, err2 := iter2.Next() + if err1 != err2 { + t.Fatalf("execution failed for different reasons: %v, %v", err1, err2) + continue + } + if !testEqual(row1, row2) { + t.Fatalf("execution returned different values: %v, %v", row1, row2) + continue + } + if row1 == nil { + continue + } + var a, b string + if err = row1.Columns(&a, &b); err != nil { + t.Fatalf("failed to parse row %v", err) + continue + } + if a == str1 && b == str2 { + gotResult = true + } + } + if !gotResult { + t.Fatalf("execution didn't return expected values") + } +} + +// Test normal txReadEnv method on BatchReadOnlyTransaction. +func TestBROTNormal(t *testing.T) { + t.Parallel() + // Set up testing environment and create txn. + var ( + txn *BatchReadOnlyTransaction + err error + row *Row + i int64 + ) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + client, _, tearDown := prepare(ctx, t, simpleDBStatements) + defer tearDown() + + if txn, err = client.BatchReadOnlyTransaction(ctx, StrongRead()); err != nil { + t.Fatal(err) + } + defer txn.Cleanup(ctx) + if _, err := txn.PartitionRead(ctx, "test", AllKeys(), simpleDBTableColumns, PartitionOptions{0, 3}); err != nil { + t.Fatal(err) + } + // Normal query should work with BatchReadOnlyTransaction + stmt2 := Statement{SQL: "SELECT 1"} + iter := txn.Query(ctx, stmt2) + defer iter.Stop() + + row, err = iter.Next() + if err != nil { + t.Errorf("query failed with %v", err) + } + if err = row.Columns(&i); err != nil { + t.Errorf("failed to parse row %v", err) + } +} + +func TestCommitTimestamp(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + client, _, tearDown := prepare(ctx, t, ctsDBStatements) + defer tearDown() + + type testTableRow struct { + Key string + Ts NullTime + } + + var ( + cts1, cts2, ts1, ts2 time.Time + err error + ) + + // Apply mutation in sequence, expect to see commit timestamp in good order, check also the commit timestamp returned + for _, it := range []struct { + k string + t *time.Time + }{ + {"a", &cts1}, + {"b", &cts2}, + } { + tt := testTableRow{Key: it.k, Ts: NullTime{CommitTimestamp, true}} + m, err := InsertStruct("TestTable", tt) + if err != nil { + t.Fatal(err) + } + *it.t, err = client.Apply(ctx, []*Mutation{m}, ApplyAtLeastOnce()) + if err != nil { + t.Fatal(err) + } + } + + txn := client.ReadOnlyTransaction() + for _, it := range []struct { + k string + t *time.Time + }{ + {"a", &ts1}, + {"b", &ts2}, + } { + if r, e := txn.ReadRow(ctx, "TestTable", Key{it.k}, []string{"Ts"}); e != nil { + t.Fatal(err) + } else { + var got testTableRow + if err := r.ToStruct(&got); err != nil { + t.Fatal(err) + } + *it.t = got.Ts.Time + } + } + if !cts1.Equal(ts1) { + t.Errorf("Expect commit timestamp returned and read to match for txn1, got %v and %v.", cts1, ts1) + } + if !cts2.Equal(ts2) { + t.Errorf("Expect commit timestamp returned and read to match for txn2, got %v and %v.", cts2, ts2) + } + + // Try writing a timestamp in the future to commit timestamp, expect error + _, err = client.Apply(ctx, []*Mutation{InsertOrUpdate("TestTable", []string{"Key", "Ts"}, []interface{}{"a", time.Now().Add(time.Hour)})}, ApplyAtLeastOnce()) + if msg, ok := matchError(err, codes.FailedPrecondition, "Cannot write timestamps in the future"); !ok { + t.Error(msg) + } +} diff --git a/vendor/cloud.google.com/go/spanner/internal/testutil/mockclient.go b/vendor/cloud.google.com/go/spanner/internal/testutil/mockclient.go new file mode 100644 index 0000000000..fd2a58812b --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/internal/testutil/mockclient.go @@ -0,0 +1,383 @@ +/* +Copyright 2017 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 testutil + +import ( + "errors" + "fmt" + "sync" + "testing" + "time" + + "golang.org/x/net/context" + "google.golang.org/grpc/status" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes/empty" + proto3 "github.com/golang/protobuf/ptypes/struct" + pbt "github.com/golang/protobuf/ptypes/timestamp" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// Action is a mocked RPC activity that MockCloudSpannerClient will take. +type Action struct { + Method string + Err error +} + +// MockCloudSpannerClient is a mock implementation of sppb.SpannerClient. +type MockCloudSpannerClient struct { + sppb.SpannerClient + + mu sync.Mutex + t *testing.T + // Live sessions on the client. + sessions map[string]bool + // Expected set of actions that will be executed by the client. + actions []Action + // Session ping history. + pings []string + // Injected error, will be returned by all APIs. + injErr map[string]error + // Client will not fail on any request. + nice bool + // Client will stall on any requests. + freezed chan struct{} +} + +// NewMockCloudSpannerClient creates new MockCloudSpannerClient instance. +func NewMockCloudSpannerClient(t *testing.T, acts ...Action) *MockCloudSpannerClient { + mc := &MockCloudSpannerClient{t: t, sessions: map[string]bool{}, injErr: map[string]error{}} + mc.SetActions(acts...) + // Produce a closed channel, so the default action of ready is to not block. + mc.Freeze() + mc.Unfreeze() + return mc +} + +// MakeNice makes this a nice mock which will not fail on any request. +func (m *MockCloudSpannerClient) MakeNice() { + m.mu.Lock() + defer m.mu.Unlock() + m.nice = true +} + +// MakeStrict makes this a strict mock which will fail on any unexpected request. +func (m *MockCloudSpannerClient) MakeStrict() { + m.mu.Lock() + defer m.mu.Unlock() + m.nice = false +} + +// InjectError injects a global error that will be returned by all calls to method +// regardless of the actions array. +func (m *MockCloudSpannerClient) InjectError(method string, err error) { + m.mu.Lock() + defer m.mu.Unlock() + m.injErr[method] = err +} + +// SetActions sets the new set of expected actions to MockCloudSpannerClient. +func (m *MockCloudSpannerClient) SetActions(acts ...Action) { + m.mu.Lock() + defer m.mu.Unlock() + m.actions = nil + for _, act := range acts { + m.actions = append(m.actions, act) + } +} + +// DumpPings dumps the ping history. +func (m *MockCloudSpannerClient) DumpPings() []string { + m.mu.Lock() + defer m.mu.Unlock() + return append([]string(nil), m.pings...) +} + +// DumpSessions dumps the internal session table. +func (m *MockCloudSpannerClient) DumpSessions() map[string]bool { + m.mu.Lock() + defer m.mu.Unlock() + st := map[string]bool{} + for s, v := range m.sessions { + st[s] = v + } + return st +} + +// CreateSession is a placeholder for SpannerClient.CreateSession. +func (m *MockCloudSpannerClient) CreateSession(c context.Context, r *sppb.CreateSessionRequest, opts ...grpc.CallOption) (*sppb.Session, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + if err := m.injErr["CreateSession"]; err != nil { + return nil, err + } + s := &sppb.Session{} + if r.Database != "mockdb" { + // Reject other databases + return s, status.Errorf(codes.NotFound, fmt.Sprintf("database not found: %v", r.Database)) + } + // Generate & record session name. + s.Name = fmt.Sprintf("mockdb-%v", time.Now().UnixNano()) + m.sessions[s.Name] = true + return s, nil +} + +// GetSession is a placeholder for SpannerClient.GetSession. +func (m *MockCloudSpannerClient) GetSession(c context.Context, r *sppb.GetSessionRequest, opts ...grpc.CallOption) (*sppb.Session, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + if err := m.injErr["GetSession"]; err != nil { + return nil, err + } + m.pings = append(m.pings, r.Name) + if _, ok := m.sessions[r.Name]; !ok { + return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Session not found: %v", r.Name)) + } + return &sppb.Session{Name: r.Name}, nil +} + +// DeleteSession is a placeholder for SpannerClient.DeleteSession. +func (m *MockCloudSpannerClient) DeleteSession(c context.Context, r *sppb.DeleteSessionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + if err := m.injErr["DeleteSession"]; err != nil { + return nil, err + } + if _, ok := m.sessions[r.Name]; !ok { + // Session not found. + return &empty.Empty{}, status.Errorf(codes.NotFound, fmt.Sprintf("Session not found: %v", r.Name)) + } + // Delete session from in-memory table. + delete(m.sessions, r.Name) + return &empty.Empty{}, nil +} + +// ExecuteStreamingSql is a mock implementation of SpannerClient.ExecuteStreamingSql. +func (m *MockCloudSpannerClient) ExecuteStreamingSql(c context.Context, r *sppb.ExecuteSqlRequest, opts ...grpc.CallOption) (sppb.Spanner_ExecuteStreamingSqlClient, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + act, err := m.expectAction("ExecuteStreamingSql") + if err != nil { + return nil, err + } + wantReq := &sppb.ExecuteSqlRequest{ + Session: "mocksession", + Transaction: &sppb.TransactionSelector{ + Selector: &sppb.TransactionSelector_SingleUse{ + SingleUse: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadOnly_{ + ReadOnly: &sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_Strong{ + Strong: true, + }, + ReturnReadTimestamp: false, + }, + }, + }, + }, + }, + Sql: "mockquery", + Params: &proto3.Struct{ + Fields: map[string]*proto3.Value{"var1": &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: "abc"}}}, + }, + ParamTypes: map[string]*sppb.Type{"var1": &sppb.Type{Code: sppb.TypeCode_STRING}}, + } + if !proto.Equal(r, wantReq) { + return nil, fmt.Errorf("got query request: %v, want: %v", r, wantReq) + } + if act.Err != nil { + return nil, act.Err + } + return nil, errors.New("query never succeeds on mock client") +} + +// StreamingRead is a placeholder for SpannerClient.StreamingRead. +func (m *MockCloudSpannerClient) StreamingRead(c context.Context, r *sppb.ReadRequest, opts ...grpc.CallOption) (sppb.Spanner_StreamingReadClient, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + act, err := m.expectAction("StreamingRead", "StreamingReadIndex") + if err != nil { + return nil, err + } + wantReq := &sppb.ReadRequest{ + Session: "mocksession", + Transaction: &sppb.TransactionSelector{ + Selector: &sppb.TransactionSelector_SingleUse{ + SingleUse: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadOnly_{ + ReadOnly: &sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_Strong{ + Strong: true, + }, + ReturnReadTimestamp: false, + }, + }, + }, + }, + }, + Table: "t_mock", + Columns: []string{"col1", "col2"}, + KeySet: &sppb.KeySet{ + Keys: []*proto3.ListValue{ + &proto3.ListValue{ + Values: []*proto3.Value{ + &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + }, + }, + }, + Ranges: []*sppb.KeyRange{}, + All: false, + }, + } + if act.Method == "StreamingIndexRead" { + wantReq.Index = "idx1" + } + if !proto.Equal(r, wantReq) { + return nil, fmt.Errorf("got query request: %v, want: %v", r, wantReq) + } + if act.Err != nil { + return nil, act.Err + } + return nil, errors.New("read never succeeds on mock client") +} + +// BeginTransaction is a placeholder for SpannerClient.BeginTransaction. +func (m *MockCloudSpannerClient) BeginTransaction(c context.Context, r *sppb.BeginTransactionRequest, opts ...grpc.CallOption) (*sppb.Transaction, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + if !m.nice { + act, err := m.expectAction("BeginTransaction") + if err != nil { + return nil, err + } + if act.Err != nil { + return nil, act.Err + } + } + resp := &sppb.Transaction{Id: []byte("transaction-1")} + if _, ok := r.Options.Mode.(*sppb.TransactionOptions_ReadOnly_); ok { + resp.ReadTimestamp = &pbt.Timestamp{Seconds: 3, Nanos: 4} + } + return resp, nil +} + +// Commit is a placeholder for SpannerClient.Commit. +func (m *MockCloudSpannerClient) Commit(c context.Context, r *sppb.CommitRequest, opts ...grpc.CallOption) (*sppb.CommitResponse, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + if !m.nice { + act, err := m.expectAction("Commit") + if err != nil { + return nil, err + } + if act.Err != nil { + return nil, act.Err + } + } + return &sppb.CommitResponse{CommitTimestamp: &pbt.Timestamp{Seconds: 1, Nanos: 2}}, nil +} + +// Rollback is a placeholder for SpannerClient.Rollback. +func (m *MockCloudSpannerClient) Rollback(c context.Context, r *sppb.RollbackRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + m.ready() + m.mu.Lock() + defer m.mu.Unlock() + if !m.nice { + act, err := m.expectAction("Rollback") + if err != nil { + return nil, err + } + if act.Err != nil { + return nil, act.Err + } + } + return nil, nil +} + +// PartitionQuery is a placeholder for SpannerServer.PartitionQuery. +func (m *MockCloudSpannerClient) PartitionQuery(ctx context.Context, r *sppb.PartitionQueryRequest, opts ...grpc.CallOption) (*sppb.PartitionResponse, error) { + m.ready() + return nil, errors.New("Unimplemented") +} + +// PartitionRead is a placeholder for SpannerServer.PartitionRead. +func (m *MockCloudSpannerClient) PartitionRead(ctx context.Context, r *sppb.PartitionReadRequest, opts ...grpc.CallOption) (*sppb.PartitionResponse, error) { + m.ready() + return nil, errors.New("Unimplemented") +} + +func (m *MockCloudSpannerClient) expectAction(methods ...string) (Action, error) { + for _, me := range methods { + if err := m.injErr[me]; err != nil { + return Action{}, err + } + } + if len(m.actions) == 0 { + m.t.Fatalf("unexpected %v executed", methods) + } + act := m.actions[0] + m.actions = m.actions[1:] + for _, me := range methods { + if me == act.Method { + return act, nil + } + } + m.t.Fatalf("unexpected call of one of %v, want method %s", methods, act.Method) + return Action{}, nil +} + +// Freeze stalls all requests. +func (m *MockCloudSpannerClient) Freeze() { + m.mu.Lock() + defer m.mu.Unlock() + m.freezed = make(chan struct{}) +} + +// Unfreeze restores processing requests. +func (m *MockCloudSpannerClient) Unfreeze() { + m.mu.Lock() + defer m.mu.Unlock() + close(m.freezed) +} + +// CheckActionsConsumed checks that all actions have been consumed. +func (m *MockCloudSpannerClient) CheckActionsConsumed() { + if len(m.actions) != 0 { + m.t.Fatalf("unconsumed mock client actions: %v", m.actions) + } +} + +// ready checks conditions before executing requests +// TODO: add checks for injected errors, actions +func (m *MockCloudSpannerClient) ready() { + m.mu.Lock() + freezed := m.freezed + m.mu.Unlock() + // check if client should be freezed + <-freezed +} diff --git a/vendor/cloud.google.com/go/spanner/internal/testutil/mockserver.go b/vendor/cloud.google.com/go/spanner/internal/testutil/mockserver.go new file mode 100644 index 0000000000..b600bb6798 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/internal/testutil/mockserver.go @@ -0,0 +1,243 @@ +/* +Copyright 2017 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 testutil + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "sync" + "testing" + "time" + + "golang.org/x/net/context" + + "github.com/golang/protobuf/ptypes/empty" + proto3 "github.com/golang/protobuf/ptypes/struct" + pbt "github.com/golang/protobuf/ptypes/timestamp" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var ( + // KvMeta is the Metadata for mocked KV table. + KvMeta = sppb.ResultSetMetadata{ + RowType: &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + { + Name: "Key", + Type: &sppb.Type{Code: sppb.TypeCode_STRING}, + }, + { + Name: "Value", + Type: &sppb.Type{Code: sppb.TypeCode_STRING}, + }, + }, + }, + } +) + +// MockCtlMsg encapsulates PartialResultSet/error that might be sent to +// client +type MockCtlMsg struct { + // If ResumeToken == true, mock server will generate a row with + // resume token. + ResumeToken bool + // If Err != nil, mock server will return error in RPC response. + Err error +} + +// MockCloudSpanner is a mock implementation of SpannerServer interface. +// TODO: make MockCloudSpanner a full-fleged Cloud Spanner implementation. +type MockCloudSpanner struct { + sppb.SpannerServer + + s *grpc.Server + t *testing.T + addr string + msgs chan MockCtlMsg + readTs time.Time + next int + + mu sync.Mutex + nextSession int + sessions map[string]*sppb.Session +} + +// Addr returns the listening address of mock server. +func (m *MockCloudSpanner) Addr() string { + return m.addr +} + +// AddMsg generates a new mocked row which can be received by client. +func (m *MockCloudSpanner) AddMsg(err error, resumeToken bool) { + msg := MockCtlMsg{ + ResumeToken: resumeToken, + Err: err, + } + if err == io.EOF { + close(m.msgs) + } else { + m.msgs <- msg + } +} + +// Done signals an end to a mocked stream. +func (m *MockCloudSpanner) Done() { + close(m.msgs) +} + +// CreateSession is a placeholder for SpannerServer.CreateSession. +func (m *MockCloudSpanner) CreateSession(c context.Context, r *sppb.CreateSessionRequest) (*sppb.Session, error) { + m.mu.Lock() + defer m.mu.Unlock() + name := fmt.Sprintf("session-%d", m.nextSession) + m.nextSession++ + s := &sppb.Session{Name: name} + m.sessions[name] = s + return s, nil +} + +// GetSession is a placeholder for SpannerServer.GetSession. +func (m *MockCloudSpanner) GetSession(c context.Context, r *sppb.GetSessionRequest) (*sppb.Session, error) { + m.mu.Lock() + defer m.mu.Unlock() + if s, ok := m.sessions[r.Name]; ok { + return s, nil + } + return nil, status.Errorf(codes.NotFound, "not found") +} + +// DeleteSession is a placeholder for SpannerServer.DeleteSession. +func (m *MockCloudSpanner) DeleteSession(c context.Context, r *sppb.DeleteSessionRequest) (*empty.Empty, error) { + m.mu.Lock() + defer m.mu.Unlock() + delete(m.sessions, r.Name) + return &empty.Empty{}, nil +} + +// EncodeResumeToken return mock resume token encoding for an uint64 integer. +func EncodeResumeToken(t uint64) []byte { + rt := make([]byte, 16) + binary.PutUvarint(rt, t) + return rt +} + +// DecodeResumeToken decodes a mock resume token into an uint64 integer. +func DecodeResumeToken(t []byte) (uint64, error) { + s, n := binary.Uvarint(t) + if n <= 0 { + return 0, fmt.Errorf("invalid resume token: %v", t) + } + return s, nil +} + +// ExecuteStreamingSql is a mock implementation of SpannerServer.ExecuteStreamingSql. +func (m *MockCloudSpanner) ExecuteStreamingSql(r *sppb.ExecuteSqlRequest, s sppb.Spanner_ExecuteStreamingSqlServer) error { + switch r.Sql { + case "SELECT * from t_unavailable": + return status.Errorf(codes.Unavailable, "mock table unavailable") + case "SELECT t.key key, t.value value FROM t_mock t": + if r.ResumeToken != nil { + s, err := DecodeResumeToken(r.ResumeToken) + if err != nil { + return err + } + m.next = int(s) + 1 + } + for { + msg, more := <-m.msgs + if !more { + break + } + if msg.Err == nil { + var rt []byte + if msg.ResumeToken { + rt = EncodeResumeToken(uint64(m.next)) + } + meta := KvMeta + meta.Transaction = &sppb.Transaction{ + ReadTimestamp: &pbt.Timestamp{ + Seconds: m.readTs.Unix(), + Nanos: int32(m.readTs.Nanosecond()), + }, + } + err := s.Send(&sppb.PartialResultSet{ + Metadata: &meta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: fmt.Sprintf("foo-%02d", m.next)}}, + {Kind: &proto3.Value_StringValue{StringValue: fmt.Sprintf("bar-%02d", m.next)}}, + }, + ResumeToken: rt, + }) + m.next = m.next + 1 + if err != nil { + return err + } + continue + } + return msg.Err + } + return nil + default: + return fmt.Errorf("unsupported SQL: %v", r.Sql) + } +} + +// StreamingRead is a placeholder for SpannerServer.StreamingRead. +func (m *MockCloudSpanner) StreamingRead(r *sppb.ReadRequest, s sppb.Spanner_StreamingReadServer) error { + return s.Send(&sppb.PartialResultSet{}) +} + +// Serve runs a MockCloudSpanner listening on a random localhost address. +func (m *MockCloudSpanner) Serve() { + m.s = grpc.NewServer() + if m.addr == "" { + m.addr = "localhost:0" + } + lis, err := net.Listen("tcp", m.addr) + if err != nil { + m.t.Fatalf("Failed to listen: %v", err) + } + _, port, err := net.SplitHostPort(lis.Addr().String()) + if err != nil { + m.t.Fatalf("Failed to parse listener address: %v", err) + } + sppb.RegisterSpannerServer(m.s, m) + m.addr = "localhost:" + port + go m.s.Serve(lis) +} + +// Stop terminates MockCloudSpanner and closes the serving port. +func (m *MockCloudSpanner) Stop() { + m.s.Stop() +} + +// NewMockCloudSpanner creates a new MockCloudSpanner instance. +func NewMockCloudSpanner(t *testing.T, ts time.Time) *MockCloudSpanner { + mcs := &MockCloudSpanner{ + t: t, + msgs: make(chan MockCtlMsg, 1000), + readTs: ts, + sessions: map[string]*sppb.Session{}, + } + return mcs +} diff --git a/vendor/cloud.google.com/go/spanner/key.go b/vendor/cloud.google.com/go/spanner/key.go new file mode 100644 index 0000000000..3bb4e9c15a --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/key.go @@ -0,0 +1,398 @@ +/* +Copyright 2017 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 spanner + +import ( + "bytes" + "fmt" + "time" + + "google.golang.org/grpc/codes" + + "cloud.google.com/go/civil" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// A Key can be either a Cloud Spanner row's primary key or a secondary index key. +// It is essentially an interface{} array, which represents a set of Cloud Spanner +// columns. A Key can be used as: +// +// - A primary key which uniquely identifies a Cloud Spanner row. +// - A secondary index key which maps to a set of Cloud Spanner rows indexed under it. +// - An endpoint of primary key/secondary index ranges; see the KeyRange type. +// +// Rows that are identified by the Key type are outputs of read operation or targets of +// delete operation in a mutation. Note that for Insert/Update/InsertOrUpdate/Update +// mutation types, although they don't require a primary key explicitly, the column list +// provided must contain enough columns that can comprise a primary key. +// +// Keys are easy to construct. For example, suppose you have a table with a +// primary key of username and product ID. To make a key for this table: +// +// key := spanner.Key{"john", 16} +// +// See the description of Row and Mutation types for how Go types are +// mapped to Cloud Spanner types. For convenience, Key type supports a wide range +// of Go types: +// - int, int8, int16, int32, int64, and NullInt64 are mapped to Cloud Spanner's INT64 type. +// - uint8, uint16 and uint32 are also mapped to Cloud Spanner's INT64 type. +// - float32, float64, NullFloat64 are mapped to Cloud Spanner's FLOAT64 type. +// - bool and NullBool are mapped to Cloud Spanner's BOOL type. +// - []byte is mapped to Cloud Spanner's BYTES type. +// - string and NullString are mapped to Cloud Spanner's STRING type. +// - time.Time and NullTime are mapped to Cloud Spanner's TIMESTAMP type. +// - civil.Date and NullDate are mapped to Cloud Spanner's DATE type. +type Key []interface{} + +// errInvdKeyPartType returns error for unsupported key part type. +func errInvdKeyPartType(part interface{}) error { + return spannerErrorf(codes.InvalidArgument, "key part has unsupported type %T", part) +} + +// keyPartValue converts a part of the Key (which is a valid Cloud Spanner type) +// into a proto3.Value. Used for encoding Key type into protobuf. +func keyPartValue(part interface{}) (pb *proto3.Value, err error) { + switch v := part.(type) { + case int: + pb, _, err = encodeValue(int64(v)) + case int8: + pb, _, err = encodeValue(int64(v)) + case int16: + pb, _, err = encodeValue(int64(v)) + case int32: + pb, _, err = encodeValue(int64(v)) + case uint8: + pb, _, err = encodeValue(int64(v)) + case uint16: + pb, _, err = encodeValue(int64(v)) + case uint32: + pb, _, err = encodeValue(int64(v)) + case float32: + pb, _, err = encodeValue(float64(v)) + case int64, float64, NullInt64, NullFloat64, bool, NullBool, []byte, string, NullString, time.Time, civil.Date, NullTime, NullDate: + pb, _, err = encodeValue(v) + default: + return nil, errInvdKeyPartType(v) + } + return pb, err +} + +// proto converts a spanner.Key into a proto3.ListValue. +func (key Key) proto() (*proto3.ListValue, error) { + lv := &proto3.ListValue{} + lv.Values = make([]*proto3.Value, 0, len(key)) + for _, part := range key { + v, err := keyPartValue(part) + if err != nil { + return nil, err + } + lv.Values = append(lv.Values, v) + } + return lv, nil +} + +// keySetProto lets a single Key act as a KeySet. +func (key Key) keySetProto() (*sppb.KeySet, error) { + kp, err := key.proto() + if err != nil { + return nil, err + } + return &sppb.KeySet{Keys: []*proto3.ListValue{kp}}, nil +} + +// String implements fmt.Stringer for Key. For string, []byte and NullString, it +// prints the uninterpreted bytes of their contents, leaving caller with the +// opportunity to escape the output. +func (key Key) String() string { + b := &bytes.Buffer{} + fmt.Fprint(b, "(") + for i, part := range []interface{}(key) { + if i != 0 { + fmt.Fprint(b, ",") + } + switch v := part.(type) { + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, float32, float64, bool: + // Use %v to print numeric types and bool. + fmt.Fprintf(b, "%v", v) + case string: + fmt.Fprintf(b, "%q", v) + case []byte: + if v != nil { + fmt.Fprintf(b, "%q", v) + } else { + fmt.Fprint(b, "") + } + case NullInt64, NullFloat64, NullBool, NullString, NullTime, NullDate: + // The above types implement fmt.Stringer. + fmt.Fprintf(b, "%s", v) + case civil.Date: + fmt.Fprintf(b, "%q", v) + case time.Time: + fmt.Fprintf(b, "%q", v.Format(time.RFC3339Nano)) + default: + fmt.Fprintf(b, "%v", v) + } + } + fmt.Fprint(b, ")") + return b.String() +} + +// AsPrefix returns a KeyRange for all keys where k is the prefix. +func (key Key) AsPrefix() KeyRange { + return KeyRange{ + Start: key, + End: key, + Kind: ClosedClosed, + } +} + +// KeyRangeKind describes the kind of interval represented by a KeyRange: +// whether it is open or closed on the left and right. +type KeyRangeKind int + +const ( + // ClosedOpen is closed on the left and open on the right: the Start + // key is included, the End key is excluded. + ClosedOpen KeyRangeKind = iota + + // ClosedClosed is closed on the left and the right: both keys are included. + ClosedClosed + + // OpenClosed is open on the left and closed on the right: the Start + // key is excluded, the End key is included. + OpenClosed + + // OpenOpen is open on the left and the right: neither key is included. + OpenOpen +) + +// A KeyRange represents a range of rows in a table or index. +// +// A range has a Start key and an End key. IncludeStart and IncludeEnd +// indicate whether the Start and End keys are included in the range. +// +// For example, consider the following table definition: +// +// CREATE TABLE UserEvents ( +// UserName STRING(MAX), +// EventDate STRING(10), +// ) PRIMARY KEY(UserName, EventDate); +// +// The following keys name rows in this table: +// +// spanner.Key{"Bob", "2014-09-23"} +// spanner.Key{"Alfred", "2015-06-12"} +// +// Since the UserEvents table's PRIMARY KEY clause names two columns, each +// UserEvents key has two elements; the first is the UserName, and the second +// is the EventDate. +// +// Key ranges with multiple components are interpreted lexicographically by +// component using the table or index key's declared sort order. For example, +// the following range returns all events for user "Bob" that occurred in the +// year 2015: +// +// spanner.KeyRange{ +// Start: spanner.Key{"Bob", "2015-01-01"}, +// End: spanner.Key{"Bob", "2015-12-31"}, +// Kind: ClosedClosed, +// } +// +// Start and end keys can omit trailing key components. This affects the +// inclusion and exclusion of rows that exactly match the provided key +// components: if IncludeStart is true, then rows that exactly match the +// provided components of the Start key are included; if IncludeStart is false +// then rows that exactly match are not included. IncludeEnd and End key +// behave in the same fashion. +// +// For example, the following range includes all events for "Bob" that occurred +// during and after the year 2000: +// +// spanner.KeyRange{ +// Start: spanner.Key{"Bob", "2000-01-01"}, +// End: spanner.Key{"Bob"}, +// Kind: ClosedClosed, +// } +// +// The next example retrieves all events for "Bob": +// +// spanner.Key{"Bob"}.AsPrefix() +// +// To retrieve events before the year 2000: +// +// spanner.KeyRange{ +// Start: spanner.Key{"Bob"}, +// End: spanner.Key{"Bob", "2000-01-01"}, +// Kind: ClosedOpen, +// } +// +// Although we specified a Kind for this KeyRange, we didn't need to, because +// the default is ClosedOpen. In later examples we'll omit Kind if it is +// ClosedOpen. +// +// The following range includes all rows in a table or under a +// index: +// +// spanner.AllKeys() +// +// This range returns all users whose UserName begins with any +// character from A to C: +// +// spanner.KeyRange{ +// Start: spanner.Key{"A"}, +// End: spanner.Key{"D"}, +// } +// +// This range returns all users whose UserName begins with B: +// +// spanner.KeyRange{ +// Start: spanner.Key{"B"}, +// End: spanner.Key{"C"}, +// } +// +// Key ranges honor column sort order. For example, suppose a table is defined +// as follows: +// +// CREATE TABLE DescendingSortedTable { +// Key INT64, +// ... +// ) PRIMARY KEY(Key DESC); +// +// The following range retrieves all rows with key values between 1 and 100 +// inclusive: +// +// spanner.KeyRange{ +// Start: spanner.Key{100}, +// End: spanner.Key{1}, +// Kind: ClosedClosed, +// } +// +// Note that 100 is passed as the start, and 1 is passed as the end, because +// Key is a descending column in the schema. +type KeyRange struct { + // Start specifies the left boundary of the key range; End specifies + // the right boundary of the key range. + Start, End Key + + // Kind describes whether the boundaries of the key range include + // their keys. + Kind KeyRangeKind +} + +// String implements fmt.Stringer for KeyRange type. +func (r KeyRange) String() string { + var left, right string + switch r.Kind { + case ClosedClosed: + left, right = "[", "]" + case ClosedOpen: + left, right = "[", ")" + case OpenClosed: + left, right = "(", "]" + case OpenOpen: + left, right = "(", ")" + default: + left, right = "?", "?" + } + return fmt.Sprintf("%s%s,%s%s", left, r.Start, r.End, right) +} + +// proto converts KeyRange into sppb.KeyRange. +func (r KeyRange) proto() (*sppb.KeyRange, error) { + var err error + var start, end *proto3.ListValue + pb := &sppb.KeyRange{} + if start, err = r.Start.proto(); err != nil { + return nil, err + } + if end, err = r.End.proto(); err != nil { + return nil, err + } + if r.Kind == ClosedClosed || r.Kind == ClosedOpen { + pb.StartKeyType = &sppb.KeyRange_StartClosed{StartClosed: start} + } else { + pb.StartKeyType = &sppb.KeyRange_StartOpen{StartOpen: start} + } + if r.Kind == ClosedClosed || r.Kind == OpenClosed { + pb.EndKeyType = &sppb.KeyRange_EndClosed{EndClosed: end} + } else { + pb.EndKeyType = &sppb.KeyRange_EndOpen{EndOpen: end} + } + return pb, nil +} + +// keySetProto lets a KeyRange act as a KeySet. +func (r KeyRange) keySetProto() (*sppb.KeySet, error) { + rp, err := r.proto() + if err != nil { + return nil, err + } + return &sppb.KeySet{Ranges: []*sppb.KeyRange{rp}}, nil +} + +// A KeySet defines a collection of Cloud Spanner keys and/or key ranges. All the +// keys are expected to be in the same table or index. The keys need not be sorted in +// any particular way. +// +// An individual Key can act as a KeySet, as can a KeyRange. Use the KeySets function +// to create a KeySet consisting of multiple Keys and KeyRanges. To obtain an empty +// KeySet, call KeySets with no arguments. +// +// If the same key is specified multiple times in the set (for example if two +// ranges, two keys, or a key and a range overlap), the Cloud Spanner backend behaves +// as if the key were only specified once. +type KeySet interface { + keySetProto() (*sppb.KeySet, error) +} + +// AllKeys returns a KeySet that represents all Keys of a table or a index. +func AllKeys() KeySet { + return all{} +} + +type all struct{} + +func (all) keySetProto() (*sppb.KeySet, error) { + return &sppb.KeySet{All: true}, nil +} + +// KeySets returns the union of the KeySets. If any of the KeySets is AllKeys, then +// the resulting KeySet will be equivalent to AllKeys. +func KeySets(keySets ...KeySet) KeySet { + u := make(union, len(keySets)) + copy(u, keySets) + return u +} + +type union []KeySet + +func (u union) keySetProto() (*sppb.KeySet, error) { + upb := &sppb.KeySet{} + for _, ks := range u { + pb, err := ks.keySetProto() + if err != nil { + return nil, err + } + if pb.All { + return pb, nil + } + upb.Keys = append(upb.Keys, pb.Keys...) + upb.Ranges = append(upb.Ranges, pb.Ranges...) + } + return upb, nil +} diff --git a/vendor/cloud.google.com/go/spanner/key_test.go b/vendor/cloud.google.com/go/spanner/key_test.go new file mode 100644 index 0000000000..32b67b454d --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/key_test.go @@ -0,0 +1,372 @@ +/* +Copyright 2017 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 spanner + +import ( + "testing" + "time" + + "cloud.google.com/go/civil" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// Test Key.String() and Key.proto(). +func TestKey(t *testing.T) { + tm, _ := time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z") + dt, _ := civil.ParseDate("2016-11-15") + for _, test := range []struct { + k Key + wantProto *proto3.ListValue + wantStr string + }{ + { + k: Key{int(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{int8(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{int16(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{int32(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{int64(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{uint8(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{uint16(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{uint32(1)}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{true}, + wantProto: listValueProto(boolProto(true)), + wantStr: "(true)", + }, + { + k: Key{float32(1.5)}, + wantProto: listValueProto(floatProto(1.5)), + wantStr: "(1.5)", + }, + { + k: Key{float64(1.5)}, + wantProto: listValueProto(floatProto(1.5)), + wantStr: "(1.5)", + }, + { + k: Key{"value"}, + wantProto: listValueProto(stringProto("value")), + wantStr: `("value")`, + }, + { + k: Key{[]byte(nil)}, + wantProto: listValueProto(nullProto()), + wantStr: "()", + }, + { + k: Key{[]byte{}}, + wantProto: listValueProto(stringProto("")), + wantStr: `("")`, + }, + { + k: Key{tm}, + wantProto: listValueProto(stringProto("2016-11-15T15:04:05.999999999Z")), + wantStr: `("2016-11-15T15:04:05.999999999Z")`, + }, + {k: Key{dt}, + wantProto: listValueProto(stringProto("2016-11-15")), + wantStr: `("2016-11-15")`, + }, + { + k: Key{[]byte("value")}, + wantProto: listValueProto(bytesProto([]byte("value"))), + wantStr: `("value")`, + }, + { + k: Key{NullInt64{1, true}}, + wantProto: listValueProto(stringProto("1")), + wantStr: "(1)", + }, + { + k: Key{NullInt64{2, false}}, + wantProto: listValueProto(nullProto()), + wantStr: "()", + }, + { + k: Key{NullFloat64{1.5, true}}, + wantProto: listValueProto(floatProto(1.5)), + wantStr: "(1.5)", + }, + { + k: Key{NullFloat64{2.0, false}}, + wantProto: listValueProto(nullProto()), + wantStr: "()", + }, + { + k: Key{NullBool{true, true}}, + wantProto: listValueProto(boolProto(true)), + wantStr: "(true)", + }, + { + k: Key{NullBool{true, false}}, + wantProto: listValueProto(nullProto()), + wantStr: "()", + }, + { + k: Key{NullString{"value", true}}, + wantProto: listValueProto(stringProto("value")), + wantStr: `("value")`, + }, + { + k: Key{NullString{"value", false}}, + wantProto: listValueProto(nullProto()), + wantStr: "()", + }, + { + k: Key{NullTime{tm, true}}, + wantProto: listValueProto(timeProto(tm)), + wantStr: `("2016-11-15T15:04:05.999999999Z")`, + }, + + { + k: Key{NullTime{time.Now(), false}}, + wantProto: listValueProto(nullProto()), + wantStr: "()", + }, + { + k: Key{NullDate{dt, true}}, + wantProto: listValueProto(dateProto(dt)), + wantStr: `("2016-11-15")`, + }, + { + k: Key{NullDate{civil.Date{}, false}}, + wantProto: listValueProto(nullProto()), + wantStr: "()", + }, + { + k: Key{int(1), NullString{"value", false}, "value", 1.5, true}, + wantProto: listValueProto(stringProto("1"), nullProto(), stringProto("value"), floatProto(1.5), boolProto(true)), + wantStr: `(1,,"value",1.5,true)`, + }, + } { + if got := test.k.String(); got != test.wantStr { + t.Errorf("%v.String() = %v, want %v", test.k, got, test.wantStr) + } + gotProto, err := test.k.proto() + if err != nil { + t.Errorf("%v.proto() returns error %v; want nil error", test.k, err) + } + if !testEqual(gotProto, test.wantProto) { + t.Errorf("%v.proto() = \n%v\nwant:\n%v", test.k, gotProto, test.wantProto) + } + } +} + +// Test KeyRange.String() and KeyRange.proto(). +func TestKeyRange(t *testing.T) { + for _, test := range []struct { + kr KeyRange + wantProto *sppb.KeyRange + wantStr string + }{ + { + kr: KeyRange{Key{"A"}, Key{"D"}, OpenOpen}, + wantProto: &sppb.KeyRange{ + StartKeyType: &sppb.KeyRange_StartOpen{StartOpen: listValueProto(stringProto("A"))}, + EndKeyType: &sppb.KeyRange_EndOpen{EndOpen: listValueProto(stringProto("D"))}, + }, + wantStr: `(("A"),("D"))`, + }, + { + kr: KeyRange{Key{1}, Key{10}, OpenClosed}, + wantProto: &sppb.KeyRange{ + StartKeyType: &sppb.KeyRange_StartOpen{StartOpen: listValueProto(stringProto("1"))}, + EndKeyType: &sppb.KeyRange_EndClosed{EndClosed: listValueProto(stringProto("10"))}, + }, + wantStr: "((1),(10)]", + }, + { + kr: KeyRange{Key{1.5, 2.1, 0.2}, Key{1.9, 0.7}, ClosedOpen}, + wantProto: &sppb.KeyRange{ + StartKeyType: &sppb.KeyRange_StartClosed{StartClosed: listValueProto(floatProto(1.5), floatProto(2.1), floatProto(0.2))}, + EndKeyType: &sppb.KeyRange_EndOpen{EndOpen: listValueProto(floatProto(1.9), floatProto(0.7))}, + }, + wantStr: "[(1.5,2.1,0.2),(1.9,0.7))", + }, + { + kr: KeyRange{Key{NullInt64{1, true}}, Key{10}, ClosedClosed}, + wantProto: &sppb.KeyRange{ + StartKeyType: &sppb.KeyRange_StartClosed{StartClosed: listValueProto(stringProto("1"))}, + EndKeyType: &sppb.KeyRange_EndClosed{EndClosed: listValueProto(stringProto("10"))}, + }, + wantStr: "[(1),(10)]", + }, + } { + if got := test.kr.String(); got != test.wantStr { + t.Errorf("%v.String() = %v, want %v", test.kr, got, test.wantStr) + } + gotProto, err := test.kr.proto() + if err != nil { + t.Errorf("%v.proto() returns error %v; want nil error", test.kr, err) + } + if !testEqual(gotProto, test.wantProto) { + t.Errorf("%v.proto() = \n%v\nwant:\n%v", test.kr, gotProto.String(), test.wantProto.String()) + } + } +} + +func TestPrefixRange(t *testing.T) { + got := Key{1}.AsPrefix() + want := KeyRange{Start: Key{1}, End: Key{1}, Kind: ClosedClosed} + if !testEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestKeySets(t *testing.T) { + int1 := intProto(1) + int2 := intProto(2) + int3 := intProto(3) + int4 := intProto(4) + for i, test := range []struct { + ks KeySet + wantProto *sppb.KeySet + }{ + { + KeySets(), + &sppb.KeySet{}, + }, + { + Key{4}, + &sppb.KeySet{ + Keys: []*proto3.ListValue{listValueProto(int4)}, + }, + }, + { + AllKeys(), + &sppb.KeySet{All: true}, + }, + { + KeySets(Key{1, 2}, Key{3, 4}), + &sppb.KeySet{ + Keys: []*proto3.ListValue{ + listValueProto(int1, int2), + listValueProto(int3, int4), + }, + }, + }, + { + KeyRange{Key{1}, Key{2}, ClosedOpen}, + &sppb.KeySet{Ranges: []*sppb.KeyRange{ + { + StartKeyType: &sppb.KeyRange_StartClosed{StartClosed: listValueProto(int1)}, + EndKeyType: &sppb.KeyRange_EndOpen{EndOpen: listValueProto(int2)}, + }, + }}, + }, + { + Key{2}.AsPrefix(), + &sppb.KeySet{Ranges: []*sppb.KeyRange{ + { + StartKeyType: &sppb.KeyRange_StartClosed{StartClosed: listValueProto(int2)}, + EndKeyType: &sppb.KeyRange_EndClosed{EndClosed: listValueProto(int2)}, + }, + }}, + }, + { + KeySets( + KeyRange{Key{1}, Key{2}, ClosedClosed}, + KeyRange{Key{3}, Key{4}, OpenClosed}, + ), + &sppb.KeySet{ + Ranges: []*sppb.KeyRange{ + { + StartKeyType: &sppb.KeyRange_StartClosed{StartClosed: listValueProto(int1)}, + EndKeyType: &sppb.KeyRange_EndClosed{EndClosed: listValueProto(int2)}, + }, + { + StartKeyType: &sppb.KeyRange_StartOpen{StartOpen: listValueProto(int3)}, + EndKeyType: &sppb.KeyRange_EndClosed{EndClosed: listValueProto(int4)}, + }, + }, + }, + }, + { + KeySets( + Key{1}, + KeyRange{Key{2}, Key{3}, ClosedClosed}, + KeyRange{Key{4}, Key{5}, OpenClosed}, + KeySets(), + Key{6}), + &sppb.KeySet{ + Keys: []*proto3.ListValue{ + listValueProto(int1), + listValueProto(intProto(6)), + }, + Ranges: []*sppb.KeyRange{ + { + StartKeyType: &sppb.KeyRange_StartClosed{StartClosed: listValueProto(int2)}, + EndKeyType: &sppb.KeyRange_EndClosed{EndClosed: listValueProto(int3)}, + }, + { + StartKeyType: &sppb.KeyRange_StartOpen{StartOpen: listValueProto(int4)}, + EndKeyType: &sppb.KeyRange_EndClosed{EndClosed: listValueProto(intProto(5))}, + }, + }, + }, + }, + { + KeySets( + Key{1}, + KeyRange{Key{2}, Key{3}, ClosedClosed}, + AllKeys(), + KeyRange{Key{4}, Key{5}, OpenClosed}, + Key{6}), + &sppb.KeySet{All: true}, + }, + } { + gotProto, err := test.ks.keySetProto() + if err != nil { + t.Errorf("#%d: %v.proto() returns error %v; want nil error", i, test.ks, err) + } + if !testEqual(gotProto, test.wantProto) { + t.Errorf("#%d: %v.proto() = \n%v\nwant:\n%v", i, test.ks, gotProto.String(), test.wantProto.String()) + } + } +} diff --git a/vendor/cloud.google.com/go/spanner/mutation.go b/vendor/cloud.google.com/go/spanner/mutation.go new file mode 100644 index 0000000000..3848049d7f --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/mutation.go @@ -0,0 +1,431 @@ +/* +Copyright 2017 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 spanner + +import ( + "reflect" + + proto3 "github.com/golang/protobuf/ptypes/struct" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" +) + +// op is the mutation operation. +type op int + +const ( + // opDelete removes a row from a table. Succeeds whether or not the + // key was present. + opDelete op = iota + // opInsert inserts a row into a table. If the row already exists, the + // write or transaction fails. + opInsert + // opInsertOrUpdate inserts a row into a table. If the row already + // exists, it updates it instead. Any column values not explicitly + // written are preserved. + opInsertOrUpdate + // opReplace inserts a row into a table, deleting any existing row. + // Unlike InsertOrUpdate, this means any values not explicitly written + // become NULL. + opReplace + // opUpdate updates a row in a table. If the row does not already + // exist, the write or transaction fails. + opUpdate +) + +// A Mutation describes a modification to one or more Cloud Spanner rows. The +// mutation represents an insert, update, delete, etc on a table. +// +// Many mutations can be applied in a single atomic commit. For purposes of +// constraint checking (such as foreign key constraints), the operations can be +// viewed as applying in the same order as the mutations are provided (so that, e.g., +// a row and its logical "child" can be inserted in the same commit). +// +// The Apply function applies series of mutations. For example, +// +// m := spanner.Insert("User", +// []string{"user_id", "profile"}, +// []interface{}{UserID, profile}) +// _, err := client.Apply(ctx, []*spanner.Mutation{m}) +// +// inserts a new row into the User table. The primary key +// for the new row is UserID (presuming that "user_id" has been declared as the +// primary key of the "User" table). +// +// To apply a series of mutations as part of an atomic read-modify-write operation, +// use ReadWriteTransaction. +// +// Updating a row +// +// Changing the values of columns in an existing row is very similar to +// inserting a new row: +// +// m := spanner.Update("User", +// []string{"user_id", "profile"}, +// []interface{}{UserID, profile}) +// _, err := client.Apply(ctx, []*spanner.Mutation{m}) +// +// Deleting a row +// +// To delete a row, use spanner.Delete: +// +// m := spanner.Delete("User", spanner.Key{UserId}) +// _, err := client.Apply(ctx, []*spanner.Mutation{m}) +// +// spanner.Delete accepts a KeySet, so you can also pass in a KeyRange, or use the +// spanner.KeySets function to build any combination of Keys and KeyRanges. +// +// Note that deleting a row in a table may also delete rows from other tables +// if cascading deletes are specified in those tables' schemas. Delete does +// nothing if the named row does not exist (does not yield an error). +// +// Deleting a field +// +// To delete/clear a field within a row, use spanner.Update with the value nil: +// +// m := spanner.Update("User", +// []string{"user_id", "profile"}, +// []interface{}{UserID, nil}) +// _, err := client.Apply(ctx, []*spanner.Mutation{m}) +// +// The valid Go types and their corresponding Cloud Spanner types that can be +// used in the Insert/Update/InsertOrUpdate functions are: +// +// string, NullString - STRING +// []string, []NullString - STRING ARRAY +// []byte - BYTES +// [][]byte - BYTES ARRAY +// int, int64, NullInt64 - INT64 +// []int, []int64, []NullInt64 - INT64 ARRAY +// bool, NullBool - BOOL +// []bool, []NullBool - BOOL ARRAY +// float64, NullFloat64 - FLOAT64 +// []float64, []NullFloat64 - FLOAT64 ARRAY +// time.Time, NullTime - TIMESTAMP +// []time.Time, []NullTime - TIMESTAMP ARRAY +// Date, NullDate - DATE +// []Date, []NullDate - DATE ARRAY +// +// To compare two Mutations for testing purposes, use reflect.DeepEqual. +type Mutation struct { + // op is the operation type of the mutation. + // See documentation for spanner.op for more details. + op op + // Table is the name of the target table to be modified. + table string + // keySet is a set of primary keys that names the rows + // in a delete operation. + keySet KeySet + // columns names the set of columns that are going to be + // modified by Insert, InsertOrUpdate, Replace or Update + // operations. + columns []string + // values specifies the new values for the target columns + // named by Columns. + values []interface{} +} + +// mapToMutationParams converts Go map into mutation parameters. +func mapToMutationParams(in map[string]interface{}) ([]string, []interface{}) { + cols := []string{} + vals := []interface{}{} + for k, v := range in { + cols = append(cols, k) + vals = append(vals, v) + } + return cols, vals +} + +// errNotStruct returns error for not getting a go struct type. +func errNotStruct(in interface{}) error { + return spannerErrorf(codes.InvalidArgument, "%T is not a go struct type", in) +} + +// structToMutationParams converts Go struct into mutation parameters. +// If the input is not a valid Go struct type, structToMutationParams +// returns error. +func structToMutationParams(in interface{}) ([]string, []interface{}, error) { + if in == nil { + return nil, nil, errNotStruct(in) + } + v := reflect.ValueOf(in) + t := v.Type() + if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct { + // t is a pointer to a struct. + if v.IsNil() { + // Return empty results. + return nil, nil, nil + } + // Get the struct value that in points to. + v = v.Elem() + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, nil, errNotStruct(in) + } + fields, err := fieldCache.Fields(t) + if err != nil { + return nil, nil, toSpannerError(err) + } + var cols []string + var vals []interface{} + for _, f := range fields { + cols = append(cols, f.Name) + vals = append(vals, v.FieldByIndex(f.Index).Interface()) + } + return cols, vals, nil +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func Insert(table string, cols []string, vals []interface{}) *Mutation { + return &Mutation{ + op: opInsert, + table: table, + columns: cols, + values: vals, + } +} + +// InsertMap returns a Mutation to insert a row into a table, specified by +// a map of column name to value. If the row already exists, the write or +// transaction fails. +func InsertMap(table string, in map[string]interface{}) *Mutation { + cols, vals := mapToMutationParams(in) + return Insert(table, cols, vals) +} + +// InsertStruct returns a Mutation to insert a row into a table, specified by +// a Go struct. If the row already exists, the write or transaction fails. +// +// The in argument must be a struct or a pointer to a struct. Its exported +// fields specify the column names and values. Use a field tag like "spanner:name" +// to provide an alternative column name, or use "spanner:-" to ignore the field. +func InsertStruct(table string, in interface{}) (*Mutation, error) { + cols, vals, err := structToMutationParams(in) + if err != nil { + return nil, err + } + return Insert(table, cols, vals), nil +} + +// Update returns a Mutation to update a row in a table. If the row does not +// already exist, the write or transaction fails. +func Update(table string, cols []string, vals []interface{}) *Mutation { + return &Mutation{ + op: opUpdate, + table: table, + columns: cols, + values: vals, + } +} + +// UpdateMap returns a Mutation to update a row in a table, specified by +// a map of column to value. If the row does not already exist, the write or +// transaction fails. +func UpdateMap(table string, in map[string]interface{}) *Mutation { + cols, vals := mapToMutationParams(in) + return Update(table, cols, vals) +} + +// UpdateStruct returns a Mutation to update a row in a table, specified by a Go +// struct. If the row does not already exist, the write or transaction fails. +func UpdateStruct(table string, in interface{}) (*Mutation, error) { + cols, vals, err := structToMutationParams(in) + if err != nil { + return nil, err + } + return Update(table, cols, vals), nil +} + +// InsertOrUpdate returns a Mutation to insert a row into a table. If the row +// already exists, it updates it instead. Any column values not explicitly +// written are preserved. +// +// For a similar example, See Update. +func InsertOrUpdate(table string, cols []string, vals []interface{}) *Mutation { + return &Mutation{ + op: opInsertOrUpdate, + table: table, + columns: cols, + values: vals, + } +} + +// InsertOrUpdateMap returns a Mutation to insert a row into a table, +// specified by a map of column to value. If the row already exists, it +// updates it instead. Any column values not explicitly written are preserved. +// +// For a similar example, See UpdateMap. +func InsertOrUpdateMap(table string, in map[string]interface{}) *Mutation { + cols, vals := mapToMutationParams(in) + return InsertOrUpdate(table, cols, vals) +} + +// InsertOrUpdateStruct returns a Mutation to insert a row into a table, +// specified by a Go struct. If the row already exists, it updates it instead. +// Any column values not explicitly written are preserved. +// +// The in argument must be a struct or a pointer to a struct. Its exported +// fields specify the column names and values. Use a field tag like "spanner:name" +// to provide an alternative column name, or use "spanner:-" to ignore the field. +// +// For a similar example, See UpdateStruct. +func InsertOrUpdateStruct(table string, in interface{}) (*Mutation, error) { + cols, vals, err := structToMutationParams(in) + if err != nil { + return nil, err + } + return InsertOrUpdate(table, cols, vals), nil +} + +// Replace returns a Mutation to insert a row into a table, deleting any +// existing row. Unlike InsertOrUpdate, this means any values not explicitly +// written become NULL. +// +// For a similar example, See Update. +func Replace(table string, cols []string, vals []interface{}) *Mutation { + return &Mutation{ + op: opReplace, + table: table, + columns: cols, + values: vals, + } +} + +// ReplaceMap returns a Mutation to insert a row into a table, deleting any +// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly +// written become NULL. The row is specified by a map of column to value. +// +// For a similar example, See UpdateMap. +func ReplaceMap(table string, in map[string]interface{}) *Mutation { + cols, vals := mapToMutationParams(in) + return Replace(table, cols, vals) +} + +// ReplaceStruct returns a Mutation to insert a row into a table, deleting any +// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly +// written become NULL. The row is specified by a Go struct. +// +// The in argument must be a struct or a pointer to a struct. Its exported +// fields specify the column names and values. Use a field tag like "spanner:name" +// to provide an alternative column name, or use "spanner:-" to ignore the field. +// +// For a similar example, See UpdateStruct. +func ReplaceStruct(table string, in interface{}) (*Mutation, error) { + cols, vals, err := structToMutationParams(in) + if err != nil { + return nil, err + } + return Replace(table, cols, vals), nil +} + +// Delete removes the rows described by the KeySet from the table. It succeeds +// whether or not the keys were present. +func Delete(table string, ks KeySet) *Mutation { + return &Mutation{ + op: opDelete, + table: table, + keySet: ks, + } +} + +// prepareWrite generates sppb.Mutation_Write from table name, column names +// and new column values. +func prepareWrite(table string, columns []string, vals []interface{}) (*sppb.Mutation_Write, error) { + v, err := encodeValueArray(vals) + if err != nil { + return nil, err + } + return &sppb.Mutation_Write{ + Table: table, + Columns: columns, + Values: []*proto3.ListValue{v}, + }, nil +} + +// errInvdMutationOp returns error for unrecognized mutation operation. +func errInvdMutationOp(m Mutation) error { + return spannerErrorf(codes.InvalidArgument, "Unknown op type: %d", m.op) +} + +// proto converts spanner.Mutation to sppb.Mutation, in preparation to send +// RPCs. +func (m Mutation) proto() (*sppb.Mutation, error) { + var pb *sppb.Mutation + switch m.op { + case opDelete: + var kp *sppb.KeySet + if m.keySet != nil { + var err error + kp, err = m.keySet.keySetProto() + if err != nil { + return nil, err + } + } + pb = &sppb.Mutation{ + Operation: &sppb.Mutation_Delete_{ + Delete: &sppb.Mutation_Delete{ + Table: m.table, + KeySet: kp, + }, + }, + } + case opInsert: + w, err := prepareWrite(m.table, m.columns, m.values) + if err != nil { + return nil, err + } + pb = &sppb.Mutation{Operation: &sppb.Mutation_Insert{Insert: w}} + case opInsertOrUpdate: + w, err := prepareWrite(m.table, m.columns, m.values) + if err != nil { + return nil, err + } + pb = &sppb.Mutation{Operation: &sppb.Mutation_InsertOrUpdate{InsertOrUpdate: w}} + case opReplace: + w, err := prepareWrite(m.table, m.columns, m.values) + if err != nil { + return nil, err + } + pb = &sppb.Mutation{Operation: &sppb.Mutation_Replace{Replace: w}} + case opUpdate: + w, err := prepareWrite(m.table, m.columns, m.values) + if err != nil { + return nil, err + } + pb = &sppb.Mutation{Operation: &sppb.Mutation_Update{Update: w}} + default: + return nil, errInvdMutationOp(m) + } + return pb, nil +} + +// mutationsProto turns a spanner.Mutation array into a sppb.Mutation array, +// it is convenient for sending batch mutations to Cloud Spanner. +func mutationsProto(ms []*Mutation) ([]*sppb.Mutation, error) { + l := make([]*sppb.Mutation, 0, len(ms)) + for _, m := range ms { + pb, err := m.proto() + if err != nil { + return nil, err + } + l = append(l, pb) + } + return l, nil +} diff --git a/vendor/cloud.google.com/go/spanner/mutation_test.go b/vendor/cloud.google.com/go/spanner/mutation_test.go new file mode 100644 index 0000000000..283c59409b --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/mutation_test.go @@ -0,0 +1,571 @@ +/* +Copyright 2017 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 spanner + +import ( + "sort" + "strings" + "testing" + + proto3 "github.com/golang/protobuf/ptypes/struct" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// keysetProto returns protobuf encoding of valid spanner.KeySet. +func keysetProto(t *testing.T, ks KeySet) *sppb.KeySet { + k, err := ks.keySetProto() + if err != nil { + t.Fatalf("cannot convert keyset %v to protobuf: %v", ks, err) + } + return k +} + +// Test encoding from spanner.Mutation to protobuf. +func TestMutationToProto(t *testing.T) { + for i, test := range []struct { + m *Mutation + want *sppb.Mutation + }{ + // Delete Mutation + { + &Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Delete_{ + Delete: &sppb.Mutation_Delete{ + Table: "t_foo", + KeySet: keysetProto(t, Key{"foo"}), + }, + }, + }, + }, + // Insert Mutation + { + &Mutation{opInsert, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Insert{ + Insert: &sppb.Mutation_Write{ + Table: "t_foo", + Columns: []string{"col1", "col2"}, + Values: []*proto3.ListValue{ + { + Values: []*proto3.Value{intProto(1), intProto(2)}, + }, + }, + }, + }, + }, + }, + // InsertOrUpdate Mutation + { + &Mutation{opInsertOrUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_InsertOrUpdate{ + InsertOrUpdate: &sppb.Mutation_Write{ + Table: "t_foo", + Columns: []string{"col1", "col2"}, + Values: []*proto3.ListValue{ + { + Values: []*proto3.Value{floatProto(1.0), floatProto(2.0)}, + }, + }, + }, + }, + }, + }, + // Replace Mutation + { + &Mutation{opReplace, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", 2.0}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Replace{ + Replace: &sppb.Mutation_Write{ + Table: "t_foo", + Columns: []string{"col1", "col2"}, + Values: []*proto3.ListValue{ + { + Values: []*proto3.Value{stringProto("one"), floatProto(2.0)}, + }, + }, + }, + }, + }, + }, + // Update Mutation + { + &Mutation{opUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Update{ + Update: &sppb.Mutation_Write{ + Table: "t_foo", + Columns: []string{"col1", "col2"}, + Values: []*proto3.ListValue{ + { + Values: []*proto3.Value{stringProto("one"), nullProto()}, + }, + }, + }, + }, + }, + }, + } { + if got, err := test.m.proto(); err != nil || !testEqual(got, test.want) { + t.Errorf("%d: (%#v).proto() = (%v, %v), want (%v, nil)", i, test.m, got, err, test.want) + } + } +} + +// mutationColumnSorter implements sort.Interface for sorting column-value pairs in a Mutation by column names. +type mutationColumnSorter struct { + Mutation +} + +// newMutationColumnSorter creates new instance of mutationColumnSorter by duplicating the input Mutation so that +// sorting won't change the input Mutation. +func newMutationColumnSorter(m *Mutation) *mutationColumnSorter { + return &mutationColumnSorter{ + Mutation{ + m.op, + m.table, + m.keySet, + append([]string(nil), m.columns...), + append([]interface{}(nil), m.values...), + }, + } +} + +// Len implements sort.Interface.Len. +func (ms *mutationColumnSorter) Len() int { + return len(ms.columns) +} + +// Swap implements sort.Interface.Swap. +func (ms *mutationColumnSorter) Swap(i, j int) { + ms.columns[i], ms.columns[j] = ms.columns[j], ms.columns[i] + ms.values[i], ms.values[j] = ms.values[j], ms.values[i] +} + +// Less implements sort.Interface.Less. +func (ms *mutationColumnSorter) Less(i, j int) bool { + return strings.Compare(ms.columns[i], ms.columns[j]) < 0 +} + +// mutationEqual returns true if two mutations in question are equal +// to each other. +func mutationEqual(t *testing.T, m1, m2 Mutation) bool { + // Two mutations are considered to be equal even if their column values have different + // orders. + ms1 := newMutationColumnSorter(&m1) + ms2 := newMutationColumnSorter(&m2) + sort.Sort(ms1) + sort.Sort(ms2) + return testEqual(ms1, ms2) +} + +// Test helper functions which help to generate spanner.Mutation. +func TestMutationHelpers(t *testing.T) { + for _, test := range []struct { + m string + got *Mutation + want *Mutation + }{ + { + "Insert", + Insert("t_foo", []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}), + &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, + }, + { + "InsertMap", + InsertMap("t_foo", map[string]interface{}{"col1": int64(1), "col2": int64(2)}), + &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, + }, + { + "InsertStruct", + func() *Mutation { + m, err := InsertStruct( + "t_foo", + struct { + notCol bool + Col1 int64 `spanner:"col1"` + Col2 int64 `spanner:"col2"` + }{false, int64(1), int64(2)}, + ) + if err != nil { + t.Errorf("cannot convert struct into mutation: %v", err) + } + return m + }(), + &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, + }, + { + "Update", + Update("t_foo", []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}), + &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, + }, + { + "UpdateMap", + UpdateMap("t_foo", map[string]interface{}{"col1": "one", "col2": []byte(nil)}), + &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, + }, + { + "UpdateStruct", + func() *Mutation { + m, err := UpdateStruct( + "t_foo", + struct { + Col1 string `spanner:"col1"` + notCol int + Col2 []byte `spanner:"col2"` + }{"one", 1, nil}, + ) + if err != nil { + t.Errorf("cannot convert struct into mutation: %v", err) + } + return m + }(), + &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, + }, + { + "InsertOrUpdate", + InsertOrUpdate("t_foo", []string{"col1", "col2"}, []interface{}{1.0, 2.0}), + &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, + }, + { + "InsertOrUpdateMap", + InsertOrUpdateMap("t_foo", map[string]interface{}{"col1": 1.0, "col2": 2.0}), + &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, + }, + { + "InsertOrUpdateStruct", + func() *Mutation { + m, err := InsertOrUpdateStruct( + "t_foo", + struct { + Col1 float64 `spanner:"col1"` + Col2 float64 `spanner:"col2"` + notCol float64 + }{1.0, 2.0, 3.0}, + ) + if err != nil { + t.Errorf("cannot convert struct into mutation: %v", err) + } + return m + }(), + &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, + }, + { + "Replace", + Replace("t_foo", []string{"col1", "col2"}, []interface{}{"one", 2.0}), + &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}}, + }, + { + "ReplaceMap", + ReplaceMap("t_foo", map[string]interface{}{"col1": "one", "col2": 2.0}), + &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}}, + }, + { + "ReplaceStruct", + func() *Mutation { + m, err := ReplaceStruct( + "t_foo", + struct { + Col1 string `spanner:"col1"` + Col2 float64 `spanner:"col2"` + notCol string + }{"one", 2.0, "foo"}, + ) + if err != nil { + t.Errorf("cannot convert struct into mutation: %v", err) + } + return m + }(), + &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}}, + }, + { + "Delete", + Delete("t_foo", Key{"foo"}), + &Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil}, + }, + { + "DeleteRange", + Delete("t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}), + &Mutation{opDelete, "t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}, nil, nil}, + }, + } { + if !mutationEqual(t, *test.got, *test.want) { + t.Errorf("%v: got Mutation %v, want %v", test.m, test.got, test.want) + } + } +} + +// Test encoding non-struct types by using *Struct helpers. +func TestBadStructs(t *testing.T) { + val := "i_am_not_a_struct" + wantErr := errNotStruct(val) + if _, gotErr := InsertStruct("t_test", val); !testEqual(gotErr, wantErr) { + t.Errorf("InsertStruct(%q) returns error %v, want %v", val, gotErr, wantErr) + } + if _, gotErr := InsertOrUpdateStruct("t_test", val); !testEqual(gotErr, wantErr) { + t.Errorf("InsertOrUpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr) + } + if _, gotErr := UpdateStruct("t_test", val); !testEqual(gotErr, wantErr) { + t.Errorf("UpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr) + } + if _, gotErr := ReplaceStruct("t_test", val); !testEqual(gotErr, wantErr) { + t.Errorf("ReplaceStruct(%q) returns error %v, want %v", val, gotErr, wantErr) + } +} + +func TestStructToMutationParams(t *testing.T) { + // Tests cases not covered elsewhere. + type S struct{ F interface{} } + + for _, test := range []struct { + in interface{} + wantCols []string + wantVals []interface{} + wantErr error + }{ + {nil, nil, nil, errNotStruct(nil)}, + {3, nil, nil, errNotStruct(3)}, + {(*S)(nil), nil, nil, nil}, + {&S{F: 1}, []string{"F"}, []interface{}{1}, nil}, + {&S{F: CommitTimestamp}, []string{"F"}, []interface{}{CommitTimestamp}, nil}, + } { + gotCols, gotVals, gotErr := structToMutationParams(test.in) + if !testEqual(gotCols, test.wantCols) { + t.Errorf("%#v: got cols %v, want %v", test.in, gotCols, test.wantCols) + } + if !testEqual(gotVals, test.wantVals) { + t.Errorf("%#v: got vals %v, want %v", test.in, gotVals, test.wantVals) + } + if !testEqual(gotErr, test.wantErr) { + t.Errorf("%#v: got err %v, want %v", test.in, gotErr, test.wantErr) + } + } +} + +// Test encoding Mutation into proto. +func TestEncodeMutation(t *testing.T) { + for _, test := range []struct { + name string + mutation Mutation + wantProto *sppb.Mutation + wantErr error + }{ + { + "OpDelete", + Mutation{opDelete, "t_test", Key{1}, nil, nil}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Delete_{ + Delete: &sppb.Mutation_Delete{ + Table: "t_test", + KeySet: &sppb.KeySet{ + Keys: []*proto3.ListValue{listValueProto(intProto(1))}, + }, + }, + }, + }, + nil, + }, + { + "OpDelete - Key error", + Mutation{opDelete, "t_test", Key{struct{}{}}, nil, nil}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Delete_{ + Delete: &sppb.Mutation_Delete{ + Table: "t_test", + KeySet: &sppb.KeySet{}, + }, + }, + }, + errInvdKeyPartType(struct{}{}), + }, + { + "OpInsert", + Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Insert{ + Insert: &sppb.Mutation_Write{ + Table: "t_test", + Columns: []string{"key", "val"}, + Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, + }, + }, + }, + nil, + }, + { + "OpInsert - Value Type Error", + Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Insert{ + Insert: &sppb.Mutation_Write{}, + }, + }, + errEncoderUnsupportedType(struct{}{}), + }, + { + "OpInsertOrUpdate", + Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_InsertOrUpdate{ + InsertOrUpdate: &sppb.Mutation_Write{ + Table: "t_test", + Columns: []string{"key", "val"}, + Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, + }, + }, + }, + nil, + }, + { + "OpInsertOrUpdate - Value Type Error", + Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_InsertOrUpdate{ + InsertOrUpdate: &sppb.Mutation_Write{}, + }, + }, + errEncoderUnsupportedType(struct{}{}), + }, + { + "OpReplace", + Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Replace{ + Replace: &sppb.Mutation_Write{ + Table: "t_test", + Columns: []string{"key", "val"}, + Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, + }, + }, + }, + nil, + }, + { + "OpReplace - Value Type Error", + Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Replace{ + Replace: &sppb.Mutation_Write{}, + }, + }, + errEncoderUnsupportedType(struct{}{}), + }, + { + "OpUpdate", + Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Update{ + Update: &sppb.Mutation_Write{ + Table: "t_test", + Columns: []string{"key", "val"}, + Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, + }, + }, + }, + nil, + }, + { + "OpUpdate - Value Type Error", + Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, + &sppb.Mutation{ + Operation: &sppb.Mutation_Update{ + Update: &sppb.Mutation_Write{}, + }, + }, + errEncoderUnsupportedType(struct{}{}), + }, + { + "OpKnown - Unknown Mutation Operation Code", + Mutation{op(100), "t_test", nil, nil, nil}, + &sppb.Mutation{}, + errInvdMutationOp(Mutation{op(100), "t_test", nil, nil, nil}), + }, + } { + gotProto, gotErr := test.mutation.proto() + if gotErr != nil { + if !testEqual(gotErr, test.wantErr) { + t.Errorf("%s: %v.proto() returns error %v, want %v", test.name, test.mutation, gotErr, test.wantErr) + } + continue + } + if !testEqual(gotProto, test.wantProto) { + t.Errorf("%s: %v.proto() = (%v, nil), want (%v, nil)", test.name, test.mutation, gotProto, test.wantProto) + } + } +} + +// Test Encoding an array of mutations. +func TestEncodeMutationArray(t *testing.T) { + for _, test := range []struct { + name string + ms []*Mutation + want []*sppb.Mutation + wantErr error + }{ + { + "Multiple Mutations", + []*Mutation{ + {opDelete, "t_test", Key{"bar"}, nil, nil}, + {opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, + }, + []*sppb.Mutation{ + { + Operation: &sppb.Mutation_Delete_{ + Delete: &sppb.Mutation_Delete{ + Table: "t_test", + KeySet: &sppb.KeySet{ + Keys: []*proto3.ListValue{listValueProto(stringProto("bar"))}, + }, + }, + }, + }, + { + Operation: &sppb.Mutation_InsertOrUpdate{ + InsertOrUpdate: &sppb.Mutation_Write{ + Table: "t_test", + Columns: []string{"key", "val"}, + Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, + }, + }, + }, + }, + nil, + }, + { + "Multiple Mutations - Bad Mutation", + []*Mutation{ + {opDelete, "t_test", Key{"bar"}, nil, nil}, + {opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", struct{}{}}}, + }, + []*sppb.Mutation{}, + errEncoderUnsupportedType(struct{}{}), + }, + } { + gotProto, gotErr := mutationsProto(test.ms) + if gotErr != nil { + if !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: mutationsProto(%v) returns error %v, want %v", test.name, test.ms, gotErr, test.wantErr) + } + continue + } + if !testEqual(gotProto, test.want) { + t.Errorf("%v: mutationsProto(%v) = (%v, nil), want (%v, nil)", test.name, test.ms, gotProto, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/spanner/not_appengine.go b/vendor/cloud.google.com/go/spanner/not_appengine.go new file mode 100644 index 0000000000..687e433549 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/not_appengine.go @@ -0,0 +1,20 @@ +// Copyright 2017 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 !appengine + +package spanner + +// numChannels is the default value for NumChannels of client +const numChannels = 4 diff --git a/vendor/cloud.google.com/go/spanner/not_go17.go b/vendor/cloud.google.com/go/spanner/not_go17.go new file mode 100644 index 0000000000..19b2c4a6bb --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/not_go17.go @@ -0,0 +1,74 @@ +// 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. + +// +build !go1.7 + +package spanner + +import ( + "reflect" + "strconv" +) + +func structTagLookup(tag reflect.StructTag, key string) (string, bool) { + // from go1.10.2 implementation of StructTag.Lookup. + for tag != "" { + // Skip leading space. + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // Scan to colon. A space, a quote or a control character is a syntax error. + // Strictly speaking, control chars include the range [0x7f, 0x9f], not just + // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters + // as it is simpler to inspect the tag's bytes than the tag's runes. + i = 0 + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { + i++ + } + if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // Scan quoted string to find value. + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if key == name { + value, err := strconv.Unquote(qvalue) + if err != nil { + break + } + return value, true + } + } + return "", false +} diff --git a/vendor/cloud.google.com/go/spanner/not_go18.go b/vendor/cloud.google.com/go/spanner/not_go18.go new file mode 100644 index 0000000000..0107cc0a5c --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/not_go18.go @@ -0,0 +1,31 @@ +// Copyright 2017 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 !go1.8 + +package spanner + +import "golang.org/x/net/context" + +// OpenCensus only supports go 1.8 and higher. + +func traceStartSpan(ctx context.Context, _ string) context.Context { + return ctx +} + +func traceEndSpan(context.Context, error) { +} + +func tracePrintf(context.Context, map[string]interface{}, string, ...interface{}) { +} diff --git a/vendor/cloud.google.com/go/spanner/oc_test.go b/vendor/cloud.google.com/go/spanner/oc_test.go new file mode 100644 index 0000000000..07edcc5962 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/oc_test.go @@ -0,0 +1,54 @@ +// 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. + +// +build go1.8 + +package spanner + +import ( + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + stestutil "cloud.google.com/go/spanner/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +// Check that stats are being exported. +func TestOCStats(t *testing.T) { + te := testutil.NewTestExporter() + defer te.Unregister() + + ms := stestutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + ctx := context.Background() + c, err := NewClient(ctx, "projects/P/instances/I/databases/D", + option.WithEndpoint(ms.Addr()), + option.WithGRPCDialOption(grpc.WithInsecure()), + option.WithoutAuthentication()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + c.Single().ReadRow(ctx, "Users", Key{"alice"}, []string{"email"}) + // Wait until we see data from the view. + select { + case <-te.Stats: + case <-time.After(1 * time.Second): + t.Fatal("no stats were exported before timeout") + } +} diff --git a/vendor/cloud.google.com/go/spanner/protoutils.go b/vendor/cloud.google.com/go/spanner/protoutils.go new file mode 100644 index 0000000000..3797980ab7 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/protoutils.go @@ -0,0 +1,113 @@ +/* +Copyright 2017 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 spanner + +import ( + "encoding/base64" + "strconv" + "time" + + "cloud.google.com/go/civil" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// Helpers to generate protobuf values and Cloud Spanner types. + +func stringProto(s string) *proto3.Value { + return &proto3.Value{Kind: stringKind(s)} +} + +func stringKind(s string) *proto3.Value_StringValue { + return &proto3.Value_StringValue{StringValue: s} +} + +func stringType() *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_STRING} +} + +func boolProto(b bool) *proto3.Value { + return &proto3.Value{Kind: &proto3.Value_BoolValue{BoolValue: b}} +} + +func boolType() *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_BOOL} +} + +func intProto(n int64) *proto3.Value { + return &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: strconv.FormatInt(n, 10)}} +} + +func intType() *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_INT64} +} + +func floatProto(n float64) *proto3.Value { + return &proto3.Value{Kind: &proto3.Value_NumberValue{NumberValue: n}} +} + +func floatType() *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_FLOAT64} +} + +func bytesProto(b []byte) *proto3.Value { + return &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: base64.StdEncoding.EncodeToString(b)}} +} + +func bytesType() *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_BYTES} +} + +func timeProto(t time.Time) *proto3.Value { + return stringProto(t.UTC().Format(time.RFC3339Nano)) +} + +func timeType() *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_TIMESTAMP} +} + +func dateProto(d civil.Date) *proto3.Value { + return stringProto(d.String()) +} + +func dateType() *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_DATE} +} + +func listProto(p ...*proto3.Value) *proto3.Value { + return &proto3.Value{Kind: &proto3.Value_ListValue{ListValue: &proto3.ListValue{Values: p}}} +} + +func listValueProto(p ...*proto3.Value) *proto3.ListValue { + return &proto3.ListValue{Values: p} +} + +func listType(t *sppb.Type) *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_ARRAY, ArrayElementType: t} +} + +func mkField(n string, t *sppb.Type) *sppb.StructType_Field { + return &sppb.StructType_Field{Name: n, Type: t} +} + +func structType(fields ...*sppb.StructType_Field) *sppb.Type { + return &sppb.Type{Code: sppb.TypeCode_STRUCT, StructType: &sppb.StructType{Fields: fields}} +} + +func nullProto() *proto3.Value { + return &proto3.Value{Kind: &proto3.Value_NullValue{NullValue: proto3.NullValue_NULL_VALUE}} +} diff --git a/vendor/cloud.google.com/go/spanner/read.go b/vendor/cloud.google.com/go/spanner/read.go new file mode 100644 index 0000000000..b7ba5a2f04 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/read.go @@ -0,0 +1,704 @@ +/* +Copyright 2017 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 spanner + +import ( + "bytes" + "io" + "log" + "sync/atomic" + "time" + + "cloud.google.com/go/internal/protostruct" + proto "github.com/golang/protobuf/proto" + proto3 "github.com/golang/protobuf/ptypes/struct" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" +) + +// streamingReceiver is the interface for receiving data from a client side +// stream. +type streamingReceiver interface { + Recv() (*sppb.PartialResultSet, error) +} + +// errEarlyReadEnd returns error for read finishes when gRPC stream is still active. +func errEarlyReadEnd() error { + return spannerErrorf(codes.FailedPrecondition, "read completed with active stream") +} + +// stream is the internal fault tolerant method for streaming data from +// Cloud Spanner. +func stream(ctx context.Context, rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error), setTimestamp func(time.Time), release func(error)) *RowIterator { + ctx, cancel := context.WithCancel(ctx) + ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.RowIterator") + return &RowIterator{ + streamd: newResumableStreamDecoder(ctx, rpc), + rowd: &partialResultSetDecoder{}, + setTimestamp: setTimestamp, + release: release, + cancel: cancel, + } +} + +// RowIterator is an iterator over Rows. +type RowIterator struct { + // The plan for the query. Available after RowIterator.Next returns iterator.Done + // if QueryWithStats was called. + QueryPlan *sppb.QueryPlan + + // Execution statistics for the query. Available after RowIterator.Next returns iterator.Done + // if QueryWithStats was called. + QueryStats map[string]interface{} + + streamd *resumableStreamDecoder + rowd *partialResultSetDecoder + setTimestamp func(time.Time) + release func(error) + cancel func() + err error + rows []*Row +} + +// 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 (r *RowIterator) Next() (*Row, error) { + if r.err != nil { + return nil, r.err + } + for len(r.rows) == 0 && r.streamd.next() { + prs := r.streamd.get() + if prs.Stats != nil { + r.QueryPlan = prs.Stats.QueryPlan + r.QueryStats = protostruct.DecodeToMap(prs.Stats.QueryStats) + } + r.rows, r.err = r.rowd.add(prs) + if r.err != nil { + return nil, r.err + } + if !r.rowd.ts.IsZero() && r.setTimestamp != nil { + r.setTimestamp(r.rowd.ts) + r.setTimestamp = nil + } + } + if len(r.rows) > 0 { + row := r.rows[0] + r.rows = r.rows[1:] + return row, nil + } + if err := r.streamd.lastErr(); err != nil { + r.err = toSpannerError(err) + } else if !r.rowd.done() { + r.err = errEarlyReadEnd() + } else { + r.err = iterator.Done + } + return nil, r.err +} + +// Do calls the provided function once in sequence for each row in the iteration. If the +// function returns a non-nil error, Do immediately returns that error. +// +// If there are no rows in the iterator, Do will return nil without calling the +// provided function. +// +// Do always calls Stop on the iterator. +func (r *RowIterator) Do(f func(r *Row) error) error { + defer r.Stop() + for { + row, err := r.Next() + switch err { + case iterator.Done: + return nil + case nil: + if err = f(row); err != nil { + return err + } + default: + return err + } + } +} + +// Stop terminates the iteration. It should be called after you finish using the iterator. +func (r *RowIterator) Stop() { + if r.streamd != nil { + defer traceEndSpan(r.streamd.ctx, r.err) + } + if r.cancel != nil { + r.cancel() + } + if r.release != nil { + r.release(r.err) + if r.err == nil { + r.err = spannerErrorf(codes.FailedPrecondition, "Next called after Stop") + } + r.release = nil + + } +} + +// partialResultQueue implements a simple FIFO queue. The zero value is a +// valid queue. +type partialResultQueue struct { + q []*sppb.PartialResultSet + first int + last int + n int // number of elements in queue +} + +// empty returns if the partialResultQueue is empty. +func (q *partialResultQueue) empty() bool { + return q.n == 0 +} + +// errEmptyQueue returns error for dequeuing an empty queue. +func errEmptyQueue() error { + return spannerErrorf(codes.OutOfRange, "empty partialResultQueue") +} + +// peekLast returns the last item in partialResultQueue; if the queue +// is empty, it returns error. +func (q *partialResultQueue) peekLast() (*sppb.PartialResultSet, error) { + if q.empty() { + return nil, errEmptyQueue() + } + return q.q[(q.last+cap(q.q)-1)%cap(q.q)], nil +} + +// push adds an item to the tail of partialResultQueue. +func (q *partialResultQueue) push(r *sppb.PartialResultSet) { + if q.q == nil { + q.q = make([]*sppb.PartialResultSet, 8 /* arbitrary */) + } + if q.n == cap(q.q) { + buf := make([]*sppb.PartialResultSet, cap(q.q)*2) + for i := 0; i < q.n; i++ { + buf[i] = q.q[(q.first+i)%cap(q.q)] + } + q.q = buf + q.first = 0 + q.last = q.n + } + q.q[q.last] = r + q.last = (q.last + 1) % cap(q.q) + q.n++ +} + +// pop removes an item from the head of partialResultQueue and returns +// it. +func (q *partialResultQueue) pop() *sppb.PartialResultSet { + if q.n == 0 { + return nil + } + r := q.q[q.first] + q.q[q.first] = nil + q.first = (q.first + 1) % cap(q.q) + q.n-- + return r +} + +// clear empties partialResultQueue. +func (q *partialResultQueue) clear() { + *q = partialResultQueue{} +} + +// dump retrieves all items from partialResultQueue and return them in a slice. +// It is used only in tests. +func (q *partialResultQueue) dump() []*sppb.PartialResultSet { + var dq []*sppb.PartialResultSet + for i := q.first; len(dq) < q.n; i = (i + 1) % cap(q.q) { + dq = append(dq, q.q[i]) + } + return dq +} + +// resumableStreamDecoderState encodes resumableStreamDecoder's status. +// See also the comments for resumableStreamDecoder.Next. +type resumableStreamDecoderState int + +const ( + unConnected resumableStreamDecoderState = iota // 0 + queueingRetryable // 1 + queueingUnretryable // 2 + aborted // 3 + finished // 4 +) + +// resumableStreamDecoder provides a resumable interface for receiving +// sppb.PartialResultSet(s) from a given query wrapped by +// resumableStreamDecoder.rpc(). +type resumableStreamDecoder struct { + // state is the current status of resumableStreamDecoder, see also + // the comments for resumableStreamDecoder.Next. + state resumableStreamDecoderState + // stateWitness when non-nil is called to observe state change, + // used for testing. + stateWitness func(resumableStreamDecoderState) + // ctx is the caller's context, used for cancel/timeout Next(). + ctx context.Context + // rpc is a factory of streamingReceiver, which might resume + // a previous stream from the point encoded in restartToken. + // rpc is always a wrapper of a Cloud Spanner query which is + // resumable. + rpc func(ctx context.Context, restartToken []byte) (streamingReceiver, error) + // stream is the current RPC streaming receiver. + stream streamingReceiver + // q buffers received yet undecoded partial results. + q partialResultQueue + // bytesBetweenResumeTokens is the proxy of the byte size of PartialResultSets being queued + // between two resume tokens. Once bytesBetweenResumeTokens is greater than + // maxBytesBetweenResumeTokens, resumableStreamDecoder goes into queueingUnretryable state. + bytesBetweenResumeTokens int32 + // maxBytesBetweenResumeTokens is the max number of bytes that can be buffered + // between two resume tokens. It is always copied from the global maxBytesBetweenResumeTokens + // atomically. + maxBytesBetweenResumeTokens int32 + // np is the next sppb.PartialResultSet ready to be returned + // to caller of resumableStreamDecoder.Get(). + np *sppb.PartialResultSet + // resumeToken stores the resume token that resumableStreamDecoder has + // last revealed to caller. + resumeToken []byte + // retryCount is the number of retries that have been carried out so far + retryCount int + // err is the last error resumableStreamDecoder has encountered so far. + err error + // backoff to compute delays between retries. + backoff exponentialBackoff +} + +// newResumableStreamDecoder creates a new resumeableStreamDecoder instance. +// Parameter rpc should be a function that creates a new stream +// beginning at the restartToken if non-nil. +func newResumableStreamDecoder(ctx context.Context, rpc func(ct context.Context, restartToken []byte) (streamingReceiver, error)) *resumableStreamDecoder { + return &resumableStreamDecoder{ + ctx: ctx, + rpc: rpc, + maxBytesBetweenResumeTokens: atomic.LoadInt32(&maxBytesBetweenResumeTokens), + backoff: defaultBackoff, + } +} + +// changeState fulfills state transition for resumableStateDecoder. +func (d *resumableStreamDecoder) changeState(target resumableStreamDecoderState) { + if d.state == queueingRetryable && d.state != target { + // Reset bytesBetweenResumeTokens because it is only meaningful/changed under + // queueingRetryable state. + d.bytesBetweenResumeTokens = 0 + } + d.state = target + if d.stateWitness != nil { + d.stateWitness(target) + } +} + +// isNewResumeToken returns if the observed resume token is different from +// the one returned from server last time. +func (d *resumableStreamDecoder) isNewResumeToken(rt []byte) bool { + if rt == nil { + return false + } + if bytes.Compare(rt, d.resumeToken) == 0 { + return false + } + return true +} + +// Next advances to the next available partial result set. If error or no +// more, returns false, call Err to determine if an error was encountered. +// The following diagram illustrates the state machine of resumableStreamDecoder +// that Next() implements. Note that state transition can be only triggered by +// RPC activities. +/* + rpc() fails retryable + +---------+ + | | rpc() fails unretryable/ctx timeouts or cancelled + | | +------------------------------------------------+ + | | | | + | v | v + | +---+---+---+ +--------+ +------+--+ + +-----+unConnected| |finished| | aborted |<----+ + | | ++-----+-+ +------+--+ | + +---+----+--+ ^ ^ ^ | + | ^ | | | | + | | | | recv() fails | + | | | | | | + | |recv() fails retryable | | | | + | |with valid ctx | | | | + | | | | | | + rpc() succeeds | +-----------------------+ | | | + | | | recv EOF recv EOF | | + | | | | | | + v | | Queue size exceeds | | | + +---+----+---+----+threshold +-------+-----------+ | | ++---------->+ +--------------->+ +-+ | +| |queueingRetryable| |queueingUnretryable| | +| | +<---------------+ | | +| +---+----------+--+ pop() returns +--+----+-----------+ | +| | | resume token | ^ | +| | | | | | +| | | | | | ++---------------+ | | | | + recv() succeeds | +----+ | + | recv() succeeds | + | | + | | + | | + | | + | | + +--------------------------------------------------+ + recv() fails unretryable + +*/ +var ( + // maxBytesBetweenResumeTokens is the maximum amount of bytes that resumableStreamDecoder + // in queueingRetryable state can use to queue PartialResultSets before getting + // into queueingUnretryable state. + maxBytesBetweenResumeTokens = int32(128 * 1024 * 1024) +) + +func (d *resumableStreamDecoder) next() bool { + for { + select { + case <-d.ctx.Done(): + // Do context check here so that even gRPC failed to do + // so, resumableStreamDecoder can still break the loop + // as expected. + d.err = errContextCanceled(d.ctx, d.err) + d.changeState(aborted) + default: + } + switch d.state { + case unConnected: + // If no gRPC stream is available, try to initiate one. + if d.stream, d.err = d.rpc(d.ctx, d.resumeToken); d.err != nil { + if isRetryable(d.err) { + d.doBackOff() + // Be explicit about state transition, although the + // state doesn't actually change. State transition + // will be triggered only by RPC activity, regardless of + // whether there is an actual state change or not. + d.changeState(unConnected) + continue + } + d.changeState(aborted) + continue + } + d.resetBackOff() + d.changeState(queueingRetryable) + continue + case queueingRetryable: + fallthrough + case queueingUnretryable: + // Receiving queue is not empty. + last, err := d.q.peekLast() + if err != nil { + // Only the case that receiving queue is empty could cause peekLast to + // return error and in such case, we should try to receive from stream. + d.tryRecv() + continue + } + if d.isNewResumeToken(last.ResumeToken) { + // Got new resume token, return buffered sppb.PartialResultSets to caller. + d.np = d.q.pop() + if d.q.empty() { + d.bytesBetweenResumeTokens = 0 + // The new resume token was just popped out from queue, record it. + d.resumeToken = d.np.ResumeToken + d.changeState(queueingRetryable) + } + return true + } + if d.bytesBetweenResumeTokens >= d.maxBytesBetweenResumeTokens && d.state == queueingRetryable { + d.changeState(queueingUnretryable) + continue + } + if d.state == queueingUnretryable { + // When there is no resume token observed, + // only yield sppb.PartialResultSets to caller under + // queueingUnretryable state. + d.np = d.q.pop() + return true + } + // Needs to receive more from gRPC stream till a new resume token + // is observed. + d.tryRecv() + continue + case aborted: + // Discard all pending items because none of them + // should be yield to caller. + d.q.clear() + return false + case finished: + // If query has finished, check if there are still buffered messages. + if d.q.empty() { + // No buffered PartialResultSet. + return false + } + // Although query has finished, there are still buffered PartialResultSets. + d.np = d.q.pop() + return true + + default: + log.Printf("Unexpected resumableStreamDecoder.state: %v", d.state) + return false + } + } +} + +// tryRecv attempts to receive a PartialResultSet from gRPC stream. +func (d *resumableStreamDecoder) tryRecv() { + var res *sppb.PartialResultSet + if res, d.err = d.stream.Recv(); d.err != nil { + if d.err == io.EOF { + d.err = nil + d.changeState(finished) + return + } + if isRetryable(d.err) && d.state == queueingRetryable { + d.err = nil + // Discard all queue items (none have resume tokens). + d.q.clear() + d.stream = nil + d.changeState(unConnected) + d.doBackOff() + return + } + d.changeState(aborted) + return + } + d.q.push(res) + if d.state == queueingRetryable && !d.isNewResumeToken(res.ResumeToken) { + // adjusting d.bytesBetweenResumeTokens + d.bytesBetweenResumeTokens += int32(proto.Size(res)) + } + d.resetBackOff() + d.changeState(d.state) +} + +// resetBackOff clears the internal retry counter of +// resumableStreamDecoder so that the next exponential +// backoff will start at a fresh state. +func (d *resumableStreamDecoder) resetBackOff() { + d.retryCount = 0 +} + +// doBackoff does an exponential backoff sleep. +func (d *resumableStreamDecoder) doBackOff() { + delay := d.backoff.delay(d.retryCount) + tracePrintf(d.ctx, nil, "Backing off stream read for %s", delay) + ticker := time.NewTicker(delay) + defer ticker.Stop() + d.retryCount++ + select { + case <-d.ctx.Done(): + case <-ticker.C: + } +} + +// get returns the most recent PartialResultSet generated by a call to next. +func (d *resumableStreamDecoder) get() *sppb.PartialResultSet { + return d.np +} + +// lastErr returns the last non-EOF error encountered. +func (d *resumableStreamDecoder) lastErr() error { + return d.err +} + +// partialResultSetDecoder assembles PartialResultSet(s) into Cloud Spanner +// Rows. +type partialResultSetDecoder struct { + row Row + tx *sppb.Transaction + chunked bool // if true, next value should be merged with last values entry. + ts time.Time // read timestamp +} + +// yield checks we have a complete row, and if so returns it. A row is not +// complete if it doesn't have enough columns, or if this is a chunked response +// and there are no further values to process. +func (p *partialResultSetDecoder) yield(chunked, last bool) *Row { + if len(p.row.vals) == len(p.row.fields) && (!chunked || !last) { + // When partialResultSetDecoder gets enough number of + // Column values, There are two cases that a new Row + // should be yield: + // 1. The incoming PartialResultSet is not chunked; + // 2. The incoming PartialResultSet is chunked, but the + // proto3.Value being merged is not the last one in + // the PartialResultSet. + // + // Use a fresh Row to simplify clients that want to use yielded results + // after the next row is retrieved. Note that fields is never changed + // so it doesn't need to be copied. + fresh := Row{ + fields: p.row.fields, + vals: make([]*proto3.Value, len(p.row.vals)), + } + copy(fresh.vals, p.row.vals) + p.row.vals = p.row.vals[:0] // empty and reuse slice + return &fresh + } + return nil +} + +// yieldTx returns transaction information via caller supplied callback. +func errChunkedEmptyRow() error { + return spannerErrorf(codes.FailedPrecondition, "got invalid chunked PartialResultSet with empty Row") +} + +// add tries to merge a new PartialResultSet into buffered Row. It returns +// any rows that have been completed as a result. +func (p *partialResultSetDecoder) add(r *sppb.PartialResultSet) ([]*Row, error) { + var rows []*Row + if r.Metadata != nil { + // Metadata should only be returned in the first result. + if p.row.fields == nil { + p.row.fields = r.Metadata.RowType.Fields + } + if p.tx == nil && r.Metadata.Transaction != nil { + p.tx = r.Metadata.Transaction + if p.tx.ReadTimestamp != nil { + p.ts = time.Unix(p.tx.ReadTimestamp.Seconds, int64(p.tx.ReadTimestamp.Nanos)) + } + } + } + if len(r.Values) == 0 { + return nil, nil + } + if p.chunked { + p.chunked = false + // Try to merge first value in r.Values into + // uncompleted row. + last := len(p.row.vals) - 1 + if last < 0 { // sanity check + return nil, errChunkedEmptyRow() + } + var err error + // If p is chunked, then we should always try to merge p.last with r.first. + if p.row.vals[last], err = p.merge(p.row.vals[last], r.Values[0]); err != nil { + return nil, err + } + r.Values = r.Values[1:] + // Merge is done, try to yield a complete Row. + if row := p.yield(r.ChunkedValue, len(r.Values) == 0); row != nil { + rows = append(rows, row) + } + } + for i, v := range r.Values { + // The rest values in r can be appened into p directly. + p.row.vals = append(p.row.vals, v) + // Again, check to see if a complete Row can be yielded because of + // the newly added value. + if row := p.yield(r.ChunkedValue, i == len(r.Values)-1); row != nil { + rows = append(rows, row) + } + } + if r.ChunkedValue { + // After dealing with all values in r, if r is chunked then p must + // be also chunked. + p.chunked = true + } + return rows, nil +} + +// isMergeable returns if a protobuf Value can be potentially merged with +// other protobuf Values. +func (p *partialResultSetDecoder) isMergeable(a *proto3.Value) bool { + switch a.Kind.(type) { + case *proto3.Value_StringValue: + return true + case *proto3.Value_ListValue: + return true + default: + return false + } +} + +// errIncompatibleMergeTypes returns error for incompatible protobuf types +// that cannot be merged by partialResultSetDecoder. +func errIncompatibleMergeTypes(a, b *proto3.Value) error { + return spannerErrorf(codes.FailedPrecondition, "incompatible type in chunked PartialResultSet. expected (%T), got (%T)", a.Kind, b.Kind) +} + +// errUnsupportedMergeType returns error for protobuf type that cannot be +// merged to other protobufs. +func errUnsupportedMergeType(a *proto3.Value) error { + return spannerErrorf(codes.FailedPrecondition, "unsupported type merge (%T)", a.Kind) +} + +// merge tries to combine two protobuf Values if possible. +func (p *partialResultSetDecoder) merge(a, b *proto3.Value) (*proto3.Value, error) { + var err error + typeErr := errIncompatibleMergeTypes(a, b) + switch t := a.Kind.(type) { + case *proto3.Value_StringValue: + s, ok := b.Kind.(*proto3.Value_StringValue) + if !ok { + return nil, typeErr + } + return &proto3.Value{ + Kind: &proto3.Value_StringValue{StringValue: t.StringValue + s.StringValue}, + }, nil + case *proto3.Value_ListValue: + l, ok := b.Kind.(*proto3.Value_ListValue) + if !ok { + return nil, typeErr + } + if l.ListValue == nil || len(l.ListValue.Values) <= 0 { + // b is an empty list, just return a. + return a, nil + } + if t.ListValue == nil || len(t.ListValue.Values) <= 0 { + // a is an empty list, just return b. + return b, nil + } + if la := len(t.ListValue.Values) - 1; p.isMergeable(t.ListValue.Values[la]) { + // When the last item in a is of type String, + // List or Struct(encoded into List by Cloud Spanner), + // try to Merge last item in a and first item in b. + t.ListValue.Values[la], err = p.merge(t.ListValue.Values[la], l.ListValue.Values[0]) + if err != nil { + return nil, err + } + l.ListValue.Values = l.ListValue.Values[1:] + } + return &proto3.Value{ + Kind: &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: append(t.ListValue.Values, l.ListValue.Values...), + }, + }, + }, nil + default: + return nil, errUnsupportedMergeType(a) + } + +} + +// Done returns if partialResultSetDecoder has already done with all buffered +// values. +func (p *partialResultSetDecoder) done() bool { + // There is no explicit end of stream marker, but ending part way + // through a row is obviously bad, or ending with the last column still + // awaiting completion. + return len(p.row.vals) == 0 && !p.chunked +} diff --git a/vendor/cloud.google.com/go/spanner/read_test.go b/vendor/cloud.google.com/go/spanner/read_test.go new file mode 100644 index 0000000000..e5f1474129 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/read_test.go @@ -0,0 +1,1733 @@ +/* +Copyright 2017 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 spanner + +import ( + "errors" + "fmt" + "io" + "sync/atomic" + "testing" + "time" + + "golang.org/x/net/context" + "google.golang.org/grpc/status" + + "github.com/golang/protobuf/proto" + proto3 "github.com/golang/protobuf/ptypes/struct" + + "cloud.google.com/go/spanner/internal/testutil" + "google.golang.org/api/iterator" + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var ( + // Mocked transaction timestamp. + trxTs = time.Unix(1, 2) + // Metadata for mocked KV table, its rows are returned by SingleUse transactions. + kvMeta = func() *sppb.ResultSetMetadata { + meta := testutil.KvMeta + meta.Transaction = &sppb.Transaction{ + ReadTimestamp: timestampProto(trxTs), + } + return &meta + }() + // Metadata for mocked ListKV table, which uses List for its key and value. + // Its rows are returned by snapshot readonly transactions, as indicated in the transaction metadata. + kvListMeta = &sppb.ResultSetMetadata{ + RowType: &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + { + Name: "Key", + Type: &sppb.Type{ + Code: sppb.TypeCode_ARRAY, + ArrayElementType: &sppb.Type{ + Code: sppb.TypeCode_STRING, + }, + }, + }, + { + Name: "Value", + Type: &sppb.Type{ + Code: sppb.TypeCode_ARRAY, + ArrayElementType: &sppb.Type{ + Code: sppb.TypeCode_STRING, + }, + }, + }, + }, + }, + Transaction: &sppb.Transaction{ + Id: transactionID{5, 6, 7, 8, 9}, + ReadTimestamp: timestampProto(trxTs), + }, + } + // Metadata for mocked schema of a query result set, which has two struct + // columns named "Col1" and "Col2", the struct's schema is like the + // following: + // + // STRUCT { + // INT + // LIST + // } + // + // Its rows are returned in readwrite transaction, as indicated in the transaction metadata. + kvObjectMeta = &sppb.ResultSetMetadata{ + RowType: &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + { + Name: "Col1", + Type: &sppb.Type{ + Code: sppb.TypeCode_STRUCT, + StructType: &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + { + Name: "foo-f1", + Type: &sppb.Type{ + Code: sppb.TypeCode_INT64, + }, + }, + { + Name: "foo-f2", + Type: &sppb.Type{ + Code: sppb.TypeCode_ARRAY, + ArrayElementType: &sppb.Type{ + Code: sppb.TypeCode_STRING, + }, + }, + }, + }, + }, + }, + }, + { + Name: "Col2", + Type: &sppb.Type{ + Code: sppb.TypeCode_STRUCT, + StructType: &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + { + Name: "bar-f1", + Type: &sppb.Type{ + Code: sppb.TypeCode_INT64, + }, + }, + { + Name: "bar-f2", + Type: &sppb.Type{ + Code: sppb.TypeCode_ARRAY, + ArrayElementType: &sppb.Type{ + Code: sppb.TypeCode_STRING, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Transaction: &sppb.Transaction{ + Id: transactionID{1, 2, 3, 4, 5}, + }, + } +) + +// String implements fmt.stringer. +func (r *Row) String() string { + return fmt.Sprintf("{fields: %s, val: %s}", r.fields, r.vals) +} + +func describeRows(l []*Row) string { + // generate a nice test failure description + var s = "[" + for i, r := range l { + if i != 0 { + s += ",\n " + } + s += fmt.Sprint(r) + } + s += "]" + return s +} + +// Helper for generating proto3 Value_ListValue instances, making +// test code shorter and readable. +func genProtoListValue(v ...string) *proto3.Value_ListValue { + r := &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: []*proto3.Value{}, + }, + } + for _, e := range v { + r.ListValue.Values = append( + r.ListValue.Values, + &proto3.Value{ + Kind: &proto3.Value_StringValue{StringValue: e}, + }, + ) + } + return r +} + +// Test Row generation logics of partialResultSetDecoder. +func TestPartialResultSetDecoder(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + var tests = []struct { + input []*sppb.PartialResultSet + wantF []*Row + wantTxID transactionID + wantTs time.Time + wantD bool + }{ + { + // Empty input. + wantD: true, + }, + // String merging examples. + { + // Single KV result. + input: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + {Kind: &proto3.Value_StringValue{StringValue: "bar"}}, + }, + }, + }, + wantF: []*Row{ + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + {Kind: &proto3.Value_StringValue{StringValue: "bar"}}, + }, + }, + }, + wantTs: trxTs, + wantD: true, + }, + { + // Incomplete partial result. + input: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + }, + }, + }, + wantTs: trxTs, + wantD: false, + }, + { + // Complete splitted result. + input: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + }, + }, + { + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "bar"}}, + }, + }, + }, + wantF: []*Row{ + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + {Kind: &proto3.Value_StringValue{StringValue: "bar"}}, + }, + }, + }, + wantTs: trxTs, + wantD: true, + }, + { + // Multi-row example with splitted row in the middle. + input: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + {Kind: &proto3.Value_StringValue{StringValue: "bar"}}, + {Kind: &proto3.Value_StringValue{StringValue: "A"}}, + }, + }, + { + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "1"}}, + {Kind: &proto3.Value_StringValue{StringValue: "B"}}, + {Kind: &proto3.Value_StringValue{StringValue: "2"}}, + }, + }, + }, + wantF: []*Row{ + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "foo"}}, + {Kind: &proto3.Value_StringValue{StringValue: "bar"}}, + }, + }, + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "A"}}, + {Kind: &proto3.Value_StringValue{StringValue: "1"}}, + }, + }, + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "B"}}, + {Kind: &proto3.Value_StringValue{StringValue: "2"}}, + }, + }, + }, + wantTs: trxTs, + wantD: true, + }, + { + // Merging example in result_set.proto. + input: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "Hello"}}, + {Kind: &proto3.Value_StringValue{StringValue: "W"}}, + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "orl"}}, + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "d"}}, + }, + }, + }, + wantF: []*Row{ + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "Hello"}}, + {Kind: &proto3.Value_StringValue{StringValue: "World"}}, + }, + }, + }, + wantTs: trxTs, + wantD: true, + }, + { + // More complex example showing completing a merge and + // starting a new merge in the same partialResultSet. + input: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "Hello"}}, + {Kind: &proto3.Value_StringValue{StringValue: "W"}}, // start split in value + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "orld"}}, // complete value + {Kind: &proto3.Value_StringValue{StringValue: "i"}}, // start split in key + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "s"}}, // complete key + {Kind: &proto3.Value_StringValue{StringValue: "not"}}, + {Kind: &proto3.Value_StringValue{StringValue: "a"}}, + {Kind: &proto3.Value_StringValue{StringValue: "qu"}}, // split in value + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "estion"}}, // complete value + }, + }, + }, + wantF: []*Row{ + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "Hello"}}, + {Kind: &proto3.Value_StringValue{StringValue: "World"}}, + }, + }, + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "is"}}, + {Kind: &proto3.Value_StringValue{StringValue: "not"}}, + }, + }, + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: "a"}}, + {Kind: &proto3.Value_StringValue{StringValue: "question"}}, + }, + }, + }, + wantTs: trxTs, + wantD: true, + }, + // List merging examples. + { + // Non-splitting Lists. + input: []*sppb.PartialResultSet{ + { + Metadata: kvListMeta, + Values: []*proto3.Value{ + { + Kind: genProtoListValue("foo-1", "foo-2"), + }, + }, + }, + { + Values: []*proto3.Value{ + { + Kind: genProtoListValue("bar-1", "bar-2"), + }, + }, + }, + }, + wantF: []*Row{ + { + fields: kvListMeta.RowType.Fields, + vals: []*proto3.Value{ + { + Kind: genProtoListValue("foo-1", "foo-2"), + }, + { + Kind: genProtoListValue("bar-1", "bar-2"), + }, + }, + }, + }, + wantTxID: transactionID{5, 6, 7, 8, 9}, + wantTs: trxTs, + wantD: true, + }, + { + // Simple List merge case: splitted string element. + input: []*sppb.PartialResultSet{ + { + Metadata: kvListMeta, + Values: []*proto3.Value{ + { + Kind: genProtoListValue("foo-1", "foo-"), + }, + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + { + Kind: genProtoListValue("2"), + }, + }, + }, + { + Values: []*proto3.Value{ + { + Kind: genProtoListValue("bar-1", "bar-2"), + }, + }, + }, + }, + wantF: []*Row{ + { + fields: kvListMeta.RowType.Fields, + vals: []*proto3.Value{ + { + Kind: genProtoListValue("foo-1", "foo-2"), + }, + { + Kind: genProtoListValue("bar-1", "bar-2"), + }, + }, + }, + }, + wantTxID: transactionID{5, 6, 7, 8, 9}, + wantTs: trxTs, + wantD: true, + }, + { + // Struct merging is also implemented by List merging. Note that + // Cloud Spanner uses proto.ListValue to encode Structs as well. + input: []*sppb.PartialResultSet{ + { + Metadata: kvObjectMeta, + Values: []*proto3.Value{ + { + Kind: &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: []*proto3.Value{ + {Kind: &proto3.Value_NumberValue{NumberValue: 23}}, + {Kind: genProtoListValue("foo-1", "fo")}, + }, + }, + }, + }, + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + { + Kind: &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: []*proto3.Value{ + {Kind: genProtoListValue("o-2", "f")}, + }, + }, + }, + }, + }, + ChunkedValue: true, + }, + { + Values: []*proto3.Value{ + { + Kind: &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: []*proto3.Value{ + {Kind: genProtoListValue("oo-3")}, + }, + }, + }, + }, + { + Kind: &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: []*proto3.Value{ + {Kind: &proto3.Value_NumberValue{NumberValue: 45}}, + {Kind: genProtoListValue("bar-1")}, + }, + }, + }, + }, + }, + }, + }, + wantF: []*Row{ + { + fields: kvObjectMeta.RowType.Fields, + vals: []*proto3.Value{ + { + Kind: &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: []*proto3.Value{ + {Kind: &proto3.Value_NumberValue{NumberValue: 23}}, + {Kind: genProtoListValue("foo-1", "foo-2", "foo-3")}, + }, + }, + }, + }, + { + Kind: &proto3.Value_ListValue{ + ListValue: &proto3.ListValue{ + Values: []*proto3.Value{ + {Kind: &proto3.Value_NumberValue{NumberValue: 45}}, + {Kind: genProtoListValue("bar-1")}, + }, + }, + }, + }, + }, + }, + }, + wantTxID: transactionID{1, 2, 3, 4, 5}, + wantD: true, + }, + } + +nextTest: + for i, test := range tests { + var rows []*Row + p := &partialResultSetDecoder{} + for j, v := range test.input { + rs, err := p.add(v) + if err != nil { + t.Errorf("test %d.%d: partialResultSetDecoder.add(%v) = %v; want nil", i, j, v, err) + continue nextTest + } + rows = append(rows, rs...) + } + if !testEqual(p.ts, test.wantTs) { + t.Errorf("got transaction(%v), want %v", p.ts, test.wantTs) + } + if !testEqual(rows, test.wantF) { + t.Errorf("test %d: rows=\n%v\n; want\n%v\n; p.row:\n%v\n", i, describeRows(rows), describeRows(test.wantF), p.row) + } + if got := p.done(); got != test.wantD { + t.Errorf("test %d: partialResultSetDecoder.done() = %v", i, got) + } + } +} + +const ( + maxBuffers = 16 // max number of PartialResultSets that will be buffered in tests. +) + +// setMaxBytesBetweenResumeTokens sets the global maxBytesBetweenResumeTokens to a smaller +// value more suitable for tests. It returns a function which should be called to restore +// the maxBytesBetweenResumeTokens to its old value +func setMaxBytesBetweenResumeTokens() func() { + o := atomic.LoadInt32(&maxBytesBetweenResumeTokens) + atomic.StoreInt32(&maxBytesBetweenResumeTokens, int32(maxBuffers*proto.Size(&sppb.PartialResultSet{ + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}}, + }, + }))) + return func() { + atomic.StoreInt32(&maxBytesBetweenResumeTokens, o) + } +} + +// keyStr generates key string for kvMeta schema. +func keyStr(i int) string { + return fmt.Sprintf("foo-%02d", i) +} + +// valStr generates value string for kvMeta schema. +func valStr(i int) string { + return fmt.Sprintf("bar-%02d", i) +} + +// Test state transitions of resumableStreamDecoder where state machine +// ends up to a non-blocking state(resumableStreamDecoder.Next returns +// on non-blocking state). +func TestRsdNonblockingStates(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + tests := []struct { + name string + msgs []testutil.MockCtlMsg + rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error) + sql string + // Expected values + want []*sppb.PartialResultSet // PartialResultSets that should be returned to caller + queue []*sppb.PartialResultSet // PartialResultSets that should be buffered + resumeToken []byte // Resume token that is maintained by resumableStreamDecoder + stateHistory []resumableStreamDecoderState // State transition history of resumableStreamDecoder + wantErr error + }{ + { + // unConnected->queueingRetryable->finished + name: "unConnected->queueingRetryable->finished", + msgs: []testutil.MockCtlMsg{ + {}, + {}, + {Err: io.EOF, ResumeToken: false}, + }, + sql: "SELECT t.key key, t.value value FROM t_mock t", + want: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}}, + }, + }, + }, + queue: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}}, + }, + }, + }, + stateHistory: []resumableStreamDecoderState{ + queueingRetryable, // do RPC + queueingRetryable, // got foo-00 + queueingRetryable, // got foo-01 + finished, // got EOF + }, + }, + { + // unConnected->queueingRetryable->aborted + name: "unConnected->queueingRetryable->aborted", + msgs: []testutil.MockCtlMsg{ + {}, + {Err: nil, ResumeToken: true}, + {}, + {Err: errors.New("I quit"), ResumeToken: false}, + }, + sql: "SELECT t.key key, t.value value FROM t_mock t", + want: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}}, + }, + }, + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}}, + }, + ResumeToken: testutil.EncodeResumeToken(1), + }, + }, + stateHistory: []resumableStreamDecoderState{ + queueingRetryable, // do RPC + queueingRetryable, // got foo-00 + queueingRetryable, // got foo-01 + queueingRetryable, // foo-01, resume token + queueingRetryable, // got foo-02 + aborted, // got error + }, + wantErr: status.Errorf(codes.Unknown, "I quit"), + }, + { + // unConnected->queueingRetryable->queueingUnretryable->queueingUnretryable + name: "unConnected->queueingRetryable->queueingUnretryable->queueingUnretryable", + msgs: func() (m []testutil.MockCtlMsg) { + for i := 0; i < maxBuffers+1; i++ { + m = append(m, testutil.MockCtlMsg{}) + } + return m + }(), + sql: "SELECT t.key key, t.value value FROM t_mock t", + want: func() (s []*sppb.PartialResultSet) { + for i := 0; i < maxBuffers+1; i++ { + s = append(s, &sppb.PartialResultSet{ + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}}, + }, + }) + } + return s + }(), + stateHistory: func() (s []resumableStreamDecoderState) { + s = append(s, queueingRetryable) // RPC + for i := 0; i < maxBuffers; i++ { + s = append(s, queueingRetryable) // the internal queue of resumableStreamDecoder fills up + } + // the first item fills up the queue and triggers state transition; + // the second item is received under queueingUnretryable state. + s = append(s, queueingUnretryable) + s = append(s, queueingUnretryable) + return s + }(), + }, + { + // unConnected->queueingRetryable->queueingUnretryable->aborted + name: "unConnected->queueingRetryable->queueingUnretryable->aborted", + msgs: func() (m []testutil.MockCtlMsg) { + for i := 0; i < maxBuffers; i++ { + m = append(m, testutil.MockCtlMsg{}) + } + m = append(m, testutil.MockCtlMsg{Err: errors.New("Just Abort It"), ResumeToken: false}) + return m + }(), + sql: "SELECT t.key key, t.value value FROM t_mock t", + want: func() (s []*sppb.PartialResultSet) { + for i := 0; i < maxBuffers; i++ { + s = append(s, &sppb.PartialResultSet{ + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}}, + }, + }) + } + return s + }(), + stateHistory: func() (s []resumableStreamDecoderState) { + s = append(s, queueingRetryable) // RPC + for i := 0; i < maxBuffers; i++ { + s = append(s, queueingRetryable) // internal queue of resumableStreamDecoder fills up + } + s = append(s, queueingUnretryable) // the last row triggers state change + s = append(s, aborted) // Error happens + return s + }(), + wantErr: status.Errorf(codes.Unknown, "Just Abort It"), + }, + } +nextTest: + for _, test := range tests { + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + mc := sppb.NewSpannerClient(dialMock(t, ms)) + if test.rpc == nil { + test.rpc = func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: test.sql, + ResumeToken: resumeToken, + }) + } + } + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r := newResumableStreamDecoder( + ctx, + test.rpc, + ) + st := []resumableStreamDecoderState{} + var lastErr error + // Once the expected number of state transitions are observed, + // send a signal by setting stateDone = true. + stateDone := false + // Set stateWitness to listen to state changes. + hl := len(test.stateHistory) // To avoid data race on test. + r.stateWitness = func(rs resumableStreamDecoderState) { + if !stateDone { + // Record state transitions. + st = append(st, rs) + if len(st) == hl { + lastErr = r.lastErr() + stateDone = true + } + } + } + // Let mock server stream given messages to resumableStreamDecoder. + for _, m := range test.msgs { + ms.AddMsg(m.Err, m.ResumeToken) + } + var rs []*sppb.PartialResultSet + for { + select { + case <-ctx.Done(): + t.Errorf("context cancelled or timeout during test") + continue nextTest + default: + } + if stateDone { + // Check if resumableStreamDecoder carried out expected + // state transitions. + if !testEqual(st, test.stateHistory) { + t.Errorf("%v: observed state transitions: \n%v\n, want \n%v\n", + test.name, st, test.stateHistory) + } + // Check if resumableStreamDecoder returns expected array of + // PartialResultSets. + if !testEqual(rs, test.want) { + t.Errorf("%v: received PartialResultSets: \n%v\n, want \n%v\n", test.name, rs, test.want) + } + // Verify that resumableStreamDecoder's internal buffering is also correct. + var q []*sppb.PartialResultSet + for { + item := r.q.pop() + if item == nil { + break + } + q = append(q, item) + } + if !testEqual(q, test.queue) { + t.Errorf("%v: PartialResultSets still queued: \n%v\n, want \n%v\n", test.name, q, test.queue) + } + // Verify resume token. + if test.resumeToken != nil && !testEqual(r.resumeToken, test.resumeToken) { + t.Errorf("%v: Resume token is %v, want %v\n", test.name, r.resumeToken, test.resumeToken) + } + // Verify error message. + if !testEqual(lastErr, test.wantErr) { + t.Errorf("%v: got error %v, want %v", test.name, lastErr, test.wantErr) + } + // Proceed to next test + continue nextTest + } + // Receive next decoded item. + if r.next() { + rs = append(rs, r.get()) + } + } + } +} + +// Test state transitions of resumableStreamDecoder where state machine +// ends up to a blocking state(resumableStreamDecoder.Next blocks +// on blocking state). +func TestRsdBlockingStates(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + tests := []struct { + name string + msgs []testutil.MockCtlMsg + rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error) + sql string + // Expected values + want []*sppb.PartialResultSet // PartialResultSets that should be returned to caller + queue []*sppb.PartialResultSet // PartialResultSets that should be buffered + resumeToken []byte // Resume token that is maintained by resumableStreamDecoder + stateHistory []resumableStreamDecoderState // State transition history of resumableStreamDecoder + wantErr error + }{ + { + // unConnected -> unConnected + name: "unConnected -> unConnected", + rpc: func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return nil, status.Errorf(codes.Unavailable, "trust me: server is unavailable") + }, + sql: "SELECT * from t_whatever", + stateHistory: []resumableStreamDecoderState{unConnected, unConnected, unConnected}, + wantErr: status.Errorf(codes.Unavailable, "trust me: server is unavailable"), + }, + { + // unConnected -> queueingRetryable + name: "unConnected -> queueingRetryable", + sql: "SELECT t.key key, t.value value FROM t_mock t", + stateHistory: []resumableStreamDecoderState{queueingRetryable}, + }, + { + // unConnected->queueingRetryable->queueingRetryable + name: "unConnected->queueingRetryable->queueingRetryable", + msgs: []testutil.MockCtlMsg{ + {}, + {Err: nil, ResumeToken: true}, + {Err: nil, ResumeToken: true}, + {}, + }, + sql: "SELECT t.key key, t.value value FROM t_mock t", + want: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}}, + }, + }, + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}}, + }, + ResumeToken: testutil.EncodeResumeToken(1), + }, + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(2)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(2)}}, + }, + ResumeToken: testutil.EncodeResumeToken(2), + }, + }, + queue: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(3)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(3)}}, + }, + }, + }, + resumeToken: testutil.EncodeResumeToken(2), + stateHistory: []resumableStreamDecoderState{ + queueingRetryable, // do RPC + queueingRetryable, // got foo-00 + queueingRetryable, // got foo-01 + queueingRetryable, // foo-01, resume token + queueingRetryable, // got foo-02 + queueingRetryable, // foo-02, resume token + queueingRetryable, // got foo-03 + }, + }, + { + // unConnected->queueingRetryable->queueingUnretryable->queueingRetryable->queueingRetryable + name: "unConnected->queueingRetryable->queueingUnretryable->queueingRetryable->queueingRetryable", + msgs: func() (m []testutil.MockCtlMsg) { + for i := 0; i < maxBuffers+1; i++ { + m = append(m, testutil.MockCtlMsg{}) + } + m = append(m, testutil.MockCtlMsg{Err: nil, ResumeToken: true}) + m = append(m, testutil.MockCtlMsg{}) + return m + }(), + sql: "SELECT t.key key, t.value value FROM t_mock t", + want: func() (s []*sppb.PartialResultSet) { + for i := 0; i < maxBuffers+2; i++ { + s = append(s, &sppb.PartialResultSet{ + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}}, + }, + }) + } + s[maxBuffers+1].ResumeToken = testutil.EncodeResumeToken(maxBuffers + 1) + return s + }(), + resumeToken: testutil.EncodeResumeToken(maxBuffers + 1), + queue: []*sppb.PartialResultSet{ + { + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(maxBuffers + 2)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(maxBuffers + 2)}}, + }, + }, + }, + stateHistory: func() (s []resumableStreamDecoderState) { + s = append(s, queueingRetryable) // RPC + for i := 0; i < maxBuffers; i++ { + s = append(s, queueingRetryable) // internal queue of resumableStreamDecoder filles up + } + for i := maxBuffers - 1; i < maxBuffers+1; i++ { + // the first item fills up the queue and triggers state change; + // the second item is received under queueingUnretryable state. + s = append(s, queueingUnretryable) + } + s = append(s, queueingUnretryable) // got (maxBuffers+1)th row under Unretryable state + s = append(s, queueingRetryable) // (maxBuffers+1)th row has resume token + s = append(s, queueingRetryable) // (maxBuffers+2)th row has no resume token + return s + }(), + }, + { + // unConnected->queueingRetryable->queueingUnretryable->finished + name: "unConnected->queueingRetryable->queueingUnretryable->finished", + msgs: func() (m []testutil.MockCtlMsg) { + for i := 0; i < maxBuffers; i++ { + m = append(m, testutil.MockCtlMsg{}) + } + m = append(m, testutil.MockCtlMsg{Err: io.EOF, ResumeToken: false}) + return m + }(), + sql: "SELECT t.key key, t.value value FROM t_mock t", + want: func() (s []*sppb.PartialResultSet) { + for i := 0; i < maxBuffers; i++ { + s = append(s, &sppb.PartialResultSet{ + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}}, + }, + }) + } + return s + }(), + stateHistory: func() (s []resumableStreamDecoderState) { + s = append(s, queueingRetryable) // RPC + for i := 0; i < maxBuffers; i++ { + s = append(s, queueingRetryable) // internal queue of resumableStreamDecoder fills up + } + s = append(s, queueingUnretryable) // last row triggers state change + s = append(s, finished) // query finishes + return s + }(), + }, + } + for _, test := range tests { + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + cc := dialMock(t, ms) + mc := sppb.NewSpannerClient(cc) + if test.rpc == nil { + // Avoid using test.sql directly in closure because for loop changes test. + sql := test.sql + test.rpc = func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: sql, + ResumeToken: resumeToken, + }) + } + } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + r := newResumableStreamDecoder( + ctx, + test.rpc, + ) + // Override backoff to make the test run faster. + r.backoff = exponentialBackoff{1 * time.Nanosecond, 1 * time.Nanosecond} + // st is the set of observed state transitions. + st := []resumableStreamDecoderState{} + // q is the content of the decoder's partial result queue when expected number of state transitions are done. + q := []*sppb.PartialResultSet{} + var lastErr error + // Once the expected number of state transitions are observed, + // send a signal to channel stateDone. + stateDone := make(chan int) + // Set stateWitness to listen to state changes. + hl := len(test.stateHistory) // To avoid data race on test. + r.stateWitness = func(rs resumableStreamDecoderState) { + select { + case <-stateDone: + // Noop after expected number of state transitions + default: + // Record state transitions. + st = append(st, rs) + if len(st) == hl { + lastErr = r.lastErr() + q = r.q.dump() + close(stateDone) + } + } + } + // Let mock server stream given messages to resumableStreamDecoder. + for _, m := range test.msgs { + ms.AddMsg(m.Err, m.ResumeToken) + } + var rs []*sppb.PartialResultSet + go func() { + for { + if !r.next() { + // Note that r.Next also exits on context cancel/timeout. + return + } + rs = append(rs, r.get()) + } + }() + // Verify that resumableStreamDecoder reaches expected state. + select { + case <-stateDone: // Note that at this point, receiver is still blocking on r.next(). + // Check if resumableStreamDecoder carried out expected + // state transitions. + if !testEqual(st, test.stateHistory) { + t.Errorf("%v: observed state transitions: \n%v\n, want \n%v\n", + test.name, st, test.stateHistory) + } + // Check if resumableStreamDecoder returns expected array of + // PartialResultSets. + if !testEqual(rs, test.want) { + t.Errorf("%v: received PartialResultSets: \n%v\n, want \n%v\n", test.name, rs, test.want) + } + // Verify that resumableStreamDecoder's internal buffering is also correct. + if !testEqual(q, test.queue) { + t.Errorf("%v: PartialResultSets still queued: \n%v\n, want \n%v\n", test.name, q, test.queue) + } + // Verify resume token. + if test.resumeToken != nil && !testEqual(r.resumeToken, test.resumeToken) { + t.Errorf("%v: Resume token is %v, want %v\n", test.name, r.resumeToken, test.resumeToken) + } + // Verify error message. + if !testEqual(lastErr, test.wantErr) { + t.Errorf("%v: got error %v, want %v", test.name, lastErr, test.wantErr) + } + case <-time.After(1 * time.Second): + t.Errorf("%v: Timeout in waiting for state change", test.name) + } + ms.Stop() + cc.Close() + } +} + +// sReceiver signals every receiving attempt through a channel, +// used by TestResumeToken to determine if the receiving of a certain +// PartialResultSet will be attempted next. +type sReceiver struct { + c chan int + rpcReceiver sppb.Spanner_ExecuteStreamingSqlClient +} + +// Recv() implements streamingReceiver.Recv for sReceiver. +func (sr *sReceiver) Recv() (*sppb.PartialResultSet, error) { + sr.c <- 1 + return sr.rpcReceiver.Recv() +} + +// waitn waits for nth receiving attempt from now on, until +// the signal for nth Recv() attempts is received or timeout. +// Note that because the way stream() works, the signal for the +// nth Recv() means that the previous n - 1 PartialResultSets +// has already been returned to caller or queued, if no error happened. +func (sr *sReceiver) waitn(n int) error { + for i := 0; i < n; i++ { + select { + case <-sr.c: + case <-time.After(10 * time.Second): + return fmt.Errorf("timeout in waiting for %v-th Recv()", i+1) + } + } + return nil +} + +// Test the handling of resumableStreamDecoder.bytesBetweenResumeTokens. +func TestQueueBytes(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + defer ms.Stop() + cc := dialMock(t, ms) + defer cc.Close() + mc := sppb.NewSpannerClient(cc) + sr := &sReceiver{ + c: make(chan int, 1000), // will never block in this test + } + wantQueueBytes := 0 + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r := newResumableStreamDecoder( + ctx, + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + r, err := mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + sr.rpcReceiver = r + return sr, err + }, + ) + go func() { + for r.next() { + } + }() + // Let server send maxBuffers / 2 rows. + for i := 0; i < maxBuffers/2; i++ { + wantQueueBytes += proto.Size(&sppb.PartialResultSet{ + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}}, + }, + }) + ms.AddMsg(nil, false) + } + if err := sr.waitn(maxBuffers/2 + 1); err != nil { + t.Fatalf("failed to wait for the first %v recv() calls: %v", maxBuffers, err) + } + if int32(wantQueueBytes) != r.bytesBetweenResumeTokens { + t.Errorf("r.bytesBetweenResumeTokens = %v, want %v", r.bytesBetweenResumeTokens, wantQueueBytes) + } + // Now send a resume token to drain the queue. + ms.AddMsg(nil, true) + // Wait for all rows to be processes. + if err := sr.waitn(1); err != nil { + t.Fatalf("failed to wait for rows to be processed: %v", err) + } + if r.bytesBetweenResumeTokens != 0 { + t.Errorf("r.bytesBetweenResumeTokens = %v, want 0", r.bytesBetweenResumeTokens) + } + // Let server send maxBuffers - 1 rows. + wantQueueBytes = 0 + for i := 0; i < maxBuffers-1; i++ { + wantQueueBytes += proto.Size(&sppb.PartialResultSet{ + Metadata: kvMeta, + Values: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}}, + }, + }) + ms.AddMsg(nil, false) + } + if err := sr.waitn(maxBuffers - 1); err != nil { + t.Fatalf("failed to wait for %v rows to be processed: %v", maxBuffers-1, err) + } + if int32(wantQueueBytes) != r.bytesBetweenResumeTokens { + t.Errorf("r.bytesBetweenResumeTokens = %v, want 0", r.bytesBetweenResumeTokens) + } + // Trigger a state transition: queueingRetryable -> queueingUnretryable. + ms.AddMsg(nil, false) + if err := sr.waitn(1); err != nil { + t.Fatalf("failed to wait for state transition: %v", err) + } + if r.bytesBetweenResumeTokens != 0 { + t.Errorf("r.bytesBetweenResumeTokens = %v, want 0", r.bytesBetweenResumeTokens) + } +} + +// Verify that client can deal with resume token correctly +func TestResumeToken(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + defer ms.Stop() + cc := dialMock(t, ms) + defer cc.Close() + mc := sppb.NewSpannerClient(cc) + sr := &sReceiver{ + c: make(chan int, 1000), // will never block in this test + } + rows := []*Row{} + done := make(chan error) + streaming := func() { + // Establish a stream to mock cloud spanner server. + iter := stream(context.Background(), + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + r, err := mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + sr.rpcReceiver = r + return sr, err + }, + nil, + func(error) {}) + defer iter.Stop() + var err error + for { + var row *Row + row, err = iter.Next() + if err == iterator.Done { + err = nil + break + } + if err != nil { + break + } + rows = append(rows, row) + } + done <- err + } + go streaming() + // Server streaming row 0 - 2, only row 1 has resume token. + // Client will receive row 0 - 2, so it will try receiving for + // 4 times (the last recv will block), and only row 0 - 1 will + // be yielded. + for i := 0; i < 3; i++ { + if i == 1 { + ms.AddMsg(nil, true) + } else { + ms.AddMsg(nil, false) + } + } + // Wait for 4 receive attempts, as explained above. + if err := sr.waitn(4); err != nil { + t.Fatalf("failed to wait for row 0 - 2: %v", err) + } + want := []*Row{ + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}}, + }, + }, + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}}, + }, + }, + } + if !testEqual(rows, want) { + t.Errorf("received rows: \n%v\n; but want\n%v\n", rows, want) + } + // Inject resumable failure. + ms.AddMsg( + status.Errorf(codes.Unavailable, "mock server unavailable"), + false, + ) + // Test if client detects the resumable failure and retries. + if err := sr.waitn(1); err != nil { + t.Fatalf("failed to wait for client to retry: %v", err) + } + // Client has resumed the query, now server resend row 2. + ms.AddMsg(nil, true) + if err := sr.waitn(1); err != nil { + t.Fatalf("failed to wait for resending row 2: %v", err) + } + // Now client should have received row 0 - 2. + want = append(want, &Row{ + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(2)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(2)}}, + }, + }) + if !testEqual(rows, want) { + t.Errorf("received rows: \n%v\n, want\n%v\n", rows, want) + } + // Sending 3rd - (maxBuffers+1)th rows without resume tokens, client should buffer them. + for i := 3; i < maxBuffers+2; i++ { + ms.AddMsg(nil, false) + } + if err := sr.waitn(maxBuffers - 1); err != nil { + t.Fatalf("failed to wait for row 3-%v: %v", maxBuffers+1, err) + } + // Received rows should be unchanged. + if !testEqual(rows, want) { + t.Errorf("receive rows: \n%v\n, want\n%v\n", rows, want) + } + // Send (maxBuffers+2)th row to trigger state change of resumableStreamDecoder: + // queueingRetryable -> queueingUnretryable + ms.AddMsg(nil, false) + if err := sr.waitn(1); err != nil { + t.Fatalf("failed to wait for row %v: %v", maxBuffers+2, err) + } + // Client should yield row 3rd - (maxBuffers+2)th to application. Therefore, application should + // see row 0 - (maxBuffers+2)th so far. + for i := 3; i < maxBuffers+3; i++ { + want = append(want, &Row{ + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}}, + }, + }) + } + if !testEqual(rows, want) { + t.Errorf("received rows: \n%v\n; want\n%v\n", rows, want) + } + // Inject resumable error, but since resumableStreamDecoder is already at queueingUnretryable + // state, query will just fail. + ms.AddMsg( + status.Errorf(codes.Unavailable, "mock server wants some sleep"), + false, + ) + var gotErr error + select { + case gotErr = <-done: + case <-time.After(10 * time.Second): + t.Fatalf("timeout in waiting for failed query to return.") + } + if wantErr := toSpannerError(status.Errorf(codes.Unavailable, "mock server wants some sleep")); !testEqual(gotErr, wantErr) { + t.Fatalf("stream() returns error: %v, but want error: %v", gotErr, wantErr) + } + + // Reconnect to mock Cloud Spanner. + rows = []*Row{} + go streaming() + // Let server send two rows without resume token. + for i := maxBuffers + 3; i < maxBuffers+5; i++ { + ms.AddMsg(nil, false) + } + if err := sr.waitn(3); err != nil { + t.Fatalf("failed to wait for row %v - %v: %v", maxBuffers+3, maxBuffers+5, err) + } + if len(rows) > 0 { + t.Errorf("client received some rows unexpectedly: %v, want nothing", rows) + } + // Let server end the query. + ms.AddMsg(io.EOF, false) + select { + case gotErr = <-done: + case <-time.After(10 * time.Second): + t.Fatalf("timeout in waiting for failed query to return") + } + if gotErr != nil { + t.Fatalf("stream() returns unexpected error: %v, but want no error", gotErr) + } + // Verify if a normal server side EOF flushes all queued rows. + want = []*Row{ + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(maxBuffers + 3)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(maxBuffers + 3)}}, + }, + }, + { + fields: kvMeta.RowType.Fields, + vals: []*proto3.Value{ + {Kind: &proto3.Value_StringValue{StringValue: keyStr(maxBuffers + 4)}}, + {Kind: &proto3.Value_StringValue{StringValue: valStr(maxBuffers + 4)}}, + }, + }, + } + if !testEqual(rows, want) { + t.Errorf("received rows: \n%v\n; but want\n%v\n", rows, want) + } +} + +// Verify that streaming query get retried upon real gRPC server transport failures. +func TestGrpcReconnect(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + defer ms.Stop() + cc := dialMock(t, ms) + defer cc.Close() + mc := sppb.NewSpannerClient(cc) + retry := make(chan int) + row := make(chan int) + var err error + go func() { + r := 0 + // Establish a stream to mock cloud spanner server. + iter := stream(context.Background(), + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + if r > 0 { + // This RPC attempt is a retry, signal it. + retry <- r + } + r++ + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + + }, + nil, + func(error) {}) + defer iter.Stop() + for { + _, err = iter.Next() + if err == iterator.Done { + err = nil + break + } + if err != nil { + break + } + row <- 0 + } + }() + // Add a message and wait for the receipt. + ms.AddMsg(nil, true) + select { + case <-row: + case <-time.After(10 * time.Second): + t.Fatalf("expect stream to be established within 10 seconds, but it didn't") + } + // Error injection: force server to close all connections. + ms.Stop() + // Test to see if client respond to the real RPC failure correctly by + // retrying RPC. + select { + case r, ok := <-retry: + if ok && r == 1 { + break + } + t.Errorf("retry count = %v, want 1", r) + case <-time.After(10 * time.Second): + t.Errorf("client library failed to respond after 10 seconds, aborting") + return + } +} + +// Test cancel/timeout for client operations. +func TestCancelTimeout(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + defer ms.Stop() + cc := dialMock(t, ms) + defer cc.Close() + mc := sppb.NewSpannerClient(cc) + done := make(chan int) + go func() { + for { + ms.AddMsg(nil, true) + } + }() + // Test cancelling query. + ctx, cancel := context.WithCancel(context.Background()) + var err error + go func() { + // Establish a stream to mock cloud spanner server. + iter := stream(ctx, + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + }, + nil, + func(error) {}) + defer iter.Stop() + for { + _, err = iter.Next() + if err == iterator.Done { + break + } + if err != nil { + done <- 0 + break + } + } + }() + cancel() + select { + case <-done: + if ErrCode(err) != codes.Canceled { + t.Errorf("streaming query is canceled and returns error %v, want error code %v", err, codes.Canceled) + } + case <-time.After(1 * time.Second): + t.Errorf("query doesn't exit timely after being cancelled") + } + // Test query timeout. + ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) + go func() { + // Establish a stream to mock cloud spanner server. + iter := stream(ctx, + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + }, + nil, + func(error) {}) + defer iter.Stop() + for { + _, err = iter.Next() + if err == iterator.Done { + err = nil + break + } + if err != nil { + break + } + } + done <- 0 + }() + select { + case <-done: + if wantErr := codes.DeadlineExceeded; ErrCode(err) != wantErr { + t.Errorf("streaming query timeout returns error %v, want error code %v", err, wantErr) + } + case <-time.After(2 * time.Second): + t.Errorf("query doesn't timeout as expected") + } +} + +func TestRowIteratorDo(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + defer ms.Stop() + cc := dialMock(t, ms) + defer cc.Close() + mc := sppb.NewSpannerClient(cc) + + for i := 0; i < 3; i++ { + ms.AddMsg(nil, false) + } + ms.AddMsg(io.EOF, true) + nRows := 0 + iter := stream(context.Background(), + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + }, + nil, + func(error) {}) + err := iter.Do(func(r *Row) error { nRows++; return nil }) + if err != nil { + t.Errorf("Using Do: %v", err) + } + if nRows != 3 { + t.Errorf("got %d rows, want 3", nRows) + } +} + +func TestRowIteratorDoWithError(t *testing.T) { + restore := setMaxBytesBetweenResumeTokens() + defer restore() + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + defer ms.Stop() + cc := dialMock(t, ms) + defer cc.Close() + mc := sppb.NewSpannerClient(cc) + + for i := 0; i < 3; i++ { + ms.AddMsg(nil, false) + } + ms.AddMsg(io.EOF, true) + iter := stream(context.Background(), + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + }, + nil, + func(error) {}) + injected := errors.New("Failed iterator") + err := iter.Do(func(r *Row) error { return injected }) + if err != injected { + t.Errorf("got <%v>, want <%v>", err, injected) + } +} + +func TestIteratorStopEarly(t *testing.T) { + ctx := context.Background() + restore := setMaxBytesBetweenResumeTokens() + defer restore() + ms := testutil.NewMockCloudSpanner(t, trxTs) + ms.Serve() + defer ms.Stop() + cc := dialMock(t, ms) + defer cc.Close() + mc := sppb.NewSpannerClient(cc) + + ms.AddMsg(nil, false) + ms.AddMsg(nil, false) + ms.AddMsg(io.EOF, true) + + iter := stream(ctx, + func(ct context.Context, resumeToken []byte) (streamingReceiver, error) { + return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{ + Sql: "SELECT t.key key, t.value value FROM t_mock t", + ResumeToken: resumeToken, + }) + }, + nil, + func(error) {}) + _, err := iter.Next() + if err != nil { + t.Fatalf("before Stop: %v", err) + } + iter.Stop() + // Stop sets r.err to the FailedPrecondition error "Next called after Stop". + // Override that here so this test can observe the Canceled error from the stream. + iter.err = nil + iter.Next() + if ErrCode(iter.streamd.lastErr()) != codes.Canceled { + t.Errorf("after Stop: got %v, wanted Canceled", err) + } +} + +func TestIteratorWithError(t *testing.T) { + injected := errors.New("Failed iterator") + iter := RowIterator{err: injected} + defer iter.Stop() + if _, err := iter.Next(); err != injected { + t.Fatalf("Expected error: %v, got %v", injected, err) + } +} + +func dialMock(t *testing.T, ms *testutil.MockCloudSpanner) *grpc.ClientConn { + cc, err := grpc.Dial(ms.Addr(), grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + t.Fatalf("Dial(%q) = %v", ms.Addr(), err) + } + return cc +} diff --git a/vendor/cloud.google.com/go/spanner/retry.go b/vendor/cloud.google.com/go/spanner/retry.go new file mode 100644 index 0000000000..3dfa58936b --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/retry.go @@ -0,0 +1,198 @@ +/* +Copyright 2017 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 spanner + +import ( + "fmt" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + edpb "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +const ( + retryInfoKey = "google.rpc.retryinfo-bin" +) + +// errRetry returns an unavailable error under error namespace EsOther. It is a +// generic retryable error that is used to mask and recover unretryable errors +// in a retry loop. +func errRetry(err error) error { + if se, ok := err.(*Error); ok { + return &Error{codes.Unavailable, fmt.Sprintf("generic Cloud Spanner retryable error: { %v }", se.Error()), se.trailers} + } + return spannerErrorf(codes.Unavailable, "generic Cloud Spanner retryable error: { %v }", err.Error()) +} + +// isErrorClosing reports whether the error is generated by gRPC layer talking to a closed server. +func isErrorClosing(err error) bool { + if err == nil { + return false + } + if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "transport is closing") { + // Handle the case when connection is closed unexpectedly. + // TODO: once gRPC is able to categorize + // this as retryable error, we should stop parsing the + // error message here. + return true + } + return false +} + +// isErrorRST reports whether the error is generated by gRPC client receiving a RST frame from server. +func isErrorRST(err error) bool { + if err == nil { + return false + } + if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "stream terminated by RST_STREAM") { + // TODO: once gRPC is able to categorize this error as "go away" or "retryable", + // we should stop parsing the error message. + return true + } + return false +} + +// isErrorUnexpectedEOF returns true if error is generated by gRPC layer +// receiving io.EOF unexpectedly. +func isErrorUnexpectedEOF(err error) bool { + if err == nil { + return false + } + if ErrCode(err) == codes.Unknown && strings.Contains(ErrDesc(err), "unexpected EOF") { + // Unexpected EOF is an transport layer issue that + // could be recovered by retries. The most likely + // scenario is a flaky RecvMsg() call due to network + // issues. + // TODO: once gRPC is able to categorize + // this as retryable error, we should stop parsing the + // error message here. + return true + } + return false +} + +// isErrorUnavailable returns true if the error is about server being unavailable. +func isErrorUnavailable(err error) bool { + if err == nil { + return false + } + if ErrCode(err) == codes.Unavailable { + return true + } + return false +} + +// isRetryable returns true if the Cloud Spanner error being checked is a retryable error. +func isRetryable(err error) bool { + if isErrorClosing(err) { + return true + } + if isErrorUnexpectedEOF(err) { + return true + } + if isErrorRST(err) { + return true + } + if isErrorUnavailable(err) { + return true + } + return false +} + +// errContextCanceled returns *spanner.Error for canceled context. +func errContextCanceled(ctx context.Context, lastErr error) error { + if ctx.Err() == context.DeadlineExceeded { + return spannerErrorf(codes.DeadlineExceeded, "%v, lastErr is <%v>", ctx.Err(), lastErr) + } + return spannerErrorf(codes.Canceled, "%v, lastErr is <%v>", ctx.Err(), lastErr) +} + +// extractRetryDelay extracts retry backoff if present. +func extractRetryDelay(err error) (time.Duration, bool) { + trailers := errTrailers(err) + if trailers == nil { + return 0, false + } + elem, ok := trailers[retryInfoKey] + if !ok || len(elem) <= 0 { + return 0, false + } + _, b, err := metadata.DecodeKeyValue(retryInfoKey, elem[0]) + if err != nil { + return 0, false + } + var retryInfo edpb.RetryInfo + if proto.Unmarshal([]byte(b), &retryInfo) != nil { + return 0, false + } + delay, err := ptypes.Duration(retryInfo.RetryDelay) + if err != nil { + return 0, false + } + return delay, true +} + +// runRetryable keeps attempting to run f until one of the following happens: +// 1) f returns nil error or an unretryable error; +// 2) context is cancelled or timeout. +// TODO: consider using https://github.com/googleapis/gax-go once it +// becomes available internally. +func runRetryable(ctx context.Context, f func(context.Context) error) error { + return toSpannerError(runRetryableNoWrap(ctx, f)) +} + +// Like runRetryable, but doesn't wrap the returned error in a spanner.Error. +func runRetryableNoWrap(ctx context.Context, f func(context.Context) error) error { + var funcErr error + retryCount := 0 + for { + select { + case <-ctx.Done(): + // Do context check here so that even f() failed to do + // so (for example, gRPC implementation bug), the loop + // can still have a chance to exit as expected. + return errContextCanceled(ctx, funcErr) + default: + } + funcErr = f(ctx) + if funcErr == nil { + return nil + } + if isRetryable(funcErr) { + // Error is retryable, do exponential backoff and continue. + b, ok := extractRetryDelay(funcErr) + if !ok { + b = defaultBackoff.delay(retryCount) + } + tracePrintf(ctx, nil, "Backing off for %s, then retrying", b) + select { + case <-ctx.Done(): + return errContextCanceled(ctx, funcErr) + case <-time.After(b): + } + retryCount++ + continue + } + // Error isn't retryable / no error, return immediately. + return funcErr + } +} diff --git a/vendor/cloud.google.com/go/spanner/retry_test.go b/vendor/cloud.google.com/go/spanner/retry_test.go new file mode 100644 index 0000000000..1ed93478d7 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/retry_test.go @@ -0,0 +1,107 @@ +/* +Copyright 2017 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 spanner + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + edpb "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Test if runRetryable loop deals with various errors correctly. +func TestRetry(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + responses := []error{ + status.Errorf(codes.Internal, "transport is closing"), + status.Errorf(codes.Unknown, "unexpected EOF"), + status.Errorf(codes.Internal, "stream terminated by RST_STREAM with error code: 2"), + status.Errorf(codes.Unavailable, "service is currently unavailable"), + errRetry(fmt.Errorf("just retry it")), + } + err := runRetryable(context.Background(), func(ct context.Context) error { + var r error + if len(responses) > 0 { + r = responses[0] + responses = responses[1:] + } + return r + }) + if err != nil { + t.Errorf("runRetryable should be able to survive all retryable errors, but it returns %v", err) + } + // Unretryable errors + injErr := errors.New("this is unretryable") + err = runRetryable(context.Background(), func(ct context.Context) error { + return injErr + }) + if wantErr := toSpannerError(injErr); !testEqual(err, wantErr) { + t.Errorf("runRetryable returns error %v, want %v", err, wantErr) + } + // Timeout + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + retryErr := errRetry(fmt.Errorf("still retrying")) + err = runRetryable(ctx, func(ct context.Context) error { + // Expect to trigger timeout in retryable runner after 10 executions. + <-time.After(100 * time.Millisecond) + // Let retryable runner to retry so that timeout will eventually happen. + return retryErr + }) + // Check error code and error message + if wantErrCode, wantErr := codes.DeadlineExceeded, errContextCanceled(ctx, retryErr); ErrCode(err) != wantErrCode || !testEqual(err, wantErr) { + t.Errorf("=\n<%v, %v>, want:\n<%v, %v>", ErrCode(err), err, wantErrCode, wantErr) + } + // Cancellation + ctx, cancel = context.WithCancel(context.Background()) + retries := 3 + retryErr = errRetry(fmt.Errorf("retry before cancel")) + err = runRetryable(ctx, func(ct context.Context) error { + retries-- + if retries == 0 { + cancel() + } + return retryErr + }) + // Check error code, error message, retry count + if wantErrCode, wantErr := codes.Canceled, errContextCanceled(ctx, retryErr); ErrCode(err) != wantErrCode || !testEqual(err, wantErr) || retries != 0 { + t.Errorf("=\n<%v, %v, %v>, want:\n<%v, %v, %v>", ErrCode(err), err, retries, wantErrCode, wantErr, 0) + } +} + +func TestRetryInfo(t *testing.T) { + b, _ := proto.Marshal(&edpb.RetryInfo{ + RetryDelay: ptypes.DurationProto(time.Second), + }) + trailers := map[string]string{ + retryInfoKey: string(b), + } + gotDelay, ok := extractRetryDelay(errRetry(toSpannerErrorWithMetadata(status.Errorf(codes.Aborted, ""), metadata.New(trailers)))) + if !ok || !testEqual(time.Second, gotDelay) { + t.Errorf(" = <%t, %v>, want ", ok, gotDelay, time.Second) + } +} diff --git a/vendor/cloud.google.com/go/spanner/row.go b/vendor/cloud.google.com/go/spanner/row.go new file mode 100644 index 0000000000..c2edd6c17f --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/row.go @@ -0,0 +1,309 @@ +/* +Copyright 2017 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 spanner + +import ( + "fmt" + "reflect" + + proto3 "github.com/golang/protobuf/ptypes/struct" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" +) + +// A Row is a view of a row of data returned by a Cloud Spanner read. +// It consists of a number of columns; the number depends on the columns +// used to construct the read. +// +// The column values can be accessed by index. For instance, if the read specified +// []string{"photo_id", "caption"}, then each row will contain two +// columns: "photo_id" with index 0, and "caption" with index 1. +// +// Column values are decoded by using one of the Column, ColumnByName, or +// Columns methods. The valid values passed to these methods depend on the +// column type. For example: +// +// var photoID int64 +// err := row.Column(0, &photoID) // Decode column 0 as an integer. +// +// var caption string +// err := row.Column(1, &caption) // Decode column 1 as a string. +// +// // Decode all the columns. +// err := row.Columns(&photoID, &caption) +// +// Supported types and their corresponding Cloud Spanner column type(s) are: +// +// *string(not NULL), *NullString - STRING +// *[]string, *[]NullString - STRING ARRAY +// *[]byte - BYTES +// *[][]byte - BYTES ARRAY +// *int64(not NULL), *NullInt64 - INT64 +// *[]int64, *[]NullInt64 - INT64 ARRAY +// *bool(not NULL), *NullBool - BOOL +// *[]bool, *[]NullBool - BOOL ARRAY +// *float64(not NULL), *NullFloat64 - FLOAT64 +// *[]float64, *[]NullFloat64 - FLOAT64 ARRAY +// *time.Time(not NULL), *NullTime - TIMESTAMP +// *[]time.Time, *[]NullTime - TIMESTAMP ARRAY +// *Date(not NULL), *NullDate - DATE +// *[]civil.Date, *[]NullDate - DATE ARRAY +// *[]*some_go_struct, *[]NullRow - STRUCT ARRAY +// *GenericColumnValue - any Cloud Spanner type +// +// For TIMESTAMP columns, the returned time.Time object will be in UTC. +// +// To fetch an array of BYTES, pass a *[][]byte. To fetch an array of (sub)rows, pass +// a *[]spanner.NullRow or a *[]*some_go_struct where some_go_struct holds all +// information of the subrow, see spanner.Row.ToStruct for the mapping between a +// Cloud Spanner row and a Go struct. To fetch an array of other types, pass a +// *[]spanner.NullXXX type of the appropriate type. Use GenericColumnValue when you +// don't know in advance what column type to expect. +// +// Row decodes the row contents lazily; as a result, each call to a getter has +// a chance of returning an error. +// +// A column value may be NULL if the corresponding value is not present in +// Cloud Spanner. The spanner.NullXXX types (spanner.NullInt64 et al.) allow fetching +// values that may be null. A NULL BYTES can be fetched into a *[]byte as nil. +// It is an error to fetch a NULL value into any other type. +type Row struct { + fields []*sppb.StructType_Field + vals []*proto3.Value // keep decoded for now +} + +// errNamesValuesMismatch returns error for when columnNames count is not equal +// to columnValues count. +func errNamesValuesMismatch(columnNames []string, columnValues []interface{}) error { + return spannerErrorf(codes.FailedPrecondition, + "different number of names(%v) and values(%v)", len(columnNames), len(columnValues)) +} + +// NewRow returns a Row containing the supplied data. This can be useful for +// mocking Cloud Spanner Read and Query responses for unit testing. +func NewRow(columnNames []string, columnValues []interface{}) (*Row, error) { + if len(columnValues) != len(columnNames) { + return nil, errNamesValuesMismatch(columnNames, columnValues) + } + r := Row{ + fields: make([]*sppb.StructType_Field, len(columnValues)), + vals: make([]*proto3.Value, len(columnValues)), + } + for i := range columnValues { + val, typ, err := encodeValue(columnValues[i]) + if err != nil { + return nil, err + } + r.fields[i] = &sppb.StructType_Field{ + Name: columnNames[i], + Type: typ, + } + r.vals[i] = val + } + return &r, nil +} + +// Size is the number of columns in the row. +func (r *Row) Size() int { + return len(r.fields) +} + +// ColumnName returns the name of column i, or empty string for invalid column. +func (r *Row) ColumnName(i int) string { + if i < 0 || i >= len(r.fields) { + return "" + } + return r.fields[i].Name +} + +// ColumnIndex returns the index of the column with the given name. The +// comparison is case-sensitive. +func (r *Row) ColumnIndex(name string) (int, error) { + found := false + var index int + if len(r.vals) != len(r.fields) { + return 0, errFieldsMismatchVals(r) + } + for i, f := range r.fields { + if f == nil { + return 0, errNilColType(i) + } + if name == f.Name { + if found { + return 0, errDupColName(name) + } + found = true + index = i + } + } + if !found { + return 0, errColNotFound(name) + } + return index, nil +} + +// ColumnNames returns all column names of the row. +func (r *Row) ColumnNames() []string { + var n []string + for _, c := range r.fields { + n = append(n, c.Name) + } + return n +} + +// errColIdxOutOfRange returns error for requested column index is out of the +// range of the target Row's columns. +func errColIdxOutOfRange(i int, r *Row) error { + return spannerErrorf(codes.OutOfRange, "column index %d out of range [0,%d)", i, len(r.vals)) +} + +// errDecodeColumn returns error for not being able to decode a indexed column. +func errDecodeColumn(i int, err error) error { + if err == nil { + return nil + } + se, ok := toSpannerError(err).(*Error) + if !ok { + return spannerErrorf(codes.InvalidArgument, "failed to decode column %v, error = <%v>", i, err) + } + se.decorate(fmt.Sprintf("failed to decode column %v", i)) + return se +} + +// errFieldsMismatchVals returns error for field count isn't equal to value count in a Row. +func errFieldsMismatchVals(r *Row) error { + return spannerErrorf(codes.FailedPrecondition, "row has different number of fields(%v) and values(%v)", + len(r.fields), len(r.vals)) +} + +// errNilColType returns error for column type for column i being nil in the row. +func errNilColType(i int) error { + return spannerErrorf(codes.FailedPrecondition, "column(%v)'s type is nil", i) +} + +// Column fetches the value from the ith column, decoding it into ptr. +// See the Row documentation for the list of acceptable argument types. +// see Client.ReadWriteTransaction for an example. +func (r *Row) Column(i int, ptr interface{}) error { + if len(r.vals) != len(r.fields) { + return errFieldsMismatchVals(r) + } + if i < 0 || i >= len(r.fields) { + return errColIdxOutOfRange(i, r) + } + if r.fields[i] == nil { + return errNilColType(i) + } + if err := decodeValue(r.vals[i], r.fields[i].Type, ptr); err != nil { + return errDecodeColumn(i, err) + } + return nil +} + +// errDupColName returns error for duplicated column name in the same row. +func errDupColName(n string) error { + return spannerErrorf(codes.FailedPrecondition, "ambiguous column name %q", n) +} + +// errColNotFound returns error for not being able to find a named column. +func errColNotFound(n string) error { + return spannerErrorf(codes.NotFound, "column %q not found", n) +} + +// ColumnByName fetches the value from the named column, decoding it into ptr. +// See the Row documentation for the list of acceptable argument types. +func (r *Row) ColumnByName(name string, ptr interface{}) error { + index, err := r.ColumnIndex(name) + if err != nil { + return err + } + return r.Column(index, ptr) +} + +// errNumOfColValue returns error for providing wrong number of values to Columns. +func errNumOfColValue(n int, r *Row) error { + return spannerErrorf(codes.InvalidArgument, + "Columns(): number of arguments (%d) does not match row size (%d)", n, len(r.vals)) +} + +// Columns fetches all the columns in the row at once. +// +// The value of the kth column will be decoded into the kth argument to Columns. See +// Row for the list of acceptable argument types. The number of arguments must be +// equal to the number of columns. Pass nil to specify that a column should be +// ignored. +func (r *Row) Columns(ptrs ...interface{}) error { + if len(ptrs) != len(r.vals) { + return errNumOfColValue(len(ptrs), r) + } + if len(r.vals) != len(r.fields) { + return errFieldsMismatchVals(r) + } + for i, p := range ptrs { + if p == nil { + continue + } + if err := r.Column(i, p); err != nil { + return err + } + } + return nil +} + +// errToStructArgType returns error for p not having the correct data type(pointer to Go struct) to +// be the argument of Row.ToStruct. +func errToStructArgType(p interface{}) error { + return spannerErrorf(codes.InvalidArgument, "ToStruct(): type %T is not a valid pointer to Go struct", p) +} + +// ToStruct fetches the columns in a row into the fields of a struct. +// The rules for mapping a row's columns into a struct's exported fields +// are: +// +// 1. If a field has a `spanner: "column_name"` tag, then decode column +// 'column_name' into the field. A special case is the `spanner: "-"` +// tag, which instructs ToStruct to ignore the field during decoding. +// +// 2. Otherwise, if the name of a field matches the name of a column (ignoring case), +// decode the column into the field. +// +// The fields of the destination struct can be of any type that is acceptable +// to spanner.Row.Column. +// +// Slice and pointer fields will be set to nil if the source column is NULL, and a +// non-nil value if the column is not NULL. To decode NULL values of other types, use +// one of the spanner.NullXXX types as the type of the destination field. +// +// If ToStruct returns an error, the contents of p are undefined. Some fields may +// have been successfully populated, while others were not; you should not use any of +// the fields. +func (r *Row) ToStruct(p interface{}) error { + // Check if p is a pointer to a struct + if t := reflect.TypeOf(p); t == nil || t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct { + return errToStructArgType(p) + } + if len(r.vals) != len(r.fields) { + return errFieldsMismatchVals(r) + } + // Call decodeStruct directly to decode the row as a typed proto.ListValue. + return decodeStruct( + &sppb.StructType{Fields: r.fields}, + &proto3.ListValue{Values: r.vals}, + p, + ) +} diff --git a/vendor/cloud.google.com/go/spanner/row_test.go b/vendor/cloud.google.com/go/spanner/row_test.go new file mode 100644 index 0000000000..2a3ab8fd42 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/row_test.go @@ -0,0 +1,1636 @@ +/* +Copyright 2017 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 spanner + +import ( + "encoding/base64" + "reflect" + "strconv" + "strings" + "testing" + "time" + + "cloud.google.com/go/civil" + proto "github.com/golang/protobuf/proto" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +var ( + tm = time.Date(2016, 11, 15, 0, 0, 0, 0, time.UTC) + dt, _ = civil.ParseDate("2016-11-15") + // row contains a column for each unique Cloud Spanner type. + row = Row{ + []*sppb.StructType_Field{ + // STRING / STRING ARRAY + {Name: "STRING", Type: stringType()}, + {Name: "NULL_STRING", Type: stringType()}, + {Name: "STRING_ARRAY", Type: listType(stringType())}, + {Name: "NULL_STRING_ARRAY", Type: listType(stringType())}, + // BYTES / BYTES ARRAY + {Name: "BYTES", Type: bytesType()}, + {Name: "NULL_BYTES", Type: bytesType()}, + {Name: "BYTES_ARRAY", Type: listType(bytesType())}, + {Name: "NULL_BYTES_ARRAY", Type: listType(bytesType())}, + // INT64 / INT64 ARRAY + {Name: "INT64", Type: intType()}, + {Name: "NULL_INT64", Type: intType()}, + {Name: "INT64_ARRAY", Type: listType(intType())}, + {Name: "NULL_INT64_ARRAY", Type: listType(intType())}, + // BOOL / BOOL ARRAY + {Name: "BOOL", Type: boolType()}, + {Name: "NULL_BOOL", Type: boolType()}, + {Name: "BOOL_ARRAY", Type: listType(boolType())}, + {Name: "NULL_BOOL_ARRAY", Type: listType(boolType())}, + // FLOAT64 / FLOAT64 ARRAY + {Name: "FLOAT64", Type: floatType()}, + {Name: "NULL_FLOAT64", Type: floatType()}, + {Name: "FLOAT64_ARRAY", Type: listType(floatType())}, + {Name: "NULL_FLOAT64_ARRAY", Type: listType(floatType())}, + // TIMESTAMP / TIMESTAMP ARRAY + {Name: "TIMESTAMP", Type: timeType()}, + {Name: "NULL_TIMESTAMP", Type: timeType()}, + {Name: "TIMESTAMP_ARRAY", Type: listType(timeType())}, + {Name: "NULL_TIMESTAMP_ARRAY", Type: listType(timeType())}, + // DATE / DATE ARRAY + {Name: "DATE", Type: dateType()}, + {Name: "NULL_DATE", Type: dateType()}, + {Name: "DATE_ARRAY", Type: listType(dateType())}, + {Name: "NULL_DATE_ARRAY", Type: listType(dateType())}, + + // STRUCT ARRAY + { + Name: "STRUCT_ARRAY", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ), + ), + }, + { + Name: "NULL_STRUCT_ARRAY", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ), + ), + }, + }, + []*proto3.Value{ + // STRING / STRING ARRAY + stringProto("value"), + nullProto(), + listProto(stringProto("value1"), nullProto(), stringProto("value3")), + nullProto(), + // BYTES / BYTES ARRAY + bytesProto([]byte("value")), + nullProto(), + listProto(bytesProto([]byte("value1")), nullProto(), bytesProto([]byte("value3"))), + nullProto(), + // INT64 / INT64 ARRAY + intProto(17), + nullProto(), + listProto(intProto(1), intProto(2), nullProto()), + nullProto(), + // BOOL / BOOL ARRAY + boolProto(true), + nullProto(), + listProto(nullProto(), boolProto(true), boolProto(false)), + nullProto(), + // FLOAT64 / FLOAT64 ARRAY + floatProto(1.7), + nullProto(), + listProto(nullProto(), nullProto(), floatProto(1.7)), + nullProto(), + // TIMESTAMP / TIMESTAMP ARRAY + timeProto(tm), + nullProto(), + listProto(nullProto(), timeProto(tm)), + nullProto(), + // DATE / DATE ARRAY + dateProto(dt), + nullProto(), + listProto(nullProto(), dateProto(dt)), + nullProto(), + // STRUCT ARRAY + listProto( + nullProto(), + listProto(intProto(3), floatProto(33.3), stringProto("three")), + nullProto(), + ), + nullProto(), + }, + } +) + +// Test helpers for getting column values. +func TestColumnValues(t *testing.T) { + vals := []interface{}{} + wantVals := []interface{}{} + // Test getting column values. + for i, wants := range [][]interface{}{ + // STRING / STRING ARRAY + {"value", NullString{"value", true}}, + {NullString{}}, + {[]NullString{{"value1", true}, {}, {"value3", true}}}, + {[]NullString(nil)}, + // BYTES / BYTES ARRAY + {[]byte("value")}, + {[]byte(nil)}, + {[][]byte{[]byte("value1"), nil, []byte("value3")}}, + {[][]byte(nil)}, + // INT64 / INT64 ARRAY + {int64(17), NullInt64{17, true}}, + {NullInt64{}}, + {[]NullInt64{{1, true}, {2, true}, {}}}, + {[]NullInt64(nil)}, + // BOOL / BOOL ARRAY + {true, NullBool{true, true}}, + {NullBool{}}, + {[]NullBool{{}, {true, true}, {false, true}}}, + {[]NullBool(nil)}, + // FLOAT64 / FLOAT64 ARRAY + {1.7, NullFloat64{1.7, true}}, + {NullFloat64{}}, + {[]NullFloat64{{}, {}, {1.7, true}}}, + {[]NullFloat64(nil)}, + // TIMESTAMP / TIMESTAMP ARRAY + {tm, NullTime{tm, true}}, + {NullTime{}}, + {[]NullTime{{}, {tm, true}}}, + {[]NullTime(nil)}, + // DATE / DATE ARRAY + {dt, NullDate{dt, true}}, + {NullDate{}}, + {[]NullDate{{}, {dt, true}}}, + {[]NullDate(nil)}, + // STRUCT ARRAY + { + []*struct { + Col1 NullInt64 + Col2 NullFloat64 + Col3 string + }{ + nil, + + { + NullInt64{3, true}, + NullFloat64{33.3, true}, + "three", + }, + nil, + }, + []NullRow{ + {}, + { + Row: Row{ + fields: []*sppb.StructType_Field{ + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + }, + vals: []*proto3.Value{ + intProto(3), + floatProto(33.3), + stringProto("three"), + }, + }, + Valid: true, + }, + {}, + }, + }, + { + []*struct { + Col1 NullInt64 + Col2 NullFloat64 + Col3 string + }(nil), + []NullRow(nil), + }, + } { + for j, want := range wants { + // Prepare Value vector to test Row.Columns. + if j == 0 { + vals = append(vals, reflect.New(reflect.TypeOf(want)).Interface()) + wantVals = append(wantVals, want) + } + // Column + gotp := reflect.New(reflect.TypeOf(want)) + err := row.Column(i, gotp.Interface()) + if err != nil { + t.Errorf("\t row.Column(%v, %T) returns error: %v, want nil", i, gotp.Interface(), err) + } + if got := reflect.Indirect(gotp).Interface(); !testEqual(got, want) { + t.Errorf("\t row.Column(%v, %T) retrives %v, want %v", i, gotp.Interface(), got, want) + } + // ColumnByName + gotp = reflect.New(reflect.TypeOf(want)) + err = row.ColumnByName(row.fields[i].Name, gotp.Interface()) + if err != nil { + t.Errorf("\t row.ColumnByName(%v, %T) returns error: %v, want nil", row.fields[i].Name, gotp.Interface(), err) + } + if got := reflect.Indirect(gotp).Interface(); !testEqual(got, want) { + t.Errorf("\t row.ColumnByName(%v, %T) retrives %v, want %v", row.fields[i].Name, gotp.Interface(), got, want) + } + } + } + // Test Row.Columns. + if err := row.Columns(vals...); err != nil { + t.Errorf("row.Columns() returns error: %v, want nil", err) + } + for i, want := range wantVals { + if got := reflect.Indirect(reflect.ValueOf(vals[i])).Interface(); !testEqual(got, want) { + t.Errorf("\t got %v(%T) for column[%v], want %v(%T)", got, got, row.fields[i].Name, want, want) + } + } +} + +// Test decoding into nil destination. +func TestNilDst(t *testing.T) { + for i, test := range []struct { + r *Row + dst interface{} + wantErr error + structDst interface{} + wantToStructErr error + }{ + { + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: stringType()}, + }, + []*proto3.Value{stringProto("value")}, + }, + nil, + errDecodeColumn(0, errNilDst(nil)), + nil, + errToStructArgType(nil), + }, + { + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: stringType()}, + }, + []*proto3.Value{stringProto("value")}, + }, + (*string)(nil), + errDecodeColumn(0, errNilDst((*string)(nil))), + (*struct{ STRING string })(nil), + errNilDst((*struct{ STRING string })(nil)), + }, + { + &Row{ + []*sppb.StructType_Field{ + { + Name: "Col0", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + ), + ), + }, + }, + []*proto3.Value{listProto( + listProto(intProto(3), floatProto(33.3)), + )}, + }, + (*[]*struct { + Col1 int + Col2 float64 + })(nil), + errDecodeColumn(0, errNilDst((*[]*struct { + Col1 int + Col2 float64 + })(nil))), + (*struct { + StructArray []*struct { + Col1 int + Col2 float64 + } `spanner:"STRUCT_ARRAY"` + })(nil), + errNilDst((*struct { + StructArray []*struct { + Col1 int + Col2 float64 + } `spanner:"STRUCT_ARRAY"` + })(nil)), + }, + } { + if gotErr := test.r.Column(0, test.dst); !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: test.r.Column() returns error %v, want %v", i, gotErr, test.wantErr) + } + if gotErr := test.r.ColumnByName("Col0", test.dst); !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: test.r.ColumnByName() returns error %v, want %v", i, gotErr, test.wantErr) + } + // Row.Columns(T) should return nil on T == nil, otherwise, it should return test.wantErr. + wantColumnsErr := test.wantErr + if test.dst == nil { + wantColumnsErr = nil + } + if gotErr := test.r.Columns(test.dst); !testEqual(gotErr, wantColumnsErr) { + t.Errorf("%v: test.r.Columns() returns error %v, want %v", i, gotErr, wantColumnsErr) + } + if gotErr := test.r.ToStruct(test.structDst); !testEqual(gotErr, test.wantToStructErr) { + t.Errorf("%v: test.r.ToStruct() returns error %v, want %v", i, gotErr, test.wantToStructErr) + } + } +} + +// Test decoding NULL columns using Go types that don't support NULL. +func TestNullTypeErr(t *testing.T) { + var tm time.Time + ntoi := func(n string) int { + for i, f := range row.fields { + if f.Name == n { + return i + } + } + t.Errorf("cannot find column name %q in row", n) + return 0 + } + for _, test := range []struct { + colName string + dst interface{} + }{ + { + "NULL_STRING", + proto.String(""), + }, + { + "NULL_INT64", + proto.Int64(0), + }, + { + "NULL_BOOL", + proto.Bool(false), + }, + { + "NULL_FLOAT64", + proto.Float64(0.0), + }, + { + "NULL_TIMESTAMP", + &tm, + }, + { + "NULL_DATE", + &dt, + }, + } { + wantErr := errDecodeColumn(ntoi(test.colName), errDstNotForNull(test.dst)) + if gotErr := row.ColumnByName(test.colName, test.dst); !testEqual(gotErr, wantErr) { + t.Errorf("row.ColumnByName(%v) returns error %v, want %v", test.colName, gotErr, wantErr) + } + } +} + +// Test using wrong destination type in column decoders. +func TestColumnTypeErr(t *testing.T) { + // badDst cannot hold any of the column values. + badDst := &struct{}{} + for i, f := range row.fields { // For each of the columns, try to decode it into badDst. + tc := f.Type.Code + var etc sppb.TypeCode + if strings.Contains(f.Name, "ARRAY") { + etc = f.Type.ArrayElementType.Code + } + wantErr := errDecodeColumn(i, errTypeMismatch(tc, etc, badDst)) + if gotErr := row.Column(i, badDst); !testEqual(gotErr, wantErr) { + t.Errorf("Column(%v): decoding into destination with wrong type %T returns error %v, want %v", + i, badDst, gotErr, wantErr) + } + if gotErr := row.ColumnByName(f.Name, badDst); !testEqual(gotErr, wantErr) { + t.Errorf("ColumnByName(%v): decoding into destination with wrong type %T returns error %v, want %v", + f.Name, badDst, gotErr, wantErr) + } + } + wantErr := errDecodeColumn(1, errTypeMismatch(sppb.TypeCode_STRING, sppb.TypeCode_TYPE_CODE_UNSPECIFIED, badDst)) + // badDst is used to receive column 1. + vals := []interface{}{nil, badDst} // Row.Column() is expected to fail at column 1. + // Skip decoding the rest columns by providing nils as the destinations. + for i := 2; i < len(row.fields); i++ { + vals = append(vals, nil) + } + if gotErr := row.Columns(vals...); !testEqual(gotErr, wantErr) { + t.Errorf("Columns(): decoding column 1 with wrong type %T returns error %v, want %v", + badDst, gotErr, wantErr) + } +} + +// Test the handling of invalid column decoding requests which cannot be mapped to correct column(s). +func TestInvalidColumnRequest(t *testing.T) { + for _, test := range []struct { + desc string + f func() error + wantErr error + }{ + { + "Request column index is out of range", + func() error { + return row.Column(10000, &struct{}{}) + }, + errColIdxOutOfRange(10000, &row), + }, + { + "Cannot find the named column", + func() error { + return row.ColumnByName("string", &struct{}{}) + }, + errColNotFound("string"), + }, + { + "Not enough arguments to call row.Columns()", + func() error { + return row.Columns(nil, nil) + }, + errNumOfColValue(2, &row), + }, + { + "Call ColumnByName on row with duplicated column names", + func() error { + var s string + r := &Row{ + []*sppb.StructType_Field{ + {Name: "Val", Type: stringType()}, + {Name: "Val", Type: stringType()}, + }, + []*proto3.Value{stringProto("value1"), stringProto("value2")}, + } + return r.ColumnByName("Val", &s) + }, + errDupColName("Val"), + }, + { + "Call ToStruct on row with duplicated column names", + func() error { + s := &struct { + Val string + }{} + r := &Row{ + []*sppb.StructType_Field{ + {Name: "Val", Type: stringType()}, + {Name: "Val", Type: stringType()}, + }, + []*proto3.Value{stringProto("value1"), stringProto("value2")}, + } + return r.ToStruct(s) + }, + errDupSpannerField("Val", &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + {Name: "Val", Type: stringType()}, + {Name: "Val", Type: stringType()}, + }, + }), + }, + { + "Call ToStruct on a row with unnamed field", + func() error { + s := &struct { + Val string + }{} + r := &Row{ + []*sppb.StructType_Field{ + {Name: "", Type: stringType()}, + }, + []*proto3.Value{stringProto("value1")}, + } + return r.ToStruct(s) + }, + errUnnamedField(&sppb.StructType{Fields: []*sppb.StructType_Field{ + {Name: "", Type: stringType()}, + }}, 0), + }, + } { + if gotErr := test.f(); !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: test.f() returns error %v, want %v", test.desc, gotErr, test.wantErr) + } + } +} + +// Test decoding the row with row.ToStruct into an invalid destination. +func TestToStructInvalidDst(t *testing.T) { + for _, test := range []struct { + desc string + dst interface{} + wantErr error + }{ + { + "Decode row as STRUCT into int32", + proto.Int(1), + errToStructArgType(proto.Int(1)), + }, + { + "Decode row as STRUCT to nil Go struct", + (*struct{})(nil), + errNilDst((*struct{})(nil)), + }, + { + "Decode row as STRUCT to Go struct with duplicated fields for the PK column", + &struct { + PK1 string `spanner:"STRING"` + PK2 string `spanner:"STRING"` + }{}, + errNoOrDupGoField(&struct { + PK1 string `spanner:"STRING"` + PK2 string `spanner:"STRING"` + }{}, "STRING"), + }, + { + "Decode row as STRUCT to Go struct with no field for the PK column", + &struct { + PK1 string `spanner:"_STRING"` + }{}, + errNoOrDupGoField(&struct { + PK1 string `spanner:"_STRING"` + }{}, "STRING"), + }, + { + "Decode row as STRUCT to Go struct with wrong type for the PK column", + &struct { + PK1 int64 `spanner:"STRING"` + }{}, + errDecodeStructField(&sppb.StructType{Fields: row.fields}, "STRING", + errTypeMismatch(sppb.TypeCode_STRING, sppb.TypeCode_TYPE_CODE_UNSPECIFIED, proto.Int64(0))), + }, + } { + if gotErr := row.ToStruct(test.dst); !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: decoding:\ngot %v\nwant %v", test.desc, gotErr, test.wantErr) + } + } +} + +// Test decoding a broken row. +func TestBrokenRow(t *testing.T) { + for i, test := range []struct { + row *Row + dst interface{} + wantErr error + }{ + { + // A row with no field. + &Row{ + []*sppb.StructType_Field{}, + []*proto3.Value{stringProto("value")}, + }, + &NullString{"value", true}, + errFieldsMismatchVals(&Row{ + []*sppb.StructType_Field{}, + []*proto3.Value{stringProto("value")}, + }), + }, + { + // A row with nil field. + &Row{ + []*sppb.StructType_Field{nil}, + []*proto3.Value{stringProto("value")}, + }, + &NullString{"value", true}, + errNilColType(0), + }, + { + // Field is not nil, but its type is nil. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: nil}, + }, + []*proto3.Value{listProto(stringProto("value1"), stringProto("value2"))}, + }, + &[]NullString{}, + errDecodeColumn(0, errNilSpannerType()), + }, + { + // Field is not nil, field type is not nil, but it is an array and its array element type is nil. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: &sppb.Type{Code: sppb.TypeCode_ARRAY}}, + }, + []*proto3.Value{listProto(stringProto("value1"), stringProto("value2"))}, + }, + &[]NullString{}, + errDecodeColumn(0, errNilArrElemType(&sppb.Type{Code: sppb.TypeCode_ARRAY})), + }, + { + // Field specifies valid type, value is nil. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: intType()}, + }, + []*proto3.Value{nil}, + }, + &NullInt64{1, true}, + errDecodeColumn(0, errNilSrc()), + }, + { + // Field specifies INT64 type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: intType()}, + }, + []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}}, + }, + &NullInt64{1, true}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")), + }, + { + // Field specifies INT64 type, but value is for Number type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: intType()}, + }, + []*proto3.Value{floatProto(1.0)}, + }, + &NullInt64{1, true}, + errDecodeColumn(0, errSrcVal(floatProto(1.0), "String")), + }, + { + // Field specifies INT64 type, but value is wrongly encoded. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: intType()}, + }, + []*proto3.Value{stringProto("&1")}, + }, + proto.Int64(0), + errDecodeColumn(0, errBadEncoding(stringProto("&1"), func() error { + _, err := strconv.ParseInt("&1", 10, 64) + return err + }())), + }, + { + // Field specifies INT64 type, but value is wrongly encoded. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: intType()}, + }, + []*proto3.Value{stringProto("&1")}, + }, + &NullInt64{}, + errDecodeColumn(0, errBadEncoding(stringProto("&1"), func() error { + _, err := strconv.ParseInt("&1", 10, 64) + return err + }())), + }, + { + // Field specifies STRING type, but value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: stringType()}, + }, + []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}}, + }, + &NullString{"value", true}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")), + }, + { + // Field specifies STRING type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: stringType()}, + }, + []*proto3.Value{listProto(stringProto("value"))}, + }, + &NullString{"value", true}, + errDecodeColumn(0, errSrcVal(listProto(stringProto("value")), "String")), + }, + { + // Field specifies FLOAT64 type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: floatType()}, + }, + []*proto3.Value{{Kind: (*proto3.Value_NumberValue)(nil)}}, + }, + &NullFloat64{1.0, true}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_NumberValue)(nil)}, "Number")), + }, + { + // Field specifies FLOAT64 type, but value is for BOOL type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: floatType()}, + }, + []*proto3.Value{boolProto(true)}, + }, + &NullFloat64{1.0, true}, + errDecodeColumn(0, errSrcVal(boolProto(true), "Number")), + }, + { + // Field specifies FLOAT64 type, but value is wrongly encoded. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: floatType()}, + }, + []*proto3.Value{stringProto("nan")}, + }, + &NullFloat64{}, + errDecodeColumn(0, errUnexpectedNumStr("nan")), + }, + { + // Field specifies FLOAT64 type, but value is wrongly encoded. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: floatType()}, + }, + []*proto3.Value{stringProto("nan")}, + }, + proto.Float64(0), + errDecodeColumn(0, errUnexpectedNumStr("nan")), + }, + { + // Field specifies BYTES type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: bytesType()}, + }, + []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}}, + }, + &[]byte{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")), + }, + { + // Field specifies BYTES type, but value is for BOOL type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: bytesType()}, + }, + []*proto3.Value{boolProto(false)}, + }, + &[]byte{}, + errDecodeColumn(0, errSrcVal(boolProto(false), "String")), + }, + { + // Field specifies BYTES type, but value is wrongly encoded. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: bytesType()}, + }, + []*proto3.Value{stringProto("&&")}, + }, + &[]byte{}, + errDecodeColumn(0, errBadEncoding(stringProto("&&"), func() error { + _, err := base64.StdEncoding.DecodeString("&&") + return err + }())), + }, + { + // Field specifies BOOL type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: boolType()}, + }, + []*proto3.Value{{Kind: (*proto3.Value_BoolValue)(nil)}}, + }, + &NullBool{false, true}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_BoolValue)(nil)}, "Bool")), + }, + { + // Field specifies BOOL type, but value is for STRING type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: boolType()}, + }, + []*proto3.Value{stringProto("false")}, + }, + &NullBool{false, true}, + errDecodeColumn(0, errSrcVal(stringProto("false"), "Bool")), + }, + { + // Field specifies TIMESTAMP type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: timeType()}, + }, + []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}}, + }, + &NullTime{time.Now(), true}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")), + }, + { + // Field specifies TIMESTAMP type, but value is for BOOL type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: timeType()}, + }, + []*proto3.Value{boolProto(false)}, + }, + &NullTime{time.Now(), true}, + errDecodeColumn(0, errSrcVal(boolProto(false), "String")), + }, + { + // Field specifies TIMESTAMP type, but value is invalid timestamp. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: timeType()}, + }, + []*proto3.Value{stringProto("junk")}, + }, + &NullTime{time.Now(), true}, + errDecodeColumn(0, errBadEncoding(stringProto("junk"), func() error { + _, err := time.Parse(time.RFC3339Nano, "junk") + return err + }())), + }, + { + // Field specifies DATE type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: dateType()}, + }, + []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}}, + }, + &NullDate{civil.Date{}, true}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")), + }, + { + // Field specifies DATE type, but value is for BOOL type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: dateType()}, + }, + []*proto3.Value{boolProto(false)}, + }, + &NullDate{civil.Date{}, true}, + errDecodeColumn(0, errSrcVal(boolProto(false), "String")), + }, + { + // Field specifies DATE type, but value is invalid timestamp. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: dateType()}, + }, + []*proto3.Value{stringProto("junk")}, + }, + &NullDate{civil.Date{}, true}, + errDecodeColumn(0, errBadEncoding(stringProto("junk"), func() error { + _, err := civil.ParseDate("junk") + return err + }())), + }, + + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(intType())}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[]NullInt64{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(intType())}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]NullInt64{}, + errDecodeColumn(0, errNilListValue("INT64")), + }, + { + // Field specifies ARRAY type, but value is for BYTES type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(intType())}, + }, + []*proto3.Value{bytesProto([]byte("value"))}, + }, + &[]NullInt64{}, + errDecodeColumn(0, errSrcVal(bytesProto([]byte("value")), "List")), + }, + { + // Field specifies ARRAY type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(intType())}, + }, + []*proto3.Value{listProto(boolProto(true))}, + }, + &[]NullInt64{}, + errDecodeColumn(0, errDecodeArrayElement(0, boolProto(true), + "INT64", errSrcVal(boolProto(true), "String"))), + }, + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(stringType())}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[]NullString{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(stringType())}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]NullString{}, + errDecodeColumn(0, errNilListValue("STRING")), + }, + { + // Field specifies ARRAY type, but value is for BOOL type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(stringType())}, + }, + []*proto3.Value{boolProto(true)}, + }, + &[]NullString{}, + errDecodeColumn(0, errSrcVal(boolProto(true), "List")), + }, + { + // Field specifies ARRAY type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(stringType())}, + }, + []*proto3.Value{listProto(boolProto(true))}, + }, + &[]NullString{}, + errDecodeColumn(0, errDecodeArrayElement(0, boolProto(true), + "STRING", errSrcVal(boolProto(true), "String"))), + }, + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(floatType())}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[]NullFloat64{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(floatType())}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]NullFloat64{}, + errDecodeColumn(0, errNilListValue("FLOAT64")), + }, + { + // Field specifies ARRAY type, but value is for STRING type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(floatType())}, + }, + []*proto3.Value{stringProto("value")}, + }, + &[]NullFloat64{}, + errDecodeColumn(0, errSrcVal(stringProto("value"), "List")), + }, + { + // Field specifies ARRAY type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(floatType())}, + }, + []*proto3.Value{listProto(boolProto(true))}, + }, + &[]NullFloat64{}, + errDecodeColumn(0, errDecodeArrayElement(0, boolProto(true), + "FLOAT64", errSrcVal(boolProto(true), "Number"))), + }, + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(bytesType())}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[][]byte{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(bytesType())}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[][]byte{}, + errDecodeColumn(0, errNilListValue("BYTES")), + }, + { + // Field specifies ARRAY type, but value is for FLOAT64 type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(bytesType())}, + }, + []*proto3.Value{floatProto(1.0)}, + }, + &[][]byte{}, + errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")), + }, + { + // Field specifies ARRAY type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(bytesType())}, + }, + []*proto3.Value{listProto(floatProto(1.0))}, + }, + &[][]byte{}, + errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0), + "BYTES", errSrcVal(floatProto(1.0), "String"))), + }, + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(boolType())}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[]NullBool{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(boolType())}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]NullBool{}, + errDecodeColumn(0, errNilListValue("BOOL")), + }, + { + // Field specifies ARRAY type, but value is for FLOAT64 type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(boolType())}, + }, + []*proto3.Value{floatProto(1.0)}, + }, + &[]NullBool{}, + errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")), + }, + { + // Field specifies ARRAY type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(boolType())}, + }, + []*proto3.Value{listProto(floatProto(1.0))}, + }, + &[]NullBool{}, + errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0), + "BOOL", errSrcVal(floatProto(1.0), "Bool"))), + }, + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(timeType())}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[]NullTime{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(timeType())}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]NullTime{}, + errDecodeColumn(0, errNilListValue("TIMESTAMP")), + }, + { + // Field specifies ARRAY type, but value is for FLOAT64 type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(timeType())}, + }, + []*proto3.Value{floatProto(1.0)}, + }, + &[]NullTime{}, + errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")), + }, + { + // Field specifies ARRAY type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(timeType())}, + }, + []*proto3.Value{listProto(floatProto(1.0))}, + }, + &[]NullTime{}, + errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0), + "TIMESTAMP", errSrcVal(floatProto(1.0), "String"))), + }, + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(dateType())}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[]NullDate{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(dateType())}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]NullDate{}, + errDecodeColumn(0, errNilListValue("DATE")), + }, + { + // Field specifies ARRAY type, but value is for FLOAT64 type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(dateType())}, + }, + []*proto3.Value{floatProto(1.0)}, + }, + &[]NullDate{}, + errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")), + }, + { + // Field specifies ARRAY type, but value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(dateType())}, + }, + []*proto3.Value{listProto(floatProto(1.0))}, + }, + &[]NullDate{}, + errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0), + "DATE", errSrcVal(floatProto(1.0), "String"))), + }, + { + // Field specifies ARRAY type, value is having a nil Kind. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ))}, + }, + []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}}, + }, + &[]*struct { + Col1 int64 + Col2 float64 + Col3 string + }{}, + errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + {Name: "Col0", Type: listType(structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ))}, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]*struct { + Col1 int64 + Col2 float64 + Col3 string + }{}, + errDecodeColumn(0, errNilListValue("STRUCT")), + }, + { + // Field specifies ARRAY type, value is having a nil ListValue. + &Row{ + []*sppb.StructType_Field{ + { + Name: "Col0", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ), + ), + }, + }, + []*proto3.Value{{Kind: &proto3.Value_ListValue{}}}, + }, + &[]NullRow{}, + errDecodeColumn(0, errNilListValue("STRUCT")), + }, + { + // Field specifies ARRAY type, value is for BYTES type. + &Row{ + []*sppb.StructType_Field{ + { + Name: "Col0", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ), + ), + }, + }, + []*proto3.Value{bytesProto([]byte("value"))}, + }, + &[]*struct { + Col1 int64 + Col2 float64 + Col3 string + }{}, + errDecodeColumn(0, errSrcVal(bytesProto([]byte("value")), "List")), + }, + { + // Field specifies ARRAY type, value is for BYTES type. + &Row{ + []*sppb.StructType_Field{ + { + Name: "Col0", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ), + ), + }, + }, + []*proto3.Value{listProto(bytesProto([]byte("value")))}, + }, + &[]NullRow{}, + errDecodeColumn(0, errNotStructElement(0, bytesProto([]byte("value")))), + }, + { + // Field specifies ARRAY type, value is for ARRAY type. + &Row{ + []*sppb.StructType_Field{ + { + Name: "Col0", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ), + ), + }, + }, + []*proto3.Value{listProto(bytesProto([]byte("value")))}, + }, + &[]*struct { + Col1 int64 + Col2 float64 + Col3 string + }{}, + errDecodeColumn(0, errDecodeArrayElement(0, bytesProto([]byte("value")), + "STRUCT", errSrcVal(bytesProto([]byte("value")), "List"))), + }, + { + // Field specifies ARRAY, but is having nil StructType. + &Row{ + []*sppb.StructType_Field{ + { + Name: "Col0", Type: listType(&sppb.Type{Code: sppb.TypeCode_STRUCT}), + }, + }, + []*proto3.Value{listProto(listProto(intProto(1), floatProto(2.0), stringProto("3")))}, + }, + &[]*struct { + Col1 int64 + Col2 float64 + Col3 string + }{}, + errDecodeColumn(0, errDecodeArrayElement(0, listProto(intProto(1), floatProto(2.0), stringProto("3")), + "STRUCT", errNilSpannerStructType())), + }, + { + // Field specifies ARRAY, but the second struct value is for BOOL type instead of FLOAT64. + &Row{ + []*sppb.StructType_Field{ + { + Name: "Col0", + Type: listType( + structType( + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + ), + ), + }, + }, + []*proto3.Value{listProto(listProto(intProto(1), boolProto(true), stringProto("3")))}, + }, + &[]*struct { + Col1 int64 + Col2 float64 + Col3 string + }{}, + errDecodeColumn( + 0, + errDecodeArrayElement( + 0, listProto(intProto(1), boolProto(true), stringProto("3")), "STRUCT", + errDecodeStructField( + &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + mkField("Col1", intType()), + mkField("Col2", floatType()), + mkField("Col3", stringType()), + }, + }, + "Col2", + errSrcVal(boolProto(true), "Number"), + ), + ), + ), + }, + } { + if gotErr := test.row.Column(0, test.dst); !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: test.row.Column(0) got error %v, want %v", i, gotErr, test.wantErr) + } + if gotErr := test.row.ColumnByName("Col0", test.dst); !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: test.row.ColumnByName(%q) got error %v, want %v", i, "Col0", gotErr, test.wantErr) + } + if gotErr := test.row.Columns(test.dst); !testEqual(gotErr, test.wantErr) { + t.Errorf("%v: test.row.Columns(%T) got error %v, want %v", i, test.dst, gotErr, test.wantErr) + } + } +} + +// Test Row.ToStruct(). +func TestToStruct(t *testing.T) { + s := []struct { + // STRING / STRING ARRAY + PrimaryKey string `spanner:"STRING"` + NullString NullString `spanner:"NULL_STRING"` + StringArray []NullString `spanner:"STRING_ARRAY"` + NullStringArray []NullString `spanner:"NULL_STRING_ARRAY"` + // BYTES / BYTES ARRAY + Bytes []byte `spanner:"BYTES"` + NullBytes []byte `spanner:"NULL_BYTES"` + BytesArray [][]byte `spanner:"BYTES_ARRAY"` + NullBytesArray [][]byte `spanner:"NULL_BYTES_ARRAY"` + // INT64 / INT64 ARRAY + Int64 int64 `spanner:"INT64"` + NullInt64 NullInt64 `spanner:"NULL_INT64"` + Int64Array []NullInt64 `spanner:"INT64_ARRAY"` + NullInt64Array []NullInt64 `spanner:"NULL_INT64_ARRAY"` + // BOOL / BOOL ARRAY + Bool bool `spanner:"BOOL"` + NullBool NullBool `spanner:"NULL_BOOL"` + BoolArray []NullBool `spanner:"BOOL_ARRAY"` + NullBoolArray []NullBool `spanner:"NULL_BOOL_ARRAY"` + // FLOAT64 / FLOAT64 ARRAY + Float64 float64 `spanner:"FLOAT64"` + NullFloat64 NullFloat64 `spanner:"NULL_FLOAT64"` + Float64Array []NullFloat64 `spanner:"FLOAT64_ARRAY"` + NullFloat64Array []NullFloat64 `spanner:"NULL_FLOAT64_ARRAY"` + // TIMESTAMP / TIMESTAMP ARRAY + Timestamp time.Time `spanner:"TIMESTAMP"` + NullTimestamp NullTime `spanner:"NULL_TIMESTAMP"` + TimestampArray []NullTime `spanner:"TIMESTAMP_ARRAY"` + NullTimestampArray []NullTime `spanner:"NULL_TIMESTAMP_ARRAY"` + // DATE / DATE ARRAY + Date civil.Date `spanner:"DATE"` + NullDate NullDate `spanner:"NULL_DATE"` + DateArray []NullDate `spanner:"DATE_ARRAY"` + NullDateArray []NullDate `spanner:"NULL_DATE_ARRAY"` + + // STRUCT ARRAY + StructArray []*struct { + Col1 int64 + Col2 float64 + Col3 string + } `spanner:"STRUCT_ARRAY"` + NullStructArray []*struct { + Col1 int64 + Col2 float64 + Col3 string + } `spanner:"NULL_STRUCT_ARRAY"` + }{ + {}, // got + { + // STRING / STRING ARRAY + "value", + NullString{}, + []NullString{{"value1", true}, {}, {"value3", true}}, + []NullString(nil), + // BYTES / BYTES ARRAY + []byte("value"), + []byte(nil), + [][]byte{[]byte("value1"), nil, []byte("value3")}, + [][]byte(nil), + // INT64 / INT64 ARRAY + int64(17), + NullInt64{}, + []NullInt64{{int64(1), true}, {int64(2), true}, {}}, + []NullInt64(nil), + // BOOL / BOOL ARRAY + true, + NullBool{}, + []NullBool{{}, {true, true}, {false, true}}, + []NullBool(nil), + // FLOAT64 / FLOAT64 ARRAY + 1.7, + NullFloat64{}, + []NullFloat64{{}, {}, {1.7, true}}, + []NullFloat64(nil), + // TIMESTAMP / TIMESTAMP ARRAY + tm, + NullTime{}, + []NullTime{{}, {tm, true}}, + []NullTime(nil), + // DATE / DATE ARRAY + dt, + NullDate{}, + []NullDate{{}, {dt, true}}, + []NullDate(nil), + // STRUCT ARRAY + []*struct { + Col1 int64 + Col2 float64 + Col3 string + }{ + nil, + + {3, 33.3, "three"}, + nil, + }, + []*struct { + Col1 int64 + Col2 float64 + Col3 string + }(nil), + }, // want + } + err := row.ToStruct(&s[0]) + if err != nil { + t.Errorf("row.ToStruct() returns error: %v, want nil", err) + } else if !testEqual(s[0], s[1]) { + t.Errorf("row.ToStruct() fetches struct %v, want %v", s[0], s[1]) + } +} + +func TestToStructEmbedded(t *testing.T) { + type ( + S1 struct{ F1 string } + S2 struct { + S1 + F2 string + } + ) + r := Row{ + []*sppb.StructType_Field{ + {Name: "F1", Type: stringType()}, + {Name: "F2", Type: stringType()}, + }, + []*proto3.Value{ + stringProto("v1"), + stringProto("v2"), + }, + } + var got S2 + if err := r.ToStruct(&got); err != nil { + t.Fatal(err) + } + want := S2{S1: S1{F1: "v1"}, F2: "v2"} + if !testEqual(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +// Test helpers for getting column names. +func TestColumnNameAndIndex(t *testing.T) { + // Test Row.Size(). + if rs := row.Size(); rs != len(row.fields) { + t.Errorf("row.Size() returns %v, want %v", rs, len(row.fields)) + } + // Test Row.Size() on empty Row. + if rs := (&Row{}).Size(); rs != 0 { + t.Errorf("empty_row.Size() returns %v, want %v", rs, 0) + } + // Test Row.ColumnName() + for i, col := range row.fields { + if cn := row.ColumnName(i); cn != col.Name { + t.Errorf("row.ColumnName(%v) returns %q, want %q", i, cn, col.Name) + } + goti, err := row.ColumnIndex(col.Name) + if err != nil { + t.Errorf("ColumnIndex(%q) error %v", col.Name, err) + continue + } + if goti != i { + t.Errorf("ColumnIndex(%q) = %d, want %d", col.Name, goti, i) + } + } + // Test Row.ColumnName on empty Row. + if cn := (&Row{}).ColumnName(0); cn != "" { + t.Errorf("empty_row.ColumnName(%v) returns %q, want %q", 0, cn, "") + } + // Test Row.ColumnIndex on empty Row. + if _, err := (&Row{}).ColumnIndex(""); err == nil { + t.Error("empty_row.ColumnIndex returns nil, want error") + } +} + +func TestNewRow(t *testing.T) { + for _, test := range []struct { + names []string + values []interface{} + want *Row + wantErr error + }{ + { + want: &Row{fields: []*sppb.StructType_Field{}, vals: []*proto3.Value{}}, + }, + { + names: []string{}, + values: []interface{}{}, + want: &Row{fields: []*sppb.StructType_Field{}, vals: []*proto3.Value{}}, + }, + { + names: []string{"a", "b"}, + values: []interface{}{}, + want: nil, + wantErr: errNamesValuesMismatch([]string{"a", "b"}, []interface{}{}), + }, + { + names: []string{"a", "b", "c"}, + values: []interface{}{5, "abc", GenericColumnValue{listType(intType()), listProto(intProto(91), nullProto(), intProto(87))}}, + want: &Row{ + []*sppb.StructType_Field{ + {Name: "a", Type: intType()}, + {Name: "b", Type: stringType()}, + {Name: "c", Type: listType(intType())}, + }, + []*proto3.Value{ + intProto(5), + stringProto("abc"), + listProto(intProto(91), nullProto(), intProto(87)), + }, + }, + }, + } { + got, err := NewRow(test.names, test.values) + if !testEqual(err, test.wantErr) { + t.Errorf("NewRow(%v,%v).err = %s, want %s", test.names, test.values, err, test.wantErr) + continue + } + if !testEqual(got, test.want) { + t.Errorf("NewRow(%v,%v) = %s, want %s", test.names, test.values, got, test.want) + continue + } + } +} + +func BenchmarkColumn(b *testing.B) { + var s string + for i := 0; i < b.N; i++ { + if err := row.Column(0, &s); err != nil { + b.Fatal(err) + } + } +} diff --git a/vendor/cloud.google.com/go/spanner/session.go b/vendor/cloud.google.com/go/spanner/session.go new file mode 100644 index 0000000000..71f1ccdf84 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/session.go @@ -0,0 +1,1080 @@ +/* +Copyright 2017 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 spanner + +import ( + "container/heap" + "container/list" + "fmt" + "log" + "math/rand" + "strings" + "sync" + "time" + + "golang.org/x/net/context" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// sessionHandle is an interface for transactions to access Cloud Spanner sessions safely. It is generated by sessionPool.take(). +type sessionHandle struct { + // mu guarantees that the inner session object is returned / destroyed only once. + mu sync.Mutex + // session is a pointer to a session object. Transactions never need to access it directly. + session *session +} + +// recycle gives the inner session object back to its home session pool. It is safe to call recycle multiple times but only the first one would take effect. +func (sh *sessionHandle) recycle() { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.session == nil { + // sessionHandle has already been recycled. + return + } + sh.session.recycle() + sh.session = nil +} + +// getID gets the Cloud Spanner session ID from the internal session object. getID returns empty string if the sessionHandle is nil or the inner session +// object has been released by recycle / destroy. +func (sh *sessionHandle) getID() string { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.session == nil { + // sessionHandle has already been recycled/destroyed. + return "" + } + return sh.session.getID() +} + +// getClient gets the Cloud Spanner RPC client associated with the session ID in sessionHandle. +func (sh *sessionHandle) getClient() sppb.SpannerClient { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.session == nil { + return nil + } + return sh.session.client +} + +// getMetadata returns the metadata associated with the session in sessionHandle. +func (sh *sessionHandle) getMetadata() metadata.MD { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.session == nil { + return nil + } + return sh.session.md +} + +// getTransactionID returns the transaction id in the session if available. +func (sh *sessionHandle) getTransactionID() transactionID { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.session == nil { + return nil + } + return sh.session.tx +} + +// destroy destroys the inner session object. It is safe to call destroy multiple times and only the first call would attempt to +// destroy the inner session object. +func (sh *sessionHandle) destroy() { + sh.mu.Lock() + s := sh.session + sh.session = nil + sh.mu.Unlock() + if s == nil { + // sessionHandle has already been destroyed. + return + } + s.destroy(false) +} + +// session wraps a Cloud Spanner session ID through which transactions are created and executed. +type session struct { + // client is the RPC channel to Cloud Spanner. It is set only once during session's creation. + client sppb.SpannerClient + // id is the unique id of the session in Cloud Spanner. It is set only once during session's creation. + id string + // pool is the session's home session pool where it was created. It is set only once during session's creation. + pool *sessionPool + // createTime is the timestamp of the session's creation. It is set only once during session's creation. + createTime time.Time + + // mu protects the following fields from concurrent access: both healthcheck workers and transactions can modify them. + mu sync.Mutex + // valid marks the validity of a session. + valid bool + // hcIndex is the index of the session inside the global healthcheck queue. If hcIndex < 0, session has been unregistered from the queue. + hcIndex int + // idleList is the linkedlist node which links the session to its home session pool's idle list. If idleList == nil, the + // session is not in idle list. + idleList *list.Element + // nextCheck is the timestamp of next scheduled healthcheck of the session. It is maintained by the global health checker. + nextCheck time.Time + // checkingHelath is true if currently this session is being processed by health checker. Must be modified under health checker lock. + checkingHealth bool + // md is the Metadata to be sent with each request. + md metadata.MD + // tx contains the transaction id if the session has been prepared for write. + tx transactionID +} + +// isValid returns true if the session is still valid for use. +func (s *session) isValid() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.valid +} + +// isWritePrepared returns true if the session is prepared for write. +func (s *session) isWritePrepared() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.tx != nil +} + +// String implements fmt.Stringer for session. +func (s *session) String() string { + s.mu.Lock() + defer s.mu.Unlock() + return fmt.Sprintf("", + s.id, s.hcIndex, s.idleList, s.valid, s.createTime, s.nextCheck) +} + +// ping verifies if the session is still alive in Cloud Spanner. +func (s *session) ping() error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + return runRetryable(ctx, func(ctx context.Context) error { + _, err := s.client.GetSession(contextWithOutgoingMetadata(ctx, s.pool.md), &sppb.GetSessionRequest{Name: s.getID()}) // s.getID is safe even when s is invalid. + return err + }) +} + +// setHcIndex atomically sets the session's index in the healthcheck queue and returns the old index. +func (s *session) setHcIndex(i int) int { + s.mu.Lock() + defer s.mu.Unlock() + oi := s.hcIndex + s.hcIndex = i + return oi +} + +// setIdleList atomically sets the session's idle list link and returns the old link. +func (s *session) setIdleList(le *list.Element) *list.Element { + s.mu.Lock() + defer s.mu.Unlock() + old := s.idleList + s.idleList = le + return old +} + +// invalidate marks a session as invalid and returns the old validity. +func (s *session) invalidate() bool { + s.mu.Lock() + defer s.mu.Unlock() + ov := s.valid + s.valid = false + return ov +} + +// setNextCheck sets the timestamp for next healthcheck on the session. +func (s *session) setNextCheck(t time.Time) { + s.mu.Lock() + defer s.mu.Unlock() + s.nextCheck = t +} + +// setTransactionID sets the transaction id in the session +func (s *session) setTransactionID(tx transactionID) { + s.mu.Lock() + defer s.mu.Unlock() + s.tx = tx +} + +// getID returns the session ID which uniquely identifies the session in Cloud Spanner. +func (s *session) getID() string { + s.mu.Lock() + defer s.mu.Unlock() + return s.id +} + +// getHcIndex returns the session's index into the global healthcheck priority queue. +func (s *session) getHcIndex() int { + s.mu.Lock() + defer s.mu.Unlock() + return s.hcIndex +} + +// getIdleList returns the session's link in its home session pool's idle list. +func (s *session) getIdleList() *list.Element { + s.mu.Lock() + defer s.mu.Unlock() + return s.idleList +} + +// getNextCheck returns the timestamp for next healthcheck on the session. +func (s *session) getNextCheck() time.Time { + s.mu.Lock() + defer s.mu.Unlock() + return s.nextCheck +} + +// recycle turns the session back to its home session pool. +func (s *session) recycle() { + s.setTransactionID(nil) + if !s.pool.recycle(s) { + // s is rejected by its home session pool because it expired and the session pool currently has enough open sessions. + s.destroy(false) + } +} + +// destroy removes the session from its home session pool, healthcheck queue and Cloud Spanner service. +func (s *session) destroy(isExpire bool) bool { + // Remove s from session pool. + if !s.pool.remove(s, isExpire) { + return false + } + // Unregister s from healthcheck queue. + s.pool.hc.unregister(s) + // Remove s from Cloud Spanner service. + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + // Ignore the error returned by runRetryable because even if we fail to explicitly destroy the session, + // it will be eventually garbage collected by Cloud Spanner. + err := runRetryable(ctx, func(ctx context.Context) error { + _, e := s.client.DeleteSession(ctx, &sppb.DeleteSessionRequest{Name: s.getID()}) + return e + }) + if err != nil { + log.Printf("Failed to delete session %v. Error: %v", s.getID(), err) + } + return true +} + +// prepareForWrite prepares the session for write if it is not already in that state. +func (s *session) prepareForWrite(ctx context.Context) error { + if s.isWritePrepared() { + return nil + } + tx, err := beginTransaction(ctx, s.getID(), s.client) + if err != nil { + return err + } + s.setTransactionID(tx) + return nil +} + +// SessionPoolConfig stores configurations of a session pool. +type SessionPoolConfig struct { + // getRPCClient is the caller supplied method for getting a gRPC client to Cloud Spanner, this makes session pool able to use client pooling. + getRPCClient func() (sppb.SpannerClient, error) + // MaxOpened is the maximum number of opened sessions allowed by the + // session pool. Defaults to NumChannels * 100. + MaxOpened uint64 + // MinOpened is the minimum number of opened sessions that the session pool + // tries to maintain. Session pool won't continue to expire sessions if number + // of opened connections drops below MinOpened. However, if session is found + // to be broken, it will still be evicted from session pool, therefore it is + // posssible that the number of opened sessions drops below MinOpened. + MinOpened uint64 + // MaxIdle is the maximum number of idle sessions, pool is allowed to keep. Defaults to 0. + MaxIdle uint64 + // MaxBurst is the maximum number of concurrent session creation requests. Defaults to 10. + MaxBurst uint64 + // WriteSessions is the fraction of sessions we try to keep prepared for write. + WriteSessions float64 + // HealthCheckWorkers is number of workers used by health checker for this pool. + HealthCheckWorkers int + // HealthCheckInterval is how often the health checker pings a session. Defaults to 5 min. + HealthCheckInterval time.Duration + // healthCheckSampleInterval is how often the health checker samples live session (for use in maintaining session pool size). Defaults to 1 min. + healthCheckSampleInterval time.Duration + // sessionLabels for the sessions created in the session pool. + sessionLabels map[string]string +} + +// errNoRPCGetter returns error for SessionPoolConfig missing getRPCClient method. +func errNoRPCGetter() error { + return spannerErrorf(codes.InvalidArgument, "require SessionPoolConfig.getRPCClient != nil, got nil") +} + +// errMinOpenedGTMapOpened returns error for SessionPoolConfig.MaxOpened < SessionPoolConfig.MinOpened when SessionPoolConfig.MaxOpened is set. +func errMinOpenedGTMaxOpened(maxOpened, minOpened uint64) error { + return spannerErrorf(codes.InvalidArgument, + "require SessionPoolConfig.MaxOpened >= SessionPoolConfig.MinOpened, got %v and %v", maxOpened, minOpened) +} + +// validate verifies that the SessionPoolConfig is good for use. +func (spc *SessionPoolConfig) validate() error { + if spc.getRPCClient == nil { + return errNoRPCGetter() + } + if spc.MinOpened > spc.MaxOpened && spc.MaxOpened > 0 { + return errMinOpenedGTMaxOpened(spc.MaxOpened, spc.MinOpened) + } + return nil +} + +// sessionPool creates and caches Cloud Spanner sessions. +type sessionPool struct { + // mu protects sessionPool from concurrent access. + mu sync.Mutex + // valid marks the validity of the session pool. + valid bool + // db is the database name that all sessions in the pool are associated with. + db string + // idleList caches idle session IDs. Session IDs in this list can be allocated for use. + idleList list.List + // idleWriteList caches idle sessions which have been prepared for write. + idleWriteList list.List + // mayGetSession is for broadcasting that session retrival/creation may proceed. + mayGetSession chan struct{} + // numOpened is the total number of open sessions from the session pool. + numOpened uint64 + // createReqs is the number of ongoing session creation requests. + createReqs uint64 + // prepareReqs is the number of ongoing session preparation request. + prepareReqs uint64 + // configuration of the session pool. + SessionPoolConfig + // Metadata to be sent with each request + md metadata.MD + // hc is the health checker + hc *healthChecker +} + +// newSessionPool creates a new session pool. +func newSessionPool(db string, config SessionPoolConfig, md metadata.MD) (*sessionPool, error) { + if err := config.validate(); err != nil { + return nil, err + } + pool := &sessionPool{ + db: db, + valid: true, + mayGetSession: make(chan struct{}), + SessionPoolConfig: config, + md: md, + } + if config.HealthCheckWorkers == 0 { + // With 10 workers and assuming average latency of 5 ms for BeginTransaction, we will be able to + // prepare 2000 tx/sec in advance. If the rate of takeWriteSession is more than that, it will + // degrade to doing BeginTransaction inline. + // TODO: consider resizing the worker pool dynamically according to the load. + config.HealthCheckWorkers = 10 + } + if config.HealthCheckInterval == 0 { + config.HealthCheckInterval = 5 * time.Minute + } + if config.healthCheckSampleInterval == 0 { + config.healthCheckSampleInterval = time.Minute + } + // On GCE VM, within the same region an healthcheck ping takes on average 10ms to finish, given a 5 minutes interval and + // 10 healthcheck workers, a healthChecker can effectively mantain 100 checks_per_worker/sec * 10 workers * 300 seconds = 300K sessions. + pool.hc = newHealthChecker(config.HealthCheckInterval, config.HealthCheckWorkers, config.healthCheckSampleInterval, pool) + close(pool.hc.ready) + return pool, nil +} + +// isValid checks if the session pool is still valid. +func (p *sessionPool) isValid() bool { + if p == nil { + return false + } + p.mu.Lock() + defer p.mu.Unlock() + return p.valid +} + +// close marks the session pool as closed. +func (p *sessionPool) close() { + if p == nil { + return + } + p.mu.Lock() + if !p.valid { + p.mu.Unlock() + return + } + p.valid = false + p.mu.Unlock() + p.hc.close() + // destroy all the sessions + p.hc.mu.Lock() + allSessions := make([]*session, len(p.hc.queue.sessions)) + copy(allSessions, p.hc.queue.sessions) + p.hc.mu.Unlock() + for _, s := range allSessions { + s.destroy(false) + } +} + +// errInvalidSessionPool returns error for using an invalid session pool. +func errInvalidSessionPool() error { + return spannerErrorf(codes.InvalidArgument, "invalid session pool") +} + +// errGetSessionTimeout returns error for context timeout during sessionPool.take(). +func errGetSessionTimeout() error { + return spannerErrorf(codes.Canceled, "timeout / context canceled during getting session") +} + +// shouldPrepareWrite returns true if we should prepare more sessions for write. +func (p *sessionPool) shouldPrepareWrite() bool { + return float64(p.numOpened)*p.WriteSessions > float64(p.idleWriteList.Len()+int(p.prepareReqs)) +} + +func (p *sessionPool) createSession(ctx context.Context) (*session, error) { + tracePrintf(ctx, nil, "Creating a new session") + doneCreate := func(done bool) { + p.mu.Lock() + if !done { + // Session creation failed, give budget back. + p.numOpened-- + } + p.createReqs-- + // Notify other waiters blocking on session creation. + close(p.mayGetSession) + p.mayGetSession = make(chan struct{}) + p.mu.Unlock() + } + sc, err := p.getRPCClient() + if err != nil { + doneCreate(false) + return nil, err + } + var s *session + err = runRetryable(ctx, func(ctx context.Context) error { + sid, e := sc.CreateSession(ctx, &sppb.CreateSessionRequest{ + Database: p.db, + Session: &sppb.Session{Labels: p.sessionLabels}, + }) + if e != nil { + return e + } + // If no error, construct the new session. + s = &session{valid: true, client: sc, id: sid.Name, pool: p, createTime: time.Now(), md: p.md} + p.hc.register(s) + return nil + }) + if err != nil { + doneCreate(false) + // Should return error directly because of the previous retries on CreateSession RPC. + return nil, err + } + doneCreate(true) + return s, nil +} + +func (p *sessionPool) isHealthy(s *session) bool { + if s.getNextCheck().Add(2 * p.hc.getInterval()).Before(time.Now()) { + // TODO: figure out if we need to schedule a new healthcheck worker here. + if err := s.ping(); shouldDropSession(err) { + // The session is already bad, continue to fetch/create a new one. + s.destroy(false) + return false + } + p.hc.scheduledHC(s) + } + return true +} + +// take returns a cached session if there are available ones; if there isn't any, it tries to allocate a new one. +// Session returned by take should be used for read operations. +func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) { + tracePrintf(ctx, nil, "Acquiring a read-only session") + ctx = contextWithOutgoingMetadata(ctx, p.md) + for { + var ( + s *session + err error + ) + + p.mu.Lock() + if !p.valid { + p.mu.Unlock() + return nil, errInvalidSessionPool() + } + if p.idleList.Len() > 0 { + // Idle sessions are available, get one from the top of the idle list. + s = p.idleList.Remove(p.idleList.Front()).(*session) + tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, + "Acquired read-only session") + } else if p.idleWriteList.Len() > 0 { + s = p.idleWriteList.Remove(p.idleWriteList.Front()).(*session) + tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, + "Acquired read-write session") + } + if s != nil { + s.setIdleList(nil) + p.mu.Unlock() + // From here, session is no longer in idle list, so healthcheck workers won't destroy it. + // If healthcheck workers failed to schedule healthcheck for the session timely, do the check here. + // Because session check is still much cheaper than session creation, they should be reused as much as possible. + if !p.isHealthy(s) { + continue + } + return &sessionHandle{session: s}, nil + } + // Idle list is empty, block if session pool has reached max session creation concurrency or max number of open sessions. + if (p.MaxOpened > 0 && p.numOpened >= p.MaxOpened) || (p.MaxBurst > 0 && p.createReqs >= p.MaxBurst) { + mayGetSession := p.mayGetSession + p.mu.Unlock() + tracePrintf(ctx, nil, "Waiting for read-only session to become available") + select { + case <-ctx.Done(): + tracePrintf(ctx, nil, "Context done waiting for session") + return nil, errGetSessionTimeout() + case <-mayGetSession: + } + continue + } + // Take budget before the actual session creation. + p.numOpened++ + p.createReqs++ + p.mu.Unlock() + if s, err = p.createSession(ctx); err != nil { + tracePrintf(ctx, nil, "Error creating session: %v", err) + return nil, toSpannerError(err) + } + tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, + "Created session") + return &sessionHandle{session: s}, nil + } +} + +// takeWriteSession returns a write prepared cached session if there are available ones; if there isn't any, it tries to allocate a new one. +// Session returned should be used for read write transactions. +func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, error) { + tracePrintf(ctx, nil, "Acquiring a read-write session") + ctx = contextWithOutgoingMetadata(ctx, p.md) + for { + var ( + s *session + err error + ) + + p.mu.Lock() + if !p.valid { + p.mu.Unlock() + return nil, errInvalidSessionPool() + } + if p.idleWriteList.Len() > 0 { + // Idle sessions are available, get one from the top of the idle list. + s = p.idleWriteList.Remove(p.idleWriteList.Front()).(*session) + tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, "Acquired read-write session") + } else if p.idleList.Len() > 0 { + s = p.idleList.Remove(p.idleList.Front()).(*session) + tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, "Acquired read-only session") + } + if s != nil { + s.setIdleList(nil) + p.mu.Unlock() + // From here, session is no longer in idle list, so healthcheck workers won't destroy it. + // If healthcheck workers failed to schedule healthcheck for the session timely, do the check here. + // Because session check is still much cheaper than session creation, they should be reused as much as possible. + if !p.isHealthy(s) { + continue + } + } else { + // Idle list is empty, block if session pool has reached max session creation concurrency or max number of open sessions. + if (p.MaxOpened > 0 && p.numOpened >= p.MaxOpened) || (p.MaxBurst > 0 && p.createReqs >= p.MaxBurst) { + mayGetSession := p.mayGetSession + p.mu.Unlock() + tracePrintf(ctx, nil, "Waiting for read-write session to become available") + select { + case <-ctx.Done(): + tracePrintf(ctx, nil, "Context done waiting for session") + return nil, errGetSessionTimeout() + case <-mayGetSession: + } + continue + } + + // Take budget before the actual session creation. + p.numOpened++ + p.createReqs++ + p.mu.Unlock() + if s, err = p.createSession(ctx); err != nil { + tracePrintf(ctx, nil, "Error creating session: %v", err) + return nil, toSpannerError(err) + } + tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, + "Created session") + } + if !s.isWritePrepared() { + if err = s.prepareForWrite(ctx); err != nil { + s.recycle() + tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, + "Error preparing session for write") + return nil, toSpannerError(err) + } + } + return &sessionHandle{session: s}, nil + } +} + +// recycle puts session s back to the session pool's idle list, it returns true if the session pool successfully recycles session s. +func (p *sessionPool) recycle(s *session) bool { + p.mu.Lock() + defer p.mu.Unlock() + if !s.isValid() || !p.valid { + // Reject the session if session is invalid or pool itself is invalid. + return false + } + // Put session at the back of the list to round robin for load balancing across channels. + if s.isWritePrepared() { + s.setIdleList(p.idleWriteList.PushBack(s)) + } else { + s.setIdleList(p.idleList.PushBack(s)) + } + // Broadcast that a session has been returned to idle list. + close(p.mayGetSession) + p.mayGetSession = make(chan struct{}) + return true +} + +// remove atomically removes session s from the session pool and invalidates s. +// If isExpire == true, the removal is triggered by session expiration and in such cases, only idle sessions can be removed. +func (p *sessionPool) remove(s *session, isExpire bool) bool { + p.mu.Lock() + defer p.mu.Unlock() + if isExpire && (p.numOpened <= p.MinOpened || s.getIdleList() == nil) { + // Don't expire session if the session is not in idle list (in use), or if number of open sessions is going below p.MinOpened. + return false + } + ol := s.setIdleList(nil) + // If the session is in the idlelist, remove it. + if ol != nil { + // Remove from whichever list it is in. + p.idleList.Remove(ol) + p.idleWriteList.Remove(ol) + } + if s.invalidate() { + // Decrease the number of opened sessions. + p.numOpened-- + // Broadcast that a session has been destroyed. + close(p.mayGetSession) + p.mayGetSession = make(chan struct{}) + return true + } + return false +} + +// hcHeap implements heap.Interface. It is used to create the priority queue for session healthchecks. +type hcHeap struct { + sessions []*session +} + +// Len impelemnts heap.Interface.Len. +func (h hcHeap) Len() int { + return len(h.sessions) +} + +// Less implements heap.Interface.Less. +func (h hcHeap) Less(i, j int) bool { + return h.sessions[i].getNextCheck().Before(h.sessions[j].getNextCheck()) +} + +// Swap implements heap.Interface.Swap. +func (h hcHeap) Swap(i, j int) { + h.sessions[i], h.sessions[j] = h.sessions[j], h.sessions[i] + h.sessions[i].setHcIndex(i) + h.sessions[j].setHcIndex(j) +} + +// Push implements heap.Interface.Push. +func (h *hcHeap) Push(s interface{}) { + ns := s.(*session) + ns.setHcIndex(len(h.sessions)) + h.sessions = append(h.sessions, ns) +} + +// Pop implements heap.Interface.Pop. +func (h *hcHeap) Pop() interface{} { + old := h.sessions + n := len(old) + s := old[n-1] + h.sessions = old[:n-1] + s.setHcIndex(-1) + return s +} + +// healthChecker performs periodical healthchecks on registered sessions. +type healthChecker struct { + // mu protects concurrent access to hcQueue. + mu sync.Mutex + // queue is the priority queue for session healthchecks. Sessions with lower nextCheck rank higher in the queue. + queue hcHeap + // interval is the average interval between two healthchecks on a session. + interval time.Duration + // workers is the number of concurrent healthcheck workers. + workers int + // waitWorkers waits for all healthcheck workers to exit + waitWorkers sync.WaitGroup + // pool is the underlying session pool. + pool *sessionPool + // sampleInterval is the interval of sampling by the maintainer. + sampleInterval time.Duration + // ready is used to signal that maintainer can start running. + ready chan struct{} + // done is used to signal that health checker should be closed. + done chan struct{} + // once is used for closing channel done only once. + once sync.Once +} + +// newHealthChecker initializes new instance of healthChecker. +func newHealthChecker(interval time.Duration, workers int, sampleInterval time.Duration, pool *sessionPool) *healthChecker { + if workers <= 0 { + workers = 1 + } + hc := &healthChecker{ + interval: interval, + workers: workers, + pool: pool, + sampleInterval: sampleInterval, + ready: make(chan struct{}), + done: make(chan struct{}), + } + hc.waitWorkers.Add(1) + go hc.maintainer() + for i := 1; i <= hc.workers; i++ { + hc.waitWorkers.Add(1) + go hc.worker(i) + } + return hc +} + +// close closes the healthChecker and waits for all healthcheck workers to exit. +func (hc *healthChecker) close() { + hc.once.Do(func() { close(hc.done) }) + hc.waitWorkers.Wait() +} + +// isClosing checks if a healthChecker is already closing. +func (hc *healthChecker) isClosing() bool { + select { + case <-hc.done: + return true + default: + return false + } +} + +// getInterval gets the healthcheck interval. +func (hc *healthChecker) getInterval() time.Duration { + hc.mu.Lock() + defer hc.mu.Unlock() + return hc.interval +} + +// scheduledHCLocked schedules next healthcheck on session s with the assumption that hc.mu is being held. +func (hc *healthChecker) scheduledHCLocked(s *session) { + // The next healthcheck will be scheduled after [interval*0.5, interval*1.5) nanoseconds. + nsFromNow := rand.Int63n(int64(hc.interval)) + int64(hc.interval)/2 + s.setNextCheck(time.Now().Add(time.Duration(nsFromNow))) + if hi := s.getHcIndex(); hi != -1 { + // Session is still being tracked by healthcheck workers. + heap.Fix(&hc.queue, hi) + } +} + +// scheduledHC schedules next healthcheck on session s. It is safe to be called concurrently. +func (hc *healthChecker) scheduledHC(s *session) { + hc.mu.Lock() + defer hc.mu.Unlock() + hc.scheduledHCLocked(s) +} + +// register registers a session with healthChecker for periodical healthcheck. +func (hc *healthChecker) register(s *session) { + hc.mu.Lock() + defer hc.mu.Unlock() + hc.scheduledHCLocked(s) + heap.Push(&hc.queue, s) +} + +// unregister unregisters a session from healthcheck queue. +func (hc *healthChecker) unregister(s *session) { + hc.mu.Lock() + defer hc.mu.Unlock() + oi := s.setHcIndex(-1) + if oi >= 0 { + heap.Remove(&hc.queue, oi) + } +} + +// markDone marks that health check for session has been performed. +func (hc *healthChecker) markDone(s *session) { + hc.mu.Lock() + defer hc.mu.Unlock() + s.checkingHealth = false +} + +// healthCheck checks the health of the session and pings it if needed. +func (hc *healthChecker) healthCheck(s *session) { + defer hc.markDone(s) + if !s.pool.isValid() { + // Session pool is closed, perform a garbage collection. + s.destroy(false) + return + } + if err := s.ping(); shouldDropSession(err) { + // Ping failed, destroy the session. + s.destroy(false) + } +} + +// worker performs the healthcheck on sessions in healthChecker's priority queue. +func (hc *healthChecker) worker(i int) { + // Returns a session which we should ping to keep it alive. + getNextForPing := func() *session { + hc.pool.mu.Lock() + defer hc.pool.mu.Unlock() + hc.mu.Lock() + defer hc.mu.Unlock() + if hc.queue.Len() <= 0 { + // Queue is empty. + return nil + } + s := hc.queue.sessions[0] + if s.getNextCheck().After(time.Now()) && hc.pool.valid { + // All sessions have been checked recently. + return nil + } + hc.scheduledHCLocked(s) + if !s.checkingHealth { + s.checkingHealth = true + return s + } + return nil + } + + // Returns a session which we should prepare for write. + getNextForTx := func() *session { + hc.pool.mu.Lock() + defer hc.pool.mu.Unlock() + if hc.pool.shouldPrepareWrite() { + if hc.pool.idleList.Len() > 0 && hc.pool.valid { + hc.mu.Lock() + defer hc.mu.Unlock() + if hc.pool.idleList.Front().Value.(*session).checkingHealth { + return nil + } + session := hc.pool.idleList.Remove(hc.pool.idleList.Front()).(*session) + session.checkingHealth = true + hc.pool.prepareReqs++ + return session + } + } + return nil + } + + for { + if hc.isClosing() { + // Exit when the pool has been closed and all sessions have been destroyed + // or when health checker has been closed. + hc.waitWorkers.Done() + return + } + ws := getNextForTx() + if ws != nil { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + err := ws.prepareForWrite(contextWithOutgoingMetadata(ctx, hc.pool.md)) + cancel() + if err != nil { + // Skip handling prepare error, session can be prepared in next cycle + log.Printf("Failed to prepare session, error: %v", toSpannerError(err)) + } + hc.pool.recycle(ws) + hc.pool.mu.Lock() + hc.pool.prepareReqs-- + hc.pool.mu.Unlock() + hc.markDone(ws) + } + rs := getNextForPing() + if rs == nil { + if ws == nil { + // No work to be done so sleep to avoid burning cpu + pause := int64(100 * time.Millisecond) + if pause > int64(hc.interval) { + pause = int64(hc.interval) + } + select { + case <-time.After(time.Duration(rand.Int63n(pause) + pause/2)): + break + case <-hc.done: + break + } + + } + continue + } + hc.healthCheck(rs) + } +} + +// maintainer maintains the maxSessionsInUse by a window of kWindowSize * sampleInterval. +// Based on this information, health checker will try to maintain the number of sessions by hc.. +func (hc *healthChecker) maintainer() { + // Wait so that pool is ready. + <-hc.ready + + var ( + windowSize uint64 = 10 + iteration uint64 + timeout <-chan time.Time + ) + + // replenishPool is run if numOpened is less than sessionsToKeep, timeouts on sampleInterval. + replenishPool := func(sessionsToKeep uint64) { + ctx, _ := context.WithTimeout(context.Background(), hc.sampleInterval) + for { + select { + case <-timeout: + return + default: + break + } + + p := hc.pool + p.mu.Lock() + // Take budget before the actual session creation. + if sessionsToKeep <= p.numOpened { + p.mu.Unlock() + break + } + p.numOpened++ + p.createReqs++ + shouldPrepareWrite := p.shouldPrepareWrite() + p.mu.Unlock() + var ( + s *session + err error + ) + if s, err = p.createSession(ctx); err != nil { + log.Printf("Failed to create session, error: %v", toSpannerError(err)) + continue + } + if shouldPrepareWrite { + if err = s.prepareForWrite(ctx); err != nil { + p.recycle(s) + log.Printf("Failed to prepare session, error: %v", toSpannerError(err)) + continue + } + } + p.recycle(s) + } + } + + // shrinkPool, scales down the session pool. + shrinkPool := func(sessionsToKeep uint64) { + for { + select { + case <-timeout: + return + default: + break + } + + p := hc.pool + p.mu.Lock() + + if sessionsToKeep >= p.numOpened { + p.mu.Unlock() + break + } + + var s *session + if p.idleList.Len() > 0 { + s = p.idleList.Front().Value.(*session) + } else if p.idleWriteList.Len() > 0 { + s = p.idleWriteList.Front().Value.(*session) + } + p.mu.Unlock() + if s != nil { + // destroy session as expire. + s.destroy(true) + } else { + break + } + } + } + + for { + if hc.isClosing() { + hc.waitWorkers.Done() + return + } + + // maxSessionsInUse is the maximum number of sessions in use concurrently over a period of time. + var maxSessionsInUse uint64 + + // Updates metrics. + hc.pool.mu.Lock() + currSessionsInUse := hc.pool.numOpened - uint64(hc.pool.idleList.Len()) - uint64(hc.pool.idleWriteList.Len()) + currSessionsOpened := hc.pool.numOpened + hc.pool.mu.Unlock() + + hc.mu.Lock() + if iteration%windowSize == 0 || maxSessionsInUse < currSessionsInUse { + maxSessionsInUse = currSessionsInUse + } + sessionsToKeep := maxUint64(hc.pool.MinOpened, + minUint64(currSessionsOpened, hc.pool.MaxIdle+maxSessionsInUse)) + hc.mu.Unlock() + + timeout = time.After(hc.sampleInterval) + // Replenish or Shrink pool if needed. + // Note: we don't need to worry about pending create session requests, we only need to sample the current sessions in use. + // the routines will not try to create extra / delete creating sessions. + if sessionsToKeep > currSessionsOpened { + replenishPool(sessionsToKeep) + } else { + shrinkPool(sessionsToKeep) + } + + select { + case <-timeout: + break + case <-hc.done: + break + } + iteration++ + } +} + +// shouldDropSession returns true if a particular error leads to the removal of a session +func shouldDropSession(err error) bool { + if err == nil { + return false + } + // If a Cloud Spanner can no longer locate the session (for example, if session is garbage collected), then caller + // should not try to return the session back into the session pool. + // TODO: once gRPC can return auxiliary error information, stop parsing the error message. + if ErrCode(err) == codes.NotFound && strings.Contains(ErrDesc(err), "Session not found:") { + return true + } + return false +} diff --git a/vendor/cloud.google.com/go/spanner/session_test.go b/vendor/cloud.google.com/go/spanner/session_test.go new file mode 100644 index 0000000000..c7262ae46a --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/session_test.go @@ -0,0 +1,886 @@ +/* +Copyright 2017 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 spanner + +import ( + "bytes" + "container/heap" + "math/rand" + "sync" + "testing" + "time" + + "golang.org/x/net/context" + "google.golang.org/grpc/status" + + "cloud.google.com/go/spanner/internal/testutil" + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" +) + +// setup prepares test environment for regular session pool tests. +func setup(t *testing.T, spc SessionPoolConfig) (sp *sessionPool, sc *testutil.MockCloudSpannerClient, cancel func()) { + sc = testutil.NewMockCloudSpannerClient(t) + spc.getRPCClient = func() (sppb.SpannerClient, error) { + return sc, nil + } + if spc.HealthCheckInterval == 0 { + spc.HealthCheckInterval = 50 * time.Millisecond + } + if spc.healthCheckSampleInterval == 0 { + spc.healthCheckSampleInterval = 10 * time.Millisecond + } + sp, err := newSessionPool("mockdb", spc, nil) + if err != nil { + t.Fatalf("cannot create session pool: %v", err) + } + cancel = func() { + sp.close() + } + return +} + +// TestSessionPoolConfigValidation tests session pool config validation. +func TestSessionPoolConfigValidation(t *testing.T) { + t.Parallel() + sc := testutil.NewMockCloudSpannerClient(t) + for _, test := range []struct { + spc SessionPoolConfig + err error + }{ + { + SessionPoolConfig{}, + errNoRPCGetter(), + }, + { + SessionPoolConfig{ + getRPCClient: func() (sppb.SpannerClient, error) { + return sc, nil + }, + MinOpened: 10, + MaxOpened: 5, + }, + errMinOpenedGTMaxOpened(5, 10), + }, + } { + if _, err := newSessionPool("mockdb", test.spc, nil); !testEqual(err, test.err) { + t.Errorf("want %v, got %v", test.err, err) + } + } +} + +// TestSessionCreation tests session creation during sessionPool.Take(). +func TestSessionCreation(t *testing.T) { + t.Parallel() + sp, sc, cancel := setup(t, SessionPoolConfig{}) + defer cancel() + // Take three sessions from session pool, this should trigger session pool to create three new sessions. + shs := make([]*sessionHandle, 3) + // gotDs holds the unique sessions taken from session pool. + gotDs := map[string]bool{} + for i := 0; i < len(shs); i++ { + var err error + shs[i], err = sp.take(context.Background()) + if err != nil { + t.Errorf("failed to get session(%v): %v", i, err) + } + gotDs[shs[i].getID()] = true + } + if len(gotDs) != len(shs) { + t.Errorf("session pool created %v sessions, want %v", len(gotDs), len(shs)) + } + if wantDs := sc.DumpSessions(); !testEqual(gotDs, wantDs) { + t.Errorf("session pool creates sessions %v, want %v", gotDs, wantDs) + } + // Verify that created sessions are recorded correctly in session pool. + sp.mu.Lock() + if int(sp.numOpened) != len(shs) { + t.Errorf("session pool reports %v open sessions, want %v", sp.numOpened, len(shs)) + } + if sp.createReqs != 0 { + t.Errorf("session pool reports %v session create requests, want 0", int(sp.createReqs)) + } + sp.mu.Unlock() + // Verify that created sessions are tracked correctly by healthcheck queue. + hc := sp.hc + hc.mu.Lock() + if hc.queue.Len() != len(shs) { + t.Errorf("healthcheck queue length = %v, want %v", hc.queue.Len(), len(shs)) + } + for _, s := range hc.queue.sessions { + if !gotDs[s.getID()] { + t.Errorf("session %v is in healthcheck queue, but it is not created by session pool", s.getID()) + } + } + hc.mu.Unlock() +} + +// TestTakeFromIdleList tests taking sessions from session pool's idle list. +func TestTakeFromIdleList(t *testing.T) { + t.Parallel() + sp, sc, cancel := setup(t, SessionPoolConfig{MaxIdle: 10}) // make sure maintainer keeps the idle sessions + defer cancel() + // Take ten sessions from session pool and recycle them. + shs := make([]*sessionHandle, 10) + for i := 0; i < len(shs); i++ { + var err error + shs[i], err = sp.take(context.Background()) + if err != nil { + t.Errorf("failed to get session(%v): %v", i, err) + } + } + // Make sure it's sampled once before recycling, otherwise it will be cleaned up. + <-time.After(sp.SessionPoolConfig.healthCheckSampleInterval) + for i := 0; i < len(shs); i++ { + shs[i].recycle() + } + // Further session requests from session pool won't cause mockclient to create more sessions. + wantSessions := sc.DumpSessions() + // Take ten sessions from session pool again, this time all sessions should come from idle list. + gotSessions := map[string]bool{} + for i := 0; i < len(shs); i++ { + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("cannot take session from session pool: %v", err) + } + gotSessions[sh.getID()] = true + } + if len(gotSessions) != 10 { + t.Errorf("got %v unique sessions, want 10", len(gotSessions)) + } + if !testEqual(gotSessions, wantSessions) { + t.Errorf("got sessions: %v, want %v", gotSessions, wantSessions) + } +} + +// TesttakeWriteSessionFromIdleList tests taking write sessions from session pool's idle list. +func TestTakeWriteSessionFromIdleList(t *testing.T) { + t.Parallel() + sp, sc, cancel := setup(t, SessionPoolConfig{MaxIdle: 20}) // make sure maintainer keeps the idle sessions + defer cancel() + + acts := make([]testutil.Action, 20) + for i := 0; i < len(acts); i++ { + acts[i] = testutil.Action{"BeginTransaction", nil} + } + sc.SetActions(acts...) + // Take ten sessions from session pool and recycle them. + shs := make([]*sessionHandle, 10) + for i := 0; i < len(shs); i++ { + var err error + shs[i], err = sp.takeWriteSession(context.Background()) + if err != nil { + t.Errorf("failed to get session(%v): %v", i, err) + } + } + // Make sure it's sampled once before recycling, otherwise it will be cleaned up. + <-time.After(sp.SessionPoolConfig.healthCheckSampleInterval) + for i := 0; i < len(shs); i++ { + shs[i].recycle() + } + // Further session requests from session pool won't cause mockclient to create more sessions. + wantSessions := sc.DumpSessions() + // Take ten sessions from session pool again, this time all sessions should come from idle list. + gotSessions := map[string]bool{} + for i := 0; i < len(shs); i++ { + sh, err := sp.takeWriteSession(context.Background()) + if err != nil { + t.Errorf("cannot take session from session pool: %v", err) + } + gotSessions[sh.getID()] = true + } + if len(gotSessions) != 10 { + t.Errorf("got %v unique sessions, want 10", len(gotSessions)) + } + if !testEqual(gotSessions, wantSessions) { + t.Errorf("got sessions: %v, want %v", gotSessions, wantSessions) + } +} + +// TestTakeFromIdleListChecked tests taking sessions from session pool's idle list, but with a extra ping check. +func TestTakeFromIdleListChecked(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, sc, cancel := setup(t, SessionPoolConfig{MaxIdle: 1}) // make sure maintainer keeps the idle sessions + defer cancel() + // Stop healthcheck workers to simulate slow pings. + sp.hc.close() + // Create a session and recycle it. + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("failed to get session: %v", err) + } + // Make sure it's sampled once before recycling, otherwise it will be cleaned up. + <-time.After(sp.SessionPoolConfig.healthCheckSampleInterval) + wantSid := sh.getID() + sh.recycle() + <-time.After(time.Second) + // Two back-to-back session requests, both of them should return the same session created before and + // none of them should trigger a session ping. + for i := 0; i < 2; i++ { + // Take the session from the idle list and recycle it. + sh, err = sp.take(context.Background()) + if err != nil { + t.Errorf("%v - failed to get session: %v", i, err) + } + if gotSid := sh.getID(); gotSid != wantSid { + t.Errorf("%v - got session id: %v, want %v", i, gotSid, wantSid) + } + // The two back-to-back session requests shouldn't trigger any session pings because sessionPool.Take + // reschedules the next healthcheck. + if got, want := sc.DumpPings(), ([]string{wantSid}); !testEqual(got, want) { + t.Errorf("%v - got ping session requests: %v, want %v", i, got, want) + } + sh.recycle() + } + // Inject session error to mockclient, and take the session from the session pool, the old session should be destroyed and + // the session pool will create a new session. + sc.InjectError("GetSession", status.Errorf(codes.NotFound, "Session not found:")) + // Delay to trigger sessionPool.Take to ping the session. + <-time.After(time.Second) + sh, err = sp.take(context.Background()) + if err != nil { + t.Errorf("failed to get session: %v", err) + } + ds := sc.DumpSessions() + if len(ds) != 1 { + t.Errorf("dumped sessions from mockclient: %v, want %v", ds, sh.getID()) + } + if sh.getID() == wantSid { + t.Errorf("sessionPool.Take still returns the same session %v, want it to create a new one", wantSid) + } +} + +// TestTakeFromIdleWriteListChecked tests taking sessions from session pool's idle list, but with a extra ping check. +func TestTakeFromIdleWriteListChecked(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, sc, cancel := setup(t, SessionPoolConfig{MaxIdle: 1}) // make sure maintainer keeps the idle sessions + defer cancel() + sc.MakeNice() + // Stop healthcheck workers to simulate slow pings. + sp.hc.close() + // Create a session and recycle it. + sh, err := sp.takeWriteSession(context.Background()) + if err != nil { + t.Errorf("failed to get session: %v", err) + } + wantSid := sh.getID() + // Make sure it's sampled once before recycling, otherwise it will be cleaned up. + <-time.After(sp.SessionPoolConfig.healthCheckSampleInterval) + sh.recycle() + <-time.After(time.Second) + // Two back-to-back session requests, both of them should return the same session created before and + // none of them should trigger a session ping. + for i := 0; i < 2; i++ { + // Take the session from the idle list and recycle it. + sh, err = sp.takeWriteSession(context.Background()) + if err != nil { + t.Errorf("%v - failed to get session: %v", i, err) + } + if gotSid := sh.getID(); gotSid != wantSid { + t.Errorf("%v - got session id: %v, want %v", i, gotSid, wantSid) + } + // The two back-to-back session requests shouldn't trigger any session pings because sessionPool.Take + // reschedules the next healthcheck. + if got, want := sc.DumpPings(), ([]string{wantSid}); !testEqual(got, want) { + t.Errorf("%v - got ping session requests: %v, want %v", i, got, want) + } + sh.recycle() + } + // Inject session error to mockclient, and take the session from the session pool, the old session should be destroyed and + // the session pool will create a new session. + sc.InjectError("GetSession", status.Errorf(codes.NotFound, "Session not found:")) + // Delay to trigger sessionPool.Take to ping the session. + <-time.After(time.Second) + sh, err = sp.takeWriteSession(context.Background()) + if err != nil { + t.Errorf("failed to get session: %v", err) + } + ds := sc.DumpSessions() + if len(ds) != 1 { + t.Errorf("dumped sessions from mockclient: %v, want %v", ds, sh.getID()) + } + if sh.getID() == wantSid { + t.Errorf("sessionPool.Take still returns the same session %v, want it to create a new one", wantSid) + } +} + +// TestMaxOpenedSessions tests max open sessions constraint. +func TestMaxOpenedSessions(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, _, cancel := setup(t, SessionPoolConfig{MaxOpened: 1}) + defer cancel() + sh1, err := sp.take(context.Background()) + if err != nil { + t.Errorf("cannot take session from session pool: %v", err) + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + // Session request will timeout due to the max open sessions constraint. + sh2, gotErr := sp.take(ctx) + if wantErr := errGetSessionTimeout(); !testEqual(gotErr, wantErr) { + t.Errorf("the second session retrival returns error %v, want %v", gotErr, wantErr) + } + go func() { + <-time.After(time.Second) + // destroy the first session to allow the next session request to proceed. + sh1.destroy() + }() + // Now session request can be processed because the first session will be destroyed. + sh2, err = sp.take(context.Background()) + if err != nil { + t.Errorf("after the first session is destroyed, session retrival still returns error %v, want nil", err) + } + if !sh2.session.isValid() || sh2.getID() == "" { + t.Errorf("got invalid session: %v", sh2.session) + } +} + +// TestMinOpenedSessions tests min open session constraint. +func TestMinOpenedSessions(t *testing.T) { + sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: 1}) + defer cancel() + // Take ten sessions from session pool and recycle them. + var ss []*session + var shs []*sessionHandle + for i := 0; i < 10; i++ { + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("failed to get session(%v): %v", i, err) + } + ss = append(ss, sh.session) + shs = append(shs, sh) + sh.recycle() + } + for _, sh := range shs { + sh.recycle() + } + // Simulate session expiration. + for _, s := range ss { + s.destroy(true) + } + sp.mu.Lock() + defer sp.mu.Unlock() + // There should be still one session left in idle list due to the min open sessions constraint. + if sp.idleList.Len() != 1 { + t.Errorf("got %v sessions in idle list, want 1 %d", sp.idleList.Len(), sp.numOpened) + } +} + +// TestMaxBurst tests max burst constraint. +func TestMaxBurst(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, sc, cancel := setup(t, SessionPoolConfig{MaxBurst: 1}) + defer cancel() + // Will cause session creation RPC to be retried forever. + sc.InjectError("CreateSession", status.Errorf(codes.Unavailable, "try later")) + // This session request will never finish until the injected error is cleared. + go sp.take(context.Background()) + // Poll for the execution of the first session request. + for { + sp.mu.Lock() + cr := sp.createReqs + sp.mu.Unlock() + if cr == 0 { + <-time.After(time.Second) + continue + } + // The first session request is being executed. + break + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + sh, gotErr := sp.take(ctx) + // Since MaxBurst == 1, the second session request should block. + if wantErr := errGetSessionTimeout(); !testEqual(gotErr, wantErr) { + t.Errorf("session retrival returns error %v, want %v", gotErr, wantErr) + } + // Let the first session request succeed. + sc.InjectError("CreateSession", nil) + // Now new session request can proceed because the first session request will eventually succeed. + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("session retrival returns error %v, want nil", err) + } + if !sh.session.isValid() || sh.getID() == "" { + t.Errorf("got invalid session: %v", sh.session) + } +} + +// TestSessionrecycle tests recycling sessions. +func TestSessionRecycle(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: 1, MaxIdle: 2}) + // Set MaxIdle to ensure shs[0] is not destroyed from scale down. + defer cancel() + + // Test session is correctly recycled and reused. + for i := 0; i < 20; i++ { + s, err := sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get the session %v: %v", i, err) + } + s.recycle() + } + if sp.numOpened != 1 { + t.Errorf("Expect session pool size %d, got %d", 1, sp.numOpened) + } +} + +// TestSessionDestroy tests destroying sessions. +func TestSessionDestroy(t *testing.T) { + t.Parallel() + sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: 1}) + defer cancel() + <-time.After(10 * time.Millisecond) // maintainer will create one session, we wait for it create session to avoid flakiness in test + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + s := sh.session + sh.recycle() + if d := s.destroy(true); d || !s.isValid() { + // Session should be remaining because of min open sessions constraint. + t.Errorf("session %v invalid, want it to stay alive. (destroy in expiration mode, success: %v)", s, d) + } + if d := s.destroy(false); !d || s.isValid() { + // Session should be destroyed. + t.Errorf("failed to destroy session %v. (destroy in default mode, success: %v)", s, d) + } +} + +// TestHcHeap tests heap operation on top of hcHeap. +func TestHcHeap(t *testing.T) { + in := []*session{ + {nextCheck: time.Unix(10, 0)}, + {nextCheck: time.Unix(0, 5)}, + {nextCheck: time.Unix(1, 8)}, + {nextCheck: time.Unix(11, 7)}, + {nextCheck: time.Unix(6, 3)}, + } + want := []*session{ + {nextCheck: time.Unix(1, 8), hcIndex: 0}, + {nextCheck: time.Unix(6, 3), hcIndex: 1}, + {nextCheck: time.Unix(8, 2), hcIndex: 2}, + {nextCheck: time.Unix(10, 0), hcIndex: 3}, + {nextCheck: time.Unix(11, 7), hcIndex: 4}, + } + hh := hcHeap{} + for _, s := range in { + heap.Push(&hh, s) + } + // Change top of the heap and do a adjustment. + hh.sessions[0].nextCheck = time.Unix(8, 2) + heap.Fix(&hh, 0) + for idx := 0; hh.Len() > 0; idx++ { + got := heap.Pop(&hh).(*session) + want[idx].hcIndex = -1 + if !testEqual(got, want[idx]) { + t.Errorf("%v: heap.Pop returns %v, want %v", idx, got, want[idx]) + } + } +} + +// TestHealthCheckScheduler tests if healthcheck workers can schedule and perform healthchecks properly. +func TestHealthCheckScheduler(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, sc, cancel := setup(t, SessionPoolConfig{}) + defer cancel() + // Create 50 sessions. + ss := []string{} + for i := 0; i < 50; i++ { + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + ss = append(ss, sh.getID()) + } + // Sleep for 1s, allowing healthcheck workers to perform some session pings. + <-time.After(time.Second) + dp := sc.DumpPings() + gotPings := map[string]int64{} + for _, p := range dp { + gotPings[p]++ + } + for _, s := range ss { + // The average ping interval is 50ms. + want := int64(time.Second) / int64(50*time.Millisecond) + if got := gotPings[s]; got < want/2 || got > want+want/2 { + t.Errorf("got %v healthchecks on session %v, want it between (%v, %v)", got, s, want/2, want+want/2) + } + } +} + +// Tests that a fractions of sessions are prepared for write by health checker. +func TestWriteSessionsPrepared(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + sp, sc, cancel := setup(t, SessionPoolConfig{WriteSessions: 0.5, MaxIdle: 20}) + sc.MakeNice() + defer cancel() + shs := make([]*sessionHandle, 10) + var err error + for i := 0; i < 10; i++ { + shs[i], err = sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + } + // Now there are 10 sessions in the pool. Release them. + for _, sh := range shs { + sh.recycle() + } + // Sleep for 1s, allowing healthcheck workers to invoke begin transaction. + <-time.After(time.Second) + wshs := make([]*sessionHandle, 5) + for i := 0; i < 5; i++ { + wshs[i], err = sp.takeWriteSession(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + if wshs[i].getTransactionID() == nil { + t.Errorf("got nil transaction id from session pool") + } + } + for _, sh := range wshs { + sh.recycle() + } + <-time.After(time.Second) + // Now force creation of 10 more sessions. + shs = make([]*sessionHandle, 20) + for i := 0; i < 20; i++ { + shs[i], err = sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + } + // Now there are 20 sessions in the pool. Release them. + for _, sh := range shs { + sh.recycle() + } + <-time.After(time.Second) + if sp.idleWriteList.Len() != 10 { + t.Errorf("Expect 10 write prepared session, got: %d", sp.idleWriteList.Len()) + } +} + +// TestTakeFromWriteQueue tests that sessionPool.take() returns write prepared sessions as well. +func TestTakeFromWriteQueue(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, sc, cancel := setup(t, SessionPoolConfig{MaxOpened: 1, WriteSessions: 1.0, MaxIdle: 1}) + sc.MakeNice() + defer cancel() + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + sh.recycle() + <-time.After(time.Second) + // The session should now be in write queue but take should also return it. + if sp.idleWriteList.Len() == 0 { + t.Errorf("write queue unexpectedly empty") + } + if sp.idleList.Len() != 0 { + t.Errorf("read queue not empty") + } + sh, err = sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + sh.recycle() +} + +// TestSessionHealthCheck tests healthchecking cases. +func TestSessionHealthCheck(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sp, sc, cancel := setup(t, SessionPoolConfig{}) + defer cancel() + // Test pinging sessions. + sh, err := sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + <-time.After(time.Second) + pings := sc.DumpPings() + if len(pings) == 0 || pings[0] != sh.getID() { + t.Errorf("healthchecker didn't send any ping to session %v", sh.getID()) + } + // Test broken session detection. + sh, err = sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + sc.InjectError("GetSession", status.Errorf(codes.NotFound, "Session not found:")) + // Wait for healthcheck workers to find the broken session and tear it down. + <-time.After(1 * time.Second) + s := sh.session + if sh.session.isValid() { + t.Errorf("session(%v) is still alive, want it to be dropped by healthcheck workers", s) + } + sc.InjectError("GetSession", nil) + // Test garbage collection. + sh, err = sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + sp.close() + if sh.session.isValid() { + t.Errorf("session(%v) is still alive, want it to be garbage collected", s) + } +} + +// TestStressSessionPool does stress test on session pool by the following concurrent operations: +// 1) Test worker gets a session from the pool. +// 2) Test worker turns a session back into the pool. +// 3) Test worker destroys a session got from the pool. +// 4) Healthcheck destroys a broken session (because a worker has already destroyed it). +// 5) Test worker closes the session pool. +// +// During the test, the session pool maintainer maintains the number of sessions, +// and it is expected that all sessions that are taken from session pool remains valid. +// When all test workers and healthcheck workers exit, mockclient, session pool +// and healthchecker should be in consistent state. + +func TestStressSessionPool(t *testing.T) { + t.Parallel() + // Use concurrent workers to test different session pool built from different configurations. + if testing.Short() { + t.SkipNow() + } + for ti, cfg := range []SessionPoolConfig{ + {}, + {MinOpened: 10, MaxOpened: 100}, + {MaxBurst: 50}, + {MinOpened: 10, MaxOpened: 200, MaxBurst: 5}, + {MinOpened: 10, MaxOpened: 200, MaxBurst: 5, WriteSessions: 0.2}, + } { + var wg sync.WaitGroup + // Create a more aggressive session healthchecker to increase test concurrency. + cfg.HealthCheckInterval = 50 * time.Millisecond + cfg.healthCheckSampleInterval = 10 * time.Millisecond + cfg.HealthCheckWorkers = 50 + sc := testutil.NewMockCloudSpannerClient(t) + sc.MakeNice() + cfg.getRPCClient = func() (sppb.SpannerClient, error) { + return sc, nil + } + sp, _ := newSessionPool("mockdb", cfg, nil) + for i := 0; i < 100; i++ { + wg.Add(1) + // Schedule a test worker. + go func(idx int, pool *sessionPool, client sppb.SpannerClient) { + defer wg.Done() + // Test worker iterates 1K times and tries different session / session pool operations. + for j := 0; j < 1000; j++ { + if idx%10 == 0 && j >= 900 { + // Close the pool in selected set of workers during the middle of the test. + pool.close() + } + // Take a write sessions ~ 20% of the times. + takeWrite := rand.Intn(5) == 4 + var ( + sh *sessionHandle + gotErr error + ) + if takeWrite { + sh, gotErr = pool.takeWriteSession(context.Background()) + } else { + sh, gotErr = pool.take(context.Background()) + } + if gotErr != nil { + if pool.isValid() { + t.Errorf("%v.%v: pool.take returns error when pool is still valid: %v", ti, idx, gotErr) + } + if wantErr := errInvalidSessionPool(); !testEqual(gotErr, wantErr) { + t.Errorf("%v.%v: got error when pool is closed: %v, want %v", ti, idx, gotErr, wantErr) + } + continue + } + // Verify if session is valid when session pool is valid. Note that if session pool is invalid after sh is taken, + // then sh might be invalidated by healthcheck workers. + if (sh.getID() == "" || sh.session == nil || !sh.session.isValid()) && pool.isValid() { + t.Errorf("%v.%v.%v: pool.take returns invalid session %v", ti, idx, takeWrite, sh.session) + } + if takeWrite && sh.getTransactionID() == nil { + t.Errorf("%v.%v: pool.takeWriteSession returns session %v without transaction", ti, idx, sh.session) + } + if rand.Intn(100) < idx { + // Random sleep before destroying/recycling the session, to give healthcheck worker a chance to step in. + <-time.After(time.Duration(rand.Int63n(int64(cfg.HealthCheckInterval)))) + } + if rand.Intn(100) < idx { + // destroy the session. + sh.destroy() + continue + } + // recycle the session. + sh.recycle() + } + }(i, sp, sc) + } + wg.Wait() + sp.hc.close() + // Here the states of healthchecker, session pool and mockclient are stable. + idleSessions := map[string]bool{} + hcSessions := map[string]bool{} + mockSessions := sc.DumpSessions() + // Dump session pool's idle list. + for sl := sp.idleList.Front(); sl != nil; sl = sl.Next() { + s := sl.Value.(*session) + if idleSessions[s.getID()] { + t.Errorf("%v: found duplicated session in idle list: %v", ti, s.getID()) + } + idleSessions[s.getID()] = true + } + for sl := sp.idleWriteList.Front(); sl != nil; sl = sl.Next() { + s := sl.Value.(*session) + if idleSessions[s.getID()] { + t.Errorf("%v: found duplicated session in idle write list: %v", ti, s.getID()) + } + idleSessions[s.getID()] = true + } + sp.mu.Lock() + if int(sp.numOpened) != len(idleSessions) { + t.Errorf("%v: number of opened sessions (%v) != number of idle sessions (%v)", ti, sp.numOpened, len(idleSessions)) + } + if sp.createReqs != 0 { + t.Errorf("%v: number of pending session creations = %v, want 0", ti, sp.createReqs) + } + // Dump healthcheck queue. + for _, s := range sp.hc.queue.sessions { + if hcSessions[s.getID()] { + t.Errorf("%v: found duplicated session in healthcheck queue: %v", ti, s.getID()) + } + hcSessions[s.getID()] = true + } + sp.mu.Unlock() + + // Verify that idleSessions == hcSessions == mockSessions. + if !testEqual(idleSessions, hcSessions) { + t.Errorf("%v: sessions in idle list (%v) != sessions in healthcheck queue (%v)", ti, idleSessions, hcSessions) + } + if !testEqual(hcSessions, mockSessions) { + t.Errorf("%v: sessions in healthcheck queue (%v) != sessions in mockclient (%v)", ti, hcSessions, mockSessions) + } + sp.close() + mockSessions = sc.DumpSessions() + if len(mockSessions) != 0 { + t.Errorf("Found live sessions: %v", mockSessions) + } + } +} + +// TestMaintainer checks the session pool maintainer maintains the number of sessions in the following cases +// 1. On initialization of session pool, replenish session pool to meet MinOpened or MaxIdle. +// 2. On increased session usage, provision extra MaxIdle sessions. +// 3. After the surge passes, scale down the session pool accordingly. +func TestMaintainer(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + var ( + minOpened uint64 = 5 + maxIdle uint64 = 4 + ) + sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: minOpened, MaxIdle: maxIdle}) + sampleInterval := sp.SessionPoolConfig.healthCheckSampleInterval + hcInterval := sp.SessionPoolConfig.HealthCheckInterval + defer cancel() + + <-time.After(sampleInterval * 1) + sp.mu.Lock() + if sp.numOpened != 5 { + t.Errorf("Replenish. Expect %d open, got %d", sp.MinOpened, sp.numOpened) + } + sp.mu.Unlock() + + // To save test time, we are not creating many sessions, because the time to create sessions will have impact on the decision on sessionsToKeep. We also parallelize the take and recycle process. + shs := make([]*sessionHandle, 10) + for i := 0; i < len(shs); i++ { + var err error + shs[i], err = sp.take(context.Background()) + if err != nil { + t.Errorf("cannot get session from session pool: %v", err) + } + } + sp.mu.Lock() + if sp.numOpened != 10 { + t.Errorf("Scale out from normal use. Expect %d open, got %d", 10, sp.numOpened) + } + sp.mu.Unlock() + + <-time.After(sampleInterval) + for _, sh := range shs[:7] { + sh.recycle() + } + + <-time.After(sampleInterval * 2) + sp.mu.Lock() + if sp.numOpened != 7 { + t.Errorf("Keep extra MaxIdle sessions. Expect %d open, got %d", 7, sp.numOpened) + } + sp.mu.Unlock() + + for _, sh := range shs[7:] { + sh.recycle() + } + <-time.After(sampleInterval*10 + hcInterval) + sp.mu.Lock() + if sp.numOpened != minOpened { + t.Errorf("Scale down. Expect %d open, got %d", minOpened, sp.numOpened) + } + sp.mu.Unlock() +} + +func (s1 *session) Equal(s2 *session) bool { + return s1.client == s2.client && + s1.id == s2.id && + s1.pool == s2.pool && + s1.createTime == s2.createTime && + s1.valid == s2.valid && + s1.hcIndex == s2.hcIndex && + s1.idleList == s2.idleList && + s1.nextCheck.Equal(s2.nextCheck) && + s1.checkingHealth == s2.checkingHealth && + testEqual(s1.md, s2.md) && + bytes.Equal(s1.tx, s2.tx) +} diff --git a/vendor/cloud.google.com/go/spanner/statement.go b/vendor/cloud.google.com/go/spanner/statement.go new file mode 100644 index 0000000000..314bdd0f9f --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/statement.go @@ -0,0 +1,101 @@ +/* +Copyright 2017 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 spanner + +import ( + "errors" + "fmt" + + proto3 "github.com/golang/protobuf/ptypes/struct" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" +) + +// A Statement is a SQL query with named parameters. +// +// A parameter placeholder consists of '@' followed by the parameter name. +// Parameter names consist of any combination of letters, numbers, and +// underscores. Names may be entirely numeric (e.g., "WHERE m.id = @5"). +// Parameters may appear anywhere that a literal value is expected. The same +// parameter name may be used more than once. It is an error to execute a +// statement with unbound parameters. On the other hand, it is allowable to +// bind parameter names that are not used. +// +// See the documentation of the Row type for how Go types are mapped to Cloud +// Spanner types. +type Statement struct { + SQL string + Params map[string]interface{} +} + +// NewStatement returns a Statement with the given SQL and an empty Params map. +func NewStatement(sql string) Statement { + return Statement{SQL: sql, Params: map[string]interface{}{}} +} + +// errBindParam returns error for not being able to bind parameter to query request. +func errBindParam(k string, v interface{}, err error) error { + if err == nil { + return nil + } + se, ok := toSpannerError(err).(*Error) + if !ok { + return spannerErrorf(codes.InvalidArgument, "failed to bind query parameter(name: %q, value: %v), error = <%v>", k, v, err) + } + se.decorate(fmt.Sprintf("failed to bind query parameter(name: %q, value: %v)", k, v)) + return se +} + +var ( + errNilParam = errors.New("use T(nil), not nil") + errNoType = errors.New("no type information") +) + +// bindParams binds parameters in a Statement to a sppb.ExecuteSqlRequest or sppb.PartitionQueryRequest. +func (s *Statement) bindParams(i interface{}) error { + params := &proto3.Struct{ + Fields: map[string]*proto3.Value{}, + } + paramTypes := map[string]*sppb.Type{} + for k, v := range s.Params { + if v == nil { + return errBindParam(k, v, errNilParam) + } + val, t, err := encodeValue(v) + if err != nil { + return errBindParam(k, v, err) + } + if t == nil { // should not happen, because of nil check above + return errBindParam(k, v, errNoType) + } + params.Fields[k] = val + paramTypes[k] = t + } + + switch r := i.(type) { + default: + return fmt.Errorf("failed to bind query parameter, unexpected request type: %v", r) + case *sppb.ExecuteSqlRequest: + r.Params = params + r.ParamTypes = paramTypes + case *sppb.PartitionQueryRequest: + r.Params = params + r.ParamTypes = paramTypes + } + return nil +} diff --git a/vendor/cloud.google.com/go/spanner/statement_test.go b/vendor/cloud.google.com/go/spanner/statement_test.go new file mode 100644 index 0000000000..6d6f96b1af --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/statement_test.go @@ -0,0 +1,199 @@ +/* +Copyright 2017 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 spanner + +import ( + "math" + "testing" + "time" + + "cloud.google.com/go/civil" + + "github.com/golang/protobuf/proto" + proto3 "github.com/golang/protobuf/ptypes/struct" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// Test Statement.bindParams. +func TestBindParams(t *testing.T) { + // Verify Statement.bindParams generates correct values and types. + st := Statement{ + SQL: "SELECT id from t_foo WHERE col = @var", + Params: map[string]interface{}{"var": nil}, + } + want := &sppb.ExecuteSqlRequest{ + Params: &proto3.Struct{ + Fields: map[string]*proto3.Value{"var": nil}, + }, + ParamTypes: map[string]*sppb.Type{"var": nil}, + } + var ( + t1, _ = time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z") + // Boundaries + t2, _ = time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00.000000000Z") + t3, _ = time.Parse(time.RFC3339Nano, "9999-12-31T23:59:59.999999999Z") + d1, _ = civil.ParseDate("2016-11-15") + // Boundaries + d2, _ = civil.ParseDate("0001-01-01") + d3, _ = civil.ParseDate("9999-12-31") + ) + + type staticStruct struct { + Field int `spanner:"field"` + } + + var ( + s1 = staticStruct{10} + s2 = staticStruct{20} + ) + + for _, test := range []struct { + val interface{} + wantField *proto3.Value + wantType *sppb.Type + }{ + // bool + {true, boolProto(true), boolType()}, + {NullBool{true, true}, boolProto(true), boolType()}, + {NullBool{true, false}, nullProto(), boolType()}, + {[]bool(nil), nullProto(), listType(boolType())}, + {[]bool{}, listProto(), listType(boolType())}, + {[]bool{true, false}, listProto(boolProto(true), boolProto(false)), listType(boolType())}, + {[]NullBool(nil), nullProto(), listType(boolType())}, + {[]NullBool{}, listProto(), listType(boolType())}, + {[]NullBool{{true, true}, {}}, listProto(boolProto(true), nullProto()), listType(boolType())}, + // int + {int(1), intProto(1), intType()}, + {[]int(nil), nullProto(), listType(intType())}, + {[]int{}, listProto(), listType(intType())}, + {[]int{1, 2}, listProto(intProto(1), intProto(2)), listType(intType())}, + // int64 + {int64(1), intProto(1), intType()}, + {NullInt64{5, true}, intProto(5), intType()}, + {NullInt64{5, false}, nullProto(), intType()}, + {[]int64(nil), nullProto(), listType(intType())}, + {[]int64{}, listProto(), listType(intType())}, + {[]int64{1, 2}, listProto(intProto(1), intProto(2)), listType(intType())}, + {[]NullInt64(nil), nullProto(), listType(intType())}, + {[]NullInt64{}, listProto(), listType(intType())}, + {[]NullInt64{{1, true}, {}}, listProto(intProto(1), nullProto()), listType(intType())}, + // float64 + {0.0, floatProto(0.0), floatType()}, + {math.Inf(1), floatProto(math.Inf(1)), floatType()}, + {math.Inf(-1), floatProto(math.Inf(-1)), floatType()}, + {math.NaN(), floatProto(math.NaN()), floatType()}, + {NullFloat64{2.71, true}, floatProto(2.71), floatType()}, + {NullFloat64{1.41, false}, nullProto(), floatType()}, + {[]float64(nil), nullProto(), listType(floatType())}, + {[]float64{}, listProto(), listType(floatType())}, + {[]float64{2.72, math.Inf(1)}, listProto(floatProto(2.72), floatProto(math.Inf(1))), listType(floatType())}, + {[]NullFloat64(nil), nullProto(), listType(floatType())}, + {[]NullFloat64{}, listProto(), listType(floatType())}, + {[]NullFloat64{{2.72, true}, {}}, listProto(floatProto(2.72), nullProto()), listType(floatType())}, + // string + {"", stringProto(""), stringType()}, + {"foo", stringProto("foo"), stringType()}, + {NullString{"bar", true}, stringProto("bar"), stringType()}, + {NullString{"bar", false}, nullProto(), stringType()}, + {[]string(nil), nullProto(), listType(stringType())}, + {[]string{}, listProto(), listType(stringType())}, + {[]string{"foo", "bar"}, listProto(stringProto("foo"), stringProto("bar")), listType(stringType())}, + {[]NullString(nil), nullProto(), listType(stringType())}, + {[]NullString{}, listProto(), listType(stringType())}, + {[]NullString{{"foo", true}, {}}, listProto(stringProto("foo"), nullProto()), listType(stringType())}, + // bytes + {[]byte{}, bytesProto([]byte{}), bytesType()}, + {[]byte{1, 2, 3}, bytesProto([]byte{1, 2, 3}), bytesType()}, + {[]byte(nil), nullProto(), bytesType()}, + {[][]byte(nil), nullProto(), listType(bytesType())}, + {[][]byte{}, listProto(), listType(bytesType())}, + {[][]byte{{1}, []byte(nil)}, listProto(bytesProto([]byte{1}), nullProto()), listType(bytesType())}, + // date + {d1, dateProto(d1), dateType()}, + {NullDate{civil.Date{}, false}, nullProto(), dateType()}, + {[]civil.Date(nil), nullProto(), listType(dateType())}, + {[]civil.Date{}, listProto(), listType(dateType())}, + {[]civil.Date{d1, d2, d3}, listProto(dateProto(d1), dateProto(d2), dateProto(d3)), listType(dateType())}, + {[]NullDate{{d2, true}, {}}, listProto(dateProto(d2), nullProto()), listType(dateType())}, + // timestamp + {t1, timeProto(t1), timeType()}, + {NullTime{}, nullProto(), timeType()}, + {[]time.Time(nil), nullProto(), listType(timeType())}, + {[]time.Time{}, listProto(), listType(timeType())}, + {[]time.Time{t1, t2, t3}, listProto(timeProto(t1), timeProto(t2), timeProto(t3)), listType(timeType())}, + {[]NullTime{{t2, true}, {}}, listProto(timeProto(t2), nullProto()), listType(timeType())}, + // Struct + { + s1, + listProto(intProto(10)), + structType(mkField("field", intType())), + }, + { + (*struct { + F1 civil.Date `spanner:""` + F2 bool + })(nil), + nullProto(), + structType( + mkField("", dateType()), + mkField("F2", boolType())), + }, + // Array-of-struct + { + []staticStruct{s1, s2}, + listProto(listProto(intProto(10)), listProto(intProto(20))), + listType(structType(mkField("field", intType()))), + }, + } { + st.Params["var"] = test.val + want.Params.Fields["var"] = test.wantField + want.ParamTypes["var"] = test.wantType + got := &sppb.ExecuteSqlRequest{} + if err := st.bindParams(got); err != nil || !proto.Equal(got, want) { + // handle NaN + if test.wantType.Code == floatType().Code && proto.MarshalTextString(got) == proto.MarshalTextString(want) { + continue + } + t.Errorf("%#v: bind result: \n(%v, %v)\nwant\n(%v, %v)\n", test.val, got, err, want, nil) + } + } + + // Verify type error reporting. + for _, test := range []struct { + val interface{} + wantErr error + }{ + { + nil, + errBindParam("var", nil, errNilParam), + }, + } { + st.Params["var"] = test.val + var got sppb.ExecuteSqlRequest + if err := st.bindParams(&got); !testEqual(err, test.wantErr) { + t.Errorf("value %#v:\ngot: %v\nwant: %v", test.val, err, test.wantErr) + } + } +} + +func TestNewStatement(t *testing.T) { + s := NewStatement("query") + if got, want := s.SQL, "query"; got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/vendor/cloud.google.com/go/spanner/timestampbound.go b/vendor/cloud.google.com/go/spanner/timestampbound.go new file mode 100644 index 0000000000..064e110dc0 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/timestampbound.go @@ -0,0 +1,240 @@ +/* +Copyright 2017 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 spanner + +import ( + "fmt" + "time" + + pbd "github.com/golang/protobuf/ptypes/duration" + pbt "github.com/golang/protobuf/ptypes/timestamp" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// timestampBoundType specifies the timestamp bound mode. +type timestampBoundType int + +const ( + strong timestampBoundType = iota // strong reads + exactStaleness // read with exact staleness + maxStaleness // read with max staleness + minReadTimestamp // read with min freshness + readTimestamp // read data at exact timestamp +) + +// TimestampBound defines how Cloud Spanner will choose a timestamp for a single +// read/query or read-only transaction. +// +// There are three types of timestamp bound: strong, bounded staleness and exact +// staleness. Strong is the default. +// +// If the Cloud Spanner database to be read is geographically distributed, stale +// read-only transactions can execute more quickly than strong or read-write +// transactions, because they are able to execute far from the leader replica. +// +// Each type of timestamp bound is discussed in detail below. A TimestampBound +// can be specified when creating transactions, see the documentation of +// spanner.Client for an example. +// +// Strong reads +// +// Strong reads are guaranteed to see the effects of all transactions that have +// committed before the start of the read. Furthermore, all rows yielded by a +// single read are consistent with each other: if any part of the read +// observes a transaction, all parts of the read see the transaction. +// +// Strong reads are not repeatable: two consecutive strong read-only +// transactions might return inconsistent results if there are concurrent +// writes. If consistency across reads is required, the reads should be +// executed within a transaction or at an exact read timestamp. +// +// Use StrongRead to create a bound of this type. +// +// Exact staleness +// +// An exact staleness timestamp bound executes reads at a user-specified timestamp. +// Reads at a timestamp are guaranteed to see a consistent prefix of the global +// transaction history: they observe modifications done by all transactions with a +// commit timestamp less than or equal to the read timestamp, and observe none of the +// modifications done by transactions with a larger commit timestamp. They will block +// until all conflicting transactions that may be assigned commit timestamps less +// than or equal to the read timestamp have finished. +// +// The timestamp can either be expressed as an absolute Cloud Spanner commit +// timestamp or a staleness relative to the current time. +// +// These modes do not require a "negotiation phase" to pick a timestamp. As a +// result, they execute slightly faster than the equivalent boundedly stale +// concurrency modes. On the other hand, boundedly stale reads usually return +// fresher results. +// +// Use ReadTimestamp and ExactStaleness to create a bound of this type. +// +// Bounded staleness +// +// Bounded staleness modes allow Cloud Spanner to pick the read timestamp, subject to +// a user-provided staleness bound. Cloud Spanner chooses the newest timestamp within +// the staleness bound that allows execution of the reads at the closest +// available replica without blocking. +// +// All rows yielded are consistent with each other: if any part of the read +// observes a transaction, all parts of the read see the transaction. Boundedly +// stale reads are not repeatable: two stale reads, even if they use the same +// staleness bound, can execute at different timestamps and thus return +// inconsistent results. +// +// Boundedly stale reads execute in two phases. The first phase negotiates a +// timestamp among all replicas needed to serve the read. In the second phase, +// reads are executed at the negotiated timestamp. +// +// As a result of this two-phase execution, bounded staleness reads are usually +// a little slower than comparable exact staleness reads. However, they are +// typically able to return fresher results, and are more likely to execute at +// the closest replica. +// +// Because the timestamp negotiation requires up-front knowledge of which rows +// will be read, it can only be used with single-use reads and single-use +// read-only transactions. +// +// Use MinReadTimestamp and MaxStaleness to create a bound of this type. +// +// Old read timestamps and garbage collection +// +// Cloud Spanner continuously garbage collects deleted and overwritten data in the +// background to reclaim storage space. This process is known as "version +// GC". By default, version GC reclaims versions after they are four hours +// old. Because of this, Cloud Spanner cannot perform reads at read timestamps more +// than four hours in the past. This restriction also applies to in-progress +// reads and/or SQL queries whose timestamps become too old while +// executing. Reads and SQL queries with too-old read timestamps fail with the +// error ErrorCode.FAILED_PRECONDITION. +type TimestampBound struct { + mode timestampBoundType + d time.Duration + t time.Time +} + +// StrongRead returns a TimestampBound that will perform reads and queries at a +// timestamp where all previously committed transactions are visible. +func StrongRead() TimestampBound { + return TimestampBound{mode: strong} +} + +// ExactStaleness returns a TimestampBound that will perform reads and queries +// at an exact staleness. +func ExactStaleness(d time.Duration) TimestampBound { + return TimestampBound{ + mode: exactStaleness, + d: d, + } +} + +// MaxStaleness returns a TimestampBound that will perform reads and queries at +// a time chosen to be at most "d" stale. +func MaxStaleness(d time.Duration) TimestampBound { + return TimestampBound{ + mode: maxStaleness, + d: d, + } +} + +// MinReadTimestamp returns a TimestampBound that bound that will perform reads +// and queries at a time chosen to be at least "t". +func MinReadTimestamp(t time.Time) TimestampBound { + return TimestampBound{ + mode: minReadTimestamp, + t: t, + } +} + +// ReadTimestamp returns a TimestampBound that will peform reads and queries at +// the given time. +func ReadTimestamp(t time.Time) TimestampBound { + return TimestampBound{ + mode: readTimestamp, + t: t, + } +} + +func (tb TimestampBound) String() string { + switch tb.mode { + case strong: + return fmt.Sprintf("(strong)") + case exactStaleness: + return fmt.Sprintf("(exactStaleness: %s)", tb.d) + case maxStaleness: + return fmt.Sprintf("(maxStaleness: %s)", tb.d) + case minReadTimestamp: + return fmt.Sprintf("(minReadTimestamp: %s)", tb.t) + case readTimestamp: + return fmt.Sprintf("(readTimestamp: %s)", tb.t) + default: + return fmt.Sprintf("{mode=%v, d=%v, t=%v}", tb.mode, tb.d, tb.t) + } +} + +// durationProto takes a time.Duration and converts it into pdb.Duration for +// calling gRPC APIs. +func durationProto(d time.Duration) *pbd.Duration { + n := d.Nanoseconds() + return &pbd.Duration{ + Seconds: n / int64(time.Second), + Nanos: int32(n % int64(time.Second)), + } +} + +// timestampProto takes a time.Time and converts it into pbt.Timestamp for calling +// gRPC APIs. +func timestampProto(t time.Time) *pbt.Timestamp { + return &pbt.Timestamp{ + Seconds: t.Unix(), + Nanos: int32(t.Nanosecond()), + } +} + +// buildTransactionOptionsReadOnly converts a spanner.TimestampBound into a sppb.TransactionOptions_ReadOnly +// transaction option, which is then used in transactional reads. +func buildTransactionOptionsReadOnly(tb TimestampBound, returnReadTimestamp bool) *sppb.TransactionOptions_ReadOnly { + pb := &sppb.TransactionOptions_ReadOnly{ + ReturnReadTimestamp: returnReadTimestamp, + } + switch tb.mode { + case strong: + pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_Strong{ + Strong: true, + } + case exactStaleness: + pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ExactStaleness{ + ExactStaleness: durationProto(tb.d), + } + case maxStaleness: + pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MaxStaleness{ + MaxStaleness: durationProto(tb.d), + } + case minReadTimestamp: + pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{ + MinReadTimestamp: timestampProto(tb.t), + } + case readTimestamp: + pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ReadTimestamp{ + ReadTimestamp: timestampProto(tb.t), + } + default: + panic(fmt.Sprintf("buildTransactionOptionsReadOnly(%v,%v)", tb, returnReadTimestamp)) + } + return pb +} diff --git a/vendor/cloud.google.com/go/spanner/timestampbound_test.go b/vendor/cloud.google.com/go/spanner/timestampbound_test.go new file mode 100644 index 0000000000..d0f68635b8 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/timestampbound_test.go @@ -0,0 +1,207 @@ +/* +Copyright 2017 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 spanner + +import ( + "testing" + "time" + + pbd "github.com/golang/protobuf/ptypes/duration" + pbt "github.com/golang/protobuf/ptypes/timestamp" + + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +// Test generating TimestampBound for strong reads. +func TestStrong(t *testing.T) { + got := StrongRead() + want := TimestampBound{mode: strong} + if !testEqual(got, want) { + t.Errorf("Strong() = %v; want %v", got, want) + } +} + +// Test generating TimestampBound for reads with exact staleness. +func TestExactStaleness(t *testing.T) { + got := ExactStaleness(10 * time.Second) + want := TimestampBound{mode: exactStaleness, d: 10 * time.Second} + if !testEqual(got, want) { + t.Errorf("ExactStaleness(10*time.Second) = %v; want %v", got, want) + } +} + +// Test generating TimestampBound for reads with max staleness. +func TestMaxStaleness(t *testing.T) { + got := MaxStaleness(10 * time.Second) + want := TimestampBound{mode: maxStaleness, d: 10 * time.Second} + if !testEqual(got, want) { + t.Errorf("MaxStaleness(10*time.Second) = %v; want %v", got, want) + } +} + +// Test generating TimestampBound for reads with minimum freshness requirement. +func TestMinReadTimestamp(t *testing.T) { + ts := time.Now() + got := MinReadTimestamp(ts) + want := TimestampBound{mode: minReadTimestamp, t: ts} + if !testEqual(got, want) { + t.Errorf("MinReadTimestamp(%v) = %v; want %v", ts, got, want) + } +} + +// Test generating TimestampBound for reads requesting data at a exact timestamp. +func TestReadTimestamp(t *testing.T) { + ts := time.Now() + got := ReadTimestamp(ts) + want := TimestampBound{mode: readTimestamp, t: ts} + if !testEqual(got, want) { + t.Errorf("ReadTimestamp(%v) = %v; want %v", ts, got, want) + } +} + +// Test TimestampBound.String. +func TestTimestampBoundString(t *testing.T) { + ts := time.Unix(1136239445, 0).UTC() + var tests = []struct { + tb TimestampBound + want string + }{ + { + tb: TimestampBound{mode: strong}, + want: "(strong)", + }, + { + tb: TimestampBound{mode: exactStaleness, d: 10 * time.Second}, + want: "(exactStaleness: 10s)", + }, + { + tb: TimestampBound{mode: maxStaleness, d: 10 * time.Second}, + want: "(maxStaleness: 10s)", + }, + { + tb: TimestampBound{mode: minReadTimestamp, t: ts}, + want: "(minReadTimestamp: 2006-01-02 22:04:05 +0000 UTC)", + }, + { + tb: TimestampBound{mode: readTimestamp, t: ts}, + want: "(readTimestamp: 2006-01-02 22:04:05 +0000 UTC)", + }, + } + for _, test := range tests { + got := test.tb.String() + if got != test.want { + t.Errorf("%#v.String():\ngot %q\nwant %q", test.tb, got, test.want) + } + } +} + +// Test time.Duration to pdb.Duration conversion. +func TestDurationProto(t *testing.T) { + var tests = []struct { + d time.Duration + want pbd.Duration + }{ + {time.Duration(0), pbd.Duration{Seconds: 0, Nanos: 0}}, + {time.Second, pbd.Duration{Seconds: 1, Nanos: 0}}, + {time.Millisecond, pbd.Duration{Seconds: 0, Nanos: 1e6}}, + {15 * time.Nanosecond, pbd.Duration{Seconds: 0, Nanos: 15}}, + {42 * time.Hour, pbd.Duration{Seconds: 151200}}, + {-(1*time.Hour + 4*time.Millisecond), pbd.Duration{Seconds: -3600, Nanos: -4e6}}, + } + for _, test := range tests { + got := durationProto(test.d) + if !testEqual(got, &test.want) { + t.Errorf("durationProto(%v) = %v; want %v", test.d, got, test.want) + } + } +} + +// Test time.Time to pbt.Timestamp conversion. +func TestTimeProto(t *testing.T) { + var tests = []struct { + t time.Time + want pbt.Timestamp + }{ + {time.Unix(0, 0), pbt.Timestamp{}}, + {time.Unix(1136239445, 12345), pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}}, + {time.Unix(-1000, 12345), pbt.Timestamp{Seconds: -1000, Nanos: 12345}}, + } + for _, test := range tests { + got := timestampProto(test.t) + if !testEqual(got, &test.want) { + t.Errorf("timestampProto(%v) = %v; want %v", test.t, got, test.want) + } + } +} + +// Test readonly transaction option builder. +func TestBuildTransactionOptionsReadOnly(t *testing.T) { + ts := time.Unix(1136239445, 12345) + var tests = []struct { + tb TimestampBound + ts bool + want sppb.TransactionOptions_ReadOnly + }{ + { + StrongRead(), false, + sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_Strong{ + Strong: true}, + ReturnReadTimestamp: false, + }, + }, + { + ExactStaleness(10 * time.Second), true, + sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_ExactStaleness{ + ExactStaleness: &pbd.Duration{Seconds: 10}}, + ReturnReadTimestamp: true, + }, + }, + { + MaxStaleness(10 * time.Second), true, + sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_MaxStaleness{ + MaxStaleness: &pbd.Duration{Seconds: 10}}, + ReturnReadTimestamp: true, + }, + }, + + { + MinReadTimestamp(ts), true, + sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{ + MinReadTimestamp: &pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}}, + ReturnReadTimestamp: true, + }, + }, + { + ReadTimestamp(ts), true, + sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_ReadTimestamp{ + ReadTimestamp: &pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}}, + ReturnReadTimestamp: true, + }, + }, + } + for _, test := range tests { + got := buildTransactionOptionsReadOnly(test.tb, test.ts) + if !testEqual(got, &test.want) { + t.Errorf("buildTransactionOptionsReadOnly(%v,%v) = %v; want %v", test.tb, test.ts, got, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/spanner/transaction.go b/vendor/cloud.google.com/go/spanner/transaction.go new file mode 100644 index 0000000000..8139316e73 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/transaction.go @@ -0,0 +1,879 @@ +/* +Copyright 2017 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 spanner + +import ( + "sync" + "time" + + "golang.org/x/net/context" + + "google.golang.org/api/iterator" + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// transactionID stores a transaction ID which uniquely identifies a transaction in Cloud Spanner. +type transactionID []byte + +// txReadEnv manages a read-transaction environment consisting of a session handle and a transaction selector. +type txReadEnv interface { + // acquire returns a read-transaction environment that can be used to perform a transactional read. + acquire(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) + // sets the transaction's read timestamp + setTimestamp(time.Time) + // release should be called at the end of every transactional read to deal with session recycling. + release(error) +} + +// txReadOnly contains methods for doing transactional reads. +type txReadOnly struct { + // read-transaction environment for performing transactional read operations. + txReadEnv +} + +// errSessionClosed returns error for using a recycled/destroyed session +func errSessionClosed(sh *sessionHandle) error { + return spannerErrorf(codes.FailedPrecondition, + "session is already recycled / destroyed: session_id = %q, rpc_client = %v", sh.getID(), sh.getClient()) +} + +// Read returns a RowIterator for reading multiple rows from the database. +func (t *txReadOnly) Read(ctx context.Context, table string, keys KeySet, columns []string) *RowIterator { + return t.ReadWithOptions(ctx, table, keys, columns, nil) +} + +// ReadUsingIndex calls ReadWithOptions with ReadOptions{Index: index}. +func (t *txReadOnly) ReadUsingIndex(ctx context.Context, table, index string, keys KeySet, columns []string) (ri *RowIterator) { + return t.ReadWithOptions(ctx, table, keys, columns, &ReadOptions{Index: index}) +} + +// ReadOptions provides options for reading rows from a database. +type ReadOptions struct { + // The index to use for reading. If non-empty, you can only read columns that are + // part of the index key, part of the primary key, or stored in the index due to + // a STORING clause in the index definition. + Index string + + // The maximum number of rows to read. A limit value less than 1 means no limit. + Limit int +} + +// ReadWithOptions returns a RowIterator for reading multiple rows from the database. +// Pass a ReadOptions to modify the read operation. +func (t *txReadOnly) ReadWithOptions(ctx context.Context, table string, keys KeySet, columns []string, opts *ReadOptions) (ri *RowIterator) { + ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.Read") + defer func() { traceEndSpan(ctx, ri.err) }() + var ( + sh *sessionHandle + ts *sppb.TransactionSelector + err error + ) + kset, err := keys.keySetProto() + if err != nil { + return &RowIterator{err: err} + } + if sh, ts, err = t.acquire(ctx); err != nil { + return &RowIterator{err: err} + } + // Cloud Spanner will return "Session not found" on bad sessions. + sid, client := sh.getID(), sh.getClient() + if sid == "" || client == nil { + // Might happen if transaction is closed in the middle of a API call. + return &RowIterator{err: errSessionClosed(sh)} + } + index := "" + limit := 0 + if opts != nil { + index = opts.Index + if opts.Limit > 0 { + limit = opts.Limit + } + } + return stream( + contextWithOutgoingMetadata(ctx, sh.getMetadata()), + func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) { + return client.StreamingRead(ctx, + &sppb.ReadRequest{ + Session: sid, + Transaction: ts, + Table: table, + Index: index, + Columns: columns, + KeySet: kset, + ResumeToken: resumeToken, + Limit: int64(limit), + }) + }, + t.setTimestamp, + t.release, + ) +} + +// errRowNotFound returns error for not being able to read the row identified by key. +func errRowNotFound(table string, key Key) error { + return spannerErrorf(codes.NotFound, "row not found(Table: %v, PrimaryKey: %v)", table, key) +} + +// ReadRow reads a single row from the database. +// +// If no row is present with the given key, then ReadRow returns an error where +// spanner.ErrCode(err) is codes.NotFound. +func (t *txReadOnly) ReadRow(ctx context.Context, table string, key Key, columns []string) (*Row, error) { + iter := t.Read(ctx, table, key, columns) + defer iter.Stop() + row, err := iter.Next() + switch err { + case iterator.Done: + return nil, errRowNotFound(table, key) + case nil: + return row, nil + default: + return nil, err + } +} + +// Query executes a query against the database. It returns a RowIterator +// for retrieving the resulting rows. +// +// Query returns only row data, without a query plan or execution statistics. +// Use QueryWithStats to get rows along with the plan and statistics. +// Use AnalyzeQuery to get just the plan. +func (t *txReadOnly) Query(ctx context.Context, statement Statement) *RowIterator { + return t.query(ctx, statement, sppb.ExecuteSqlRequest_NORMAL) +} + +// Query executes a query against the database. It returns a RowIterator +// for retrieving the resulting rows. The RowIterator will also be populated +// with a query plan and execution statistics. +func (t *txReadOnly) QueryWithStats(ctx context.Context, statement Statement) *RowIterator { + return t.query(ctx, statement, sppb.ExecuteSqlRequest_PROFILE) +} + +// AnalyzeQuery returns the query plan for statement. +func (t *txReadOnly) AnalyzeQuery(ctx context.Context, statement Statement) (*sppb.QueryPlan, error) { + iter := t.query(ctx, statement, sppb.ExecuteSqlRequest_PLAN) + for { + _, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + return nil, err + } + } + if iter.QueryPlan == nil { + return nil, spannerErrorf(codes.Internal, "query plan unavailable") + } + return iter.QueryPlan, nil +} + +func (t *txReadOnly) query(ctx context.Context, statement Statement, mode sppb.ExecuteSqlRequest_QueryMode) (ri *RowIterator) { + ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.Query") + defer func() { traceEndSpan(ctx, ri.err) }() + var ( + sh *sessionHandle + ts *sppb.TransactionSelector + err error + ) + if sh, ts, err = t.acquire(ctx); err != nil { + return &RowIterator{err: err} + } + // Cloud Spanner will return "Session not found" on bad sessions. + sid, client := sh.getID(), sh.getClient() + if sid == "" || client == nil { + // Might happen if transaction is closed in the middle of a API call. + return &RowIterator{err: errSessionClosed(sh)} + } + req := &sppb.ExecuteSqlRequest{ + Session: sid, + Transaction: ts, + Sql: statement.SQL, + QueryMode: mode, + } + if err := statement.bindParams(req); err != nil { + return &RowIterator{err: err} + } + return stream( + contextWithOutgoingMetadata(ctx, sh.getMetadata()), + func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) { + req.ResumeToken = resumeToken + return client.ExecuteStreamingSql(ctx, req) + }, + t.setTimestamp, + t.release) +} + +// txState is the status of a transaction. +type txState int + +const ( + // transaction is new, waiting to be initialized. + txNew txState = iota + // transaction is being initialized. + txInit + // transaction is active and can perform read/write. + txActive + // transaction is closed, cannot be used anymore. + txClosed +) + +// errRtsUnavailable returns error for read transaction's read timestamp being unavailable. +func errRtsUnavailable() error { + return spannerErrorf(codes.Internal, "read timestamp is unavailable") +} + +// errTxNotInitialized returns error for using an uninitialized transaction. +func errTxNotInitialized() error { + return spannerErrorf(codes.InvalidArgument, "cannot use a uninitialized transaction") +} + +// errTxClosed returns error for using a closed transaction. +func errTxClosed() error { + return spannerErrorf(codes.InvalidArgument, "cannot use a closed transaction") +} + +// errUnexpectedTxState returns error for transaction enters an unexpected state. +func errUnexpectedTxState(ts txState) error { + return spannerErrorf(codes.FailedPrecondition, "unexpected transaction state: %v", ts) +} + +// ReadOnlyTransaction provides a snapshot transaction with guaranteed +// consistency across reads, but does not allow writes. Read-only +// transactions can be configured to read at timestamps in the past. +// +// Read-only transactions do not take locks. Instead, they work by choosing a +// Cloud Spanner timestamp, then executing all reads at that timestamp. Since they do +// not acquire locks, they do not block concurrent read-write transactions. +// +// Unlike locking read-write transactions, read-only transactions never +// abort. They can fail if the chosen read timestamp is garbage collected; +// however, the default garbage collection policy is generous enough that most +// applications do not need to worry about this in practice. See the +// documentation of TimestampBound for more details. +// +// A ReadOnlyTransaction consumes resources on the server until Close is +// called. +type ReadOnlyTransaction struct { + // txReadOnly contains methods for performing transactional reads. + txReadOnly + + // singleUse indicates that the transaction can be used for only one read. + singleUse bool + + // sp is the session pool for allocating a session to execute the read-only transaction. It is set only once during initialization of the ReadOnlyTransaction. + sp *sessionPool + // mu protects concurrent access to the internal states of ReadOnlyTransaction. + mu sync.Mutex + // tx is the transaction ID in Cloud Spanner that uniquely identifies the ReadOnlyTransaction. + tx transactionID + // txReadyOrClosed is for broadcasting that transaction ID has been returned by Cloud Spanner or that transaction is closed. + txReadyOrClosed chan struct{} + // state is the current transaction status of the ReadOnly transaction. + state txState + // sh is the sessionHandle allocated from sp. + sh *sessionHandle + // rts is the read timestamp returned by transactional reads. + rts time.Time + // tb is the read staleness bound specification for transactional reads. + tb TimestampBound +} + +// errTxInitTimeout returns error for timeout in waiting for initialization of the transaction. +func errTxInitTimeout() error { + return spannerErrorf(codes.Canceled, "timeout/context canceled in waiting for transaction's initialization") +} + +// getTimestampBound returns the read staleness bound specified for the ReadOnlyTransaction. +func (t *ReadOnlyTransaction) getTimestampBound() TimestampBound { + t.mu.Lock() + defer t.mu.Unlock() + return t.tb +} + +// begin starts a snapshot read-only Transaction on Cloud Spanner. +func (t *ReadOnlyTransaction) begin(ctx context.Context) error { + var ( + locked bool + tx transactionID + rts time.Time + sh *sessionHandle + err error + ) + defer func() { + if !locked { + t.mu.Lock() + // Not necessary, just to make it clear that t.mu is being held when locked == true. + locked = true + } + if t.state != txClosed { + // Signal other initialization routines. + close(t.txReadyOrClosed) + t.txReadyOrClosed = make(chan struct{}) + } + t.mu.Unlock() + if err != nil && sh != nil { + // Got a valid session handle, but failed to initialize transaction on Cloud Spanner. + if shouldDropSession(err) { + sh.destroy() + } + // If sh.destroy was already executed, this becomes a noop. + sh.recycle() + } + }() + sh, err = t.sp.take(ctx) + if err != nil { + return err + } + err = runRetryable(contextWithOutgoingMetadata(ctx, sh.getMetadata()), func(ctx context.Context) error { + res, e := sh.getClient().BeginTransaction(ctx, &sppb.BeginTransactionRequest{ + Session: sh.getID(), + Options: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadOnly_{ + ReadOnly: buildTransactionOptionsReadOnly(t.getTimestampBound(), true), + }, + }, + }) + if e != nil { + return e + } + tx = res.Id + if res.ReadTimestamp != nil { + rts = time.Unix(res.ReadTimestamp.Seconds, int64(res.ReadTimestamp.Nanos)) + } + return nil + }) + t.mu.Lock() + locked = true // defer function will be executed with t.mu being held. + if t.state == txClosed { // During the execution of t.begin(), t.Close() was invoked. + return errSessionClosed(sh) + } + // If begin() fails, this allows other queries to take over the initialization. + t.tx = nil + if err == nil { + t.tx = tx + t.rts = rts + t.sh = sh + // State transite to txActive. + t.state = txActive + } + return err +} + +// acquire implements txReadEnv.acquire. +func (t *ReadOnlyTransaction) acquire(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) { + if err := checkNestedTxn(ctx); err != nil { + return nil, nil, err + } + if t.singleUse { + return t.acquireSingleUse(ctx) + } + return t.acquireMultiUse(ctx) +} + +func (t *ReadOnlyTransaction) acquireSingleUse(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) { + t.mu.Lock() + defer t.mu.Unlock() + switch t.state { + case txClosed: + // A closed single-use transaction can never be reused. + return nil, nil, errTxClosed() + case txNew: + t.state = txClosed + ts := &sppb.TransactionSelector{ + Selector: &sppb.TransactionSelector_SingleUse{ + SingleUse: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadOnly_{ + ReadOnly: buildTransactionOptionsReadOnly(t.tb, true), + }, + }, + }, + } + sh, err := t.sp.take(ctx) + if err != nil { + return nil, nil, err + } + // Install session handle into t, which can be used for readonly operations later. + t.sh = sh + return sh, ts, nil + } + us := t.state + // SingleUse transaction should only be in either txNew state or txClosed state. + return nil, nil, errUnexpectedTxState(us) +} + +func (t *ReadOnlyTransaction) acquireMultiUse(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) { + for { + t.mu.Lock() + switch t.state { + case txClosed: + t.mu.Unlock() + return nil, nil, errTxClosed() + case txNew: + // State transit to txInit so that no further TimestampBound change is accepted. + t.state = txInit + t.mu.Unlock() + continue + case txInit: + if t.tx != nil { + // Wait for a transaction ID to become ready. + txReadyOrClosed := t.txReadyOrClosed + t.mu.Unlock() + select { + case <-txReadyOrClosed: + // Need to check transaction state again. + continue + case <-ctx.Done(): + // The waiting for initialization is timeout, return error directly. + return nil, nil, errTxInitTimeout() + } + } + // Take the ownership of initializing the transaction. + t.tx = transactionID{} + t.mu.Unlock() + // Begin a read-only transaction. + // TODO: consider adding a transaction option which allow queries to initiate transactions by themselves. Note that this option might not be + // always good because the ID of the new transaction won't be ready till the query returns some data or completes. + if err := t.begin(ctx); err != nil { + return nil, nil, err + } + // If t.begin() succeeded, t.state should have been changed to txActive, so we can just continue here. + continue + case txActive: + sh := t.sh + ts := &sppb.TransactionSelector{ + Selector: &sppb.TransactionSelector_Id{ + Id: t.tx, + }, + } + t.mu.Unlock() + return sh, ts, nil + } + state := t.state + t.mu.Unlock() + return nil, nil, errUnexpectedTxState(state) + } +} + +func (t *ReadOnlyTransaction) setTimestamp(ts time.Time) { + t.mu.Lock() + defer t.mu.Unlock() + if t.rts.IsZero() { + t.rts = ts + } +} + +// release implements txReadEnv.release. +func (t *ReadOnlyTransaction) release(err error) { + t.mu.Lock() + sh := t.sh + t.mu.Unlock() + if sh != nil { // sh could be nil if t.acquire() fails. + if shouldDropSession(err) { + sh.destroy() + } + if t.singleUse { + // If session handle is already destroyed, this becomes a noop. + sh.recycle() + } + } +} + +// Close closes a ReadOnlyTransaction, the transaction cannot perform any reads after being closed. +func (t *ReadOnlyTransaction) Close() { + if t.singleUse { + return + } + t.mu.Lock() + if t.state != txClosed { + t.state = txClosed + close(t.txReadyOrClosed) + } + sh := t.sh + t.mu.Unlock() + if sh == nil { + return + } + // If session handle is already destroyed, this becomes a noop. + // If there are still active queries and if the recycled session is reused before they complete, Cloud Spanner will cancel them + // on behalf of the new transaction on the session. + if sh != nil { + sh.recycle() + } +} + +// Timestamp returns the timestamp chosen to perform reads and +// queries in this transaction. The value can only be read after some +// read or query has either returned some data or completed without +// returning any data. +func (t *ReadOnlyTransaction) Timestamp() (time.Time, error) { + t.mu.Lock() + defer t.mu.Unlock() + if t.rts.IsZero() { + return t.rts, errRtsUnavailable() + } + return t.rts, nil +} + +// WithTimestampBound specifies the TimestampBound to use for read or query. +// This can only be used before the first read or query is invoked. Note: +// bounded staleness is not available with general ReadOnlyTransactions; use a +// single-use ReadOnlyTransaction instead. +// +// The returned value is the ReadOnlyTransaction so calls can be chained. +func (t *ReadOnlyTransaction) WithTimestampBound(tb TimestampBound) *ReadOnlyTransaction { + t.mu.Lock() + defer t.mu.Unlock() + if t.state == txNew { + // Only allow to set TimestampBound before the first query. + t.tb = tb + } + return t +} + +// ReadWriteTransaction provides a locking read-write transaction. +// +// This type of transaction is the only way to write data into Cloud Spanner; +// (*Client).Apply and (*Client).ApplyAtLeastOnce use transactions +// internally. These transactions rely on pessimistic locking and, if +// necessary, two-phase commit. Locking read-write transactions may abort, +// requiring the application to retry. However, the interface exposed by +// (*Client).ReadWriteTransaction eliminates the need for applications to write +// retry loops explicitly. +// +// Locking transactions may be used to atomically read-modify-write data +// anywhere in a database. This type of transaction is externally consistent. +// +// Clients should attempt to minimize the amount of time a transaction is +// active. Faster transactions commit with higher probability and cause less +// contention. Cloud Spanner attempts to keep read locks active as long as the +// transaction continues to do reads. Long periods of inactivity at the client +// may cause Cloud Spanner to release a transaction's locks and abort it. +// +// Reads performed within a transaction acquire locks on the data being +// read. Writes can only be done at commit time, after all reads have been +// completed. Conceptually, a read-write transaction consists of zero or more +// reads or SQL queries followed by a commit. +// +// See (*Client).ReadWriteTransaction for an example. +// +// Semantics +// +// Cloud Spanner can commit the transaction if all read locks it acquired are still +// valid at commit time, and it is able to acquire write locks for all +// writes. Cloud Spanner can abort the transaction for any reason. If a commit +// attempt returns ABORTED, Cloud Spanner guarantees that the transaction has not +// modified any user data in Cloud Spanner. +// +// Unless the transaction commits, Cloud Spanner makes no guarantees about how long +// the transaction's locks were held for. It is an error to use Cloud Spanner locks +// for any sort of mutual exclusion other than between Cloud Spanner transactions +// themselves. +// +// Aborted transactions +// +// Application code does not need to retry explicitly; RunInTransaction will +// automatically retry a transaction if an attempt results in an abort. The +// lock priority of a transaction increases after each prior aborted +// transaction, meaning that the next attempt has a slightly better chance of +// success than before. +// +// Under some circumstances (e.g., many transactions attempting to modify the +// same row(s)), a transaction can abort many times in a short period before +// successfully committing. Thus, it is not a good idea to cap the number of +// retries a transaction can attempt; instead, it is better to limit the total +// amount of wall time spent retrying. +// +// Idle transactions +// +// A transaction is considered idle if it has no outstanding reads or SQL +// queries and has not started a read or SQL query within the last 10 +// seconds. Idle transactions can be aborted by Cloud Spanner so that they don't hold +// on to locks indefinitely. In that case, the commit will fail with error +// ABORTED. +// +// If this behavior is undesirable, periodically executing a simple SQL query +// in the transaction (e.g., SELECT 1) prevents the transaction from becoming +// idle. +type ReadWriteTransaction struct { + // txReadOnly contains methods for performing transactional reads. + txReadOnly + // sh is the sessionHandle allocated from sp. It is set only once during the initialization of ReadWriteTransaction. + sh *sessionHandle + // tx is the transaction ID in Cloud Spanner that uniquely identifies the ReadWriteTransaction. + // It is set only once in ReadWriteTransaction.begin() during the initialization of ReadWriteTransaction. + tx transactionID + // mu protects concurrent access to the internal states of ReadWriteTransaction. + mu sync.Mutex + // state is the current transaction status of the read-write transaction. + state txState + // wb is the set of buffered mutations waiting to be committed. + wb []*Mutation +} + +// BufferWrite adds a list of mutations to the set of updates that will be +// applied when the transaction is committed. It does not actually apply the +// write until the transaction is committed, so the operation does not +// block. The effects of the write won't be visible to any reads (including +// reads done in the same transaction) until the transaction commits. +// +// See the example for Client.ReadWriteTransaction. +func (t *ReadWriteTransaction) BufferWrite(ms []*Mutation) error { + t.mu.Lock() + defer t.mu.Unlock() + if t.state == txClosed { + return errTxClosed() + } + if t.state != txActive { + return errUnexpectedTxState(t.state) + } + t.wb = append(t.wb, ms...) + return nil +} + +// acquire implements txReadEnv.acquire. +func (t *ReadWriteTransaction) acquire(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) { + ts := &sppb.TransactionSelector{ + Selector: &sppb.TransactionSelector_Id{ + Id: t.tx, + }, + } + t.mu.Lock() + defer t.mu.Unlock() + switch t.state { + case txClosed: + return nil, nil, errTxClosed() + case txActive: + return t.sh, ts, nil + } + return nil, nil, errUnexpectedTxState(t.state) +} + +// release implements txReadEnv.release. +func (t *ReadWriteTransaction) release(err error) { + t.mu.Lock() + sh := t.sh + t.mu.Unlock() + if sh != nil && shouldDropSession(err) { + sh.destroy() + } +} + +func beginTransaction(ctx context.Context, sid string, client sppb.SpannerClient) (transactionID, error) { + var tx transactionID + err := runRetryable(ctx, func(ctx context.Context) error { + res, e := client.BeginTransaction(ctx, &sppb.BeginTransactionRequest{ + Session: sid, + Options: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadWrite_{ + ReadWrite: &sppb.TransactionOptions_ReadWrite{}, + }, + }, + }) + if e != nil { + return e + } + tx = res.Id + return nil + }) + if err != nil { + return nil, err + } + return tx, nil +} + +// begin starts a read-write transacton on Cloud Spanner, it is always called before any of the public APIs. +func (t *ReadWriteTransaction) begin(ctx context.Context) error { + if t.tx != nil { + t.state = txActive + return nil + } + tx, err := beginTransaction(contextWithOutgoingMetadata(ctx, t.sh.getMetadata()), t.sh.getID(), t.sh.getClient()) + if err == nil { + t.tx = tx + t.state = txActive + return nil + } + if shouldDropSession(err) { + t.sh.destroy() + } + return err +} + +// commit tries to commit a readwrite transaction to Cloud Spanner. It also returns the commit timestamp for the transactions. +func (t *ReadWriteTransaction) commit(ctx context.Context) (time.Time, error) { + var ts time.Time + t.mu.Lock() + t.state = txClosed // No further operations after commit. + mPb, err := mutationsProto(t.wb) + t.mu.Unlock() + if err != nil { + return ts, err + } + // In case that sessionHandle was destroyed but transaction body fails to report it. + sid, client := t.sh.getID(), t.sh.getClient() + if sid == "" || client == nil { + return ts, errSessionClosed(t.sh) + } + err = runRetryable(contextWithOutgoingMetadata(ctx, t.sh.getMetadata()), func(ctx context.Context) error { + var trailer metadata.MD + res, e := client.Commit(ctx, &sppb.CommitRequest{ + Session: sid, + Transaction: &sppb.CommitRequest_TransactionId{ + TransactionId: t.tx, + }, + Mutations: mPb, + }, grpc.Trailer(&trailer)) + if e != nil { + return toSpannerErrorWithMetadata(e, trailer) + } + if tstamp := res.GetCommitTimestamp(); tstamp != nil { + ts = time.Unix(tstamp.Seconds, int64(tstamp.Nanos)) + } + return nil + }) + if shouldDropSession(err) { + t.sh.destroy() + } + return ts, err +} + +// rollback is called when a commit is aborted or the transaction body runs into error. +func (t *ReadWriteTransaction) rollback(ctx context.Context) { + t.mu.Lock() + // Forbid further operations on rollbacked transaction. + t.state = txClosed + t.mu.Unlock() + // In case that sessionHandle was destroyed but transaction body fails to report it. + sid, client := t.sh.getID(), t.sh.getClient() + if sid == "" || client == nil { + return + } + err := runRetryable(contextWithOutgoingMetadata(ctx, t.sh.getMetadata()), func(ctx context.Context) error { + _, e := client.Rollback(ctx, &sppb.RollbackRequest{ + Session: sid, + TransactionId: t.tx, + }) + return e + }) + if shouldDropSession(err) { + t.sh.destroy() + } + return +} + +// runInTransaction executes f under a read-write transaction context. +func (t *ReadWriteTransaction) runInTransaction(ctx context.Context, f func(context.Context, *ReadWriteTransaction) error) (time.Time, error) { + var ( + ts time.Time + err error + ) + if err = f(context.WithValue(ctx, transactionInProgressKey{}, 1), t); err == nil { + // Try to commit if transaction body returns no error. + ts, err = t.commit(ctx) + } + if err != nil { + if isAbortErr(err) { + // Retry the transaction using the same session on ABORT error. + // Cloud Spanner will create the new transaction with the previous one's wound-wait priority. + err = errRetry(err) + return ts, err + } + // Not going to commit, according to API spec, should rollback the transaction. + t.rollback(ctx) + return ts, err + } + // err == nil, return commit timestamp. + return ts, nil +} + +// writeOnlyTransaction provides the most efficient way of doing write-only transactions. It essentially does blind writes to Cloud Spanner. +type writeOnlyTransaction struct { + // sp is the session pool which writeOnlyTransaction uses to get Cloud Spanner sessions for blind writes. + sp *sessionPool +} + +// applyAtLeastOnce commits a list of mutations to Cloud Spanner at least once, unless one of the following happens: +// 1) Context times out. +// 2) An unretryable error (e.g. database not found) occurs. +// 3) There is a malformed Mutation object. +func (t *writeOnlyTransaction) applyAtLeastOnce(ctx context.Context, ms ...*Mutation) (time.Time, error) { + var ( + ts time.Time + sh *sessionHandle + ) + mPb, err := mutationsProto(ms) + if err != nil { + // Malformed mutation found, just return the error. + return ts, err + } + err = runRetryable(ctx, func(ct context.Context) error { + var e error + var trailers metadata.MD + if sh == nil || sh.getID() == "" || sh.getClient() == nil { + // No usable session for doing the commit, take one from pool. + sh, e = t.sp.take(ctx) + if e != nil { + // sessionPool.Take already retries for session creations/retrivals. + return e + } + } + res, e := sh.getClient().Commit(contextWithOutgoingMetadata(ctx, sh.getMetadata()), &sppb.CommitRequest{ + Session: sh.getID(), + Transaction: &sppb.CommitRequest_SingleUseTransaction{ + SingleUseTransaction: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadWrite_{ + ReadWrite: &sppb.TransactionOptions_ReadWrite{}, + }, + }, + }, + Mutations: mPb, + }, grpc.Trailer(&trailers)) + if e != nil { + if isAbortErr(e) { + // Mask ABORT error as retryable, because aborted transactions are allowed to be retried. + return errRetry(toSpannerErrorWithMetadata(e, trailers)) + } + if shouldDropSession(e) { + // Discard the bad session. + sh.destroy() + } + return e + } + if tstamp := res.GetCommitTimestamp(); tstamp != nil { + ts = time.Unix(tstamp.Seconds, int64(tstamp.Nanos)) + } + return nil + }) + if sh != nil { + sh.recycle() + } + return ts, err +} + +// isAbortedErr returns true if the error indicates that an gRPC call is aborted on the server side. +func isAbortErr(err error) bool { + if err == nil { + return false + } + if ErrCode(err) == codes.Aborted { + return true + } + return false +} diff --git a/vendor/cloud.google.com/go/spanner/transaction_test.go b/vendor/cloud.google.com/go/spanner/transaction_test.go new file mode 100644 index 0000000000..a78049a622 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/transaction_test.go @@ -0,0 +1,222 @@ +/* +Copyright 2017 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 spanner + +import ( + "errors" + "sync" + "testing" + "time" + + "cloud.google.com/go/spanner/internal/testutil" + + "golang.org/x/net/context" + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" +) + +var ( + errAbrt = spannerErrorf(codes.Aborted, "") + errUsr = errors.New("error") +) + +// setup sets up a Client using mockclient +func mockClient(t *testing.T) (*sessionPool, *testutil.MockCloudSpannerClient, *Client) { + var ( + mc = testutil.NewMockCloudSpannerClient(t) + spc = SessionPoolConfig{} + database = "mockdb" + ) + spc.getRPCClient = func() (sppb.SpannerClient, error) { + return mc, nil + } + sp, err := newSessionPool(database, spc, nil) + if err != nil { + t.Fatalf("cannot create session pool: %v", err) + } + return sp, mc, &Client{ + database: database, + idleSessions: sp, + } +} + +// TestReadOnlyAcquire tests acquire for ReadOnlyTransaction. +func TestReadOnlyAcquire(t *testing.T) { + t.Parallel() + _, mc, client := mockClient(t) + defer client.Close() + mc.SetActions( + testutil.Action{"BeginTransaction", errUsr}, + testutil.Action{"BeginTransaction", nil}, + testutil.Action{"BeginTransaction", nil}, + ) + + // Singleuse should only be used once. + txn := client.Single() + defer txn.Close() + _, _, e := txn.acquire(context.Background()) + if e != nil { + t.Errorf("Acquire for single use, got %v, want nil.", e) + } + _, _, e = txn.acquire(context.Background()) + if wantErr := errTxClosed(); !testEqual(e, wantErr) { + t.Errorf("Second acquire for single use, got %v, want %v.", e, wantErr) + } + // Multiuse can recover from acquire failure. + txn = client.ReadOnlyTransaction() + _, _, e = txn.acquire(context.Background()) + if wantErr := toSpannerError(errUsr); !testEqual(e, wantErr) { + t.Errorf("Acquire for multi use, got %v, want %v.", e, wantErr) + } + _, _, e = txn.acquire(context.Background()) + if e != nil { + t.Errorf("Acquire for multi use, got %v, want nil.", e) + } + txn.Close() + // Multiuse can not be used after close. + _, _, e = txn.acquire(context.Background()) + if wantErr := errTxClosed(); !testEqual(e, wantErr) { + t.Errorf("Second acquire for multi use, got %v, want %v.", e, wantErr) + } + // Multiuse can be acquired concurrently. + txn = client.ReadOnlyTransaction() + defer txn.Close() + mc.Freeze() + var ( + sh1 *sessionHandle + sh2 *sessionHandle + ts1 *sppb.TransactionSelector + ts2 *sppb.TransactionSelector + wg = sync.WaitGroup{} + ) + acquire := func(sh **sessionHandle, ts **sppb.TransactionSelector) { + defer wg.Done() + var e error + *sh, *ts, e = txn.acquire(context.Background()) + if e != nil { + t.Errorf("Concurrent acquire for multiuse, got %v, expect nil.", e) + } + } + wg.Add(2) + go acquire(&sh1, &ts1) + go acquire(&sh2, &ts2) + <-time.After(100 * time.Millisecond) + mc.Unfreeze() + wg.Wait() + if !testEqual(sh1.session, sh2.session) { + t.Errorf("Expect acquire to get same session handle, got %v and %v.", sh1, sh2) + } + if !testEqual(ts1, ts2) { + t.Errorf("Expect acquire to get same transaction selector, got %v and %v.", ts1, ts2) + } +} + +// TestRetryOnAbort tests transaction retries on abort. +func TestRetryOnAbort(t *testing.T) { + t.Parallel() + _, mc, client := mockClient(t) + defer client.Close() + // commit in writeOnlyTransaction + mc.SetActions( + testutil.Action{"Commit", errAbrt}, // abort on first commit + testutil.Action{"Commit", nil}, + ) + + ms := []*Mutation{ + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(1), "Foo", int64(50)}), + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(2), "Bar", int64(1)}), + } + if _, e := client.Apply(context.Background(), ms, ApplyAtLeastOnce()); e != nil { + t.Errorf("applyAtLeastOnce retry on abort, got %v, want nil.", e) + } + // begin and commit in ReadWriteTransaction + mc.SetActions( + testutil.Action{"BeginTransaction", nil}, // let takeWriteSession succeed and get a session handle + testutil.Action{"Commit", errAbrt}, // let first commit fail and retry will begin new transaction + testutil.Action{"BeginTransaction", errAbrt}, // this time we can fail the begin attempt + testutil.Action{"BeginTransaction", nil}, + testutil.Action{"Commit", nil}, + ) + + if _, e := client.Apply(context.Background(), ms); e != nil { + t.Errorf("ReadWriteTransaction retry on abort, got %v, want nil.", e) + } +} + +// TestBadSession tests bad session (session not found error). +// TODO: session closed from transaction close +func TestBadSession(t *testing.T) { + t.Parallel() + ctx := context.Background() + sp, mc, client := mockClient(t) + defer client.Close() + var sid string + // Prepare a session, get the session id for use in testing. + if s, e := sp.take(ctx); e != nil { + t.Fatal("Prepare session failed.") + } else { + sid = s.getID() + s.recycle() + } + + wantErr := spannerErrorf(codes.NotFound, "Session not found: %v", sid) + // ReadOnlyTransaction + mc.SetActions( + testutil.Action{"BeginTransaction", wantErr}, + testutil.Action{"BeginTransaction", wantErr}, + testutil.Action{"BeginTransaction", wantErr}, + ) + txn := client.ReadOnlyTransaction() + defer txn.Close() + if _, _, got := txn.acquire(ctx); !testEqual(wantErr, got) { + t.Errorf("Expect acquire to fail, got %v, want %v.", got, wantErr) + } + // The failure should recycle the session, we expect it to be used in following requests. + if got := txn.Query(ctx, NewStatement("SELECT 1")); !testEqual(wantErr, got.err) { + t.Errorf("Expect Query to fail, got %v, want %v.", got.err, wantErr) + } + if got := txn.Read(ctx, "Users", KeySets(Key{"alice"}, Key{"bob"}), []string{"name", "email"}); !testEqual(wantErr, got.err) { + t.Errorf("Expect Read to fail, got %v, want %v.", got.err, wantErr) + } + // writeOnlyTransaction + ms := []*Mutation{ + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(1), "Foo", int64(50)}), + Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(2), "Bar", int64(1)}), + } + mc.SetActions(testutil.Action{"Commit", wantErr}) + if _, got := client.Apply(context.Background(), ms, ApplyAtLeastOnce()); !testEqual(wantErr, got) { + t.Errorf("Expect applyAtLeastOnce to fail, got %v, want %v.", got, wantErr) + } +} + +func TestFunctionErrorReturned(t *testing.T) { + t.Parallel() + _, mc, client := mockClient(t) + defer client.Close() + mc.SetActions( + testutil.Action{"BeginTransaction", nil}, + testutil.Action{"Rollback", nil}, + ) + + want := errors.New("an error") + _, got := client.ReadWriteTransaction(context.Background(), + func(context.Context, *ReadWriteTransaction) error { return want }) + if got != want { + t.Errorf("got <%v>, want <%v>", got, want) + } + mc.CheckActionsConsumed() +} diff --git a/vendor/cloud.google.com/go/spanner/util.go b/vendor/cloud.google.com/go/spanner/util.go new file mode 100644 index 0000000000..f4f2b25bd3 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/util.go @@ -0,0 +1,33 @@ +/* +Copyright 2017 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 spanner + +// maxUint64 returns the maximum of two uint64 +func maxUint64(a, b uint64) uint64 { + if a > b { + return a + } + return b +} + +// minUint64 returns the minimum of two uint64 +func minUint64(a, b uint64) uint64 { + if a > b { + return b + } + return a +} diff --git a/vendor/cloud.google.com/go/spanner/util_test.go b/vendor/cloud.google.com/go/spanner/util_test.go new file mode 100644 index 0000000000..270d2ae264 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/util_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2017 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 spanner + +import ( + "cloud.google.com/go/internal/testutil" + "github.com/google/go-cmp/cmp" +) + +func testEqual(a, b interface{}) bool { + return testutil.Equal(a, b, + cmp.AllowUnexported(TimestampBound{}, Error{}, Mutation{}, Row{}, + Partition{}, BatchReadOnlyTransactionID{})) +} diff --git a/vendor/cloud.google.com/go/spanner/value.go b/vendor/cloud.google.com/go/spanner/value.go new file mode 100644 index 0000000000..8fa28914d0 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/value.go @@ -0,0 +1,1608 @@ +/* +Copyright 2017 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 spanner + +import ( + "encoding/base64" + "fmt" + "math" + "reflect" + "strconv" + "time" + + "cloud.google.com/go/civil" + "cloud.google.com/go/internal/fields" + proto "github.com/golang/protobuf/proto" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" + "google.golang.org/grpc/codes" +) + +const commitTimestampPlaceholderString = "spanner.commit_timestamp()" + +var ( + // CommitTimestamp is a special value used to tell Cloud Spanner + // to insert the commit timestamp of the transaction into a column. + // It can be used in a Mutation, or directly used in + // InsertStruct or InsertMap. See ExampleCommitTimestamp. + // This is just a placeholder and the actual value stored in this + // variable has no meaning. + CommitTimestamp time.Time = commitTimestamp + commitTimestamp = time.Unix(0, 0).In(time.FixedZone("CommitTimestamp placeholder", 0xDB)) +) + +// NullInt64 represents a Cloud Spanner INT64 that may be NULL. +type NullInt64 struct { + Int64 int64 + Valid bool // Valid is true if Int64 is not NULL. +} + +// String implements Stringer.String for NullInt64 +func (n NullInt64) String() string { + if !n.Valid { + return fmt.Sprintf("%v", "") + } + return fmt.Sprintf("%v", n.Int64) +} + +// NullString represents a Cloud Spanner STRING that may be NULL. +type NullString struct { + StringVal string + Valid bool // Valid is true if StringVal is not NULL. +} + +// String implements Stringer.String for NullString +func (n NullString) String() string { + if !n.Valid { + return fmt.Sprintf("%v", "") + } + return fmt.Sprintf("%q", n.StringVal) +} + +// NullFloat64 represents a Cloud Spanner FLOAT64 that may be NULL. +type NullFloat64 struct { + Float64 float64 + Valid bool // Valid is true if Float64 is not NULL. +} + +// Cloud Spanner STRUCT (aka STRUCT) values (https://cloud.google.com/spanner/docs/data-types#struct-type) +// can be represented by a Go struct value. +// The spanner.StructType of such values is built from the field types and field tag information +// of the Go struct. If a field in the struct type definition has a "spanner:" tag, +// then the value of the "spanner" key in the tag is used as the name for that field in the +// built spanner.StructType, otherwise the field name in the struct definition is used. To specify a +// field with an empty field name in a Cloud Spanner STRUCT type, use the `spanner:""` tag +// annotation against the corresponding field in the Go struct's type definition. +// +// A STRUCT value can contain STRUCT-typed and Array-of-STRUCT typed fields and these can be +// specified using named struct-typed and []struct-typed fields inside a Go struct. However, +// embedded struct fields are not allowed. Unexported struct fields are ignored. +// +// NULL STRUCT values in Cloud Spanner are typed. A nil pointer to a Go struct value can be used to +// specify a NULL STRUCT value of the corresponding spanner.StructType. Nil and empty slices of a +// Go STRUCT type can be used to specify NULL and empty array values respectively of the +// corresponding spanner.StructType. A slice of pointers to a Go struct type can be used to specify +// an array of NULL-able STRUCT values. + +// String implements Stringer.String for NullFloat64 +func (n NullFloat64) String() string { + if !n.Valid { + return fmt.Sprintf("%v", "") + } + return fmt.Sprintf("%v", n.Float64) +} + +// NullBool represents a Cloud Spanner BOOL that may be NULL. +type NullBool struct { + Bool bool + Valid bool // Valid is true if Bool is not NULL. +} + +// String implements Stringer.String for NullBool +func (n NullBool) String() string { + if !n.Valid { + return fmt.Sprintf("%v", "") + } + return fmt.Sprintf("%v", n.Bool) +} + +// NullTime represents a Cloud Spanner TIMESTAMP that may be null. +type NullTime struct { + Time time.Time + Valid bool // Valid is true if Time is not NULL. +} + +// String implements Stringer.String for NullTime +func (n NullTime) String() string { + if !n.Valid { + return fmt.Sprintf("%s", "") + } + return fmt.Sprintf("%q", n.Time.Format(time.RFC3339Nano)) +} + +// NullDate represents a Cloud Spanner DATE that may be null. +type NullDate struct { + Date civil.Date + Valid bool // Valid is true if Date is not NULL. +} + +// String implements Stringer.String for NullDate +func (n NullDate) String() string { + if !n.Valid { + return fmt.Sprintf("%s", "") + } + return fmt.Sprintf("%q", n.Date) +} + +// NullRow represents a Cloud Spanner STRUCT that may be NULL. +// See also the document for Row. +// Note that NullRow is not a valid Cloud Spanner column Type. +type NullRow struct { + Row Row + Valid bool // Valid is true if Row is not NULL. +} + +// GenericColumnValue represents the generic encoded value and type of the +// column. See google.spanner.v1.ResultSet proto for details. This can be +// useful for proxying query results when the result types are not known in +// advance. +// +// If you populate a GenericColumnValue from a row using Row.Column or related +// methods, do not modify the contents of Type and Value. +type GenericColumnValue struct { + Type *sppb.Type + Value *proto3.Value +} + +// Decode decodes a GenericColumnValue. The ptr argument should be a pointer +// to a Go value that can accept v. +func (v GenericColumnValue) Decode(ptr interface{}) error { + return decodeValue(v.Value, v.Type, ptr) +} + +// NewGenericColumnValue creates a GenericColumnValue from Go value that is +// valid for Cloud Spanner. +func newGenericColumnValue(v interface{}) (*GenericColumnValue, error) { + value, typ, err := encodeValue(v) + if err != nil { + return nil, err + } + return &GenericColumnValue{Value: value, Type: typ}, nil +} + +// errTypeMismatch returns error for destination not having a compatible type +// with source Cloud Spanner type. +func errTypeMismatch(srcCode, elCode sppb.TypeCode, dst interface{}) error { + s := srcCode.String() + if srcCode == sppb.TypeCode_ARRAY { + s = fmt.Sprintf("%v[%v]", srcCode, elCode) + } + return spannerErrorf(codes.InvalidArgument, "type %T cannot be used for decoding %s", dst, s) +} + +// errNilSpannerType returns error for nil Cloud Spanner type in decoding. +func errNilSpannerType() error { + return spannerErrorf(codes.FailedPrecondition, "unexpected nil Cloud Spanner data type in decoding") +} + +// errNilSrc returns error for decoding from nil proto value. +func errNilSrc() error { + return spannerErrorf(codes.FailedPrecondition, "unexpected nil Cloud Spanner value in decoding") +} + +// errNilDst returns error for decoding into nil interface{}. +func errNilDst(dst interface{}) error { + return spannerErrorf(codes.InvalidArgument, "cannot decode into nil type %T", dst) +} + +// errNilArrElemType returns error for input Cloud Spanner data type being a array but without a +// non-nil array element type. +func errNilArrElemType(t *sppb.Type) error { + return spannerErrorf(codes.FailedPrecondition, "array type %v is with nil array element type", t) +} + +func errUnsupportedEmbeddedStructFields(fname string) error { + return spannerErrorf(codes.InvalidArgument, "Embedded field: %s. Embedded and anonymous fields are not allowed "+ + "when converting Go structs to Cloud Spanner STRUCT values. To create a STRUCT value with an "+ + "unnamed field, use a `spanner:\"\"` field tag.", fname) +} + +// errDstNotForNull returns error for decoding a SQL NULL value into a destination which doesn't +// support NULL values. +func errDstNotForNull(dst interface{}) error { + return spannerErrorf(codes.InvalidArgument, "destination %T cannot support NULL SQL values", dst) +} + +// errBadEncoding returns error for decoding wrongly encoded types. +func errBadEncoding(v *proto3.Value, err error) error { + return spannerErrorf(codes.FailedPrecondition, "%v wasn't correctly encoded: <%v>", v, err) +} + +func parseNullTime(v *proto3.Value, p *NullTime, code sppb.TypeCode, isNull bool) error { + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_TIMESTAMP { + return errTypeMismatch(code, sppb.TypeCode_TYPE_CODE_UNSPECIFIED, p) + } + if isNull { + *p = NullTime{} + return nil + } + x, err := getStringValue(v) + if err != nil { + return err + } + y, err := time.Parse(time.RFC3339Nano, x) + if err != nil { + return errBadEncoding(v, err) + } + p.Valid = true + p.Time = y + return nil +} + +// decodeValue decodes a protobuf Value into a pointer to a Go value, as +// specified by sppb.Type. +func decodeValue(v *proto3.Value, t *sppb.Type, ptr interface{}) error { + if v == nil { + return errNilSrc() + } + if t == nil { + return errNilSpannerType() + } + code := t.Code + acode := sppb.TypeCode_TYPE_CODE_UNSPECIFIED + if code == sppb.TypeCode_ARRAY { + if t.ArrayElementType == nil { + return errNilArrElemType(t) + } + acode = t.ArrayElementType.Code + } + _, isNull := v.Kind.(*proto3.Value_NullValue) + + // Do the decoding based on the type of ptr. + switch p := ptr.(type) { + case nil: + return errNilDst(nil) + case *string: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_STRING { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + return errDstNotForNull(ptr) + } + x, err := getStringValue(v) + if err != nil { + return err + } + *p = x + case *NullString: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_STRING { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = NullString{} + break + } + x, err := getStringValue(v) + if err != nil { + return err + } + p.Valid = true + p.StringVal = x + case *[]NullString: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_STRING { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeNullStringArray(x) + if err != nil { + return err + } + *p = y + case *[]string: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_STRING { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeStringArray(x) + if err != nil { + return err + } + *p = y + case *[]byte: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_BYTES { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getStringValue(v) + if err != nil { + return err + } + y, err := base64.StdEncoding.DecodeString(x) + if err != nil { + return errBadEncoding(v, err) + } + *p = y + case *[][]byte: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_BYTES { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeByteArray(x) + if err != nil { + return err + } + *p = y + case *int64: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_INT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + return errDstNotForNull(ptr) + } + x, err := getStringValue(v) + if err != nil { + return err + } + y, err := strconv.ParseInt(x, 10, 64) + if err != nil { + return errBadEncoding(v, err) + } + *p = y + case *NullInt64: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_INT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = NullInt64{} + break + } + x, err := getStringValue(v) + if err != nil { + return err + } + y, err := strconv.ParseInt(x, 10, 64) + if err != nil { + return errBadEncoding(v, err) + } + p.Valid = true + p.Int64 = y + case *[]NullInt64: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_INT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeNullInt64Array(x) + if err != nil { + return err + } + *p = y + case *[]int64: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_INT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeInt64Array(x) + if err != nil { + return err + } + *p = y + case *bool: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_BOOL { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + return errDstNotForNull(ptr) + } + x, err := getBoolValue(v) + if err != nil { + return err + } + *p = x + case *NullBool: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_BOOL { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = NullBool{} + break + } + x, err := getBoolValue(v) + if err != nil { + return err + } + p.Valid = true + p.Bool = x + case *[]NullBool: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_BOOL { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeNullBoolArray(x) + if err != nil { + return err + } + *p = y + case *[]bool: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_BOOL { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeBoolArray(x) + if err != nil { + return err + } + *p = y + case *float64: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_FLOAT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + return errDstNotForNull(ptr) + } + x, err := getFloat64Value(v) + if err != nil { + return err + } + *p = x + case *NullFloat64: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_FLOAT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = NullFloat64{} + break + } + x, err := getFloat64Value(v) + if err != nil { + return err + } + p.Valid = true + p.Float64 = x + case *[]NullFloat64: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_FLOAT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeNullFloat64Array(x) + if err != nil { + return err + } + *p = y + case *[]float64: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_FLOAT64 { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeFloat64Array(x) + if err != nil { + return err + } + *p = y + case *time.Time: + var nt NullTime + if isNull { + return errDstNotForNull(ptr) + } + err := parseNullTime(v, &nt, code, isNull) + if err != nil { + return nil + } + *p = nt.Time + case *NullTime: + err := parseNullTime(v, p, code, isNull) + if err != nil { + return err + } + case *[]NullTime: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_TIMESTAMP { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeNullTimeArray(x) + if err != nil { + return err + } + *p = y + case *[]time.Time: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_TIMESTAMP { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeTimeArray(x) + if err != nil { + return err + } + *p = y + case *civil.Date: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_DATE { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + return errDstNotForNull(ptr) + } + x, err := getStringValue(v) + if err != nil { + return err + } + y, err := civil.ParseDate(x) + if err != nil { + return errBadEncoding(v, err) + } + *p = y + case *NullDate: + if p == nil { + return errNilDst(p) + } + if code != sppb.TypeCode_DATE { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = NullDate{} + break + } + x, err := getStringValue(v) + if err != nil { + return err + } + y, err := civil.ParseDate(x) + if err != nil { + return errBadEncoding(v, err) + } + p.Valid = true + p.Date = y + case *[]NullDate: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_DATE { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeNullDateArray(x) + if err != nil { + return err + } + *p = y + case *[]civil.Date: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_DATE { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeDateArray(x) + if err != nil { + return err + } + *p = y + case *[]NullRow: + if p == nil { + return errNilDst(p) + } + if acode != sppb.TypeCode_STRUCT { + return errTypeMismatch(code, acode, ptr) + } + if isNull { + *p = nil + break + } + x, err := getListValue(v) + if err != nil { + return err + } + y, err := decodeRowArray(t.ArrayElementType.StructType, x) + if err != nil { + return err + } + *p = y + case *GenericColumnValue: + *p = GenericColumnValue{Type: t, Value: v} + default: + // Check if the proto encoding is for an array of structs. + if !(code == sppb.TypeCode_ARRAY && acode == sppb.TypeCode_STRUCT) { + return errTypeMismatch(code, acode, ptr) + } + vp := reflect.ValueOf(p) + if !vp.IsValid() { + return errNilDst(p) + } + if !isPtrStructPtrSlice(vp.Type()) { + // The container is not a pointer to a struct pointer slice. + return errTypeMismatch(code, acode, ptr) + } + // Only use reflection for nil detection on slow path. + // Also, IsNil panics on many types, so check it after the type check. + if vp.IsNil() { + return errNilDst(p) + } + if isNull { + // The proto Value is encoding NULL, set the pointer to struct + // slice to nil as well. + vp.Elem().Set(reflect.Zero(vp.Elem().Type())) + break + } + x, err := getListValue(v) + if err != nil { + return err + } + if err = decodeStructArray(t.ArrayElementType.StructType, x, p); err != nil { + return err + } + } + return nil +} + +// errSrvVal returns an error for getting a wrong source protobuf value in decoding. +func errSrcVal(v *proto3.Value, want string) error { + return spannerErrorf(codes.FailedPrecondition, "cannot use %v(Kind: %T) as %s Value", + v, v.GetKind(), want) +} + +// getStringValue returns the string value encoded in proto3.Value v whose +// kind is proto3.Value_StringValue. +func getStringValue(v *proto3.Value) (string, error) { + if x, ok := v.GetKind().(*proto3.Value_StringValue); ok && x != nil { + return x.StringValue, nil + } + return "", errSrcVal(v, "String") +} + +// getBoolValue returns the bool value encoded in proto3.Value v whose +// kind is proto3.Value_BoolValue. +func getBoolValue(v *proto3.Value) (bool, error) { + if x, ok := v.GetKind().(*proto3.Value_BoolValue); ok && x != nil { + return x.BoolValue, nil + } + return false, errSrcVal(v, "Bool") +} + +// getListValue returns the proto3.ListValue contained in proto3.Value v whose +// kind is proto3.Value_ListValue. +func getListValue(v *proto3.Value) (*proto3.ListValue, error) { + if x, ok := v.GetKind().(*proto3.Value_ListValue); ok && x != nil { + return x.ListValue, nil + } + return nil, errSrcVal(v, "List") +} + +// errUnexpectedNumStr returns error for decoder getting a unexpected string for +// representing special float values. +func errUnexpectedNumStr(s string) error { + return spannerErrorf(codes.FailedPrecondition, "unexpected string value %q for number", s) +} + +// getFloat64Value returns the float64 value encoded in proto3.Value v whose +// kind is proto3.Value_NumberValue / proto3.Value_StringValue. +// Cloud Spanner uses string to encode NaN, Infinity and -Infinity. +func getFloat64Value(v *proto3.Value) (float64, error) { + switch x := v.GetKind().(type) { + case *proto3.Value_NumberValue: + if x == nil { + break + } + return x.NumberValue, nil + case *proto3.Value_StringValue: + if x == nil { + break + } + switch x.StringValue { + case "NaN": + return math.NaN(), nil + case "Infinity": + return math.Inf(1), nil + case "-Infinity": + return math.Inf(-1), nil + default: + return 0, errUnexpectedNumStr(x.StringValue) + } + } + return 0, errSrcVal(v, "Number") +} + +// errNilListValue returns error for unexpected nil ListValue in decoding Cloud Spanner ARRAYs. +func errNilListValue(sqlType string) error { + return spannerErrorf(codes.FailedPrecondition, "unexpected nil ListValue in decoding %v array", sqlType) +} + +// errDecodeArrayElement returns error for failure in decoding single array element. +func errDecodeArrayElement(i int, v proto.Message, sqlType string, err error) error { + se, ok := toSpannerError(err).(*Error) + if !ok { + return spannerErrorf(codes.Unknown, + "cannot decode %v(array element %v) as %v, error = <%v>", v, i, sqlType, err) + } + se.decorate(fmt.Sprintf("cannot decode %v(array element %v) as %v", v, i, sqlType)) + return se +} + +// decodeNullStringArray decodes proto3.ListValue pb into a NullString slice. +func decodeNullStringArray(pb *proto3.ListValue) ([]NullString, error) { + if pb == nil { + return nil, errNilListValue("STRING") + } + a := make([]NullString, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, stringType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "STRING", err) + } + } + return a, nil +} + +// decodeStringArray decodes proto3.ListValue pb into a string slice. +func decodeStringArray(pb *proto3.ListValue) ([]string, error) { + if pb == nil { + return nil, errNilListValue("STRING") + } + a := make([]string, len(pb.Values)) + st := stringType() + for i, v := range pb.Values { + if err := decodeValue(v, st, &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "STRING", err) + } + } + return a, nil +} + +// decodeNullInt64Array decodes proto3.ListValue pb into a NullInt64 slice. +func decodeNullInt64Array(pb *proto3.ListValue) ([]NullInt64, error) { + if pb == nil { + return nil, errNilListValue("INT64") + } + a := make([]NullInt64, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, intType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "INT64", err) + } + } + return a, nil +} + +// decodeInt64Array decodes proto3.ListValue pb into a int64 slice. +func decodeInt64Array(pb *proto3.ListValue) ([]int64, error) { + if pb == nil { + return nil, errNilListValue("INT64") + } + a := make([]int64, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, intType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "INT64", err) + } + } + return a, nil +} + +// decodeNullBoolArray decodes proto3.ListValue pb into a NullBool slice. +func decodeNullBoolArray(pb *proto3.ListValue) ([]NullBool, error) { + if pb == nil { + return nil, errNilListValue("BOOL") + } + a := make([]NullBool, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, boolType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "BOOL", err) + } + } + return a, nil +} + +// decodeBoolArray decodes proto3.ListValue pb into a bool slice. +func decodeBoolArray(pb *proto3.ListValue) ([]bool, error) { + if pb == nil { + return nil, errNilListValue("BOOL") + } + a := make([]bool, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, boolType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "BOOL", err) + } + } + return a, nil +} + +// decodeNullFloat64Array decodes proto3.ListValue pb into a NullFloat64 slice. +func decodeNullFloat64Array(pb *proto3.ListValue) ([]NullFloat64, error) { + if pb == nil { + return nil, errNilListValue("FLOAT64") + } + a := make([]NullFloat64, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, floatType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "FLOAT64", err) + } + } + return a, nil +} + +// decodeFloat64Array decodes proto3.ListValue pb into a float64 slice. +func decodeFloat64Array(pb *proto3.ListValue) ([]float64, error) { + if pb == nil { + return nil, errNilListValue("FLOAT64") + } + a := make([]float64, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, floatType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "FLOAT64", err) + } + } + return a, nil +} + +// decodeByteArray decodes proto3.ListValue pb into a slice of byte slice. +func decodeByteArray(pb *proto3.ListValue) ([][]byte, error) { + if pb == nil { + return nil, errNilListValue("BYTES") + } + a := make([][]byte, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, bytesType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "BYTES", err) + } + } + return a, nil +} + +// decodeNullTimeArray decodes proto3.ListValue pb into a NullTime slice. +func decodeNullTimeArray(pb *proto3.ListValue) ([]NullTime, error) { + if pb == nil { + return nil, errNilListValue("TIMESTAMP") + } + a := make([]NullTime, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, timeType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "TIMESTAMP", err) + } + } + return a, nil +} + +// decodeTimeArray decodes proto3.ListValue pb into a time.Time slice. +func decodeTimeArray(pb *proto3.ListValue) ([]time.Time, error) { + if pb == nil { + return nil, errNilListValue("TIMESTAMP") + } + a := make([]time.Time, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, timeType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "TIMESTAMP", err) + } + } + return a, nil +} + +// decodeNullDateArray decodes proto3.ListValue pb into a NullDate slice. +func decodeNullDateArray(pb *proto3.ListValue) ([]NullDate, error) { + if pb == nil { + return nil, errNilListValue("DATE") + } + a := make([]NullDate, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, dateType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "DATE", err) + } + } + return a, nil +} + +// decodeDateArray decodes proto3.ListValue pb into a civil.Date slice. +func decodeDateArray(pb *proto3.ListValue) ([]civil.Date, error) { + if pb == nil { + return nil, errNilListValue("DATE") + } + a := make([]civil.Date, len(pb.Values)) + for i, v := range pb.Values { + if err := decodeValue(v, dateType(), &a[i]); err != nil { + return nil, errDecodeArrayElement(i, v, "DATE", err) + } + } + return a, nil +} + +func errNotStructElement(i int, v *proto3.Value) error { + return errDecodeArrayElement(i, v, "STRUCT", + spannerErrorf(codes.FailedPrecondition, "%v(type: %T) doesn't encode Cloud Spanner STRUCT", v, v)) +} + +// decodeRowArray decodes proto3.ListValue pb into a NullRow slice according to +// the structural information given in sppb.StructType ty. +func decodeRowArray(ty *sppb.StructType, pb *proto3.ListValue) ([]NullRow, error) { + if pb == nil { + return nil, errNilListValue("STRUCT") + } + a := make([]NullRow, len(pb.Values)) + for i := range pb.Values { + switch v := pb.Values[i].GetKind().(type) { + case *proto3.Value_ListValue: + a[i] = NullRow{ + Row: Row{ + fields: ty.Fields, + vals: v.ListValue.Values, + }, + Valid: true, + } + // Null elements not currently supported by the server, see + // https://cloud.google.com/spanner/docs/query-syntax#using-structs-with-select + case *proto3.Value_NullValue: + // no-op, a[i] is NullRow{} already + default: + return nil, errNotStructElement(i, pb.Values[i]) + } + } + return a, nil +} + +// errNilSpannerStructType returns error for unexpected nil Cloud Spanner STRUCT schema type in decoding. +func errNilSpannerStructType() error { + return spannerErrorf(codes.FailedPrecondition, "unexpected nil StructType in decoding Cloud Spanner STRUCT") +} + +// errUnnamedField returns error for decoding a Cloud Spanner STRUCT with unnamed field into a Go struct. +func errUnnamedField(ty *sppb.StructType, i int) error { + return spannerErrorf(codes.InvalidArgument, "unnamed field %v in Cloud Spanner STRUCT %+v", i, ty) +} + +// errNoOrDupGoField returns error for decoding a Cloud Spanner +// STRUCT into a Go struct which is either missing a field, or has duplicate fields. +func errNoOrDupGoField(s interface{}, f string) error { + return spannerErrorf(codes.InvalidArgument, "Go struct %+v(type %T) has no or duplicate fields for Cloud Spanner STRUCT field %v", s, s, f) +} + +// errDupColNames returns error for duplicated Cloud Spanner STRUCT field names found in decoding a Cloud Spanner STRUCT into a Go struct. +func errDupSpannerField(f string, ty *sppb.StructType) error { + return spannerErrorf(codes.InvalidArgument, "duplicated field name %q in Cloud Spanner STRUCT %+v", f, ty) +} + +// errDecodeStructField returns error for failure in decoding a single field of a Cloud Spanner STRUCT. +func errDecodeStructField(ty *sppb.StructType, f string, err error) error { + se, ok := toSpannerError(err).(*Error) + if !ok { + return spannerErrorf(codes.Unknown, + "cannot decode field %v of Cloud Spanner STRUCT %+v, error = <%v>", f, ty, err) + } + se.decorate(fmt.Sprintf("cannot decode field %v of Cloud Spanner STRUCT %+v", f, ty)) + return se +} + +// decodeStruct decodes proto3.ListValue pb into struct referenced by pointer ptr, according to +// the structural information given in sppb.StructType ty. +func decodeStruct(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error { + if reflect.ValueOf(ptr).IsNil() { + return errNilDst(ptr) + } + if ty == nil { + return errNilSpannerStructType() + } + // t holds the structural information of ptr. + t := reflect.TypeOf(ptr).Elem() + // v is the actual value that ptr points to. + v := reflect.ValueOf(ptr).Elem() + + fields, err := fieldCache.Fields(t) + if err != nil { + return toSpannerError(err) + } + seen := map[string]bool{} + for i, f := range ty.Fields { + if f.Name == "" { + return errUnnamedField(ty, i) + } + sf := fields.Match(f.Name) + if sf == nil { + return errNoOrDupGoField(ptr, f.Name) + } + if seen[f.Name] { + // We don't allow duplicated field name. + return errDupSpannerField(f.Name, ty) + } + // Try to decode a single field. + if err := decodeValue(pb.Values[i], f.Type, v.FieldByIndex(sf.Index).Addr().Interface()); err != nil { + return errDecodeStructField(ty, f.Name, err) + } + // Mark field f.Name as processed. + seen[f.Name] = true + } + return nil +} + +// isPtrStructPtrSlice returns true if ptr is a pointer to a slice of struct pointers. +func isPtrStructPtrSlice(t reflect.Type) bool { + if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice { + // t is not a pointer to a slice. + return false + } + if t = t.Elem(); t.Elem().Kind() != reflect.Ptr || t.Elem().Elem().Kind() != reflect.Struct { + // the slice that t points to is not a slice of struct pointers. + return false + } + return true +} + +// decodeStructArray decodes proto3.ListValue pb into struct slice referenced by pointer ptr, according to the +// structural information given in a sppb.StructType. +func decodeStructArray(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error { + if pb == nil { + return errNilListValue("STRUCT") + } + // Type of the struct pointers stored in the slice that ptr points to. + ts := reflect.TypeOf(ptr).Elem().Elem() + // The slice that ptr points to, might be nil at this point. + v := reflect.ValueOf(ptr).Elem() + // Allocate empty slice. + v.Set(reflect.MakeSlice(v.Type(), 0, len(pb.Values))) + // Decode every struct in pb.Values. + for i, pv := range pb.Values { + // Check if pv is a NULL value. + if _, isNull := pv.Kind.(*proto3.Value_NullValue); isNull { + // Append a nil pointer to the slice. + v.Set(reflect.Append(v, reflect.New(ts).Elem())) + continue + } + // Allocate empty struct. + s := reflect.New(ts.Elem()) + // Get proto3.ListValue l from proto3.Value pv. + l, err := getListValue(pv) + if err != nil { + return errDecodeArrayElement(i, pv, "STRUCT", err) + } + // Decode proto3.ListValue l into struct referenced by s.Interface(). + if err = decodeStruct(ty, l, s.Interface()); err != nil { + return errDecodeArrayElement(i, pv, "STRUCT", err) + } + // Append the decoded struct back into the slice. + v.Set(reflect.Append(v, s)) + } + return nil +} + +// errEncoderUnsupportedType returns error for not being able to encode a value of +// certain type. +func errEncoderUnsupportedType(v interface{}) error { + return spannerErrorf(codes.InvalidArgument, "client doesn't support type %T", v) +} + +// encodeValue encodes a Go native type into a proto3.Value. +func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) { + pb := &proto3.Value{ + Kind: &proto3.Value_NullValue{NullValue: proto3.NullValue_NULL_VALUE}, + } + var pt *sppb.Type + var err error + switch v := v.(type) { + case nil: + case string: + pb.Kind = stringKind(v) + pt = stringType() + case NullString: + if v.Valid { + return encodeValue(v.StringVal) + } + pt = stringType() + case []string: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(stringType()) + case []NullString: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(stringType()) + case []byte: + if v != nil { + pb.Kind = stringKind(base64.StdEncoding.EncodeToString(v)) + } + pt = bytesType() + case [][]byte: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(bytesType()) + case int: + pb.Kind = stringKind(strconv.FormatInt(int64(v), 10)) + pt = intType() + case []int: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(intType()) + case int64: + pb.Kind = stringKind(strconv.FormatInt(v, 10)) + pt = intType() + case []int64: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(intType()) + case NullInt64: + if v.Valid { + return encodeValue(v.Int64) + } + pt = intType() + case []NullInt64: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(intType()) + case bool: + pb.Kind = &proto3.Value_BoolValue{BoolValue: v} + pt = boolType() + case []bool: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(boolType()) + case NullBool: + if v.Valid { + return encodeValue(v.Bool) + } + pt = boolType() + case []NullBool: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(boolType()) + case float64: + pb.Kind = &proto3.Value_NumberValue{NumberValue: v} + pt = floatType() + case []float64: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(floatType()) + case NullFloat64: + if v.Valid { + return encodeValue(v.Float64) + } + pt = floatType() + case []NullFloat64: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(floatType()) + case time.Time: + if v == commitTimestamp { + pb.Kind = stringKind(commitTimestampPlaceholderString) + } else { + pb.Kind = stringKind(v.UTC().Format(time.RFC3339Nano)) + } + pt = timeType() + case []time.Time: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(timeType()) + case NullTime: + if v.Valid { + return encodeValue(v.Time) + } + pt = timeType() + case []NullTime: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(timeType()) + case civil.Date: + pb.Kind = stringKind(v.String()) + pt = dateType() + case []civil.Date: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(dateType()) + case NullDate: + if v.Valid { + return encodeValue(v.Date) + } + pt = dateType() + case []NullDate: + if v != nil { + pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] }) + if err != nil { + return nil, nil, err + } + } + pt = listType(dateType()) + case GenericColumnValue: + // Deep clone to ensure subsequent changes to v before + // transmission don't affect our encoded value. + pb = proto.Clone(v.Value).(*proto3.Value) + pt = proto.Clone(v.Type).(*sppb.Type) + case []GenericColumnValue: + return nil, nil, errEncoderUnsupportedType(v) + default: + if !isStructOrArrayOfStructValue(v) { + return nil, nil, errEncoderUnsupportedType(v) + } + typ := reflect.TypeOf(v) + + // Value is a Go struct value/ptr. + if (typ.Kind() == reflect.Struct) || + (typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct) { + return encodeStruct(v) + } + + // Value is a slice of Go struct values/ptrs. + if typ.Kind() == reflect.Slice { + return encodeStructArray(v) + } + } + return pb, pt, nil +} + +// Encodes a Go struct value/ptr in v to the spanner Value and Type protos. v itself must +// be non-nil. +func encodeStruct(v interface{}) (*proto3.Value, *sppb.Type, error) { + typ := reflect.TypeOf(v) + val := reflect.ValueOf(v) + + // Pointer to struct. + if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct { + typ = typ.Elem() + if val.IsNil() { + // nil pointer to struct, representing a NULL STRUCT value. Use a dummy value to + // get the type. + _, st, err := encodeStruct(reflect.Zero(typ).Interface()) + if err != nil { + return nil, nil, err + } + return nullProto(), st, nil + } + val = val.Elem() + } + + if typ.Kind() != reflect.Struct { + return nil, nil, errEncoderUnsupportedType(v) + } + + stf := make([]*sppb.StructType_Field, 0, typ.NumField()) + stv := make([]*proto3.Value, 0, typ.NumField()) + + for i := 0; i < typ.NumField(); i++ { + // If the field has a 'spanner' tag, use the value of that tag as the field name. + // This is used to build STRUCT types with unnamed/duplicate fields. + sf := typ.Field(i) + fval := val.Field(i) + + // Embedded fields are not allowed. + if sf.Anonymous { + return nil, nil, errUnsupportedEmbeddedStructFields(sf.Name) + } + + // Unexported fields are ignored. + if !fval.CanInterface() { + continue + } + + fname, ok := structTagLookup(sf.Tag, "spanner") + if !ok { + fname = sf.Name + } + + eval, etype, err := encodeValue(fval.Interface()) + if err != nil { + return nil, nil, err + } + + stf = append(stf, mkField(fname, etype)) + stv = append(stv, eval) + } + + return listProto(stv...), structType(stf...), nil +} + +// Encodes a slice of Go struct values/ptrs in v to the spanner Value and Type protos. v itself +// must be non-nil. +func encodeStructArray(v interface{}) (*proto3.Value, *sppb.Type, error) { + etyp := reflect.TypeOf(v).Elem() + sliceval := reflect.ValueOf(v) + + // Slice of pointers to structs. + if etyp.Kind() == reflect.Ptr { + etyp = etyp.Elem() + } + + // Use a dummy struct value to get the element type + _, elemTyp, err := encodeStruct(reflect.Zero(etyp).Interface()) + if err != nil { + return nil, nil, err + } + + // nil slice represents a NULL array-of-struct. + if sliceval.IsNil() { + return nullProto(), listType(elemTyp), nil + } + + values := make([]*proto3.Value, 0, sliceval.Len()) + + for i := 0; i < sliceval.Len(); i++ { + ev, _, err := encodeStruct(sliceval.Index(i).Interface()) + if err != nil { + return nil, nil, err + } + values = append(values, ev) + } + return listProto(values...), listType(elemTyp), nil +} + +func isStructOrArrayOfStructValue(v interface{}) bool { + typ := reflect.TypeOf(v) + if typ.Kind() == reflect.Slice { + typ = typ.Elem() + } + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + return typ.Kind() == reflect.Struct +} + +func isSupportedMutationType(v interface{}) bool { + switch v.(type) { + case string, NullString, []string, []NullString, + []byte, [][]byte, + int, []int, int64, []int64, NullInt64, []NullInt64, + bool, []bool, NullBool, []NullBool, + float64, []float64, NullFloat64, []NullFloat64, + time.Time, []time.Time, NullTime, []NullTime, + civil.Date, []civil.Date, NullDate, []NullDate, + GenericColumnValue: + return true + default: + return false + } +} + +// encodeValueArray encodes a Value array into a proto3.ListValue. +func encodeValueArray(vs []interface{}) (*proto3.ListValue, error) { + lv := &proto3.ListValue{} + lv.Values = make([]*proto3.Value, 0, len(vs)) + for _, v := range vs { + if !isSupportedMutationType(v) { + return nil, errEncoderUnsupportedType(v) + } + pb, _, err := encodeValue(v) + if err != nil { + return nil, err + } + lv.Values = append(lv.Values, pb) + } + return lv, nil +} + +// encodeArray assumes that all values of the array element type encode without error. +func encodeArray(len int, at func(int) interface{}) (*proto3.Value, error) { + vs := make([]*proto3.Value, len) + var err error + for i := 0; i < len; i++ { + vs[i], _, err = encodeValue(at(i)) + if err != nil { + return nil, err + } + } + return listProto(vs...), nil +} + +func spannerTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) { + if s := t.Get("spanner"); s != "" { + if s == "-" { + return "", false, nil, nil + } + return s, true, nil, nil + } + return "", true, nil, nil +} + +var fieldCache = fields.NewCache(spannerTagParser, nil, nil) diff --git a/vendor/cloud.google.com/go/spanner/value_benchmarks_test.go b/vendor/cloud.google.com/go/spanner/value_benchmarks_test.go new file mode 100644 index 0000000000..2ae68d7dba --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/value_benchmarks_test.go @@ -0,0 +1,214 @@ +// Copyright 2017 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 go1.7 + +package spanner + +import ( + "reflect" + "strconv" + "testing" + + "cloud.google.com/go/civil" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +func BenchmarkEncodeIntArray(b *testing.B) { + for _, s := range []struct { + name string + f func(a []int) (*proto3.Value, *sppb.Type, error) + }{ + {"Orig", encodeIntArrayOrig}, + {"Func", encodeIntArrayFunc}, + {"Reflect", encodeIntArrayReflect}, + } { + b.Run(s.name, func(b *testing.B) { + for _, size := range []int{1, 10, 100, 1000} { + a := make([]int, size) + b.Run(strconv.Itoa(size), func(b *testing.B) { + for i := 0; i < b.N; i++ { + s.f(a) + } + }) + } + }) + } +} + +func encodeIntArrayOrig(a []int) (*proto3.Value, *sppb.Type, error) { + vs := make([]*proto3.Value, len(a)) + var err error + for i := range a { + vs[i], _, err = encodeValue(a[i]) + if err != nil { + return nil, nil, err + } + } + return listProto(vs...), listType(intType()), nil +} + +func encodeIntArrayFunc(a []int) (*proto3.Value, *sppb.Type, error) { + v, err := encodeArray(len(a), func(i int) interface{} { return a[i] }) + if err != nil { + return nil, nil, err + } + return v, listType(intType()), nil +} + +func encodeIntArrayReflect(a []int) (*proto3.Value, *sppb.Type, error) { + v, err := encodeArrayReflect(a) + if err != nil { + return nil, nil, err + } + return v, listType(intType()), nil +} + +func encodeArrayReflect(a interface{}) (*proto3.Value, error) { + va := reflect.ValueOf(a) + len := va.Len() + vs := make([]*proto3.Value, len) + var err error + for i := 0; i < len; i++ { + vs[i], _, err = encodeValue(va.Index(i).Interface()) + if err != nil { + return nil, err + } + } + return listProto(vs...), nil +} + +func BenchmarkDecodeGeneric(b *testing.B) { + v := stringProto("test") + t := stringType() + var g GenericColumnValue + b.ResetTimer() + for i := 0; i < b.N; i++ { + decodeValue(v, t, &g) + } +} + +func BenchmarkDecodeArray(b *testing.B) { + for _, size := range []int{1, 10, 100, 1000} { + vals := make([]*proto3.Value, size) + for i := 0; i < size; i++ { + vals[i] = dateProto(d1) + } + lv := &proto3.ListValue{Values: vals} + b.Run(strconv.Itoa(size), func(b *testing.B) { + for _, s := range []struct { + name string + decode func(*proto3.ListValue) + }{ + {"DateDirect", decodeArray_Date_direct}, + {"DateFunc", decodeArray_Date_func}, + {"DateReflect", decodeArray_Date_reflect}, + {"StringDecodeStringArray", decodeStringArrayWrap}, + {"StringDirect", decodeArray_String_direct}, + {"StringFunc", decodeArray_String_func}, + {"StringReflect", decodeArray_String_reflect}, + } { + b.Run(s.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + s.decode(lv) + } + }) + } + }) + + } +} + +func decodeArray_Date_direct(pb *proto3.ListValue) { + a := make([]civil.Date, len(pb.Values)) + t := dateType() + for i, v := range pb.Values { + if err := decodeValue(v, t, &a[i]); err != nil { + panic(err) + } + } +} + +func decodeArray_Date_func(pb *proto3.ListValue) { + a := make([]civil.Date, len(pb.Values)) + if err := decodeArray_func(pb, "DATE", dateType(), func(i int) interface{} { return &a[i] }); err != nil { + panic(err) + } +} + +func decodeArray_Date_reflect(pb *proto3.ListValue) { + var a []civil.Date + if err := decodeArray_reflect(pb, "DATE", dateType(), &a); err != nil { + panic(err) + } +} + +func decodeStringArrayWrap(pb *proto3.ListValue) { + if _, err := decodeStringArray(pb); err != nil { + panic(err) + } +} + +func decodeArray_String_direct(pb *proto3.ListValue) { + a := make([]string, len(pb.Values)) + t := stringType() + for i, v := range pb.Values { + if err := decodeValue(v, t, &a[i]); err != nil { + panic(err) + } + } +} + +func decodeArray_String_func(pb *proto3.ListValue) { + + a := make([]string, len(pb.Values)) + if err := decodeArray_func(pb, "STRING", stringType(), func(i int) interface{} { return &a[i] }); err != nil { + panic(err) + } +} + +func decodeArray_String_reflect(pb *proto3.ListValue) { + var a []string + if err := decodeArray_reflect(pb, "STRING", stringType(), &a); err != nil { + panic(err) + } +} + +func decodeArray_func(pb *proto3.ListValue, name string, typ *sppb.Type, elptr func(int) interface{}) error { + if pb == nil { + return errNilListValue(name) + } + for i, v := range pb.Values { + if err := decodeValue(v, typ, elptr(i)); err != nil { + return errDecodeArrayElement(i, v, name, err) + } + } + return nil +} + +func decodeArray_reflect(pb *proto3.ListValue, name string, typ *sppb.Type, aptr interface{}) error { + if pb == nil { + return errNilListValue(name) + } + av := reflect.ValueOf(aptr).Elem() + av.Set(reflect.MakeSlice(av.Type(), len(pb.Values), len(pb.Values))) + for i, v := range pb.Values { + if err := decodeValue(v, typ, av.Index(i).Addr().Interface()); err != nil { + av.Set(reflect.Zero(av.Type())) // reset slice to nil + return errDecodeArrayElement(i, v, name, err) + } + } + return nil +} diff --git a/vendor/cloud.google.com/go/spanner/value_test.go b/vendor/cloud.google.com/go/spanner/value_test.go new file mode 100644 index 0000000000..ccaccc1ae3 --- /dev/null +++ b/vendor/cloud.google.com/go/spanner/value_test.go @@ -0,0 +1,1116 @@ +/* +Copyright 2017 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 spanner + +import ( + "math" + "reflect" + "testing" + "time" + + "cloud.google.com/go/civil" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +var ( + t1 = mustParseTime("2016-11-15T15:04:05.999999999Z") + // Boundaries + t2 = mustParseTime("0000-01-01T00:00:00.000000000Z") + t3 = mustParseTime("9999-12-31T23:59:59.999999999Z") + // Local timezone + t4 = time.Now() + d1 = mustParseDate("2016-11-15") + d2 = mustParseDate("1678-01-01") +) + +func mustParseTime(s string) time.Time { + t, err := time.Parse(time.RFC3339Nano, s) + if err != nil { + panic(err) + } + return t +} + +func mustParseDate(s string) civil.Date { + d, err := civil.ParseDate(s) + if err != nil { + panic(err) + } + return d +} + +// Test encoding Values. +func TestEncodeValue(t *testing.T) { + var ( + tString = stringType() + tInt = intType() + tBool = boolType() + tFloat = floatType() + tBytes = bytesType() + tTime = timeType() + tDate = dateType() + ) + for i, test := range []struct { + in interface{} + want *proto3.Value + wantType *sppb.Type + }{ + // STRING / STRING ARRAY + {"abc", stringProto("abc"), tString}, + {NullString{"abc", true}, stringProto("abc"), tString}, + {NullString{"abc", false}, nullProto(), tString}, + {[]string(nil), nullProto(), listType(tString)}, + {[]string{"abc", "bcd"}, listProto(stringProto("abc"), stringProto("bcd")), listType(tString)}, + {[]NullString{{"abcd", true}, {"xyz", false}}, listProto(stringProto("abcd"), nullProto()), listType(tString)}, + // BYTES / BYTES ARRAY + {[]byte("foo"), bytesProto([]byte("foo")), tBytes}, + {[]byte(nil), nullProto(), tBytes}, + {[][]byte{nil, []byte("ab")}, listProto(nullProto(), bytesProto([]byte("ab"))), listType(tBytes)}, + {[][]byte(nil), nullProto(), listType(tBytes)}, + // INT64 / INT64 ARRAY + {7, intProto(7), tInt}, + {[]int(nil), nullProto(), listType(tInt)}, + {[]int{31, 127}, listProto(intProto(31), intProto(127)), listType(tInt)}, + {int64(81), intProto(81), tInt}, + {[]int64(nil), nullProto(), listType(tInt)}, + {[]int64{33, 129}, listProto(intProto(33), intProto(129)), listType(tInt)}, + {NullInt64{11, true}, intProto(11), tInt}, + {NullInt64{11, false}, nullProto(), tInt}, + {[]NullInt64{{35, true}, {131, false}}, listProto(intProto(35), nullProto()), listType(tInt)}, + // BOOL / BOOL ARRAY + {true, boolProto(true), tBool}, + {NullBool{true, true}, boolProto(true), tBool}, + {NullBool{true, false}, nullProto(), tBool}, + {[]bool{true, false}, listProto(boolProto(true), boolProto(false)), listType(tBool)}, + {[]NullBool{{true, true}, {true, false}}, listProto(boolProto(true), nullProto()), listType(tBool)}, + // FLOAT64 / FLOAT64 ARRAY + {3.14, floatProto(3.14), tFloat}, + {NullFloat64{3.1415, true}, floatProto(3.1415), tFloat}, + {NullFloat64{math.Inf(1), true}, floatProto(math.Inf(1)), tFloat}, + {NullFloat64{3.14159, false}, nullProto(), tFloat}, + {[]float64(nil), nullProto(), listType(tFloat)}, + {[]float64{3.141, 0.618, math.Inf(-1)}, listProto(floatProto(3.141), floatProto(0.618), floatProto(math.Inf(-1))), listType(tFloat)}, + {[]NullFloat64{{3.141, true}, {0.618, false}}, listProto(floatProto(3.141), nullProto()), listType(tFloat)}, + // TIMESTAMP / TIMESTAMP ARRAY + {t1, timeProto(t1), tTime}, + {NullTime{t1, true}, timeProto(t1), tTime}, + {NullTime{t1, false}, nullProto(), tTime}, + {[]time.Time(nil), nullProto(), listType(tTime)}, + {[]time.Time{t1, t2, t3, t4}, listProto(timeProto(t1), timeProto(t2), timeProto(t3), timeProto(t4)), listType(tTime)}, + {[]NullTime{{t1, true}, {t1, false}}, listProto(timeProto(t1), nullProto()), listType(tTime)}, + // DATE / DATE ARRAY + {d1, dateProto(d1), tDate}, + {NullDate{d1, true}, dateProto(d1), tDate}, + {NullDate{civil.Date{}, false}, nullProto(), tDate}, + {[]civil.Date(nil), nullProto(), listType(tDate)}, + {[]civil.Date{d1, d2}, listProto(dateProto(d1), dateProto(d2)), listType(tDate)}, + {[]NullDate{{d1, true}, {civil.Date{}, false}}, listProto(dateProto(d1), nullProto()), listType(tDate)}, + // GenericColumnValue + {GenericColumnValue{tString, stringProto("abc")}, stringProto("abc"), tString}, + {GenericColumnValue{tString, nullProto()}, nullProto(), tString}, + // not actually valid (stringProto inside int list), but demonstrates pass-through. + { + GenericColumnValue{ + Type: listType(tInt), + Value: listProto(intProto(5), nullProto(), stringProto("bcd")), + }, + listProto(intProto(5), nullProto(), stringProto("bcd")), + listType(tInt), + }, + // placeholder + {CommitTimestamp, stringProto(commitTimestampPlaceholderString), tTime}, + } { + got, gotType, err := encodeValue(test.in) + if err != nil { + t.Fatalf("#%d: got error during encoding: %v, want nil", i, err) + } + if !testEqual(got, test.want) { + t.Errorf("#%d: got encode result: %v, want %v", i, got, test.want) + } + if !testEqual(gotType, test.wantType) { + t.Errorf("#%d: got encode type: %v, want %v", i, gotType, test.wantType) + } + } +} + +type encodeTest struct { + desc string + in interface{} + want *proto3.Value + wantType *sppb.Type +} + +func checkStructEncoding(desc string, got *proto3.Value, gotType *sppb.Type, + want *proto3.Value, wantType *sppb.Type, t *testing.T) { + if !testEqual(got, want) { + t.Errorf("Test %s: got encode result: %v, want %v", desc, got, want) + } + if !testEqual(gotType, wantType) { + t.Errorf("Test %s: got encode type: %v, want %v", desc, gotType, wantType) + } +} + +// Testcase code +func encodeStructValue(test encodeTest, t *testing.T) { + got, gotType, err := encodeValue(test.in) + if err != nil { + t.Fatalf("Test %s: got error during encoding: %v, want nil", test.desc, err) + } + checkStructEncoding(test.desc, got, gotType, test.want, test.wantType, t) +} + +func TestEncodeStructValuePointers(t *testing.T) { + type structf struct { + F int `spanner:"ff2"` + } + nestedStructProto := structType(mkField("ff2", intType())) + + type testType struct { + Stringf string + Structf *structf + ArrStructf []*structf + } + testTypeProto := structType( + mkField("Stringf", stringType()), + mkField("Structf", nestedStructProto), + mkField("ArrStructf", listType(nestedStructProto))) + + for _, test := range []encodeTest{ + { + "Pointer to Go struct with pointers-to-(array)-struct fields.", + &testType{"hello", &structf{50}, []*structf{&structf{30}, &structf{40}}}, + listProto( + stringProto("hello"), + listProto(intProto(50)), + listProto( + listProto(intProto(30)), + listProto(intProto(40)))), + testTypeProto, + }, + { + "Nil pointer to Go struct representing a NULL struct value.", + (*testType)(nil), + nullProto(), + testTypeProto, + }, + { + "Slice of pointers to Go structs with NULL and non-NULL elements.", + []*testType{ + (*testType)(nil), + &testType{"hello", nil, []*structf{nil, &structf{40}}}, + &testType{"world", &structf{70}, nil}, + }, + listProto( + nullProto(), + listProto( + stringProto("hello"), + nullProto(), + listProto(nullProto(), listProto(intProto(40)))), + listProto( + stringProto("world"), + listProto(intProto(70)), + nullProto())), + listType(testTypeProto), + }, + { + "Nil slice of pointers to structs representing a NULL array of structs.", + []*testType(nil), + nullProto(), + listType(testTypeProto), + }, + { + "Empty slice of pointers to structs representing an empty array of structs.", + []*testType{}, + listProto(), + listType(testTypeProto), + }, + } { + encodeStructValue(test, t) + } +} + +func TestEncodeStructValueErrors(t *testing.T) { + type Embedded struct { + A int + } + type embedded struct { + B bool + } + x := 0 + + for _, test := range []struct { + desc string + in interface{} + wantErr error + }{ + { + "Unsupported embedded fields.", + struct{ Embedded }{Embedded{10}}, + errUnsupportedEmbeddedStructFields("Embedded"), + }, + { + "Unsupported pointer to embedded fields.", + struct{ *Embedded }{&Embedded{10}}, + errUnsupportedEmbeddedStructFields("Embedded"), + }, + { + "Unsupported embedded + unexported fields.", + struct { + int + *bool + embedded + }{10, nil, embedded{false}}, + errUnsupportedEmbeddedStructFields("int"), + }, + { + "Unsupported type.", + (**struct{})(nil), + errEncoderUnsupportedType((**struct{})(nil)), + }, + { + "Unsupported type.", + 3, + errEncoderUnsupportedType(3), + }, + { + "Unsupported type.", + &x, + errEncoderUnsupportedType(&x), + }, + } { + _, _, got := encodeStruct(test.in) + if got == nil || !testEqual(test.wantErr, got) { + t.Errorf("Test: %s, expected error %v during decoding, got %v", test.desc, test.wantErr, got) + } + } +} + +func TestEncodeStructValueArrayStructFields(t *testing.T) { + type structf struct { + Intff int + } + + structfType := structType(mkField("Intff", intType())) + for _, test := range []encodeTest{ + { + "Unnamed array-of-struct-typed field.", + struct { + Intf int + ArrStructf []structf `spanner:""` + }{10, []structf{structf{1}, structf{2}}}, + listProto( + intProto(10), + listProto( + listProto(intProto(1)), + listProto(intProto(2)))), + structType( + mkField("Intf", intType()), + mkField("", listType(structfType))), + }, + { + "Null array-of-struct-typed field.", + struct { + Intf int + ArrStructf []structf + }{10, []structf(nil)}, + listProto(intProto(10), nullProto()), + structType( + mkField("Intf", intType()), + mkField("ArrStructf", listType(structfType))), + }, + { + "Array-of-struct-typed field representing empty array.", + struct { + Intf int + ArrStructf []structf + }{10, []structf{}}, + listProto(intProto(10), listProto([]*proto3.Value{}...)), + structType( + mkField("Intf", intType()), + mkField("ArrStructf", listType(structfType))), + }, + { + "Array-of-struct-typed field with nullable struct elements.", + struct { + Intf int + ArrStructf []*structf + }{ + 10, + []*structf{(*structf)(nil), &structf{1}}, + }, + listProto( + intProto(10), + listProto( + nullProto(), + listProto(intProto(1)))), + structType( + mkField("Intf", intType()), + mkField("ArrStructf", listType(structfType))), + }, + } { + encodeStructValue(test, t) + } +} + +func TestEncodeStructValueStructFields(t *testing.T) { + type structf struct { + Intff int + } + structfType := structType(mkField("Intff", intType())) + for _, test := range []encodeTest{ + { + "Named struct-type field.", + struct { + Intf int + Structf structf + }{10, structf{10}}, + listProto(intProto(10), listProto(intProto(10))), + structType( + mkField("Intf", intType()), + mkField("Structf", structfType)), + }, + { + "Unnamed struct-type field.", + struct { + Intf int + Structf structf `spanner:""` + }{10, structf{10}}, + listProto(intProto(10), listProto(intProto(10))), + structType( + mkField("Intf", intType()), + mkField("", structfType)), + }, + { + "Duplicate struct-typed field.", + struct { + Structf1 structf `spanner:""` + Structf2 structf `spanner:""` + }{structf{10}, structf{20}}, + listProto(listProto(intProto(10)), listProto(intProto(20))), + structType( + mkField("", structfType), + mkField("", structfType)), + }, + { + "Null struct-typed field.", + struct { + Intf int + Structf *structf + }{10, nil}, + listProto(intProto(10), nullProto()), + structType( + mkField("Intf", intType()), + mkField("Structf", structfType)), + }, + { + "Empty struct-typed field.", + struct { + Intf int + Structf struct{} + }{10, struct{}{}}, + listProto(intProto(10), listProto([]*proto3.Value{}...)), + structType( + mkField("Intf", intType()), + mkField("Structf", structType([]*sppb.StructType_Field{}...))), + }, + } { + encodeStructValue(test, t) + } +} + +func TestEncodeStructValueFieldNames(t *testing.T) { + type embedded struct { + B bool + } + + for _, test := range []encodeTest{ + { + "Duplicate fields.", + struct { + Field1 int `spanner:"field"` + DupField1 int `spanner:"field"` + }{10, 20}, + listProto(intProto(10), intProto(20)), + structType( + mkField("field", intType()), + mkField("field", intType())), + }, + { + "Duplicate Fields (different types).", + struct { + IntField int `spanner:"field"` + StringField string `spanner:"field"` + }{10, "abc"}, + listProto(intProto(10), stringProto("abc")), + structType( + mkField("field", intType()), + mkField("field", stringType())), + }, + { + "Duplicate unnamed fields.", + struct { + Dup int `spanner:""` + Dup1 int `spanner:""` + }{10, 20}, + listProto(intProto(10), intProto(20)), + structType( + mkField("", intType()), + mkField("", intType())), + }, + { + "Named and unnamed fields.", + struct { + Field string + Field1 int `spanner:""` + Field2 string `spanner:"field"` + }{"abc", 10, "def"}, + listProto(stringProto("abc"), intProto(10), stringProto("def")), + structType( + mkField("Field", stringType()), + mkField("", intType()), + mkField("field", stringType())), + }, + { + "Ignored unexported fields.", + struct { + Field int + field bool + Field1 string `spanner:"field"` + }{10, false, "abc"}, + listProto(intProto(10), stringProto("abc")), + structType( + mkField("Field", intType()), + mkField("field", stringType())), + }, + { + "Ignored unexported struct/slice fields.", + struct { + a []*embedded + b []embedded + c embedded + d *embedded + Field1 string `spanner:"field"` + }{nil, nil, embedded{}, nil, "def"}, + listProto(stringProto("def")), + structType( + mkField("field", stringType())), + }, + } { + encodeStructValue(test, t) + } +} + +func TestEncodeStructValueBasicFields(t *testing.T) { + StructTypeProto := structType( + mkField("Stringf", stringType()), + mkField("Intf", intType()), + mkField("Boolf", boolType()), + mkField("Floatf", floatType()), + mkField("Bytef", bytesType()), + mkField("Timef", timeType()), + mkField("Datef", dateType())) + + for _, test := range []encodeTest{ + { + "Basic types.", + struct { + Stringf string + Intf int + Boolf bool + Floatf float64 + Bytef []byte + Timef time.Time + Datef civil.Date + }{"abc", 300, false, 3.45, []byte("foo"), t1, d1}, + listProto( + stringProto("abc"), + intProto(300), + boolProto(false), + floatProto(3.45), + bytesProto([]byte("foo")), + timeProto(t1), + dateProto(d1)), + StructTypeProto, + }, + { + "Basic types null values.", + struct { + Stringf NullString + Intf NullInt64 + Boolf NullBool + Floatf NullFloat64 + Bytef []byte + Timef NullTime + Datef NullDate + }{ + NullString{"abc", false}, + NullInt64{4, false}, + NullBool{false, false}, + NullFloat64{5.6, false}, + nil, + NullTime{t1, false}, + NullDate{d1, false}, + }, + listProto( + nullProto(), + nullProto(), + nullProto(), + nullProto(), + nullProto(), + nullProto(), + nullProto()), + StructTypeProto, + }, + } { + encodeStructValue(test, t) + } +} + +func TestEncodeStructValueArrayFields(t *testing.T) { + StructTypeProto := structType( + mkField("Stringf", listType(stringType())), + mkField("Intf", listType(intType())), + mkField("Int64f", listType(intType())), + mkField("Boolf", listType(boolType())), + mkField("Floatf", listType(floatType())), + mkField("Bytef", listType(bytesType())), + mkField("Timef", listType(timeType())), + mkField("Datef", listType(dateType()))) + + for _, test := range []encodeTest{ + { + "Arrays of basic types with non-nullable elements", + struct { + Stringf []string + Intf []int + Int64f []int64 + Boolf []bool + Floatf []float64 + Bytef [][]byte + Timef []time.Time + Datef []civil.Date + }{ + []string{"abc", "def"}, + []int{4, 67}, + []int64{5, 68}, + []bool{false, true}, + []float64{3.45, 0.93}, + [][]byte{[]byte("foo"), nil}, + []time.Time{t1, t2}, + []civil.Date{d1, d2}, + }, + listProto( + listProto(stringProto("abc"), stringProto("def")), + listProto(intProto(4), intProto(67)), + listProto(intProto(5), intProto(68)), + listProto(boolProto(false), boolProto(true)), + listProto(floatProto(3.45), floatProto(0.93)), + listProto(bytesProto([]byte("foo")), nullProto()), + listProto(timeProto(t1), timeProto(t2)), + listProto(dateProto(d1), dateProto(d2))), + StructTypeProto, + }, + { + "Arrays of basic types with nullable elements.", + struct { + Stringf []NullString + Intf []NullInt64 + Int64f []NullInt64 + Boolf []NullBool + Floatf []NullFloat64 + Bytef [][]byte + Timef []NullTime + Datef []NullDate + }{ + []NullString{NullString{"abc", false}, NullString{"def", true}}, + []NullInt64{NullInt64{4, false}, NullInt64{67, true}}, + []NullInt64{NullInt64{5, false}, NullInt64{68, true}}, + []NullBool{NullBool{true, false}, NullBool{false, true}}, + []NullFloat64{NullFloat64{3.45, false}, NullFloat64{0.93, true}}, + [][]byte{[]byte("foo"), nil}, + []NullTime{NullTime{t1, false}, NullTime{t2, true}}, + []NullDate{NullDate{d1, false}, NullDate{d2, true}}, + }, + listProto( + listProto(nullProto(), stringProto("def")), + listProto(nullProto(), intProto(67)), + listProto(nullProto(), intProto(68)), + listProto(nullProto(), boolProto(false)), + listProto(nullProto(), floatProto(0.93)), + listProto(bytesProto([]byte("foo")), nullProto()), + listProto(nullProto(), timeProto(t2)), + listProto(nullProto(), dateProto(d2))), + StructTypeProto, + }, + { + "Null arrays of basic types.", + struct { + Stringf []NullString + Intf []NullInt64 + Int64f []NullInt64 + Boolf []NullBool + Floatf []NullFloat64 + Bytef [][]byte + Timef []NullTime + Datef []NullDate + }{ + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + listProto( + nullProto(), + nullProto(), + nullProto(), + nullProto(), + nullProto(), + nullProto(), + nullProto(), + nullProto()), + StructTypeProto, + }, + } { + encodeStructValue(test, t) + } +} + +// Test decoding Values. +func TestDecodeValue(t *testing.T) { + for i, test := range []struct { + in *proto3.Value + t *sppb.Type + want interface{} + fail bool + }{ + // STRING + {stringProto("abc"), stringType(), "abc", false}, + {nullProto(), stringType(), "abc", true}, + {stringProto("abc"), stringType(), NullString{"abc", true}, false}, + {nullProto(), stringType(), NullString{}, false}, + // STRING ARRAY with []NullString + { + listProto(stringProto("abc"), nullProto(), stringProto("bcd")), + listType(stringType()), + []NullString{{"abc", true}, {}, {"bcd", true}}, + false, + }, + {nullProto(), listType(stringType()), []NullString(nil), false}, + // STRING ARRAY with []string + { + listProto(stringProto("abc"), stringProto("bcd")), + listType(stringType()), + []string{"abc", "bcd"}, + false, + }, + // BYTES + {bytesProto([]byte("ab")), bytesType(), []byte("ab"), false}, + {nullProto(), bytesType(), []byte(nil), false}, + // BYTES ARRAY + {listProto(bytesProto([]byte("ab")), nullProto()), listType(bytesType()), [][]byte{[]byte("ab"), nil}, false}, + {nullProto(), listType(bytesType()), [][]byte(nil), false}, + //INT64 + {intProto(15), intType(), int64(15), false}, + {nullProto(), intType(), int64(0), true}, + {intProto(15), intType(), NullInt64{15, true}, false}, + {nullProto(), intType(), NullInt64{}, false}, + // INT64 ARRAY with []NullInt64 + {listProto(intProto(91), nullProto(), intProto(87)), listType(intType()), []NullInt64{{91, true}, {}, {87, true}}, false}, + {nullProto(), listType(intType()), []NullInt64(nil), false}, + // INT64 ARRAY with []int64 + {listProto(intProto(91), intProto(87)), listType(intType()), []int64{91, 87}, false}, + // BOOL + {boolProto(true), boolType(), true, false}, + {nullProto(), boolType(), true, true}, + {boolProto(true), boolType(), NullBool{true, true}, false}, + {nullProto(), boolType(), NullBool{}, false}, + // BOOL ARRAY with []NullBool + {listProto(boolProto(true), boolProto(false), nullProto()), listType(boolType()), []NullBool{{true, true}, {false, true}, {}}, false}, + {nullProto(), listType(boolType()), []NullBool(nil), false}, + // BOOL ARRAY with []bool + {listProto(boolProto(true), boolProto(false)), listType(boolType()), []bool{true, false}, false}, + // FLOAT64 + {floatProto(3.14), floatType(), 3.14, false}, + {nullProto(), floatType(), 0.00, true}, + {floatProto(3.14), floatType(), NullFloat64{3.14, true}, false}, + {nullProto(), floatType(), NullFloat64{}, false}, + // FLOAT64 ARRAY with []NullFloat64 + { + listProto(floatProto(math.Inf(1)), floatProto(math.Inf(-1)), nullProto(), floatProto(3.1)), + listType(floatType()), + []NullFloat64{{math.Inf(1), true}, {math.Inf(-1), true}, {}, {3.1, true}}, + false, + }, + {nullProto(), listType(floatType()), []NullFloat64(nil), false}, + // FLOAT64 ARRAY with []float64 + { + listProto(floatProto(math.Inf(1)), floatProto(math.Inf(-1)), floatProto(3.1)), + listType(floatType()), + []float64{math.Inf(1), math.Inf(-1), 3.1}, + false, + }, + // TIMESTAMP + {timeProto(t1), timeType(), t1, false}, + {timeProto(t1), timeType(), NullTime{t1, true}, false}, + {nullProto(), timeType(), NullTime{}, false}, + // TIMESTAMP ARRAY with []NullTime + {listProto(timeProto(t1), timeProto(t2), timeProto(t3), nullProto()), listType(timeType()), []NullTime{{t1, true}, {t2, true}, {t3, true}, {}}, false}, + {nullProto(), listType(timeType()), []NullTime(nil), false}, + // TIMESTAMP ARRAY with []time.Time + {listProto(timeProto(t1), timeProto(t2), timeProto(t3)), listType(timeType()), []time.Time{t1, t2, t3}, false}, + // DATE + {dateProto(d1), dateType(), d1, false}, + {dateProto(d1), dateType(), NullDate{d1, true}, false}, + {nullProto(), dateType(), NullDate{}, false}, + // DATE ARRAY with []NullDate + {listProto(dateProto(d1), dateProto(d2), nullProto()), listType(dateType()), []NullDate{{d1, true}, {d2, true}, {}}, false}, + {nullProto(), listType(dateType()), []NullDate(nil), false}, + // DATE ARRAY with []civil.Date + {listProto(dateProto(d1), dateProto(d2)), listType(dateType()), []civil.Date{d1, d2}, false}, + // STRUCT ARRAY + // STRUCT schema is equal to the following Go struct: + // type s struct { + // Col1 NullInt64 + // Col2 []struct { + // SubCol1 float64 + // SubCol2 string + // } + // } + { + in: listProto( + listProto( + intProto(3), + listProto( + listProto(floatProto(3.14), stringProto("this")), + listProto(floatProto(0.57), stringProto("siht")), + ), + ), + listProto( + nullProto(), + nullProto(), + ), + nullProto(), + ), + t: listType( + structType( + mkField("Col1", intType()), + mkField( + "Col2", + listType( + structType( + mkField("SubCol1", floatType()), + mkField("SubCol2", stringType()), + ), + ), + ), + ), + ), + want: []NullRow{ + { + Row: Row{ + fields: []*sppb.StructType_Field{ + mkField("Col1", intType()), + mkField( + "Col2", + listType( + structType( + mkField("SubCol1", floatType()), + mkField("SubCol2", stringType()), + ), + ), + ), + }, + vals: []*proto3.Value{ + intProto(3), + listProto( + listProto(floatProto(3.14), stringProto("this")), + listProto(floatProto(0.57), stringProto("siht")), + ), + }, + }, + Valid: true, + }, + { + Row: Row{ + fields: []*sppb.StructType_Field{ + mkField("Col1", intType()), + mkField( + "Col2", + listType( + structType( + mkField("SubCol1", floatType()), + mkField("SubCol2", stringType()), + ), + ), + ), + }, + vals: []*proto3.Value{ + nullProto(), + nullProto(), + }, + }, + Valid: true, + }, + {}, + }, + fail: false, + }, + { + in: listProto( + listProto( + intProto(3), + listProto( + listProto(floatProto(3.14), stringProto("this")), + listProto(floatProto(0.57), stringProto("siht")), + ), + ), + listProto( + nullProto(), + nullProto(), + ), + nullProto(), + ), + t: listType( + structType( + mkField("Col1", intType()), + mkField( + "Col2", + listType( + structType( + mkField("SubCol1", floatType()), + mkField("SubCol2", stringType()), + ), + ), + ), + ), + ), + want: []*struct { + Col1 NullInt64 + StructCol []*struct { + SubCol1 NullFloat64 + SubCol2 string + } `spanner:"Col2"` + }{ + { + Col1: NullInt64{3, true}, + StructCol: []*struct { + SubCol1 NullFloat64 + SubCol2 string + }{ + { + SubCol1: NullFloat64{3.14, true}, + SubCol2: "this", + }, + { + SubCol1: NullFloat64{0.57, true}, + SubCol2: "siht", + }, + }, + }, + { + Col1: NullInt64{}, + StructCol: []*struct { + SubCol1 NullFloat64 + SubCol2 string + }(nil), + }, + nil, + }, + fail: false, + }, + // GenericColumnValue + {stringProto("abc"), stringType(), GenericColumnValue{stringType(), stringProto("abc")}, false}, + {nullProto(), stringType(), GenericColumnValue{stringType(), nullProto()}, false}, + // not actually valid (stringProto inside int list), but demonstrates pass-through. + { + in: listProto(intProto(5), nullProto(), stringProto("bcd")), + t: listType(intType()), + want: GenericColumnValue{ + Type: listType(intType()), + Value: listProto(intProto(5), nullProto(), stringProto("bcd")), + }, + fail: false, + }, + } { + gotp := reflect.New(reflect.TypeOf(test.want)) + if err := decodeValue(test.in, test.t, gotp.Interface()); err != nil { + if !test.fail { + t.Errorf("%d: cannot decode %v(%v): %v", i, test.in, test.t, err) + } + continue + } + if test.fail { + t.Errorf("%d: decoding %v(%v) succeeds unexpectedly, want error", i, test.in, test.t) + continue + } + got := reflect.Indirect(gotp).Interface() + if !testEqual(got, test.want) { + t.Errorf("%d: unexpected decoding result - got %v, want %v", i, got, test.want) + continue + } + } +} + +// Test error cases for decodeValue. +func TestDecodeValueErrors(t *testing.T) { + var s string + for i, test := range []struct { + in *proto3.Value + t *sppb.Type + v interface{} + }{ + {nullProto(), stringType(), nil}, + {nullProto(), stringType(), 1}, + {timeProto(t1), timeType(), &s}, + } { + err := decodeValue(test.in, test.t, test.v) + if err == nil { + t.Errorf("#%d: want error, got nil", i) + } + } +} + +// Test NaN encoding/decoding. +func TestNaN(t *testing.T) { + // Decode NaN value. + f := 0.0 + nf := NullFloat64{} + // To float64 + if err := decodeValue(floatProto(math.NaN()), floatType(), &f); err != nil { + t.Errorf("decodeValue returns %q for %v, want nil", err, floatProto(math.NaN())) + } + if !math.IsNaN(f) { + t.Errorf("f = %v, want %v", f, math.NaN()) + } + // To NullFloat64 + if err := decodeValue(floatProto(math.NaN()), floatType(), &nf); err != nil { + t.Errorf("decodeValue returns %q for %v, want nil", err, floatProto(math.NaN())) + } + if !math.IsNaN(nf.Float64) || !nf.Valid { + t.Errorf("f = %v, want %v", f, NullFloat64{math.NaN(), true}) + } + // Encode NaN value + // From float64 + v, _, err := encodeValue(math.NaN()) + if err != nil { + t.Errorf("encodeValue returns %q for NaN, want nil", err) + } + x, ok := v.GetKind().(*proto3.Value_NumberValue) + if !ok { + t.Errorf("incorrect type for v.GetKind(): %T, want *proto3.Value_NumberValue", v.GetKind()) + } + if !math.IsNaN(x.NumberValue) { + t.Errorf("x.NumberValue = %v, want %v", x.NumberValue, math.NaN()) + } + // From NullFloat64 + v, _, err = encodeValue(NullFloat64{math.NaN(), true}) + if err != nil { + t.Errorf("encodeValue returns %q for NaN, want nil", err) + } + x, ok = v.GetKind().(*proto3.Value_NumberValue) + if !ok { + t.Errorf("incorrect type for v.GetKind(): %T, want *proto3.Value_NumberValue", v.GetKind()) + } + if !math.IsNaN(x.NumberValue) { + t.Errorf("x.NumberValue = %v, want %v", x.NumberValue, math.NaN()) + } +} + +func TestGenericColumnValue(t *testing.T) { + for _, test := range []struct { + in GenericColumnValue + want interface{} + fail bool + }{ + {GenericColumnValue{stringType(), stringProto("abc")}, "abc", false}, + {GenericColumnValue{stringType(), stringProto("abc")}, 5, true}, + {GenericColumnValue{listType(intType()), listProto(intProto(91), nullProto(), intProto(87))}, []NullInt64{{91, true}, {}, {87, true}}, false}, + {GenericColumnValue{intType(), intProto(42)}, GenericColumnValue{intType(), intProto(42)}, false}, // trippy! :-) + } { + gotp := reflect.New(reflect.TypeOf(test.want)) + if err := test.in.Decode(gotp.Interface()); err != nil { + if !test.fail { + t.Errorf("cannot decode %v to %v: %v", test.in, test.want, err) + } + continue + } + if test.fail { + t.Errorf("decoding %v to %v succeeds unexpectedly", test.in, test.want) + } + + // Test we can go backwards as well. + v, err := newGenericColumnValue(test.want) + if err != nil { + t.Errorf("NewGenericColumnValue failed: %v", err) + continue + } + if !testEqual(*v, test.in) { + t.Errorf("unexpected encode result - got %v, want %v", v, test.in) + } + } +} + +func TestDecodeStruct(t *testing.T) { + stype := &sppb.StructType{Fields: []*sppb.StructType_Field{ + {Name: "Id", Type: stringType()}, + {Name: "Time", Type: timeType()}, + }} + lv := listValueProto(stringProto("id"), timeProto(t1)) + + type ( + S1 struct { + Id string + Time time.Time + } + S2 struct { + Id string + Time string + } + ) + var ( + s1 S1 + s2 S2 + ) + + for i, test := range []struct { + ptr interface{} + want interface{} + fail bool + }{ + { + ptr: &s1, + want: &S1{Id: "id", Time: t1}, + }, + { + ptr: &s2, + fail: true, + }, + } { + err := decodeStruct(stype, lv, test.ptr) + if (err != nil) != test.fail { + t.Errorf("#%d: got error %v, wanted fail: %v", i, err, test.fail) + } + if err == nil && !testEqual(test.ptr, test.want) { + t.Errorf("#%d: got %+v, want %+v", i, test.ptr, test.want) + } + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1/Recognize_smoke_test.go b/vendor/cloud.google.com/go/speech/apiv1/Recognize_smoke_test.go new file mode 100644 index 0000000000..4b95cec03d --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1/Recognize_smoke_test.go @@ -0,0 +1,80 @@ +// 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 speech + +import ( + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestSpeechSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var languageCode string = "en-US" + var sampleRateHertz int32 = 44100 + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var config = &speechpb.RecognitionConfig{ + LanguageCode: languageCode, + SampleRateHertz: sampleRateHertz, + Encoding: encoding, + } + var uri string = "gs://gapic-toolkit/hello.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.RecognizeRequest{ + Config: config, + Audio: audio, + } + + if _, err := c.Recognize(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1/doc.go b/vendor/cloud.google.com/go/speech/apiv1/doc.go new file mode 100644 index 0000000000..f6e6d1821c --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1/doc.go @@ -0,0 +1,45 @@ +// 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 speech is an auto-generated package for the +// Google Cloud Speech API. + +// +// Google Cloud Speech API. +package speech // import "cloud.google.com/go/speech/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1/mock_test.go b/vendor/cloud.google.com/go/speech/apiv1/mock_test.go new file mode 100644 index 0000000000..389aafe250 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1/mock_test.go @@ -0,0 +1,405 @@ +// 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 speech + +import ( + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockSpeechServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + speechpb.SpeechServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockSpeechServer) Recognize(ctx context.Context, req *speechpb.RecognizeRequest) (*speechpb.RecognizeResponse, 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].(*speechpb.RecognizeResponse), nil +} + +func (s *mockSpeechServer) LongRunningRecognize(ctx context.Context, req *speechpb.LongRunningRecognizeRequest) (*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 *mockSpeechServer) StreamingRecognize(stream speechpb.Speech_StreamingRecognizeServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + for { + if req, err := stream.Recv(); err == io.EOF { + break + } else if err != nil { + return err + } else { + s.reqs = append(s.reqs, req) + } + } + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*speechpb.StreamingRecognizeResponse)); err != nil { + return err + } + } + return nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockSpeech mockSpeechServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + speechpb.RegisterSpeechServer(serv, &mockSpeech) + + 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 TestSpeechRecognize(t *testing.T) { + var expectedResponse *speechpb.RecognizeResponse = &speechpb.RecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + mockSpeech.resps = append(mockSpeech.resps[:0], expectedResponse) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.RecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Recognize(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpeech.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 TestSpeechRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = gstatus.Error(errCode, "test error") + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.RecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Recognize(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 TestSpeechLongRunningRecognize(t *testing.T) { + var expectedResponse *speechpb.LongRunningRecognizeResponse = &speechpb.LongRunningRecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockSpeech.resps = append(mockSpeech.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.LongRunningRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.LongRunningRecognize(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, mockSpeech.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 TestSpeechLongRunningRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = nil + mockSpeech.resps = append(mockSpeech.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.LongRunningRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.LongRunningRecognize(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 TestSpeechStreamingRecognize(t *testing.T) { + var expectedResponse *speechpb.StreamingRecognizeResponse = &speechpb.StreamingRecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + mockSpeech.resps = append(mockSpeech.resps[:0], expectedResponse) + + var request *speechpb.StreamingRecognizeRequest = &speechpb.StreamingRecognizeRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRecognize(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpeech.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 TestSpeechStreamingRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = gstatus.Error(errCode, "test error") + + var request *speechpb.StreamingRecognizeRequest = &speechpb.StreamingRecognizeRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRecognize(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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/vendor/cloud.google.com/go/speech/apiv1/speech_client.go b/vendor/cloud.google.com/go/speech/apiv1/speech_client.go new file mode 100644 index 0000000000..82a0453042 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1/speech_client.go @@ -0,0 +1,265 @@ +// 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 speech + +import ( + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "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 { + Recognize []gax.CallOption + LongRunningRecognize []gax.CallOption + StreamingRecognize []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("speech.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{ + Recognize: retry[[2]string{"default", "idempotent"}], + LongRunningRecognize: retry[[2]string{"default", "non_idempotent"}], + StreamingRecognize: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud Speech 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 speechpb.SpeechClient + + // 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 speech client. +// +// Service that implements Google Cloud Speech API. +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: speechpb.NewSpeechClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + 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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// Recognize performs synchronous speech recognition: receive results after all audio +// has been sent and processed. +func (c *Client) Recognize(ctx context.Context, req *speechpb.RecognizeRequest, opts ...gax.CallOption) (*speechpb.RecognizeResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Recognize[0:len(c.CallOptions.Recognize):len(c.CallOptions.Recognize)], opts...) + var resp *speechpb.RecognizeResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.Recognize(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// LongRunningRecognize performs asynchronous speech recognition: receive results via the +// google.longrunning.Operations interface. Returns either an +// Operation.error or an Operation.response which contains +// a LongRunningRecognizeResponse message. +func (c *Client) LongRunningRecognize(ctx context.Context, req *speechpb.LongRunningRecognizeRequest, opts ...gax.CallOption) (*LongRunningRecognizeOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.LongRunningRecognize[0:len(c.CallOptions.LongRunningRecognize):len(c.CallOptions.LongRunningRecognize)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.LongRunningRecognize(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &LongRunningRecognizeOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// StreamingRecognize performs bidirectional streaming speech recognition: receive results while +// sending audio. This method is only available via the gRPC API (not REST). +func (c *Client) StreamingRecognize(ctx context.Context, opts ...gax.CallOption) (speechpb.Speech_StreamingRecognizeClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.StreamingRecognize[0:len(c.CallOptions.StreamingRecognize):len(c.CallOptions.StreamingRecognize)], opts...) + var resp speechpb.Speech_StreamingRecognizeClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.StreamingRecognize(ctx, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// LongRunningRecognizeOperation manages a long-running operation from LongRunningRecognize. +type LongRunningRecognizeOperation struct { + lro *longrunning.Operation +} + +// LongRunningRecognizeOperation returns a new LongRunningRecognizeOperation from a given name. +// The name must be that of a previously created LongRunningRecognizeOperation, possibly from a different process. +func (c *Client) LongRunningRecognizeOperation(name string) *LongRunningRecognizeOperation { + return &LongRunningRecognizeOperation{ + 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 *LongRunningRecognizeOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*speechpb.LongRunningRecognizeResponse, error) { + var resp speechpb.LongRunningRecognizeResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *LongRunningRecognizeOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*speechpb.LongRunningRecognizeResponse, error) { + var resp speechpb.LongRunningRecognizeResponse + 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 *LongRunningRecognizeOperation) Metadata() (*speechpb.LongRunningRecognizeMetadata, error) { + var meta speechpb.LongRunningRecognizeMetadata + 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 *LongRunningRecognizeOperation) 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 *LongRunningRecognizeOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/speech/apiv1/speech_client_example_test.go b/vendor/cloud.google.com/go/speech/apiv1/speech_client_example_test.go new file mode 100644 index 0000000000..1377f42434 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1/speech_client_example_test.go @@ -0,0 +1,110 @@ +// 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 speech_test + +import ( + "io" + + "cloud.google.com/go/speech/apiv1" + "golang.org/x/net/context" + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_Recognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &speechpb.RecognizeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Recognize(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_LongRunningRecognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &speechpb.LongRunningRecognizeRequest{ + // TODO: Fill request struct fields. + } + op, err := c.LongRunningRecognize(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_StreamingRecognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + stream, err := c.StreamingRecognize(ctx) + if err != nil { + // TODO: Handle error. + } + go func() { + reqs := []*speechpb.StreamingRecognizeRequest{ + // TODO: Create requests. + } + for _, req := range reqs { + if err := stream.Send(req); err != nil { + // TODO: Handle error. + } + } + stream.CloseSend() + }() + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1beta1/SyncRecognize_smoke_test.go b/vendor/cloud.google.com/go/speech/apiv1beta1/SyncRecognize_smoke_test.go new file mode 100644 index 0000000000..8277d7f66c --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1beta1/SyncRecognize_smoke_test.go @@ -0,0 +1,80 @@ +// 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 speech + +import ( + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1beta1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestSpeechSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var languageCode string = "en-US" + var sampleRate int32 = 44100 + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var config = &speechpb.RecognitionConfig{ + LanguageCode: languageCode, + SampleRate: sampleRate, + Encoding: encoding, + } + var uri string = "gs://gapic-toolkit/hello.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.SyncRecognizeRequest{ + Config: config, + Audio: audio, + } + + if _, err := c.SyncRecognize(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1beta1/doc.go b/vendor/cloud.google.com/go/speech/apiv1beta1/doc.go new file mode 100644 index 0000000000..f136941af2 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1beta1/doc.go @@ -0,0 +1,46 @@ +// 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 speech is an auto-generated package for the +// Google Cloud Speech API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Google Cloud Speech API. +package speech // import "cloud.google.com/go/speech/apiv1beta1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1beta1/mock_test.go b/vendor/cloud.google.com/go/speech/apiv1beta1/mock_test.go new file mode 100644 index 0000000000..627e90766c --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1beta1/mock_test.go @@ -0,0 +1,400 @@ +// 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 speech + +import ( + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockSpeechServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + speechpb.SpeechServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockSpeechServer) SyncRecognize(ctx context.Context, req *speechpb.SyncRecognizeRequest) (*speechpb.SyncRecognizeResponse, 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].(*speechpb.SyncRecognizeResponse), nil +} + +func (s *mockSpeechServer) AsyncRecognize(ctx context.Context, req *speechpb.AsyncRecognizeRequest) (*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 *mockSpeechServer) StreamingRecognize(stream speechpb.Speech_StreamingRecognizeServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + for { + if req, err := stream.Recv(); err == io.EOF { + break + } else if err != nil { + return err + } else { + s.reqs = append(s.reqs, req) + } + } + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*speechpb.StreamingRecognizeResponse)); err != nil { + return err + } + } + return nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockSpeech mockSpeechServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + speechpb.RegisterSpeechServer(serv, &mockSpeech) + + 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 TestSpeechSyncRecognize(t *testing.T) { + var expectedResponse *speechpb.SyncRecognizeResponse = &speechpb.SyncRecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + mockSpeech.resps = append(mockSpeech.resps[:0], expectedResponse) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRate int32 = 44100 + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRate: sampleRate, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.SyncRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SyncRecognize(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpeech.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 TestSpeechSyncRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = gstatus.Error(errCode, "test error") + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRate int32 = 44100 + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRate: sampleRate, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.SyncRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SyncRecognize(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 TestSpeechAsyncRecognize(t *testing.T) { + var expectedResponse *speechpb.AsyncRecognizeResponse = &speechpb.AsyncRecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockSpeech.resps = append(mockSpeech.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRate int32 = 44100 + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRate: sampleRate, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.AsyncRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AsyncRecognize(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, mockSpeech.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 TestSpeechAsyncRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = nil + mockSpeech.resps = append(mockSpeech.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRate int32 = 44100 + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRate: sampleRate, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.AsyncRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AsyncRecognize(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 TestSpeechStreamingRecognize(t *testing.T) { + var resultIndex int32 = 520358448 + var expectedResponse = &speechpb.StreamingRecognizeResponse{ + ResultIndex: resultIndex, + } + + mockSpeech.err = nil + mockSpeech.reqs = nil + + mockSpeech.resps = append(mockSpeech.resps[:0], expectedResponse) + + var request *speechpb.StreamingRecognizeRequest = &speechpb.StreamingRecognizeRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRecognize(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpeech.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 TestSpeechStreamingRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = gstatus.Error(errCode, "test error") + + var request *speechpb.StreamingRecognizeRequest = &speechpb.StreamingRecognizeRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRecognize(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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/vendor/cloud.google.com/go/speech/apiv1beta1/speech_client.go b/vendor/cloud.google.com/go/speech/apiv1beta1/speech_client.go new file mode 100644 index 0000000000..e5c7b900de --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1beta1/speech_client.go @@ -0,0 +1,267 @@ +// 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 speech + +import ( + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "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 { + SyncRecognize []gax.CallOption + AsyncRecognize []gax.CallOption + StreamingRecognize []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("speech.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{ + SyncRecognize: retry[[2]string{"default", "idempotent"}], + AsyncRecognize: retry[[2]string{"default", "idempotent"}], + StreamingRecognize: retry[[2]string{"default", "non_idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud Speech 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 speechpb.SpeechClient + + // 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 speech client. +// +// Service that implements Google Cloud Speech API. +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: speechpb.NewSpeechClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + 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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// SyncRecognize performs synchronous speech recognition: receive results after all audio +// has been sent and processed. +func (c *Client) SyncRecognize(ctx context.Context, req *speechpb.SyncRecognizeRequest, opts ...gax.CallOption) (*speechpb.SyncRecognizeResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SyncRecognize[0:len(c.CallOptions.SyncRecognize):len(c.CallOptions.SyncRecognize)], opts...) + var resp *speechpb.SyncRecognizeResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.SyncRecognize(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AsyncRecognize performs asynchronous speech recognition: receive results via the +// [google.longrunning.Operations] +// (/speech/reference/rest/v1beta1/operations#Operation) +// interface. Returns either an +// Operation.error or an Operation.response which contains +// an AsyncRecognizeResponse message. +func (c *Client) AsyncRecognize(ctx context.Context, req *speechpb.AsyncRecognizeRequest, opts ...gax.CallOption) (*AsyncRecognizeOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AsyncRecognize[0:len(c.CallOptions.AsyncRecognize):len(c.CallOptions.AsyncRecognize)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AsyncRecognize(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &AsyncRecognizeOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// StreamingRecognize performs bidirectional streaming speech recognition: receive results while +// sending audio. This method is only available via the gRPC API (not REST). +func (c *Client) StreamingRecognize(ctx context.Context, opts ...gax.CallOption) (speechpb.Speech_StreamingRecognizeClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.StreamingRecognize[0:len(c.CallOptions.StreamingRecognize):len(c.CallOptions.StreamingRecognize)], opts...) + var resp speechpb.Speech_StreamingRecognizeClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.StreamingRecognize(ctx, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AsyncRecognizeOperation manages a long-running operation from AsyncRecognize. +type AsyncRecognizeOperation struct { + lro *longrunning.Operation +} + +// AsyncRecognizeOperation returns a new AsyncRecognizeOperation from a given name. +// The name must be that of a previously created AsyncRecognizeOperation, possibly from a different process. +func (c *Client) AsyncRecognizeOperation(name string) *AsyncRecognizeOperation { + return &AsyncRecognizeOperation{ + 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 *AsyncRecognizeOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*speechpb.AsyncRecognizeResponse, error) { + var resp speechpb.AsyncRecognizeResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *AsyncRecognizeOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*speechpb.AsyncRecognizeResponse, error) { + var resp speechpb.AsyncRecognizeResponse + 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 *AsyncRecognizeOperation) Metadata() (*speechpb.AsyncRecognizeMetadata, error) { + var meta speechpb.AsyncRecognizeMetadata + 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 *AsyncRecognizeOperation) 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 *AsyncRecognizeOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/speech/apiv1beta1/speech_client_example_test.go b/vendor/cloud.google.com/go/speech/apiv1beta1/speech_client_example_test.go new file mode 100644 index 0000000000..8627565812 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1beta1/speech_client_example_test.go @@ -0,0 +1,110 @@ +// 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 speech_test + +import ( + "io" + + "cloud.google.com/go/speech/apiv1beta1" + "golang.org/x/net/context" + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1beta1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_SyncRecognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &speechpb.SyncRecognizeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SyncRecognize(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_AsyncRecognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &speechpb.AsyncRecognizeRequest{ + // TODO: Fill request struct fields. + } + op, err := c.AsyncRecognize(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_StreamingRecognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + stream, err := c.StreamingRecognize(ctx) + if err != nil { + // TODO: Handle error. + } + go func() { + reqs := []*speechpb.StreamingRecognizeRequest{ + // TODO: Create requests. + } + for _, req := range reqs { + if err := stream.Send(req); err != nil { + // TODO: Handle error. + } + } + stream.CloseSend() + }() + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1p1beta1/Recognize_smoke_test.go b/vendor/cloud.google.com/go/speech/apiv1p1beta1/Recognize_smoke_test.go new file mode 100644 index 0000000000..9df22c36d5 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1p1beta1/Recognize_smoke_test.go @@ -0,0 +1,80 @@ +// 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 speech + +import ( + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1p1beta1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestSpeechSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var languageCode string = "en-US" + var sampleRateHertz int32 = 44100 + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var config = &speechpb.RecognitionConfig{ + LanguageCode: languageCode, + SampleRateHertz: sampleRateHertz, + Encoding: encoding, + } + var uri string = "gs://gapic-toolkit/hello.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.RecognizeRequest{ + Config: config, + Audio: audio, + } + + if _, err := c.Recognize(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1p1beta1/doc.go b/vendor/cloud.google.com/go/speech/apiv1p1beta1/doc.go new file mode 100644 index 0000000000..6781bd1565 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1p1beta1/doc.go @@ -0,0 +1,46 @@ +// 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 speech is an auto-generated package for the +// Cloud Speech API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Converts audio to text by applying powerful neural network models. +package speech // import "cloud.google.com/go/speech/apiv1p1beta1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/speech/apiv1p1beta1/mock_test.go b/vendor/cloud.google.com/go/speech/apiv1p1beta1/mock_test.go new file mode 100644 index 0000000000..21e6705af3 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1p1beta1/mock_test.go @@ -0,0 +1,405 @@ +// 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 speech + +import ( + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1p1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockSpeechServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + speechpb.SpeechServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockSpeechServer) Recognize(ctx context.Context, req *speechpb.RecognizeRequest) (*speechpb.RecognizeResponse, 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].(*speechpb.RecognizeResponse), nil +} + +func (s *mockSpeechServer) LongRunningRecognize(ctx context.Context, req *speechpb.LongRunningRecognizeRequest) (*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 *mockSpeechServer) StreamingRecognize(stream speechpb.Speech_StreamingRecognizeServer) error { + md, _ := metadata.FromIncomingContext(stream.Context()) + if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { + return fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) + } + for { + if req, err := stream.Recv(); err == io.EOF { + break + } else if err != nil { + return err + } else { + s.reqs = append(s.reqs, req) + } + } + if s.err != nil { + return s.err + } + for _, v := range s.resps { + if err := stream.Send(v.(*speechpb.StreamingRecognizeResponse)); err != nil { + return err + } + } + return nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockSpeech mockSpeechServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + speechpb.RegisterSpeechServer(serv, &mockSpeech) + + 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 TestSpeechRecognize(t *testing.T) { + var expectedResponse *speechpb.RecognizeResponse = &speechpb.RecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + mockSpeech.resps = append(mockSpeech.resps[:0], expectedResponse) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.RecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Recognize(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpeech.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 TestSpeechRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = gstatus.Error(errCode, "test error") + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.RecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.Recognize(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 TestSpeechLongRunningRecognize(t *testing.T) { + var expectedResponse *speechpb.LongRunningRecognizeResponse = &speechpb.LongRunningRecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockSpeech.resps = append(mockSpeech.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.LongRunningRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.LongRunningRecognize(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, mockSpeech.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 TestSpeechLongRunningRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = nil + mockSpeech.resps = append(mockSpeech.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var encoding speechpb.RecognitionConfig_AudioEncoding = speechpb.RecognitionConfig_FLAC + var sampleRateHertz int32 = 44100 + var languageCode string = "en-US" + var config = &speechpb.RecognitionConfig{ + Encoding: encoding, + SampleRateHertz: sampleRateHertz, + LanguageCode: languageCode, + } + var uri string = "gs://bucket_name/file_name.flac" + var audio = &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Uri{ + Uri: uri, + }, + } + var request = &speechpb.LongRunningRecognizeRequest{ + Config: config, + Audio: audio, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.LongRunningRecognize(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 TestSpeechStreamingRecognize(t *testing.T) { + var expectedResponse *speechpb.StreamingRecognizeResponse = &speechpb.StreamingRecognizeResponse{} + + mockSpeech.err = nil + mockSpeech.reqs = nil + + mockSpeech.resps = append(mockSpeech.resps[:0], expectedResponse) + + var request *speechpb.StreamingRecognizeRequest = &speechpb.StreamingRecognizeRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRecognize(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockSpeech.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 TestSpeechStreamingRecognizeError(t *testing.T) { + errCode := codes.PermissionDenied + mockSpeech.err = gstatus.Error(errCode, "test error") + + var request *speechpb.StreamingRecognizeRequest = &speechpb.StreamingRecognizeRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + stream, err := c.StreamingRecognize(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := stream.Send(request); err != nil { + t.Fatal(err) + } + if err := stream.CloseSend(); err != nil { + t.Fatal(err) + } + resp, err := stream.Recv() + + 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/vendor/cloud.google.com/go/speech/apiv1p1beta1/speech_client.go b/vendor/cloud.google.com/go/speech/apiv1p1beta1/speech_client.go new file mode 100644 index 0000000000..7e1e06d221 --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1p1beta1/speech_client.go @@ -0,0 +1,265 @@ +// 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 speech + +import ( + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1p1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "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 { + Recognize []gax.CallOption + LongRunningRecognize []gax.CallOption + StreamingRecognize []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("speech.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{ + Recognize: retry[[2]string{"default", "idempotent"}], + LongRunningRecognize: retry[[2]string{"default", "non_idempotent"}], + StreamingRecognize: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Cloud Speech 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 speechpb.SpeechClient + + // 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 speech client. +// +// Service that implements Google Cloud Speech API. +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: speechpb.NewSpeechClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + 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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// Recognize performs synchronous speech recognition: receive results after all audio +// has been sent and processed. +func (c *Client) Recognize(ctx context.Context, req *speechpb.RecognizeRequest, opts ...gax.CallOption) (*speechpb.RecognizeResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.Recognize[0:len(c.CallOptions.Recognize):len(c.CallOptions.Recognize)], opts...) + var resp *speechpb.RecognizeResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.Recognize(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// LongRunningRecognize performs asynchronous speech recognition: receive results via the +// google.longrunning.Operations interface. Returns either an +// Operation.error or an Operation.response which contains +// a LongRunningRecognizeResponse message. +func (c *Client) LongRunningRecognize(ctx context.Context, req *speechpb.LongRunningRecognizeRequest, opts ...gax.CallOption) (*LongRunningRecognizeOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.LongRunningRecognize[0:len(c.CallOptions.LongRunningRecognize):len(c.CallOptions.LongRunningRecognize)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.LongRunningRecognize(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &LongRunningRecognizeOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// StreamingRecognize performs bidirectional streaming speech recognition: receive results while +// sending audio. This method is only available via the gRPC API (not REST). +func (c *Client) StreamingRecognize(ctx context.Context, opts ...gax.CallOption) (speechpb.Speech_StreamingRecognizeClient, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.StreamingRecognize[0:len(c.CallOptions.StreamingRecognize):len(c.CallOptions.StreamingRecognize)], opts...) + var resp speechpb.Speech_StreamingRecognizeClient + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.StreamingRecognize(ctx, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// LongRunningRecognizeOperation manages a long-running operation from LongRunningRecognize. +type LongRunningRecognizeOperation struct { + lro *longrunning.Operation +} + +// LongRunningRecognizeOperation returns a new LongRunningRecognizeOperation from a given name. +// The name must be that of a previously created LongRunningRecognizeOperation, possibly from a different process. +func (c *Client) LongRunningRecognizeOperation(name string) *LongRunningRecognizeOperation { + return &LongRunningRecognizeOperation{ + 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 *LongRunningRecognizeOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*speechpb.LongRunningRecognizeResponse, error) { + var resp speechpb.LongRunningRecognizeResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *LongRunningRecognizeOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*speechpb.LongRunningRecognizeResponse, error) { + var resp speechpb.LongRunningRecognizeResponse + 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 *LongRunningRecognizeOperation) Metadata() (*speechpb.LongRunningRecognizeMetadata, error) { + var meta speechpb.LongRunningRecognizeMetadata + 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 *LongRunningRecognizeOperation) 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 *LongRunningRecognizeOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/speech/apiv1p1beta1/speech_client_example_test.go b/vendor/cloud.google.com/go/speech/apiv1p1beta1/speech_client_example_test.go new file mode 100644 index 0000000000..c3ccb0755c --- /dev/null +++ b/vendor/cloud.google.com/go/speech/apiv1p1beta1/speech_client_example_test.go @@ -0,0 +1,110 @@ +// 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 speech_test + +import ( + "io" + + "cloud.google.com/go/speech/apiv1p1beta1" + "golang.org/x/net/context" + speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1p1beta1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_Recognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &speechpb.RecognizeRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.Recognize(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_LongRunningRecognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &speechpb.LongRunningRecognizeRequest{ + // TODO: Fill request struct fields. + } + op, err := c.LongRunningRecognize(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_StreamingRecognize() { + ctx := context.Background() + c, err := speech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + stream, err := c.StreamingRecognize(ctx) + if err != nil { + // TODO: Handle error. + } + go func() { + reqs := []*speechpb.StreamingRecognizeRequest{ + // TODO: Create requests. + } + for _, req := range reqs { + if err := stream.Send(req); err != nil { + // TODO: Handle error. + } + } + stream.CloseSend() + }() + for { + resp, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // TODO: handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/storage/acl.go b/vendor/cloud.google.com/go/storage/acl.go new file mode 100644 index 0000000000..d56795c714 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/acl.go @@ -0,0 +1,245 @@ +// 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 storage + +import ( + "net/http" + "reflect" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + "google.golang.org/api/googleapi" + raw "google.golang.org/api/storage/v1" +) + +// ACLRole is the level of access to grant. +type ACLRole string + +const ( + RoleOwner ACLRole = "OWNER" + RoleReader ACLRole = "READER" + RoleWriter ACLRole = "WRITER" +) + +// ACLEntity refers to a user or group. +// They are sometimes referred to as grantees. +// +// It could be in the form of: +// "user-", "user-", "group-", "group-", +// "domain-" and "project-team-". +// +// Or one of the predefined constants: AllUsers, AllAuthenticatedUsers. +type ACLEntity string + +const ( + AllUsers ACLEntity = "allUsers" + AllAuthenticatedUsers ACLEntity = "allAuthenticatedUsers" +) + +// ACLRule represents a grant for a role to an entity (user, group or team) for a Google Cloud Storage object or bucket. +type ACLRule struct { + Entity ACLEntity + Role ACLRole +} + +// ACLHandle provides operations on an access control list for a Google Cloud Storage bucket or object. +type ACLHandle struct { + c *Client + bucket string + object string + isDefault bool + userProject string // for requester-pays buckets +} + +// Delete permanently deletes the ACL entry for the given entity. +func (a *ACLHandle) Delete(ctx context.Context, entity ACLEntity) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Delete") + defer func() { trace.EndSpan(ctx, err) }() + + if a.object != "" { + return a.objectDelete(ctx, entity) + } + if a.isDefault { + return a.bucketDefaultDelete(ctx, entity) + } + return a.bucketDelete(ctx, entity) +} + +// Set sets the permission level for the given entity. +func (a *ACLHandle) Set(ctx context.Context, entity ACLEntity, role ACLRole) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Set") + defer func() { trace.EndSpan(ctx, err) }() + + if a.object != "" { + return a.objectSet(ctx, entity, role, false) + } + if a.isDefault { + return a.objectSet(ctx, entity, role, true) + } + return a.bucketSet(ctx, entity, role) +} + +// List retrieves ACL entries. +func (a *ACLHandle) List(ctx context.Context) (rules []ACLRule, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.List") + defer func() { trace.EndSpan(ctx, err) }() + + if a.object != "" { + return a.objectList(ctx) + } + if a.isDefault { + return a.bucketDefaultList(ctx) + } + return a.bucketList(ctx) +} + +func (a *ACLHandle) bucketDefaultList(ctx context.Context) ([]ACLRule, error) { + var acls *raw.ObjectAccessControls + var err error + err = runWithRetry(ctx, func() error { + req := a.c.raw.DefaultObjectAccessControls.List(a.bucket) + a.configureCall(req, ctx) + acls, err = req.Do() + return err + }) + if err != nil { + return nil, err + } + return toACLRules(acls.Items), nil +} + +func (a *ACLHandle) bucketDefaultDelete(ctx context.Context, entity ACLEntity) error { + return runWithRetry(ctx, func() error { + req := a.c.raw.DefaultObjectAccessControls.Delete(a.bucket, string(entity)) + a.configureCall(req, ctx) + return req.Do() + }) +} + +func (a *ACLHandle) bucketList(ctx context.Context) ([]ACLRule, error) { + var acls *raw.BucketAccessControls + var err error + err = runWithRetry(ctx, func() error { + req := a.c.raw.BucketAccessControls.List(a.bucket) + a.configureCall(req, ctx) + acls, err = req.Do() + return err + }) + if err != nil { + return nil, err + } + r := make([]ACLRule, len(acls.Items)) + for i, v := range acls.Items { + r[i].Entity = ACLEntity(v.Entity) + r[i].Role = ACLRole(v.Role) + } + return r, nil +} + +func (a *ACLHandle) bucketSet(ctx context.Context, entity ACLEntity, role ACLRole) error { + acl := &raw.BucketAccessControl{ + Bucket: a.bucket, + Entity: string(entity), + Role: string(role), + } + err := runWithRetry(ctx, func() error { + req := a.c.raw.BucketAccessControls.Update(a.bucket, string(entity), acl) + a.configureCall(req, ctx) + _, err := req.Do() + return err + }) + if err != nil { + return err + } + return nil +} + +func (a *ACLHandle) bucketDelete(ctx context.Context, entity ACLEntity) error { + err := runWithRetry(ctx, func() error { + req := a.c.raw.BucketAccessControls.Delete(a.bucket, string(entity)) + a.configureCall(req, ctx) + return req.Do() + }) + if err != nil { + return err + } + return nil +} + +func (a *ACLHandle) objectList(ctx context.Context) ([]ACLRule, error) { + var acls *raw.ObjectAccessControls + var err error + err = runWithRetry(ctx, func() error { + req := a.c.raw.ObjectAccessControls.List(a.bucket, a.object) + a.configureCall(req, ctx) + acls, err = req.Do() + return err + }) + if err != nil { + return nil, err + } + return toACLRules(acls.Items), nil +} + +func (a *ACLHandle) objectSet(ctx context.Context, entity ACLEntity, role ACLRole, isBucketDefault bool) error { + type setRequest interface { + Do(opts ...googleapi.CallOption) (*raw.ObjectAccessControl, error) + Header() http.Header + } + + acl := &raw.ObjectAccessControl{ + Bucket: a.bucket, + Entity: string(entity), + Role: string(role), + } + var req setRequest + if isBucketDefault { + req = a.c.raw.DefaultObjectAccessControls.Update(a.bucket, string(entity), acl) + } else { + req = a.c.raw.ObjectAccessControls.Update(a.bucket, a.object, string(entity), acl) + } + a.configureCall(req, ctx) + return runWithRetry(ctx, func() error { + _, err := req.Do() + return err + }) +} + +func (a *ACLHandle) objectDelete(ctx context.Context, entity ACLEntity) error { + return runWithRetry(ctx, func() error { + req := a.c.raw.ObjectAccessControls.Delete(a.bucket, a.object, string(entity)) + a.configureCall(req, ctx) + return req.Do() + }) +} + +func (a *ACLHandle) configureCall(call interface { + Header() http.Header +}, ctx context.Context) { + vc := reflect.ValueOf(call) + vc.MethodByName("Context").Call([]reflect.Value{reflect.ValueOf(ctx)}) + if a.userProject != "" { + vc.MethodByName("UserProject").Call([]reflect.Value{reflect.ValueOf(a.userProject)}) + } + setClientHeader(call.Header()) +} + +func toACLRules(items []*raw.ObjectAccessControl) []ACLRule { + r := make([]ACLRule, 0, len(items)) + for _, item := range items { + r = append(r, ACLRule{Entity: ACLEntity(item.Entity), Role: ACLRole(item.Role)}) + } + return r +} diff --git a/vendor/cloud.google.com/go/storage/bucket.go b/vendor/cloud.google.com/go/storage/bucket.go new file mode 100644 index 0000000000..74e7ba3be4 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/bucket.go @@ -0,0 +1,990 @@ +// 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 storage + +import ( + "fmt" + "net/http" + "reflect" + "time" + + "cloud.google.com/go/internal/optional" + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + "google.golang.org/api/googleapi" + "google.golang.org/api/iterator" + raw "google.golang.org/api/storage/v1" +) + +// BucketHandle provides operations on a Google Cloud Storage bucket. +// Use Client.Bucket to get a handle. +type BucketHandle struct { + c *Client + name string + acl ACLHandle + defaultObjectACL ACLHandle + conds *BucketConditions + userProject string // project for Requester Pays buckets +} + +// Bucket returns a BucketHandle, which provides operations on the named bucket. +// This call does not perform any network operations. +// +// The supplied name must contain only lowercase letters, numbers, dashes, +// underscores, and dots. The full specification for valid bucket names can be +// found at: +// https://cloud.google.com/storage/docs/bucket-naming +func (c *Client) Bucket(name string) *BucketHandle { + return &BucketHandle{ + c: c, + name: name, + acl: ACLHandle{ + c: c, + bucket: name, + }, + defaultObjectACL: ACLHandle{ + c: c, + bucket: name, + isDefault: true, + }, + } +} + +// Create creates the Bucket in the project. +// If attrs is nil the API defaults will be used. +func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *BucketAttrs) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create") + defer func() { trace.EndSpan(ctx, err) }() + + var bkt *raw.Bucket + if attrs != nil { + bkt = attrs.toRawBucket() + } else { + bkt = &raw.Bucket{} + } + bkt.Name = b.name + // If there is lifecycle information but no location, explicitly set + // the location. This is a GCS quirk/bug. + if bkt.Location == "" && bkt.Lifecycle != nil { + bkt.Location = "US" + } + req := b.c.raw.Buckets.Insert(projectID, bkt) + setClientHeader(req.Header()) + return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err }) +} + +// Delete deletes the Bucket. +func (b *BucketHandle) Delete(ctx context.Context) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Delete") + defer func() { trace.EndSpan(ctx, err) }() + + req, err := b.newDeleteCall() + if err != nil { + return err + } + return runWithRetry(ctx, func() error { return req.Context(ctx).Do() }) +} + +func (b *BucketHandle) newDeleteCall() (*raw.BucketsDeleteCall, error) { + req := b.c.raw.Buckets.Delete(b.name) + setClientHeader(req.Header()) + if err := applyBucketConds("BucketHandle.Delete", b.conds, req); err != nil { + return nil, err + } + if b.userProject != "" { + req.UserProject(b.userProject) + } + return req, nil +} + +// ACL returns an ACLHandle, which provides access to the bucket's access control list. +// This controls who can list, create or overwrite the objects in a bucket. +// This call does not perform any network operations. +func (b *BucketHandle) ACL() *ACLHandle { + return &b.acl +} + +// DefaultObjectACL returns an ACLHandle, which provides access to the bucket's default object ACLs. +// These ACLs are applied to newly created objects in this bucket that do not have a defined ACL. +// This call does not perform any network operations. +func (b *BucketHandle) DefaultObjectACL() *ACLHandle { + return &b.defaultObjectACL +} + +// Object returns an ObjectHandle, which provides operations on the named object. +// This call does not perform any network operations. +// +// name must consist entirely of valid UTF-8-encoded runes. The full specification +// for valid object names can be found at: +// https://cloud.google.com/storage/docs/bucket-naming +func (b *BucketHandle) Object(name string) *ObjectHandle { + return &ObjectHandle{ + c: b.c, + bucket: b.name, + object: name, + acl: ACLHandle{ + c: b.c, + bucket: b.name, + object: name, + userProject: b.userProject, + }, + gen: -1, + userProject: b.userProject, + } +} + +// Attrs returns the metadata for the bucket. +func (b *BucketHandle) Attrs(ctx context.Context) (attrs *BucketAttrs, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Attrs") + defer func() { trace.EndSpan(ctx, err) }() + + req, err := b.newGetCall() + if err != nil { + return nil, err + } + var resp *raw.Bucket + err = runWithRetry(ctx, func() error { + resp, err = req.Context(ctx).Do() + return err + }) + if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { + return nil, ErrBucketNotExist + } + if err != nil { + return nil, err + } + return newBucket(resp) +} + +func (b *BucketHandle) newGetCall() (*raw.BucketsGetCall, error) { + req := b.c.raw.Buckets.Get(b.name).Projection("full") + setClientHeader(req.Header()) + if err := applyBucketConds("BucketHandle.Attrs", b.conds, req); err != nil { + return nil, err + } + if b.userProject != "" { + req.UserProject(b.userProject) + } + return req, nil +} + +func (b *BucketHandle) Update(ctx context.Context, uattrs BucketAttrsToUpdate) (attrs *BucketAttrs, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create") + defer func() { trace.EndSpan(ctx, err) }() + + req, err := b.newPatchCall(&uattrs) + if err != nil { + return nil, err + } + // TODO(jba): retry iff metagen is set? + rb, err := req.Context(ctx).Do() + if err != nil { + return nil, err + } + return newBucket(rb) +} + +func (b *BucketHandle) newPatchCall(uattrs *BucketAttrsToUpdate) (*raw.BucketsPatchCall, error) { + rb := uattrs.toRawBucket() + req := b.c.raw.Buckets.Patch(b.name, rb).Projection("full") + setClientHeader(req.Header()) + if err := applyBucketConds("BucketHandle.Update", b.conds, req); err != nil { + return nil, err + } + if b.userProject != "" { + req.UserProject(b.userProject) + } + return req, nil +} + +// BucketAttrs represents the metadata for a Google Cloud Storage bucket. +// Read-only fields are ignored by BucketHandle.Create. +type BucketAttrs struct { + // Name is the name of the bucket. + // This field is read-only. + Name string + + // ACL is the list of access control rules on the bucket. + ACL []ACLRule + + // DefaultObjectACL is the list of access controls to + // apply to new objects when no object ACL is provided. + DefaultObjectACL []ACLRule + + // Location is the location of the bucket. It defaults to "US". + Location string + + // MetaGeneration is the metadata generation of the bucket. + // This field is read-only. + MetaGeneration int64 + + // StorageClass is the default storage class of the bucket. This defines + // how objects in the bucket are stored and determines the SLA + // and the cost of storage. Typical values are "MULTI_REGIONAL", + // "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and + // "DURABLE_REDUCED_AVAILABILITY". Defaults to "STANDARD", which + // is equivalent to "MULTI_REGIONAL" or "REGIONAL" depending on + // the bucket's location settings. + StorageClass string + + // Created is the creation time of the bucket. + // This field is read-only. + Created time.Time + + // VersioningEnabled reports whether this bucket has versioning enabled. + VersioningEnabled bool + + // Labels are the bucket's labels. + Labels map[string]string + + // RequesterPays reports whether the bucket is a Requester Pays bucket. + // Clients performing operations on Requester Pays buckets must provide + // a user project (see BucketHandle.UserProject), which will be billed + // for the operations. + RequesterPays bool + + // Lifecycle is the lifecycle configuration for objects in the bucket. + Lifecycle Lifecycle + + // Retention policy enforces a minimum retention time for all objects + // contained in the bucket. A RetentionPolicy of nil implies the bucket + // has no minimum data retention. + // + // This feature is in private alpha release. It is not currently available to + // most customers. It might be changed in backwards-incompatible ways and is not + // subject to any SLA or deprecation policy. + RetentionPolicy *RetentionPolicy + + // The bucket's Cross-Origin Resource Sharing (CORS) configuration. + CORS []CORS + + // The encryption configuration used by default for newly inserted objects. + Encryption *BucketEncryption +} + +// Lifecycle is the lifecycle configuration for objects in the bucket. +type Lifecycle struct { + Rules []LifecycleRule +} + +// Retention policy enforces a minimum retention time for all objects +// contained in the bucket. +// +// Any attempt to overwrite or delete objects younger than the retention +// period will result in an error. An unlocked retention policy can be +// modified or removed from the bucket via the Update method. A +// locked retention policy cannot be removed or shortened in duration +// for the lifetime of the bucket. +// +// This feature is in private alpha release. It is not currently available to +// most customers. It might be changed in backwards-incompatible ways and is not +// subject to any SLA or deprecation policy. +type RetentionPolicy struct { + // RetentionPeriod specifies the duration that objects need to be + // retained. Retention duration must be greater than zero and less than + // 100 years. Note that enforcement of retention periods less than a day + // is not guaranteed. Such periods should only be used for testing + // purposes. + RetentionPeriod time.Duration + + // EffectiveTime is the time from which the policy was enforced and + // effective. This field is read-only. + EffectiveTime time.Time +} + +const ( + // RFC3339 date with only the date segment, used for CreatedBefore in LifecycleRule. + rfc3339Date = "2006-01-02" + + // DeleteAction is a lifecycle action that deletes a live and/or archived + // objects. Takes precedence over SetStorageClass actions. + DeleteAction = "Delete" + + // SetStorageClassAction changes the storage class of live and/or archived + // objects. + SetStorageClassAction = "SetStorageClass" +) + +// LifecycleRule is a lifecycle configuration rule. +// +// When all the configured conditions are met by an object in the bucket, the +// configured action will automatically be taken on that object. +type LifecycleRule struct { + // Action is the action to take when all of the associated conditions are + // met. + Action LifecycleAction + + // Condition is the set of conditions that must be met for the associated + // action to be taken. + Condition LifecycleCondition +} + +// LifecycleAction is a lifecycle configuration action. +type LifecycleAction struct { + // Type is the type of action to take on matching objects. + // + // Acceptable values are "Delete" to delete matching objects and + // "SetStorageClass" to set the storage class defined in StorageClass on + // matching objects. + Type string + + // StorageClass is the storage class to set on matching objects if the Action + // is "SetStorageClass". + StorageClass string +} + +// Liveness specifies whether the object is live or not. +type Liveness int + +const ( + // LiveAndArchived includes both live and archived objects. + LiveAndArchived Liveness = iota + // Live specifies that the object is still live. + Live + // Archived specifies that the object is archived. + Archived +) + +// LifecycleCondition is a set of conditions used to match objects and take an +// action automatically. +// +// All configured conditions must be met for the associated action to be taken. +type LifecycleCondition struct { + // AgeInDays is the age of the object in days. + AgeInDays int64 + + // CreatedBefore is the time the object was created. + // + // This condition is satisfied when an object is created before midnight of + // the specified date in UTC. + CreatedBefore time.Time + + // Liveness specifies the object's liveness. Relevant only for versioned objects + Liveness Liveness + + // MatchesStorageClasses is the condition matching the object's storage + // class. + // + // Values include "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", + // "STANDARD", and "DURABLE_REDUCED_AVAILABILITY". + MatchesStorageClasses []string + + // NumNewerVersions is the condition matching objects with a number of newer versions. + // + // If the value is N, this condition is satisfied when there are at least N + // versions (including the live version) newer than this version of the + // object. + NumNewerVersions int64 +} + +func newBucket(b *raw.Bucket) (*BucketAttrs, error) { + if b == nil { + return nil, nil + } + rp, err := toRetentionPolicy(b.RetentionPolicy) + if err != nil { + return nil, err + } + bucket := &BucketAttrs{ + Name: b.Name, + Location: b.Location, + MetaGeneration: b.Metageneration, + StorageClass: b.StorageClass, + Created: convertTime(b.TimeCreated), + VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled, + Labels: b.Labels, + RequesterPays: b.Billing != nil && b.Billing.RequesterPays, + Lifecycle: toLifecycle(b.Lifecycle), + RetentionPolicy: rp, + CORS: toCORS(b.Cors), + Encryption: toBucketEncryption(b.Encryption), + } + acl := make([]ACLRule, len(b.Acl)) + for i, rule := range b.Acl { + acl[i] = ACLRule{ + Entity: ACLEntity(rule.Entity), + Role: ACLRole(rule.Role), + } + } + bucket.ACL = acl + objACL := make([]ACLRule, len(b.DefaultObjectAcl)) + for i, rule := range b.DefaultObjectAcl { + objACL[i] = ACLRule{ + Entity: ACLEntity(rule.Entity), + Role: ACLRole(rule.Role), + } + } + bucket.DefaultObjectACL = objACL + return bucket, nil +} + +// toRawBucket copies the editable attribute from b to the raw library's Bucket type. +func (b *BucketAttrs) toRawBucket() *raw.Bucket { + var acl []*raw.BucketAccessControl + if len(b.ACL) > 0 { + acl = make([]*raw.BucketAccessControl, len(b.ACL)) + for i, rule := range b.ACL { + acl[i] = &raw.BucketAccessControl{ + Entity: string(rule.Entity), + Role: string(rule.Role), + } + } + } + dACL := toRawObjectACL(b.DefaultObjectACL) + // Copy label map. + var labels map[string]string + if len(b.Labels) > 0 { + labels = make(map[string]string, len(b.Labels)) + for k, v := range b.Labels { + labels[k] = v + } + } + // Ignore VersioningEnabled if it is false. This is OK because + // we only call this method when creating a bucket, and by default + // new buckets have versioning off. + var v *raw.BucketVersioning + if b.VersioningEnabled { + v = &raw.BucketVersioning{Enabled: true} + } + var bb *raw.BucketBilling + if b.RequesterPays { + bb = &raw.BucketBilling{RequesterPays: true} + } + return &raw.Bucket{ + Name: b.Name, + DefaultObjectAcl: dACL, + Location: b.Location, + StorageClass: b.StorageClass, + Acl: acl, + Versioning: v, + Labels: labels, + Billing: bb, + Lifecycle: toRawLifecycle(b.Lifecycle), + RetentionPolicy: b.RetentionPolicy.toRawRetentionPolicy(), + Cors: toRawCORS(b.CORS), + Encryption: b.Encryption.toRawBucketEncryption(), + } +} + +// CORS is the bucket's Cross-Origin Resource Sharing (CORS) configuration. +type CORS struct { + // MaxAge is the value to return in the Access-Control-Max-Age + // header used in preflight responses. + MaxAge time.Duration + + // Methods is the list of HTTP methods on which to include CORS response + // headers, (GET, OPTIONS, POST, etc) Note: "*" is permitted in the list + // of methods, and means "any method". + Methods []string + + // Origins is the list of Origins eligible to receive CORS response + // headers. Note: "*" is permitted in the list of origins, and means + // "any Origin". + Origins []string + + // ResponseHeaders is the list of HTTP headers other than the simple + // response headers to give permission for the user-agent to share + // across domains. + ResponseHeaders []string +} + +// BucketEncryption is a bucket's encryption configuration. +type BucketEncryption struct { + // A Cloud KMS key name, in the form + // projects/P/locations/L/keyRings/R/cryptoKeys/K, that will be used to encrypt + // objects inserted into this bucket, if no encryption method is specified. + // The key's location must be the same as the bucket's. + DefaultKMSKeyName string +} + +type BucketAttrsToUpdate struct { + // If set, updates whether the bucket uses versioning. + VersioningEnabled optional.Bool + + // If set, updates whether the bucket is a Requester Pays bucket. + RequesterPays optional.Bool + + // If set, updates the retention policy of the bucket. Using + // RetentionPolicy.RetentionPeriod = 0 will delete the existing policy. + // + // This feature is in private alpha release. It is not currently available to + // most customers. It might be changed in backwards-incompatible ways and is not + // subject to any SLA or deprecation policy. + RetentionPolicy *RetentionPolicy + + // If set, replaces the CORS configuration with a new configuration. + // An empty (rather than nil) slice causes all CORS policies to be removed. + CORS []CORS + + // If set, replaces the encryption configuration of the bucket. Using + // BucketEncryption.DefaultKMSKeyName = "" will delete the existing + // configuration. + Encryption *BucketEncryption + + // If set, replaces the lifecycle configuration of the bucket. + Lifecycle *Lifecycle + + setLabels map[string]string + deleteLabels map[string]bool +} + +// SetLabel causes a label to be added or modified when ua is used +// in a call to Bucket.Update. +func (ua *BucketAttrsToUpdate) SetLabel(name, value string) { + if ua.setLabels == nil { + ua.setLabels = map[string]string{} + } + ua.setLabels[name] = value +} + +// DeleteLabel causes a label to be deleted when ua is used in a +// call to Bucket.Update. +func (ua *BucketAttrsToUpdate) DeleteLabel(name string) { + if ua.deleteLabels == nil { + ua.deleteLabels = map[string]bool{} + } + ua.deleteLabels[name] = true +} + +func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket { + rb := &raw.Bucket{} + if ua.CORS != nil { + rb.Cors = toRawCORS(ua.CORS) + rb.ForceSendFields = append(rb.ForceSendFields, "Cors") + } + if ua.RetentionPolicy != nil { + if ua.RetentionPolicy.RetentionPeriod == 0 { + rb.NullFields = append(rb.NullFields, "RetentionPolicy") + rb.RetentionPolicy = nil + } else { + rb.RetentionPolicy = ua.RetentionPolicy.toRawRetentionPolicy() + } + } + if ua.VersioningEnabled != nil { + rb.Versioning = &raw.BucketVersioning{ + Enabled: optional.ToBool(ua.VersioningEnabled), + ForceSendFields: []string{"Enabled"}, + } + } + if ua.RequesterPays != nil { + rb.Billing = &raw.BucketBilling{ + RequesterPays: optional.ToBool(ua.RequesterPays), + ForceSendFields: []string{"RequesterPays"}, + } + } + if ua.Encryption != nil { + if ua.Encryption.DefaultKMSKeyName == "" { + rb.NullFields = append(rb.NullFields, "Encryption") + rb.Encryption = nil + } else { + rb.Encryption = ua.Encryption.toRawBucketEncryption() + } + } + if ua.Lifecycle != nil { + rb.Lifecycle = toRawLifecycle(*ua.Lifecycle) + } + if ua.setLabels != nil || ua.deleteLabels != nil { + rb.Labels = map[string]string{} + for k, v := range ua.setLabels { + rb.Labels[k] = v + } + if len(rb.Labels) == 0 && len(ua.deleteLabels) > 0 { + rb.ForceSendFields = append(rb.ForceSendFields, "Labels") + } + for l := range ua.deleteLabels { + rb.NullFields = append(rb.NullFields, "Labels."+l) + } + } + return rb +} + +// If returns a new BucketHandle that applies a set of preconditions. +// Preconditions already set on the BucketHandle are ignored. +// Operations on the new handle will return an error if the preconditions are not +// satisfied. The only valid preconditions for buckets are MetagenerationMatch +// and MetagenerationNotMatch. +func (b *BucketHandle) If(conds BucketConditions) *BucketHandle { + b2 := *b + b2.conds = &conds + return &b2 +} + +// BucketConditions constrain bucket methods to act on specific metagenerations. +// +// The zero value is an empty set of constraints. +type BucketConditions struct { + // MetagenerationMatch specifies that the bucket must have the given + // metageneration for the operation to occur. + // If MetagenerationMatch is zero, it has no effect. + MetagenerationMatch int64 + + // MetagenerationNotMatch specifies that the bucket must not have the given + // metageneration for the operation to occur. + // If MetagenerationNotMatch is zero, it has no effect. + MetagenerationNotMatch int64 +} + +func (c *BucketConditions) validate(method string) error { + if *c == (BucketConditions{}) { + return fmt.Errorf("storage: %s: empty conditions", method) + } + if c.MetagenerationMatch != 0 && c.MetagenerationNotMatch != 0 { + return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method) + } + return nil +} + +// UserProject returns a new BucketHandle that passes the project ID as the user +// project for all subsequent calls. Calls with a user project will be billed to that +// project rather than to the bucket's owning project. +// +// A user project is required for all operations on Requester Pays buckets. +func (b *BucketHandle) UserProject(projectID string) *BucketHandle { + b2 := *b + b2.userProject = projectID + b2.acl.userProject = projectID + b2.defaultObjectACL.userProject = projectID + return &b2 +} + +// LockRetentionPolicy locks a bucket's retention policy until a previously-configured +// RetentionPeriod past the EffectiveTime. Note that if RetentionPeriod is set to less +// than a day, the retention policy is treated as a development configuration and locking +// will have no effect. The BucketHandle must have a metageneration condition that +// matches the bucket's metageneration. See BucketHandle.If. +// +// This feature is in private alpha release. It is not currently available to +// most customers. It might be changed in backwards-incompatible ways and is not +// subject to any SLA or deprecation policy. +func (b *BucketHandle) LockRetentionPolicy(ctx context.Context) error { + var metageneration int64 + if b.conds != nil { + metageneration = b.conds.MetagenerationMatch + } + req := b.c.raw.Buckets.LockRetentionPolicy(b.name, metageneration) + _, err := req.Context(ctx).Do() + return err +} + +// applyBucketConds modifies the provided call using the conditions in conds. +// call is something that quacks like a *raw.WhateverCall. +func applyBucketConds(method string, conds *BucketConditions, call interface{}) error { + if conds == nil { + return nil + } + if err := conds.validate(method); err != nil { + return err + } + cval := reflect.ValueOf(call) + switch { + case conds.MetagenerationMatch != 0: + if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) { + return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method) + } + case conds.MetagenerationNotMatch != 0: + if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) { + return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method) + } + } + return nil +} + +func (rp *RetentionPolicy) toRawRetentionPolicy() *raw.BucketRetentionPolicy { + if rp == nil { + return nil + } + return &raw.BucketRetentionPolicy{ + RetentionPeriod: int64(rp.RetentionPeriod / time.Second), + } +} + +func toRetentionPolicy(rp *raw.BucketRetentionPolicy) (*RetentionPolicy, error) { + if rp == nil { + return nil, nil + } + t, err := time.Parse(time.RFC3339, rp.EffectiveTime) + if err != nil { + return nil, err + } + return &RetentionPolicy{ + RetentionPeriod: time.Duration(rp.RetentionPeriod) * time.Second, + EffectiveTime: t, + }, nil +} + +func toRawCORS(c []CORS) []*raw.BucketCors { + var out []*raw.BucketCors + for _, v := range c { + out = append(out, &raw.BucketCors{ + MaxAgeSeconds: int64(v.MaxAge / time.Second), + Method: v.Methods, + Origin: v.Origins, + ResponseHeader: v.ResponseHeaders, + }) + } + return out +} + +func toCORS(rc []*raw.BucketCors) []CORS { + var out []CORS + for _, v := range rc { + out = append(out, CORS{ + MaxAge: time.Duration(v.MaxAgeSeconds) * time.Second, + Methods: v.Method, + Origins: v.Origin, + ResponseHeaders: v.ResponseHeader, + }) + } + return out +} + +func toRawLifecycle(l Lifecycle) *raw.BucketLifecycle { + var rl raw.BucketLifecycle + if len(l.Rules) == 0 { + return nil + } + for _, r := range l.Rules { + rr := &raw.BucketLifecycleRule{ + Action: &raw.BucketLifecycleRuleAction{ + Type: r.Action.Type, + StorageClass: r.Action.StorageClass, + }, + Condition: &raw.BucketLifecycleRuleCondition{ + Age: r.Condition.AgeInDays, + MatchesStorageClass: r.Condition.MatchesStorageClasses, + NumNewerVersions: r.Condition.NumNewerVersions, + }, + } + + switch r.Condition.Liveness { + case LiveAndArchived: + rr.Condition.IsLive = nil + case Live: + rr.Condition.IsLive = googleapi.Bool(true) + case Archived: + rr.Condition.IsLive = googleapi.Bool(false) + } + + if !r.Condition.CreatedBefore.IsZero() { + rr.Condition.CreatedBefore = r.Condition.CreatedBefore.Format(rfc3339Date) + } + rl.Rule = append(rl.Rule, rr) + } + return &rl +} + +func toLifecycle(rl *raw.BucketLifecycle) Lifecycle { + var l Lifecycle + if rl == nil { + return l + } + for _, rr := range rl.Rule { + r := LifecycleRule{ + Action: LifecycleAction{ + Type: rr.Action.Type, + StorageClass: rr.Action.StorageClass, + }, + Condition: LifecycleCondition{ + AgeInDays: rr.Condition.Age, + MatchesStorageClasses: rr.Condition.MatchesStorageClass, + NumNewerVersions: rr.Condition.NumNewerVersions, + }, + } + + switch { + case rr.Condition.IsLive == nil: + r.Condition.Liveness = LiveAndArchived + case *rr.Condition.IsLive == true: + r.Condition.Liveness = Live + case *rr.Condition.IsLive == false: + r.Condition.Liveness = Archived + } + + if rr.Condition.CreatedBefore != "" { + r.Condition.CreatedBefore, _ = time.Parse(rfc3339Date, rr.Condition.CreatedBefore) + } + l.Rules = append(l.Rules, r) + } + return l +} + +func (e *BucketEncryption) toRawBucketEncryption() *raw.BucketEncryption { + if e == nil { + return nil + } + return &raw.BucketEncryption{ + DefaultKmsKeyName: e.DefaultKMSKeyName, + } +} + +func toBucketEncryption(e *raw.BucketEncryption) *BucketEncryption { + if e == nil { + return nil + } + return &BucketEncryption{DefaultKMSKeyName: e.DefaultKmsKeyName} +} + +// Objects returns an iterator over the objects in the bucket that match the Query q. +// If q is nil, no filtering is done. +func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator { + it := &ObjectIterator{ + ctx: ctx, + bucket: b, + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.items) }, + func() interface{} { b := it.items; it.items = nil; return b }) + if q != nil { + it.query = *q + } + return it +} + +// An ObjectIterator is an iterator over ObjectAttrs. +type ObjectIterator struct { + ctx context.Context + bucket *BucketHandle + query Query + pageInfo *iterator.PageInfo + nextFunc func() error + items []*ObjectAttrs +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *ObjectIterator) 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 iterator.Done, all subsequent +// calls will return iterator.Done. +// +// If Query.Delimiter is non-empty, some of the ObjectAttrs returned by Next will +// have a non-empty Prefix field, and a zero value for all other fields. These +// represent prefixes. +func (it *ObjectIterator) Next() (*ObjectAttrs, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + item := it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) { + req := it.bucket.c.raw.Objects.List(it.bucket.name) + setClientHeader(req.Header()) + req.Projection("full") + req.Delimiter(it.query.Delimiter) + req.Prefix(it.query.Prefix) + req.Versions(it.query.Versions) + req.PageToken(pageToken) + if it.bucket.userProject != "" { + req.UserProject(it.bucket.userProject) + } + if pageSize > 0 { + req.MaxResults(int64(pageSize)) + } + var resp *raw.Objects + var err error + err = runWithRetry(it.ctx, func() error { + resp, err = req.Context(it.ctx).Do() + return err + }) + if err != nil { + if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { + err = ErrBucketNotExist + } + return "", err + } + for _, item := range resp.Items { + it.items = append(it.items, newObject(item)) + } + for _, prefix := range resp.Prefixes { + it.items = append(it.items, &ObjectAttrs{Prefix: prefix}) + } + return resp.NextPageToken, nil +} + +// Buckets returns an iterator over the buckets in the project. You may +// optionally set the iterator's Prefix field to restrict the list to buckets +// whose names begin with the prefix. By default, all buckets in the project +// are returned. +func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator { + it := &BucketIterator{ + ctx: ctx, + client: c, + projectID: projectID, + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.buckets) }, + func() interface{} { b := it.buckets; it.buckets = nil; return b }) + return it +} + +// A BucketIterator is an iterator over BucketAttrs. +type BucketIterator struct { + // Prefix restricts the iterator to buckets whose names begin with it. + Prefix string + + ctx context.Context + client *Client + projectID string + buckets []*BucketAttrs + pageInfo *iterator.PageInfo + nextFunc func() error +} + +// Next returns the next result. Its second return value is iterator.Done if +// there are no more results. Once Next returns iterator.Done, all subsequent +// calls will return iterator.Done. +func (it *BucketIterator) Next() (*BucketAttrs, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + b := it.buckets[0] + it.buckets = it.buckets[1:] + return b, nil +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } + +func (it *BucketIterator) fetch(pageSize int, pageToken string) (token string, err error) { + req := it.client.raw.Buckets.List(it.projectID) + setClientHeader(req.Header()) + req.Projection("full") + req.Prefix(it.Prefix) + req.PageToken(pageToken) + if pageSize > 0 { + req.MaxResults(int64(pageSize)) + } + var resp *raw.Buckets + err = runWithRetry(it.ctx, func() error { + resp, err = req.Context(it.ctx).Do() + return err + }) + if err != nil { + return "", err + } + for _, item := range resp.Items { + b, err := newBucket(item) + if err != nil { + return "", err + } + it.buckets = append(it.buckets, b) + } + return resp.NextPageToken, nil +} diff --git a/vendor/cloud.google.com/go/storage/bucket_test.go b/vendor/cloud.google.com/go/storage/bucket_test.go new file mode 100644 index 0000000000..59d04a383e --- /dev/null +++ b/vendor/cloud.google.com/go/storage/bucket_test.go @@ -0,0 +1,355 @@ +// Copyright 2017 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 storage + +import ( + "net/http" + "reflect" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "google.golang.org/api/googleapi" + raw "google.golang.org/api/storage/v1" +) + +func TestBucketAttrsToRawBucket(t *testing.T) { + t.Parallel() + attrs := &BucketAttrs{ + Name: "name", + ACL: []ACLRule{{Entity: "bob@example.com", Role: RoleOwner}}, + DefaultObjectACL: []ACLRule{{Entity: AllUsers, Role: RoleReader}}, + Location: "loc", + StorageClass: "class", + RetentionPolicy: &RetentionPolicy{ + RetentionPeriod: 3 * time.Second, + }, + VersioningEnabled: false, + // should be ignored: + MetaGeneration: 39, + Created: time.Now(), + Labels: map[string]string{"label": "value"}, + CORS: []CORS{ + { + MaxAge: time.Hour, + Methods: []string{"GET", "POST"}, + Origins: []string{"*"}, + ResponseHeaders: []string{"FOO"}, + }, + }, + Encryption: &BucketEncryption{DefaultKMSKeyName: "key"}, + } + got := attrs.toRawBucket() + want := &raw.Bucket{ + Name: "name", + Acl: []*raw.BucketAccessControl{ + {Entity: "bob@example.com", Role: "OWNER"}, + }, + DefaultObjectAcl: []*raw.ObjectAccessControl{ + {Entity: "allUsers", Role: "READER"}, + }, + Location: "loc", + StorageClass: "class", + RetentionPolicy: &raw.BucketRetentionPolicy{ + RetentionPeriod: 3, + }, + Versioning: nil, // ignore VersioningEnabled if false + Labels: map[string]string{"label": "value"}, + Cors: []*raw.BucketCors{ + { + MaxAgeSeconds: 3600, + Method: []string{"GET", "POST"}, + Origin: []string{"*"}, + ResponseHeader: []string{"FOO"}, + }, + }, + Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key"}, + } + if msg := testutil.Diff(got, want); msg != "" { + t.Error(msg) + } + + attrs.VersioningEnabled = true + attrs.RequesterPays = true + got = attrs.toRawBucket() + want.Versioning = &raw.BucketVersioning{Enabled: true} + want.Billing = &raw.BucketBilling{RequesterPays: true} + if msg := testutil.Diff(got, want); msg != "" { + t.Error(msg) + } +} + +func TestBucketAttrsToUpdateToRawBucket(t *testing.T) { + t.Parallel() + au := &BucketAttrsToUpdate{ + VersioningEnabled: false, + RequesterPays: false, + RetentionPolicy: &RetentionPolicy{RetentionPeriod: time.Hour}, + Encryption: &BucketEncryption{DefaultKMSKeyName: "key2"}, + Lifecycle: &Lifecycle{ + Rules: []LifecycleRule{ + { + Action: LifecycleAction{Type: "Delete"}, + Condition: LifecycleCondition{AgeInDays: 30}, + }, + }, + }, + } + au.SetLabel("a", "foo") + au.DeleteLabel("b") + au.SetLabel("c", "") + got := au.toRawBucket() + want := &raw.Bucket{ + Versioning: &raw.BucketVersioning{ + Enabled: false, + ForceSendFields: []string{"Enabled"}, + }, + Labels: map[string]string{ + "a": "foo", + "c": "", + }, + Billing: &raw.BucketBilling{ + RequesterPays: false, + ForceSendFields: []string{"RequesterPays"}, + }, + RetentionPolicy: &raw.BucketRetentionPolicy{RetentionPeriod: 3600}, + Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key2"}, + NullFields: []string{"Labels.b"}, + Lifecycle: &raw.BucketLifecycle{ + Rule: []*raw.BucketLifecycleRule{ + { + Action: &raw.BucketLifecycleRuleAction{Type: "Delete"}, + Condition: &raw.BucketLifecycleRuleCondition{Age: 30}, + }, + }, + }, + } + if msg := testutil.Diff(got, want); msg != "" { + t.Error(msg) + } + + var au2 BucketAttrsToUpdate + au2.DeleteLabel("b") + got = au2.toRawBucket() + want = &raw.Bucket{ + Labels: map[string]string{}, + ForceSendFields: []string{"Labels"}, + NullFields: []string{"Labels.b"}, + } + if msg := testutil.Diff(got, want); msg != "" { + t.Error(msg) + } + + // Test nulls. + au3 := &BucketAttrsToUpdate{ + RetentionPolicy: &RetentionPolicy{}, + Encryption: &BucketEncryption{}, + } + got = au3.toRawBucket() + want = &raw.Bucket{ + NullFields: []string{"RetentionPolicy", "Encryption"}, + } + if msg := testutil.Diff(got, want); msg != "" { + t.Error(msg) + } +} + +func TestCallBuilders(t *testing.T) { + rc, err := raw.New(&http.Client{}) + if err != nil { + t.Fatal(err) + } + c := &Client{raw: rc} + const metagen = 17 + + b := c.Bucket("name") + bm := b.If(BucketConditions{MetagenerationMatch: metagen}).UserProject("p") + + equal := func(x, y interface{}) bool { + return testutil.Equal(x, y, + cmp.AllowUnexported( + raw.BucketsGetCall{}, + raw.BucketsDeleteCall{}, + raw.BucketsPatchCall{}, + ), + cmp.FilterPath(func(p cmp.Path) bool { + return p[len(p)-1].Type() == reflect.TypeOf(&raw.Service{}) + }, cmp.Ignore()), + ) + } + + for i, test := range []struct { + callFunc func(*BucketHandle) (interface{}, error) + want interface { + Header() http.Header + } + metagenFunc func(interface{}) + }{ + { + func(b *BucketHandle) (interface{}, error) { return b.newGetCall() }, + rc.Buckets.Get("name").Projection("full"), + func(req interface{}) { req.(*raw.BucketsGetCall).IfMetagenerationMatch(metagen).UserProject("p") }, + }, + { + func(b *BucketHandle) (interface{}, error) { return b.newDeleteCall() }, + rc.Buckets.Delete("name"), + func(req interface{}) { req.(*raw.BucketsDeleteCall).IfMetagenerationMatch(metagen).UserProject("p") }, + }, + { + func(b *BucketHandle) (interface{}, error) { + return b.newPatchCall(&BucketAttrsToUpdate{ + VersioningEnabled: false, + RequesterPays: false, + }) + }, + rc.Buckets.Patch("name", &raw.Bucket{ + Versioning: &raw.BucketVersioning{ + Enabled: false, + ForceSendFields: []string{"Enabled"}, + }, + Billing: &raw.BucketBilling{ + RequesterPays: false, + ForceSendFields: []string{"RequesterPays"}, + }, + }).Projection("full"), + func(req interface{}) { req.(*raw.BucketsPatchCall).IfMetagenerationMatch(metagen).UserProject("p") }, + }, + } { + got, err := test.callFunc(b) + if err != nil { + t.Fatal(err) + } + setClientHeader(test.want.Header()) + if !equal(got, test.want) { + t.Errorf("#%d: got %#v, want %#v", i, got, test.want) + } + got, err = test.callFunc(bm) + if err != nil { + t.Fatal(err) + } + test.metagenFunc(test.want) + if !equal(got, test.want) { + t.Errorf("#%d:\ngot %#v\nwant %#v", i, got, test.want) + } + } + + // Error. + bm = b.If(BucketConditions{MetagenerationMatch: 1, MetagenerationNotMatch: 2}) + if _, err := bm.newGetCall(); err == nil { + t.Errorf("got nil, want error") + } + if _, err := bm.newDeleteCall(); err == nil { + t.Errorf("got nil, want error") + } + if _, err := bm.newPatchCall(&BucketAttrsToUpdate{}); err == nil { + t.Errorf("got nil, want error") + } +} + +func TestNewBucket(t *testing.T) { + labels := map[string]string{"a": "b"} + matchClasses := []string{"MULTI_REGIONAL", "REGIONAL", "STANDARD"} + rb := &raw.Bucket{ + Name: "name", + Location: "loc", + Metageneration: 3, + StorageClass: "sc", + TimeCreated: "2017-10-23T04:05:06Z", + Versioning: &raw.BucketVersioning{Enabled: true}, + Labels: labels, + Billing: &raw.BucketBilling{RequesterPays: true}, + Lifecycle: &raw.BucketLifecycle{ + Rule: []*raw.BucketLifecycleRule{{ + Action: &raw.BucketLifecycleRuleAction{ + Type: "SetStorageClass", + StorageClass: "NEARLINE", + }, + Condition: &raw.BucketLifecycleRuleCondition{ + Age: 10, + IsLive: googleapi.Bool(true), + CreatedBefore: "2017-01-02", + MatchesStorageClass: matchClasses, + NumNewerVersions: 3, + }, + }}, + }, + RetentionPolicy: &raw.BucketRetentionPolicy{ + RetentionPeriod: 3, + EffectiveTime: time.Now().Format(time.RFC3339), + }, + Cors: []*raw.BucketCors{ + { + MaxAgeSeconds: 3600, + Method: []string{"GET", "POST"}, + Origin: []string{"*"}, + ResponseHeader: []string{"FOO"}, + }, + }, + Acl: []*raw.BucketAccessControl{ + {Bucket: "name", Role: "READER", Email: "joe@example.com", Entity: "allUsers"}, + }, + Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key"}, + } + want := &BucketAttrs{ + Name: "name", + Location: "loc", + MetaGeneration: 3, + StorageClass: "sc", + Created: time.Date(2017, 10, 23, 4, 5, 6, 0, time.UTC), + VersioningEnabled: true, + Labels: labels, + RequesterPays: true, + Lifecycle: Lifecycle{ + Rules: []LifecycleRule{ + { + Action: LifecycleAction{ + Type: SetStorageClassAction, + StorageClass: "NEARLINE", + }, + Condition: LifecycleCondition{ + AgeInDays: 10, + Liveness: Live, + CreatedBefore: time.Date(2017, 1, 2, 0, 0, 0, 0, time.UTC), + MatchesStorageClasses: matchClasses, + NumNewerVersions: 3, + }, + }, + }, + }, + RetentionPolicy: &RetentionPolicy{ + RetentionPeriod: 3 * time.Second, + }, + CORS: []CORS{ + { + MaxAge: time.Hour, + Methods: []string{"GET", "POST"}, + Origins: []string{"*"}, + ResponseHeaders: []string{"FOO"}, + }, + }, + Encryption: &BucketEncryption{DefaultKMSKeyName: "key"}, + ACL: []ACLRule{{Entity: "allUsers", Role: RoleReader}}, + DefaultObjectACL: []ACLRule{}, + } + got, err := newBucket(rb) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(got, want, cmpopts.IgnoreTypes(time.Time{})); diff != "" { + t.Errorf("got=-, want=+:\n%s", diff) + } +} diff --git a/vendor/cloud.google.com/go/storage/copy.go b/vendor/cloud.google.com/go/storage/copy.go new file mode 100644 index 0000000000..98ca5bd7c8 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/copy.go @@ -0,0 +1,222 @@ +// 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 storage + +import ( + "errors" + "fmt" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + raw "google.golang.org/api/storage/v1" +) + +// CopierFrom creates a Copier that can copy src to dst. +// You can immediately call Run on the returned Copier, or +// you can configure it first. +// +// For Requester Pays buckets, the user project of dst is billed, unless it is empty, +// in which case the user project of src is billed. +func (dst *ObjectHandle) CopierFrom(src *ObjectHandle) *Copier { + return &Copier{dst: dst, src: src} +} + +// A Copier copies a source object to a destination. +type Copier struct { + // ObjectAttrs are optional attributes to set on the destination object. + // Any attributes must be initialized before any calls on the Copier. Nil + // or zero-valued attributes are ignored. + ObjectAttrs + + // RewriteToken can be set before calling Run to resume a copy + // operation. After Run returns a non-nil error, RewriteToken will + // have been updated to contain the value needed to resume the copy. + RewriteToken string + + // ProgressFunc can be used to monitor the progress of a multi-RPC copy + // operation. If ProgressFunc is not nil and copying requires multiple + // calls to the underlying service (see + // https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite), then + // ProgressFunc will be invoked after each call with the number of bytes of + // content copied so far and the total size in bytes of the source object. + // + // ProgressFunc is intended to make upload progress available to the + // application. For example, the implementation of ProgressFunc may update + // a progress bar in the application's UI, or log the result of + // float64(copiedBytes)/float64(totalBytes). + // + // ProgressFunc should return quickly without blocking. + ProgressFunc func(copiedBytes, totalBytes uint64) + + // The Cloud KMS key, in the form projects/P/locations/L/keyRings/R/cryptoKeys/K, + // that will be used to encrypt the object. Overrides the object's KMSKeyName, if + // any. + // + // Providing both a DestinationKMSKeyName and a customer-supplied encryption key + // (via ObjectHandle.Key) on the destination object will result in an error when + // Run is called. + DestinationKMSKeyName string + + dst, src *ObjectHandle +} + +// Run performs the copy. +func (c *Copier) Run(ctx context.Context) (attrs *ObjectAttrs, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Copier.Run") + defer func() { trace.EndSpan(ctx, err) }() + + if err := c.src.validate(); err != nil { + return nil, err + } + if err := c.dst.validate(); err != nil { + return nil, err + } + if c.DestinationKMSKeyName != "" && c.dst.encryptionKey != nil { + return nil, errors.New("storage: cannot use DestinationKMSKeyName with a customer-supplied encryption key") + } + // Convert destination attributes to raw form, omitting the bucket. + // If the bucket is included but name or content-type aren't, the service + // returns a 400 with "Required" as the only message. Omitting the bucket + // does not cause any problems. + rawObject := c.ObjectAttrs.toRawObject("") + for { + res, err := c.callRewrite(ctx, rawObject) + if err != nil { + return nil, err + } + if c.ProgressFunc != nil { + c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize)) + } + if res.Done { // Finished successfully. + return newObject(res.Resource), nil + } + } +} + +func (c *Copier) callRewrite(ctx context.Context, rawObj *raw.Object) (*raw.RewriteResponse, error) { + call := c.dst.c.raw.Objects.Rewrite(c.src.bucket, c.src.object, c.dst.bucket, c.dst.object, rawObj) + + call.Context(ctx).Projection("full") + if c.RewriteToken != "" { + call.RewriteToken(c.RewriteToken) + } + if c.DestinationKMSKeyName != "" { + call.DestinationKmsKeyName(c.DestinationKMSKeyName) + } + if err := applyConds("Copy destination", c.dst.gen, c.dst.conds, call); err != nil { + return nil, err + } + if c.dst.userProject != "" { + call.UserProject(c.dst.userProject) + } else if c.src.userProject != "" { + call.UserProject(c.src.userProject) + } + if err := applySourceConds(c.src.gen, c.src.conds, call); err != nil { + return nil, err + } + if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil { + return nil, err + } + if err := setEncryptionHeaders(call.Header(), c.src.encryptionKey, true); err != nil { + return nil, err + } + var res *raw.RewriteResponse + var err error + setClientHeader(call.Header()) + err = runWithRetry(ctx, func() error { res, err = call.Do(); return err }) + if err != nil { + return nil, err + } + c.RewriteToken = res.RewriteToken + return res, nil +} + +// ComposerFrom creates a Composer that can compose srcs into dst. +// You can immediately call Run on the returned Composer, or you can +// configure it first. +// +// The encryption key for the destination object will be used to decrypt all +// source objects and encrypt the destination object. It is an error +// to specify an encryption key for any of the source objects. +func (dst *ObjectHandle) ComposerFrom(srcs ...*ObjectHandle) *Composer { + return &Composer{dst: dst, srcs: srcs} +} + +// A Composer composes source objects into a destination object. +// +// For Requester Pays buckets, the user project of dst is billed. +type Composer struct { + // ObjectAttrs are optional attributes to set on the destination object. + // Any attributes must be initialized before any calls on the Composer. Nil + // or zero-valued attributes are ignored. + ObjectAttrs + + dst *ObjectHandle + srcs []*ObjectHandle +} + +// Run performs the compose operation. +func (c *Composer) Run(ctx context.Context) (attrs *ObjectAttrs, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Composer.Run") + defer func() { trace.EndSpan(ctx, err) }() + + if err := c.dst.validate(); err != nil { + return nil, err + } + if len(c.srcs) == 0 { + return nil, errors.New("storage: at least one source object must be specified") + } + + req := &raw.ComposeRequest{} + // Compose requires a non-empty Destination, so we always set it, + // even if the caller-provided ObjectAttrs is the zero value. + req.Destination = c.ObjectAttrs.toRawObject(c.dst.bucket) + for _, src := range c.srcs { + if err := src.validate(); err != nil { + return nil, err + } + if src.bucket != c.dst.bucket { + return nil, fmt.Errorf("storage: all source objects must be in bucket %q, found %q", c.dst.bucket, src.bucket) + } + if src.encryptionKey != nil { + return nil, fmt.Errorf("storage: compose source %s.%s must not have encryption key", src.bucket, src.object) + } + srcObj := &raw.ComposeRequestSourceObjects{ + Name: src.object, + } + if err := applyConds("ComposeFrom source", src.gen, src.conds, composeSourceObj{srcObj}); err != nil { + return nil, err + } + req.SourceObjects = append(req.SourceObjects, srcObj) + } + + call := c.dst.c.raw.Objects.Compose(c.dst.bucket, c.dst.object, req).Context(ctx) + if err := applyConds("ComposeFrom destination", c.dst.gen, c.dst.conds, call); err != nil { + return nil, err + } + if c.dst.userProject != "" { + call.UserProject(c.dst.userProject) + } + if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil { + return nil, err + } + var obj *raw.Object + setClientHeader(call.Header()) + err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err }) + if err != nil { + return nil, err + } + return newObject(obj), nil +} diff --git a/vendor/cloud.google.com/go/storage/copy_test.go b/vendor/cloud.google.com/go/storage/copy_test.go new file mode 100644 index 0000000000..0b4fa05bde --- /dev/null +++ b/vendor/cloud.google.com/go/storage/copy_test.go @@ -0,0 +1,80 @@ +// 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 storage + +import ( + "net/http" + "strings" + "testing" + + "golang.org/x/net/context" + "google.golang.org/api/option" +) + +func TestCopyMissingFields(t *testing.T) { + // Verify that copying checks for missing fields.a + t.Parallel() + var tests = []struct { + srcBucket, srcName, destBucket, destName string + errMsg string + }{ + { + "mybucket", "", "mybucket", "destname", + "name is empty", + }, + { + "mybucket", "srcname", "mybucket", "", + "name is empty", + }, + { + "", "srcfile", "mybucket", "destname", + "name is empty", + }, + { + "mybucket", "srcfile", "", "destname", + "name is empty", + }, + } + ctx := context.Background() + client, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: &fakeTransport{}})) + if err != nil { + t.Fatal(err) + } + for i, test := range tests { + src := client.Bucket(test.srcBucket).Object(test.srcName) + dst := client.Bucket(test.destBucket).Object(test.destName) + _, err := dst.CopierFrom(src).Run(ctx) + if !strings.Contains(err.Error(), test.errMsg) { + t.Errorf("CopyTo test #%v:\ngot err %q\nwant err %q", i, err, test.errMsg) + } + } +} + +func TestCopyBothEncryptionKeys(t *testing.T) { + // Test that using both a customer-supplied key and a KMS key is an error. + ctx := context.Background() + client, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: &fakeTransport{}})) + if err != nil { + t.Fatal(err) + } + dest := client.Bucket("b").Object("d").Key(testEncryptionKey) + c := dest.CopierFrom(client.Bucket("b").Object("s")) + c.DestinationKMSKeyName = "key" + if _, err := c.Run(ctx); err == nil { + t.Error("got nil, want error") + } else if !strings.Contains(err.Error(), "KMS") { + t.Errorf(`got %q, want it to contain "KMS"`, err) + } +} diff --git a/vendor/cloud.google.com/go/storage/doc.go b/vendor/cloud.google.com/go/storage/doc.go new file mode 100644 index 0000000000..5bd7708e53 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/doc.go @@ -0,0 +1,165 @@ +// 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 storage provides an easy way to work with Google Cloud Storage. +Google Cloud Storage stores data in named objects, which are grouped into buckets. + +More information about Google Cloud Storage is available at +https://cloud.google.com/storage/docs. + +See https://godoc.org/cloud.google.com/go for authentication, timeouts, +connection pooling and similar aspects of this package. + +All of the methods of this package use exponential backoff to retry calls +that fail with certain errors, as described in +https://cloud.google.com/storage/docs/exponential-backoff. + + +Creating a Client + +To start working with this package, create a client: + + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + +The client will use your default application credentials. + +If you only wish to access public data, you can create +an unauthenticated client with + + client, err := storage.NewClient(ctx, option.WithoutAuthentication()) + +Buckets + +A Google Cloud Storage bucket is a collection of objects. To work with a +bucket, make a bucket handle: + + bkt := client.Bucket(bucketName) + +A handle is a reference to a bucket. You can have a handle even if the +bucket doesn't exist yet. To create a bucket in Google Cloud Storage, +call Create on the handle: + + if err := bkt.Create(ctx, projectID, nil); err != nil { + // TODO: Handle error. + } + +Note that although buckets are associated with projects, bucket names are +global across all projects. + +Each bucket has associated metadata, represented in this package by +BucketAttrs. The third argument to BucketHandle.Create allows you to set +the initial BucketAttrs of a bucket. To retrieve a bucket's attributes, use +Attrs: + + attrs, err := bkt.Attrs(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Printf("bucket %s, created at %s, is located in %s with storage class %s\n", + attrs.Name, attrs.Created, attrs.Location, attrs.StorageClass) + +Objects + +An object holds arbitrary data as a sequence of bytes, like a file. You +refer to objects using a handle, just as with buckets, but unlike buckets +you don't explicitly create an object. Instead, the first time you write +to an object it will be created. You can use the standard Go io.Reader +and io.Writer interfaces to read and write object data: + + obj := bkt.Object("data") + // Write something to obj. + // w implements io.Writer. + w := obj.NewWriter(ctx) + // Write some text to obj. This will either create the object or overwrite whatever is there already. + if _, err := fmt.Fprintf(w, "This object contains text.\n"); err != nil { + // TODO: Handle error. + } + // Close, just like writing a file. + if err := w.Close(); err != nil { + // TODO: Handle error. + } + + // Read it back. + r, err := obj.NewReader(ctx) + if err != nil { + // TODO: Handle error. + } + defer r.Close() + if _, err := io.Copy(os.Stdout, r); err != nil { + // TODO: Handle error. + } + // Prints "This object contains text." + +Objects also have attributes, which you can fetch with Attrs: + + objAttrs, err := obj.Attrs(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Printf("object %s has size %d and can be read using %s\n", + objAttrs.Name, objAttrs.Size, objAttrs.MediaLink) + +ACLs + +Both objects and buckets have ACLs (Access Control Lists). An ACL is a list of +ACLRules, each of which specifies the role of a user, group or project. ACLs +are suitable for fine-grained control, but you may prefer using IAM to control +access at the project level (see +https://cloud.google.com/storage/docs/access-control/iam). + +To list the ACLs of a bucket or object, obtain an ACLHandle and call its List method: + + acls, err := obj.ACL().List(ctx) + if err != nil { + // TODO: Handle error. + } + for _, rule := range acls { + fmt.Printf("%s has role %s\n", rule.Entity, rule.Role) + } + +You can also set and delete ACLs. + +Conditions + +Every object has a generation and a metageneration. The generation changes +whenever the content changes, and the metageneration changes whenever the +metadata changes. Conditions let you check these values before an operation; +the operation only executes if the conditions match. You can use conditions to +prevent race conditions in read-modify-write operations. + +For example, say you've read an object's metadata into objAttrs. Now +you want to write to that object, but only if its contents haven't changed +since you read it. Here is how to express that: + + w = obj.If(storage.Conditions{GenerationMatch: objAttrs.Generation}).NewWriter(ctx) + // Proceed with writing as above. + +Signed URLs + +You can obtain a URL that lets anyone read or write an object for a limited time. +You don't need to create a client to do this. See the documentation of +SignedURL for details. + + url, err := storage.SignedURL(bucketName, "shared-object", opts) + if err != nil { + // TODO: Handle error. + } + fmt.Println(url) +*/ +package storage // import "cloud.google.com/go/storage" diff --git a/vendor/cloud.google.com/go/storage/example_test.go b/vendor/cloud.google.com/go/storage/example_test.go new file mode 100644 index 0000000000..255ac9c189 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/example_test.go @@ -0,0 +1,641 @@ +// 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 storage_test + +import ( + "fmt" + "hash/crc32" + "io" + "io/ioutil" + "log" + "os" + "time" + + "cloud.google.com/go/storage" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +func ExampleNewClient() { + ctx := context.Background() + // Use Google Application Default Credentials to authorize and authenticate the client. + // More information about Application Default Credentials and how to enable is at + // https://developers.google.com/identity/protocols/application-default-credentials. + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // Use the client. + + // Close the client when finished. + if err := client.Close(); err != nil { + // TODO: handle error. + } +} + +// This example shows how to create an unauthenticated client, which +// can be used to access public data. +func ExampleNewClient_unauthenticated() { + ctx := context.Background() + client, err := storage.NewClient(ctx, option.WithoutAuthentication()) + if err != nil { + // TODO: handle error. + } + // Use the client. + + // Close the client when finished. + if err := client.Close(); err != nil { + // TODO: handle error. + } +} + +func ExampleBucketHandle_Create() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + if err := client.Bucket("my-bucket").Create(ctx, "my-project", nil); err != nil { + // TODO: handle error. + } +} + +func ExampleBucketHandle_Delete() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + if err := client.Bucket("my-bucket").Delete(ctx); err != nil { + // TODO: handle error. + } +} + +func ExampleBucketHandle_Attrs() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + attrs, err := client.Bucket("my-bucket").Attrs(ctx) + if err != nil { + // TODO: handle error. + } + fmt.Println(attrs) +} + +func ExampleBucketHandle_Update() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // Enable versioning in the bucket, regardless of its previous value. + attrs, err := client.Bucket("my-bucket").Update(ctx, + storage.BucketAttrsToUpdate{VersioningEnabled: true}) + if err != nil { + // TODO: handle error. + } + fmt.Println(attrs) +} + +// If your update is based on the bucket's previous attributes, match the +// metageneration number to make sure the bucket hasn't changed since you read it. +func ExampleBucketHandle_Update_readModifyWrite() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + b := client.Bucket("my-bucket") + attrs, err := b.Attrs(ctx) + if err != nil { + // TODO: handle error. + } + var au storage.BucketAttrsToUpdate + au.SetLabel("lab", attrs.Labels["lab"]+"-more") + if attrs.Labels["delete-me"] == "yes" { + au.DeleteLabel("delete-me") + } + attrs, err = b. + If(storage.BucketConditions{MetagenerationMatch: attrs.MetaGeneration}). + Update(ctx, au) + if err != nil { + // TODO: handle error. + } + fmt.Println(attrs) +} + +func ExampleClient_Buckets() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + it := client.Bucket("my-bucket") + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleBucketIterator_Next() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + it := client.Buckets(ctx, "my-project") + for { + bucketAttrs, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(bucketAttrs) + } +} + +func ExampleBucketHandle_Objects() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + it := client.Bucket("my-bucket").Objects(ctx, nil) + _ = it // TODO: iterate using Next or iterator.Pager. +} + +func ExampleBucketHandle_AddNotification() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + b := client.Bucket("my-bucket") + n, err := b.AddNotification(ctx, &storage.Notification{ + TopicProjectID: "my-project", + TopicID: "my-topic", + PayloadFormat: storage.JSONPayload, + }) + if err != nil { + // TODO: handle error. + } + fmt.Println(n.ID) +} + +func ExampleBucketHandle_LockRetentionPolicy() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + b := client.Bucket("my-bucket") + attrs, err := b.Attrs(ctx) + if err != nil { + // TODO: handle error. + } + // Note that locking the bucket without first attaching a RetentionPolicy + // that's at least 1 day is a no-op + err = b.If(storage.BucketConditions{MetagenerationMatch: attrs.MetaGeneration}).LockRetentionPolicy(ctx) + if err != nil { + // TODO: handle err + } +} + +func ExampleBucketHandle_Notifications() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + b := client.Bucket("my-bucket") + ns, err := b.Notifications(ctx) + if err != nil { + // TODO: handle error. + } + for id, n := range ns { + fmt.Printf("%s: %+v\n", id, n) + } +} + +var notificationID string + +func ExampleBucketHandle_DeleteNotification() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + b := client.Bucket("my-bucket") + // TODO: Obtain notificationID from BucketHandle.AddNotification + // or BucketHandle.Notifications. + err = b.DeleteNotification(ctx, notificationID) + if err != nil { + // TODO: handle error. + } +} + +func ExampleObjectIterator_Next() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + it := client.Bucket("my-bucket").Objects(ctx, nil) + for { + objAttrs, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(objAttrs) + } +} + +func ExampleSignedURL() { + pkey, err := ioutil.ReadFile("my-private-key.pem") + if err != nil { + // TODO: handle error. + } + url, err := storage.SignedURL("my-bucket", "my-object", &storage.SignedURLOptions{ + GoogleAccessID: "xxx@developer.gserviceaccount.com", + PrivateKey: pkey, + Method: "GET", + Expires: time.Now().Add(48 * time.Hour), + }) + if err != nil { + // TODO: handle error. + } + fmt.Println(url) +} + +func ExampleObjectHandle_Attrs() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + objAttrs, err := client.Bucket("my-bucket").Object("my-object").Attrs(ctx) + if err != nil { + // TODO: handle error. + } + fmt.Println(objAttrs) +} + +func ExampleObjectHandle_Attrs_withConditions() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + obj := client.Bucket("my-bucket").Object("my-object") + // Read the object. + objAttrs1, err := obj.Attrs(ctx) + if err != nil { + // TODO: handle error. + } + // Do something else for a while. + time.Sleep(5 * time.Minute) + // Now read the same contents, even if the object has been written since the last read. + objAttrs2, err := obj.Generation(objAttrs1.Generation).Attrs(ctx) + if err != nil { + // TODO: handle error. + } + fmt.Println(objAttrs1, objAttrs2) +} + +func ExampleObjectHandle_Update() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // Change only the content type of the object. + objAttrs, err := client.Bucket("my-bucket").Object("my-object").Update(ctx, storage.ObjectAttrsToUpdate{ + ContentType: "text/html", + ContentDisposition: "", // delete ContentDisposition + }) + if err != nil { + // TODO: handle error. + } + fmt.Println(objAttrs) +} + +func ExampleObjectHandle_NewReader() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + rc, err := client.Bucket("my-bucket").Object("my-object").NewReader(ctx) + if err != nil { + // TODO: handle error. + } + slurp, err := ioutil.ReadAll(rc) + rc.Close() + if err != nil { + // TODO: handle error. + } + fmt.Println("file contents:", slurp) +} + +func ExampleObjectHandle_NewRangeReader() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // Read only the first 64K. + rc, err := client.Bucket("bucketname").Object("filename1").NewRangeReader(ctx, 0, 64*1024) + if err != nil { + // TODO: handle error. + } + slurp, err := ioutil.ReadAll(rc) + rc.Close() + if err != nil { + // TODO: handle error. + } + fmt.Println("first 64K of file contents:", slurp) +} + +func ExampleObjectHandle_NewWriter() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx) + _ = wc // TODO: Use the Writer. +} + +func ExampleWriter_Write() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx) + wc.ContentType = "text/plain" + wc.ACL = []storage.ACLRule{{storage.AllUsers, storage.RoleReader}} + if _, err := wc.Write([]byte("hello world")); err != nil { + // TODO: handle error. + // Note that Write may return nil in some error situations, + // so always check the error from Close. + } + if err := wc.Close(); err != nil { + // TODO: handle error. + } + fmt.Println("updated object:", wc.Attrs()) +} + +// To make sure the data you write is uncorrupted, use an MD5 or CRC32c +// checksum. This example illustrates CRC32c. +func ExampleWriter_Write_checksum() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + data := []byte("verify me") + wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx) + wc.CRC32C = crc32.Checksum(data, crc32.MakeTable(crc32.Castagnoli)) + wc.SendCRC32C = true + if _, err := wc.Write([]byte("hello world")); err != nil { + // TODO: handle error. + // Note that Write may return nil in some error situations, + // so always check the error from Close. + } + if err := wc.Close(); err != nil { + // TODO: handle error. + } + fmt.Println("updated object:", wc.Attrs()) +} + +func ExampleObjectHandle_Delete() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // To delete multiple objects in a bucket, list them with an + // ObjectIterator, then Delete them. + + // If you are using this package on the App Engine Flex runtime, + // you can init a bucket client with your app's default bucket name. + // See http://godoc.org/google.golang.org/appengine/file#DefaultBucketName. + bucket := client.Bucket("my-bucket") + it := bucket.Objects(ctx, nil) + for { + objAttrs, err := it.Next() + if err != nil && err != iterator.Done { + // TODO: Handle error. + } + if err == iterator.Done { + break + } + if err := bucket.Object(objAttrs.Name).Delete(ctx); err != nil { + // TODO: Handle error. + } + } + fmt.Println("deleted all object items in the bucket specified.") +} + +func ExampleACLHandle_Delete() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // No longer grant access to the bucket to everyone on the Internet. + if err := client.Bucket("my-bucket").ACL().Delete(ctx, storage.AllUsers); err != nil { + // TODO: handle error. + } +} + +func ExampleACLHandle_Set() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // Let any authenticated user read my-bucket/my-object. + obj := client.Bucket("my-bucket").Object("my-object") + if err := obj.ACL().Set(ctx, storage.AllAuthenticatedUsers, storage.RoleReader); err != nil { + // TODO: handle error. + } +} + +func ExampleACLHandle_List() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // List the default object ACLs for my-bucket. + aclRules, err := client.Bucket("my-bucket").DefaultObjectACL().List(ctx) + if err != nil { + // TODO: handle error. + } + fmt.Println(aclRules) +} + +func ExampleCopier_Run() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + src := client.Bucket("bucketname").Object("file1") + dst := client.Bucket("another-bucketname").Object("file2") + + // Copy content and modify metadata. + copier := dst.CopierFrom(src) + copier.ContentType = "text/plain" + attrs, err := copier.Run(ctx) + if err != nil { + // TODO: Handle error, possibly resuming with copier.RewriteToken. + } + fmt.Println(attrs) + + // Just copy content. + attrs, err = dst.CopierFrom(src).Run(ctx) + if err != nil { + // TODO: Handle error. No way to resume. + } + fmt.Println(attrs) +} + +func ExampleCopier_Run_progress() { + // Display progress across multiple rewrite RPCs. + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + src := client.Bucket("bucketname").Object("file1") + dst := client.Bucket("another-bucketname").Object("file2") + + copier := dst.CopierFrom(src) + copier.ProgressFunc = func(copiedBytes, totalBytes uint64) { + log.Printf("copy %.1f%% done", float64(copiedBytes)/float64(totalBytes)*100) + } + if _, err := copier.Run(ctx); err != nil { + // TODO: handle error. + } +} + +var key1, key2 []byte + +func ExampleObjectHandle_CopierFrom_rotateEncryptionKeys() { + // To rotate the encryption key on an object, copy it onto itself. + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + obj := client.Bucket("bucketname").Object("obj") + // Assume obj is encrypted with key1, and we want to change to key2. + _, err = obj.Key(key2).CopierFrom(obj.Key(key1)).Run(ctx) + if err != nil { + // TODO: handle error. + } +} + +func ExampleComposer_Run() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + bkt := client.Bucket("bucketname") + src1 := bkt.Object("o1") + src2 := bkt.Object("o2") + dst := bkt.Object("o3") + // Compose and modify metadata. + c := dst.ComposerFrom(src1, src2) + c.ContentType = "text/plain" + attrs, err := c.Run(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Println(attrs) + // Just compose. + attrs, err = dst.ComposerFrom(src1, src2).Run(ctx) + if err != nil { + // TODO: Handle error. + } + fmt.Println(attrs) +} + +var gen int64 + +func ExampleObjectHandle_Generation() { + // Read an object's contents from generation gen, regardless of the + // current generation of the object. + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + obj := client.Bucket("my-bucket").Object("my-object") + rc, err := obj.Generation(gen).NewReader(ctx) + if err != nil { + // TODO: handle error. + } + defer rc.Close() + if _, err := io.Copy(os.Stdout, rc); err != nil { + // TODO: handle error. + } +} + +func ExampleObjectHandle_If() { + // Read from an object only if the current generation is gen. + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + obj := client.Bucket("my-bucket").Object("my-object") + rc, err := obj.If(storage.Conditions{GenerationMatch: gen}).NewReader(ctx) + if err != nil { + // TODO: handle error. + } + defer rc.Close() + if _, err := io.Copy(os.Stdout, rc); err != nil { + // TODO: handle error. + } +} + +var secretKey []byte + +func ExampleObjectHandle_Key() { + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + obj := client.Bucket("my-bucket").Object("my-object") + // Encrypt the object's contents. + w := obj.Key(secretKey).NewWriter(ctx) + if _, err := w.Write([]byte("top secret")); err != nil { + // TODO: handle error. + } + if err := w.Close(); err != nil { + // TODO: handle error. + } +} diff --git a/vendor/cloud.google.com/go/storage/go110.go b/vendor/cloud.google.com/go/storage/go110.go new file mode 100644 index 0000000000..75be3cbb5d --- /dev/null +++ b/vendor/cloud.google.com/go/storage/go110.go @@ -0,0 +1,30 @@ +// Copyright 2017 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 go1.10 + +package storage + +import "google.golang.org/api/googleapi" + +func shouldRetry(err error) bool { + switch e := err.(type) { + case *googleapi.Error: + // Retry on 429 and 5xx, according to + // https://cloud.google.com/storage/docs/exponential-backoff. + return e.Code == 429 || (e.Code >= 500 && e.Code < 600) + default: + return false + } +} diff --git a/vendor/cloud.google.com/go/storage/go17.go b/vendor/cloud.google.com/go/storage/go17.go new file mode 100644 index 0000000000..5950205dc8 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/go17.go @@ -0,0 +1,30 @@ +// Copyright 2017 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 go1.7 + +package storage + +import ( + "context" + "net/http" +) + +func withContext(r *http.Request, ctx context.Context) *http.Request { + return r.WithContext(ctx) +} + +func goHTTPUncompressed(res *http.Response) bool { + return res.Uncompressed +} diff --git a/vendor/cloud.google.com/go/storage/iam.go b/vendor/cloud.google.com/go/storage/iam.go new file mode 100644 index 0000000000..d2cef426fc --- /dev/null +++ b/vendor/cloud.google.com/go/storage/iam.go @@ -0,0 +1,129 @@ +// Copyright 2017 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 storage + +import ( + "cloud.google.com/go/iam" + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + raw "google.golang.org/api/storage/v1" + iampb "google.golang.org/genproto/googleapis/iam/v1" +) + +// IAM provides access to IAM access control for the bucket. +func (b *BucketHandle) IAM() *iam.Handle { + return iam.InternalNewHandleClient(&iamClient{ + raw: b.c.raw, + userProject: b.userProject, + }, b.name) +} + +// iamClient implements the iam.client interface. +type iamClient struct { + raw *raw.Service + userProject string +} + +func (c *iamClient) Get(ctx context.Context, resource string) (p *iampb.Policy, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.IAM.Get") + defer func() { trace.EndSpan(ctx, err) }() + + call := c.raw.Buckets.GetIamPolicy(resource) + setClientHeader(call.Header()) + if c.userProject != "" { + call.UserProject(c.userProject) + } + var rp *raw.Policy + err = runWithRetry(ctx, func() error { + rp, err = call.Context(ctx).Do() + return err + }) + if err != nil { + return nil, err + } + return iamFromStoragePolicy(rp), nil +} + +func (c *iamClient) Set(ctx context.Context, resource string, p *iampb.Policy) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.IAM.Set") + defer func() { trace.EndSpan(ctx, err) }() + + rp := iamToStoragePolicy(p) + call := c.raw.Buckets.SetIamPolicy(resource, rp) + setClientHeader(call.Header()) + if c.userProject != "" { + call.UserProject(c.userProject) + } + return runWithRetry(ctx, func() error { + _, err := call.Context(ctx).Do() + return err + }) +} + +func (c *iamClient) Test(ctx context.Context, resource string, perms []string) (permissions []string, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.IAM.Test") + defer func() { trace.EndSpan(ctx, err) }() + + call := c.raw.Buckets.TestIamPermissions(resource, perms) + setClientHeader(call.Header()) + if c.userProject != "" { + call.UserProject(c.userProject) + } + var res *raw.TestIamPermissionsResponse + err = runWithRetry(ctx, func() error { + res, err = call.Context(ctx).Do() + return err + }) + if err != nil { + return nil, err + } + return res.Permissions, nil +} + +func iamToStoragePolicy(ip *iampb.Policy) *raw.Policy { + return &raw.Policy{ + Bindings: iamToStorageBindings(ip.Bindings), + Etag: string(ip.Etag), + } +} + +func iamToStorageBindings(ibs []*iampb.Binding) []*raw.PolicyBindings { + var rbs []*raw.PolicyBindings + for _, ib := range ibs { + rbs = append(rbs, &raw.PolicyBindings{ + Role: ib.Role, + Members: ib.Members, + }) + } + return rbs +} + +func iamFromStoragePolicy(rp *raw.Policy) *iampb.Policy { + return &iampb.Policy{ + Bindings: iamFromStorageBindings(rp.Bindings), + Etag: []byte(rp.Etag), + } +} + +func iamFromStorageBindings(rbs []*raw.PolicyBindings) []*iampb.Binding { + var ibs []*iampb.Binding + for _, rb := range rbs { + ibs = append(ibs, &iampb.Binding{ + Role: rb.Role, + Members: rb.Members, + }) + } + return ibs +} diff --git a/vendor/cloud.google.com/go/storage/integration_test.go b/vendor/cloud.google.com/go/storage/integration_test.go new file mode 100644 index 0000000000..2210075695 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/integration_test.go @@ -0,0 +1,2411 @@ +// 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 storage + +import ( + "bytes" + "compress/gzip" + "crypto/md5" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "flag" + "fmt" + "hash/crc32" + "io" + "io/ioutil" + "log" + "math/rand" + "net/http" + "os" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp/cmpopts" + "golang.org/x/net/context" + + "cloud.google.com/go/httpreplay" + "cloud.google.com/go/iam" + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/internal/uid" + "google.golang.org/api/googleapi" + "google.golang.org/api/iterator" + itesting "google.golang.org/api/iterator/testing" + "google.golang.org/api/option" +) + +const ( + testPrefix = "go-integration-test" + replayFilename = "storage.replay" +) + +var ( + record = flag.Bool("record", false, "record RPCs") + + uidSpace *uid.Space + bucketName string + // Use our own random number generator to isolate the sequence of random numbers from + // other packages. This makes it possible to use HTTP replay and draw the same sequence + // of numbers as during recording. + rng *rand.Rand + newTestClient func(ctx context.Context, opts ...option.ClientOption) (*Client, error) + + replaying bool +) + +func TestMain(m *testing.M) { + cleanup := initIntegrationTest() + exit := m.Run() + if err := cleanup(); err != nil { + // Don't fail the test if cleanup fails. + log.Printf("Post-test cleanup failed: %v", err) + } + os.Exit(exit) +} + +// If integration tests will be run, create a unique bucket for them. +// Also, set newTestClient to handle record/replay. +// Return a cleanup function. +func initIntegrationTest() func() error { + flag.Parse() // needed for testing.Short() + switch { + case testing.Short() && *record: + log.Fatal("cannot combine -short and -record") + return nil + + case testing.Short() && httpreplay.Supported() && testutil.CanReplay(replayFilename) && testutil.ProjID() != "": + // go test -short with a replay file will replay the integration tests, if + // the appropriate environment variables have been set. + replaying = true + httpreplay.DebugHeaders() + replayer, err := httpreplay.NewReplayer(replayFilename) + if err != nil { + log.Fatal(err) + } + var t time.Time + if err := json.Unmarshal(replayer.Initial(), &t); err != nil { + log.Fatal(err) + } + initUIDsAndRand(t) + newTestClient = func(ctx context.Context, _ ...option.ClientOption) (*Client, error) { + hc, err := replayer.Client(ctx) // no creds needed + if err != nil { + return nil, err + } + return NewClient(ctx, option.WithHTTPClient(hc)) + } + log.Printf("replaying from %s", replayFilename) + return func() error { return replayer.Close() } + + case testing.Short(): + // go test -short without a replay file skips the integration tests. + if testutil.CanReplay(replayFilename) && testutil.ProjID() != "" { + log.Print("replay not supported for Go versions before 1.8") + } + newTestClient = nil + return func() error { return nil } + + default: // Run integration tests against a real backend. + now := time.Now().UTC() + initUIDsAndRand(now) + var cleanup func() error + if *record && httpreplay.Supported() { + // Remember the time for replay. + nowBytes, err := json.Marshal(now) + if err != nil { + log.Fatal(err) + } + recorder, err := httpreplay.NewRecorder(replayFilename, nowBytes) + if err != nil { + log.Fatalf("could not record: %v", err) + } + newTestClient = func(ctx context.Context, opts ...option.ClientOption) (*Client, error) { + hc, err := recorder.Client(ctx, opts...) + if err != nil { + return nil, err + } + return NewClient(ctx, option.WithHTTPClient(hc)) + } + cleanup = func() error { + err1 := cleanupBuckets() + err2 := recorder.Close() + if err1 != nil { + return err1 + } + return err2 + } + log.Printf("recording to %s", replayFilename) + } else { + if *record { + log.Print("record not supported for Go versions before 1.8") + } + newTestClient = NewClient + cleanup = cleanupBuckets + } + ctx := context.Background() + client := config(ctx) + if client == nil { + return func() error { return nil } + } + defer client.Close() + if err := client.Bucket(bucketName).Create(ctx, testutil.ProjID(), nil); err != nil { + log.Fatalf("creating bucket %q: %v", bucketName, err) + } + return cleanup + } +} + +func initUIDsAndRand(t time.Time) { + uidSpace = uid.NewSpace(testPrefix, &uid.Options{Time: t}) + bucketName = uidSpace.New() + // Use our own random source, to avoid other parts of the program taking + // random numbers from the global source and putting record and replay + // out of sync. + rng = testutil.NewRand(t) +} + +// testConfig returns the Client used to access GCS. testConfig skips +// the current test if credentials are not available or when being run +// in Short mode. +func testConfig(ctx context.Context, t *testing.T) *Client { + if testing.Short() && !replaying { + t.Skip("Integration tests skipped in short mode") + } + client := config(ctx) + if client == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + return client +} + +// config is like testConfig, but it doesn't need a *testing.T. +func config(ctx context.Context) *Client { + ts := testutil.TokenSource(ctx, ScopeFullControl) + if ts == nil { + return nil + } + client, err := newTestClient(ctx, option.WithTokenSource(ts)) + if err != nil { + log.Fatalf("NewClient: %v", err) + } + return client +} + +func TestIntegration_BucketMethods(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + projectID := testutil.ProjID() + newBucketName := uidSpace.New() + b := client.Bucket(newBucketName) + // Test Create and Delete. + h.mustCreate(b, projectID, nil) + attrs := h.mustBucketAttrs(b) + if got, want := attrs.MetaGeneration, int64(1); got != want { + t.Errorf("got metagen %d, want %d", got, want) + } + if got, want := attrs.StorageClass, "STANDARD"; got != want { + t.Errorf("got storage class %q, want %q", got, want) + } + if attrs.VersioningEnabled { + t.Error("got versioning enabled, wanted it disabled") + } + h.mustDeleteBucket(b) + + // Test Create and Delete with attributes. + labels := map[string]string{ + "l1": "v1", + "empty": "", + } + attrs = &BucketAttrs{ + StorageClass: "NEARLINE", + VersioningEnabled: true, + Labels: labels, + Lifecycle: Lifecycle{ + Rules: []LifecycleRule{{ + Action: LifecycleAction{ + Type: SetStorageClassAction, + StorageClass: "NEARLINE", + }, + Condition: LifecycleCondition{ + AgeInDays: 10, + Liveness: Archived, + CreatedBefore: time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC), + MatchesStorageClasses: []string{"MULTI_REGIONAL", "STANDARD"}, + NumNewerVersions: 3, + }, + }, { + Action: LifecycleAction{ + Type: DeleteAction, + }, + Condition: LifecycleCondition{ + AgeInDays: 30, + Liveness: Live, + CreatedBefore: time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC), + MatchesStorageClasses: []string{"NEARLINE"}, + NumNewerVersions: 10, + }, + }}, + }, + } + h.mustCreate(b, projectID, attrs) + attrs = h.mustBucketAttrs(b) + if got, want := attrs.MetaGeneration, int64(1); got != want { + t.Errorf("got metagen %d, want %d", got, want) + } + if got, want := attrs.StorageClass, "NEARLINE"; got != want { + t.Errorf("got storage class %q, want %q", got, want) + } + if !attrs.VersioningEnabled { + t.Error("got versioning disabled, wanted it enabled") + } + if got, want := attrs.Labels, labels; !testutil.Equal(got, want) { + t.Errorf("labels: got %v, want %v", got, want) + } + h.mustDeleteBucket(b) +} + +func TestIntegration_BucketUpdate(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + b := client.Bucket(bucketName) + attrs := h.mustBucketAttrs(b) + if attrs.VersioningEnabled { + t.Fatal("bucket should not have versioning by default") + } + if len(attrs.Labels) > 0 { + t.Fatal("bucket should not have labels initially") + } + + // Using empty BucketAttrsToUpdate should be a no-nop. + attrs = h.mustUpdateBucket(b, BucketAttrsToUpdate{}) + if attrs.VersioningEnabled { + t.Fatal("should not have versioning") + } + if len(attrs.Labels) > 0 { + t.Fatal("should not have labels") + } + + // Turn on versioning, add some labels. + ua := BucketAttrsToUpdate{VersioningEnabled: true} + ua.SetLabel("l1", "v1") + ua.SetLabel("empty", "") + attrs = h.mustUpdateBucket(b, ua) + if !attrs.VersioningEnabled { + t.Fatal("should have versioning now") + } + wantLabels := map[string]string{ + "l1": "v1", + "empty": "", + } + if !testutil.Equal(attrs.Labels, wantLabels) { + t.Fatalf("got %v, want %v", attrs.Labels, wantLabels) + } + + // Turn off versioning again; add and remove some more labels. + ua = BucketAttrsToUpdate{VersioningEnabled: false} + ua.SetLabel("l1", "v2") // update + ua.SetLabel("new", "new") // create + ua.DeleteLabel("empty") // delete + ua.DeleteLabel("absent") // delete non-existent + attrs = h.mustUpdateBucket(b, ua) + if attrs.VersioningEnabled { + t.Fatal("should have versioning off") + } + wantLabels = map[string]string{ + "l1": "v2", + "new": "new", + } + if !testutil.Equal(attrs.Labels, wantLabels) { + t.Fatalf("got %v, want %v", attrs.Labels, wantLabels) + } + + // Configure a lifecycle + wantLifecycle := Lifecycle{ + Rules: []LifecycleRule{ + { + Action: LifecycleAction{Type: "Delete"}, + Condition: LifecycleCondition{AgeInDays: 30}, + }, + }, + } + ua = BucketAttrsToUpdate{Lifecycle: &wantLifecycle} + attrs = h.mustUpdateBucket(b, ua) + if !testutil.Equal(attrs.Lifecycle, wantLifecycle) { + t.Fatalf("got %v, want %v", attrs.Lifecycle, wantLifecycle) + } +} + +func TestIntegration_ConditionalDelete(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + o := client.Bucket(bucketName).Object("conddel") + + wc := o.NewWriter(ctx) + wc.ContentType = "text/plain" + h.mustWrite(wc, []byte("foo")) + + gen := wc.Attrs().Generation + metaGen := wc.Attrs().Metageneration + + if err := o.Generation(gen - 1).Delete(ctx); err == nil { + t.Fatalf("Unexpected successful delete with Generation") + } + if err := o.If(Conditions{MetagenerationMatch: metaGen + 1}).Delete(ctx); err == nil { + t.Fatalf("Unexpected successful delete with IfMetaGenerationMatch") + } + if err := o.If(Conditions{MetagenerationNotMatch: metaGen}).Delete(ctx); err == nil { + t.Fatalf("Unexpected successful delete with IfMetaGenerationNotMatch") + } + if err := o.Generation(gen).Delete(ctx); err != nil { + t.Fatalf("final delete failed: %v", err) + } +} + +func TestIntegration_Objects(t *testing.T) { + // TODO(jba): Use subtests (Go 1.7). + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + bkt := client.Bucket(bucketName) + + const defaultType = "text/plain" + + // Populate object names and make a map for their contents. + objects := []string{ + "obj1", + "obj2", + "obj/with/slashes", + } + contents := make(map[string][]byte) + + // Test Writer. + for _, obj := range objects { + c := randomContents() + if err := writeObject(ctx, bkt.Object(obj), defaultType, c); err != nil { + t.Errorf("Write for %v failed with %v", obj, err) + } + contents[obj] = c + } + + testObjectIterator(t, bkt, objects) + + // Test Reader. + for _, obj := range objects { + rc, err := bkt.Object(obj).NewReader(ctx) + if err != nil { + t.Errorf("Can't create a reader for %v, errored with %v", obj, err) + continue + } + if !rc.checkCRC { + t.Errorf("%v: not checking CRC", obj) + } + slurp, err := ioutil.ReadAll(rc) + if err != nil { + t.Errorf("Can't ReadAll object %v, errored with %v", obj, err) + } + if got, want := slurp, contents[obj]; !bytes.Equal(got, want) { + t.Errorf("Contents (%q) = %q; want %q", obj, got, want) + } + if got, want := rc.Size(), len(contents[obj]); got != int64(want) { + t.Errorf("Size (%q) = %d; want %d", obj, got, want) + } + if got, want := rc.ContentType(), "text/plain"; got != want { + t.Errorf("ContentType (%q) = %q; want %q", obj, got, want) + } + if got, want := rc.CacheControl(), "public, max-age=60"; got != want { + t.Errorf("CacheControl (%q) = %q; want %q", obj, got, want) + } + rc.Close() + + // Check early close. + buf := make([]byte, 1) + rc, err = bkt.Object(obj).NewReader(ctx) + if err != nil { + t.Fatalf("%v: %v", obj, err) + } + _, err = rc.Read(buf) + if err != nil { + t.Fatalf("%v: %v", obj, err) + } + if got, want := buf, contents[obj][:1]; !bytes.Equal(got, want) { + t.Errorf("Contents[0] (%q) = %q; want %q", obj, got, want) + } + if err := rc.Close(); err != nil { + t.Errorf("%v Close: %v", obj, err) + } + } + + obj := objects[0] + objlen := int64(len(contents[obj])) + // Test Range Reader. + for i, r := range []struct { + offset, length, want int64 + }{ + {0, objlen, objlen}, + {0, objlen / 2, objlen / 2}, + {objlen / 2, objlen, objlen / 2}, + {0, 0, 0}, + {objlen / 2, 0, 0}, + {objlen / 2, -1, objlen / 2}, + {0, objlen * 2, objlen}, + } { + rc, err := bkt.Object(obj).NewRangeReader(ctx, r.offset, r.length) + if err != nil { + t.Errorf("%+v: Can't create a range reader for %v, errored with %v", i, obj, err) + continue + } + if rc.Size() != objlen { + t.Errorf("%+v: Reader has a content-size of %d, want %d", i, rc.Size(), objlen) + } + if rc.Remain() != r.want { + t.Errorf("%+v: Reader's available bytes reported as %d, want %d", i, rc.Remain(), r.want) + } + slurp, err := ioutil.ReadAll(rc) + if err != nil { + t.Errorf("%+v: can't ReadAll object %v, errored with %v", r, obj, err) + continue + } + if len(slurp) != int(r.want) { + t.Errorf("%+v: RangeReader (%d, %d): Read %d bytes, wanted %d bytes", i, r.offset, r.length, len(slurp), r.want) + continue + } + if got, want := slurp, contents[obj][r.offset:r.offset+r.want]; !bytes.Equal(got, want) { + t.Errorf("RangeReader (%d, %d) = %q; want %q", r.offset, r.length, got, want) + } + rc.Close() + } + + objName := objects[0] + + // Test NewReader googleapi.Error. + // Since a 429 or 5xx is hard to cause, we trigger a 416. + realLen := len(contents[objName]) + _, err := bkt.Object(objName).NewRangeReader(ctx, int64(realLen*2), 10) + if err, ok := err.(*googleapi.Error); !ok { + t.Error("NewRangeReader did not return a googleapi.Error") + } else { + if err.Code != 416 { + t.Errorf("Code = %d; want %d", err.Code, 416) + } + if len(err.Header) == 0 { + t.Error("Missing googleapi.Error.Header") + } + if len(err.Body) == 0 { + t.Error("Missing googleapi.Error.Body") + } + } + + // Test StatObject. + o := h.mustObjectAttrs(bkt.Object(objName)) + if got, want := o.Name, objName; got != want { + t.Errorf("Name (%v) = %q; want %q", objName, got, want) + } + if got, want := o.ContentType, defaultType; got != want { + t.Errorf("ContentType (%v) = %q; want %q", objName, got, want) + } + created := o.Created + // Check that the object is newer than its containing bucket. + bAttrs := h.mustBucketAttrs(bkt) + if o.Created.Before(bAttrs.Created) { + t.Errorf("Object %v is older than its containing bucket, %v", o, bAttrs) + } + + // Test object copy. + copyName := "copy-" + objName + copyObj, err := bkt.Object(copyName).CopierFrom(bkt.Object(objName)).Run(ctx) + if err != nil { + t.Errorf("Copier.Run failed with %v", err) + } else if !namesEqual(copyObj, bucketName, copyName) { + t.Errorf("Copy object bucket, name: got %q.%q, want %q.%q", + copyObj.Bucket, copyObj.Name, bucketName, copyName) + } + + // Copying with attributes. + const contentEncoding = "identity" + copier := bkt.Object(copyName).CopierFrom(bkt.Object(objName)) + copier.ContentEncoding = contentEncoding + copyObj, err = copier.Run(ctx) + if err != nil { + t.Errorf("Copier.Run failed with %v", err) + } else { + if !namesEqual(copyObj, bucketName, copyName) { + t.Errorf("Copy object bucket, name: got %q.%q, want %q.%q", + copyObj.Bucket, copyObj.Name, bucketName, copyName) + } + if copyObj.ContentEncoding != contentEncoding { + t.Errorf("Copy ContentEncoding: got %q, want %q", copyObj.ContentEncoding, contentEncoding) + } + } + + // Test UpdateAttrs. + metadata := map[string]string{"key": "value"} + updated, err := bkt.Object(objName).Update(ctx, ObjectAttrsToUpdate{ + ContentType: "text/html", + ContentLanguage: "en", + Metadata: metadata, + ACL: []ACLRule{{Entity: "domain-google.com", Role: RoleReader}}, + }) + if err != nil { + t.Errorf("UpdateAttrs failed with %v", err) + } else { + if got, want := updated.ContentType, "text/html"; got != want { + t.Errorf("updated.ContentType == %q; want %q", got, want) + } + if got, want := updated.ContentLanguage, "en"; got != want { + t.Errorf("updated.ContentLanguage == %q; want %q", updated.ContentLanguage, want) + } + if got, want := updated.Metadata, metadata; !testutil.Equal(got, want) { + t.Errorf("updated.Metadata == %+v; want %+v", updated.Metadata, want) + } + if got, want := updated.Created, created; got != want { + t.Errorf("updated.Created == %q; want %q", got, want) + } + if !updated.Created.Before(updated.Updated) { + t.Errorf("updated.Updated should be newer than update.Created") + } + } + // Delete ContentType and ContentLanguage. + updated, err = bkt.Object(objName).Update(ctx, ObjectAttrsToUpdate{ + ContentType: "", + ContentLanguage: "", + Metadata: map[string]string{}, + }) + if err != nil { + t.Errorf("UpdateAttrs failed with %v", err) + } else { + if got, want := updated.ContentType, ""; got != want { + t.Errorf("updated.ContentType == %q; want %q", got, want) + } + if got, want := updated.ContentLanguage, ""; got != want { + t.Errorf("updated.ContentLanguage == %q; want %q", updated.ContentLanguage, want) + } + if updated.Metadata != nil { + t.Errorf("updated.Metadata == %+v; want nil", updated.Metadata) + } + if got, want := updated.Created, created; got != want { + t.Errorf("updated.Created == %q; want %q", got, want) + } + if !updated.Created.Before(updated.Updated) { + t.Errorf("updated.Updated should be newer than update.Created") + } + } + + // Test checksums. + checksumCases := []struct { + name string + contents [][]byte + size int64 + md5 string + crc32c uint32 + }{ + { + name: "checksum-object", + contents: [][]byte{[]byte("hello"), []byte("world")}, + size: 10, + md5: "fc5e038d38a57032085441e7fe7010b0", + crc32c: 1456190592, + }, + { + name: "zero-object", + contents: [][]byte{}, + size: 0, + md5: "d41d8cd98f00b204e9800998ecf8427e", + crc32c: 0, + }, + } + for _, c := range checksumCases { + wc := bkt.Object(c.name).NewWriter(ctx) + for _, data := range c.contents { + if _, err := wc.Write(data); err != nil { + t.Errorf("Write(%q) failed with %q", data, err) + } + } + if err = wc.Close(); err != nil { + t.Errorf("%q: close failed with %q", c.name, err) + } + obj := wc.Attrs() + if got, want := obj.Size, c.size; got != want { + t.Errorf("Object (%q) Size = %v; want %v", c.name, got, want) + } + if got, want := fmt.Sprintf("%x", obj.MD5), c.md5; got != want { + t.Errorf("Object (%q) MD5 = %q; want %q", c.name, got, want) + } + if got, want := obj.CRC32C, c.crc32c; got != want { + t.Errorf("Object (%q) CRC32C = %v; want %v", c.name, got, want) + } + } + + // Test public ACL. + publicObj := objects[0] + if err = bkt.Object(publicObj).ACL().Set(ctx, AllUsers, RoleReader); err != nil { + t.Errorf("PutACLEntry failed with %v", err) + } + publicClient, err := newTestClient(ctx, option.WithoutAuthentication()) + if err != nil { + t.Fatal(err) + } + + slurp := h.mustRead(publicClient.Bucket(bucketName).Object(publicObj)) + if !bytes.Equal(slurp, contents[publicObj]) { + t.Errorf("Public object's content: got %q, want %q", slurp, contents[publicObj]) + } + + // Test writer error handling. + wc := publicClient.Bucket(bucketName).Object(publicObj).NewWriter(ctx) + if _, err := wc.Write([]byte("hello")); err != nil { + t.Errorf("Write unexpectedly failed with %v", err) + } + if err = wc.Close(); err == nil { + t.Error("Close expected an error, found none") + } + + // Test deleting the copy object. + h.mustDeleteObject(bkt.Object(copyName)) + // Deleting it a second time should return ErrObjectNotExist. + if err := bkt.Object(copyName).Delete(ctx); err != ErrObjectNotExist { + t.Errorf("second deletion of %v = %v; want ErrObjectNotExist", copyName, err) + } + _, err = bkt.Object(copyName).Attrs(ctx) + if err != ErrObjectNotExist { + t.Errorf("Copy is expected to be deleted, stat errored with %v", err) + } + + // Test object composition. + var compSrcs []*ObjectHandle + var wantContents []byte + for _, obj := range objects { + compSrcs = append(compSrcs, bkt.Object(obj)) + wantContents = append(wantContents, contents[obj]...) + } + checkCompose := func(obj *ObjectHandle, wantContentType string) { + rc := h.mustNewReader(obj) + slurp, err = ioutil.ReadAll(rc) + if err != nil { + t.Fatalf("ioutil.ReadAll: %v", err) + } + defer rc.Close() + if !bytes.Equal(slurp, wantContents) { + t.Errorf("Composed object contents\ngot: %q\nwant: %q", slurp, wantContents) + } + if got := rc.ContentType(); got != wantContentType { + t.Errorf("Composed object content-type = %q, want %q", got, wantContentType) + } + } + + // Compose should work even if the user sets no destination attributes. + compDst := bkt.Object("composed1") + c := compDst.ComposerFrom(compSrcs...) + if _, err := c.Run(ctx); err != nil { + t.Fatalf("ComposeFrom error: %v", err) + } + checkCompose(compDst, "application/octet-stream") + + // It should also work if we do. + compDst = bkt.Object("composed2") + c = compDst.ComposerFrom(compSrcs...) + c.ContentType = "text/json" + if _, err := c.Run(ctx); err != nil { + t.Fatalf("ComposeFrom error: %v", err) + } + checkCompose(compDst, "text/json") +} + +func TestIntegration_Encoding(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + bkt := client.Bucket(bucketName) + + // Test content encoding + const zeroCount = 20 << 1 // TODO: should be 20 << 20 + obj := bkt.Object("gzip-test") + w := obj.NewWriter(ctx) + w.ContentEncoding = "gzip" + gw := gzip.NewWriter(w) + if _, err := io.Copy(gw, io.LimitReader(zeros{}, zeroCount)); err != nil { + t.Fatalf("io.Copy, upload: %v", err) + } + if err := gw.Close(); err != nil { + t.Errorf("gzip.Close(): %v", err) + } + if err := w.Close(); err != nil { + t.Errorf("w.Close(): %v", err) + } + r, err := obj.NewReader(ctx) + if err != nil { + t.Fatalf("NewReader(gzip-test): %v", err) + } + n, err := io.Copy(ioutil.Discard, r) + if err != nil { + t.Errorf("io.Copy, download: %v", err) + } + if n != zeroCount { + t.Errorf("downloaded bad data: got %d bytes, want %d", n, zeroCount) + } + + // Test NotFound. + _, err = bkt.Object("obj-not-exists").NewReader(ctx) + if err != ErrObjectNotExist { + t.Errorf("Object should not exist, err found to be %v", err) + } +} + +func namesEqual(obj *ObjectAttrs, bucketName, objectName string) bool { + return obj.Bucket == bucketName && obj.Name == objectName +} + +func testObjectIterator(t *testing.T, bkt *BucketHandle, objects []string) { + ctx := context.Background() + h := testHelper{t} + // Collect the list of items we expect: ObjectAttrs in lexical order by name. + names := make([]string, len(objects)) + copy(names, objects) + sort.Strings(names) + var attrs []*ObjectAttrs + for _, name := range names { + attrs = append(attrs, h.mustObjectAttrs(bkt.Object(name))) + } + msg, ok := itesting.TestIterator(attrs, + func() interface{} { return bkt.Objects(ctx, &Query{Prefix: "obj"}) }, + func(it interface{}) (interface{}, error) { return it.(*ObjectIterator).Next() }) + if !ok { + t.Errorf("ObjectIterator.Next: %s", msg) + } + // TODO(jba): test query.Delimiter != "" +} + +func TestIntegration_SignedURL(t *testing.T) { + if testing.Short() { // do not test during replay + t.Skip("Integration tests skipped in short mode") + } + // To test SignedURL, we need a real user email and private key. Extract them + // from the JSON key file. + jwtConf, err := testutil.JWTConfig() + if err != nil { + t.Fatal(err) + } + if jwtConf == nil { + t.Skip("JSON key file is not present") + } + + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + + bkt := client.Bucket(bucketName) + obj := "signedURL" + contents := []byte("This is a test of SignedURL.\n") + md5 := "Jyxvgwm9n2MsrGTMPbMeYA==" // base64-encoded MD5 of contents + if err := writeObject(ctx, bkt.Object(obj), "text/plain", contents); err != nil { + t.Fatalf("writing: %v", err) + } + for _, test := range []struct { + desc string + opts SignedURLOptions + headers map[string][]string + fail bool + }{ + { + desc: "basic", + }, + { + desc: "MD5 sent and matches", + opts: SignedURLOptions{MD5: md5}, + headers: map[string][]string{"Content-MD5": {md5}}, + }, + { + desc: "MD5 not sent", + opts: SignedURLOptions{MD5: md5}, + fail: true, + }, + { + desc: "Content-Type sent and matches", + opts: SignedURLOptions{ContentType: "text/plain"}, + headers: map[string][]string{"Content-Type": {"text/plain"}}, + }, + { + desc: "Content-Type sent but does not match", + opts: SignedURLOptions{ContentType: "text/plain"}, + headers: map[string][]string{"Content-Type": {"application/json"}}, + fail: true, + }, + { + desc: "Canonical headers sent and match", + opts: SignedURLOptions{Headers: []string{ + " X-Goog-Foo: Bar baz ", + "X-Goog-Novalue", // ignored: no value + "X-Google-Foo", // ignored: wrong prefix + }}, + headers: map[string][]string{"X-Goog-foo": {"Bar baz "}}, + }, + { + desc: "Canonical headers sent but don't match", + opts: SignedURLOptions{Headers: []string{" X-Goog-Foo: Bar baz"}}, + headers: map[string][]string{"X-Goog-Foo": {"bar baz"}}, + fail: true, + }, + } { + opts := test.opts + opts.GoogleAccessID = jwtConf.Email + opts.PrivateKey = jwtConf.PrivateKey + opts.Method = "GET" + opts.Expires = time.Now().Add(time.Hour) + u, err := SignedURL(bucketName, obj, &opts) + if err != nil { + t.Errorf("%s: SignedURL: %v", test.desc, err) + continue + } + got, err := getURL(u, test.headers) + if err != nil && !test.fail { + t.Errorf("%s: getURL %q: %v", test.desc, u, err) + } else if err == nil && !bytes.Equal(got, contents) { + t.Errorf("%s: got %q, want %q", test.desc, got, contents) + } + } +} + +// Make a GET request to a URL using an unauthenticated client, and return its contents. +func getURL(url string, headers map[string][]string) ([]byte, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + req.Header = headers + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("code=%d, body=%s", res.StatusCode, string(bytes)) + } + return bytes, nil +} + +func TestIntegration_ACL(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + + bkt := client.Bucket(bucketName) + + entity := ACLEntity("domain-google.com") + rule := ACLRule{Entity: entity, Role: RoleReader} + if err := bkt.DefaultObjectACL().Set(ctx, entity, RoleReader); err != nil { + t.Errorf("Can't put default ACL rule for the bucket, errored with %v", err) + } + acl, err := bkt.DefaultObjectACL().List(ctx) + if err != nil { + t.Errorf("DefaultObjectACL.List for bucket %q: %v", bucketName, err) + } else if !hasRule(acl, rule) { + t.Errorf("default ACL missing %#v", rule) + } + aclObjects := []string{"acl1", "acl2"} + for _, obj := range aclObjects { + c := randomContents() + if err := writeObject(ctx, bkt.Object(obj), "", c); err != nil { + t.Errorf("Write for %v failed with %v", obj, err) + } + } + name := aclObjects[0] + o := bkt.Object(name) + acl, err = o.ACL().List(ctx) + if err != nil { + t.Errorf("Can't retrieve ACL of %v", name) + } else if !hasRule(acl, rule) { + t.Errorf("object ACL missing %+v", rule) + } + if err := o.ACL().Delete(ctx, entity); err != nil { + t.Errorf("object ACL: could not delete entity %s", entity) + } + // Delete the default ACL rule. We can't move this code earlier in the + // test, because the test depends on the fact that the object ACL inherits + // it. + if err := bkt.DefaultObjectACL().Delete(ctx, entity); err != nil { + t.Errorf("default ACL: could not delete entity %s", entity) + } + + entity2 := ACLEntity("user-jbd@google.com") + rule2 := ACLRule{Entity: entity2, Role: RoleReader} + if err := bkt.ACL().Set(ctx, entity2, RoleReader); err != nil { + t.Errorf("Error while putting bucket ACL rule: %v", err) + } + bACL, err := bkt.ACL().List(ctx) + if err != nil { + t.Errorf("Error while getting the ACL of the bucket: %v", err) + } else if !hasRule(bACL, rule2) { + t.Errorf("bucket ACL missing %+v", rule2) + } + if err := bkt.ACL().Delete(ctx, entity2); err != nil { + t.Errorf("Error while deleting bucket ACL rule: %v", err) + } + +} + +func hasRule(acl []ACLRule, rule ACLRule) bool { + for _, r := range acl { + if r == rule { + return true + } + } + return false +} + +func TestIntegration_ValidObjectNames(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + + bkt := client.Bucket(bucketName) + + validNames := []string{ + "gopher", + "Гоферови", + "a", + strings.Repeat("a", 1024), + } + for _, name := range validNames { + if err := writeObject(ctx, bkt.Object(name), "", []byte("data")); err != nil { + t.Errorf("Object %q write failed: %v. Want success", name, err) + continue + } + defer bkt.Object(name).Delete(ctx) + } + + invalidNames := []string{ + "", // Too short. + strings.Repeat("a", 1025), // Too long. + "new\nlines", + "bad\xffunicode", + } + for _, name := range invalidNames { + // Invalid object names will either cause failure during Write or Close. + if err := writeObject(ctx, bkt.Object(name), "", []byte("data")); err != nil { + continue + } + defer bkt.Object(name).Delete(ctx) + t.Errorf("%q should have failed. Didn't", name) + } +} + +func TestIntegration_WriterContentType(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + + obj := client.Bucket(bucketName).Object("content") + testCases := []struct { + content string + setType, wantType string + }{ + { + content: "It was the best of times, it was the worst of times.", + wantType: "text/plain; charset=utf-8", + }, + { + content: "My first page", + wantType: "text/html; charset=utf-8", + }, + { + content: "My first page", + setType: "text/html", + wantType: "text/html", + }, + { + content: "My first page", + setType: "image/jpeg", + wantType: "image/jpeg", + }, + } + for i, tt := range testCases { + if err := writeObject(ctx, obj, tt.setType, []byte(tt.content)); err != nil { + t.Errorf("writing #%d: %v", i, err) + } + attrs, err := obj.Attrs(ctx) + if err != nil { + t.Errorf("obj.Attrs: %v", err) + continue + } + if got := attrs.ContentType; got != tt.wantType { + t.Errorf("Content-Type = %q; want %q\nContent: %q\nSet Content-Type: %q", got, tt.wantType, tt.content, tt.setType) + } + } +} + +func TestIntegration_ZeroSizedObject(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + obj := client.Bucket(bucketName).Object("zero") + + // Check writing it works as expected. + w := obj.NewWriter(ctx) + if err := w.Close(); err != nil { + t.Fatalf("Writer.Close: %v", err) + } + defer obj.Delete(ctx) + + // Check we can read it too. + body := h.mustRead(obj) + if len(body) != 0 { + t.Errorf("Body is %v, want empty []byte{}", body) + } +} + +func TestIntegration_Encryption(t *testing.T) { + // This function tests customer-supplied encryption keys for all operations + // involving objects. Bucket and ACL operations aren't tested because they + // aren't affected by customer encryption. Neither is deletion. + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + obj := client.Bucket(bucketName).Object("customer-encryption") + key := []byte("my-secret-AES-256-encryption-key") + keyHash := sha256.Sum256(key) + keyHashB64 := base64.StdEncoding.EncodeToString(keyHash[:]) + key2 := []byte("My-Secret-AES-256-Encryption-Key") + contents := "top secret." + + checkMetadataCall := func(msg string, f func(o *ObjectHandle) (*ObjectAttrs, error)) { + // Performing a metadata operation without the key should succeed. + attrs, err := f(obj) + if err != nil { + t.Fatalf("%s: %v", msg, err) + } + // The key hash should match... + if got, want := attrs.CustomerKeySHA256, keyHashB64; got != want { + t.Errorf("%s: key hash: got %q, want %q", msg, got, want) + } + // ...but CRC and MD5 should not be present. + if attrs.CRC32C != 0 { + t.Errorf("%s: CRC: got %v, want 0", msg, attrs.CRC32C) + } + if len(attrs.MD5) > 0 { + t.Errorf("%s: MD5: got %v, want len == 0", msg, attrs.MD5) + } + + // Performing a metadata operation with the key should succeed. + attrs, err = f(obj.Key(key)) + if err != nil { + t.Fatalf("%s: %v", msg, err) + } + // Check the key and content hashes. + if got, want := attrs.CustomerKeySHA256, keyHashB64; got != want { + t.Errorf("%s: key hash: got %q, want %q", msg, got, want) + } + if attrs.CRC32C == 0 { + t.Errorf("%s: CRC: got 0, want non-zero", msg) + } + if len(attrs.MD5) == 0 { + t.Errorf("%s: MD5: got len == 0, want len > 0", msg) + } + } + + checkRead := func(msg string, o *ObjectHandle, k []byte, wantContents string) { + // Reading the object without the key should fail. + if _, err := readObject(ctx, o); err == nil { + t.Errorf("%s: reading without key: want error, got nil", msg) + } + // Reading the object with the key should succeed. + got := h.mustRead(o.Key(k)) + gotContents := string(got) + // And the contents should match what we wrote. + if gotContents != wantContents { + t.Errorf("%s: contents: got %q, want %q", msg, gotContents, wantContents) + } + } + + checkReadUnencrypted := func(msg string, obj *ObjectHandle, wantContents string) { + got := h.mustRead(obj) + gotContents := string(got) + if gotContents != wantContents { + t.Errorf("%s: got %q, want %q", msg, gotContents, wantContents) + } + } + + // Write to obj using our own encryption key, which is a valid 32-byte + // AES-256 key. + h.mustWrite(obj.Key(key).NewWriter(ctx), []byte(contents)) + + checkMetadataCall("Attrs", func(o *ObjectHandle) (*ObjectAttrs, error) { + return o.Attrs(ctx) + }) + + checkMetadataCall("Update", func(o *ObjectHandle) (*ObjectAttrs, error) { + return o.Update(ctx, ObjectAttrsToUpdate{ContentLanguage: "en"}) + }) + + checkRead("first object", obj, key, contents) + + obj2 := client.Bucket(bucketName).Object("customer-encryption-2") + // Copying an object without the key should fail. + if _, err := obj2.CopierFrom(obj).Run(ctx); err == nil { + t.Fatal("want error, got nil") + } + // Copying an object with the key should succeed. + if _, err := obj2.CopierFrom(obj.Key(key)).Run(ctx); err != nil { + t.Fatal(err) + } + // The destination object is not encrypted; we can read it without a key. + checkReadUnencrypted("copy dest", obj2, contents) + + // Providing a key on the destination but not the source should fail, + // since the source is encrypted. + if _, err := obj2.Key(key2).CopierFrom(obj).Run(ctx); err == nil { + t.Fatal("want error, got nil") + } + + // But copying with keys for both source and destination should succeed. + if _, err := obj2.Key(key2).CopierFrom(obj.Key(key)).Run(ctx); err != nil { + t.Fatal(err) + } + // And the destination should be encrypted, meaning we can only read it + // with a key. + checkRead("copy destination", obj2, key2, contents) + + // Change obj2's key to prepare for compose, where all objects must have + // the same key. Also illustrates key rotation: copy an object to itself + // with a different key. + if _, err := obj2.Key(key).CopierFrom(obj2.Key(key2)).Run(ctx); err != nil { + t.Fatal(err) + } + obj3 := client.Bucket(bucketName).Object("customer-encryption-3") + // Composing without keys should fail. + if _, err := obj3.ComposerFrom(obj, obj2).Run(ctx); err == nil { + t.Fatal("want error, got nil") + } + // Keys on the source objects result in an error. + if _, err := obj3.ComposerFrom(obj.Key(key), obj2).Run(ctx); err == nil { + t.Fatal("want error, got nil") + } + // A key on the destination object both decrypts the source objects + // and encrypts the destination. + if _, err := obj3.Key(key).ComposerFrom(obj, obj2).Run(ctx); err != nil { + t.Fatalf("got %v, want nil", err) + } + // Check that the destination in encrypted. + checkRead("compose destination", obj3, key, contents+contents) + + // You can't compose one or more unencrypted source objects into an + // encrypted destination object. + _, err := obj2.CopierFrom(obj2.Key(key)).Run(ctx) // unencrypt obj2 + if err != nil { + t.Fatal(err) + } + if _, err := obj3.Key(key).ComposerFrom(obj2).Run(ctx); err == nil { + t.Fatal("got nil, want error") + } +} + +func TestIntegration_NonexistentBucket(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + + bkt := client.Bucket(uidSpace.New()) + if _, err := bkt.Attrs(ctx); err != ErrBucketNotExist { + t.Errorf("Attrs: got %v, want ErrBucketNotExist", err) + } + it := bkt.Objects(ctx, nil) + if _, err := it.Next(); err != ErrBucketNotExist { + t.Errorf("Objects: got %v, want ErrBucketNotExist", err) + } +} + +func TestIntegration_PerObjectStorageClass(t *testing.T) { + const ( + defaultStorageClass = "STANDARD" + newStorageClass = "MULTI_REGIONAL" + ) + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + bkt := client.Bucket(bucketName) + + // The bucket should have the default storage class. + battrs := h.mustBucketAttrs(bkt) + if battrs.StorageClass != defaultStorageClass { + t.Fatalf("bucket storage class: got %q, want %q", + battrs.StorageClass, defaultStorageClass) + } + // Write an object; it should start with the bucket's storage class. + obj := bkt.Object("posc") + h.mustWrite(obj.NewWriter(ctx), []byte("foo")) + oattrs, err := obj.Attrs(ctx) + if err != nil { + t.Fatal(err) + } + if oattrs.StorageClass != defaultStorageClass { + t.Fatalf("object storage class: got %q, want %q", + oattrs.StorageClass, defaultStorageClass) + } + // Now use Copy to change the storage class. + copier := obj.CopierFrom(obj) + copier.StorageClass = newStorageClass + oattrs2, err := copier.Run(ctx) + if err != nil { + log.Fatal(err) + } + if oattrs2.StorageClass != newStorageClass { + t.Fatalf("new object storage class: got %q, want %q", + oattrs2.StorageClass, newStorageClass) + } + + // We can also write a new object using a non-default storage class. + obj2 := bkt.Object("posc2") + w := obj2.NewWriter(ctx) + w.StorageClass = newStorageClass + h.mustWrite(w, []byte("xxx")) + if w.Attrs().StorageClass != newStorageClass { + t.Fatalf("new object storage class: got %q, want %q", + w.Attrs().StorageClass, newStorageClass) + } +} + +func TestIntegration_BucketInCopyAttrs(t *testing.T) { + // Confirm that if bucket is included in the object attributes of a rewrite + // call, but object name and content-type aren't, then we get an error. See + // the comment in Copier.Run. + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + bkt := client.Bucket(bucketName) + obj := bkt.Object("bucketInCopyAttrs") + h.mustWrite(obj.NewWriter(ctx), []byte("foo")) + copier := obj.CopierFrom(obj) + rawObject := copier.ObjectAttrs.toRawObject(bucketName) + _, err := copier.callRewrite(ctx, rawObject) + if err == nil { + t.Errorf("got nil, want error") + } +} + +func TestIntegration_NoUnicodeNormalization(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + bkt := client.Bucket("storage-library-test-bucket") + h := testHelper{t} + + for _, tst := range []struct { + nameQuoted, content string + }{ + {`"Caf\u00e9"`, "Normalization Form C"}, + {`"Cafe\u0301"`, "Normalization Form D"}, + } { + name, err := strconv.Unquote(tst.nameQuoted) + if err != nil { + t.Fatalf("invalid name: %s: %v", tst.nameQuoted, err) + } + if got := string(h.mustRead(bkt.Object(name))); got != tst.content { + t.Errorf("content of %s is %q, want %q", tst.nameQuoted, got, tst.content) + } + } +} + +func TestIntegration_HashesOnUpload(t *testing.T) { + // Check that the user can provide hashes on upload, and that these are checked. + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + obj := client.Bucket(bucketName).Object("hashesOnUpload-1") + data := []byte("I can't wait to be verified") + + write := func(w *Writer) error { + if _, err := w.Write(data); err != nil { + _ = w.Close() + return err + } + return w.Close() + } + + crc32c := crc32.Checksum(data, crc32cTable) + // The correct CRC should succeed. + w := obj.NewWriter(ctx) + w.CRC32C = crc32c + w.SendCRC32C = true + if err := write(w); err != nil { + t.Fatal(err) + } + + // If we change the CRC, validation should fail. + w = obj.NewWriter(ctx) + w.CRC32C = crc32c + 1 + w.SendCRC32C = true + if err := write(w); err == nil { + t.Fatal("write with bad CRC32c: want error, got nil") + } + + // If we have the wrong CRC but forget to send it, we succeed. + w = obj.NewWriter(ctx) + w.CRC32C = crc32c + 1 + if err := write(w); err != nil { + t.Fatal(err) + } + + // MD5 + md5 := md5.Sum(data) + // The correct MD5 should succeed. + w = obj.NewWriter(ctx) + w.MD5 = md5[:] + if err := write(w); err != nil { + t.Fatal(err) + } + + // If we change the MD5, validation should fail. + w = obj.NewWriter(ctx) + w.MD5 = append([]byte(nil), md5[:]...) + w.MD5[0]++ + if err := write(w); err == nil { + t.Fatal("write with bad MD5: want error, got nil") + } +} + +func TestIntegration_BucketIAM(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + + bkt := client.Bucket(bucketName) + + // This bucket is unique to this test run. So we don't have + // to worry about other runs interfering with our IAM policy + // changes. + + member := "projectViewer:" + testutil.ProjID() + role := iam.RoleName("roles/storage.objectViewer") + // Get the bucket's IAM policy. + policy, err := bkt.IAM().Policy(ctx) + if err != nil { + t.Fatalf("Getting policy: %v", err) + } + // The member should not have the role. + if policy.HasRole(member, role) { + t.Errorf("member %q has role %q", member, role) + } + // Change the policy. + policy.Add(member, role) + if err := bkt.IAM().SetPolicy(ctx, policy); err != nil { + t.Fatalf("SetPolicy: %v", err) + } + // Confirm that the binding was added. + policy, err = bkt.IAM().Policy(ctx) + if err != nil { + t.Fatalf("Getting policy: %v", err) + } + if !policy.HasRole(member, role) { + t.Errorf("member %q does not have role %q", member, role) + } + + // Check TestPermissions. + // This client should have all these permissions (and more). + perms := []string{"storage.buckets.get", "storage.buckets.delete"} + got, err := bkt.IAM().TestPermissions(ctx, perms) + if err != nil { + t.Fatalf("TestPermissions: %v", err) + } + sort.Strings(perms) + sort.Strings(got) + if !testutil.Equal(got, perms) { + t.Errorf("got %v, want %v", got, perms) + } +} + +func TestIntegration_RequesterPays(t *testing.T) { + // This test needs a second project and user (token source) to test + // all possibilities. Since we need these things for Firestore already, + // we use them here. + // + // There are up to three entities involved in a requester-pays call: + // + // 1. The user making the request. Here, we use + // a. The account used to create the token source used for all our + // integration tests (see testutil.TokenSource). + // b. The account used for the Firestore tests. + // 2. The project that owns the requester-pays bucket. Here, that + // is the test project ID (see testutil.ProjID). + // 3. The project provided as the userProject parameter of the request; + // the project to be billed. This test uses: + // a. The project that owns the requester-pays bucket (same as (2)) + // b. Another project (the Firestore project). + // + // The following must hold for this test to work: + // - (1a) must have resourcemanager.projects.createBillingAssignment permission + // (Owner role) on (2) (the project, not the bucket). + // - (1b) must NOT have that permission on (2). + // - (1b) must have serviceusage.services.use permission (Editor role) on (3b). + // - (1b) must NOT have that permission on (3a). + // - (1a) must NOT have that permission on (3b). + const wantErrorCode = 400 + + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + bucketName2 := uidSpace.New() + b1 := client.Bucket(bucketName2) + projID := testutil.ProjID() + // Use Firestore project as a project that does not contain the bucket. + otherProjID := os.Getenv(envFirestoreProjID) + if otherProjID == "" { + t.Fatalf("need a second project (env var %s)", envFirestoreProjID) + } + ts := testutil.TokenSourceEnv(ctx, envFirestorePrivateKey, ScopeFullControl) + if ts == nil { + t.Fatalf("need a second account (env var %s)", envFirestorePrivateKey) + } + otherClient, err := newTestClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + defer otherClient.Close() + b2 := otherClient.Bucket(bucketName2) + user, err := keyFileEmail(os.Getenv("GCLOUD_TESTS_GOLANG_KEY")) + if err != nil { + t.Fatal(err) + } + otherUser, err := keyFileEmail(os.Getenv(envFirestorePrivateKey)) + if err != nil { + t.Fatal(err) + } + + // Create a requester-pays bucket. The bucket is contained in the project projID. + h.mustCreate(b1, projID, &BucketAttrs{RequesterPays: true}) + if err := b1.ACL().Set(ctx, ACLEntity("user-"+otherUser), RoleOwner); err != nil { + t.Fatal(err) + } + + // Extract the error code from err if it's a googleapi.Error. + errCode := func(err error) int { + if err == nil { + return 0 + } + if err, ok := err.(*googleapi.Error); ok { + return err.Code + } + return -1 + } + + // Call f under various conditions. + // Here b and ob refer to the same bucket, but b is bound to client, + // while ob is bound to otherClient. The clients differ in their credentials, + // i.e. the identity of the user making the RPC: b's user is an Owner on the + // bucket's containing project, ob's is not. + call := func(msg string, f func(*BucketHandle) error) { + // user: an Owner on the containing project + // userProject: absent + // result: success, by the rule permitting access by owners of the containing bucket. + if err := f(b1); err != nil { + t.Errorf("%s: %v, want nil\n"+ + "confirm that %s is an Owner on %s", + msg, err, user, projID) + } + // user: an Owner on the containing project + // userProject: containing project + // result: success, by the same rule as above; userProject is unnecessary but allowed. + if err := f(b1.UserProject(projID)); err != nil { + t.Errorf("%s: got %v, want nil", msg, err) + } + // user: not an Owner on the containing project + // userProject: absent + // result: failure, by the standard requester-pays rule + err := f(b2) + if got, want := errCode(err), wantErrorCode; got != want { + t.Errorf("%s: got error %v with code %d, want code %d\n"+ + "confirm that %s is NOT an Owner on %s", + msg, err, got, want, otherUser, projID) + } + // user: not an Owner on the containing project + // userProject: not the containing one, but user has Editor role on it + // result: success, by the standard requester-pays rule + if err := f(b2.UserProject(otherProjID)); err != nil { + t.Errorf("%s: got %v, want nil\n"+ + "confirm that %s is an Editor on %s and that that project has billing enabled", + msg, err, otherUser, otherProjID) + } + // user: not an Owner on the containing project + // userProject: the containing one, on which the user does NOT have Editor permission. + // result: failure + err = f(b2.UserProject("veener-jba")) + if got, want := errCode(err), 403; got != want { + t.Errorf("%s: got error %v, want code %d\n"+ + "confirm that %s is NOT an Editor on %s", + msg, err, want, otherUser, "veener-jba") + } + } + + // Getting its attributes requires a user project. + var attrs *BucketAttrs + call("Bucket attrs", func(b *BucketHandle) error { + a, err := b.Attrs(ctx) + if a != nil { + attrs = a + } + return err + }) + if attrs != nil { + if got, want := attrs.RequesterPays, true; got != want { + t.Fatalf("attr.RequesterPays = %t, want %t", got, want) + } + } + // Object operations. + call("write object", func(b *BucketHandle) error { + return writeObject(ctx, b.Object("foo"), "text/plain", []byte("hello")) + }) + call("read object", func(b *BucketHandle) error { + _, err := readObject(ctx, b.Object("foo")) + return err + }) + call("object attrs", func(b *BucketHandle) error { + _, err := b.Object("foo").Attrs(ctx) + return err + }) + call("update object", func(b *BucketHandle) error { + _, err := b.Object("foo").Update(ctx, ObjectAttrsToUpdate{ContentLanguage: "en"}) + return err + }) + + // ACL operations. + entity := ACLEntity("domain-google.com") + call("bucket acl set", func(b *BucketHandle) error { + return b.ACL().Set(ctx, entity, RoleReader) + }) + call("bucket acl list", func(b *BucketHandle) error { + _, err := b.ACL().List(ctx) + return err + }) + call("bucket acl delete", func(b *BucketHandle) error { + err := b.ACL().Delete(ctx, entity) + if errCode(err) == 404 { + // Since we call the function multiple times, it will + // fail with NotFound for all but the first. + return nil + } + return err + }) + call("default object acl set", func(b *BucketHandle) error { + return b.DefaultObjectACL().Set(ctx, entity, RoleReader) + }) + call("default object acl list", func(b *BucketHandle) error { + _, err := b.DefaultObjectACL().List(ctx) + return err + }) + call("default object acl delete", func(b *BucketHandle) error { + err := b.DefaultObjectACL().Delete(ctx, entity) + if errCode(err) == 404 { + return nil + } + return err + }) + call("object acl set", func(b *BucketHandle) error { + return b.Object("foo").ACL().Set(ctx, entity, RoleReader) + }) + call("object acl list", func(b *BucketHandle) error { + _, err := b.Object("foo").ACL().List(ctx) + return err + }) + call("object acl delete", func(b *BucketHandle) error { + err := b.Object("foo").ACL().Delete(ctx, entity) + if errCode(err) == 404 { + return nil + } + return err + }) + + // Copy and compose. + call("copy", func(b *BucketHandle) error { + _, err := b.Object("copy").CopierFrom(b.Object("foo")).Run(ctx) + return err + }) + call("compose", func(b *BucketHandle) error { + _, err := b.Object("compose").ComposerFrom(b.Object("foo"), b.Object("copy")).Run(ctx) + return err + }) + call("delete object", func(b *BucketHandle) error { + // Make sure the object exists, so we don't get confused by ErrObjectNotExist. + // The storage service may perform validation in any order (perhaps in parallel), + // so if we delete an object that doesn't exist and for which we lack permission, + // we could see either of those two errors. (See Google-internal bug 78341001.) + h.mustWrite(b1.Object("foo").NewWriter(ctx), []byte("hello")) // note: b1, not b. + return b.Object("foo").Delete(ctx) + }) + b1.Object("foo").Delete(ctx) // Make sure object is deleted. + for _, obj := range []string{"copy", "compose"} { + if err := b1.UserProject(projID).Object(obj).Delete(ctx); err != nil { + t.Fatalf("could not delete %q: %v", obj, err) + } + } + + h.mustDeleteBucket(b1) +} + +// TODO(jba): move to testutil, factor out from firestore/integration_test.go. +const ( + envFirestoreProjID = "GCLOUD_TESTS_GOLANG_FIRESTORE_PROJECT_ID" + envFirestorePrivateKey = "GCLOUD_TESTS_GOLANG_FIRESTORE_KEY" +) + +func keyFileEmail(filename string) (string, error) { + bytes, err := ioutil.ReadFile(filename) + if err != nil { + return "", err + } + var v struct { + ClientEmail string `json:"client_email"` + } + if err := json.Unmarshal(bytes, &v); err != nil { + return "", err + } + return v.ClientEmail, nil +} + +func TestIntegration_Notifications(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + bkt := client.Bucket(bucketName) + + checkNotifications := func(msg string, want map[string]*Notification) { + got, err := bkt.Notifications(ctx) + if err != nil { + t.Fatal(err) + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("%s: got=-, want=+:\n%s", msg, diff) + } + } + checkNotifications("initial", map[string]*Notification{}) + + nArg := &Notification{ + TopicProjectID: testutil.ProjID(), + TopicID: "go-storage-notification-test", + PayloadFormat: NoPayload, + } + n, err := bkt.AddNotification(ctx, nArg) + if err != nil { + t.Fatal(err) + } + nArg.ID = n.ID + if !testutil.Equal(n, nArg) { + t.Errorf("got %+v, want %+v", n, nArg) + } + checkNotifications("after add", map[string]*Notification{n.ID: n}) + + if err := bkt.DeleteNotification(ctx, n.ID); err != nil { + t.Fatal(err) + } + checkNotifications("after delete", map[string]*Notification{}) +} + +func TestIntegration_PublicBucket(t *testing.T) { + // Confirm that an unauthenticated client can access a public bucket. + // See https://cloud.google.com/storage/docs/public-datasets/landsat + if testing.Short() && !replaying { + t.Skip("Integration tests skipped in short mode") + } + + const landsatBucket = "gcp-public-data-landsat" + const landsatPrefix = "LC08/PRE/044/034/LC80440342016259LGN00/" + const landsatObject = landsatPrefix + "LC80440342016259LGN00_MTL.txt" + + // Create an unauthenticated client. + ctx := context.Background() + client, err := newTestClient(ctx, option.WithoutAuthentication()) + if err != nil { + t.Fatal(err) + } + defer client.Close() + h := testHelper{t} + bkt := client.Bucket(landsatBucket) + obj := bkt.Object(landsatObject) + + // Read a public object. + bytes := h.mustRead(obj) + if got, want := len(bytes), 7903; got != want { + t.Errorf("len(bytes) = %d, want %d", got, want) + } + + // List objects in a public bucket. + iter := bkt.Objects(ctx, &Query{Prefix: landsatPrefix}) + gotCount := 0 + for { + _, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + gotCount++ + } + if wantCount := 13; gotCount != wantCount { + t.Errorf("object count: got %d, want %d", gotCount, wantCount) + } + + errCode := func(err error) int { + if err, ok := err.(*googleapi.Error); !ok { + return -1 + } else { + return err.Code + } + } + + // Reading from or writing to a non-public bucket fails. + c := testConfig(ctx, t) + defer c.Close() + nonPublicObj := client.Bucket(bucketName).Object("noauth") + // Oddly, reading returns 403 but writing returns 401. + _, err = readObject(ctx, nonPublicObj) + if got, want := errCode(err), 403; got != want { + t.Errorf("got code %d; want %d\nerror: %v", got, want, err) + } + err = writeObject(ctx, nonPublicObj, "text/plain", []byte("b")) + if got, want := errCode(err), 401; got != want { + t.Errorf("got code %d; want %d\nerror: %v", got, want, err) + } +} + +func TestIntegration_ReadCRC(t *testing.T) { + // Test that the checksum is handled correctly when reading files. + // For gzipped files, see https://github.com/GoogleCloudPlatform/google-cloud-dotnet/issues/1641. + if testing.Short() && !replaying { + t.Skip("Integration tests skipped in short mode") + } + + const ( + // This is an uncompressed file. + // See https://cloud.google.com/storage/docs/public-datasets/landsat + uncompressedBucket = "gcp-public-data-landsat" + uncompressedObject = "LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt" + + gzippedBucket = "storage-library-test-bucket" + gzippedObject = "gzipped-text.txt" + gzippedContents = "hello world" // uncompressed contents of the file + ) + ctx := context.Background() + client, err := newTestClient(ctx, option.WithoutAuthentication()) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + for _, test := range []struct { + desc string + obj *ObjectHandle + offset, length int64 + readCompressed bool // don't decompress a gzipped file + + wantErr bool + wantCheck bool // Should Reader try to check the CRC? + }{ + { + desc: "uncompressed, entire file", + obj: client.Bucket(uncompressedBucket).Object(uncompressedObject), + offset: 0, + length: -1, + readCompressed: false, + wantCheck: true, + }, + { + desc: "uncompressed, entire file, don't decompress", + obj: client.Bucket(uncompressedBucket).Object(uncompressedObject), + offset: 0, + length: -1, + readCompressed: true, + wantCheck: true, + }, + { + desc: "uncompressed, suffix", + obj: client.Bucket(uncompressedBucket).Object(uncompressedObject), + offset: 1, + length: -1, + readCompressed: false, + wantCheck: false, + }, + { + desc: "uncompressed, prefix", + obj: client.Bucket(uncompressedBucket).Object(uncompressedObject), + offset: 0, + length: 18, + readCompressed: false, + wantCheck: false, + }, + { + // When a gzipped file is unzipped on read, we can't verify the checksum + // because it was computed against the zipped contents. We can detect + // this case using http.Response.Uncompressed. + desc: "compressed, entire file, unzipped", + obj: client.Bucket(gzippedBucket).Object(gzippedObject), + offset: 0, + length: -1, + readCompressed: false, + wantCheck: false, + }, + { + // When we read a gzipped file uncompressed, it's like reading a regular file: + // the served content and the CRC match. + desc: "compressed, entire file, read compressed", + obj: client.Bucket(gzippedBucket).Object(gzippedObject), + offset: 0, + length: -1, + readCompressed: true, + wantCheck: true, + }, + { + desc: "compressed, partial, server unzips", + obj: client.Bucket(gzippedBucket).Object(gzippedObject), + offset: 1, + length: 8, + readCompressed: false, + wantErr: true, // GCS can't serve part of a gzipped object + wantCheck: false, + }, + { + desc: "compressed, partial, read compressed", + obj: client.Bucket(gzippedBucket).Object(gzippedObject), + offset: 1, + length: 8, + readCompressed: true, + wantCheck: false, + }, + } { + obj := test.obj.ReadCompressed(test.readCompressed) + r, err := obj.NewRangeReader(ctx, test.offset, test.length) + if err != nil { + if test.wantErr { + continue + } + t.Fatalf("%s: %v", test.desc, err) + } + if got, want := r.checkCRC, test.wantCheck; got != want { + t.Errorf("%s, checkCRC: got %t, want %t", test.desc, got, want) + } + _, err = ioutil.ReadAll(r) + _ = r.Close() + if err != nil { + t.Fatalf("%s: %v", test.desc, err) + } + } +} + +func TestIntegration_CancelWrite(t *testing.T) { + // Verify that canceling the writer's context immediately stops uploading an object. + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + bkt := client.Bucket(bucketName) + + cctx, cancel := context.WithCancel(ctx) + defer cancel() + obj := bkt.Object("cancel-write") + w := obj.NewWriter(cctx) + w.ChunkSize = googleapi.MinUploadChunkSize + buf := make([]byte, w.ChunkSize) + // Write the first chunk. This is read in its entirety before sending the request + // (see google.golang.org/api/gensupport.PrepareUpload), so we expect it to return + // without error. + _, err := w.Write(buf) + if err != nil { + t.Fatal(err) + } + // Now cancel the context. + cancel() + // The next Write should return context.Canceled. + _, err = w.Write(buf) + if err != context.Canceled { + t.Fatalf("got %v, wanted context.Canceled", err) + } + // The Close should too. + err = w.Close() + if err != context.Canceled { + t.Fatalf("got %v, wanted context.Canceled", err) + } +} + +func TestIntegration_UpdateCORS(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + initialSettings := []CORS{ + { + MaxAge: time.Hour, + Methods: []string{"POST"}, + Origins: []string{"some-origin.com"}, + ResponseHeaders: []string{"foo-bar"}, + }, + } + + for _, test := range []struct { + input []CORS + want []CORS + }{ + { + input: []CORS{ + { + MaxAge: time.Hour, + Methods: []string{"GET"}, + Origins: []string{"*"}, + ResponseHeaders: []string{"some-header"}, + }, + }, + want: []CORS{ + { + MaxAge: time.Hour, + Methods: []string{"GET"}, + Origins: []string{"*"}, + ResponseHeaders: []string{"some-header"}, + }, + }, + }, + { + input: []CORS{}, + want: nil, + }, + { + input: nil, + want: []CORS{ + { + MaxAge: time.Hour, + Methods: []string{"POST"}, + Origins: []string{"some-origin.com"}, + ResponseHeaders: []string{"foo-bar"}, + }, + }, + }, + } { + bkt := client.Bucket(uidSpace.New()) + h.mustCreate(bkt, testutil.ProjID(), &BucketAttrs{CORS: initialSettings}) + defer h.mustDeleteBucket(bkt) + h.mustUpdateBucket(bkt, BucketAttrsToUpdate{CORS: test.input}) + attrs := h.mustBucketAttrs(bkt) + if diff := testutil.Diff(attrs.CORS, test.want); diff != "" { + t.Errorf("input: %v\ngot=-, want=+:\n%s", test.input, diff) + } + } +} + +func TestIntegration_UpdateRetentionPolicy(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + initial := &RetentionPolicy{RetentionPeriod: time.Minute} + + for _, test := range []struct { + input *RetentionPolicy + want *RetentionPolicy + }{ + { // Update + input: &RetentionPolicy{RetentionPeriod: time.Hour}, + want: &RetentionPolicy{RetentionPeriod: time.Hour}, + }, + { // Update even with timestamp (EffectiveTime should be ignored) + input: &RetentionPolicy{RetentionPeriod: time.Hour, EffectiveTime: time.Now()}, + want: &RetentionPolicy{RetentionPeriod: time.Hour}, + }, + { // Remove + input: &RetentionPolicy{}, + want: nil, + }, + { // Remove even with timestamp (EffectiveTime should be ignored) + input: &RetentionPolicy{EffectiveTime: time.Now()}, + want: nil, + }, + { // Ignore + input: nil, + want: initial, + }, + } { + bkt := client.Bucket(uidSpace.New()) + h.mustCreate(bkt, testutil.ProjID(), &BucketAttrs{RetentionPolicy: initial}) + defer h.mustDeleteBucket(bkt) + h.mustUpdateBucket(bkt, BucketAttrsToUpdate{RetentionPolicy: test.input}) + attrs := h.mustBucketAttrs(bkt) + if attrs.RetentionPolicy != nil && attrs.RetentionPolicy.EffectiveTime.Unix() == 0 { + // Should be set by the server and parsed by the client + t.Fatal("EffectiveTime should be set, but it was not") + } + if diff := testutil.Diff(attrs.RetentionPolicy, test.want, cmpopts.IgnoreTypes(time.Time{})); diff != "" { + t.Errorf("input: %v\ngot=-, want=+:\n%s", test.input, diff) + } + } +} + +func TestIntegration_DeleteObjectInBucketWithRetentionPolicy(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + bkt := client.Bucket(uidSpace.New()) + h.mustCreate(bkt, testutil.ProjID(), &BucketAttrs{RetentionPolicy: &RetentionPolicy{RetentionPeriod: 25 * time.Hour}}) + + oh := bkt.Object("some-object") + if err := writeObject(ctx, oh, "text/plain", []byte("hello world")); err != nil { + t.Fatal(err) + } + + if err := oh.Delete(ctx); err == nil { + t.Fatal("expected to err deleting an object in a bucket with retention period, but got nil") + } + + // Remove the retention period + h.mustUpdateBucket(bkt, BucketAttrsToUpdate{RetentionPolicy: &RetentionPolicy{RetentionPeriod: 0}}) + h.mustDeleteObject(oh) + h.mustDeleteBucket(bkt) +} + +func TestIntegration_LockBucket(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + bkt := client.Bucket(uidSpace.New()) + h.mustCreate(bkt, testutil.ProjID(), &BucketAttrs{RetentionPolicy: &RetentionPolicy{RetentionPeriod: time.Hour * 25}}) + attrs := h.mustBucketAttrs(bkt) + err := bkt.If(BucketConditions{MetagenerationMatch: attrs.MetaGeneration}).LockRetentionPolicy(ctx) + if err != nil { + t.Fatal("could not lock", err) + } + + _, err = bkt.Update(ctx, BucketAttrsToUpdate{RetentionPolicy: &RetentionPolicy{RetentionPeriod: time.Hour}}) + if err == nil { + t.Fatal("Expected error updating locked bucket, got nil") + } +} + +func TestIntegration_LockBucket_MetagenerationRequired(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + bkt := client.Bucket(uidSpace.New()) + h.mustCreate(bkt, testutil.ProjID(), &BucketAttrs{ + RetentionPolicy: &RetentionPolicy{RetentionPeriod: time.Hour * 25}, + }) + err := bkt.LockRetentionPolicy(ctx) + if err == nil { + t.Fatal("expected error locking bucket without metageneration condition, got nil") + } +} + +func TestIntegration_KMS(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + h := testHelper{t} + + keyRingName := os.Getenv("GCLOUD_TESTS_GOLANG_KEYRING") + if keyRingName == "" { + t.Fatal("GCLOUD_TESTS_GOLANG_KEYRING must be set. See CONTRIBUTING.md for details") + } + keyName1 := keyRingName + "/cryptoKeys/key1" + keyName2 := keyRingName + "/cryptoKeys/key2" + contents := []byte("my secret") + + write := func(obj *ObjectHandle, setKey bool) { + w := obj.NewWriter(ctx) + if setKey { + w.KMSKeyName = keyName1 + } + h.mustWrite(w, contents) + } + + checkRead := func(obj *ObjectHandle) { + got := h.mustRead(obj) + if !bytes.Equal(got, contents) { + t.Errorf("got %v, want %v", got, contents) + } + attrs := h.mustObjectAttrs(obj) + if len(attrs.KMSKeyName) < len(keyName1) || attrs.KMSKeyName[:len(keyName1)] != keyName1 { + t.Errorf("got %q, want %q", attrs.KMSKeyName, keyName1) + } + } + + // Write an object with a key, then read it to verify its contents and the presence of the key name. + bkt := client.Bucket(bucketName) + obj := bkt.Object("kms") + write(obj, true) + checkRead(obj) + h.mustDeleteObject(obj) + + // Encrypt an object with a CSEK, then copy it using a CMEK. + src := bkt.Object("csek").Key(testEncryptionKey) + if err := writeObject(ctx, src, "text/plain", contents); err != nil { + t.Fatal(err) + } + dest := bkt.Object("cmek") + c := dest.CopierFrom(src) + c.DestinationKMSKeyName = keyName1 + if _, err := c.Run(ctx); err != nil { + t.Fatal(err) + } + checkRead(dest) + src.Delete(ctx) + dest.Delete(ctx) + + // Create a bucket with a default key, then write and read an object. + bkt = client.Bucket(uidSpace.New()) + h.mustCreate(bkt, testutil.ProjID(), &BucketAttrs{ + Location: "US", + Encryption: &BucketEncryption{DefaultKMSKeyName: keyName1}, + }) + defer h.mustDeleteBucket(bkt) + + attrs := h.mustBucketAttrs(bkt) + if got, want := attrs.Encryption.DefaultKMSKeyName, keyName1; got != want { + t.Fatalf("got %q, want %q", got, want) + } + obj = bkt.Object("kms") + write(obj, false) + checkRead(obj) + h.mustDeleteObject(obj) + + // Update the bucket's default key to a different name. + // (This key doesn't have to exist.) + attrs = h.mustUpdateBucket(bkt, BucketAttrsToUpdate{Encryption: &BucketEncryption{DefaultKMSKeyName: keyName2}}) + if got, want := attrs.Encryption.DefaultKMSKeyName, keyName2; got != want { + t.Fatalf("got %q, want %q", got, want) + } + attrs = h.mustBucketAttrs(bkt) + if got, want := attrs.Encryption.DefaultKMSKeyName, keyName2; got != want { + t.Fatalf("got %q, want %q", got, want) + } + + // Remove the default KMS key. + attrs = h.mustUpdateBucket(bkt, BucketAttrsToUpdate{Encryption: &BucketEncryption{DefaultKMSKeyName: ""}}) + if attrs.Encryption != nil { + t.Fatalf("got %#v, want nil", attrs.Encryption) + } +} + +type testHelper struct { + t *testing.T +} + +func (h testHelper) mustCreate(b *BucketHandle, projID string, attrs *BucketAttrs) { + if err := b.Create(context.Background(), projID, attrs); err != nil { + h.t.Fatalf("%s: bucket create: %v", loc(), err) + } +} + +func (h testHelper) mustDeleteBucket(b *BucketHandle) { + if err := b.Delete(context.Background()); err != nil { + h.t.Fatalf("%s: bucket delete: %v", loc(), err) + } +} + +func (h testHelper) mustBucketAttrs(b *BucketHandle) *BucketAttrs { + attrs, err := b.Attrs(context.Background()) + if err != nil { + h.t.Fatalf("%s: bucket attrs: %v", loc(), err) + } + return attrs +} + +func (h testHelper) mustUpdateBucket(b *BucketHandle, ua BucketAttrsToUpdate) *BucketAttrs { + attrs, err := b.Update(context.Background(), ua) + if err != nil { + h.t.Fatalf("%s: update: %v", loc(), err) + } + return attrs +} + +func (h testHelper) mustObjectAttrs(o *ObjectHandle) *ObjectAttrs { + attrs, err := o.Attrs(context.Background()) + if err != nil { + h.t.Fatalf("%s: object attrs: %v", loc(), err) + } + return attrs +} + +func (h testHelper) mustDeleteObject(o *ObjectHandle) { + if err := o.Delete(context.Background()); err != nil { + h.t.Fatalf("%s: object delete: %v", loc(), err) + } +} + +func (h testHelper) mustWrite(w *Writer, data []byte) { + if _, err := w.Write(data); err != nil { + w.Close() + h.t.Fatalf("%s: write: %v", loc(), err) + } + if err := w.Close(); err != nil { + h.t.Fatalf("%s: close write: %v", loc(), err) + } +} + +func (h testHelper) mustRead(obj *ObjectHandle) []byte { + data, err := readObject(context.Background(), obj) + if err != nil { + h.t.Fatalf("%s: read: %v", loc(), err) + } + return data +} + +func (h testHelper) mustNewReader(obj *ObjectHandle) *Reader { + r, err := obj.NewReader(context.Background()) + if err != nil { + h.t.Fatalf("%s: new reader: %v", loc(), err) + } + return r +} + +func writeObject(ctx context.Context, obj *ObjectHandle, contentType string, contents []byte) error { + w := obj.NewWriter(ctx) + w.ContentType = contentType + w.CacheControl = "public, max-age=60" + if contents != nil { + if _, err := w.Write(contents); err != nil { + _ = w.Close() + return err + } + } + return w.Close() +} + +// loc returns a string describing the file and line of its caller's call site. In +// other words, if a test function calls a helper, and the helper calls loc, then the +// string will refer to the line on which the test function called the helper. +// TODO(jba): use t.Helper once we drop go 1.6. +func loc() string { + _, file, line, ok := runtime.Caller(2) + if !ok { + return "???" + } + return fmt.Sprintf("%s:%d", filepath.Base(file), line) +} + +func readObject(ctx context.Context, obj *ObjectHandle) ([]byte, error) { + r, err := obj.NewReader(ctx) + if err != nil { + return nil, err + } + defer r.Close() + return ioutil.ReadAll(r) +} + +// cleanupBuckets deletes the bucket used for testing, as well as old +// testing buckets that weren't cleaned previously. +func cleanupBuckets() error { + if testing.Short() { + return nil // Don't clean up in short mode. + } + ctx := context.Background() + client := config(ctx) + if client == nil { + return nil // Don't cleanup if we're not configured correctly. + } + defer client.Close() + if err := killBucket(ctx, client, bucketName); err != nil { + return err + } + + // Delete buckets whose name begins with our test prefix, and which were + // created a while ago. (Unfortunately GCS doesn't provide last-modified + // time, which would be a better way to check for staleness.) + const expireAge = 24 * time.Hour + projectID := testutil.ProjID() + it := client.Buckets(ctx, projectID) + it.Prefix = testPrefix + for { + bktAttrs, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return err + } + if time.Since(bktAttrs.Created) > expireAge { + log.Printf("deleting bucket %q, which is more than %s old", bktAttrs.Name, expireAge) + if err := killBucket(ctx, client, bktAttrs.Name); err != nil { + return err + } + } + } + return nil +} + +// killBucket deletes a bucket and all its objects. +func killBucket(ctx context.Context, client *Client, bucketName string) error { + bkt := client.Bucket(bucketName) + // Bucket must be empty to delete. + it := bkt.Objects(ctx, nil) + for { + objAttrs, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return err + } + if err := bkt.Object(objAttrs.Name).Delete(ctx); err != nil { + return fmt.Errorf("deleting %q: %v", bucketName+"/"+objAttrs.Name, err) + } + } + // GCS is eventually consistent, so this delete may fail because the + // replica still sees an object in the bucket. We log the error and expect + // a later test run to delete the bucket. + if err := bkt.Delete(ctx); err != nil { + log.Printf("deleting %q: %v", bucketName, err) + } + return nil +} + +func randomContents() []byte { + h := md5.New() + io.WriteString(h, fmt.Sprintf("hello world%d", rng.Intn(100000))) + return h.Sum(nil) +} + +type zeros struct{} + +func (zeros) Read(p []byte) (int, error) { return len(p), nil } diff --git a/vendor/cloud.google.com/go/storage/invoke.go b/vendor/cloud.google.com/go/storage/invoke.go new file mode 100644 index 0000000000..955ef72121 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/invoke.go @@ -0,0 +1,36 @@ +// 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 storage + +import ( + "cloud.google.com/go/internal" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" +) + +// runWithRetry calls the function until it returns nil or a non-retryable error, or +// the context is done. +func runWithRetry(ctx context.Context, call func() error) error { + return internal.Retry(ctx, gax.Backoff{}, func() (stop bool, err error) { + err = call() + if err == nil { + return true, nil + } + if shouldRetry(err) { + return false, nil + } + return true, err + }) +} diff --git a/vendor/cloud.google.com/go/storage/invoke_test.go b/vendor/cloud.google.com/go/storage/invoke_test.go new file mode 100644 index 0000000000..bd401388f6 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/invoke_test.go @@ -0,0 +1,56 @@ +// 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 storage + +import ( + "errors" + "testing" + + "golang.org/x/net/context" + "google.golang.org/api/googleapi" +) + +func TestInvoke(t *testing.T) { + t.Parallel() + ctx := context.Background() + // Time-based tests are flaky. We just make sure that invoke eventually + // returns with the right error. + + for _, test := range []struct { + count int // number of times to return retryable error + retryCode int // error code for retryable error + err error // error to return after count returns of retryCode + }{ + {0, 0, nil}, + {0, 0, errors.New("foo")}, + {1, 429, nil}, + {1, 429, errors.New("bar")}, + {2, 518, nil}, + {2, 599, &googleapi.Error{Code: 428}}, + } { + counter := 0 + call := func() error { + counter++ + if counter <= test.count { + return &googleapi.Error{Code: test.retryCode} + } + return test.err + } + got := runWithRetry(ctx, call) + if got != test.err { + t.Errorf("%v: got %v, want %v", test, got, test.err) + } + } +} diff --git a/vendor/cloud.google.com/go/storage/not_go110.go b/vendor/cloud.google.com/go/storage/not_go110.go new file mode 100644 index 0000000000..700fde1ccb --- /dev/null +++ b/vendor/cloud.google.com/go/storage/not_go110.go @@ -0,0 +1,40 @@ +// Copyright 2017 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 !go1.10 + +package storage + +import ( + "net/url" + "strings" + + "google.golang.org/api/googleapi" +) + +func shouldRetry(err error) bool { + switch e := err.(type) { + case *googleapi.Error: + // Retry on 429 and 5xx, according to + // https://cloud.google.com/storage/docs/exponential-backoff. + return e.Code == 429 || (e.Code >= 500 && e.Code < 600) + case *url.Error: + // Retry on REFUSED_STREAM. + // Unfortunately the error type is unexported, so we resort to string + // matching. + return strings.Contains(e.Error(), "REFUSED_STREAM") + default: + return false + } +} diff --git a/vendor/cloud.google.com/go/storage/not_go17.go b/vendor/cloud.google.com/go/storage/not_go17.go new file mode 100644 index 0000000000..28b584744c --- /dev/null +++ b/vendor/cloud.google.com/go/storage/not_go17.go @@ -0,0 +1,33 @@ +// Copyright 2017 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 !go1.7 + +package storage + +import ( + "net/http" +) + +func withContext(r *http.Request, _ interface{}) *http.Request { + // In Go 1.6 and below, ignore the context. + return r +} + +// Go 1.6 doesn't have http.Response.Uncompressed, so we can't know whether the Go +// HTTP stack uncompressed a gzip file. As a good approximation, assume that +// the lack of a Content-Length header means that it did uncompress. +func goHTTPUncompressed(res *http.Response) bool { + return res.Header.Get("Content-Length") == "" +} diff --git a/vendor/cloud.google.com/go/storage/notifications.go b/vendor/cloud.google.com/go/storage/notifications.go new file mode 100644 index 0000000000..d5e1395510 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/notifications.go @@ -0,0 +1,188 @@ +// Copyright 2017 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 storage + +import ( + "errors" + "fmt" + "regexp" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + raw "google.golang.org/api/storage/v1" +) + +// A Notification describes how to send Cloud PubSub messages when certain +// events occur in a bucket. +type Notification struct { + //The ID of the notification. + ID string + + // The ID of the topic to which this subscription publishes. + TopicID string + + // The ID of the project to which the topic belongs. + TopicProjectID string + + // Only send notifications about listed event types. If empty, send notifications + // for all event types. + // See https://cloud.google.com/storage/docs/pubsub-notifications#events. + EventTypes []string + + // If present, only apply this notification configuration to object names that + // begin with this prefix. + ObjectNamePrefix string + + // An optional list of additional attributes to attach to each Cloud PubSub + // message published for this notification subscription. + CustomAttributes map[string]string + + // The contents of the message payload. + // See https://cloud.google.com/storage/docs/pubsub-notifications#payload. + PayloadFormat string +} + +// Values for Notification.PayloadFormat. +const ( + // Send no payload with notification messages. + NoPayload = "NONE" + + // Send object metadata as JSON with notification messages. + JSONPayload = "JSON_API_V1" +) + +// Values for Notification.EventTypes. +const ( + // Event that occurs when an object is successfully created. + ObjectFinalizeEvent = "OBJECT_FINALIZE" + + // Event that occurs when the metadata of an existing object changes. + ObjectMetadataUpdateEvent = "OBJECT_METADATA_UPDATE" + + // Event that occurs when an object is permanently deleted. + ObjectDeleteEvent = "OBJECT_DELETE" + + // Event that occurs when the live version of an object becomes an + // archived version. + ObjectArchiveEvent = "OBJECT_ARCHIVE" +) + +func toNotification(rn *raw.Notification) *Notification { + n := &Notification{ + ID: rn.Id, + EventTypes: rn.EventTypes, + ObjectNamePrefix: rn.ObjectNamePrefix, + CustomAttributes: rn.CustomAttributes, + PayloadFormat: rn.PayloadFormat, + } + n.TopicProjectID, n.TopicID = parseNotificationTopic(rn.Topic) + return n +} + +var topicRE = regexp.MustCompile("^//pubsub.googleapis.com/projects/([^/]+)/topics/([^/]+)") + +// parseNotificationTopic extracts the project and topic IDs from from the full +// resource name returned by the service. If the name is malformed, it returns +// "?" for both IDs. +func parseNotificationTopic(nt string) (projectID, topicID string) { + matches := topicRE.FindStringSubmatch(nt) + if matches == nil { + return "?", "?" + } + return matches[1], matches[2] +} + +func toRawNotification(n *Notification) *raw.Notification { + return &raw.Notification{ + Id: n.ID, + Topic: fmt.Sprintf("//pubsub.googleapis.com/projects/%s/topics/%s", + n.TopicProjectID, n.TopicID), + EventTypes: n.EventTypes, + ObjectNamePrefix: n.ObjectNamePrefix, + CustomAttributes: n.CustomAttributes, + PayloadFormat: string(n.PayloadFormat), + } +} + +// AddNotification adds a notification to b. You must set n's TopicProjectID, TopicID +// and PayloadFormat, and must not set its ID. The other fields are all optional. The +// returned Notification's ID can be used to refer to it. +func (b *BucketHandle) AddNotification(ctx context.Context, n *Notification) (ret *Notification, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.AddNotification") + defer func() { trace.EndSpan(ctx, err) }() + + if n.ID != "" { + return nil, errors.New("storage: AddNotification: ID must not be set") + } + if n.TopicProjectID == "" { + return nil, errors.New("storage: AddNotification: missing TopicProjectID") + } + if n.TopicID == "" { + return nil, errors.New("storage: AddNotification: missing TopicID") + } + call := b.c.raw.Notifications.Insert(b.name, toRawNotification(n)) + setClientHeader(call.Header()) + if b.userProject != "" { + call.UserProject(b.userProject) + } + rn, err := call.Context(ctx).Do() + if err != nil { + return nil, err + } + return toNotification(rn), nil +} + +// Notifications returns all the Notifications configured for this bucket, as a map +// indexed by notification ID. +func (b *BucketHandle) Notifications(ctx context.Context) (n map[string]*Notification, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Notifications") + defer func() { trace.EndSpan(ctx, err) }() + + call := b.c.raw.Notifications.List(b.name) + setClientHeader(call.Header()) + if b.userProject != "" { + call.UserProject(b.userProject) + } + var res *raw.Notifications + err = runWithRetry(ctx, func() error { + res, err = call.Context(ctx).Do() + return err + }) + if err != nil { + return nil, err + } + return notificationsToMap(res.Items), nil +} + +func notificationsToMap(rns []*raw.Notification) map[string]*Notification { + m := map[string]*Notification{} + for _, rn := range rns { + m[rn.Id] = toNotification(rn) + } + return m +} + +// DeleteNotification deletes the notification with the given ID. +func (b *BucketHandle) DeleteNotification(ctx context.Context, id string) (err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.DeleteNotification") + defer func() { trace.EndSpan(ctx, err) }() + + call := b.c.raw.Notifications.Delete(b.name, id) + setClientHeader(call.Header()) + if b.userProject != "" { + call.UserProject(b.userProject) + } + return call.Context(ctx).Do() +} diff --git a/vendor/cloud.google.com/go/storage/notifications_test.go b/vendor/cloud.google.com/go/storage/notifications_test.go new file mode 100644 index 0000000000..79900cb254 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/notifications_test.go @@ -0,0 +1,98 @@ +// Copyright 2017 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 storage + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + raw "google.golang.org/api/storage/v1" +) + +func TestParseNotificationTopic(t *testing.T) { + for _, test := range []struct { + in string + wantProjectID string + wantTopicID string + }{ + {"", "?", "?"}, + {"foobar", "?", "?"}, + {"//pubsub.googleapis.com/projects/foo", "?", "?"}, + {"//pubsub.googleapis.com/projects/my-project/topics/my-topic", + "my-project", "my-topic"}, + } { + gotProjectID, gotTopicID := parseNotificationTopic(test.in) + if gotProjectID != test.wantProjectID || gotTopicID != test.wantTopicID { + t.Errorf("%q: got (%q, %q), want (%q, %q)", + test.in, gotProjectID, gotTopicID, test.wantProjectID, test.wantTopicID) + } + } + +} + +func TestConvertNotification(t *testing.T) { + want := &Notification{ + ID: "id", + TopicProjectID: "my-project", + TopicID: "my-topic", + EventTypes: []string{ObjectFinalizeEvent}, + ObjectNamePrefix: "prefix", + CustomAttributes: map[string]string{"a": "b"}, + PayloadFormat: JSONPayload, + } + got := toNotification(toRawNotification(want)) + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("got=-, want=+:\n%s", diff) + } +} + +func TestNotificationsToMap(t *testing.T) { + got := notificationsToMap(nil) + want := map[string]*Notification{} + if !testutil.Equal(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } + + in := []*raw.Notification{ + {Id: "a", Topic: "//pubsub.googleapis.com/projects/P1/topics/T1"}, + {Id: "b", Topic: "//pubsub.googleapis.com/projects/P2/topics/T2"}, + {Id: "c", Topic: "//pubsub.googleapis.com/projects/P3/topics/T3"}, + } + got = notificationsToMap(in) + want = map[string]*Notification{ + "a": &Notification{ID: "a", TopicProjectID: "P1", TopicID: "T1"}, + "b": &Notification{ID: "b", TopicProjectID: "P2", TopicID: "T2"}, + "c": &Notification{ID: "c", TopicProjectID: "P3", TopicID: "T3"}, + } + if diff := testutil.Diff(got, want); diff != "" { + t.Errorf("got=-, want=+:\n%s", diff) + } +} + +func TestAddNotificationsErrors(t *testing.T) { + c := &Client{} + b := c.Bucket("b") + for _, n := range []*Notification{ + {ID: "foo", TopicProjectID: "p", TopicID: "t"}, // has ID + {TopicProjectID: "p"}, // missing TopicID + {TopicID: "t"}, // missing TopicProjectID + } { + _, err := b.AddNotification(context.Background(), n) + if err == nil { + t.Errorf("%+v: got nil, want error", n) + } + } +} diff --git a/vendor/cloud.google.com/go/storage/oc_test.go b/vendor/cloud.google.com/go/storage/oc_test.go new file mode 100644 index 0000000000..73c79c1e6e --- /dev/null +++ b/vendor/cloud.google.com/go/storage/oc_test.go @@ -0,0 +1,40 @@ +// 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. + +// +build go1.8 + +package storage + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" +) + +func TestIntegration_OCTracing(t *testing.T) { + ctx := context.Background() + client := testConfig(ctx, t) + defer client.Close() + + te := testutil.NewTestExporter() + defer te.Unregister() + + bkt := client.Bucket(bucketName) + bkt.Attrs(ctx) + + if len(te.Spans) == 0 { + t.Fatalf("Expected some spans to be created, but got %d", 0) + } +} diff --git a/vendor/cloud.google.com/go/storage/reader.go b/vendor/cloud.google.com/go/storage/reader.go new file mode 100644 index 0000000000..94fe98157e --- /dev/null +++ b/vendor/cloud.google.com/go/storage/reader.go @@ -0,0 +1,303 @@ +// 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 storage + +import ( + "errors" + "fmt" + "hash/crc32" + "io" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "strconv" + "strings" + + "cloud.google.com/go/internal/trace" + "golang.org/x/net/context" + "google.golang.org/api/googleapi" +) + +var crc32cTable = crc32.MakeTable(crc32.Castagnoli) + +// NewReader creates a new Reader to read the contents of the +// object. +// ErrObjectNotExist will be returned if the object is not found. +// +// The caller must call Close on the returned Reader when done reading. +func (o *ObjectHandle) NewReader(ctx context.Context) (*Reader, error) { + return o.NewRangeReader(ctx, 0, -1) +} + +// NewRangeReader reads part of an object, reading at most length bytes +// starting at the given offset. If length is negative, the object is read +// until the end. +func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64) (r *Reader, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.NewRangeReader") + defer func() { trace.EndSpan(ctx, err) }() + + if err := o.validate(); err != nil { + return nil, err + } + if offset < 0 { + return nil, fmt.Errorf("storage: invalid offset %d < 0", offset) + } + if o.conds != nil { + if err := o.conds.validate("NewRangeReader"); err != nil { + return nil, err + } + } + u := &url.URL{ + Scheme: "https", + Host: "storage.googleapis.com", + Path: fmt.Sprintf("/%s/%s", o.bucket, o.object), + RawQuery: conditionsQuery(o.gen, o.conds), + } + verb := "GET" + if length == 0 { + verb = "HEAD" + } + req, err := http.NewRequest(verb, u.String(), nil) + if err != nil { + return nil, err + } + req = withContext(req, ctx) + if o.userProject != "" { + req.Header.Set("X-Goog-User-Project", o.userProject) + } + if o.readCompressed { + req.Header.Set("Accept-Encoding", "gzip") + } + if err := setEncryptionHeaders(req.Header, o.encryptionKey, false); err != nil { + return nil, err + } + + // Define a function that initiates a Read with offset and length, assuming we + // have already read seen bytes. + reopen := func(seen int64) (*http.Response, error) { + start := offset + seen + if length < 0 && start > 0 { + req.Header.Set("Range", fmt.Sprintf("bytes=%d-", start)) + } else if length > 0 { + // The end character isn't affected by how many bytes we've seen. + req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, offset+length-1)) + } + var res *http.Response + err = runWithRetry(ctx, func() error { + res, err = o.c.hc.Do(req) + if err != nil { + return err + } + if res.StatusCode == http.StatusNotFound { + res.Body.Close() + return ErrObjectNotExist + } + if res.StatusCode < 200 || res.StatusCode > 299 { + body, _ := ioutil.ReadAll(res.Body) + res.Body.Close() + return &googleapi.Error{ + Code: res.StatusCode, + Header: res.Header, + Body: string(body), + } + } + if start > 0 && length != 0 && res.StatusCode != http.StatusPartialContent { + res.Body.Close() + return errors.New("storage: partial request not satisfied") + } + return nil + }) + if err != nil { + return nil, err + } + return res, nil + } + + res, err := reopen(0) + if err != nil { + return nil, err + } + var ( + size int64 // total size of object, even if a range was requested. + checkCRC bool + crc uint32 + ) + if res.StatusCode == http.StatusPartialContent { + cr := strings.TrimSpace(res.Header.Get("Content-Range")) + if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") { + + return nil, fmt.Errorf("storage: invalid Content-Range %q", cr) + } + size, err = strconv.ParseInt(cr[strings.LastIndex(cr, "/")+1:], 10, 64) + if err != nil { + return nil, fmt.Errorf("storage: invalid Content-Range %q", cr) + } + } else { + size = res.ContentLength + // Check the CRC iff all of the following hold: + // - We asked for content (length != 0). + // - We got all the content (status != PartialContent). + // - The server sent a CRC header. + // - The Go http stack did not uncompress the file. + // - We were not served compressed data that was uncompressed on download. + // The problem with the last two cases is that the CRC will not match -- GCS + // computes it on the compressed contents, but we compute it on the + // uncompressed contents. + if length != 0 && !goHTTPUncompressed(res) && !uncompressedByServer(res) { + crc, checkCRC = parseCRC32c(res) + } + } + + remain := res.ContentLength + body := res.Body + if length == 0 { + remain = 0 + body.Close() + body = emptyBody + } + return &Reader{ + body: body, + size: size, + remain: remain, + contentType: res.Header.Get("Content-Type"), + contentEncoding: res.Header.Get("Content-Encoding"), + cacheControl: res.Header.Get("Cache-Control"), + wantCRC: crc, + checkCRC: checkCRC, + reopen: reopen, + }, nil +} + +func uncompressedByServer(res *http.Response) bool { + // If the data is stored as gzip but is not encoded as gzip, then it + // was uncompressed by the server. + return res.Header.Get("X-Goog-Stored-Content-Encoding") == "gzip" && + res.Header.Get("Content-Encoding") != "gzip" +} + +func parseCRC32c(res *http.Response) (uint32, bool) { + const prefix = "crc32c=" + for _, spec := range res.Header["X-Goog-Hash"] { + if strings.HasPrefix(spec, prefix) { + c, err := decodeUint32(spec[len(prefix):]) + if err == nil { + return c, true + } + } + } + return 0, false +} + +var emptyBody = ioutil.NopCloser(strings.NewReader("")) + +// Reader reads a Cloud Storage object. +// It implements io.Reader. +// +// Typically, a Reader computes the CRC of the downloaded content and compares it to +// the stored CRC, returning an error from Read if there is a mismatch. This integrity check +// is skipped if transcoding occurs. See https://cloud.google.com/storage/docs/transcoding. +type Reader struct { + body io.ReadCloser + seen, remain, size int64 + contentType string + contentEncoding string + cacheControl string + checkCRC bool // should we check the CRC? + wantCRC uint32 // the CRC32c value the server sent in the header + gotCRC uint32 // running crc + reopen func(seen int64) (*http.Response, error) +} + +// Close closes the Reader. It must be called when done reading. +func (r *Reader) Close() error { + return r.body.Close() +} + +func (r *Reader) Read(p []byte) (int, error) { + n, err := r.readWithRetry(p) + if r.remain != -1 { + r.remain -= int64(n) + } + if r.checkCRC { + r.gotCRC = crc32.Update(r.gotCRC, crc32cTable, p[:n]) + // Check CRC here. It would be natural to check it in Close, but + // everybody defers Close on the assumption that it doesn't return + // anything worth looking at. + if err == io.EOF { + if r.gotCRC != r.wantCRC { + return n, fmt.Errorf("storage: bad CRC on read: got %d, want %d", + r.gotCRC, r.wantCRC) + } + } + } + return n, err +} + +func (r *Reader) readWithRetry(p []byte) (int, error) { + n := 0 + for len(p[n:]) > 0 { + m, err := r.body.Read(p[n:]) + n += m + r.seen += int64(m) + if !shouldRetryRead(err) { + return n, err + } + // Read failed, but we will try again. Send a ranged read request that takes + // into account the number of bytes we've already seen. + res, err := r.reopen(r.seen) + if err != nil { + // reopen already retries + return n, err + } + r.body.Close() + r.body = res.Body + } + return n, nil +} + +func shouldRetryRead(err error) bool { + if err == nil { + return false + } + return strings.HasSuffix(err.Error(), "INTERNAL_ERROR") && strings.Contains(reflect.TypeOf(err).String(), "http2") +} + +// Size returns the size of the object in bytes. +// The returned value is always the same and is not affected by +// calls to Read or Close. +func (r *Reader) Size() int64 { + return r.size +} + +// Remain returns the number of bytes left to read, or -1 if unknown. +func (r *Reader) Remain() int64 { + return r.remain +} + +// ContentType returns the content type of the object. +func (r *Reader) ContentType() string { + return r.contentType +} + +// ContentEncoding returns the content encoding of the object. +func (r *Reader) ContentEncoding() string { + return r.contentEncoding +} + +// CacheControl returns the cache control of the object. +func (r *Reader) CacheControl() string { + return r.cacheControl +} diff --git a/vendor/cloud.google.com/go/storage/reader_test.go b/vendor/cloud.google.com/go/storage/reader_test.go new file mode 100644 index 0000000000..a4e30107dc --- /dev/null +++ b/vendor/cloud.google.com/go/storage/reader_test.go @@ -0,0 +1,297 @@ +// 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 storage + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "strconv" + "strings" + "testing" + + "golang.org/x/net/context" + "google.golang.org/api/option" +) + +const readData = "0123456789" + +func TestRangeReader(t *testing.T) { + hc, close := newTestServer(handleRangeRead) + defer close() + ctx := context.Background() + c, err := NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatal(err) + } + obj := c.Bucket("b").Object("o") + for _, test := range []struct { + offset, length int64 + want string + }{ + {0, -1, readData}, + {0, 10, readData}, + {0, 5, readData[:5]}, + {1, 3, readData[1:4]}, + {6, -1, readData[6:]}, + {4, 20, readData[4:]}, + } { + r, err := obj.NewRangeReader(ctx, test.offset, test.length) + if err != nil { + t.Errorf("%d/%d: %v", test.offset, test.length, err) + continue + } + gotb, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("%d/%d: %v", test.offset, test.length, err) + continue + } + if got := string(gotb); got != test.want { + t.Errorf("%d/%d: got %q, want %q", test.offset, test.length, got, test.want) + } + } +} + +func handleRangeRead(w http.ResponseWriter, r *http.Request) { + rh := strings.TrimSpace(r.Header.Get("Range")) + data := readData + var from, to int + if rh == "" { + from = 0 + to = len(data) + } else { + // assume "bytes=N-" or "bytes=N-M" + var err error + i := strings.IndexRune(rh, '=') + j := strings.IndexRune(rh, '-') + from, err = strconv.Atoi(rh[i+1 : j]) + if err != nil { + w.WriteHeader(500) + return + } + to = len(data) + if j+1 < len(rh) { + to, err = strconv.Atoi(rh[j+1:]) + if err != nil { + w.WriteHeader(500) + return + } + to++ // Range header is inclusive, Go slice is exclusive + } + if from >= len(data) && to != from { + w.WriteHeader(416) + return + } + if from > len(data) { + from = len(data) + } + if to > len(data) { + to = len(data) + } + } + data = data[from:to] + if data != readData { + w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", from, to-1, len(readData))) + w.WriteHeader(http.StatusPartialContent) + } + if _, err := w.Write([]byte(data)); err != nil { + panic(err) + } +} + +type http2Error string + +func (h http2Error) Error() string { + return string(h) +} + +func TestRangeReaderRetry(t *testing.T) { + retryErr := http2Error("blah blah INTERNAL_ERROR") + readBytes := []byte(readData) + hc, close := newTestServer(handleRangeRead) + defer close() + ctx := context.Background() + c, err := NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatal(err) + } + + obj := c.Bucket("b").Object("o") + for i, test := range []struct { + offset, length int64 + bodies []fakeReadCloser + want string + }{ + { + offset: 0, + length: -1, + bodies: []fakeReadCloser{ + {data: readBytes, counts: []int{10}, err: io.EOF}, + }, + want: readData, + }, + { + offset: 0, + length: -1, + bodies: []fakeReadCloser{ + {data: readBytes, counts: []int{3}, err: retryErr}, + {data: readBytes[3:], counts: []int{5, 2}, err: io.EOF}, + }, + want: readData, + }, + { + offset: 0, + length: -1, + bodies: []fakeReadCloser{ + {data: readBytes, counts: []int{5}, err: retryErr}, + {data: readBytes[5:], counts: []int{1, 3}, err: retryErr}, + {data: readBytes[9:], counts: []int{1}, err: io.EOF}, + }, + want: readData, + }, + { + offset: 0, + length: 5, + bodies: []fakeReadCloser{ + {data: readBytes, counts: []int{3}, err: retryErr}, + {data: readBytes[3:], counts: []int{2}, err: io.EOF}, + }, + want: readData[:5], + }, + { + offset: 1, + length: 5, + bodies: []fakeReadCloser{ + {data: readBytes, counts: []int{3}, err: retryErr}, + {data: readBytes[3:], counts: []int{2}, err: io.EOF}, + }, + want: readData[:5], + }, + { + offset: 1, + length: 3, + bodies: []fakeReadCloser{ + {data: readBytes[1:], counts: []int{1}, err: retryErr}, + {data: readBytes[2:], counts: []int{2}, err: io.EOF}, + }, + want: readData[1:4], + }, + { + offset: 4, + length: -1, + bodies: []fakeReadCloser{ + {data: readBytes[4:], counts: []int{1}, err: retryErr}, + {data: readBytes[5:], counts: []int{4}, err: retryErr}, + {data: readBytes[9:], counts: []int{1}, err: io.EOF}, + }, + want: readData[4:], + }, + } { + r, err := obj.NewRangeReader(ctx, test.offset, test.length) + if err != nil { + t.Errorf("#%d: %v", i, err) + continue + } + r.body = &test.bodies[0] + b := 0 + r.reopen = func(int64) (*http.Response, error) { + b++ + return &http.Response{Body: &test.bodies[b]}, nil + } + buf := make([]byte, len(readData)/2) + var gotb []byte + for { + n, err := r.Read(buf) + gotb = append(gotb, buf[:n]...) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("#%d: %v", i, err) + } + } + if err != nil { + t.Errorf("#%d: %v", i, err) + continue + } + if got := string(gotb); got != test.want { + t.Errorf("#%d: got %q, want %q", i, got, test.want) + } + } +} + +type fakeReadCloser struct { + data []byte + counts []int // how much of data to deliver on each read + err error // error to return with last count + + d int // current position in data + c int // current position in counts +} + +func (f *fakeReadCloser) Close() error { + return nil +} + +func (f *fakeReadCloser) Read(buf []byte) (int, error) { + i := f.c + n := 0 + if i < len(f.counts) { + n = f.counts[i] + } + var err error + if i >= len(f.counts)-1 { + err = f.err + } + copy(buf, f.data[f.d:f.d+n]) + if len(buf) < n { + n = len(buf) + f.counts[i] -= n + err = nil + } else { + f.c++ + } + f.d += n + return n, err +} + +func TestFakeReadCloser(t *testing.T) { + e := errors.New("") + f := &fakeReadCloser{ + data: []byte(readData), + counts: []int{1, 2, 3}, + err: e, + } + wants := []string{"0", "12", "345"} + buf := make([]byte, 10) + for i := 0; i < 3; i++ { + n, err := f.Read(buf) + if got, want := n, f.counts[i]; got != want { + t.Fatalf("i=%d: got %d, want %d", i, got, want) + } + var wantErr error + if i == 2 { + wantErr = e + } + if err != wantErr { + t.Fatalf("i=%d: got error %v, want %v", i, err, wantErr) + } + if got, want := string(buf[:n]), wants[i]; got != want { + t.Fatalf("i=%d: got %q, want %q", i, got, want) + } + } +} diff --git a/vendor/cloud.google.com/go/storage/storage.go b/vendor/cloud.google.com/go/storage/storage.go new file mode 100644 index 0000000000..dbf286d689 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/storage.go @@ -0,0 +1,1076 @@ +// 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 storage + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" + + "cloud.google.com/go/internal/trace" + "google.golang.org/api/option" + htransport "google.golang.org/api/transport/http" + + "cloud.google.com/go/internal/optional" + "cloud.google.com/go/internal/version" + "golang.org/x/net/context" + "google.golang.org/api/googleapi" + raw "google.golang.org/api/storage/v1" +) + +var ( + ErrBucketNotExist = errors.New("storage: bucket doesn't exist") + ErrObjectNotExist = errors.New("storage: object doesn't exist") +) + +const userAgent = "gcloud-golang-storage/20151204" + +const ( + // ScopeFullControl grants permissions to manage your + // data and permissions in Google Cloud Storage. + ScopeFullControl = raw.DevstorageFullControlScope + + // ScopeReadOnly grants permissions to + // view your data in Google Cloud Storage. + ScopeReadOnly = raw.DevstorageReadOnlyScope + + // ScopeReadWrite grants permissions to manage your + // data in Google Cloud Storage. + ScopeReadWrite = raw.DevstorageReadWriteScope +) + +var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo) + +func setClientHeader(headers http.Header) { + headers.Set("x-goog-api-client", xGoogHeader) +} + +// Client is a client for interacting with Google Cloud Storage. +// +// Clients should be reused instead of created as needed. +// The methods of Client are safe for concurrent use by multiple goroutines. +type Client struct { + hc *http.Client + raw *raw.Service +} + +// NewClient creates a new Google Cloud Storage client. +// The default scope is ScopeFullControl. To use a different scope, like ScopeReadOnly, use option.WithScopes. +func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { + o := []option.ClientOption{ + option.WithScopes(ScopeFullControl), + option.WithUserAgent(userAgent), + } + opts = append(o, opts...) + hc, ep, err := htransport.NewClient(ctx, opts...) + if err != nil { + return nil, fmt.Errorf("dialing: %v", err) + } + rawService, err := raw.New(hc) + if err != nil { + return nil, fmt.Errorf("storage client: %v", err) + } + if ep != "" { + rawService.BasePath = ep + } + return &Client{ + hc: hc, + raw: rawService, + }, nil +} + +// Close closes the Client. +// +// Close need not be called at program exit. +func (c *Client) Close() error { + // Set fields to nil so that subsequent uses + // will panic. + c.hc = nil + c.raw = nil + return nil +} + +// SignedURLOptions allows you to restrict the access to the signed URL. +type SignedURLOptions struct { + // GoogleAccessID represents the authorizer of the signed URL generation. + // It is typically the Google service account client email address from + // the Google Developers Console in the form of "xxx@developer.gserviceaccount.com". + // Required. + GoogleAccessID string + + // PrivateKey is the Google service account private key. It is obtainable + // from the Google Developers Console. + // At https://console.developers.google.com/project//apiui/credential, + // create a service account client ID or reuse one of your existing service account + // credentials. Click on the "Generate new P12 key" to generate and download + // a new private key. Once you download the P12 file, use the following command + // to convert it into a PEM file. + // + // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes + // + // Provide the contents of the PEM file as a byte slice. + // Exactly one of PrivateKey or SignBytes must be non-nil. + PrivateKey []byte + + // SignBytes is a function for implementing custom signing. + // If your application is running on Google App Engine, you can use appengine's internal signing function: + // ctx := appengine.NewContext(request) + // acc, _ := appengine.ServiceAccount(ctx) + // url, err := SignedURL("bucket", "object", &SignedURLOptions{ + // GoogleAccessID: acc, + // SignBytes: func(b []byte) ([]byte, error) { + // _, signedBytes, err := appengine.SignBytes(ctx, b) + // return signedBytes, err + // }, + // // etc. + // }) + // + // Exactly one of PrivateKey or SignBytes must be non-nil. + SignBytes func([]byte) ([]byte, error) + + // Method is the HTTP method to be used with the signed URL. + // Signed URLs can be used with GET, HEAD, PUT, and DELETE requests. + // Required. + Method string + + // Expires is the expiration time on the signed URL. It must be + // a datetime in the future. + // Required. + Expires time.Time + + // ContentType is the content type header the client must provide + // to use the generated signed URL. + // Optional. + ContentType string + + // Headers is a list of extension headers the client must provide + // in order to use the generated signed URL. + // Optional. + Headers []string + + // MD5 is the base64 encoded MD5 checksum of the file. + // If provided, the client should provide the exact value on the request + // header in order to use the signed URL. + // Optional. + MD5 string +} + +var ( + canonicalHeaderRegexp = regexp.MustCompile(`(?i)^(x-goog-[^:]+):(.*)?$`) + excludedCanonicalHeaders = map[string]bool{ + "x-goog-encryption-key": true, + "x-goog-encryption-key-sha256": true, + } +) + +// sanitizeHeaders applies the specifications for canonical extension headers at +// https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers. +func sanitizeHeaders(hdrs []string) []string { + headerMap := map[string][]string{} + for _, hdr := range hdrs { + // No leading or trailing whitespaces. + sanitizedHeader := strings.TrimSpace(hdr) + + // Only keep canonical headers, discard any others. + headerMatches := canonicalHeaderRegexp.FindStringSubmatch(sanitizedHeader) + if len(headerMatches) == 0 { + continue + } + + header := strings.ToLower(strings.TrimSpace(headerMatches[1])) + if excludedCanonicalHeaders[headerMatches[1]] { + // Do not keep any deliberately excluded canonical headers when signing. + continue + } + value := strings.TrimSpace(headerMatches[2]) + if len(value) > 0 { + // Remove duplicate headers by appending the values of duplicates + // in their order of appearance. + headerMap[header] = append(headerMap[header], value) + } + } + + var sanitizedHeaders []string + for header, values := range headerMap { + // There should be no spaces around the colon separating the + // header name from the header value or around the values + // themselves. The values should be separated by commas. + // NOTE: The semantics for headers without a value are not clear. + // However from specifications these should be edge-cases + // anyway and we should assume that there will be no + // canonical headers using empty values. Any such headers + // are discarded at the regexp stage above. + sanitizedHeaders = append( + sanitizedHeaders, + fmt.Sprintf("%s:%s", header, strings.Join(values, ",")), + ) + } + sort.Strings(sanitizedHeaders) + return sanitizedHeaders +} + +// SignedURL returns a URL for the specified object. Signed URLs allow +// the users access to a restricted resource for a limited time without having a +// Google account or signing in. For more information about the signed +// URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs. +func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) { + if opts == nil { + return "", errors.New("storage: missing required SignedURLOptions") + } + if opts.GoogleAccessID == "" { + return "", errors.New("storage: missing required GoogleAccessID") + } + if (opts.PrivateKey == nil) == (opts.SignBytes == nil) { + return "", errors.New("storage: exactly one of PrivateKey or SignedBytes must be set") + } + if opts.Method == "" { + return "", errors.New("storage: missing required method option") + } + if opts.Expires.IsZero() { + return "", errors.New("storage: missing required expires option") + } + if opts.MD5 != "" { + md5, err := base64.StdEncoding.DecodeString(opts.MD5) + if err != nil || len(md5) != 16 { + return "", errors.New("storage: invalid MD5 checksum") + } + } + opts.Headers = sanitizeHeaders(opts.Headers) + + signBytes := opts.SignBytes + if opts.PrivateKey != nil { + key, err := parseKey(opts.PrivateKey) + if err != nil { + return "", err + } + signBytes = func(b []byte) ([]byte, error) { + sum := sha256.Sum256(b) + return rsa.SignPKCS1v15( + rand.Reader, + key, + crypto.SHA256, + sum[:], + ) + } + } + + u := &url.URL{ + Path: fmt.Sprintf("/%s/%s", bucket, name), + } + + buf := &bytes.Buffer{} + fmt.Fprintf(buf, "%s\n", opts.Method) + fmt.Fprintf(buf, "%s\n", opts.MD5) + fmt.Fprintf(buf, "%s\n", opts.ContentType) + fmt.Fprintf(buf, "%d\n", opts.Expires.Unix()) + if len(opts.Headers) > 0 { + fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n")) + } + fmt.Fprintf(buf, "%s", u.String()) + + b, err := signBytes(buf.Bytes()) + if err != nil { + return "", err + } + encoded := base64.StdEncoding.EncodeToString(b) + u.Scheme = "https" + u.Host = "storage.googleapis.com" + q := u.Query() + q.Set("GoogleAccessId", opts.GoogleAccessID) + q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix())) + q.Set("Signature", string(encoded)) + u.RawQuery = q.Encode() + return u.String(), nil +} + +// ObjectHandle provides operations on an object in a Google Cloud Storage bucket. +// Use BucketHandle.Object to get a handle. +type ObjectHandle struct { + c *Client + bucket string + object string + acl ACLHandle + gen int64 // a negative value indicates latest + conds *Conditions + encryptionKey []byte // AES-256 key + userProject string // for requester-pays buckets + readCompressed bool // Accept-Encoding: gzip +} + +// ACL provides access to the object's access control list. +// This controls who can read and write this object. +// This call does not perform any network operations. +func (o *ObjectHandle) ACL() *ACLHandle { + return &o.acl +} + +// Generation returns a new ObjectHandle that operates on a specific generation +// of the object. +// By default, the handle operates on the latest generation. Not +// all operations work when given a specific generation; check the API +// endpoints at https://cloud.google.com/storage/docs/json_api/ for details. +func (o *ObjectHandle) Generation(gen int64) *ObjectHandle { + o2 := *o + o2.gen = gen + return &o2 +} + +// If returns a new ObjectHandle that applies a set of preconditions. +// Preconditions already set on the ObjectHandle are ignored. +// Operations on the new handle will return an error if the preconditions are not +// satisfied. See https://cloud.google.com/storage/docs/generations-preconditions +// for more details. +func (o *ObjectHandle) If(conds Conditions) *ObjectHandle { + o2 := *o + o2.conds = &conds + return &o2 +} + +// Key returns a new ObjectHandle that uses the supplied encryption +// key to encrypt and decrypt the object's contents. +// +// Encryption key must be a 32-byte AES-256 key. +// See https://cloud.google.com/storage/docs/encryption for details. +func (o *ObjectHandle) Key(encryptionKey []byte) *ObjectHandle { + o2 := *o + o2.encryptionKey = encryptionKey + return &o2 +} + +// Attrs returns meta information about the object. +// ErrObjectNotExist will be returned if the object is not found. +func (o *ObjectHandle) Attrs(ctx context.Context) (attrs *ObjectAttrs, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Attrs") + defer func() { trace.EndSpan(ctx, err) }() + + if err := o.validate(); err != nil { + return nil, err + } + call := o.c.raw.Objects.Get(o.bucket, o.object).Projection("full").Context(ctx) + if err := applyConds("Attrs", o.gen, o.conds, call); err != nil { + return nil, err + } + if o.userProject != "" { + call.UserProject(o.userProject) + } + if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil { + return nil, err + } + var obj *raw.Object + setClientHeader(call.Header()) + err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err }) + if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { + return nil, ErrObjectNotExist + } + if err != nil { + return nil, err + } + return newObject(obj), nil +} + +// Update updates an object with the provided attributes. +// All zero-value attributes are ignored. +// ErrObjectNotExist will be returned if the object is not found. +func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (oa *ObjectAttrs, err error) { + ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Update") + defer func() { trace.EndSpan(ctx, err) }() + + if err := o.validate(); err != nil { + return nil, err + } + var attrs ObjectAttrs + // Lists of fields to send, and set to null, in the JSON. + var forceSendFields, nullFields []string + if uattrs.ContentType != nil { + attrs.ContentType = optional.ToString(uattrs.ContentType) + // For ContentType, sending the empty string is a no-op. + // Instead we send a null. + if attrs.ContentType == "" { + nullFields = append(nullFields, "ContentType") + } else { + forceSendFields = append(forceSendFields, "ContentType") + } + } + if uattrs.ContentLanguage != nil { + attrs.ContentLanguage = optional.ToString(uattrs.ContentLanguage) + // For ContentLanguage it's an error to send the empty string. + // Instead we send a null. + if attrs.ContentLanguage == "" { + nullFields = append(nullFields, "ContentLanguage") + } else { + forceSendFields = append(forceSendFields, "ContentLanguage") + } + } + if uattrs.ContentEncoding != nil { + attrs.ContentEncoding = optional.ToString(uattrs.ContentEncoding) + forceSendFields = append(forceSendFields, "ContentEncoding") + } + if uattrs.ContentDisposition != nil { + attrs.ContentDisposition = optional.ToString(uattrs.ContentDisposition) + forceSendFields = append(forceSendFields, "ContentDisposition") + } + if uattrs.CacheControl != nil { + attrs.CacheControl = optional.ToString(uattrs.CacheControl) + forceSendFields = append(forceSendFields, "CacheControl") + } + if uattrs.Metadata != nil { + attrs.Metadata = uattrs.Metadata + if len(attrs.Metadata) == 0 { + // Sending the empty map is a no-op. We send null instead. + nullFields = append(nullFields, "Metadata") + } else { + forceSendFields = append(forceSendFields, "Metadata") + } + } + if uattrs.ACL != nil { + attrs.ACL = uattrs.ACL + // It's an error to attempt to delete the ACL, so + // we don't append to nullFields here. + forceSendFields = append(forceSendFields, "Acl") + } + rawObj := attrs.toRawObject(o.bucket) + rawObj.ForceSendFields = forceSendFields + rawObj.NullFields = nullFields + call := o.c.raw.Objects.Patch(o.bucket, o.object, rawObj).Projection("full").Context(ctx) + if err := applyConds("Update", o.gen, o.conds, call); err != nil { + return nil, err + } + if o.userProject != "" { + call.UserProject(o.userProject) + } + if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil { + return nil, err + } + var obj *raw.Object + setClientHeader(call.Header()) + err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err }) + if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { + return nil, ErrObjectNotExist + } + if err != nil { + return nil, err + } + return newObject(obj), nil +} + +// ObjectAttrsToUpdate is used to update the attributes of an object. +// Only fields set to non-nil values will be updated. +// Set a field to its zero value to delete it. +// +// For example, to change ContentType and delete ContentEncoding and +// Metadata, use +// ObjectAttrsToUpdate{ +// ContentType: "text/html", +// ContentEncoding: "", +// Metadata: map[string]string{}, +// } +type ObjectAttrsToUpdate struct { + ContentType optional.String + ContentLanguage optional.String + ContentEncoding optional.String + ContentDisposition optional.String + CacheControl optional.String + Metadata map[string]string // set to map[string]string{} to delete + ACL []ACLRule +} + +// Delete deletes the single specified object. +func (o *ObjectHandle) Delete(ctx context.Context) error { + if err := o.validate(); err != nil { + return err + } + call := o.c.raw.Objects.Delete(o.bucket, o.object).Context(ctx) + if err := applyConds("Delete", o.gen, o.conds, call); err != nil { + return err + } + if o.userProject != "" { + call.UserProject(o.userProject) + } + // Encryption doesn't apply to Delete. + setClientHeader(call.Header()) + err := runWithRetry(ctx, func() error { return call.Do() }) + switch e := err.(type) { + case nil: + return nil + case *googleapi.Error: + if e.Code == http.StatusNotFound { + return ErrObjectNotExist + } + } + return err +} + +// ReadCompressed when true causes the read to happen without decompressing. +func (o *ObjectHandle) ReadCompressed(compressed bool) *ObjectHandle { + o2 := *o + o2.readCompressed = compressed + return &o2 +} + +// NewWriter returns a storage Writer that writes to the GCS object +// associated with this ObjectHandle. +// +// A new object will be created unless an object with this name already exists. +// Otherwise any previous object with the same name will be replaced. +// The object will not be available (and any previous object will remain) +// until Close has been called. +// +// Attributes can be set on the object by modifying the returned Writer's +// ObjectAttrs field before the first call to Write. If no ContentType +// attribute is specified, the content type will be automatically sniffed +// using net/http.DetectContentType. +// +// It is the caller's responsibility to call Close when writing is done. +func (o *ObjectHandle) NewWriter(ctx context.Context) *Writer { + return &Writer{ + ctx: ctx, + o: o, + donec: make(chan struct{}), + ObjectAttrs: ObjectAttrs{Name: o.object}, + ChunkSize: googleapi.DefaultUploadChunkSize, + } +} + +func (o *ObjectHandle) validate() error { + if o.bucket == "" { + return errors.New("storage: bucket name is empty") + } + if o.object == "" { + return errors.New("storage: object name is empty") + } + if !utf8.ValidString(o.object) { + return fmt.Errorf("storage: object name %q is not valid UTF-8", o.object) + } + return nil +} + +// parseKey converts the binary contents of a private key file to an +// *rsa.PrivateKey. It detects whether the private key is in a PEM container or +// not. If so, it extracts the private key from PEM container before +// conversion. It only supports PEM containers with no passphrase. +func parseKey(key []byte) (*rsa.PrivateKey, error) { + if block, _ := pem.Decode(key); block != nil { + key = block.Bytes + } + parsedKey, err := x509.ParsePKCS8PrivateKey(key) + if err != nil { + parsedKey, err = x509.ParsePKCS1PrivateKey(key) + if err != nil { + return nil, err + } + } + parsed, ok := parsedKey.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("oauth2: private key is invalid") + } + return parsed, nil +} + +func toRawObjectACL(oldACL []ACLRule) []*raw.ObjectAccessControl { + var acl []*raw.ObjectAccessControl + if len(oldACL) > 0 { + acl = make([]*raw.ObjectAccessControl, len(oldACL)) + for i, rule := range oldACL { + acl[i] = &raw.ObjectAccessControl{ + Entity: string(rule.Entity), + Role: string(rule.Role), + } + } + } + return acl +} + +// toRawObject copies the editable attributes from o to the raw library's Object type. +func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object { + acl := toRawObjectACL(o.ACL) + return &raw.Object{ + Bucket: bucket, + Name: o.Name, + ContentType: o.ContentType, + ContentEncoding: o.ContentEncoding, + ContentLanguage: o.ContentLanguage, + CacheControl: o.CacheControl, + ContentDisposition: o.ContentDisposition, + StorageClass: o.StorageClass, + Acl: acl, + Metadata: o.Metadata, + } +} + +// ObjectAttrs represents the metadata for a Google Cloud Storage (GCS) object. +type ObjectAttrs struct { + // Bucket is the name of the bucket containing this GCS object. + // This field is read-only. + Bucket string + + // Name is the name of the object within the bucket. + // This field is read-only. + Name string + + // ContentType is the MIME type of the object's content. + ContentType string + + // ContentLanguage is the content language of the object's content. + ContentLanguage string + + // CacheControl is the Cache-Control header to be sent in the response + // headers when serving the object data. + CacheControl string + + // ACL is the list of access control rules for the object. + ACL []ACLRule + + // Owner is the owner of the object. This field is read-only. + // + // If non-zero, it is in the form of "user-". + Owner string + + // Size is the length of the object's content. This field is read-only. + Size int64 + + // ContentEncoding is the encoding of the object's content. + ContentEncoding string + + // ContentDisposition is the optional Content-Disposition header of the object + // sent in the response headers. + ContentDisposition string + + // MD5 is the MD5 hash of the object's content. This field is read-only, + // except when used from a Writer. If set on a Writer, the uploaded + // data is rejected if its MD5 hash does not match this field. + MD5 []byte + + // CRC32C is the CRC32 checksum of the object's content using + // the Castagnoli93 polynomial. This field is read-only, except when + // used from a Writer. If set on a Writer and Writer.SendCRC32C + // is true, the uploaded data is rejected if its CRC32c hash does not + // match this field. + CRC32C uint32 + + // MediaLink is an URL to the object's content. This field is read-only. + MediaLink string + + // Metadata represents user-provided metadata, in key/value pairs. + // It can be nil if no metadata is provided. + Metadata map[string]string + + // Generation is the generation number of the object's content. + // This field is read-only. + Generation int64 + + // Metageneration is the version of the metadata for this + // object at this generation. This field is used for preconditions + // and for detecting changes in metadata. A metageneration number + // is only meaningful in the context of a particular generation + // of a particular object. This field is read-only. + Metageneration int64 + + // StorageClass is the storage class of the object. + // This value defines how objects in the bucket are stored and + // determines the SLA and the cost of storage. Typical values are + // "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" + // and "DURABLE_REDUCED_AVAILABILITY". + // It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL" + // or "REGIONAL" depending on the bucket's location settings. + StorageClass string + + // Created is the time the object was created. This field is read-only. + Created time.Time + + // Deleted is the time the object was deleted. + // If not deleted, it is the zero value. This field is read-only. + Deleted time.Time + + // Updated is the creation or modification time of the object. + // For buckets with versioning enabled, changing an object's + // metadata does not change this property. This field is read-only. + Updated time.Time + + // CustomerKeySHA256 is the base64-encoded SHA-256 hash of the + // customer-supplied encryption key for the object. It is empty if there is + // no customer-supplied encryption key. + // See // https://cloud.google.com/storage/docs/encryption for more about + // encryption in Google Cloud Storage. + CustomerKeySHA256 string + + // Cloud KMS key name, in the form + // projects/P/locations/L/keyRings/R/cryptoKeys/K, used to encrypt this object, + // if the object is encrypted by such a key. + // + // Providing both a KMSKeyName and a customer-supplied encryption key (via + // ObjectHandle.Key) will result in an error when writing an object. + KMSKeyName string + + // Prefix is set only for ObjectAttrs which represent synthetic "directory + // entries" when iterating over buckets using Query.Delimiter. See + // ObjectIterator.Next. When set, no other fields in ObjectAttrs will be + // populated. + Prefix string +} + +// convertTime converts a time in RFC3339 format to time.Time. +// If any error occurs in parsing, the zero-value time.Time is silently returned. +func convertTime(t string) time.Time { + var r time.Time + if t != "" { + r, _ = time.Parse(time.RFC3339, t) + } + return r +} + +func newObject(o *raw.Object) *ObjectAttrs { + if o == nil { + return nil + } + acl := make([]ACLRule, len(o.Acl)) + for i, rule := range o.Acl { + acl[i] = ACLRule{ + Entity: ACLEntity(rule.Entity), + Role: ACLRole(rule.Role), + } + } + owner := "" + if o.Owner != nil { + owner = o.Owner.Entity + } + md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash) + crc32c, _ := decodeUint32(o.Crc32c) + var sha256 string + if o.CustomerEncryption != nil { + sha256 = o.CustomerEncryption.KeySha256 + } + return &ObjectAttrs{ + Bucket: o.Bucket, + Name: o.Name, + ContentType: o.ContentType, + ContentLanguage: o.ContentLanguage, + CacheControl: o.CacheControl, + ACL: acl, + Owner: owner, + ContentEncoding: o.ContentEncoding, + ContentDisposition: o.ContentDisposition, + Size: int64(o.Size), + MD5: md5, + CRC32C: crc32c, + MediaLink: o.MediaLink, + Metadata: o.Metadata, + Generation: o.Generation, + Metageneration: o.Metageneration, + StorageClass: o.StorageClass, + CustomerKeySHA256: sha256, + KMSKeyName: o.KmsKeyName, + Created: convertTime(o.TimeCreated), + Deleted: convertTime(o.TimeDeleted), + Updated: convertTime(o.Updated), + } +} + +// Decode a uint32 encoded in Base64 in big-endian byte order. +func decodeUint32(b64 string) (uint32, error) { + d, err := base64.StdEncoding.DecodeString(b64) + if err != nil { + return 0, err + } + if len(d) != 4 { + return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d) + } + return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil +} + +// Encode a uint32 as Base64 in big-endian byte order. +func encodeUint32(u uint32) string { + b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)} + return base64.StdEncoding.EncodeToString(b) +} + +// Query represents a query to filter objects from a bucket. +type Query struct { + // Delimiter returns results in a directory-like fashion. + // Results will contain only objects whose names, aside from the + // prefix, do not contain delimiter. Objects whose names, + // aside from the prefix, contain delimiter will have their name, + // truncated after the delimiter, returned in prefixes. + // Duplicate prefixes are omitted. + // Optional. + Delimiter string + + // Prefix is the prefix filter to query objects + // whose names begin with this prefix. + // Optional. + Prefix string + + // Versions indicates whether multiple versions of the same + // object will be included in the results. + Versions bool +} + +// contentTyper implements ContentTyper to enable an +// io.ReadCloser to specify its MIME type. +type contentTyper struct { + io.Reader + t string +} + +func (c *contentTyper) ContentType() string { + return c.t +} + +// Conditions constrain methods to act on specific generations of +// objects. +// +// The zero value is an empty set of constraints. Not all conditions or +// combinations of conditions are applicable to all methods. +// See https://cloud.google.com/storage/docs/generations-preconditions +// for details on how these operate. +type Conditions struct { + // Generation constraints. + // At most one of the following can be set to a non-zero value. + + // GenerationMatch specifies that the object must have the given generation + // for the operation to occur. + // If GenerationMatch is zero, it has no effect. + // Use DoesNotExist to specify that the object does not exist in the bucket. + GenerationMatch int64 + + // GenerationNotMatch specifies that the object must not have the given + // generation for the operation to occur. + // If GenerationNotMatch is zero, it has no effect. + GenerationNotMatch int64 + + // DoesNotExist specifies that the object must not exist in the bucket for + // the operation to occur. + // If DoesNotExist is false, it has no effect. + DoesNotExist bool + + // Metadata generation constraints. + // At most one of the following can be set to a non-zero value. + + // MetagenerationMatch specifies that the object must have the given + // metageneration for the operation to occur. + // If MetagenerationMatch is zero, it has no effect. + MetagenerationMatch int64 + + // MetagenerationNotMatch specifies that the object must not have the given + // metageneration for the operation to occur. + // If MetagenerationNotMatch is zero, it has no effect. + MetagenerationNotMatch int64 +} + +func (c *Conditions) validate(method string) error { + if *c == (Conditions{}) { + return fmt.Errorf("storage: %s: empty conditions", method) + } + if !c.isGenerationValid() { + return fmt.Errorf("storage: %s: multiple conditions specified for generation", method) + } + if !c.isMetagenerationValid() { + return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method) + } + return nil +} + +func (c *Conditions) isGenerationValid() bool { + n := 0 + if c.GenerationMatch != 0 { + n++ + } + if c.GenerationNotMatch != 0 { + n++ + } + if c.DoesNotExist { + n++ + } + return n <= 1 +} + +func (c *Conditions) isMetagenerationValid() bool { + return c.MetagenerationMatch == 0 || c.MetagenerationNotMatch == 0 +} + +// applyConds modifies the provided call using the conditions in conds. +// call is something that quacks like a *raw.WhateverCall. +func applyConds(method string, gen int64, conds *Conditions, call interface{}) error { + cval := reflect.ValueOf(call) + if gen >= 0 { + if !setConditionField(cval, "Generation", gen) { + return fmt.Errorf("storage: %s: generation not supported", method) + } + } + if conds == nil { + return nil + } + if err := conds.validate(method); err != nil { + return err + } + switch { + case conds.GenerationMatch != 0: + if !setConditionField(cval, "IfGenerationMatch", conds.GenerationMatch) { + return fmt.Errorf("storage: %s: ifGenerationMatch not supported", method) + } + case conds.GenerationNotMatch != 0: + if !setConditionField(cval, "IfGenerationNotMatch", conds.GenerationNotMatch) { + return fmt.Errorf("storage: %s: ifGenerationNotMatch not supported", method) + } + case conds.DoesNotExist: + if !setConditionField(cval, "IfGenerationMatch", int64(0)) { + return fmt.Errorf("storage: %s: DoesNotExist not supported", method) + } + } + switch { + case conds.MetagenerationMatch != 0: + if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) { + return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method) + } + case conds.MetagenerationNotMatch != 0: + if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) { + return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method) + } + } + return nil +} + +func applySourceConds(gen int64, conds *Conditions, call *raw.ObjectsRewriteCall) error { + if gen >= 0 { + call.SourceGeneration(gen) + } + if conds == nil { + return nil + } + if err := conds.validate("CopyTo source"); err != nil { + return err + } + switch { + case conds.GenerationMatch != 0: + call.IfSourceGenerationMatch(conds.GenerationMatch) + case conds.GenerationNotMatch != 0: + call.IfSourceGenerationNotMatch(conds.GenerationNotMatch) + case conds.DoesNotExist: + call.IfSourceGenerationMatch(0) + } + switch { + case conds.MetagenerationMatch != 0: + call.IfSourceMetagenerationMatch(conds.MetagenerationMatch) + case conds.MetagenerationNotMatch != 0: + call.IfSourceMetagenerationNotMatch(conds.MetagenerationNotMatch) + } + return nil +} + +// setConditionField sets a field on a *raw.WhateverCall. +// We can't use anonymous interfaces because the return type is +// different, since the field setters are builders. +func setConditionField(call reflect.Value, name string, value interface{}) bool { + m := call.MethodByName(name) + if !m.IsValid() { + return false + } + m.Call([]reflect.Value{reflect.ValueOf(value)}) + return true +} + +// conditionsQuery returns the generation and conditions as a URL query +// string suitable for URL.RawQuery. It assumes that the conditions +// have been validated. +func conditionsQuery(gen int64, conds *Conditions) string { + // URL escapes are elided because integer strings are URL-safe. + var buf []byte + + appendParam := func(s string, n int64) { + if len(buf) > 0 { + buf = append(buf, '&') + } + buf = append(buf, s...) + buf = strconv.AppendInt(buf, n, 10) + } + + if gen >= 0 { + appendParam("generation=", gen) + } + if conds == nil { + return string(buf) + } + switch { + case conds.GenerationMatch != 0: + appendParam("ifGenerationMatch=", conds.GenerationMatch) + case conds.GenerationNotMatch != 0: + appendParam("ifGenerationNotMatch=", conds.GenerationNotMatch) + case conds.DoesNotExist: + appendParam("ifGenerationMatch=", 0) + } + switch { + case conds.MetagenerationMatch != 0: + appendParam("ifMetagenerationMatch=", conds.MetagenerationMatch) + case conds.MetagenerationNotMatch != 0: + appendParam("ifMetagenerationNotMatch=", conds.MetagenerationNotMatch) + } + return string(buf) +} + +// composeSourceObj wraps a *raw.ComposeRequestSourceObjects, but adds the methods +// that modifyCall searches for by name. +type composeSourceObj struct { + src *raw.ComposeRequestSourceObjects +} + +func (c composeSourceObj) Generation(gen int64) { + c.src.Generation = gen +} + +func (c composeSourceObj) IfGenerationMatch(gen int64) { + // It's safe to overwrite ObjectPreconditions, since its only field is + // IfGenerationMatch. + c.src.ObjectPreconditions = &raw.ComposeRequestSourceObjectsObjectPreconditions{ + IfGenerationMatch: gen, + } +} + +func setEncryptionHeaders(headers http.Header, key []byte, copySource bool) error { + if key == nil { + return nil + } + // TODO(jbd): Ask the API team to return a more user-friendly error + // and avoid doing this check at the client level. + if len(key) != 32 { + return errors.New("storage: not a 32-byte AES-256 key") + } + var cs string + if copySource { + cs = "copy-source-" + } + headers.Set("x-goog-"+cs+"encryption-algorithm", "AES256") + headers.Set("x-goog-"+cs+"encryption-key", base64.StdEncoding.EncodeToString(key)) + keyHash := sha256.Sum256(key) + headers.Set("x-goog-"+cs+"encryption-key-sha256", base64.StdEncoding.EncodeToString(keyHash[:])) + return nil +} + +// TODO(jbd): Add storage.objects.watch. diff --git a/vendor/cloud.google.com/go/storage/storage.replay b/vendor/cloud.google.com/go/storage/storage.replay new file mode 100644 index 0000000000..c96b950a95 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/storage.replay @@ -0,0 +1,46908 @@ +{ + "Initial": "IjIwMTgtMDYtMjhUMjE6MDk6MTUuMjMyOTM1MzM5WiI=", + "Version": "0.1", + "Entries": [ + { + "ID": "4894bcbf1ad88b2b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "60" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "aac95449e98a5cc1066ea485caef4af6/15239871814941585171;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIn0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "461" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:16 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220455000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrii75:4136,/bns/yw/borg/yw/bns/blobstore2/bitpusher/293.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=e041W4XfNISchwS8k4SIAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/293.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/293:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWGpuQklBYXBFMzRVRmRLMW51MmNiZUh2Y0lGX3k4MG51TlhVekVhMEkwcnVua1ZkNHhneUdEaV9FWEdKWjlIZjhJcmhxRF9oeW1SbGlWRXAydE11RUM4ZUpMODAwQ0htM1N3WHMwaExndXFUQzkzakhOX1gwWWhlaHVSVDhqaEtmU2lLd21pRTVsWU1kVGZFMThMN0t5M1ZFQVQzMThQb0pPSkhfV0Ewa3RRMmZCQU9xWm5mOWNKT2swBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrK2IAJDmKkNI-9ObBn-8E92Fm8LsvzMfJPyOhYw_Co97AW_NveRZhNKIvDoSFnhH024bWZ8Zk0mhSPdUHW42D2cqJCFmnE8kkK1euWBhSKUf9z3mc" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjE2LjM2M1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "f96e58868b85f56a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "60" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "cc56170cde109f4d124a507da781b103/17590376030333694786;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAxIn0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "461" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:17 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220456000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnt4:4443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=fE41W4ujK8PdhQTRkIzQBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/178:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVnRsUmwycmRKZGpOcHhwZXdydnFpcGlDX2hnVEdraUJaTFRWZ1AzaGQ3Ql9hVGM4N25MS1lKMThsOHpFT1I5SkdjZm9qWDlMZ3JZc09sNW8yb0ljOGlaZ3J3dHdLVDN1R3FSdlAyMDI5SW1Pam11RTRnUC02c3U1VlRQbTFQVnEyN1diU2FET28wSmtwYkVub3hlWGt1TXIwbTNCdThITkF0LXhMVk1EMnc4WVNHZGw5UGlFWHZHOVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrRur57V19DVfTY_UxEG3Jdcj_krvWCUsvVcom2X15_Fi8hbSwJt3nw6mPzjwLa8OyMRPzx-o52FRIZX-I4Ql3HA2XizQSRKlgClyv-4MJMdAcCsUg" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTcuMDQ2WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjE3LjA0NloiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "702889255ceab095", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "34b1144f74f525381ceaea60a2264736/759047364799723106;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:17 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:17 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220457000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrkx25:4469,/bns/yw/borg/yw/bns/blobstore2/bitpusher/627.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=fU41W4zWDsfQhASqvYj4Bg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/627.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/627:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVnRsUmwycmRKZGpOcHhwZXdydnFpcGlDX2hnVEdraUJaTFRWZ1AzaGQ3Ql9hVGM4N25MS1lKMThsOHpFT1I5SkdjZm9qWDlMZ3JZc09sNW8yb0ljOGlaZ3J3dHdLVDN1R3FSdlAyMDI5SW1Pam11RTRnUC02c3U1VlRQbTFQVnEyN1diU2FET28wSmtwYkVub3hlWGt1TXIwbTNCdThITkF0LXhMVk1EMnc4WVNHZGw5UGlFWHZHOVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq1kRg5SkX0QMVUY976lJzdFr9Z_E8r68kePRaQJ6hkrySXPTpXglq91dx2bFy9vDf4dRWxk3cESeWhk8WmYBYhz3HowJwT1EKtGVo5oCa5TNnARC4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTcuMDQ2WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjE3LjA0NloiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBRT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUU9Igp9Cg==" + } + }, + { + "ID": "0594eaa98b455f85", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "570035b7298f18843cc671a911c30762/2374181297998657921;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:18 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220457000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmm2:4409,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=fU41W5fYH4yihwS80rTIBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/623:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVnRsUmwycmRKZGpOcHhwZXdydnFpcGlDX2hnVEdraUJaTFRWZ1AzaGQ3Ql9hVGM4N25MS1lKMThsOHpFT1I5SkdjZm9qWDlMZ3JZc09sNW8yb0ljOGlaZ3J3dHdLVDN1R3FSdlAyMDI5SW1Pam11RTRnUC02c3U1VlRQbTFQVnEyN1diU2FET28wSmtwYkVub3hlWGt1TXIwbTNCdThITkF0LXhMVk1EMnc4WVNHZGw5UGlFWHZHOVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqIYrHJ8DVSh6gqz0oxMZdpr3g2ob8k4gvBAsbLtYhbDyfv83sYyDWAWPmXjeVVZxqhAfjLHE-Wb11uc3RBtmLX4DI6aJICdAhJ8L-GNTQjJpbK0vo" + ] + }, + "Body": "" + } + }, + { + "ID": "a6b92d918142a520", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "543" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "81e8e0333cc2c4eb7d144f976f500fee/3917258732376325281;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsYWJlbHMiOnsiZW1wdHkiOiIiLCJsMSI6InYxIn0sImxpZmVjeWNsZSI6eyJydWxlIjpbeyJhY3Rpb24iOnsic3RvcmFnZUNsYXNzIjoiTkVBUkxJTkUiLCJ0eXBlIjoiU2V0U3RvcmFnZUNsYXNzIn0sImNvbmRpdGlvbiI6eyJhZ2UiOjEwLCJjcmVhdGVkQmVmb3JlIjoiMjAxNy0wMS0wMSIsImlzTGl2ZSI6ZmFsc2UsIm1hdGNoZXNTdG9yYWdlQ2xhc3MiOlsiTVVMVElfUkVHSU9OQUwiLCJTVEFOREFSRCJdLCJudW1OZXdlclZlcnNpb25zIjozfX0seyJhY3Rpb24iOnsidHlwZSI6IkRlbGV0ZSJ9LCJjb25kaXRpb24iOnsiYWdlIjozMCwiY3JlYXRlZEJlZm9yZSI6IjIwMTctMDEtMDEiLCJpc0xpdmUiOnRydWUsIm1hdGNoZXNTdG9yYWdlQ2xhc3MiOlsiTkVBUkxJTkUiXSwibnVtTmV3ZXJWZXJzaW9ucyI6MTB9fV19LCJsb2NhdGlvbiI6IlVTIiwibmFtZSI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsInN0b3JhZ2VDbGFzcyI6Ik5FQVJMSU5FIiwidmVyc2lvbmluZyI6eyJlbmFibGVkIjp0cnVlfX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "1120" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:18 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220457000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmm15:4085,/bns/yw/borg/yw/bns/blobstore2/bitpusher/138.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=fk41W5iWBYq-hQT_maLIAg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/138.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/138:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVnRsUmwycmRKZGpOcHhwZXdydnFpcGlDX2hnVEdraUJaTFRWZ1AzaGQ3Ql9hVGM4N25MS1lKMThsOHpFT1I5SkdjZm9qWDlMZ3JZc09sNW8yb0ljOGlaZ3J3dHdLVDN1R3FSdlAyMDI5SW1Pam11RTRnUC02c3U1VlRQbTFQVnEyN1diU2FET28wSmtwYkVub3hlWGt1TXIwbTNCdThITkF0LXhMVk1EMnc4WVNHZGw5UGlFWHZHOVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upar5flAB4LbTgR4lpyZwBaf9dg7cs2MI3wdCsSMMogcFgj4nklYLLdN5s74qzcPVguCBXABHAb_tvPnWSk_k47APH-zsptB4asG6ANNpoR7J6RFKE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTguNjg4WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjE4LjY4OFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJ2ZXJzaW9uaW5nIjogewogICJlbmFibGVkIjogdHJ1ZQogfSwKICJsaWZlY3ljbGUiOiB7CiAgInJ1bGUiOiBbCiAgIHsKICAgICJhY3Rpb24iOiB7CiAgICAgInR5cGUiOiAiU2V0U3RvcmFnZUNsYXNzIiwKICAgICAic3RvcmFnZUNsYXNzIjogIk5FQVJMSU5FIgogICAgfSwKICAgICJjb25kaXRpb24iOiB7CiAgICAgImFnZSI6IDEwLAogICAgICJjcmVhdGVkQmVmb3JlIjogIjIwMTctMDEtMDEiLAogICAgICJpc0xpdmUiOiBmYWxzZSwKICAgICAibWF0Y2hlc1N0b3JhZ2VDbGFzcyI6IFsKICAgICAgIk1VTFRJX1JFR0lPTkFMIiwKICAgICAgIlNUQU5EQVJEIgogICAgIF0sCiAgICAgIm51bU5ld2VyVmVyc2lvbnMiOiAzCiAgICB9CiAgIH0sCiAgIHsKICAgICJhY3Rpb24iOiB7CiAgICAgInR5cGUiOiAiRGVsZXRlIgogICAgfSwKICAgICJjb25kaXRpb24iOiB7CiAgICAgImFnZSI6IDMwLAogICAgICJjcmVhdGVkQmVmb3JlIjogIjIwMTctMDEtMDEiLAogICAgICJpc0xpdmUiOiB0cnVlLAogICAgICJtYXRjaGVzU3RvcmFnZUNsYXNzIjogWwogICAgICAiTkVBUkxJTkUiCiAgICAgXSwKICAgICAibnVtTmV3ZXJWZXJzaW9ucyI6IDEwCiAgICB9CiAgIH0KICBdCiB9LAogImxhYmVscyI6IHsKICAiZW1wdHkiOiAiIiwKICAibDEiOiAidjEiCiB9LAogInN0b3JhZ2VDbGFzcyI6ICJORUFSTElORSIsCiAiZXRhZyI6ICJDQUU9Igp9Cg==" + } + }, + { + "ID": "e4eee42f13bf4757", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6a77decf8644571905d0fbd5139b674a/5532112294388367296;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3393" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:19 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:19 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220457000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlc6:4325,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=fk41W8jVNsPrhAT61qjACQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/635:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVnRsUmwycmRKZGpOcHhwZXdydnFpcGlDX2hnVEdraUJaTFRWZ1AzaGQ3Ql9hVGM4N25MS1lKMThsOHpFT1I5SkdjZm9qWDlMZ3JZc09sNW8yb0ljOGlaZ3J3dHdLVDN1R3FSdlAyMDI5SW1Pam11RTRnUC02c3U1VlRQbTFQVnEyN1diU2FET28wSmtwYkVub3hlWGt1TXIwbTNCdThITkF0LXhMVk1EMnc4WVNHZGw5UGlFWHZHOVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo8xe2oZZ6Sr9gc4Lya8AML2vB-PGV8KSkTlBm8Gd70bXS5y14RwgCLDqFAURG9CjSocSnp1_8BdMSGA6FzfS3va0nILzRa-bmchhQqzkJ_oLXEhMM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTguNjg4WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjE4LjY4OFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDEiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBRT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInZlcnNpb25pbmciOiB7CiAgImVuYWJsZWQiOiB0cnVlCiB9LAogImxpZmVjeWNsZSI6IHsKICAicnVsZSI6IFsKICAgewogICAgImFjdGlvbiI6IHsKICAgICAidHlwZSI6ICJTZXRTdG9yYWdlQ2xhc3MiLAogICAgICJzdG9yYWdlQ2xhc3MiOiAiTkVBUkxJTkUiCiAgICB9LAogICAgImNvbmRpdGlvbiI6IHsKICAgICAiYWdlIjogMTAsCiAgICAgImNyZWF0ZWRCZWZvcmUiOiAiMjAxNy0wMS0wMSIsCiAgICAgImlzTGl2ZSI6IGZhbHNlLAogICAgICJtYXRjaGVzU3RvcmFnZUNsYXNzIjogWwogICAgICAiTVVMVElfUkVHSU9OQUwiLAogICAgICAiU1RBTkRBUkQiCiAgICAgXSwKICAgICAibnVtTmV3ZXJWZXJzaW9ucyI6IDMKICAgIH0KICAgfSwKICAgewogICAgImFjdGlvbiI6IHsKICAgICAidHlwZSI6ICJEZWxldGUiCiAgICB9LAogICAgImNvbmRpdGlvbiI6IHsKICAgICAiYWdlIjogMzAsCiAgICAgImNyZWF0ZWRCZWZvcmUiOiAiMjAxNy0wMS0wMSIsCiAgICAgImlzTGl2ZSI6IHRydWUsCiAgICAgIm1hdGNoZXNTdG9yYWdlQ2xhc3MiOiBbCiAgICAgICJORUFSTElORSIKICAgICBdLAogICAgICJudW1OZXdlclZlcnNpb25zIjogMTAKICAgIH0KICAgfQogIF0KIH0sCiAibGFiZWxzIjogewogICJsMSI6ICJ2MSIsCiAgImVtcHR5IjogIiIKIH0sCiAic3RvcmFnZUNsYXNzIjogIk5FQVJMSU5FIiwKICJldGFnIjogIkNBRT0iCn0K" + } + }, + { + "ID": "d4561802dfb38485", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "64744c8459ac15555a4ae98065fa6ecf/7075189733060936416;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0001?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:19 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220457000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrea13:4108,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=f041W9G-DYbJN_DHnpAH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/159:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVnRsUmwycmRKZGpOcHhwZXdydnFpcGlDX2hnVEdraUJaTFRWZ1AzaGQ3Ql9hVGM4N25MS1lKMThsOHpFT1I5SkdjZm9qWDlMZ3JZc09sNW8yb0ljOGlaZ3J3dHdLVDN1R3FSdlAyMDI5SW1Pam11RTRnUC02c3U1VlRQbTFQVnEyN1diU2FET28wSmtwYkVub3hlWGt1TXIwbTNCdThITkF0LXhMVk1EMnc4WVNHZGw5UGlFWHZHOVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo0dUn-HtjyWUGeprKWaSAC9bVjvcK6KQbMto5d0Yn_o3FATKEYaaWyXVO49TLHK2HMfNydIOtlpwO6XUgtCBN2lN5w6N_r5hjWdq-Gg760GUah73M" + ] + }, + "Body": "" + } + }, + { + "ID": "ce9f8a7c7eabd607", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4776e7d2e5f109be406e23610b94c34c/8690323666259805440;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:19 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:19 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220459000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcg4:4086,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=f041W4TaLNPFhgTc2byYBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/485:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVS1SZ1BGU1hJenM2V2NvXzlwTWlBcEwtQ1FpcUFZQk10ZE5HbG9SM2R2aGNFZjVEbTkwb2dmcHA4Q0ZwekhoS0p2UzZsbWRrSnk0cDdLTVRuWERNRWRuakVOVmt1WUctd1JuclgxZDJqNGtVMlhkMWpYR2kybUloTXpPVU9acEhRZllxRzczV2t2TUhBMVBHSzB6UUVrTXBGZ25fUmxadFNhQ3ZtTS1IUTJsOUFZQnRqM19IQ2o0M2MwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urk67ktnXpE8GX9HoNpP-qT3FJ12_nPkWFUTNCnuIatUmr4CyWOvtvdRbGwAirnOnZjPVAFV3Cx_kIGTANjMVme0d_1YxzPU_oTyny_NlgSVWfnz6E" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjE2LjM2M1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBRT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUU9Igp9Cg==" + } + }, + { + "ID": "885192582708de64", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3a71b0149d98b7864bc75c21dd6814db/10233401100637538335;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:21 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220460000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrir7:4320,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=gE41W5CVAYbUhQSJtoaQBA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/66:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVS1SZ1BGU1hJenM2V2NvXzlwTWlBcEwtQ1FpcUFZQk10ZE5HbG9SM2R2aGNFZjVEbTkwb2dmcHA4Q0ZwekhoS0p2UzZsbWRrSnk0cDdLTVRuWERNRWRuakVOVmt1WUctd1JuclgxZDJqNGtVMlhkMWpYR2kybUloTXpPVU9acEhRZllxRzczV2t2TUhBMVBHSzB6UUVrTXBGZ25fUmxadFNhQ3ZtTS1IUTJsOUFZQnRqM19IQ2o0M2MwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur9ftxiajem1HYhAdYDKJwu6OHkBBk26Y2IclyxT_eh9LxtvMtiuUqE458QxNyHhXtv_WuRSfsORRqZTaXnwmZfE4Jx2w8y-luVpG2CQxN8WQskKb0" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjIxLjQyM1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "02c4837deb455fa0", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "64" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9f2c73a1f1c087566791d44d4d8a56a9/11848536133331258175;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsYWJlbHMiOnsiZW1wdHkiOiIiLCJsMSI6InYxIn0sInZlcnNpb25pbmciOnsiZW5hYmxlZCI6dHJ1ZX19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2818" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:23 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220461000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqq22:4024,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=gU41W8XOJtDXN_6_oYgI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/129:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVS1SZ1BGU1hJenM2V2NvXzlwTWlBcEwtQ1FpcUFZQk10ZE5HbG9SM2R2aGNFZjVEbTkwb2dmcHA4Q0ZwekhoS0p2UzZsbWRrSnk0cDdLTVRuWERNRWRuakVOVmt1WUctd1JuclgxZDJqNGtVMlhkMWpYR2kybUloTXpPVU9acEhRZllxRzczV2t2TUhBMVBHSzB6UUVrTXBGZ25fUmxadFNhQ3ZtTS1IUTJsOUFZQnRqM19IQ2o0M2MwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrKvUxRc_c9zaIsEAWtRuMMsEUQsO-5NxB5GSLP1Glk3ZIiz4DoDWmVKIxN9UhxwEn55wzfghL260iBjLTxTkBIQTCcUzyGxRYaK8tv867gYxKDSRU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjIzLjEyNVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjMiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FNPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FNPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FNPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQU09IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInZlcnNpb25pbmciOiB7CiAgImVuYWJsZWQiOiB0cnVlCiB9LAogImxhYmVscyI6IHsKICAiZW1wdHkiOiAiIiwKICAibDEiOiAidjEiCiB9LAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQU09Igp9Cg==" + } + }, + { + "ID": "e446bf2316497466", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "93" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d4cf567837277835fa4308f895248b89/13463670066530192990;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsYWJlbHMiOnsiYWJzZW50IjpudWxsLCJlbXB0eSI6bnVsbCwibDEiOiJ2MiIsIm5ldyI6Im5ldyJ9LCJ2ZXJzaW9uaW5nIjp7ImVuYWJsZWQiOmZhbHNlfX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2820" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:25 GMT" + ], + "Etag": [ + "CAQ=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220460000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlx13:4318,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=g041W-7DFMuGhASj3anICg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/615:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVS1SZ1BGU1hJenM2V2NvXzlwTWlBcEwtQ1FpcUFZQk10ZE5HbG9SM2R2aGNFZjVEbTkwb2dmcHA4Q0ZwekhoS0p2UzZsbWRrSnk0cDdLTVRuWERNRWRuakVOVmt1WUctd1JuclgxZDJqNGtVMlhkMWpYR2kybUloTXpPVU9acEhRZllxRzczV2t2TUhBMVBHSzB6UUVrTXBGZ25fUmxadFNhQ3ZtTS1IUTJsOUFZQnRqM19IQ2o0M2MwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uoj6FYhYkhLWm0Klxd-dTzNZjQVaXPN10dz23hwG9zOFNfURAc05lNLOS2asdbcnhKzVMwVXRdoYcU5PIm7ImSIdy56G2IQoOWofcDDI7Tta6bJ0L8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI0LjgxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjQiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FRPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FRPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBUT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQVE9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FRPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQVE9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInZlcnNpb25pbmciOiB7CiAgImVuYWJsZWQiOiBmYWxzZQogfSwKICJsYWJlbHMiOiB7CiAgImwxIjogInYyIiwKICAibmV3IjogIm5ldyIKIH0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBUT0iCn0K" + } + }, + { + "ID": "7293d6ac37b9323f", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "77" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "def5710b438326c09f7de66f4c6f41d0/15006747505202762110;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJsaWZlY3ljbGUiOnsicnVsZSI6W3siYWN0aW9uIjp7InR5cGUiOiJEZWxldGUifSwiY29uZGl0aW9uIjp7ImFnZSI6MzB9fV19fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2951" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:26 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220460000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrb66:4113,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=hU41W8imAsKVN86YjeAJ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/211:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVS1SZ1BGU1hJenM2V2NvXzlwTWlBcEwtQ1FpcUFZQk10ZE5HbG9SM2R2aGNFZjVEbTkwb2dmcHA4Q0ZwekhoS0p2UzZsbWRrSnk0cDdLTVRuWERNRWRuakVOVmt1WUctd1JuclgxZDJqNGtVMlhkMWpYR2kybUloTXpPVU9acEhRZllxRzczV2t2TUhBMVBHSzB6UUVrTXBGZ25fUmxadFNhQ3ZtTS1IUTJsOUFZQnRqM19IQ2o0M2MwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoauiUoJ_ibIazo0pv_4PrjGN2IoAGcMHzq0zGJWy0TmKxyO9ElwzPb0mpEyuusjqtGr6p4zN8il0uKuJhdBoIu9D_VaA2OGlMZVPdjMrld_GhvCoE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI2LjMyNVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjUiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FVPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FVPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FVPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQVU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInZlcnNpb25pbmciOiB7CiAgImVuYWJsZWQiOiBmYWxzZQogfSwKICJsaWZlY3ljbGUiOiB7CiAgInJ1bGUiOiBbCiAgIHsKICAgICJhY3Rpb24iOiB7CiAgICAgInR5cGUiOiAiRGVsZXRlIgogICAgfSwKICAgICJjb25kaXRpb24iOiB7CiAgICAgImFnZSI6IDMwCiAgICB9CiAgIH0KICBdCiB9LAogImxhYmVscyI6IHsKICAibDEiOiAidjIiLAogICJuZXciOiAibmV3IgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FVPSIKfQo=" + } + }, + { + "ID": "fc1b1a45e393a465", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=cbbe4d535e9909e8273e811bca7bac1e632704a165a724f2f2a1c376ba31" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "467d045eca34aa94febc6bd512de5dee/15814174286200427405;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1jYmJlNGQ1MzVlOTkwOWU4MjczZTgxMWJjYTdiYWMxZTYzMjcwNGExNjVhNzI0ZjJmMmExYzM3NmJhMzENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJjb25kZGVsIn0KDQotLWNiYmU0ZDUzNWU5OTA5ZTgyNzNlODExYmNhN2JhYzFlNjMyNzA0YTE2NWE3MjRmMmYyYTFjMzc2YmEzMQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNCmZvbw0KLS1jYmJlNGQ1MzVlOTkwOWU4MjczZTgxMWJjYTdiYWMxZTYzMjcwNGExNjVhNzI0ZjJmMmExYzM3NmJhMzEtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3593" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:27 GMT" + ], + "Etag": [ + "CN3l7aui99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220466000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbp63:4350,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=hk41W-HsJcjPhASU8bPwBw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/169:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBV0lpMk9IOEN4aGlpRmlNTldxV1ZiTDZtX0l4X19KUnJZQWhWTWJzME94M1RMM29kRDVnbk9oYnBRUFFzSXJfUlRsVVJmM3VRdEdOY3ZjNThyLTJZcUpSYWtCVVRWMXdyM2I5a0dJQ3lpX3ZPT1F3SHNIdFdKbzZ2VmtfeVJEZTZVMlFQdHduRTg0NzZOSmVISFlCLVVwT29JbTVLX29kRi1DaEdTbkR0anQ0WG10N0xCSDBJbTFzejQwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoEmCj35ggazuw8SVr1-0vMX7JUNgyWYvX0psNeA-OFShP8W4gMOPFFKgabnmRLoZdCdOIdxVuluiGzdekR0lKPm7_id2NYu2M_OiYYVfo5-47OxWw" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb25kZGVsLzE1MzAyMjAxNjcwMDA3OTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb25kZGVsIiwKICJuYW1lIjogImNvbmRkZWwiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2NzAwMDc5NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyNy4wMDBaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjcuMDAwWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI3LjAwMFoiLAogInNpemUiOiAiMyIsCiAibWQ1SGFzaCI6ICJyTDBZMjB6QytGenQ3MlZQek1TazJBPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29uZGRlbD9nZW5lcmF0aW9uPTE1MzAyMjAxNjcwMDA3OTcmYWx0PW1lZGlhIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbmRkZWwvMTUzMDIyMDE2NzAwMDc5Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29uZGRlbC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY29uZGRlbCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjcwMDA3OTciLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNOM2w3YXVpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29uZGRlbC8xNTMwMjIwMTY3MDAwNzk3L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29uZGRlbC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbmRkZWwiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY3MDAwNzk3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNOM2w3YXVpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29uZGRlbC8xNTMwMjIwMTY3MDAwNzk3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29uZGRlbC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbmRkZWwiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY3MDAwNzk3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTjNsN2F1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbmRkZWwvMTUzMDIyMDE2NzAwMDc5Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb25kZGVsL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbmRkZWwiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY3MDAwNzk3IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTjNsN2F1aTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiejhTdUhRPT0iLAogImV0YWciOiAiQ04zbDdhdWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "95afcb6c55a5f426", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026generation=1530220167000796", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "97870c9e0f14a660a1f0da2aa9af4e21/16621881434106795165;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026generation=1530220167000796" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12471" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:27 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220467000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vryy6:4071,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=h041W_eYCMqfhQTz8pv4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/574:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBV0lpMk9IOEN4aGlpRmlNTldxV1ZiTDZtX0l4X19KUnJZQWhWTWJzME94M1RMM29kRDVnbk9oYnBRUFFzSXJfUlRsVVJmM3VRdEdOY3ZjNThyLTJZcUpSYWtCVVRWMXdyM2I5a0dJQ3lpX3ZPT1F3SHNIdFdKbzZ2VmtfeVJEZTZVMlFQdHduRTg0NzZOSmVISFlCLVVwT29JbTVLX29kRi1DaEdTbkR0anQ0WG10N0xCSDBJbTFzejQwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UocakxQExr0ez25XP0QcxcWsQst5f4MKbbjJ3b8fdbRFHTzkbuVFHmgt92HWZ33mj4lKZQ9v6S6CPoHiSf1AXj4P4aJ5NLgBLoF-JNwz2bZiA9Vyn0" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb25kZGVsIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6Ok9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29uZGRlbFxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YTo4NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6MjcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmRlbGV0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTEzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb25kZGVsXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1OT1RfRk9VTkQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb25kZGVsXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbmRkZWxcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPW5vdEZvdW5kLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5OT1RfRk9VTkQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbmRkZWxcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29uZGRlbFxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLm5hbWUsIG1lc3NhZ2U9Tm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb25kZGVsLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLm5hbWUsIG1lc3NhZ2U9Tm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb25kZGVsLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbmRkZWw6IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbmRkZWxcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29uZGRlbFxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwNCwKICAibWVzc2FnZSI6ICJObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbmRkZWwiCiB9Cn0K" + } + }, + { + "ID": "f489902c46dca10a", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026ifMetagenerationMatch=2", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "151d42a830b2c6d206add0167878d246/17429308215104394925;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026ifMetagenerationMatch=2" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 412, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12589" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:27 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220466000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrgg74:4491,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=h041W_HnDsKKhATFuJ7wBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/94:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBV0lpMk9IOEN4aGlpRmlNTldxV1ZiTDZtX0l4X19KUnJZQWhWTWJzME94M1RMM29kRDVnbk9oYnBRUFFzSXJfUlRsVVJmM3VRdEdOY3ZjNThyLTJZcUpSYWtCVVRWMXdyM2I5a0dJQ3lpX3ZPT1F3SHNIdFdKbzZ2VmtfeVJEZTZVMlFQdHduRTg0NzZOSmVISFlCLVVwT29JbTVLX29kRi1DaEdTbkR0anQ0WG10N0xCSDBJbTFzejQwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrtreDvscPIkTzKfF3LvYgOS-N4flSvGTYqwv9ESVC19wMSdNSYv9sfGF3_LATmvSC_zGpJ0qYB-1pO9S8ZsHlcNCEGuHWr3ujFt20bmFaMro614EM" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiY29uZGl0aW9uTm90TWV0IiwKICAgICJtZXNzYWdlIjogIlByZWNvbmRpdGlvbiBGYWlsZWQiLAogICAgImxvY2F0aW9uVHlwZSI6ICJoZWFkZXIiLAogICAgImxvY2F0aW9uIjogIklmLU1hdGNoIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OklOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IEV4cGVjdGVkIG1ldGFkYXRhIGdlbmVyYXRpb24gdG8gbWF0Y2ggMiwgYnV0IGFjdHVhbCB2YWx1ZSB3YXMgMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogRXhwZWN0ZWQgbWV0YWRhdGEgZ2VuZXJhdGlvbiB0byBtYXRjaCAyLCBidXQgYWN0dWFsIHZhbHVlIHdhcyAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UFJFQ09ORElUSU9OX0ZBSUxFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBFeHBlY3RlZCBtZXRhZGF0YSBnZW5lcmF0aW9uIHRvIG1hdGNoIDIsIGJ1dCBhY3R1YWwgdmFsdWUgd2FzIDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IEV4cGVjdGVkIG1ldGFkYXRhIGdlbmVyYXRpb24gdG8gbWF0Y2ggMiwgYnV0IGFjdHVhbCB2YWx1ZSB3YXMgMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPXByZWNvbmRpdGlvbkZhaWxlZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uQ09ORElUSU9OX05PVF9NRVQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBFeHBlY3RlZCBtZXRhZGF0YSBnZW5lcmF0aW9uIHRvIG1hdGNoIDIsIGJ1dCBhY3R1YWwgdmFsdWUgd2FzIDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IEV4cGVjdGVkIG1ldGFkYXRhIGdlbmVyYXRpb24gdG8gbWF0Y2ggMiwgYnV0IGFjdHVhbCB2YWx1ZSB3YXMgMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Q09ORElUSU9OX05PVF9NRVQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1udWxsLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249aGVhZGVycy5JZi1NYXRjaCwgbWVzc2FnZT1QcmVjb25kaXRpb24gRmFpbGVkLCByZWFzb249Y29uZGl0aW9uTm90TWV0LCBycGNDb2RlPTQxMn0gUHJlY29uZGl0aW9uIEZhaWxlZDogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OklOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IEV4cGVjdGVkIG1ldGFkYXRhIGdlbmVyYXRpb24gdG8gbWF0Y2ggMiwgYnV0IGFjdHVhbCB2YWx1ZSB3YXMgMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogRXhwZWN0ZWQgbWV0YWRhdGEgZ2VuZXJhdGlvbiB0byBtYXRjaCAyLCBidXQgYWN0dWFsIHZhbHVlIHdhcyAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQxMiwKICAibWVzc2FnZSI6ICJQcmVjb25kaXRpb24gRmFpbGVkIgogfQp9Cg==" + } + }, + { + "ID": "77e7d2a8cd026dbf", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026ifMetagenerationNotMatch=1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "88c2e0fe30fdb1ace690e681b05eb39f/18164959972274214845;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026ifMetagenerationNotMatch=1" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 304, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:27 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220467000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrad2:4118,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=h041W6fIG8LvhASL0qegBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/59:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBV0lpMk9IOEN4aGlpRmlNTldxV1ZiTDZtX0l4X19KUnJZQWhWTWJzME94M1RMM29kRDVnbk9oYnBRUFFzSXJfUlRsVVJmM3VRdEdOY3ZjNThyLTJZcUpSYWtCVVRWMXdyM2I5a0dJQ3lpX3ZPT1F3SHNIdFdKbzZ2VmtfeVJEZTZVMlFQdHduRTg0NzZOSmVISFlCLVVwT29JbTVLX29kRi1DaEdTbkR0anQ0WG10N0xCSDBJbTFzejQwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UokO39IHexblHEShFmtez_7szpL8GJyBEYNs5ro2TJxxBgww-9zO9Edf8WLA7vTWEkgUzJgQDmEtfJg6i1fEEsmewRdK1BHBc38wnUCIlw_K2Ow5tE" + ] + }, + "Body": "" + } + }, + { + "ID": "32d34045587e2f4c", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026generation=1530220167000797", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "91dbc62daaaca34c89a4a82225456109/525923055044188620;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/conddel?alt=json\u0026generation=1530220167000797" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220467000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vro65:4092,/bns/yw/borg/yw/bns/blobstore2/bitpusher/377.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=h041W5r2J4TRhATJ_5PIDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/377.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/377:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBV0lpMk9IOEN4aGlpRmlNTldxV1ZiTDZtX0l4X19KUnJZQWhWTWJzME94M1RMM29kRDVnbk9oYnBRUFFzSXJfUlRsVVJmM3VRdEdOY3ZjNThyLTJZcUpSYWtCVVRWMXdyM2I5a0dJQ3lpX3ZPT1F3SHNIdFdKbzZ2VmtfeVJEZTZVMlFQdHduRTg0NzZOSmVISFlCLVVwT29JbTVLX29kRi1DaEdTbkR0anQ0WG10N0xCSDBJbTFzejQwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrGOZDuXz0QIACs6D64x_IsTamgFc2TsmEdD75evxJf4kJjRaoSrDTJ8LuKO556KzxoKpbgPwXdc4ZfosR-scmTP2bHZRkolC2hl4nxkMjdj8zuXSI" + ] + }, + "Body": "" + } + }, + { + "ID": "f5c0b62392ca2593", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=6a20dd68f7c979879fc0c956ef2152235c6136aa1d285146943f1bc6b0f1" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "26219656612ad02743a0bcc77da1b159/1333349836058565340;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS02YTIwZGQ2OGY3Yzk3OTg3OWZjMGM5NTZlZjIxNTIyMzVjNjEzNmFhMWQyODUxNDY5NDNmMWJjNmIwZjENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJvYmoxIn0KDQotLTZhMjBkZDY4ZjdjOTc5ODc5ZmMwYzk1NmVmMjE1MjIzNWM2MTM2YWExZDI4NTE0Njk0M2YxYmM2YjBmMQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNCrNg930V187kDew/HV3qDtUNCi0tNmEyMGRkNjhmN2M5Nzk4NzlmYzBjOTU2ZWYyMTUyMjM1YzYxMzZhYTFkMjg1MTQ2OTQzZjFiYzZiMGYxLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3585" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Etag": [ + "CLmfy6yi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220468000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrpa10:4215,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iE41W8GxD8zuhATb8oXwCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/87:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqZaopDVDmx9NAOzVHM8U5DRbgLvaVZ9_2aGtwOMEMdvChrqKYt62WBsnuI6mVwRVPQxWdNo1EmnaskSykMtmPaydFdm21ekHybYgJtrT4GFh4fi_k" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxIiwKICJuYW1lIjogIm9iajEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOC41MjlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogInNpemUiOiAiMTYiLAogIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajE/Z2VuZXJhdGlvbj0xNTMwMjIwMTY4NTMxODk3JmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iajEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIjBTRzRpdz09IiwKICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "d3043714055a0788", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=3a91a75601edfb8d7ea7dc372b04c63fdb448d7f012ecd0d694c69c63ebe" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6698119df266a591c646374fd2b22ffc/2141058087737908460;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0zYTkxYTc1NjAxZWRmYjhkN2VhN2RjMzcyYjA0YzYzZmRiNDQ4ZDdmMDEyZWNkMGQ2OTRjNjljNjNlYmUNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJvYmoyIn0KDQotLTNhOTFhNzU2MDFlZGZiOGQ3ZWE3ZGMzNzJiMDRjNjNmZGI0NDhkN2YwMTJlY2QwZDY5NGM2OWM2M2ViZQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNCmKE3my2HBY1doDVv6ftYG0NCi0tM2E5MWE3NTYwMWVkZmI4ZDdlYTdkYzM3MmIwNGM2M2ZkYjQ0OGQ3ZjAxMmVjZDBkNjk0YzY5YzYzZWJlLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3585" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Etag": [ + "CKqT6ayi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220468000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlg5:4422,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iE41W7OLKcPdhQTRkIzQBQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/178:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UranMFbIP9_07rN-Kaa4woBw9zgofso8N_porArA_BXrBxUnAF5nPm-4Zbebt0rH7m_iCIXAoS_ogauMfzLmyx-iG8cjzLAzxtnf91BaZs9YZym934" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyIiwKICJuYW1lIjogIm9iajIiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS4wMjFaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogInNpemUiOiAiMTYiLAogIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajI/Z2VuZXJhdGlvbj0xNTMwMjIwMTY5MDIxODY2JmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iajIiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDS3FUNmF5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDS3FUNmF5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIk5FeEN3dz09IiwKICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "c3483029a09f8b5e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=048769566a98658173dd0ed405503d3ce2f25325dd6c089f8dbd86963c8a" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6842fad30715381571df2b7ffd703b43/2948483769257434620;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0wNDg3Njk1NjZhOTg2NTgxNzNkZDBlZDQwNTUwM2QzY2UyZjI1MzI1ZGQ2YzA4OWY4ZGJkODY5NjNjOGENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJvYmovd2l0aC9zbGFzaGVzIn0KDQotLTA0ODc2OTU2NmE5ODY1ODE3M2RkMGVkNDA1NTAzZDNjZTJmMjUzMjVkZDZjMDg5ZjhkYmQ4Njk2M2M4YQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNCkQ6mVQxst+8JZcK4+P54MANCi0tMDQ4NzY5NTY2YTk4NjU4MTczZGQwZWQ0MDU1MDNkM2NlMmYyNTMyNWRkNmMwODlmOGRiZDg2OTYzYzhhLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3801" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Etag": [ + "CL2via2i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220468000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vru67:4406,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iU41W9uFEIObhATkprbADg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/265:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upg5TgkFYrQM5BMY1_baJJNRzlXFd8c-5g14i2HmOzcMO7N60LVL_ekKmpasysor13g5z3aUXCZvItDPgy2Z3xn1sFGuMcHMPX4984JqzcQkN8yl-Q" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcyIsCiAibmFtZSI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuNTQ5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICJzaXplIjogIjE2IiwKICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcz9nZW5lcmF0aW9uPTE1MzAyMjAxNjk1NDk3NTcmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIit6aUk4UT09IiwKICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "1a137acd0380ec8f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj%2Fwith%2Fslashes?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3799ccf76cec57bfc77716c897d3ca12/4491561203635167259;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj%2Fwith%2Fslashes?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3801" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Etag": [ + "CL2via2i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrih23:4317,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iU41W_3yLsGVhgSpl5CYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/577:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq_irlpSMXrwME8HWOmSkJbYFxbNe5tzle05tqH-mFvHc0T21_sCHwLPIxoljvNOnTfA7HlNAEUE-451Svlo2X9IzNmtReTJRShOhzrObjfWwbQ9qI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcyIsCiAibmFtZSI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuNTQ5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICJzaXplIjogIjE2IiwKICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcz9nZW5lcmF0aW9uPTE1MzAyMjAxNjk1NDk3NTcmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIit6aUk4UT09IiwKICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "8150b68568abb1f7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "06df82a7d8328bee655373cd63173861/6106696236328887099;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3585" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Etag": [ + "CLmfy6yi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnk10:4091,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iU41W_STNsaJN5i7i4AF" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/488:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrUEnDwjRAkAVD3asS3cF5gqyVf3cLvlkvnQHvSYWiYz4_I-otRA4FaiNTUDQBpF80iBEws08QyhBKcVcVGdbIJ4gmiQxkTJhUisKS5g9KuToUsMy8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxIiwKICJuYW1lIjogIm9iajEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOC41MjlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogInNpemUiOiAiMTYiLAogIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajE/Z2VuZXJhdGlvbj0xNTMwMjIwMTY4NTMxODk3JmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iajEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIjBTRzRpdz09IiwKICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "0ec45426baa60062", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj2?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "e93bbc7db8ba8527ce1f59a10685e390/7649773675001521754;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj2?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3585" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:30 GMT" + ], + "Etag": [ + "CKqT6ayi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrhm7:4367,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=iU41W5KkPMaNhQTT7aKgCA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/199:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrLNXRfHYVaTqkQi4m5cXrPGhacpzI_nY7u2jgIypAszhu_cguWs5Zpkeo8WfDAz-yrs6cKrrdv_9lYxEjUS3ijxYrs-IH3M5Hfkm_9zF3a9xkycmo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyIiwKICJuYW1lIjogIm9iajIiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS4wMjFaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogInNpemUiOiAiMTYiLAogIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajI/Z2VuZXJhdGlvbj0xNTMwMjIwMTY5MDIxODY2JmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iajIiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDS3FUNmF5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDS3FUNmF5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIk5FeEN3dz09IiwKICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "13413fd8fc205d4b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "299e7eede79a80e59a1affa714bcfea4/8457199356504270954;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "11512" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:30 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:30 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vriv8:4034,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ik41W8uXCIbJN_DHnpAH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/159:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urhzq1uFKIpx_NGYAURKAGtuMliaGFM6V3ly_6lxR9zcqy0yhvrjy_GoGtbTFaI_sv2BvGfHU7r3BhKxHOVwjpYShE6u7P5GOu4Wubvfl4EMci_eyY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "efc8ff87eedf37c2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3f7e9055fe1d7a8ede1936c44cc185ac/9264907608200391034;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "4058" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:30 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:30 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vraz188:4355,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ik41W8GqIIeHhgSr9ppQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/172:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqefkcYLS45rcDAHsRzngQ-TgbEGa0icRNnIrXlgO19Mivt94_f3GzOqE6KJSKjYaUPAnt0QLXig1rX9rQLPsl7W_JGrNldVDmlWuMFLxm137NrI4A" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJuZXh0UGFnZVRva2VuIjogIkNoQnZZbW92ZDJsMGFDOXpiR0Z6YUdWeiIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqL3dpdGgvc2xhc2hlcy8xNTMwMjIwMTY5NTQ5NzU3IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcyIsCiAgICJuYW1lIjogIm9iai93aXRoL3NsYXNoZXMiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuNTQ5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuNTQ5WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInNpemUiOiAiMTYiLAogICAibWQ1SGFzaCI6ICI2NWpBZHZLZkc2R2hhUzVXK0V6ZzRBPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcz9nZW5lcmF0aW9uPTE1MzAyMjAxNjk1NDk3NTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqL3dpdGgvc2xhc2hlcy8xNTMwMjIwMTY5NTQ5NzU3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiAgIH0sCiAgICJjcmMzMmMiOiAiK3ppSThRPT0iLAogICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogIH0KIF0KfQo=" + } + }, + { + "ID": "8b421c5c4e2659a2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=ChBvYmovd2l0aC9zbGFzaGVz\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "978fd0e80e94e81d65dc7582a3e42cf1/10072334389198056329;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=ChBvYmovd2l0aC9zbGFzaGVz\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3826" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:31 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:31 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbx62:4007,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ik41W7KgOMW6N4SVrcgJ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/271:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uosb1GWyqrVurypP2l3JC-_-N9YoWX6Kx5s2E1BA8_Jk_1nt1f2OBXoSim5AGv2L4dONKW1rHHag2v3YGIYNGNbfYw8zi8lwdeEI9a__YtkuUL8R5A" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJuZXh0UGFnZVRva2VuIjogIkNnUnZZbW94IiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "d1df84046d12168f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "826851aa6dce4c2e43f12fb815888c20/10880041537104424089;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3796" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:31 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:31 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vran64:4046,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=i041W7DSE9WyhgSNxaeACw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/444:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UppX_KJTA3k09SXNfARULQUmvPmkPvkMU3opXATrCwVbJNM5zgxWAVQLd83-vrYOSSbuysBGjp4o2TYliSwhGmxVZ68ryXTQJSXES3O5JwQ0u63N5w" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "04d9031f5137a5f9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c95ec7df07c4af058438ccfac7656ae6/11615411823575723689;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "4058" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:32 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdp6:4183,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=i041W-iCLMytN6euonA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/282:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up05VGkB7YXb2vQPGys_WfbM82yYCarFdoy9HT16O-qsXExSje-ZtosvtNy6NTY9kGsAakz-8EnBeU88lx4HqfByDYoG0jDubhXhgHWqdbu78cqdUY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJuZXh0UGFnZVRva2VuIjogIkNoQnZZbW92ZDJsMGFDOXpiR0Z6YUdWeiIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqL3dpdGgvc2xhc2hlcy8xNTMwMjIwMTY5NTQ5NzU3IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcyIsCiAgICJuYW1lIjogIm9iai93aXRoL3NsYXNoZXMiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuNTQ5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuNTQ5WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInNpemUiOiAiMTYiLAogICAibWQ1SGFzaCI6ICI2NWpBZHZLZkc2R2hhUzVXK0V6ZzRBPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcz9nZW5lcmF0aW9uPTE1MzAyMjAxNjk1NDk3NTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqL3dpdGgvc2xhc2hlcy8xNTMwMjIwMTY5NTQ5NzU3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiAgIH0sCiAgICJjcmMzMmMiOiAiK3ppSThRPT0iLAogICAiZXRhZyI6ICJDTDJ2aWEyaTk5c0NFQUU9IgogIH0KIF0KfQo=" + } + }, + { + "ID": "592249ac20bddfcf", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=ChBvYmovd2l0aC9zbGFzaGVz\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3eb980ac4d3c6cac2c713bf35bf73c96/12423120075271843769;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=ChBvYmovd2l0aC9zbGFzaGVz\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3826" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:32 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrkq18:4062,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jE41W7uQB9bshgThiaaoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/372:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrHa8GZSUitR0i0ta1F8toS3GsZOuzYvCdEaPUuQyagvrWyajl1pBv4qRHNfGmIqY-Nh57MqdTJxjkvlGYGTxPYzUGsn5XbfxQJ4u0avNMngtglAGM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJuZXh0UGFnZVRva2VuIjogIkNnUnZZbW94IiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "90f376028d14eb70", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "ea299add47cd4c16008a35c16ca5f5d3/13230545756774658504;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=1\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3796" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:32 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjo78:4395,/bns/yw/borg/yw/bns/blobstore2/bitpusher/357.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jE41W-PmH8rIhgS6grugDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/357.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/357:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqwV4y6wPcZSfrXOSH-pSph8YR3o9tZa8lbHkX4zMmbqmzASp7sUmInchtseiaMlPQKIilrTnVoiueumx2Biwj3WZGcp5eQFpQeuBSpO2DWOJE4YiQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "2d51eb030b67ca32", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "82035445dbd767768e821b927095d7f3/14038254008470778584;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "7792" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:33 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:33 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbj6:4034,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jE41W62iOJLuhATIx7SoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/25:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur1LRoBMWwTPLn3xkUa8JxLYcyL1UpOW4INLhckG6de0XC6xkX1AkoZv14C35uJ7JSMkqRvb0mRKdXQx5NCIDdOqOqi5sbqiNZmk8T-1INTCQUO_wo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJuZXh0UGFnZVRva2VuIjogIkNnUnZZbW94IiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "e6620a388569db46", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9e64e891adcfa1b5cd01bd60e0f9b99d/14773623195447227624;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3796" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:33 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:33 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrb72:4249,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jU41W-P3E4rvhAT60qWICA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/4:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur3qvOrK7pxdk8gJd0v0NuoLLifLkoqPPyKvRNgzUsqXIJwFQHL3DHgb6y8zaOFKHrjqLEHAihl2GDQPElz3vLYzqiHw1_N_SCPxZgFGdR8-413emw" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "86becefc237bd364", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "baf957ec60006b74fc8e9ba81a23e8df/15581049976461604344;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "7792" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:34 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:34 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqi11:4293,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jU41W_iLLNDXN_6_oYgI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/129:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UppRHDugDAdknaDx9ZuQi1hauyRY8Ors2_1_RynX5jt5pTncgR7qgy0AkMjpqKXddjaVrKAmoSP0hQkOC1OmodNqitSN9Py2kJ9BFPqfkT0ugOfeuw" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJuZXh0UGFnZVRva2VuIjogIkNnUnZZbW94IiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "e78229e57bf974a4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "09be85a7548e3976936076cb5f6cdced/16388758228141012743;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=2\u0026pageToken=CgRvYmox\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3796" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:34 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:34 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdx5:4442,/bns/yw/borg/yw/bns/blobstore2/bitpusher/303.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jk41W-OICIn2hASxs6mIBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/303.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/303:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqkjDpaf7gZHMwLOk2SPlnp0_0GTRJ9eyZDvyqQcU7aoCY21T8M72EQPZ5lmpvQsES_1k7GF-I7n2tJd0vPj-ILKgEAWhLvgIeZri5K3KamLmEpEo8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "60d80c39ecceb057", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=3\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "82715ce074809478346276b957d4fbc8/17196183905365637143;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=3\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "11512" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:34 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:34 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjd10:4494,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jk41W6TZH8aNhAT3nLboBA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/461:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrUEQEsnXQMujKiw-fvVdfk80hrML9Q_nxq1lBctTGOsHxTLCIQVSkHfzMYGnOhu2fF00B65yrXJYUAym9PwfolQDph5Xv8TZQZaHYZY_qn6--WxoM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "a04eaf3b4ae44091", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=3\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "cdef4382eea54e043aa73d55836c6321/18003892157044980263;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=3\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "11512" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:35 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:35 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrrw17:4231,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=jk41W4LqOMqfhQTz8pv4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/574:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrbMdn-b99ajZVYCxF3REWI4Ext84-NrkzOnoblBEoY_qv5m2uDgztDa1xRFVO6a7CMoSeoAC7yIx89WHuE-DjI7-o0GOgsG2Ogypx6BqWMUBarZWQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "461bef1c57ca975b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=13\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0dd9f04e40b8fae3203af922ffe24c59/292799844800215863;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=13\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "11512" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:35 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:35 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrrx10:4196,/bns/yw/borg/yw/bns/blobstore2/bitpusher/432.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=j041W4H6E8aLhgTpw42oBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/432.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/432:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq4nuNo9VT485tUcyHrNcqi0joLJpNTs5QiYv3Px7f11v5iu4af2GvgXSg9H293blRNKTN84zZbw-m4CTVkZNER5CjZEWvKkvoIDzyrjpwsJog2Ab4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "85927c2ac92f848c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=13\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "515913ed2bfdf1e1056c3a2b85280c3f/1100506996984773958;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026maxResults=13\u0026pageToken=\u0026prefix=obj\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "11512" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:35 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:35 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vru126:4204,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=j041W-bOLIbRhQSY0oKoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/197:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrBJ22FbpCwJAxB9lZXipnXPJbVH69vaDtYFgxq61AyGNiUgfG6LM9RAeXGIajDE3J0-EtYcT_K_U21f3ezkZBuiH2fXk2sBSdmImwbgkzX1LzpBO4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIiLAogICAibmFtZSI6ICJvYmoyIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjkuMDIxWiIsCiAgICJzaXplIjogIjE2IiwKICAgIm1kNUhhc2giOiAiZFNDT0V4QTExL00wZ0xZeHU3VEFYdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMj9nZW5lcmF0aW9uPTE1MzAyMjAxNjkwMjE4NjYmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjkwMjE4NjYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0txVDZheWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajIvMTUzMDIyMDE2OTAyMTg2Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJORXhDd3c9PSIsCiAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "48b121210ad9b807", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d9d4cb100b4384cbe3c86b7cdbb33b94/2715640930183643238;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/59,/bns/xg/borg/xg/bns/blobstore2/bitpusher/121.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=j041W7fhOuS6_QSfwby4Dg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/121.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/121:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uqb1opt0M-MHQO-wrQZpYCtx5nnQJrZOfLQCT0HvtbVRwvFuwNf4tfYKJ8QR56FyZZKHGMVeHXt5YUc4UZouoyppkosKw" + ] + }, + "Body": "s2D3fRXXzuQN7D8dXeoO1Q==" + } + }, + { + "ID": "7829287913c9fe11", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a8147feb8239640e7b1b8af62173b291/4258437997669319558;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/23,/bns/xg/borg/xg/bns/blobstore2/bitpusher/47.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W-voCqKw_QSj-JawBw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/47.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/47:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrwT9_0XcT8hjdaDGWZiNWSJ0LG8CbFdEhZW28FXnYMd5kxDsVp0u1O5s3pRMCLgUV8LGjRlIxcWyc73VUqAlBol6he-w" + ] + }, + "Body": "s2D3fRXXzuQN7D8dXeoO1Q==" + } + }, + { + "ID": "9f5cfdd9997d31af", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj2", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "22b3ac0e98ccbbbf53f1a64098277d41/5873571926573352613;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj2" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"75208e131035d7f33480b631bbb4c05f\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:29 GMT" + ], + "X-Goog-Generation": [ + "1530220169021866" + ], + "X-Goog-Hash": [ + "crc32c=NExCww==", + "md5=dSCOExA11/M0gLYxu7TAXw==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/40,/bns/xg/borg/xg/bns/blobstore2/bitpusher/149.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W-3ZDcqy_QTb176YCQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/149.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/149:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqM18bz2itPeoojZNo52cV0pihD2B6eQ0ApscmLqKNR_ZzrfY4C6X0Jybs23qkd11PnQLHTHPmtlVZpx92MCrPLIHlKsw" + ] + }, + "Body": "YoTebLYcFjV2gNW/p+1gbQ==" + } + }, + { + "ID": "fc492c4202ecd7c5", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj2", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "69669d82c3f213ea7407a7d932e47323/7488705859772221893;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj2" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"75208e131035d7f33480b631bbb4c05f\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:29 GMT" + ], + "X-Goog-Generation": [ + "1530220169021866" + ], + "X-Goog-Hash": [ + "crc32c=NExCww==", + "md5=dSCOExA11/M0gLYxu7TAXw==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/11,/bns/xg/borg/xg/bns/blobstore2/bitpusher/131.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W4-sFOuw_QTclaj4Bg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/131.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/131:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upnse8HoBW-0YldZuhLl7hpJGmqSe-IV1uBrhodTGvgIG7CEOXus2bV9tAEtG3SrEKIK6Eyei4Cfg1v9AQ6EKtNJHi-jw" + ] + }, + "Body": "YoTebLYcFjV2gNW/p+1gbQ==" + } + }, + { + "ID": "e707b62778940528", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj/with/slashes", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5b6840135aa7aaa1d01e2a9ac76eaae3/9031783298444856548;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj/with/slashes" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"eb98c076f29f1ba1a1692e56f84ce0e0\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:29 GMT" + ], + "X-Goog-Generation": [ + "1530220169549757" + ], + "X-Goog-Hash": [ + "crc32c=+ziI8Q==", + "md5=65jAdvKfG6GhaS5W+Ezg4A==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/31,/bns/xg/borg/xg/bns/blobstore2/bitpusher/64.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W6ycF-G6_QTXlpDYCA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/64.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/64:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpKzf-53kkMXoQUtpGD6BOpmyYJ627jn_JVRYrT_KDTo7y6zlRkRdggAPZRS4voYA-NC5-zfenMRD3eENg-RTGJlQDzpA" + ] + }, + "Body": "RDqZVDGy37wllwrj4/ngwA==" + } + }, + { + "ID": "620b570fcaafd08e", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj/with/slashes", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f3e7e4db6c3c58dc380b06c6cf8db4d4/10646918331138576132;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj/with/slashes" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"eb98c076f29f1ba1a1692e56f84ce0e0\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:29 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:29 GMT" + ], + "X-Goog-Generation": [ + "1530220169549757" + ], + "X-Goog-Hash": [ + "crc32c=+ziI8Q==", + "md5=65jAdvKfG6GhaS5W+Ezg4A==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/52,/bns/xg/borg/xg/bns/blobstore2/bitpusher/30.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W5HCHMWx_QTMs5DgCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/30.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/30:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqDZk2HckUqyCkuPESHFvxA-hnpRC85igBPKymvh0PLBv81_2wGTXb9w8w7WheC87rgs_9VRv2IB7Tw_R3gZakItErVXg" + ] + }, + "Body": "RDqZVDGy37wllwrj4/ngwA==" + } + }, + { + "ID": "29b6e6f2f843c43d", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "Range": [ + "bytes=0-15" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f587be6cffd48a0a576a0486cfc3be00/12189995765516309027;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/17,/bns/xg/borg/xg/bns/blobstore2/bitpusher/159.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W9akH462_QTv9L6YCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/159.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/159:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur2Yeddnh9InceCCZ7DmPoKwpkPWpe14OtDUZ784oGSwsssCD4ab4cXxY0sde3ilZkZb1ruc0Gigl595wjGEWSXu7FtYA" + ] + }, + "Body": "s2D3fRXXzuQN7D8dXeoO1Q==" + } + }, + { + "ID": "c8d6a5cabc8d00bf", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "Range": [ + "bytes=0-7" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d84eac24f0d488af979bcf8aa3dd638e/13805129698715178307;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 206, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "8" + ], + "Content-Range": [ + "bytes 0-7/16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/14,/bns/xg/borg/xg/bns/blobstore2/bitpusher/101.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W7HcIau9_QTV4ZuIDA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/101.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/101:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrRC_wYY0I5C-35b6g5QzR6l19muTAGLTrkOz38d2HiAYn4A9Cy06B0bZXT8EhDF3B1ATbzD9SPWITUqG6LfvndqBhDLQ" + ] + }, + "Body": "s2D3fRXXzuQ=" + } + }, + { + "ID": "97a1b1b7d9db0df7", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "Range": [ + "bytes=8-23" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fc6bc92fd596da040350682b2dd27d1d/15420263631914113122;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 206, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "8" + ], + "Content-Range": [ + "bytes 8-15/16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/38,/bns/xg/borg/xg/bns/blobstore2/bitpusher/67.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W9DYJO-5_QSkq6PACQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/67.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/67:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uquu66Cg0MSbBKLvE04TCqVEkY26mdrw35Qk2-OU7sSVQr9zCbM2FgWS1HBnbzcNfTGIEVQ5zBJ2kqB5L1cDC-y3UVVuw" + ] + }, + "Body": "Dew/HV3qDtU=" + } + }, + { + "ID": "60456bd2d2c87bee", + "Request": { + "Method": "HEAD", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3e6f7f4f1c9dbf4e5c400d441bcc06a5/16963342170081532802;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/34,/bns/xg/borg/xg/bns/blobstore2/bitpusher/160.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W_bVJ-u__QT7z4OgCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/160.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/160:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrxfZYPdc10vAKQrjaUhsSO4dbFIJiiRno-4bvrmywb2SjAs5WdQicMOs0tKC6gKPVreEtFGvueNbpsO4Q-Tg-Zq86KUg" + ] + }, + "Body": "" + } + }, + { + "ID": "6e68b0af9f2eafb2", + "Request": { + "Method": "HEAD", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0144029a94c7dcb3c1b1d65bc499f55b/131732029570981537;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/42,/bns/xg/borg/xg/bns/blobstore2/bitpusher/38.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W4aEKuu3_QSPz4KIBw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/38.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/38:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrnVCrUCHZbxD0JKkwA9mldooWkAAiAan_VAi4W2PM6eeBb6Zqm9TuoNRjvm5ycFuNrohJ8z_dfOGmIAeVQIP1GIwu3lg" + ] + }, + "Body": "" + } + }, + { + "ID": "379730b7842e0e91", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "Range": [ + "bytes=8-" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5b218a79cbb70365843766280e596346/1674809468243550657;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 206, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "8" + ], + "Content-Range": [ + "bytes 8-15/16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/50,/bns/xg/borg/xg/bns/blobstore2/bitpusher/103.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W_rCLOy4_QSK34WgDA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/103.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/103:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqeRZdHlDOetIbvTn3vPdolDhBIbyzbCX_bT-Dxvyin7ZlI66tviZBBqF0W9ARTZLxZmqQDxPGTLv2u3vw7ImSfZeq_0A" + ] + }, + "Body": "Dew/HV3qDtU=" + } + }, + { + "ID": "ca917a8409eca3c9", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "Range": [ + "bytes=0-31" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5647ecad7d379c8ec19a116413269b39/3289943401442485472;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/37,/bns/xg/borg/xg/bns/blobstore2/bitpusher/74.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W43qLu2y_QTGyoSQAg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/74.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/74:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqY4LMAegVwug1y4tPpL09OIpgRRok9upPxefHqakSWXIMBTEBXwPB28RwWWlg-8L4LXMnXq8lwDj9CwhJlDTmYeinrsg" + ] + }, + "Body": "s2D3fRXXzuQN7D8dXeoO1Q==" + } + }, + { + "ID": "cb0965cbfc92ba76", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Authorization": [ + "REDACTED" + ], + "Range": [ + "bytes=32-41" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1737a1980a90d18fda6edb3ffab3e1e8/4833021939609904896;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 416, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "167" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/3,/bns/xg/borg/xg/bns/blobstore2/bitpusher/155.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W6mnMYy4_QSw05CACg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/155.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/155:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur6J9bM8geC6p6R-DClohGS_2fbZZQQdnomhj-vFnmCTx0EQFBaARrociLC8gmj1jWY5ZxmjFtYRDFsyfK2EcqT7_vkIg" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+SW52YWxpZFJhbmdlPC9Db2RlPjxNZXNzYWdlPlRoZSByZXF1ZXN0ZWQgcmFuZ2UgY2Fubm90IGJlIHNhdGlzZmllZC48L01lc3NhZ2U+PERldGFpbHM+Ynl0ZXM9MzItNDE8L0RldGFpbHM+PC9FcnJvcj4=" + } + }, + { + "ID": "688ddec595938bb4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3797ab06b0d1274de2ceaade021977ae/6448155868513937951;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3585" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:36 GMT" + ], + "Etag": [ + "CLmfy6yi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrfw21:4154,/bns/yw/borg/yw/bns/blobstore2/bitpusher/533.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W8WfNIS1hQSt54II" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/533.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/533:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrsQTG9A8R97Dt1j-4A0Fwxfet7hrERRNlYuLUW_EJAEueiK5ZQhcRokbsdvieH_v8l01rQQY6JbED3YKaW6Ln7YxG0nobocofM14gnacORyuJYqsY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxIiwKICJuYW1lIjogIm9iajEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOC41MjlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogInNpemUiOiAiMTYiLAogIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajE/Z2VuZXJhdGlvbj0xNTMwMjIwMTY4NTMxODk3JmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iajEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIjBTRzRpdz09IiwKICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "a6bc0186787568ea", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "467e894c85a51a84e532b306fdd7f2ae/8063289801712807231;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2951" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:37 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:37 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrb72:4249,/bns/yw/borg/yw/bns/blobstore2/bitpusher/473.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kE41W4fsOJbkhASluqCQBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/473.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/473:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UprxJYbtq9DV_89EUeAxYWUcsLi3dTL4gW_J2722jCGD_fhzDqr0l5wUZ_SxdFOqxCWiLZOzKAObhoSIr9nLvkhmA1FZwKlwB_tSujZqqqbuPHLVXo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI2LjMyNVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjUiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FVPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FVPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FVPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQVU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInZlcnNpb25pbmciOiB7CiAgImVuYWJsZWQiOiBmYWxzZQogfSwKICJsaWZlY3ljbGUiOiB7CiAgInJ1bGUiOiBbCiAgIHsKICAgICJhY3Rpb24iOiB7CiAgICAgInR5cGUiOiAiRGVsZXRlIgogICAgfSwKICAgICJjb25kaXRpb24iOiB7CiAgICAgImFnZSI6IDMwCiAgICB9CiAgIH0KICBdCiB9LAogImxhYmVscyI6IHsKICAibDEiOiAidjIiLAogICJuZXciOiAibmV3IgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FVPSIKfQo=" + } + }, + { + "ID": "e14d6952df65c642", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c0c20e7b9defa1952944f1201170891f/9606367240385441886;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3865" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:37 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220477000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vray64:4443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kU41W9SfDcLxhATkzq7YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/221:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqDHX0nOIPHd8vFxRuw2Pv3VEnIpuGgUbc6KwnZYMSvxGGT_veQwvbFw0VO6UDmFnUD41Vmxbsdp11OreHm1i-sKov27EMO3lRpJqHeoUh7rIAsbo8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiMTYiLAogIm9iamVjdFNpemUiOiAiMTYiLAogImRvbmUiOiB0cnVlLAogInJlc291cmNlIjogewogICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMS8xNTMwMjIwMTc3NzMyMTM4IiwKICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvcHktb2JqMSIsCiAgIm5hbWUiOiAiY29weS1vYmoxIiwKICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3NzczMjEzOCIsCiAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluIiwKICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTozNy43MzFaIiwKICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjM3LjczMVoiLAogICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjM3LjczMVoiLAogICJzaXplIjogIjE2IiwKICAibWQ1SGFzaCI6ICIwUTgyMjdzZVZMNkJVT01zS2NGbzl3PT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvcHktb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNzc3MzIxMzgmYWx0PW1lZGlhIiwKICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgImFjbCI6IFsKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxLzE1MzAyMjAxNzc3MzIxMzgvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb3B5LW9iajEvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY29weS1vYmoxIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNzc3MzIxMzgiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJPV05FUiIsCiAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICJ0ZWFtIjogIm93bmVycyIKICAgIH0sCiAgICAiZXRhZyI6ICJDS3JrL0xDaTk5c0NFQUU9IgogICB9LAogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajEvMTUzMDIyMDE3NzczMjEzOC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb3B5LW9iajEvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogImNvcHktb2JqMSIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc3NzMyMTM4IiwKICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgIH0sCiAgICAiZXRhZyI6ICJDS3JrL0xDaTk5c0NFQUU9IgogICB9LAogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajEvMTUzMDIyMDE3NzczMjEzOC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb3B5LW9iajEvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogImNvcHktb2JqMSIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc3NzMyMTM4IiwKICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICB9LAogICAgImV0YWciOiAiQ0tyay9MQ2k5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxLzE1MzAyMjAxNzc3MzIxMzgvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvcHktb2JqMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogImNvcHktb2JqMSIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc3NzMyMTM4IiwKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAicm9sZSI6ICJPV05FUiIsCiAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgImV0YWciOiAiQ0tyay9MQ2k5OXNDRUFFPSIKICAgfQogIF0sCiAgIm93bmVyIjogewogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiAgfSwKICAiY3JjMzJjIjogIjBTRzRpdz09IiwKICAiZXRhZyI6ICJDS3JrL0xDaTk5c0NFQUU9IgogfQp9Cg==" + } + }, + { + "ID": "313e21219dd1c35e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "31" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "630509a56d2fff1ce9dac023818b28c3/11221502268784259966;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb250ZW50RW5jb2RpbmciOiJpZGVudGl0eSJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3827" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:38 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220477000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vri10:4099,/bns/yw/borg/yw/bns/blobstore2/bitpusher/168.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kU41W-GLNISPN-3XkeAI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/168.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/168:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo6kGByxRE4u32D8cDjVBmVxpFPWsR2OjucDWN7W7VdO7c_W-yP7jy-HzhVr2FGtjJAWvjAvdxCrw0KLJV-S4tmVFdML1WocaDbAenWN4K2E5tLUFk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiMTYiLAogIm9iamVjdFNpemUiOiAiMTYiLAogImRvbmUiOiB0cnVlLAogInJlc291cmNlIjogewogICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMS8xNTMwMjIwMTc4MjQ2NzY1IiwKICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvcHktb2JqMSIsCiAgIm5hbWUiOiAiY29weS1vYmoxIiwKICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3ODI0Njc2NSIsCiAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjM4LjI0NVoiLAogICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzguMjQ1WiIsCiAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzguMjQ1WiIsCiAgInNpemUiOiAiMTYiLAogICJtZDVIYXNoIjogIjBRODIyN3NlVkw2QlVPTXNLY0ZvOXc9PSIsCiAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29weS1vYmoxP2dlbmVyYXRpb249MTUzMDIyMDE3ODI0Njc2NSZhbHQ9bWVkaWEiLAogICJjb250ZW50RW5jb2RpbmciOiAiaWRlbnRpdHkiLAogICJhY2wiOiBbCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMS8xNTMwMjIwMTc4MjQ2NzY1L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29weS1vYmoxL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogImNvcHktb2JqMSIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc4MjQ2NzY1IiwKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJvd25lcnMiCiAgICB9LAogICAgImV0YWciOiAiQ08yWW5MR2k5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxLzE1MzAyMjAxNzgyNDY3NjUvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29weS1vYmoxL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJjb3B5LW9iajEiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3ODI0Njc2NSIsCiAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJPV05FUiIsCiAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICB9LAogICAgImV0YWciOiAiQ08yWW5MR2k5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxLzE1MzAyMjAxNzgyNDY3NjUvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29weS1vYmoxL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJjb3B5LW9iajEiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3ODI0Njc2NSIsCiAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJSRUFERVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgfSwKICAgICJldGFnIjogIkNPMlluTEdpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMS8xNTMwMjIwMTc4MjQ2NzY1L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb3B5LW9iajEvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJjb3B5LW9iajEiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3ODI0Njc2NSIsCiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJldGFnIjogIkNPMlluTEdpOTlzQ0VBRT0iCiAgIH0KICBdLAogICJvd25lciI6IHsKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogIH0sCiAgImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAgImV0YWciOiAiQ08yWW5MR2k5OXNDRUFFPSIKIH0KfQo=" + } + }, + { + "ID": "8663b7abbb7c6cb4", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "193" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "bcbda194e19cea602b94b8d06148507c/12764579707456894621;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJhY2wiOlt7ImVudGl0eSI6ImRvbWFpbi1nb29nbGUuY29tIiwicm9sZSI6IlJFQURFUiJ9XSwiYnVja2V0IjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwiY29udGVudExhbmd1YWdlIjoiZW4iLCJjb250ZW50VHlwZSI6InRleHQvaHRtbCIsIm1ldGFkYXRhIjp7ImtleSI6InZhbHVlIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2345" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:38 GMT" + ], + "Etag": [ + "CLmfy6yi99sCEAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220478000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbj127:4134,/bns/yw/borg/yw/bns/blobstore2/bitpusher/325.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kk41W5GbH4yXhATojpP4BQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/325.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/325:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UphSvHqQuG1fJP5k8TdMfWif4dSYo5aQgcWlrQItsCbrzG3SHotYpgn2OYYSoYJe3kmBr0HxryuYtASxUsI9JTRqA-q8M3kX6gkw7uRJrpUa4lofpE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxIiwKICJuYW1lIjogIm9iajEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMiIsCiAiY29udGVudFR5cGUiOiAidGV4dC9odG1sIiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTozOC42MTBaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MjguNTI5WiIsCiAic2l6ZSI6ICIxNiIsCiAibWQ1SGFzaCI6ICIwUTgyMjdzZVZMNkJVT01zS2NGbzl3PT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMT9nZW5lcmF0aW9uPTE1MzAyMjAxNjg1MzE4OTcmYWx0PW1lZGlhIiwKICJjb250ZW50TGFuZ3VhZ2UiOiAiZW4iLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogIm1ldGFkYXRhIjogewogICJrZXkiOiAidmFsdWUiCiB9LAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS8xNTMwMjIwMTY4NTMxODk3L2RvbWFpbi1nb29nbGUuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC9kb21haW4tZ29vZ2xlLmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJvYmoxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogICAicm9sZSI6ICJSRUFERVIiLAogICAiZG9tYWluIjogImdvb2dsZS5jb20iLAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iajEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiMFNHNGl3PT0iLAogImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFJPSIKfQo=" + } + }, + { + "ID": "d01924ee22a5e28b", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "120" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "e7282820e82b7404aa715108087c0dad/14379432169974020541;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLCJjb250ZW50TGFuZ3VhZ2UiOm51bGwsImNvbnRlbnRUeXBlIjpudWxsLCJtZXRhZGF0YSI6bnVsbH0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2254" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:38 GMT" + ], + "Etag": [ + "CLmfy6yi99sCEAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220478000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrca6:4103,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kk41W62_LIaIN5_KqfgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/157:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoyIO4lSbzcv6b9Z6ZtTiGpW4ZQPAN_XdliDqTJ_08kdxqCaFJQJMODd-XneaT913FVG2lxQwQgeqNip6z1Oz_Oail_AlWein-v_QZqpRR41Y-TEZk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxIiwKICJuYW1lIjogIm9iajEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMyIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOC41MjlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzguODA0WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogInNpemUiOiAiMTYiLAogIm1kNUhhc2giOiAiMFE4MjI3c2VWTDZCVU9Nc0tjRm85dz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajE/Z2VuZXJhdGlvbj0xNTMwMjIwMTY4NTMxODk3JmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTcvZG9tYWluLWdvb2dsZS5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogIm9iajEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgImVudGl0eSI6ICJkb21haW4tZ29vZ2xlLmNvbSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS8xNTMwMjIwMTY4NTMxODk3L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAib2JqMSIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNMbWZ5NnlpOTlzQ0VBTT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICIwU0c0aXc9PSIsCiAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQU09Igp9Cg==" + } + }, + { + "ID": "6045bf7b735b0a67", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=7e7e4a23d8ea62390b67358209339449e904e1d02dae853d0d01ee350328" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7c82ff278ce3f89423a206bc536383a0/15187140421670206156;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS03ZTdlNGEyM2Q4ZWE2MjM5MGI2NzM1ODIwOTMzOTQ0OWU5MDRlMWQwMmRhZTg1M2QwZDAxZWUzNTAzMjgNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJjaGVja3N1bS1vYmplY3QifQoNCi0tN2U3ZTRhMjNkOGVhNjIzOTBiNjczNTgyMDkzMzk0NDllOTA0ZTFkMDJkYWU4NTNkMGQwMWVlMzUwMzI4DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KaGVsbG93b3JsZA0KLS03ZTdlNGEyM2Q4ZWE2MjM5MGI2NzM1ODIwOTMzOTQ0OWU5MDRlMWQwMmRhZTg1M2QwZDAxZWUzNTAzMjgtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3737" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:39 GMT" + ], + "Etag": [ + "CLTD2LGi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220478000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrzz3:4408,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=kk41W-7_OML7hQTg1oPgDg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/306:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoZOe2FN2pXxq3rPyGdhc-Ku4vCBxdDI2EVDUe7uV9fsAhOKfhOjrpd7Xka1oSExxpmPlluHRTD2Xec5MHJadLjDnTkNy7Ke2HsVHYzODcBQ9lAcLQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jaGVja3N1bS1vYmplY3QvMTUzMDIyMDE3OTIzNTI1MiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NoZWNrc3VtLW9iamVjdCIsCiAibmFtZSI6ICJjaGVja3N1bS1vYmplY3QiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3OTIzNTI1MiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTozOS4yMzNaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzkuMjMzWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjM5LjIzM1oiLAogInNpemUiOiAiMTAiLAogIm1kNUhhc2giOiAiL0Y0RGpUaWxjRElJVkVIbi9uQVFzQT09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NoZWNrc3VtLW9iamVjdD9nZW5lcmF0aW9uPTE1MzAyMjAxNzkyMzUyNTImYWx0PW1lZGlhIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NoZWNrc3VtLW9iamVjdC8xNTMwMjIwMTc5MjM1MjUyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jaGVja3N1bS1vYmplY3QvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNoZWNrc3VtLW9iamVjdCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNzkyMzUyNTIiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNMVEQyTEdpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY2hlY2tzdW0tb2JqZWN0LzE1MzAyMjAxNzkyMzUyNTIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jaGVja3N1bS1vYmplY3QvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjaGVja3N1bS1vYmplY3QiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5MjM1MjUyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNMVEQyTEdpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY2hlY2tzdW0tb2JqZWN0LzE1MzAyMjAxNzkyMzUyNTIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jaGVja3N1bS1vYmplY3QvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjaGVja3N1bS1vYmplY3QiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5MjM1MjUyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTFREMkxHaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NoZWNrc3VtLW9iamVjdC8xNTMwMjIwMTc5MjM1MjUyL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NoZWNrc3VtLW9iamVjdC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjaGVja3N1bS1vYmplY3QiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5MjM1MjUyIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTFREMkxHaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiVnN1MGdBPT0iLAogImV0YWciOiAiQ0xURDJMR2k5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "6a283467add5f489", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=60852424d55bc5b87b6af6dabe9c44c7d8c4504630f6fa03d5fe427c7da3" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0c4f94386b20fa363887eb8f5addac39/15994566103172955356;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS02MDg1MjQyNGQ1NWJjNWI4N2I2YWY2ZGFiZTljNDRjN2Q4YzQ1MDQ2MzBmNmZhMDNkNWZlNDI3YzdkYTMNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJ6ZXJvLW9iamVjdCJ9Cg0KLS02MDg1MjQyNGQ1NWJjNWI4N2I2YWY2ZGFiZTljNDRjN2Q4YzQ1MDQ2MzBmNmZhMDNkNWZlNDI3YzdkYTMNCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA0KDQoNCi0tNjA4NTI0MjRkNTViYzViODdiNmFmNmRhYmU5YzQ0YzdkOGM0NTA0NjMwZjZmYTAzZDVmZTQyN2M3ZGEzLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3672" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:39 GMT" + ], + "Etag": [ + "CNGJ/7Gi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220479000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrp67:4155,/bns/yw/borg/yw/bns/blobstore2/bitpusher/617.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=k041W_KfH9W5hQTt_5nwBQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/617.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/617:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqIv15aSqR9gSjfZYLSmon21VwUAnxNkuJ2dspn0pTZxM6H3sSkW8lnoufNJeDG7JVLgxVm_dTJeYDys120LNgYkrIbFH3u2uFsNPLGmQ7WHem_-F8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC96ZXJvLW9iamVjdC8xNTMwMjIwMTc5ODY2ODMzIiwKICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vemVyby1vYmplY3QiLAogIm5hbWUiOiAiemVyby1vYmplY3QiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3OTg2NjgzMyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTozOS44NjZaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzkuODY2WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjM5Ljg2NloiLAogInNpemUiOiAiMCIsCiAibWQ1SGFzaCI6ICIxQjJNMlk4QXNnVHBnQW1ZN1BoQ2ZnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vemVyby1vYmplY3Q/Z2VuZXJhdGlvbj0xNTMwMjIwMTc5ODY2ODMzJmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC96ZXJvLW9iamVjdC8xNTMwMjIwMTc5ODY2ODMzL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby96ZXJvLW9iamVjdC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiemVyby1vYmplY3QiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5ODY2ODMzIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTkdKLzdHaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3plcm8tb2JqZWN0LzE1MzAyMjAxNzk4NjY4MzMvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby96ZXJvLW9iamVjdC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInplcm8tb2JqZWN0IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3OTg2NjgzMyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTkdKLzdHaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3plcm8tb2JqZWN0LzE1MzAyMjAxNzk4NjY4MzMvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby96ZXJvLW9iamVjdC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInplcm8tb2JqZWN0IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3OTg2NjgzMyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ05HSi83R2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC96ZXJvLW9iamVjdC8xNTMwMjIwMTc5ODY2ODMzL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3plcm8tb2JqZWN0L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInplcm8tb2JqZWN0IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE3OTg2NjgzMyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ05HSi83R2k5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIkFBQUFBQT09IiwKICJldGFnIjogIkNOR0ovN0dpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "34ded95cec6605e7", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1/acl/allUsers?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "98" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "42ab4aeb033292a5f82f50e8e701cca2/17537644637045473276;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1/acl/allUsers?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLCJlbnRpdHkiOiJhbGxVc2VycyIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "446" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:40 GMT" + ], + "Etag": [ + "CLmfy6yi99sCEAQ=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220478000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmm15:4085,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=k041W_zZO8LdhAT6tKGwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/625:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoY6-UW0CwaMvW5TNGFYbHsYsxRX5kagdVsCROaQ9LUo9mjKiRbzaB9Wkgc_6Og7uSGdcdJERRU7OAwdv0W3mnTNtkWESPjRRxKrNh56ZmdPxHf_8A" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS8xNTMwMjIwMTY4NTMxODk3L2FsbFVzZXJzIiwKICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMS9hY2wvYWxsVXNlcnMiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogIm9iamVjdCI6ICJvYmoxIiwKICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogImVudGl0eSI6ICJhbGxVc2VycyIsCiAicm9sZSI6ICJSRUFERVIiLAogImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFRPSIKfQo=" + } + }, + { + "ID": "dd1d1deff158b341", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "991001488e8e0f5d9d251856e505a6a1/706315971511566875;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=60" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:40 GMT" + ], + "Etag": [ + "\"d10f36dbbb1e54be8150e32c29c168f7\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:40 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:28 GMT" + ], + "X-Goog-Generation": [ + "1530220168531897" + ], + "X-Goog-Hash": [ + "crc32c=0SG4iw==", + "md5=0Q8227seVL6BUOMsKcFo9w==" + ], + "X-Goog-Metageneration": [ + "4" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "16" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/31,/bns/xg/borg/xg/bns/blobstore2/bitpusher/32.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lE41W8imFqiy_QT2gJXQCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/32.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/32:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqDYIYcHuMMhijljPeEHULmbJV5oi-_oX32R-z8Ay42iJbua6fch5_nu3gWEFLUBCqAnV-I5r6z-XVVmAHIS5wfnVJK1Q" + ] + }, + "Body": "s2D3fRXXzuQN7D8dXeoO1Q==" + } + }, + { + "ID": "592f59a35e374016", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "multipart/related; boundary=b7e421b974b4ebbbddfa0abcd4140668a78f837d2e4f7bd876954117a3ba" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a52cd24c99a1cf37de87331b764db59c/1441966629186536235;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1iN2U0MjFiOTc0YjRlYmJiZGRmYTBhYmNkNDE0MDY2OGE3OGY4MzdkMmU0ZjdiZDg3Njk1NDExN2EzYmENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJvYmoxIn0KDQotLWI3ZTQyMWI5NzRiNGViYmJkZGZhMGFiY2Q0MTQwNjY4YTc4ZjgzN2QyZTRmN2JkODc2OTU0MTE3YTNiYQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCmhlbGxvDQotLWI3ZTQyMWI5NzRiNGViYmJkZGZhMGFiY2Q0MTQwNjY4YTc4ZjgzN2QyZTRmN2JkODc2OTU0MTE3YTNiYS0tDQo=" + }, + "Response": { + "StatusCode": 401, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "30747" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:40 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "Www-Authenticate": [ + "Bearer realm=\"https://accounts.google.com/\"" + ], + "X-Google-Backends": [ + "vreb3:4090,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lE41W-OqGcfUN9mJjPgB" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/435:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "GgIYBiAB" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoO3vg5fVHOvIZB9vRWoUpw81y1iuz-R2La-GOOxPgz6KxEXIl6I00PflQIxCM36Cw_p6sqJEVZLGwuAY6nNVXbxavvt-znRoi8IPh_dtBhs53hidE" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLiIsCiAgICAibG9jYXRpb25UeXBlIjogImhlYWRlciIsCiAgICAibG9jYXRpb24iOiAiQXV0aG9yaXphdGlvbiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1MT0dJTl9SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9Y29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUxPR0lOX1JFUVVJUkVELCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPXVuYXV0aG9yaXplZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uUkVRVUlSRUQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPVJFUVVJUkVELCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5hdXRoZW50aWNhdGVkX3VzZXIsIG1lc3NhZ2U9QW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLiwgdW5uYW1lZEFyZ3VtZW50cz1bXX0sIGxvY2F0aW9uPWhlYWRlcnMuQXV0aG9yaXphdGlvbiwgbWVzc2FnZT1Bbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAxfSBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXtXV1ctQXV0aGVudGljYXRlPVtCZWFyZXIgcmVhbG09XCJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vXCJdfSwgaHR0cFN0YXR1cz11bmF1dGhvcml6ZWQsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBlcnJvclByb3RvQ29kZT1SRVFVSVJFRCwgZXJyb3JQcm90b0RvbWFpbj1nZGF0YS5Db3JlRXJyb3JEb21haW4sIGZpbHRlcmVkTWVzc2FnZT1udWxsLCBsb2NhdGlvbj1lbnRpdHkuYXV0aGVudGljYXRlZF91c2VyLCBtZXNzYWdlPUFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1oZWFkZXJzLkF1dGhvcml6YXRpb24sIG1lc3NhZ2U9QW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLiwgcmVhc29uPXJlcXVpcmVkLCBycGNDb2RlPTQwMX0gQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuYXV0aC5BdXRoZW50aWNhdG9ySW50ZXJjZXB0b3IuYWRkQ2hhbGxlbmdlSGVhZGVyKEF1dGhlbnRpY2F0b3JJbnRlcmNlcHRvci5qYXZhOjI2NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmF1dGguQXV0aGVudGljYXRvckludGVyY2VwdG9yLnByb2Nlc3NFcnJvclJlc3BvbnNlKEF1dGhlbnRpY2F0b3JJbnRlcmNlcHRvci5qYXZhOjIzMSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmF1dGguR2FpYU1pbnRJbnRlcmNlcHRvci5wcm9jZXNzRXJyb3JSZXNwb25zZShHYWlhTWludEludGVyY2VwdG9yLmphdmE6NzQ3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuQXJvdW5kSW50ZXJjZXB0b3JXcmFwcGVyLnByb2Nlc3NFcnJvclJlc3BvbnNlKEFyb3VuZEludGVyY2VwdG9yV3JhcHBlci5qYXZhOjI4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc3RhdHMuU3RhdHNCb290c3RyYXAkSW50ZXJjZXB0b3JTdGF0c1JlY29yZGVyLnByb2Nlc3NFcnJvclJlc3BvbnNlKFN0YXRzQm9vdHN0cmFwLmphdmE6MzEyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuSW50ZXJjZXB0aW9ucyRBcm91bmRJbnRlcmNlcHRpb24uaGFuZGxlRXJyb3JSZXNwb25zZShJbnRlcmNlcHRpb25zLmphdmE6MjAyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuSW50ZXJjZXB0aW9ucyRBcm91bmRJbnRlcmNlcHRpb24uYWNjZXNzJDIwMChJbnRlcmNlcHRpb25zLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuSW50ZXJjZXB0aW9ucyRBcm91bmRJbnRlcmNlcHRpb24kMS5jYWxsKEludGVyY2VwdGlvbnMuamF2YToxNDQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLmludGVyY2VwdC5JbnRlcmNlcHRpb25zJEFyb3VuZEludGVyY2VwdGlvbiQxLmNhbGwoSW50ZXJjZXB0aW9ucy5qYXZhOjEzNylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0RXhjZXB0aW9uKEFic3RyYWN0RnV0dXJlLmphdmE6NzE2KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9TE9HSU5fUkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9dW5hdXRob3JpemVkLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LmF1dGhlbnRpY2F0ZWRfdXNlciwgbWVzc2FnZT1Bbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249aGVhZGVycy5BdXRob3JpemF0aW9uLCBtZXNzYWdlPUFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS4sIHJlYXNvbj1yZXF1aXJlZCwgcnBjQ29kZT00MDF9IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdC4uLiAxOSBtb3JlXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAxLAogICJtZXNzYWdlIjogIkFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS4iCiB9Cn0K" + } + }, + { + "ID": "c1a944df53fd8fc4", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2439dc7cd39040458030bc9205c27fca/2249393410184135995;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:41 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220479000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vryy3:4212,/bns/yw/borg/yw/bns/blobstore2/bitpusher/522.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lE41W5GOLcSnhQTgoLeoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/522.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/522:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoRRSVS9-NSrNiyahJHHoDW1i0nGUeTDiuoSZXwbiPkvqIPs7zIoM6hYHmpeDveP92nl0gtvNteFJlQyLXmx-dquE6rXU89hswkv7KP4KSOdQjnk9A" + ] + }, + "Body": "" + } + }, + { + "ID": "f502ab13eb16bade", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b5eaebec6a6cab0efb2315645f4b12c0/3056820191198512715;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12497" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:41 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:41 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220479000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjp14:4237,/bns/yw/borg/yw/bns/blobstore2/bitpusher/133.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lU41W8brCI6IhQSY0ILACg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/133.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/133:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqlsHu9J5W-VxNO-cxU9Ar22Em0uM89QE858y4rpMV0HIB6ofHYniXq5gyWC6IuibtLD43gbr4qej2U5lxoQpOfjZLoUTMQuk72ADa418PBIktpgXM" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajEiLAogICAgImRlYnVnSW5mbyI6ICJjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1OT1RfRk9VTkQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPU5PVF9GT1VORCwgZXJyb3JQcm90b0RvbWFpbj1nZGF0YS5Db3JlRXJyb3JEb21haW4sIGZpbHRlcmVkTWVzc2FnZT1udWxsLCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQubmFtZSwgbWVzc2FnZT1ObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMSwgdW5uYW1lZEFyZ3VtZW50cz1bXX0sIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZV9pZC5uYW1lLCBtZXNzYWdlPU5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMTogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6Ok9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwNCwKICAibWVzc2FnZSI6ICJObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMSIKIH0KfQo=" + } + }, + { + "ID": "47e8d9448dceec2b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fc3f2bccad8cb44b2f00fbe3b7bc8c68/4671954124397447530;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/copy-obj1?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12437" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:41 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:41 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220469000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrw187:4237,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lU41W_HWDMHvhAS3rbPYCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/101:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrUJpe5YfSJu_icHpRBUFUMt9UV58bb8MgUc1PGysJqH4zXT0jds-8TiTaaFqaedJFsNK-_3RCVVfV6ZVkM5d64FxdEk9HvtQqqP1ZudaFzDVaqgc0" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajEiLAogICAgImRlYnVnSW5mbyI6ICJjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6MzMzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZ2V0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1OT1RfRk9VTkQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6MzMzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZ2V0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6T0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogT0JKRUNUX05PVF9GT1VORDogTm8gc3VjaCBvYmplY3Q6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb3B5LW9iajFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6MzMzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZ2V0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPU5PVF9GT1VORCwgZXJyb3JQcm90b0RvbWFpbj1nZGF0YS5Db3JlRXJyb3JEb21haW4sIGZpbHRlcmVkTWVzc2FnZT1udWxsLCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQubmFtZSwgbWVzc2FnZT1ObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMSwgdW5uYW1lZEFyZ3VtZW50cz1bXX0sIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZV9pZC5uYW1lLCBtZXNzYWdlPU5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMTogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6Ok9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE9CSkVDVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggb2JqZWN0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29weS1vYmoxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5HZXRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldE9iamVjdC5qYXZhOjMzMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmdldChPYmplY3RzRGVsZWdhdG9yLmphdmE6ODEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBPQkpFQ1RfTk9UX0ZPVU5EOiBPQkpFQ1RfTk9UX0ZPVU5EOiBObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwNCwKICAibWVzc2FnZSI6ICJObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvcHktb2JqMSIKIH0KfQo=" + } + }, + { + "ID": "024d2b7ddf821088", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed1/compose?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "156" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a802d6afab7f8bdefddf2f0c900a92fc/6215031558775114890;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed1/compose?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6Im9iajEifSx7Im5hbWUiOiJvYmoyIn0seyJuYW1lIjoib2JqL3dpdGgvc2xhc2hlcyJ9XX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "800" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:41 GMT" + ], + "Etag": [ + "CPOQ+bKi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220477000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbd184:4417,/bns/yw/borg/yw/bns/blobstore2/bitpusher/238.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lU41W-LXH8GphQST_ovoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/238.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/238:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqauuIjA7aCDmXplEE0U_AMwzdx7YVJ6LKdaS9L46_U64F-EQ-paHups1d3KpSZYpVvi16qYPV-HA2Ajg0v4CBzrp_nj6lP_NkxlySOoRfMZhQZaM0" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb21wb3NlZDEvMTUzMDIyMDE4MTg2NjYxMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbXBvc2VkMSIsCiAibmFtZSI6ICJjb21wb3NlZDEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MTg2NjYxMSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0MS44NjVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDEuODY1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQxLjg2NVoiLAogInNpemUiOiAiNDgiLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29tcG9zZWQxP2dlbmVyYXRpb249MTUzMDIyMDE4MTg2NjYxMSZhbHQ9bWVkaWEiLAogImNyYzMyYyI6ICJvdUl1NFE9PSIsCiAiY29tcG9uZW50Q291bnQiOiAzLAogImV0YWciOiAiQ1BPUStiS2k5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "c616fcd5c099c293", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/composed1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4bbc25ef894e6e26454178bf690b73ff/7830165491974049705;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/composed1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "48" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:42 GMT" + ], + "Etag": [ + "\"-CPOQ+bKi99sCEAE=\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:42 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:41 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Component-Count": [ + "3" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:41 GMT" + ], + "X-Goog-Generation": [ + "1530220181866611" + ], + "X-Goog-Hash": [ + "crc32c=ouIu4Q==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "48" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/8,/bns/xg/borg/xg/bns/blobstore2/bitpusher/56.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lU41W43MO6-5_QT08q2oCA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/56.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/56:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UogE3YK17fOi9DWYEk4cpa9m_8HM5yG4rwVgqe7i1Iy22-RV-YXCw1Un8Vt1_OWsBgSRMp6MWdiWKL0fhY0K1SsxM6kFA" + ] + }, + "Body": "s2D3fRXXzuQN7D8dXeoO1WKE3my2HBY1doDVv6ftYG1EOplUMbLfvCWXCuPj+eDA" + } + }, + { + "ID": "8901dc5a7f965e82", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed2/compose?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "182" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3e2514d221bdf6b31206abac50f017c5/9373244030141469385;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed2/compose?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLCJjb250ZW50VHlwZSI6InRleHQvanNvbiJ9LCJzb3VyY2VPYmplY3RzIjpbeyJuYW1lIjoib2JqMSJ9LHsibmFtZSI6Im9iajIifSx7Im5hbWUiOiJvYmovd2l0aC9zbGFzaGVzIn1dfQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "829" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:42 GMT" + ], + "Etag": [ + "COrnmLOi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220477000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqi23:4269,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lk41W5C1BMT2N87Rh_gH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/102:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVXdQOWhxWjhxbzB5c3VBNFFpbFdwZ3lfbldIREZ6RkxMRGtiRWRCU2xucjN3bnRQTWl2SDFJRm9OMEJTNnlBVHVnc0Jua20ydDNMOVl6dFlBVE9hVEUzYkpyeDlpMC1JTmd6d3NLM1Q2cEV6cVV4TmNtRjZUTHZZQkpWUkx1al84bDdCZTlzMXlMcXI3UXNlcnV4dUdjU2lNRmFvUDJrbTl0a3FtNUFxRGhpYUNNY2w5ZXBZVERiTUkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqrDSD_F30QrEmritwRpafHlQe167lqAXjAQrw6Gg3mkjEV30T4pdMgUEhbNSTMXj2NV6gcOKH81mCJkss2uBbr-D4bJdlKpJUBbSk3y9i8zLLu9cY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb21wb3NlZDIvMTUzMDIyMDE4MjM4NTY0MiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbXBvc2VkMiIsCiAibmFtZSI6ICJjb21wb3NlZDIiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MjM4NTY0MiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9qc29uIiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQyLjM4NVoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0Mi4zODVaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDIuMzg1WiIsCiAic2l6ZSI6ICI0OCIsCiAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb21wb3NlZDI/Z2VuZXJhdGlvbj0xNTMwMjIwMTgyMzg1NjQyJmFsdD1tZWRpYSIsCiAiY3JjMzJjIjogIm91SXU0UT09IiwKICJjb21wb25lbnRDb3VudCI6IDMsCiAiZXRhZyI6ICJDT3JubUxPaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "fa892cdb56ce33b3", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/composed2", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a1f43403e2af5b7a3e0f4538908a154c/10988377963340404200;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/composed2" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "48" + ], + "Content-Type": [ + "text/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:42 GMT" + ], + "Etag": [ + "\"-COrnmLOi99sCEAE=\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:42 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:42 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Component-Count": [ + "3" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:42 GMT" + ], + "X-Goog-Generation": [ + "1530220182385642" + ], + "X-Goog-Hash": [ + "crc32c=ouIu4Q==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "48" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/63,/bns/xg/borg/xg/bns/blobstore2/bitpusher/45.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lk41W47sHbC6_QTIkYuwBw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/45.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/45:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up861RvQZNGZ9FgjowHTduZbfP0CgIbyX4f_GKliPjsEeEHUHgtboFp7bOIAy3D6r68LUJrDw-_27vpxhScFNAzlq8FtQ" + ] + }, + "Body": "s2D3fRXXzuQN7D8dXeoO1WKE3my2HBY1doDVv6ftYG1EOplUMbLfvCWXCuPj+eDA" + } + }, + { + "ID": "48e868edd18d7490", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=7da19fbb5c7edb82d69a0e3571eca6df0b4c73e6bde967ec9440c8debf90" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "ff61edfce806046a971f3b3649fd8748/11795804740043102200;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS03ZGExOWZiYjVjN2VkYjgyZDY5YTBlMzU3MWVjYTZkZjBiNGM3M2U2YmRlOTY3ZWM5NDQwYzhkZWJmOTANCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNvbnRlbnRFbmNvZGluZyI6Imd6aXAiLCJuYW1lIjoiZ3ppcC10ZXN0In0KDQotLTdkYTE5ZmJiNWM3ZWRiODJkNjlhMGUzNTcxZWNhNmRmMGI0YzczZTZiZGU5NjdlYzk0NDBjOGRlYmY5MA0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LWd6aXANCg0KH4sIAAAAAAAA/2IgEgACAAD//7E97OkoAAAADQotLTdkYTE5ZmJiNWM3ZWRiODJkNjlhMGUzNTcxZWNhNmRmMGI0YzczZTZiZGU5NjdlYzk0NDBjOGRlYmY5MC0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3662" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:43 GMT" + ], + "Etag": [ + "CL+UwLOi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220482000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnb21:4197,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=lk41W9DMKoPThQS6q4CABQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/608:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBV2laelVCeUhpa1dlOEtEeVV3YWN6M3pQN00tMldaQ01aNTlMSEpWblZPTHlCbE5wV0R3T0lmblMwM2V4MHA3MTRqczRhUVM0d0hrTTVwTnAyVG80d0NrREpMQ3hqckxDVUVsNG1XQ01OVkVLMWtwSDVHV2V1NDA0dU9MU0k0cWNNRWQ1amRCUllQNTJ4TWpMLTgwNWdQaVp3ZllSYVdLZG95RE9kakhzWVdvVE1WaEt2M1MyZ2NselUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur9KrwBYDlb2rTmOtJ5p3M5Nkx1TJIqo-HGOjGriqYBOFkNFIBl4hM4uUn_IAQ1oUgnc36WxQmJef7BJ28O-LXLxLD6VJOxmoj16Zv24PnQlN3EDao" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9nemlwLXRlc3QvMTUzMDIyMDE4MzAzMDMzNSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2d6aXAtdGVzdCIsCiAibmFtZSI6ICJnemlwLXRlc3QiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MzAzMDMzNSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24veC1nemlwIiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQzLjAyOVoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0My4wMjlaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDMuMDI5WiIsCiAic2l6ZSI6ICIyNyIsCiAibWQ1SGFzaCI6ICJPdEN3K2FSUklScUtHRkFFT2F4K3F3PT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vZ3ppcC10ZXN0P2dlbmVyYXRpb249MTUzMDIyMDE4MzAzMDMzNSZhbHQ9bWVkaWEiLAogImNvbnRlbnRFbmNvZGluZyI6ICJnemlwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2d6aXAtdGVzdC8xNTMwMjIwMTgzMDMwMzM1L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9nemlwLXRlc3QvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImd6aXAtdGVzdCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODMwMzAzMzUiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNMK1V3TE9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvZ3ppcC10ZXN0LzE1MzAyMjAxODMwMzAzMzUvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9nemlwLXRlc3QvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJnemlwLXRlc3QiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgzMDMwMzM1IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNMK1V3TE9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvZ3ppcC10ZXN0LzE1MzAyMjAxODMwMzAzMzUvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9nemlwLXRlc3QvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJnemlwLXRlc3QiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgzMDMwMzM1IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTCtVd0xPaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2d6aXAtdGVzdC8xNTMwMjIwMTgzMDMwMzM1L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2d6aXAtdGVzdC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJnemlwLXRlc3QiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgzMDMwMzM1IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTCtVd0xPaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiOURod0JBPT0iLAogImV0YWciOiAiQ0wrVXdMT2k5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "ddf65dd5c4745d32", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/gzip-test", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "30fe7f483d1d547c2fb109738e66324c/13338882178715736599;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/gzip-test" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "none" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Type": [ + "application/x-gzip" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:43 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:43 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:43 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Accept-Encoding" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:43 GMT" + ], + "X-Goog-Generation": [ + "1530220183030335" + ], + "X-Goog-Hash": [ + "crc32c=9DhwBA==", + "md5=OtCw+aRRIRqKGFAEOax+qw==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "gzip" + ], + "X-Goog-Stored-Content-Length": [ + "27" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/2,/bns/xg/borg/xg/bns/blobstore2/bitpusher/92.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=l041W7P7C4G9_QS-lbDYAw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/92.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "gunzipped,chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/92:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UobcNcq1kFGC46XBHiGfNcVdyMURbQJxiYXlKgH9BwUdazfXFtR2CA7eMbdNodywydL4HyZ5x7rqagjZXdEuZNoUr_I-Q" + ] + }, + "Body": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" + } + }, + { + "ID": "f2e9354e39d7fcec", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj-not-exists", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "efec7d6d3344685c7a4ec9108dfa65ba/14954016111914605879;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/obj-not-exists" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "225" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:43 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:43 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/0,/bns/xg/borg/xg/bns/blobstore2/bitpusher/85.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=l041W-zEFOy2_QTdo4vQAw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/85.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/85:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uri0yYDeoFdk2V7tB7_K3FyBXHrkwYoQT8n9dI4pL31Kvefzs21ennuSKGhDkL8WmeKh9NXWXViOUCH550FeUbYbn4wdw" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+Tm9TdWNoS2V5PC9Db2RlPjxNZXNzYWdlPlRoZSBzcGVjaWZpZWQga2V5IGRvZXMgbm90IGV4aXN0LjwvTWVzc2FnZT48RGV0YWlscz5ObyBzdWNoIG9iamVjdDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai1ub3QtZXhpc3RzPC9EZXRhaWxzPjwvRXJyb3I+" + } + }, + { + "ID": "f762f3eb36f18ca9", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=1aae4b1173687e1e552a21c1e13f46ff06b464cbcddc7afcc5fa5144972d" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9d9c6370fae418c69db0b2f9aee1ef70/15761724363610725959;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xYWFlNGIxMTczNjg3ZTFlNTUyYTIxYzFlMTNmNDZmZjA2YjQ2NGNiY2RkYzdhZmNjNWZhNTE0NDk3MmQNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJzaWduZWRVUkwifQoNCi0tMWFhZTRiMTE3MzY4N2UxZTU1MmEyMWMxZTEzZjQ2ZmYwNmI0NjRjYmNkZGM3YWZjYzVmYTUxNDQ5NzJkDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW4NCg0KVGhpcyBpcyBhIHRlc3Qgb2YgU2lnbmVkVVJMLgoNCi0tMWFhZTRiMTE3MzY4N2UxZTU1MmEyMWMxZTEzZjQ2ZmYwNmI0NjRjYmNkZGM3YWZjYzVmYTUxNDQ5NzJkLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3665" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:44 GMT" + ], + "Etag": [ + "CJfI/bOi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220483000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrit75:4012,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=l041W_6AK9OkhATsyYKoCg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/521:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVTV5S0NDdGhFZmVyUVdmSnU5MVotNzRJTk5nQ2tsYVhUUFFuSm9ZZVA5Q3poc1RBa0J5M1dJejZWSmxQV2Jadk56dzFVV0lJdkNzXzdFZTRuUkxyWThVVk1ySmhqZUQ3djNuWmFJSTkwN21yQjc2TDQwUXNGaV9PUXd5QmNub1ZxeXNSdlpFUWEtb0pDbFRnZFRJN2cxdExkTmdRM0UzM0diMG9NRE5SQXEtcy1WN0RLUjZGYkI1RTQwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqIIObb67CqrJjZGGujPJBAIykqJDmdlXHQAWdaBFkxxF-zoDl0OMjwc799a4A9LiuX_wkNphZQYtzh6obDerT5owlC7oQoqL5MAVZ_dbrzQVEdIB4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9zaWduZWRVUkwvMTUzMDIyMDE4NDAzNjM3NSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3NpZ25lZFVSTCIsCiAibmFtZSI6ICJzaWduZWRVUkwiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NDAzNjM3NSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0NC4wMzVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDQuMDM1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQ0LjAzNVoiLAogInNpemUiOiAiMjkiLAogIm1kNUhhc2giOiAiSnl4dmd3bTluMk1zckdUTVBiTWVZQT09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3NpZ25lZFVSTD9nZW5lcmF0aW9uPTE1MzAyMjAxODQwMzYzNzUmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3NpZ25lZFVSTC8xNTMwMjIwMTg0MDM2Mzc1L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9zaWduZWRVUkwvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInNpZ25lZFVSTCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODQwMzYzNzUiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNKZkkvYk9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvc2lnbmVkVVJMLzE1MzAyMjAxODQwMzYzNzUvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9zaWduZWRVUkwvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJzaWduZWRVUkwiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg0MDM2Mzc1IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNKZkkvYk9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvc2lnbmVkVVJMLzE1MzAyMjAxODQwMzYzNzUvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9zaWduZWRVUkwvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJzaWduZWRVUkwiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg0MDM2Mzc1IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDSmZJL2JPaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3NpZ25lZFVSTC8xNTMwMjIwMTg0MDM2Mzc1L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3NpZ25lZFVSTC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJzaWduZWRVUkwiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg0MDM2Mzc1IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDSmZJL2JPaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiWlRxQUx3PT0iLAogImV0YWciOiAiQ0pmSS9iT2k5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "2909f3034e24d6b6", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/defaultObjectAcl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "774aa5d4650144644bf39c7c459842c5/17304520331601617254;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/defaultObjectAcl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "136" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:46 GMT" + ], + "Etag": [ + "CAY=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220484000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrad2:4118,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=mE41W4S2OcjPhASU8bPwBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/169:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UphZ-uykk5v_9im8sU8HNODNZrG3PHS_jU3B1HIQMWlz5Xzq7AAvsZuJ_NcmUVK6OMPGeoo7lsgLweeRTTMO9-m-uUS8PD57ReqOBO5kuxefraZd7c" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQVk9Igp9Cg==" + } + }, + { + "ID": "2351849636a66d32", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/defaultObjectAcl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6528ce8ff655c97055943e9a2efcd142/473191661772743814;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/defaultObjectAcl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "860" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:46 GMT" + ], + "Etag": [ + "CAY=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:46 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220486000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrgh6:4422,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=mk41W7uhIIXGhQT1vofYAg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/288:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo6CObBwq6TxDHOaGuZBnZADr2sYexUwwIE4cZM6Woa5n2Ey447AbCGJWiYtTnCtkz53DCaMJ5enRA7rjNKTMICXhogTNsbN2z6GmMLOZ92dm4B72M" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBWT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDQVk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBWT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogICAicm9sZSI6ICJSRUFERVIiLAogICAiZG9tYWluIjogImdvb2dsZS5jb20iLAogICAiZXRhZyI6ICJDQVk9IgogIH0KIF0KfQo=" + } + }, + { + "ID": "d98ee334def0f34f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=1140cd01479a2c64a90225b404d50ba85d4a2ef71e9331520c5fa74e9b77" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "047674c33f2c372fd784b59934451106/1280899913452152469;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xMTQwY2QwMTQ3OWEyYzY0YTkwMjI1YjQwNGQ1MGJhODVkNGEyZWY3MWU5MzMxNTIwYzVmYTc0ZTliNzcNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJhY2wxIn0KDQotLTExNDBjZDAxNDc5YTJjNjRhOTAyMjViNDA0ZDUwYmE4NWQ0YTJlZjcxZTkzMzE1MjBjNWZhNzRlOWI3Nw0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0Kq9MHb1aG9kabpZe9PWcqEw0KLS0xMTQwY2QwMTQ3OWEyYzY0YTkwMjI1YjQwNGQ1MGJhODVkNGEyZWY3MWU5MzMxNTIwYzVmYTc0ZTliNzctLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "4122" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:47 GMT" + ], + "Etag": [ + "CKf/urWi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220486000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqi23:4269,/bns/yw/borg/yw/bns/blobstore2/bitpusher/405.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=mk41W8XvMsm8hgT99KeQAw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/405.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/405:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoLrEVwNy5VE-TES0hKaqSWqA0be-05nfrTIXWY_GWg7TYZCgmLaFsHJuxqmqFD3o7q-SXZtkOmyZFJYPh6cjLWlI4T3GQ2nCUKejWJnJzxuZLPEXQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wxLzE1MzAyMjAxODcxNDAwMDciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hY2wxIiwKICJuYW1lIjogImFjbDEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzE0MDAwNyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQ3LjEzOVoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0Ny4xMzlaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDcuMTM5WiIsCiAic2l6ZSI6ICIxNiIsCiAibWQ1SGFzaCI6ICJoMWNsc0Y0K0FCVGRvbTFjc04wUWlnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMT9nZW5lcmF0aW9uPTE1MzAyMjAxODcxNDAwMDcmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDEvMTUzMDIyMDE4NzE0MDAwNy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYWNsMSIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODcxNDAwMDciLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNLZi91cldpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMS8xNTMwMjIwMTg3MTQwMDA3L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFjbDEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3MTQwMDA3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNLZi91cldpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMS8xNTMwMjIwMTg3MTQwMDA3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFjbDEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3MTQwMDA3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDS2YvdXJXaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDEvMTUzMDIyMDE4NzE0MDAwNy9kb21haW4tZ29vZ2xlLmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvZG9tYWluLWdvb2dsZS5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYWNsMSIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODcxNDAwMDciLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ0tmL3VyV2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wxLzE1MzAyMjAxODcxNDAwMDcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJhY2wxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzE0MDAwNyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0tmL3VyV2k5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIks2Ty9Ydz09IiwKICJldGFnIjogIkNLZi91cldpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "39bd214f0babdc97", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=9e2920d032337a48d45556501082fea3c6fe3b342016d99292273bc34b6e" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fb6a5b3da784870011234b949929f9f0/2088325594971678629;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS05ZTI5MjBkMDMyMzM3YTQ4ZDQ1NTU2NTAxMDgyZmVhM2M2ZmUzYjM0MjAxNmQ5OTI5MjI3M2JjMzRiNmUNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJhY2wyIn0KDQotLTllMjkyMGQwMzIzMzdhNDhkNDU1NTY1MDEwODJmZWEzYzZmZTNiMzQyMDE2ZDk5MjkyMjczYmMzNGI2ZQ0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KJYKubtu2jgTY+SuH+jDyLA0KLS05ZTI5MjBkMDMyMzM3YTQ4ZDQ1NTU2NTAxMDgyZmVhM2M2ZmUzYjM0MjAxNmQ5OTI5MjI3M2JjMzRiNmUtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "4122" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:47 GMT" + ], + "Etag": [ + "COqx37Wi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220486000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcb9:4119,/bns/yw/borg/yw/bns/blobstore2/bitpusher/388.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=m041W6WBF4q0hQSgpJTwCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/388.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/388:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo7r7nWi4H4Sws49spxiHCbiAticOsjCwgbBSw9LqfLrmOiFU1ghgJdUFHvMowypIPB0G5RbmdpbMU38Eeedzxst3L36r3sdbdVk1UW8UnxPxrl3Fo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wyLzE1MzAyMjAxODc3MzYyOTgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hY2wyIiwKICJuYW1lIjogImFjbDIiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzczNjI5OCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQ3LjczNVoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0Ny43MzVaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDcuNzM1WiIsCiAic2l6ZSI6ICIxNiIsCiAibWQ1SGFzaCI6ICJZUG9zbEJ4WWFhOHVmU3NHdTArMUpRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMj9nZW5lcmF0aW9uPTE1MzAyMjAxODc3MzYyOTgmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDIvMTUzMDIyMDE4NzczNjI5OC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYWNsMiIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODc3MzYyOTgiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNPcXgzN1dpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMi8xNTMwMjIwMTg3NzM2Mjk4L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFjbDIiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3NzM2Mjk4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNPcXgzN1dpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMi8xNTMwMjIwMTg3NzM2Mjk4L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFjbDIiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3NzM2Mjk4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDT3F4MzdXaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDIvMTUzMDIyMDE4NzczNjI5OC9kb21haW4tZ29vZ2xlLmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMi9hY2wvZG9tYWluLWdvb2dsZS5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYWNsMiIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODc3MzYyOTgiLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ09xeDM3V2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wyLzE1MzAyMjAxODc3MzYyOTgvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMi9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJhY2wyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzczNjI5OCIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ09xeDM3V2k5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIlhYTUFVZz09IiwKICJldGFnIjogIkNPcXgzN1dpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "96cd0d46514ba385", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl1/acl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "e76eaf6ce30d28dddefbcb534211ba4e/3631404133139098309;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl1/acl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3177" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:47 GMT" + ], + "Etag": [ + "CKf/urWi99sCEAE=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:47 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220486000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrno9:4236,/bns/yw/borg/yw/bns/blobstore2/bitpusher/337.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=m041W5iONMO3hgTcw4ywBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/337.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/337:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqsGecvmY1V1_kreiviQyhF_V7LxU5Boelr3sYPMFjjzO0-b4fPruC4dzT1h937eTkjhb2MFQn9x_m-u_i4QxyleU_Yx2kGLFmhiQZxT7PpaJr1Iw0" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDEvMTUzMDIyMDE4NzE0MDAwNy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYWNsMSIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODcxNDAwMDciLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNLZi91cldpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMS8xNTMwMjIwMTg3MTQwMDA3L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFjbDEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3MTQwMDA3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNLZi91cldpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMS8xNTMwMjIwMTg3MTQwMDA3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFjbDEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3MTQwMDA3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDS2YvdXJXaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDEvMTUzMDIyMDE4NzE0MDAwNy9kb21haW4tZ29vZ2xlLmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvZG9tYWluLWdvb2dsZS5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYWNsMSIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODcxNDAwMDciLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ0tmL3VyV2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wxLzE1MzAyMjAxODcxNDAwMDcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJhY2wxIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzE0MDAwNyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0tmL3VyV2k5OXNDRUFFPSIKICB9CiBdCn0K" + } + }, + { + "ID": "bc5070d802430429", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl1/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c25098d7f9b7a2d4185bedf2b82653a2/5246538066338033124;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl1/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:48 GMT" + ], + "Etag": [ + "CKf/urWi99sCEAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220486000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdm66:4408,/bns/yw/borg/yw/bns/blobstore2/bitpusher/115.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=m041W9j3OMXWhATtno6wAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/115.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/115:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UokrX1lq2KSGwbLu4HXMiVl8OFJYtJ1xU0r1vFFdj5WvglGy-O0V_gGjZh8IBq_wV0jShiBmUePqSGCho1lj_4q1NscTnnt1rn6whJBKdnA1uTCT_g" + ] + }, + "Body": "" + } + }, + { + "ID": "70b7c033bf4ab886", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/defaultObjectAcl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f1612a78c4155f9a288b333903d294e1/6789615500715700228;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/defaultObjectAcl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:49 GMT" + ], + "Etag": [ + "CAc=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220486000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrga3:4223,/bns/yw/borg/yw/bns/blobstore2/bitpusher/450.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=nE41W6TgGcSIN-P5taAD" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/450.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/450:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqGjMk4BjDMz-rV3RIrBbQVOCDwvAmXfZKwc1AF2tMMyrlbbf_6Xrs8_nzGmsU2jymmftSG08O1YY7K5rh8aSnu-O_Wlc6DShnoZKI1OJ2ws8UPIg4" + ] + }, + "Body": "" + } + }, + { + "ID": "0177228e93af4f1f", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/acl/user-jbd%40google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "109" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "ba03cf857148d0f818de668c391fc90c/8404749433914635043;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/acl/user-jbd%40google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLCJlbnRpdHkiOiJ1c2VyLWpiZEBnb29nbGUuY29tIiwicm9sZSI6IlJFQURFUiJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "412" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:51 GMT" + ], + "Etag": [ + "CAg=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220486000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrs190:4262,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=nU41W8GGM9GChQTP5Cs" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/139:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrHDS24BEnBOBsv2OFCFFJsOnI1k74OHALVpsNm82MYtll7WRNaz4qQEpT10iYZe6YI-2Os9B6PnW1hmDbFM2MWom0EXS2aoWF3CtUTjnVCZZTfYeA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvdXNlci1qYmRAZ29vZ2xlLmNvbSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvdXNlci1qYmRAZ29vZ2xlLmNvbSIsCiAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAiZW50aXR5IjogInVzZXItamJkQGdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJlbWFpbCI6ICJqYmRAZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQWc9Igp9Cg==" + } + }, + { + "ID": "84c5aadb2112f234", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/acl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2d265656b029475cf52db890b3555311/9947827972082054723;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/acl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2019" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:51 GMT" + ], + "Etag": [ + "CAg=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:09:51 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220484000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrgb23:4373,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=n041W5SFGo7ShASVqYPICg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/111:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpmzuuXBcCDwvFWNq3v9poEFtypyMMXfyyLQZOOyDWl-YK2nLwYHGdz0JY3vUJa3D10N823KnI9LCTZjwfDfr3BP_WgXQQ-JvuOj2cMS7ucPxEPqTA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBZz0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNBZz0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQWc9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3VzZXItamJkQGdvb2dsZS5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvdXNlci1qYmRAZ29vZ2xlLmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImVudGl0eSI6ICJ1c2VyLWpiZEBnb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImVtYWlsIjogImpiZEBnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ0FnPSIKICB9CiBdCn0K" + } + }, + { + "ID": "0f2d04d8b7caad02", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/acl/user-jbd%40google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "228ec9346e5b342eaa001da48009214e/11562961905280989538;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/acl/user-jbd%40google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:53 GMT" + ], + "Etag": [ + "CAk=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220484000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqd20:4135,/bns/yw/borg/yw/bns/blobstore2/bitpusher/331.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=n041W9niK8KOhgT_2Z64Bg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/331.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/331:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVjhOWUY3WG0teTQzbE8yZ3h4U0s5bXJMR2tBeVVoXzNfLVJPY3JRQVBxZjIwVFJNelpvc0t2NnFrajQtdXZscnZmTmc0c1JSWWpuTTd1WkI2elZhU3BFUHZya3E0b1U3dzE5bEtwS29UbDFwZjFDUEYyQjN5SXdDQXZGRUd6UGI4OWpQN3hLUzRwelNDcDRxM2gzUkNQOTIyTWEzMEtmM1lWa2pMbXI5YjE4SFg2cXJSVGZpUTBYVmMwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq6f1lkIod9wTp8TNM5ONZhyT1hTJ6gcmde-dv8YkWjnmjjJpMPWUEZKkRQJQTnACEVA_l92JGDnCrCd-suzJ0liqpKw94mu0TrE91SDym9NZZlePQ" + ] + }, + "Body": "" + } + }, + { + "ID": "99a955616e08e7f7", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=1cb32567e90df66d66230a07ee190f73674ec61703c4785cef837764e83c" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "eb3234186995dc227a832c9f94dad4aa/12370388681983687538;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xY2IzMjU2N2U5MGRmNjZkNjYyMzBhMDdlZTE5MGY3MzY3NGVjNjE3MDNjNDc4NWNlZjgzNzc2NGU4M2MNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJnb3BoZXIifQoNCi0tMWNiMzI1NjdlOTBkZjY2ZDY2MjMwYTA3ZWUxOTBmNzM2NzRlYzYxNzAzYzQ3ODVjZWY4Mzc3NjRlODNjDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KZGF0YQ0KLS0xY2IzMjU2N2U5MGRmNjZkNjYyMzBhMDdlZTE5MGY3MzY3NGVjNjE3MDNjNDc4NWNlZjgzNzc2NGU4M2MtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3631" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:53 GMT" + ], + "Etag": [ + "CM3Mwbii99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrm187:4148,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=oU41W7DLDMPrhAT61qjACQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/635:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqagHqVWXAyPAPQ8zAvteazAicObC_IMDwI-W_8-eNfaVFlO_MR0e3OXRJDkvpqEr9kD_aiPqpv0KZT1ZtWo8F1Ltq6jBV-rdhgSgSM96to8tVNdlE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9nb3BoZXIvMTUzMDIyMDE5MzUzOTY2MSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2dvcGhlciIsCiAibmFtZSI6ICJnb3BoZXIiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5MzUzOTY2MSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1My41MzlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTMuNTM5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjUzLjUzOVoiLAogInNpemUiOiAiNCIsCiAibWQ1SGFzaCI6ICJqWGQvT0YwOS9zaUJYU0QzU1dBbTNBPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vZ29waGVyP2dlbmVyYXRpb249MTUzMDIyMDE5MzUzOTY2MSZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvZ29waGVyLzE1MzAyMjAxOTM1Mzk2NjEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2dvcGhlci9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiZ29waGVyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5MzUzOTY2MSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ00zTXdiaWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9nb3BoZXIvMTUzMDIyMDE5MzUzOTY2MS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2dvcGhlci9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImdvcGhlciIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTM1Mzk2NjEiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ00zTXdiaWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9nb3BoZXIvMTUzMDIyMDE5MzUzOTY2MS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2dvcGhlci9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImdvcGhlciIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTM1Mzk2NjEiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNNM013YmlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvZ29waGVyLzE1MzAyMjAxOTM1Mzk2NjEvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vZ29waGVyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImdvcGhlciIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTM1Mzk2NjEiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNNM013YmlpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJydGg5MFE9PSIsCiAiZXRhZyI6ICJDTTNNd2JpaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "f0c7d123698452f3", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=427f18bdc83ba54bb911a056981fd9dac6ffed29d386def98267906e0c14" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a8dae98dd1df4c3679959294583f3922/13177814363503213698;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS00MjdmMThiZGM4M2JhNTRiYjkxMWEwNTY5ODFmZDlkYWM2ZmZlZDI5ZDM4NmRlZjk4MjY3OTA2ZTBjMTQNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiLQk9C+0YTQtdGA0L7QstC4In0KDQotLTQyN2YxOGJkYzgzYmE1NGJiOTExYTA1Njk4MWZkOWRhYzZmZmVkMjlkMzg2ZGVmOTgyNjc5MDZlMGMxNA0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCmRhdGENCi0tNDI3ZjE4YmRjODNiYTU0YmI5MTFhMDU2OTgxZmQ5ZGFjNmZmZWQyOWQzODZkZWY5ODI2NzkwNmUwYzE0LS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3983" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:54 GMT" + ], + "Etag": [ + "CM2t2bii99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqe13:4484,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=oU41W4nlJ8zuhATb8oXwCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/87:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up0Kp-_rhM3q9GmL-crBU15VTpjU1uRBHf8cV3p0slPjUQOSjvs0r7RGaKCMlZq-Ex_oYDK-N4xbOrvNyv1sac0kshzVI6Llj9hmgH6dfcayvoYmKQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC/Qk9C+0YTQtdGA0L7QstC4LzE1MzAyMjAxOTM5Mjg5MDkiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby8lRDAlOTMlRDAlQkUlRDElODQlRDAlQjUlRDElODAlRDAlQkUlRDAlQjIlRDAlQjgiLAogIm5hbWUiOiAi0JPQvtGE0LXRgNC+0LLQuCIsCiAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTkzOTI4OTA5IiwKICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjUzLjkyOFoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1My45MjhaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTMuOTI4WiIsCiAic2l6ZSI6ICI0IiwKICJtZDVIYXNoIjogImpYZC9PRjA5L3NpQlhTRDNTV0FtM0E9PSIsCiAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby8lRDAlOTMlRDAlQkUlRDElODQlRDAlQjUlRDElODAlRDAlQkUlRDAlQjIlRDAlQjg/Z2VuZXJhdGlvbj0xNTMwMjIwMTkzOTI4OTA5JmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC/Qk9C+0YTQtdGA0L7QstC4LzE1MzAyMjAxOTM5Mjg5MDkvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vLyVEMCU5MyVEMCVCRSVEMSU4NCVEMCVCNSVEMSU4MCVEMCVCRSVEMCVCMiVEMCVCOC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAi0JPQvtGE0LXRgNC+0LLQuCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTM5Mjg5MDkiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNNMnQyYmlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAv0JPQvtGE0LXRgNC+0LLQuC8xNTMwMjIwMTkzOTI4OTA5L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vJUQwJTkzJUQwJUJFJUQxJTg0JUQwJUI1JUQxJTgwJUQwJUJFJUQwJUIyJUQwJUI4L2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAi0JPQvtGE0LXRgNC+0LLQuCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTM5Mjg5MDkiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ00ydDJiaWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC/Qk9C+0YTQtdGA0L7QstC4LzE1MzAyMjAxOTM5Mjg5MDkvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby8lRDAlOTMlRDAlQkUlRDElODQlRDAlQjUlRDElODAlRDAlQkUlRDAlQjIlRDAlQjgvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICLQk9C+0YTQtdGA0L7QstC4IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5MzkyODkwOSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ00ydDJiaWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC/Qk9C+0YTQtdGA0L7QstC4LzE1MzAyMjAxOTM5Mjg5MDkvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vJUQwJTkzJUQwJUJFJUQxJTg0JUQwJUI1JUQxJTgwJUQwJUJFJUQwJUIyJUQwJUI4L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogItCT0L7RhNC10YDQvtCy0LgiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTkzOTI4OTA5IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTTJ0MmJpaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAicnRoOTBRPT0iLAogImV0YWciOiAiQ00ydDJiaWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "648ab817a7dcd4ed", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=13e42ec2afd15c942cb5cef65876e78c7133f19778e94756baaadb161a4f" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "14b11502b56570d331e174d7f2736b2e/13913466120656322193;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xM2U0MmVjMmFmZDE1Yzk0MmNiNWNlZjY1ODc2ZTc4YzcxMzNmMTk3NzhlOTQ3NTZiYWFhZGIxNjFhNGYNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJhIn0KDQotLTEzZTQyZWMyYWZkMTVjOTQyY2I1Y2VmNjU4NzZlNzhjNzEzM2YxOTc3OGU5NDc1NmJhYWFkYjE2MWE0Zg0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCmRhdGENCi0tMTNlNDJlYzJhZmQxNWM5NDJjYjVjZWY2NTg3NmU3OGM3MTMzZjE5Nzc4ZTk0NzU2YmFhYWRiMTYxYTRmLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3551" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:54 GMT" + ], + "Etag": [ + "CLWC/7ii99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmi15:4409,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ok41W-WVC8PdhQTRkIzQBQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/178:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoO53ZbJzducEuiCmkp9RIYBh54FdtgvwGDYkb-GDKmNh3iHN7hIM236Lv8X5VmWWQEu4wCKHbRC5fJ36oD8z5kuPZv064DJ7vq4d936KprcJ0k5N4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hLzE1MzAyMjAxOTQ1NDU5NzMiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hIiwKICJuYW1lIjogImEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NDU0NTk3MyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1NC41NDVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTQuNTQ1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU0LjU0NVoiLAogInNpemUiOiAiNCIsCiAibWQ1SGFzaCI6ICJqWGQvT0YwOS9zaUJYU0QzU1dBbTNBPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYT9nZW5lcmF0aW9uPTE1MzAyMjAxOTQ1NDU5NzMmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2EvMTUzMDIyMDE5NDU0NTk3My9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYSIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTQ1NDU5NzMiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNMV0MvN2lpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYS8xNTMwMjIwMTk0NTQ1OTczL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk0NTQ1OTczIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNMV0MvN2lpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYS8xNTMwMjIwMTk0NTQ1OTczL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk0NTQ1OTczIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTFdDLzdpaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2EvMTUzMDIyMDE5NDU0NTk3My91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk0NTQ1OTczIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTFdDLzdpaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAicnRoOTBRPT0iLAogImV0YWciOiAiQ0xXQy83aWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "90fdc92270436fac", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=13a394285542c06641323851ed46a911e8b79dbbbbc1c272932b0dfd5a4b" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "88f22a247a02fe4e70346891d8a59b31/14720891802175848353;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xM2EzOTQyODU1NDJjMDY2NDEzMjM4NTFlZDQ2YTkxMWU4Yjc5ZGJiYmJjMWMyNzI5MzJiMGRmZDVhNGINCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIn0KDQotLTEzYTM5NDI4NTU0MmMwNjY0MTMyMzg1MWVkNDZhOTExZThiNzlkYmJiYmMxYzI3MjkzMmIwZGZkNWE0Yg0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCmRhdGENCi0tMTNhMzk0Mjg1NTQyYzA2NjQxMzIzODUxZWQ0NmE5MTFlOGI3OWRiYmJiYzFjMjcyOTMyYjBkZmQ1YTRiLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "19919" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:55 GMT" + ], + "Etag": [ + "CMKQnLmi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlv17:4254,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=ok41W9niKIbUhQSJtoaQBA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/66:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur3VZyf-85h2phKdWb6nroMuYbaYab9Fns7toeruYmUKDVD2MYv7gEs7c1cPr_-4VNYBPZPGNMEezPWD3FQY8YKrIX7f_T4JAsGRrK7_QMSMI5JFm0" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhLzE1MzAyMjAxOTUwMjI5MTQiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwKICJuYW1lIjogImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NTAyMjkxNCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1NS4wMjFaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTUuMDIxWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU1LjAyMVoiLAogInNpemUiOiAiNCIsCiAibWQ1SGFzaCI6ICJqWGQvT0YwOS9zaUJYU0QzU1dBbTNBPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYT9nZW5lcmF0aW9uPTE1MzAyMjAxOTUwMjI5MTQmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEvMTUzMDIyMDE5NTAyMjkxNC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTUwMjI5MTQiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNNS1FuTG1pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYS8xNTMwMjIwMTk1MDIyOTE0L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk1MDIyOTE0IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNNS1FuTG1pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYS8xNTMwMjIwMTk1MDIyOTE0L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk1MDIyOTE0IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTUtRbkxtaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEvMTUzMDIyMDE5NTAyMjkxNC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk1MDIyOTE0IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTUtRbkxtaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAicnRoOTBRPT0iLAogImV0YWciOiAiQ01LUW5MbWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "6e956130833be610", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=3ea3118b335fc0969b49406c18574363665ce8cd02f1c43f2cc4175f86f1" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "498cae4d92a3cc889fc011a420e47543/15528600053855191473;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0zZWEzMTE4YjMzNWZjMDk2OWI0OTQwNmMxODU3NDM2MzY2NWNlOGNkMDJmMWM0M2YyY2M0MTc1Zjg2ZjENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCJ9Cg0KLS0zZWEzMTE4YjMzNWZjMDk2OWI0OTQwNmMxODU3NDM2MzY2NWNlOGNkMDJmMWM0M2YyY2M0MTc1Zjg2ZjENCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA0KDQpkYXRhDQotLTNlYTMxMThiMzM1ZmMwOTY5YjQ5NDA2YzE4NTc0MzYzNjY1Y2U4Y2QwMmYxYzQzZjJjYzQxNzVmODZmMS0tDQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "2986" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:55 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vref1:4135,/bns/yw/borg/yw/bns/blobstore2/bitpusher/492.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=o041W6eFCoikhwST84aoBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/492.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/492:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrFw3F9-jMwuKWVC9-xnTwYt0cW3Oh6chNioyWBQMpcuU28XKmSx90_nOTcfNwQejt2IeD5HGTjYb7VKQuPJHY2NZVUan97kLRfBaRZRMlyHbg_QXE" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiUmVxdWlyZWQiLAogICAgImRlYnVnSW5mbyI6ICJjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1udWxsLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9YmFkUmVxdWVzdCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uUkVRVUlSRUQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPW51bGwsIGVycm9yUHJvdG9Db2RlPVJFUVVJUkVELCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZS5pZC5uYW1lLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2UuaWQubmFtZSwgbWVzc2FnZT1SZXF1aXJlZCwgcmVhc29uPXJlcXVpcmVkLCBycGNDb2RlPTQwMH0gUmVxdWlyZWRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAwLAogICJtZXNzYWdlIjogIlJlcXVpcmVkIgogfQp9Cg==" + } + }, + { + "ID": "bfc3a23bf2413d53", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=2c448e20a64f79664bc9f2f5b69707bc81a3ffbcc6d4c32f483dfd2f2080" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "ea5d5fd39435fbec28830e85024a2f1a/16336026834869568193;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0yYzQ0OGUyMGE2NGY3OTY2NGJjOWYyZjViNjk3MDdiYzgxYTNmZmJjYzZkNGMzMmY0ODNkZmQyZjIwODANCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSJ9Cg0KLS0yYzQ0OGUyMGE2NGY3OTY2NGJjOWYyZjViNjk3MDdiYzgxYTNmZmJjYzZkNGMzMmY0ODNkZmQyZjIwODANCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA0KDQpkYXRhDQotLTJjNDQ4ZTIwYTY0Zjc5NjY0YmM5ZjJmNWI2OTcwN2JjODFhM2ZmYmNjNmQ0YzMyZjQ4M2RmZDJmMjA4MC0tDQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "4823" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:55 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrca6:4103,/bns/yw/borg/yw/bns/blobstore2/bitpusher/39.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=o041W4qdDYW8hQTu37noBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/39.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/39:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoU5LDIVpJ3td5YJH1dVE5w_pW6tB1KqSuIrzx425Ye5svlXaA-Fgob1GigFSp7Qr1uT4V_3hsIszwf1ULpcg8AInCdLra4ncgtCKKQUl3y5mvo-o4" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiaW52YWxpZCIsCiAgICAibWVzc2FnZSI6ICJUaGUgbWF4aW11bSBvYmplY3QgbGVuZ3RoIGlzIDEwMjQgY2hhcmFjdGVycywgYnV0IGdvdCBhIG5hbWUgd2l0aCAxMDI1IGNoYXJhY3RlcnM6ICcnYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEuLi4nJyIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1JTlZBTElEX1ZBTFVFLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89bnVsbCwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLklOVkFMSURfVkFMVUUsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPW51bGwsIGVycm9yUHJvdG9Db2RlPUlOVkFMSURfVkFMVUUsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlLmlkLm5hbWUsIG1lc3NhZ2U9VGhlIG1heGltdW0gb2JqZWN0IGxlbmd0aCBpcyAxMDI0IGNoYXJhY3RlcnMsIGJ1dCBnb3QgYSBuYW1lIHdpdGggMTAyNSBjaGFyYWN0ZXJzOiAnJ2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhLi4uJycsIHVubmFtZWRBcmd1bWVudHM9W2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhXX0sIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZS5pZC5uYW1lLCBtZXNzYWdlPVRoZSBtYXhpbXVtIG9iamVjdCBsZW5ndGggaXMgMTAyNCBjaGFyYWN0ZXJzLCBidXQgZ290IGEgbmFtZSB3aXRoIDEwMjUgY2hhcmFjdGVyczogJydhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYS4uLicnLCByZWFzb249aW52YWxpZCwgcnBjQ29kZT00MDB9IFRoZSBtYXhpbXVtIG9iamVjdCBsZW5ndGggaXMgMTAyNCBjaGFyYWN0ZXJzLCBidXQgZ290IGEgbmFtZSB3aXRoIDEwMjUgY2hhcmFjdGVyczogJydhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYS4uLicnXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJUaGUgbWF4aW11bSBvYmplY3QgbGVuZ3RoIGlzIDEwMjQgY2hhcmFjdGVycywgYnV0IGdvdCBhIG5hbWUgd2l0aCAxMDI1IGNoYXJhY3RlcnM6ICcnYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEuLi4nJyIKIH0KfQo=" + } + }, + { + "ID": "9ed96e503f84919c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=4172849951d926924647753eca6e8244dd974d5c565c0185df72a6c5dca1" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "09023a71bcd05f78724499464eaf7681/17143733987054060753;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS00MTcyODQ5OTUxZDkyNjkyNDY0Nzc1M2VjYTZlODI0NGRkOTc0ZDVjNTY1YzAxODVkZjcyYTZjNWRjYTENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJuZXdcbmxpbmVzIn0KDQotLTQxNzI4NDk5NTFkOTI2OTI0NjQ3NzUzZWNhNmU4MjQ0ZGQ5NzRkNWM1NjVjMDE4NWRmNzJhNmM1ZGNhMQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCmRhdGENCi0tNDE3Mjg0OTk1MWQ5MjY5MjQ2NDc3NTNlY2E2ZTgyNDRkZDk3NGQ1YzU2NWMwMTg1ZGY3MmE2YzVkY2ExLS0NCg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "3308" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:55 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqf26:4362,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=o041W__NEIy7N5zWgrgN" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/575:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uqc9Dj5tRU1gKUnpK5dsUTqYDQLJsCPTNEew-uyTBQ_CRxaYw5nYnQJCVO2-FfYmdQsNeB1yv5C76raH0B14oQRje_EprVl9UykDcII3-Fv8AenW3g" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiaW52YWxpZCIsCiAgICAibWVzc2FnZSI6ICJEaXNhbGxvd2VkIHVuaWNvZGUgY2hhcmFjdGVycyBwcmVzZW50IGluIG9iamVjdCBuYW1lICcnbmV3XG5saW5lcycnIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUlOVkFMSURfVkFMVUUsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1udWxsLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9YmFkUmVxdWVzdCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uSU5WQUxJRF9WQUxVRSwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9bnVsbCwgZXJyb3JQcm90b0NvZGU9SU5WQUxJRF9WQUxVRSwgZXJyb3JQcm90b0RvbWFpbj1nZGF0YS5Db3JlRXJyb3JEb21haW4sIGZpbHRlcmVkTWVzc2FnZT1udWxsLCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2UuaWQubmFtZSwgbWVzc2FnZT1EaXNhbGxvd2VkIHVuaWNvZGUgY2hhcmFjdGVycyBwcmVzZW50IGluIG9iamVjdCBuYW1lICcnbmV3XG5saW5lcycnLCB1bm5hbWVkQXJndW1lbnRzPVtuZXdcbmxpbmVzXX0sIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZS5pZC5uYW1lLCBtZXNzYWdlPURpc2FsbG93ZWQgdW5pY29kZSBjaGFyYWN0ZXJzIHByZXNlbnQgaW4gb2JqZWN0IG5hbWUgJyduZXdcbmxpbmVzJycsIHJlYXNvbj1pbnZhbGlkLCBycGNDb2RlPTQwMH0gRGlzYWxsb3dlZCB1bmljb2RlIGNoYXJhY3RlcnMgcHJlc2VudCBpbiBvYmplY3QgbmFtZSAnJ25ld1xubGluZXMnJ1xuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiRGlzYWxsb3dlZCB1bmljb2RlIGNoYXJhY3RlcnMgcHJlc2VudCBpbiBvYmplY3QgbmFtZSAnJ25ld1xubGluZXMnJyIKIH0KfQo=" + } + }, + { + "ID": "35ea04d4db1ae690", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9b6b9f683997de187c068477b57799de/17879104273542202848;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:55 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrns11:4000,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=o041W_rJE8LvhASL0qegBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/59:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpHBEx7pOu0vK4X2Amd9qOBc_bAsMUvvZwohTWWwsen5q7CX3vBFZsNdxgjhUwqbjDTI7pyHyKJHPw-gV-yZNxlXWC9Z7I_u9AuubOya3xmsxok854" + ] + }, + "Body": "" + } + }, + { + "ID": "06def560bf4659d2", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/a?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "dcbbe08ca61a51ac67cd06be6d9f8568/240349922193803248;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/a?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:56 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlc13:4291,/bns/yw/borg/yw/bns/blobstore2/bitpusher/252.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=o041W_6BNYG0N6SfkrgK" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/252.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/252:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoDajvmZLPCqviI4gFjUxbRb_f2n4zJ4OVKCIHqj-k_TAoAIsj8ZLfJk_V5dwBbjAIPupM7lYeB09gM-17g1wLFooIjgYkIMQNtbO1x0lxIAOXD0pM" + ] + }, + "Body": "" + } + }, + { + "ID": "66e43d987629dedb", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/%D0%93%D0%BE%D1%84%D0%B5%D1%80%D0%BE%D0%B2%D0%B8?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d023368525f9f123c8fc9bde081cd783/1047775603713329152;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/%D0%93%D0%BE%D1%84%D0%B5%D1%80%D0%BE%D0%B2%D0%B8?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:56 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmy17:4271,/bns/yw/borg/yw/bns/blobstore2/bitpusher/409.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pE41W6b4CNXkhQS3p57YBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/409.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/409:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urr_qCFeBrgDddi343YcOPMZCFQ40zZAYkclzzrIGurLEzJC-MIsWRJkW0JaB-aELSwN9xXrWWX7xMQVB4koaISJRKw0tCS4WHirMhhqvD67jm9idg" + ] + }, + "Body": "" + } + }, + { + "ID": "8c47e385d8035d0f", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/gopher?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4f2041f81a3a1f48f529a3803317f812/1855202384710928912;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/gopher?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:56 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220493000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrr67:4115,/bns/yw/borg/yw/bns/blobstore2/bitpusher/661.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pE41W6yPGojUhATjwKuwCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/661.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/661:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVjREeG5mSTUtSjhEaXc4NktVeXB0QzQ4d2FqRWlNRUx2YmQ1cWVGT2RzUTcyV2ZrdDVpT1Z5Zy1adWFNX2ZON3JHSEFQQ0VnWHBvUy1QRElRR0VKczdNV0U5YTJJZGVnM1JhVkVNY1pNTUFCX0dpaEFiRTBhclNTTjgzNS1wYVA3SS1Vc3pJbElrSjctby1nT0d5di1vaGhWSllrOHZSQnlqbUdNdm5WbndGcDF1NUJSdFdGNlVqcVEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrXRpFUdB6GbHVhfBkUgLF2k-TNbpsIq93B7oCXq6eR8fFlKKwp93WpK2VS5CNhpQdiPI9vNsFau6j8X0fO1mW8oqD6lxpxsK6WQwi7W1HJsXkeqAo" + ] + }, + "Body": "" + } + }, + { + "ID": "1b0d034e9ea4fe87", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=f2df8e7173d4c098211b7c07f39b42d7482c9e97a71ae864aa52041561b8" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2993a5e0f86d18ebff0cdbef758c99f8/2662909536912263967;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1mMmRmOGU3MTczZDRjMDk4MjExYjdjMDdmMzliNDJkNzQ4MmM5ZTk3YTcxYWU4NjRhYTUyMDQxNTYxYjgNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJjb250ZW50In0KDQotLWYyZGY4ZTcxNzNkNGMwOTgyMTFiN2MwN2YzOWI0MmQ3NDgyYzllOTdhNzFhZTg2NGFhNTIwNDE1NjFiOA0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCkl0IHdhcyB0aGUgYmVzdCBvZiB0aW1lcywgaXQgd2FzIHRoZSB3b3JzdCBvZiB0aW1lcy4NCi0tZjJkZjhlNzE3M2Q0YzA5ODIxMWI3YzA3ZjM5YjQyZDc0ODJjOWU5N2E3MWFlODY0YWE1MjA0MTU2MWI4LS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3648" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:57 GMT" + ], + "Etag": [ + "CKDMo7qi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220496000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdh7:4070,/bns/yw/borg/yw/bns/blobstore2/bitpusher/541.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pE41W7rKOMmChQTn2LjwAw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/541.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/541:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrkvmNLFBXpUJBoB--BVnbWPsY9NdRIHdDYPbAmZtjgnYCibolMubDlJDN5Zg-cMJQ-7kIK8eER_5VlpQF-IlU159vlqREDLrfC7i7LDtT7_ue-qEY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTcyNDI0MDAiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1Ny4yNDFaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTcuMjQxWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU3LjI0MVoiLAogInNpemUiOiAiNTIiLAogIm1kNUhhc2giOiAiSzI4NUF3S1dXZlZSZEJjQ1VYaHpOZz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQ/Z2VuZXJhdGlvbj0xNTMwMjIwMTk3MjQyNDAwJmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTcyNDI0MDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3MjQyNDAwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDS0RNbzdxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzI0MjQwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDS0RNbzdxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzI0MjQwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0tETW83cWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTcyNDI0MDAvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0tETW83cWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIkZjWE04UT09IiwKICJldGFnIjogIkNLRE1vN3FpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "f848fe9809281608", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c6d6c513c9f0d3b6914dc0f35e7e8b84/4205988075079683647;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3648" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:57 GMT" + ], + "Etag": [ + "CKDMo7qi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220497000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnb20:4079,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pU41W6vRFsaNhQTT7aKgCA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/199:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq00WHGFHQY2FpH4LHW_hqfk2bNgDJmxwOEpZzJqU6DZ8GhMZhJ7MCHn6JF3mHFBXQ474SxZP4rN1MjbFuz8yScN0lpSAs3l9tQ9fSIWMF3WGSVuDE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTcyNDI0MDAiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1Ny4yNDFaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTcuMjQxWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU3LjI0MVoiLAogInNpemUiOiAiNTIiLAogIm1kNUhhc2giOiAiSzI4NUF3S1dXZlZSZEJjQ1VYaHpOZz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQ/Z2VuZXJhdGlvbj0xNTMwMjIwMTk3MjQyNDAwJmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTcyNDI0MDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3MjQyNDAwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDS0RNbzdxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzI0MjQwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDS0RNbzdxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzI0MjQwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0tETW83cWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTcyNDI0MDAvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzI0MjQwMCIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0tETW83cWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIkZjWE04UT09IiwKICJldGFnIjogIkNLRE1vN3FpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "03dbb890c811291b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=b80003ae132fb33abc115cafb6f0c30af1a8ffe8751065b49d45a3113f18" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c27f9a0b7e2cb2ba0efd957e4d8bf0cc/5013413756582432847;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1iODAwMDNhZTEzMmZiMzNhYmMxMTVjYWZiNmYwYzMwYWYxYThmZmU4NzUxMDY1YjQ5ZDQ1YTMxMTNmMTgNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsIm5hbWUiOiJjb250ZW50In0KDQotLWI4MDAwM2FlMTMyZmIzM2FiYzExNWNhZmI2ZjBjMzBhZjFhOGZmZTg3NTEwNjViNDlkNDVhMzExM2YxOA0KQ29udGVudC1UeXBlOiB0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLTgNCg0KPGh0bWw+PGhlYWQ+PHRpdGxlPk15IGZpcnN0IHBhZ2U8L3RpdGxlPjwvaGVhZD48L2h0bWw+DQotLWI4MDAwM2FlMTMyZmIzM2FiYzExNWNhZmI2ZjBjMzBhZjFhOGZmZTg3NTEwNjViNDlkNDVhMzExM2YxOC0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3647" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:57 GMT" + ], + "Etag": [ + "CMbJx7qi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220497000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrs185:4159,/bns/yw/borg/yw/bns/blobstore2/bitpusher/410.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pU41W5LsG82_N9XTiegC" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/410.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/410:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqIDJtqxo9-WBFheeukLz43noVFaLT4o1KAL3RbjvEYD0gU7ydIVvScSlqE1rdvzShZMFNx50clcs6vKRgZa970TgSzTCsgtT8yZ2MZj1nYhyFXfEE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTc4MzE4NzgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzgzMTg3OCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04IiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU3LjgzMFoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1Ny44MzBaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTcuODMwWiIsCiAic2l6ZSI6ICI1NCIsCiAibWQ1SGFzaCI6ICJOOHA4L3M5RndkQUFubHZyL2xFQWpRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudD9nZW5lcmF0aW9uPTE1MzAyMjAxOTc4MzE4NzgmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzgzMTg3OC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY29udGVudCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTc4MzE4NzgiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNNYkp4N3FpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk3ODMxODc4L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3ODMxODc4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNNYkp4N3FpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk3ODMxODc4L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3ODMxODc4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTWJKeDdxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzgzMTg3OC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3ODMxODc4IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTWJKeDdxaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiR29VYnNRPT0iLAogImV0YWciOiAiQ01iSng3cWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "626e81a1b9ea8b77", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d54098cb76b2583850f50ce4e6f54533/6628547685486465902;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3647" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:58 GMT" + ], + "Etag": [ + "CMbJx7qi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220497000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbg79:4060,/bns/yw/borg/yw/bns/blobstore2/bitpusher/174.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pU41W4SNOoXbhQTUy7PoAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/174.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/174:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqXSy2-roNaXZlGirS3ki30np34sbTqJMeoBbYALBdQV06Iv-aL-gEMizaPOCE37aefNGVfkuOzhxZJWm8idnDJmnFxG3ecEPh4-ENXvs_VTvCfaoA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTc4MzE4NzgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5NzgzMTg3OCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04IiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU3LjgzMFoiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1Ny44MzBaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTcuODMwWiIsCiAic2l6ZSI6ICI1NCIsCiAibWQ1SGFzaCI6ICJOOHA4L3M5RndkQUFubHZyL2xFQWpRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudD9nZW5lcmF0aW9uPTE1MzAyMjAxOTc4MzE4NzgmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzgzMTg3OC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY29udGVudCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTc4MzE4NzgiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNNYkp4N3FpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk3ODMxODc4L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3ODMxODc4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNNYkp4N3FpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk3ODMxODc4L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3ODMxODc4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTWJKeDdxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5NzgzMTg3OC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk3ODMxODc4IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTWJKeDdxaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiR29VYnNRPT0iLAogImV0YWciOiAiQ01iSng3cWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "276a9f6b2ac731e3", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=89d44f73f47145425f5a2ba603bd25a49448e6e726d9ba111d8cad176971" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1021f70a254fbf9561a1fa0ab5ffdf3e/7364199442656285822;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS04OWQ0NGY3M2Y0NzE0NTQyNWY1YTJiYTYwM2JkMjVhNDk0NDhlNmU3MjZkOWJhMTExZDhjYWQxNzY5NzENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9odG1sIiwibmFtZSI6ImNvbnRlbnQifQoNCi0tODlkNDRmNzNmNDcxNDU0MjVmNWEyYmE2MDNiZDI1YTQ5NDQ4ZTZlNzI2ZDliYTExMWQ4Y2FkMTc2OTcxDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQo8aHRtbD48aGVhZD48dGl0bGU+TXkgZmlyc3QgcGFnZTwvdGl0bGU+PC9oZWFkPjwvaHRtbD4NCi0tODlkNDRmNzNmNDcxNDU0MjVmNWEyYmE2MDNiZDI1YTQ5NDQ4ZTZlNzI2ZDliYTExMWQ4Y2FkMTc2OTcxLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3632" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:58 GMT" + ], + "Etag": [ + "CPKz5rqi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220497000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrr129:4461,/bns/yw/borg/yw/bns/blobstore2/bitpusher/209.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pk41W_iDAoKlN9Wct9gI" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/209.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/209:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpHxWxraCeF9hmGibg_gmN4VBZbhzCc1SGwM52fLSJ2O5mQ3DUMnBPBQnsd0968qfS7_G68fpEsiRV5RlBOhquvQ_51XyL_V7P2-6Xr2zIjnfBNnqk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTgzMzcwMTAiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODMzNzAxMCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9odG1sIiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU4LjMzNloiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OC4zMzZaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTguMzM2WiIsCiAic2l6ZSI6ICI1NCIsCiAibWQ1SGFzaCI6ICJOOHA4L3M5RndkQUFubHZyL2xFQWpRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudD9nZW5lcmF0aW9uPTE1MzAyMjAxOTgzMzcwMTAmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODMzNzAxMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY29udGVudCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTgzMzcwMTAiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNQS3o1cnFpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk4MzM3MDEwL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4MzM3MDEwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNQS3o1cnFpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk4MzM3MDEwL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4MzM3MDEwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDUEt6NXJxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODMzNzAxMC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4MzM3MDEwIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDUEt6NXJxaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiR29VYnNRPT0iLAogImV0YWciOiAiQ1BLejVycWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "8b6203f93036dbb5", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "209deeb618ba9abfc93443ae91f661cb/8979051905173477277;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3632" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:58 GMT" + ], + "Etag": [ + "CPKz5rqi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220497000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vril66:4366,/bns/yw/borg/yw/bns/blobstore2/bitpusher/572.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pk41W4-IHIazhQSrsq7YAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/572.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/572:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoIr_GMG0ILQ7U7z9_J7KTZ_Ugw77QWQo0y1dZyPUuK23TPWZ2j-cw2JXZ4ed78ZVmgMsMyLILDf8UHCbhCpiDS0vFsKfSatIEPxVFhwYK5_cZlKlM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTgzMzcwMTAiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODMzNzAxMCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9odG1sIiwKICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU4LjMzNloiLAogInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OC4zMzZaIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTguMzM2WiIsCiAic2l6ZSI6ICI1NCIsCiAibWQ1SGFzaCI6ICJOOHA4L3M5RndkQUFubHZyL2xFQWpRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudD9nZW5lcmF0aW9uPTE1MzAyMjAxOTgzMzcwMTAmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODMzNzAxMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY29udGVudCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTgzMzcwMTAiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNQS3o1cnFpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk4MzM3MDEwL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4MzM3MDEwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNQS3o1cnFpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29udGVudC8xNTMwMjIwMTk4MzM3MDEwL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4MzM3MDEwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDUEt6NXJxaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODMzNzAxMC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4MzM3MDEwIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDUEt6NXJxaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiR29VYnNRPT0iLAogImV0YWciOiAiQ1BLejVycWk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "1d490da082c036af", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=87d496845552f7d7655171e9d649aed807c3ef958ec8acca114e0a264bcb" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d4efe0fb3a5f211b07f053078f2e83be/9786760156852820397;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS04N2Q0OTY4NDU1NTJmN2Q3NjU1MTcxZTlkNjQ5YWVkODA3YzNlZjk1OGVjOGFjY2ExMTRlMGEyNjRiY2INCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoiaW1hZ2UvanBlZyIsIm5hbWUiOiJjb250ZW50In0KDQotLTg3ZDQ5Njg0NTU1MmY3ZDc2NTUxNzFlOWQ2NDlhZWQ4MDdjM2VmOTU4ZWM4YWNjYTExNGUwYTI2NGJjYg0KQ29udGVudC1UeXBlOiBpbWFnZS9qcGVnDQoNCjxodG1sPjxoZWFkPjx0aXRsZT5NeSBmaXJzdCBwYWdlPC90aXRsZT48L2hlYWQ+PC9odG1sPg0KLS04N2Q0OTY4NDU1NTJmN2Q3NjU1MTcxZTlkNjQ5YWVkODA3YzNlZjk1OGVjOGFjY2ExMTRlMGEyNjRiY2ItLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3633" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:58 GMT" + ], + "Etag": [ + "CPnFhLui99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220496000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrab126:4331,/bns/yw/borg/yw/bns/blobstore2/bitpusher/58.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pk41W6mcIcbuN4yunbAI" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/58.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/58:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo8QqqIx2CSC4JkschiiIL4X-NEPRn0gu4fpbtT6VuUByCMMC_mUCaKWmzznMtoo68f1EFQjSUEigoWXcoUhsErEVBxVmBE4v8K4hfg4aBcvYX1Etk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAiaW1hZ2UvanBlZyIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OC44MjlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTguODI5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU4LjgyOVoiLAogInNpemUiOiAiNTQiLAogIm1kNUhhc2giOiAiTjhwOC9zOUZ3ZEFBbmx2ci9sRUFqUT09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQ/Z2VuZXJhdGlvbj0xNTMwMjIwMTk4ODMwODQxJmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4ODMwODQxIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDUG5GaEx1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODgzMDg0MS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDUG5GaEx1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODgzMDg0MS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ1BuRmhMdWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ1BuRmhMdWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIkdvVWJzUT09IiwKICJldGFnIjogIkNQbkZoTHVpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "cedc12deb6059fb8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "18a82be18aee94367d71165df3df6b31/11329837595525389517;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3633" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:59 GMT" + ], + "Etag": [ + "CPnFhLui99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220497000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbs2:4372,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=pk41W9v0OoH1N7-RjPgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/198:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVY5c1lPRXhEb2MyQmdCcVF6cjJmX3VLZklsYUU2d01Jb3dkMnc0TGt6eVZoNnJfQ19nX21KOF8tQURvN1poT0FnYlJHc2V0MHlBZ2I0b0RSX251NW8tVHFVb1dNb2M3eE5QREhmaGdtSDlIcGEydnBoY2xLR1B3UExLbVNId3YxNW9ybjZrNVdLUjRSaTdRZ0FtUU85ZnNVOWNPcW5jY2k4OGo1dDBDNGdBSFdOQ1U5TThHWE05Q3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ury2W-eAJPw7j9Yu_cF0HwKKqY0JZbb97e6ek1xsphTVm8TRIIogCJwfdlJasWrSzf3g0KgH-UUBtCkusptF4VvkUfqSnK7v6Wf-tY5JvK49tbDs_0" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50IiwKICJuYW1lIjogImNvbnRlbnQiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAiaW1hZ2UvanBlZyIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OC44MjlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTguODI5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU4LjgyOVoiLAogInNpemUiOiAiNTQiLAogIm1kNUhhc2giOiAiTjhwOC9zOUZ3ZEFBbmx2ci9sRUFqUT09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQ/Z2VuZXJhdGlvbj0xNTMwMjIwMTk4ODMwODQxJmFsdD1tZWRpYSIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNvbnRlbnQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4ODMwODQxIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDUG5GaEx1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODgzMDg0MS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDUG5GaEx1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODgzMDg0MS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ1BuRmhMdWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5ODgzMDg0MSIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ1BuRmhMdWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIkdvVWJzUT09IiwKICJldGFnIjogIkNQbkZoTHVpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "38c271bf60b23a28", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=54d5fbb1eb76657ee72b5895ed49f986df9a9274e3811c1623b9f2d3aa4d" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0a13d2be8a1d72edf6267891a6e45d71/12137264376539831772;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "LS01NGQ1ZmJiMWViNzY2NTdlZTcyYjU4OTVlZDQ5Zjk4NmRmOWE5Mjc0ZTM4MTFjMTYyM2I5ZjJkM2FhNGQNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJjdXN0b21lci1lbmNyeXB0aW9uIn0KDQotLTU0ZDVmYmIxZWI3NjY1N2VlNzJiNTg5NWVkNDlmOTg2ZGY5YTkyNzRlMzgxMWMxNjIzYjlmMmQzYWE0ZA0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCnRvcCBzZWNyZXQuDQotLTU0ZDVmYmIxZWI3NjY1N2VlNzJiNTg5NWVkNDlmOTg2ZGY5YTkyNzRlMzgxMWMxNjIzYjlmMmQzYWE0ZC0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3927" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:59 GMT" + ], + "Etag": [ + "CIKPqbui99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220499000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjn1:4228,/bns/yw/borg/yw/bns/blobstore2/bitpusher/627.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=p041W8OgCMfQhASqvYj4Bg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/627.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/627:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqXdSkusGIs30LhiZHX1Y9mBKxxGWhGh3dhwRDwr8TwCgo2aufzTJK9q5PN9sjYk5XS5m7An6H6vyhHttrRhVxhI4YDRI3ugeR1WPh6OcyAyerIBIk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uIiwKICJuYW1lIjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OS40MjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTkuNDI4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU5LjQyOFoiLAogInNpemUiOiAiMTEiLAogIm1kNUhhc2giOiAieHdXTkZhMFZkWFBtbEF3cmxjQUpjZz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24/Z2VuZXJhdGlvbj0xNTMwMjIwMTk5NDMwMDE4JmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogInIwTkdyZz09IiwKICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBRT0iLAogImN1c3RvbWVyRW5jcnlwdGlvbiI6IHsKICAiZW5jcnlwdGlvbkFsZ29yaXRobSI6ICJBRVMyNTYiLAogICJrZXlTaGEyNTYiOiAiSCtMbW5YaFJvZUk2VE1XNWJzVjZIeVVrNnB5R2MySU1icVliQVhCY3BzMD0iCiB9Cn0K" + } + }, + { + "ID": "371d81ec245776ad", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5717bbadeba43f6dcb9369c6c1793c3e/13752398305443799292;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3864" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:59 GMT" + ], + "Etag": [ + "CIKPqbui99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220499000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnv1:4379,/bns/yw/borg/yw/bns/blobstore2/bitpusher/456.scotty,acatli15:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=p041W5TGIcSGhAS92oPIAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/456.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/456:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqreX95Wn8kj-CYOGECmSzSGcyMKtOoOgUgq5xb8hjZH02EJs7RPnNuLmE5lszAuSoildmyo-ukFZNufiAVn6X1ugqn6X5UbwWKSM6LhJD1jy_Mu0o" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uIiwKICJuYW1lIjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OS40MjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTkuNDI4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU5LjQyOFoiLAogInNpemUiOiAiMTEiLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbj9nZW5lcmF0aW9uPTE1MzAyMjAxOTk0MzAwMTgmYWx0PW1lZGlhIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbiIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBRT0iLAogImN1c3RvbWVyRW5jcnlwdGlvbiI6IHsKICAiZW5jcnlwdGlvbkFsZ29yaXRobSI6ICJBRVMyNTYiLAogICJrZXlTaGEyNTYiOiAiSCtMbW5YaFJvZUk2VE1XNWJzVjZIeVVrNnB5R2MySU1icVliQVhCY3BzMD0iCiB9Cn0K" + } + }, + { + "ID": "4eb70fcee77f753a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b7977dc8db30e9f63f8e1a20c075173c/15295475744116433691;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3927" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:59 GMT" + ], + "Etag": [ + "CIKPqbui99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220499000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbk123:4181,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=p041W8CZJtPFhgTc2byYBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/485:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrHoFZefGRruC4qMn7-dXFwwWh8a5O0tNPBSnmZGlCNGwtfuKKh2iThpGt9H0e6_mdKZc5-v0P4luJ-ekS04KMD1zlaf95OYyc4maNFFnLNRg4pBXc" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uIiwKICJuYW1lIjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OS40MjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTkuNDI4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU5LjQyOFoiLAogInNpemUiOiAiMTEiLAogIm1kNUhhc2giOiAieHdXTkZhMFZkWFBtbEF3cmxjQUpjZz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24/Z2VuZXJhdGlvbj0xNTMwMjIwMTk5NDMwMDE4JmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogInIwTkdyZz09IiwKICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBRT0iLAogImN1c3RvbWVyRW5jcnlwdGlvbiI6IHsKICAiZW5jcnlwdGlvbkFsZ29yaXRobSI6ICJBRVMyNTYiLAogICJrZXlTaGEyNTYiOiAiSCtMbW5YaFJvZUk2VE1XNWJzVjZIeVVrNnB5R2MySU1icVliQVhCY3BzMD0iCiB9Cn0K" + } + }, + { + "ID": "a57e85fd3b1cfaea", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "85" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "79ac7f9febfe9df03ac3c60148a63fc1/16910610776810153531;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLCJjb250ZW50TGFuZ3VhZ2UiOiJlbiJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3890" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:09:59 GMT" + ], + "Etag": [ + "CIKPqbui99sCEAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220499000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnd9:4058,/bns/yw/borg/yw/bns/blobstore2/bitpusher/377.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=p041W5CAK4TRhATJ_5PIDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/377.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/377:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqOzpmt5l-YmRdi25NV92ucozZ7J9fWtYniJfZtuoH8c5wDNLNZatITzCc2MugTagvZEwWRS0Q-dNtCQi_Z4BV3XJ0wJqaadJs3JD5MIXuL10BX7oM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uIiwKICJuYW1lIjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMiIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OS40MjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTkuNzc3WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU5LjQyOFoiLAogInNpemUiOiAiMTEiLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbj9nZW5lcmF0aW9uPTE1MzAyMjAxOTk0MzAwMTgmYWx0PW1lZGlhIiwKICJjb250ZW50TGFuZ3VhZ2UiOiAiZW4iLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbiIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbiIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbiIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBST0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImV0YWciOiAiQ0lLUHFidWk5OXNDRUFJPSIsCiAiY3VzdG9tZXJFbmNyeXB0aW9uIjogewogICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgImtleVNoYTI1NiI6ICJIK0xtblhoUm9lSTZUTVc1YnNWNkh5VWs2cHlHYzJJTWJxWWJBWEJjcHMwPSIKIH0KfQo=" + } + }, + { + "ID": "2926e09274bd218e", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "85" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d454d2809ba7dd7e297b12ea54b82b20/7225616749947226;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLCJjb250ZW50TGFuZ3VhZ2UiOiJlbiJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3953" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:00 GMT" + ], + "Etag": [ + "CIKPqbui99sCEAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220499000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjt17:4254,/bns/yw/borg/yw/bns/blobstore2/bitpusher/114.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=p041W8C5N8buhAS64LzQCA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/114.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/114:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoIy1F234ITAjMGrfM_0b0JRoFg_x9umNWLbw2b_Or4NMkuiC7b-Q5G-Ot6SVU_nQV-kl1HA3F5dWhf-BGDGUxL9nWTTVKDpiV1WzL17yttSlstf6Y" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uIiwKICJuYW1lIjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMyIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo1OS40MjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTkuOTgzWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU5LjQyOFoiLAogInNpemUiOiAiMTEiLAogIm1kNUhhc2giOiAieHdXTkZhMFZkWFBtbEF3cmxjQUpjZz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24/Z2VuZXJhdGlvbj0xNTMwMjIwMTk5NDMwMDE4JmFsdD1tZWRpYSIsCiAiY29udGVudExhbmd1YWdlIjogImVuIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbiIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNJS1BxYnVpOTlzQ0VBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQU09IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAicjBOR3JnPT0iLAogImV0YWciOiAiQ0lLUHFidWk5OXNDRUFNPSIsCiAiY3VzdG9tZXJFbmNyeXB0aW9uIjogewogICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgImtleVNoYTI1NiI6ICJIK0xtblhoUm9lSTZUTVc1YnNWNkh5VWs2cHlHYzJJTWJxWWJBWEJjcHMwPSIKIH0KfQo=" + } + }, + { + "ID": "39306d07e8b20900", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7d7816f203a873d196cc4a87fd1843f0/1622359545653914746;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "277" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:00 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:00 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/10,/bns/xg/borg/xg/bns/blobstore2/bitpusher/40.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qE41W7DKBue3_QS0wLWABw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/40.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/40:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UolfdzStX-wAFl0LuRdl_1swWi0qR8youtSqWG4lefiz1Jx_PZP39ROzfuXFkBHqyYOdSxusNC9DnZgACtW2EaPnZACRg" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+UmVzb3VyY2VJc0VuY3J5cHRlZFdpdGhDdXN0b21lckVuY3J5cHRpb25LZXk8L0NvZGU+PE1lc3NhZ2U+VGhlIHJlc291cmNlIGlzIGVuY3J5cHRlZCB3aXRoIGEgY3VzdG9tZXIgZW5jcnlwdGlvbiBrZXkuPC9NZXNzYWdlPjxEZXRhaWxzPlRoZSByZXF1ZXN0ZWQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LjwvRGV0YWlscz48L0Vycm9yPg==" + } + }, + { + "ID": "fe458b2bd9a01e7a", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f64656d03e6a0b592a632b7e35ab4186/3237493478852849561;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Language": [ + "en" + ], + "Content-Length": [ + "11" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:00 GMT" + ], + "Etag": [ + "\"-CIKPqbui99sCEAM=\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:09:59 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:09:59 GMT" + ], + "X-Goog-Generation": [ + "1530220199430018" + ], + "X-Goog-Hash": [ + "crc32c=r0NGrg==", + "md5=xwWNFa0VdXPmlAwrlcAJcg==" + ], + "X-Goog-Metageneration": [ + "3" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "11" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/27,/bns/xg/borg/xg/bns/blobstore2/bitpusher/147.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qE41W6qiCq26_QTjx5L4CA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/147.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/147:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UolqAOK8woLeIpHzh90968j-oxlfGoRaMdpM6p35Poyh3bGCzJ8W2R2OVOQghZBmQOmm9SJGCI9i1dIymrOZO8WhzIV0g" + ] + }, + "Body": "dG9wIHNlY3JldC4=" + } + }, + { + "ID": "df90fab289df0948", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "be85febb27f4425b9090ea835c7a124d/4780290546338525881;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14415" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:00 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220500000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vreu9:4494,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qE41W_H-EMKKhATFuJ7wBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/94:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoXlpkGnK60K9y_ZFvw8x9xs6uYpDbZQrlZUL9QEtro6WOsFo1yufSZF-PfCVC2oY83qisBMQAp-8FaBbCw10pQ2Uy84HhaHOxKAhwP0JSCVnz_G7w" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVzb3VyY2VJc0VuY3J5cHRlZFdpdGhDdXN0b21lckVuY3J5cHRpb25LZXkiLAogICAgIm1lc3NhZ2UiOiAiVGhlIHRhcmdldCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuIiwKICAgICJleHRlbmRlZEhlbHAiOiAiaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL3N0b3JhZ2UvZG9jcy9lbmNyeXB0aW9uI2N1c3RvbWVyLXN1cHBsaWVkX2VuY3J5cHRpb25fa2V5cyIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFRoZSByZXF1ZXN0ZWQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMxMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLnJld3JpdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyNClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBUaGUgcmVxdWVzdGVkIG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUHJlcGFyZVJld3JpdGVPcGVyYXRpb24udmFsaWRhdGVTb3VyY2VFbmNyeXB0aW9uKFByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLmphdmE6NTg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5QcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5ydW4oUHJlcGFyZVJld3JpdGVPcGVyYXRpb24uamF2YTozMzApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3RJbnRlcm5hbChSZXdyaXRlci5qYXZhOjM1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUmV3cml0ZXIucmV3cml0ZU9iamVjdChSZXdyaXRlci5qYXZhOjMyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QucmV3cml0ZUluRnJvbnRlbmQoUmV3cml0ZU9iamVjdC5qYXZhOjIyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YToyMDYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6NTIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0Li4uIDE0IG1vcmVcblxuY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUlOVkFMSURfVkFMVUUsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBUaGUgcmVxdWVzdGVkIG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogVGhlIHJlcXVlc3RlZCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLnZhbGlkYXRlU291cmNlRW5jcnlwdGlvbihQcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5qYXZhOjU4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUHJlcGFyZVJld3JpdGVPcGVyYXRpb24ucnVuKFByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLmphdmE6MzMwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0SW50ZXJuYWwoUmV3cml0ZXIuamF2YTozNTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3QoUmV3cml0ZXIuamF2YTozMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LnJld3JpdGVJbkZyb250ZW5kKFJld3JpdGVPYmplY3QuamF2YToyMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6MjA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjUyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdC4uLiAxNCBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1odHRwczovL2Nsb3VkLmdvb2dsZS5jb20vc3RvcmFnZS9kb2NzL2VuY3J5cHRpb24jY3VzdG9tZXItc3VwcGxpZWRfZW5jcnlwdGlvbl9rZXlzLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLlJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBUaGUgcmVxdWVzdGVkIG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogVGhlIHJlcXVlc3RlZCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLnZhbGlkYXRlU291cmNlRW5jcnlwdGlvbihQcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5qYXZhOjU4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUHJlcGFyZVJld3JpdGVPcGVyYXRpb24ucnVuKFByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLmphdmE6MzMwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0SW50ZXJuYWwoUmV3cml0ZXIuamF2YTozNTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3QoUmV3cml0ZXIuamF2YTozMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LnJld3JpdGVJbkZyb250ZW5kKFJld3JpdGVPYmplY3QuamF2YToyMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6MjA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjUyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdC4uLiAxNCBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPVJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZLCBlcnJvclByb3RvRG9tYWluPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1udWxsLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1UaGUgdGFyZ2V0IG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4sIHJlYXNvbj1yZXNvdXJjZUlzRW5jcnlwdGVkV2l0aEN1c3RvbWVyRW5jcnlwdGlvbktleSwgcnBjQ29kZT00MDB9IFRoZSB0YXJnZXQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogVGhlIHJlcXVlc3RlZCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzExKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IucmV3cml0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFRoZSByZXF1ZXN0ZWQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5QcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi52YWxpZGF0ZVNvdXJjZUVuY3J5cHRpb24oUHJlcGFyZVJld3JpdGVPcGVyYXRpb24uamF2YTo1ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLnJ1bihQcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5qYXZhOjMzMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUmV3cml0ZXIucmV3cml0ZU9iamVjdEludGVybmFsKFJld3JpdGVyLmphdmE6MzU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0KFJld3JpdGVyLmphdmE6MzI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5yZXdyaXRlSW5Gcm9udGVuZChSZXdyaXRlT2JqZWN0LmphdmE6MjI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjIwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YTo1Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHQuLi4gMTQgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJUaGUgdGFyZ2V0IG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4iCiB9Cn0K" + } + }, + { + "ID": "0bfe74a87eed41bd", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "36e8b255175f1af08a2f293ad6951ad0/6395424475242558936;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Copy-Source-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Copy-Source-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Copy-Source-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "4059" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:01 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220500000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqi11:4293,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qE41W6jAIIaihQTOw5HwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/68:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uql8nyy24U3c7ik54Hzc8E_nZErbg4ho9H0jwtsR6KnlbmLWAaBpeZ5IlRYSXRx6XrmTWzZmD7tvb5Bjy5etY5T-gt7AfAoMuCAmcMXszpGHXZTnFM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiMTEiLAogIm9iamVjdFNpemUiOiAiMTEiLAogImRvbmUiOiB0cnVlLAogInJlc291cmNlIjogewogICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAxMTIxMjE2IiwKICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMiIsCiAgIm5hbWUiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMTEyMTIxNiIsCiAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowMS4xMTlaIiwKICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjAxLjExOVoiLAogICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjAxLjExOVoiLAogICJzaXplIjogIjExIiwKICAibWQ1SGFzaCI6ICJ4d1dORmEwVmRYUG1sQXdybGNBSmNnPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMj9nZW5lcmF0aW9uPTE1MzAyMjAyMDExMjEyMTYmYWx0PW1lZGlhIiwKICAiY29udGVudExhbmd1YWdlIjogImVuIiwKICAiYWNsIjogWwogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwMTEyMTIxNi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTIiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMTEyMTIxNiIsCiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAib3duZXJzIgogICAgfSwKICAgICJldGFnIjogIkNNQ3JrTHlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAxMTIxMjE2L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDExMjEyMTYiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgfSwKICAgICJldGFnIjogIkNNQ3JrTHlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAxMTIxMjE2L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDExMjEyMTYiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiUkVBREVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAidmlld2VycyIKICAgIH0sCiAgICAiZXRhZyI6ICJDTUNya0x5aTk5c0NFQUU9IgogICB9LAogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwMTEyMTIxNi91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDExMjEyMTYiLAogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiZXRhZyI6ICJDTUNya0x5aTk5c0NFQUU9IgogICB9CiAgXSwKICAib3duZXIiOiB7CiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICB9LAogICJjcmMzMmMiOiAicjBOR3JnPT0iLAogICJldGFnIjogIkNNQ3JrTHlpOTlzQ0VBRT0iCiB9Cn0K" + } + }, + { + "ID": "c52c29d0fc0560a5", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-2", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "ed31d0959d2856e13759a49d1e1fa00f/7938501913915128056;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-2" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Language": [ + "en" + ], + "Content-Length": [ + "11" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:01 GMT" + ], + "Etag": [ + "\"c7058d15ad157573e6940c2b95c00972\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:01 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:10:01 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:10:01 GMT" + ], + "X-Goog-Generation": [ + "1530220201121216" + ], + "X-Goog-Hash": [ + "crc32c=r0NGrg==", + "md5=xwWNFa0VdXPmlAwrlcAJcg==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "11" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/1,/bns/xg/borg/xg/bns/blobstore2/bitpusher/49.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qU41W47IEKi0_QS5hYHYBw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/49.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/49:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urt1LvXF92mwgf9esqXCmdHwAQpM9u5m14vWRsJpnjqz41h10pofdTzqW2lR8H1rgSVrSmcVU9rtdsbuVUIqGC6bhKPkA" + ] + }, + "Body": "dG9wIHNlY3JldC4=" + } + }, + { + "ID": "9588fa1690d3ce7b", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "397fc42482369505af1538e4c22cd5f1/9553635847114062615;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "FnBvfQ1dDsyS8kHD+aB6HHIglDoQ5Im7WYDm3XYTGrQ=" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14415" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:01 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220500000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjm11:4231,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qU41W421FoObhATkprbADg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/265:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur66aoFNFzb9iRinfsN3nke7iwYup3ylBhDXfBoJ72CRNpAtX7dupHKYb4X2uZNSFPCkke9y7L3Uo5gdjqnZpIjQHH_KM52n_hGw4tY1zs0SuSqn6g" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVzb3VyY2VJc0VuY3J5cHRlZFdpdGhDdXN0b21lckVuY3J5cHRpb25LZXkiLAogICAgIm1lc3NhZ2UiOiAiVGhlIHRhcmdldCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuIiwKICAgICJleHRlbmRlZEhlbHAiOiAiaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL3N0b3JhZ2UvZG9jcy9lbmNyeXB0aW9uI2N1c3RvbWVyLXN1cHBsaWVkX2VuY3J5cHRpb25fa2V5cyIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFRoZSByZXF1ZXN0ZWQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMxMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLnJld3JpdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyNClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBUaGUgcmVxdWVzdGVkIG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUHJlcGFyZVJld3JpdGVPcGVyYXRpb24udmFsaWRhdGVTb3VyY2VFbmNyeXB0aW9uKFByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLmphdmE6NTg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5QcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5ydW4oUHJlcGFyZVJld3JpdGVPcGVyYXRpb24uamF2YTozMzApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3RJbnRlcm5hbChSZXdyaXRlci5qYXZhOjM1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUmV3cml0ZXIucmV3cml0ZU9iamVjdChSZXdyaXRlci5qYXZhOjMyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QucmV3cml0ZUluRnJvbnRlbmQoUmV3cml0ZU9iamVjdC5qYXZhOjIyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YToyMDYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6NTIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0Li4uIDE0IG1vcmVcblxuY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUlOVkFMSURfVkFMVUUsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBUaGUgcmVxdWVzdGVkIG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogVGhlIHJlcXVlc3RlZCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLnZhbGlkYXRlU291cmNlRW5jcnlwdGlvbihQcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5qYXZhOjU4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUHJlcGFyZVJld3JpdGVPcGVyYXRpb24ucnVuKFByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLmphdmE6MzMwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0SW50ZXJuYWwoUmV3cml0ZXIuamF2YTozNTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3QoUmV3cml0ZXIuamF2YTozMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LnJld3JpdGVJbkZyb250ZW5kKFJld3JpdGVPYmplY3QuamF2YToyMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6MjA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjUyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdC4uLiAxNCBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1odHRwczovL2Nsb3VkLmdvb2dsZS5jb20vc3RvcmFnZS9kb2NzL2VuY3J5cHRpb24jY3VzdG9tZXItc3VwcGxpZWRfZW5jcnlwdGlvbl9rZXlzLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLlJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBUaGUgcmVxdWVzdGVkIG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogVGhlIHJlcXVlc3RlZCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLnZhbGlkYXRlU291cmNlRW5jcnlwdGlvbihQcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5qYXZhOjU4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUHJlcGFyZVJld3JpdGVPcGVyYXRpb24ucnVuKFByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLmphdmE6MzMwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0SW50ZXJuYWwoUmV3cml0ZXIuamF2YTozNTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3QoUmV3cml0ZXIuamF2YTozMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LnJld3JpdGVJbkZyb250ZW5kKFJld3JpdGVPYmplY3QuamF2YToyMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6MjA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjUyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdC4uLiAxNCBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPVJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZLCBlcnJvclByb3RvRG9tYWluPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1udWxsLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1UaGUgdGFyZ2V0IG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4sIHJlYXNvbj1yZXNvdXJjZUlzRW5jcnlwdGVkV2l0aEN1c3RvbWVyRW5jcnlwdGlvbktleSwgcnBjQ29kZT00MDB9IFRoZSB0YXJnZXQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogVGhlIHJlcXVlc3RlZCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzExKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IucmV3cml0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFRoZSByZXF1ZXN0ZWQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5QcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi52YWxpZGF0ZVNvdXJjZUVuY3J5cHRpb24oUHJlcGFyZVJld3JpdGVPcGVyYXRpb24uamF2YTo1ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlByZXBhcmVSZXdyaXRlT3BlcmF0aW9uLnJ1bihQcmVwYXJlUmV3cml0ZU9wZXJhdGlvbi5qYXZhOjMzMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUmV3cml0ZXIucmV3cml0ZU9iamVjdEludGVybmFsKFJld3JpdGVyLmphdmE6MzU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0KFJld3JpdGVyLmphdmE6MzI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5yZXdyaXRlSW5Gcm9udGVuZChSZXdyaXRlT2JqZWN0LmphdmE6MjI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjIwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YTo1Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHQuLi4gMTQgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJUaGUgdGFyZ2V0IG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4iCiB9Cn0K" + } + }, + { + "ID": "88546f1c3da9cd5f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5848140aaf3d8d614406c698872eb038/11168770879807782455;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Copy-Source-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Copy-Source-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Copy-Source-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "FnBvfQ1dDsyS8kHD+aB6HHIglDoQ5Im7WYDm3XYTGrQ=" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "4189" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:02 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220501000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vran9:4304,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qU41W-71JdbshgThiaaoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/372:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpNP9NRLaSXiqH5dTkf6a7oWUTFaYNaIUvt9m39E3KsmpM_u16PADADA8HqaVu4NphcHg9YUk5qVQVfFViJ6Qs-iRhbmwa8jvKssI-1aGZqObn-LzA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiMTEiLAogIm9iamVjdFNpemUiOiAiMTEiLAogImRvbmUiOiB0cnVlLAogInJlc291cmNlIjogewogICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAyMTIwMDA1IiwKICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMiIsCiAgIm5hbWUiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMjEyMDAwNSIsCiAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowMi4xMThaIiwKICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjAyLjExOFoiLAogICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjAyLjExOFoiLAogICJzaXplIjogIjExIiwKICAibWQ1SGFzaCI6ICJ4d1dORmEwVmRYUG1sQXdybGNBSmNnPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMj9nZW5lcmF0aW9uPTE1MzAyMjAyMDIxMjAwMDUmYWx0PW1lZGlhIiwKICAiY29udGVudExhbmd1YWdlIjogImVuIiwKICAiYWNsIjogWwogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwMjEyMDAwNS9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTIiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMjEyMDAwNSIsCiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAib3duZXJzIgogICAgfSwKICAgICJldGFnIjogIkNNV216YnlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAyMTIwMDA1L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDIxMjAwMDUiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgfSwKICAgICJldGFnIjogIkNNV216YnlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAyMTIwMDA1L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDIxMjAwMDUiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiUkVBREVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAidmlld2VycyIKICAgIH0sCiAgICAiZXRhZyI6ICJDTVdtemJ5aTk5c0NFQUU9IgogICB9LAogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwMjEyMDAwNS91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDIxMjAwMDUiLAogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiZXRhZyI6ICJDTVdtemJ5aTk5c0NFQUU9IgogICB9CiAgXSwKICAib3duZXIiOiB7CiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICB9LAogICJjcmMzMmMiOiAicjBOR3JnPT0iLAogICJldGFnIjogIkNNV216YnlpOTlzQ0VBRT0iLAogICJjdXN0b21lckVuY3J5cHRpb24iOiB7CiAgICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgICJrZXlTaGEyNTYiOiAiRm5CdmZRMWREc3lTOGtIRCthQjZISElnbERvUTVJbTdXWURtM1hZVEdyUT0iCiAgfQogfQp9Cg==" + } + }, + { + "ID": "56c21a950a4a624f", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-2", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "179c5bddca9b97f16f230f06437f231b/12711848314185449815;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-2" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "277" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:02 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:02 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/36,/bns/xg/borg/xg/bns/blobstore2/bitpusher/53.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qk41W5mkDo23_QTen5L4Bw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/53.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/53:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urx404ha6bbLMoE_hP0ALCpOUrG_9sZ_pcfJWRil9s42gtRk8RaDpV_54bCYRvQeIHVZ9nKpIRlMuxszfvyKG-WUdIyuA" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+UmVzb3VyY2VJc0VuY3J5cHRlZFdpdGhDdXN0b21lckVuY3J5cHRpb25LZXk8L0NvZGU+PE1lc3NhZ2U+VGhlIHJlc291cmNlIGlzIGVuY3J5cHRlZCB3aXRoIGEgY3VzdG9tZXIgZW5jcnlwdGlvbiBrZXkuPC9NZXNzYWdlPjxEZXRhaWxzPlRoZSByZXF1ZXN0ZWQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LjwvRGV0YWlscz48L0Vycm9yPg==" + } + }, + { + "ID": "e67d6916fe79ae8c", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-2", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "17af4f828829c9712a6a5fe7ed19eaff/14326982247384384630;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-2" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "FnBvfQ1dDsyS8kHD+aB6HHIglDoQ5Im7WYDm3XYTGrQ=" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Language": [ + "en" + ], + "Content-Length": [ + "11" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:02 GMT" + ], + "Etag": [ + "\"-CMWmzbyi99sCEAE=\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:10:02 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key-Sha256": [ + "FnBvfQ1dDsyS8kHD+aB6HHIglDoQ5Im7WYDm3XYTGrQ=" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:10:02 GMT" + ], + "X-Goog-Generation": [ + "1530220202120005" + ], + "X-Goog-Hash": [ + "crc32c=r0NGrg==", + "md5=xwWNFa0VdXPmlAwrlcAJcg==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "11" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/63,/bns/xg/borg/xg/bns/blobstore2/bitpusher/56.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qk41W7L_EK-5_QT08q2oCA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/56.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/56:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UptMfJjhHcgFXr0AT1_nA3F-lHuBWF63jPz_6m64ZS2uQJ-0vSpGz9CdybRzptjbUl4jzzaatSuuuDQJPAWeToO9wUhwQ" + ] + }, + "Body": "dG9wIHNlY3JldC4=" + } + }, + { + "ID": "7f2bef31f98ed68a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8fd4988bee0dddc5339e9f09320c258b/15870059686056953750;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Copy-Source-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Copy-Source-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Copy-Source-Encryption-Key-Sha256": [ + "FnBvfQ1dDsyS8kHD+aB6HHIglDoQ5Im7WYDm3XYTGrQ=" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "4189" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:03 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220501000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrch2:4197,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=qk41W6z6FsuGhASj3anICg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/615:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqGdVDupV3bJA8L1Ywq5QGBoKVIEkAjgd9oKSS3b6cmE9Nwc5tXmehEO_ufTcwmrtwB3pDLwjKe6IVhgFnsvAyLxfWDCZ7OZfuBsmRdC04yk3IgOnE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiMTEiLAogIm9iamVjdFNpemUiOiAiMTEiLAogImRvbmUiOiB0cnVlLAogInJlc291cmNlIjogewogICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAyOTE3MTI2IiwKICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMiIsCiAgIm5hbWUiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMjkxNzEyNiIsCiAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowMi45MTVaIiwKICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjAyLjkxNVoiLAogICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjAyLjkxNVoiLAogICJzaXplIjogIjExIiwKICAibWQ1SGFzaCI6ICJ4d1dORmEwVmRYUG1sQXdybGNBSmNnPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMj9nZW5lcmF0aW9uPTE1MzAyMjAyMDI5MTcxMjYmYWx0PW1lZGlhIiwKICAiY29udGVudExhbmd1YWdlIjogImVuIiwKICAiYWNsIjogWwogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwMjkxNzEyNi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTIiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMjkxNzEyNiIsCiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAib3duZXJzIgogICAgfSwKICAgICJldGFnIjogIkNJYjYvYnlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAyOTE3MTI2L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDI5MTcxMjYiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgfSwKICAgICJldGFnIjogIkNJYjYvYnlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjAyOTE3MTI2L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDI5MTcxMjYiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiUkVBREVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAidmlld2VycyIKICAgIH0sCiAgICAiZXRhZyI6ICJDSWI2L2J5aTk5c0NFQUU9IgogICB9LAogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwMjkxNzEyNi91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDI5MTcxMjYiLAogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiZXRhZyI6ICJDSWI2L2J5aTk5c0NFQUU9IgogICB9CiAgXSwKICAib3duZXIiOiB7CiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICB9LAogICJjcmMzMmMiOiAicjBOR3JnPT0iLAogICJldGFnIjogIkNJYjYvYnlpOTlzQ0VBRT0iLAogICJjdXN0b21lckVuY3J5cHRpb24iOiB7CiAgICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgICJrZXlTaGEyNTYiOiAiSCtMbW5YaFJvZUk2VE1XNWJzVjZIeVVrNnB5R2MySU1icVliQVhCY3BzMD0iCiAgfQogfQp9Cg==" + } + }, + { + "ID": "1efddd998227a4d4", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3/compose?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "160" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b8e63c7a4670718ff9a6f0f9b3be2d28/17485194718750739125;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3/compose?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImN1c3RvbWVyLWVuY3J5cHRpb24ifSx7Im5hbWUiOiJjdXN0b21lci1lbmNyeXB0aW9uLTIifV19Cg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14106" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:03 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220500000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjm11:4231,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=q041W77QCcGVhgSpl5CYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/577:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urm6cO9IorZDpmjRE3g27IjkJJozx3KvL_D3MMR_CzEdJbdMNPKRUinh3rEGFhHZDLehio4j9DIBOFr3Eaf6jculnECQaMe6RAd6tLtLTdqWUylTHo" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVzb3VyY2VJc0VuY3J5cHRlZFdpdGhDdXN0b21lckVuY3J5cHRpb25LZXkiLAogICAgIm1lc3NhZ2UiOiAiVGhlIHRhcmdldCBvYmplY3QgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuIiwKICAgICJleHRlbmRlZEhlbHAiOiAiaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL3N0b3JhZ2UvZG9jcy9lbmNyeXB0aW9uI2N1c3RvbWVyLXN1cHBsaWVkX2VuY3J5cHRpb25fa2V5cyIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBDb21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbikgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6MjAwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuQ29tcG9zZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoQ29tcG9zZU9iamVjdC5qYXZhOjQ3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5jb21wb3NlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IENvbXBvbmVudCBvYmplY3QgKGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uKSBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcblxuY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUlOVkFMSURfVkFMVUUsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogQ29tcG9uZW50IG9iamVjdCAoZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24pIGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuQ29tcG9zZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoQ29tcG9zZU9iamVjdC5qYXZhOjIwMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YTo0Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuY29tcG9zZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBDb21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbikgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1odHRwczovL2Nsb3VkLmdvb2dsZS5jb20vc3RvcmFnZS9kb2NzL2VuY3J5cHRpb24jY3VzdG9tZXItc3VwcGxpZWRfZW5jcnlwdGlvbl9rZXlzLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLlJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogQ29tcG9uZW50IG9iamVjdCAoZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24pIGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuQ29tcG9zZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoQ29tcG9zZU9iamVjdC5qYXZhOjIwMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YTo0Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuY29tcG9zZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBDb21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbikgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPVJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZLCBlcnJvclByb3RvRG9tYWluPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1Db21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbikgaXMgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1UaGUgdGFyZ2V0IG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4sIHJlYXNvbj1yZXNvdXJjZUlzRW5jcnlwdGVkV2l0aEN1c3RvbWVyRW5jcnlwdGlvbktleSwgcnBjQ29kZT00MDB9IFRoZSB0YXJnZXQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfSVNfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IENvbXBvbmVudCBvYmplY3QgKGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uKSBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFU09VUkNFX0lTX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9JU19FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogQ29tcG9uZW50IG9iamVjdCAoZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24pIGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJUaGUgdGFyZ2V0IG9iamVjdCBpcyBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4iCiB9Cn0K" + } + }, + { + "ID": "75a3ef475028fcb4", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3/compose?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "160" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "218741f514726382a90b830c5b3575b8/1389235235898380260;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3/compose?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImN1c3RvbWVyLWVuY3J5cHRpb24ifSx7Im5hbWUiOiJjdXN0b21lci1lbmNyeXB0aW9uLTIifV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "974" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:03 GMT" + ], + "Etag": [ + "CJfQsb2i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220501000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnq12:4113,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=q041W_2GGobJN_DHnpAH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/159:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpKWDA_HFqJq56HCK0QLT1GYVaF1X5UaQTNkIsstf5BzVjCZbDpKK5hdwwyo64neh--YnDgkxJoDW2SSRELDSJlga1bLa6fKU8uxdcqkwsGUZsKovM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTMvMTUzMDIyMDIwMzc2MzczNSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMyIsCiAibmFtZSI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTMiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMzc2MzczNSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowMy43NjNaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDMuNzYzWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjAzLjc2M1oiLAogInNpemUiOiAiMjIiLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0zP2dlbmVyYXRpb249MTUzMDIyMDIwMzc2MzczNSZhbHQ9bWVkaWEiLAogImNyYzMyYyI6ICI1ajF5cGc9PSIsCiAiY29tcG9uZW50Q291bnQiOiAyLAogImV0YWciOiAiQ0pmUXNiMmk5OXNDRUFFPSIsCiAiY3VzdG9tZXJFbmNyeXB0aW9uIjogewogICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgImtleVNoYTI1NiI6ICJIK0xtblhoUm9lSTZUTVc1YnNWNkh5VWs2cHlHYzJJTWJxWWJBWEJjcHMwPSIKIH0KfQo=" + } + }, + { + "ID": "092b8804c70da499", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-3", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "58824328e4edf108c04928560362009e/3004370268592099844;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-3" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "277" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:04 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:04 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/16,/bns/xg/borg/xg/bns/blobstore2/bitpusher/62.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rE41W-xewbr9BLCthdgI" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/62.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/62:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo5uPYdI4gKpeBpU7j-z1n69VyHXH1RKDWWkpN3ZFAGF8x5jHwHHXiDcwu2jNhtR3qslaynj3y6rDLO2w5vQohcZeMkOg" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+UmVzb3VyY2VJc0VuY3J5cHRlZFdpdGhDdXN0b21lckVuY3J5cHRpb25LZXk8L0NvZGU+PE1lc3NhZ2U+VGhlIHJlc291cmNlIGlzIGVuY3J5cHRlZCB3aXRoIGEgY3VzdG9tZXIgZW5jcnlwdGlvbiBrZXkuPC9NZXNzYWdlPjxEZXRhaWxzPlRoZSByZXF1ZXN0ZWQgb2JqZWN0IGlzIGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LjwvRGV0YWlscz48L0Vycm9yPg==" + } + }, + { + "ID": "3b97fe33d57b3926", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-3", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "cc76473e3a646a45f0ed8e2a974990ef/4547447707264734499;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/customer-encryption-3" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "22" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:04 GMT" + ], + "Etag": [ + "\"-CJfQsb2i99sCEAE=\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:10:03 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Component-Count": [ + "2" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:10:03 GMT" + ], + "X-Goog-Generation": [ + "1530220203763735" + ], + "X-Goog-Hash": [ + "crc32c=5j1ypg==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "22" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/61,/bns/xg/borg/xg/bns/blobstore2/bitpusher/92.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rE41W9-6A4G9_QS-lbDYAw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/92.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/92:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upgu4DfLXTFm3j450Ym2XS7ep8BgZY869LBBR_U2i5ZAQxgkjRtMr7yfTnqIHAwXZTiWLFn9reDzZQICWXcLRowuiOcwQ" + ] + }, + "Body": "dG9wIHNlY3JldC50b3Agc2VjcmV0Lg==" + } + }, + { + "ID": "79b47ea52fe38759", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "34e9e2cb5a16c30952d8a1932d908943/6162581640463603779;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Copy-Source-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Copy-Source-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Copy-Source-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "4059" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:04 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220500000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjc6:4102,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rE41W-XTCcKVN86YjeAJ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/211:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Trace": [ + "http://trace.corp.google.com/trace?host=ywdm185-v6\u0026trace_id=0xcc3f338c852de\u0026id=0x7d0dbbe80fb81d35\u0026start=2018-06-28_14:10:04\u0026rpc_duration=0.564735" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uqs2ZLyVULOXjUe8kEFi_y8xeotFxSghbm67VhU7Bvxf9GMpFOyWMG6jsrugFKW4ho9g62h8tXwlJzdFrCfrQH-ZOdWiFOT5derVue5RShnCHd5yHo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiMTEiLAogIm9iamVjdFNpemUiOiAiMTEiLAogImRvbmUiOiB0cnVlLAogInJlc291cmNlIjogewogICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjA0NDg2Nzk1IiwKICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMiIsCiAgIm5hbWUiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNDQ4Njc5NSIsCiAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNC40ODZaIiwKICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA0LjQ4NloiLAogICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA0LjQ4NloiLAogICJzaXplIjogIjExIiwKICAibWQ1SGFzaCI6ICJ4d1dORmEwVmRYUG1sQXdybGNBSmNnPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMj9nZW5lcmF0aW9uPTE1MzAyMjAyMDQ0ODY3OTUmYWx0PW1lZGlhIiwKICAiY29udGVudExhbmd1YWdlIjogImVuIiwKICAiYWNsIjogWwogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwNDQ4Njc5NS9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTIiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNDQ4Njc5NSIsCiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAib3duZXJzIgogICAgfSwKICAgICJldGFnIjogIkNJdmgzYjJpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjA0NDg2Nzk1L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDQ0ODY3OTUiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgfSwKICAgICJldGFnIjogIkNJdmgzYjJpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMi8xNTMwMjIwMjA0NDg2Nzk1L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDQ0ODY3OTUiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiUkVBREVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAidmlld2VycyIKICAgIH0sCiAgICAiZXRhZyI6ICJDSXZoM2IyaTk5c0NFQUU9IgogICB9LAogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwNDQ4Njc5NS91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDQ0ODY3OTUiLAogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiZXRhZyI6ICJDSXZoM2IyaTk5c0NFQUU9IgogICB9CiAgXSwKICAib3duZXIiOiB7CiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICB9LAogICJjcmMzMmMiOiAicjBOR3JnPT0iLAogICJldGFnIjogIkNJdmgzYjJpOTlzQ0VBRT0iCiB9Cn0K" + } + }, + { + "ID": "b670314f74ee7943", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3/compose?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "129" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a67d1ef1e27d5f4427e84e038289fd54/7777715569367636834;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3/compose?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "H+LmnXhRoeI6TMW5bsV6HyUk6pyGc2IMbqYbAXBcps0=" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImN1c3RvbWVyLWVuY3J5cHRpb24tMiJ9XX0K" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14232" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:05 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220500000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrp184:4303,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rE41W9nRLcaJN5i7i4AF" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/488:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBWEpmN2FRVFdXZUxuM29aWWExSm9CVzNNX1NJX1o2OEpHWUFkZHNKWGpLQmZwdS1QRGtjUnFWOWpRYkJnbFBGLVdNN1R4T2ltektlTjRhbVIzcGJ0dUdCNW1JdmNlT0ExQTAzejZhVnh5RU40alpoZVp6OXBMV2xFTHJaVjF4VDl2NW1mcWFjVFp2RV90LWoxZjQxYWtGMjNZRm9sM1k3UkN5b3EwOHFnUnFqYWxJdE9yclkzZkhCMGcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UokOdEV6TxWi8nJbLcP0uoWMJLeBodAvA-7-YF1tqHMP1UJjWKDs2wt39NMIjE-4GmZq4H2FVWcLF-h3qBEgWoSryOCqpaRCA6SvnaGHwWllgnqHFI" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVzb3VyY2VOb3RFbmNyeXB0ZWRXaXRoQ3VzdG9tZXJFbmNyeXB0aW9uS2V5IiwKICAgICJtZXNzYWdlIjogIlRoZSB0YXJnZXQgb2JqZWN0IGlzIG5vdCBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4iLAogICAgImV4dGVuZGVkSGVscCI6ICJodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vc3RvcmFnZS9kb2NzL2VuY3J5cHRpb24jY3VzdG9tZXItc3VwcGxpZWRfZW5jcnlwdGlvbl9rZXlzIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9OT1RfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IENvbXBvbmVudCBvYmplY3QgKGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIpIHVzIG5vdCBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBDb21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0yKSB1cyBub3QgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1JTlZBTElEX1ZBTFVFLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9OT1RfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IENvbXBvbmVudCBvYmplY3QgKGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIpIHVzIG5vdCBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBDb21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0yKSB1cyBub3QgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1odHRwczovL2Nsb3VkLmdvb2dsZS5jb20vc3RvcmFnZS9kb2NzL2VuY3J5cHRpb24jY3VzdG9tZXItc3VwcGxpZWRfZW5jcnlwdGlvbl9rZXlzLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLlJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWSwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9OT1RfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IENvbXBvbmVudCBvYmplY3QgKGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIpIHVzIG5vdCBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBDb21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0yKSB1cyBub3QgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPVJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWSwgZXJyb3JQcm90b0RvbWFpbj1jbG91ZC5iaWdzdG9yZS5hcGkuQmlnc3RvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5lbmNyeXB0aW9uS2V5LCBtZXNzYWdlPUNvbXBvbmVudCBvYmplY3QgKGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIpIHVzIG5vdCBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkuZW5jcnlwdGlvbktleSwgbWVzc2FnZT1UaGUgdGFyZ2V0IG9iamVjdCBpcyBub3QgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuLCByZWFzb249cmVzb3VyY2VOb3RFbmNyeXB0ZWRXaXRoQ3VzdG9tZXJFbmNyeXB0aW9uS2V5LCBycGNDb2RlPTQwMH0gVGhlIHRhcmdldCBvYmplY3QgaXMgbm90IGVuY3J5cHRlZCBieSBhIGN1c3RvbWVyLXN1cHBsaWVkIGVuY3J5cHRpb24ga2V5LjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBSRVNPVVJDRV9OT1RfRU5DUllQVEVEX1dJVEhfQ1VTVE9NRVJfRU5DUllQVElPTl9LRVk6IENvbXBvbmVudCBvYmplY3QgKGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIpIHVzIG5vdCBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFU09VUkNFX05PVF9FTkNSWVBURURfV0lUSF9DVVNUT01FUl9FTkNSWVBUSU9OX0tFWTogUkVTT1VSQ0VfTk9UX0VOQ1JZUFRFRF9XSVRIX0NVU1RPTUVSX0VOQ1JZUFRJT05fS0VZOiBDb21wb25lbnQgb2JqZWN0IChnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0yKSB1cyBub3QgZW5jcnlwdGVkIGJ5IGEgY3VzdG9tZXItc3VwcGxpZWQgZW5jcnlwdGlvbiBrZXkuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAwLAogICJtZXNzYWdlIjogIlRoZSB0YXJnZXQgb2JqZWN0IGlzIG5vdCBlbmNyeXB0ZWQgYnkgYSBjdXN0b21lci1zdXBwbGllZCBlbmNyeXB0aW9uIGtleS4iCiB9Cn0K" + } + }, + { + "ID": "b24ff6aaa727f09b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1af8af6b54e0673d7b80770310f700ba/9320512636853313154;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2951" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:05 GMT" + ], + "Etag": [ + "CAk=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:05 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220505000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrae2:4360,/bns/yw/borg/yw/bns/blobstore2/bitpusher/395.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rU41W83IB8LBhQTk4ZewAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/395.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/395:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVnJZa216TkpmUHFPLVpSdGhXUTJobXlwOVZXV0dHVVh3cXh6RlJjMlREcXd3bVQ5TFo3YXgtQV9mWGRJMTI0V2dBS1JBUjhiZk9iVFAyX0o3M29kVXV4cXh6MG5uSmpPTkp3a0xZcm9PVzZNTk1SeFNXbzRxVEVianJ5OTJZX25kakZtY2VYTGZqaDdjNERqc1dYWkp3Z2gzMVBVYjBpTklFYW90RnFKWGVtNTNuM0JySm5TSFg2c1kwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpfyKYlJzTkiYjo7mmYzBBnG6VTGptXvaNNdR2gnyL-U782EcVfHkfAJkoMF5ef2iMrhmjEvQD9Q2gnwwNnQMWR9z3xRNfJ2vc4BJXaEfl9k9HCGWs" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjUyLjkyMFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjkiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FrPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FrPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBaz0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQWs9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FrPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQWs9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInZlcnNpb25pbmciOiB7CiAgImVuYWJsZWQiOiBmYWxzZQogfSwKICJsaWZlY3ljbGUiOiB7CiAgInJ1bGUiOiBbCiAgIHsKICAgICJhY3Rpb24iOiB7CiAgICAgInR5cGUiOiAiRGVsZXRlIgogICAgfSwKICAgICJjb25kaXRpb24iOiB7CiAgICAgImFnZSI6IDMwCiAgICB9CiAgIH0KICBdCiB9LAogImxhYmVscyI6IHsKICAibmV3IjogIm5ldyIsCiAgImwxIjogInYyIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FrPSIKfQo=" + } + }, + { + "ID": "ac61737896c6360c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=6f86a673abac38c2d59a71e9f95e3f604b7cb65c1d5cd4b4f8dc16487f5c" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4d5d445994aeeb09a17d1d7b2f373c34/10128219789054582674;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS02Zjg2YTY3M2FiYWMzOGMyZDU5YTcxZTlmOTVlM2Y2MDRiN2NiNjVjMWQ1Y2Q0YjRmOGRjMTY0ODdmNWMNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJwb3NjIn0KDQotLTZmODZhNjczYWJhYzM4YzJkNTlhNzFlOWY5NWUzZjYwNGI3Y2I2NWMxZDVjZDRiNGY4ZGMxNjQ4N2Y1Yw0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCmZvbw0KLS02Zjg2YTY3M2FiYWMzOGMyZDU5YTcxZTlmOTVlM2Y2MDRiN2NiNjVjMWQ1Y2Q0YjRmOGRjMTY0ODdmNWMtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:05 GMT" + ], + "Etag": [ + "CKWTor6i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220505000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrkz87:4025,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rU41W4HrH8ytN6euonA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/282:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVnJZa216TkpmUHFPLVpSdGhXUTJobXlwOVZXV0dHVVh3cXh6RlJjMlREcXd3bVQ5TFo3YXgtQV9mWGRJMTI0V2dBS1JBUjhiZk9iVFAyX0o3M29kVXV4cXh6MG5uSmpPTkp3a0xZcm9PVzZNTk1SeFNXbzRxVEVianJ5OTJZX25kakZtY2VYTGZqaDdjNERqc1dYWkp3Z2gzMVBVYjBpTklFYW90RnFKWGVtNTNuM0JySm5TSFg2c1kwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpmhIKSsXW7jHwOmX_xlL325ERhV-k3JrriYcGfjlthEOuI1LO2b9qg5-WuUAv1CnQiPy_oDHukNs0Uk34GcixxOoZ4XtKBKWJN4yRIty6Hy4aQltk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjLzE1MzAyMjAyMDU2MDczMzMiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjIiwKICJuYW1lIjogInBvc2MiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNTYwNzMzMyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNS42MDVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDUuNjA1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA1LjYwNVoiLAogInNpemUiOiAiMyIsCiAibWQ1SGFzaCI6ICJyTDBZMjB6QytGenQ3MlZQek1TazJBPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYz9nZW5lcmF0aW9uPTE1MzAyMjAyMDU2MDczMzMmYWx0PW1lZGlhIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNTYwNzMzMy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAicG9zYyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDU2MDczMzMiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNLV1RvcjZpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcG9zYy8xNTMwMjIwMjA1NjA3MzMzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA1NjA3MzMzIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNLV1RvcjZpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcG9zYy8xNTMwMjIwMjA1NjA3MzMzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA1NjA3MzMzIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDS1dUb3I2aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNTYwNzMzMy91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA1NjA3MzMzIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDS1dUb3I2aTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiejhTdUhRPT0iLAogImV0YWciOiAiQ0tXVG9yNmk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "62917309c103a389", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9c43495f0a367cef364c3a8c8d786ff5/11743354821748368049;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:05 GMT" + ], + "Etag": [ + "CKWTor6i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220505000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrq64:4037,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rU41W92ZLIeHhgSr9ppQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/172:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVnJZa216TkpmUHFPLVpSdGhXUTJobXlwOVZXV0dHVVh3cXh6RlJjMlREcXd3bVQ5TFo3YXgtQV9mWGRJMTI0V2dBS1JBUjhiZk9iVFAyX0o3M29kVXV4cXh6MG5uSmpPTkp3a0xZcm9PVzZNTk1SeFNXbzRxVEVianJ5OTJZX25kakZtY2VYTGZqaDdjNERqc1dYWkp3Z2gzMVBVYjBpTklFYW90RnFKWGVtNTNuM0JySm5TSFg2c1kwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqxkgCuAqQWhnVurEmuURXH9ywpEIGJIYP9_Xg7gN8WPuqMPONvKvnqoQp1gYGv6lkT-1ju-KUpbofg7IIQMupMdxwFBBbrf2jOb-1VebG9dard-XY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjLzE1MzAyMjAyMDU2MDczMzMiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjIiwKICJuYW1lIjogInBvc2MiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNTYwNzMzMyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNS42MDVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDUuNjA1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA1LjYwNVoiLAogInNpemUiOiAiMyIsCiAibWQ1SGFzaCI6ICJyTDBZMjB6QytGenQ3MlZQek1TazJBPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYz9nZW5lcmF0aW9uPTE1MzAyMjAyMDU2MDczMzMmYWx0PW1lZGlhIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNTYwNzMzMy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAicG9zYyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDU2MDczMzMiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNLV1RvcjZpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcG9zYy8xNTMwMjIwMjA1NjA3MzMzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA1NjA3MzMzIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNLV1RvcjZpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcG9zYy8xNTMwMjIwMjA1NjA3MzMzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA1NjA3MzMzIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDS1dUb3I2aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNTYwNzMzMy91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA1NjA3MzMzIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDS1dUb3I2aTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiejhTdUhRPT0iLAogImV0YWciOiAiQ0tXVG9yNmk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "5c479d8c5a48f5aa", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/posc?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "34" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b5aad2c76e48670df22255bcb445faca/13286432256126035409;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/posc?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJzdG9yYWdlQ2xhc3MiOiJNVUxUSV9SRUdJT05BTCJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3717" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:06 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220505000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqk15:4133,/bns/yw/borg/yw/bns/blobstore2/bitpusher/502.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rU41W4TYMYf0N5mFp7gP" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/502.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/502:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVnJZa216TkpmUHFPLVpSdGhXUTJobXlwOVZXV0dHVVh3cXh6RlJjMlREcXd3bVQ5TFo3YXgtQV9mWGRJMTI0V2dBS1JBUjhiZk9iVFAyX0o3M29kVXV4cXh6MG5uSmpPTkp3a0xZcm9PVzZNTk1SeFNXbzRxVEVianJ5OTJZX25kakZtY2VYTGZqaDdjNERqc1dYWkp3Z2gzMVBVYjBpTklFYW90RnFKWGVtNTNuM0JySm5TSFg2c1kwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpNA_7ih0Mp9gJB_JeIbzd78W64edUfrETNFwMaMYLQ5zEO5P4aCqMARqGjJDafrj4LWDvAFhQm_m5OruOm7ZU3AqKfYzlcTLNtkzRSDfe0x7xqcYY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiMyIsCiAib2JqZWN0U2l6ZSI6ICIzIiwKICJkb25lIjogdHJ1ZSwKICJyZXNvdXJjZSI6IHsKICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjLzE1MzAyMjAyMDYyMzk5OTMiLAogICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYyIsCiAgIm5hbWUiOiAicG9zYyIsCiAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDYyMzk5OTMiLAogICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNi4yMzlaIiwKICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA2LjIzOVoiLAogICJzdG9yYWdlQ2xhc3MiOiAiTVVMVElfUkVHSU9OQUwiLAogICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA2LjIzOVoiLAogICJzaXplIjogIjMiLAogICJtZDVIYXNoIjogInJMMFkyMHpDK0Z6dDcyVlB6TVNrMkE9PSIsCiAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYz9nZW5lcmF0aW9uPTE1MzAyMjAyMDYyMzk5OTMmYWx0PW1lZGlhIiwKICAiYWNsIjogWwogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjLzE1MzAyMjAyMDYyMzk5OTMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogInBvc2MiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNjIzOTk5MyIsCiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAib3duZXJzIgogICAgfSwKICAgICJldGFnIjogIkNQbmh5TDZpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNjIzOTk5My9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJwb3NjIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDYyMzk5OTMiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgfSwKICAgICJldGFnIjogIkNQbmh5TDZpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNjIzOTk5My9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJwb3NjIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDYyMzk5OTMiLAogICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiUkVBREVSIiwKICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgInRlYW0iOiAidmlld2VycyIKICAgIH0sCiAgICAiZXRhZyI6ICJDUG5oeUw2aTk5c0NFQUU9IgogICB9LAogICB7CiAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjLzE1MzAyMjAyMDYyMzk5OTMvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgIm9iamVjdCI6ICJwb3NjIiwKICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDYyMzk5OTMiLAogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJyb2xlIjogIk9XTkVSIiwKICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiZXRhZyI6ICJDUG5oeUw2aTk5c0NFQUU9IgogICB9CiAgXSwKICAib3duZXIiOiB7CiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICB9LAogICJjcmMzMmMiOiAiejhTdUhRPT0iLAogICJldGFnIjogIkNQbmh5TDZpOTlzQ0VBRT0iCiB9Cn0K" + } + }, + { + "ID": "64538cde9063c048", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=edf9e0b0c7747f3d1e6f37536a0f46fa4153c6a85485bf5f9162d61ed2eb" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3e4ec4374c8ee55d4050384d5a03724e/14093857937628850144;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1lZGY5ZTBiMGM3NzQ3ZjNkMWU2ZjM3NTM2YTBmNDZmYTQxNTNjNmE4NTQ4NWJmNWY5MTYyZDYxZWQyZWINCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJwb3NjMiIsInN0b3JhZ2VDbGFzcyI6Ik1VTFRJX1JFR0lPTkFMIn0KDQotLWVkZjllMGIwYzc3NDdmM2QxZTZmMzc1MzZhMGY0NmZhNDE1M2M2YTg1NDg1YmY1ZjkxNjJkNjFlZDJlYg0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCnh4eA0KLS1lZGY5ZTBiMGM3NzQ3ZjNkMWU2ZjM3NTM2YTBmNDZmYTQxNTNjNmE4NTQ4NWJmNWY5MTYyZDYxZWQyZWItLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3582" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:06 GMT" + ], + "Etag": [ + "CMWJ4r6i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220505000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrir7:4320,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rk41W9OYFoyihwS80rTIBg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/623:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVnJZa216TkpmUHFPLVpSdGhXUTJobXlwOVZXV0dHVVh3cXh6RlJjMlREcXd3bVQ5TFo3YXgtQV9mWGRJMTI0V2dBS1JBUjhiZk9iVFAyX0o3M29kVXV4cXh6MG5uSmpPTkp3a0xZcm9PVzZNTk1SeFNXbzRxVEVianJ5OTJZX25kakZtY2VYTGZqaDdjNERqc1dYWkp3Z2gzMVBVYjBpTklFYW90RnFKWGVtNTNuM0JySm5TSFg2c1kwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrLn1pweAKhfkG2ToYtMYfFeZdqJJTyPxalnx59FEbZ8TYVkq4cl7OlfLbRfU-faGPlKhunn2tu8_owQkd__QIR7ZUox6FBUr1l03587xCe0y-93Ig" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjMi8xNTMwMjIwMjA2NjU0NjYxIiwKICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYzIiLAogIm5hbWUiOiAicG9zYzIiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNjY1NDY2MSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNi42NTRaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDYuNjU0WiIsCiAic3RvcmFnZUNsYXNzIjogIk1VTFRJX1JFR0lPTkFMIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA2LjY1NFoiLAogInNpemUiOiAiMyIsCiAibWQ1SGFzaCI6ICI5V0dxOXU4TDhVMUNDTHRHcE15enJRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYzI/Z2VuZXJhdGlvbj0xNTMwMjIwMjA2NjU0NjYxJmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjMi8xNTMwMjIwMjA2NjU0NjYxL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAicG9zYzIiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA2NjU0NjYxIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTVdKNHI2aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MyLzE1MzAyMjAyMDY2NTQ2NjEvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNjY1NDY2MSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTVdKNHI2aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MyLzE1MzAyMjAyMDY2NTQ2NjEvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNjY1NDY2MSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ01XSjRyNmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjMi8xNTMwMjIwMjA2NjU0NjYxL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MyL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInBvc2MyIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNjY1NDY2MSIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ01XSjRyNmk5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIjE3cUFCUT09IiwKICJldGFnIjogIkNNV0o0cjZpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "178f15ccb2d976d2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=92cd41ede4bbd0c6c4b248b1553479d7df048dd3c72d662cf4a332da14de" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b556522f634141cded0a2f55d8ea903a/14901566189324970224;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS05MmNkNDFlZGU0YmJkMGM2YzRiMjQ4YjE1NTM0NzlkN2RmMDQ4ZGQzYzcyZDY2MmNmNGEzMzJkYTE0ZGUNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJidWNrZXRJbkNvcHlBdHRycyJ9Cg0KLS05MmNkNDFlZGU0YmJkMGM2YzRiMjQ4YjE1NTM0NzlkN2RmMDQ4ZGQzYzcyZDY2MmNmNGEzMzJkYTE0ZGUNCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA0KDQpmb28NCi0tOTJjZDQxZWRlNGJiZDBjNmM0YjI0OGIxNTUzNDc5ZDdkZjA0OGRkM2M3MmQ2NjJjZjRhMzMyZGExNGRlLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3768" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:07 GMT" + ], + "Etag": [ + "CKaxhb+i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220506000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrss5:4314,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=rk41W-rtN9WyhgSNxaeACw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/444:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW44U2NvQW45ZXphX0pmQTNpLXZESWxDOVVJYXZLV3I4VkJ0aGNoSkh0ZmczTDVUVXJNWlVIWDdIenlsTGg0ZnRCRHh0M1JDMGJlOGRQdUZUaVFEVUNNMFRnSl9TSFBRU291bERTY3VaZzYybTQ0Si1qcjVFbEllc0dnZTJCR1JWRV9IeldyUWo2UmE4Z2VReFdQNWZyek5qelVzV0dlaUtqbExvYTk4bzZjN19mVm5MWW1oUEtLYkEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoexMgwHtPWkTlhFnrTa-8qBaKMHwh-YCgkDLvkfuSuvrlv1gSZn7lrNW5yIomFcDRkCruHQ_9uYddSlKhYDlG04lhvnFA0o-8BPzYFCipfHZebJ2w" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9idWNrZXRJbkNvcHlBdHRycy8xNTMwMjIwMjA3MjMzMTkwIiwKICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYnVja2V0SW5Db3B5QXR0cnMiLAogIm5hbWUiOiAiYnVja2V0SW5Db3B5QXR0cnMiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzIzMzE5MCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNy4yMzFaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDcuMjMxWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA3LjIzMVoiLAogInNpemUiOiAiMyIsCiAibWQ1SGFzaCI6ICJyTDBZMjB6QytGenQ3MlZQek1TazJBPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYnVja2V0SW5Db3B5QXR0cnM/Z2VuZXJhdGlvbj0xNTMwMjIwMjA3MjMzMTkwJmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9idWNrZXRJbkNvcHlBdHRycy8xNTMwMjIwMjA3MjMzMTkwL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9idWNrZXRJbkNvcHlBdHRycy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiYnVja2V0SW5Db3B5QXR0cnMiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA3MjMzMTkwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDS2F4aGIraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2J1Y2tldEluQ29weUF0dHJzLzE1MzAyMjAyMDcyMzMxOTAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9idWNrZXRJbkNvcHlBdHRycy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImJ1Y2tldEluQ29weUF0dHJzIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzIzMzE5MCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDS2F4aGIraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2J1Y2tldEluQ29weUF0dHJzLzE1MzAyMjAyMDcyMzMxOTAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9idWNrZXRJbkNvcHlBdHRycy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImJ1Y2tldEluQ29weUF0dHJzIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzIzMzE5MCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0theGhiK2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9idWNrZXRJbkNvcHlBdHRycy8xNTMwMjIwMjA3MjMzMTkwL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2J1Y2tldEluQ29weUF0dHJzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImJ1Y2tldEluQ29weUF0dHJzIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzIzMzE5MCIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0theGhiK2k5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIno4U3VIUT09IiwKICJldGFnIjogIkNLYXhoYitpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "e2ced8916b827780", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/bucketInCopyAttrs/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/bucketInCopyAttrs?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "62" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "15e689305a038e1e59307970e6191833/15636936475796269568;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/bucketInCopyAttrs/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/bucketInCopyAttrs?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAifQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "3010" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:07 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220507000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrv125:4223,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=r041W6jgFZLuhATIx7SoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/25:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVW44U2NvQW45ZXphX0pmQTNpLXZESWxDOVVJYXZLV3I4VkJ0aGNoSkh0ZmczTDVUVXJNWlVIWDdIenlsTGg0ZnRCRHh0M1JDMGJlOGRQdUZUaVFEVUNNMFRnSl9TSFBRU291bERTY3VaZzYybTQ0Si1qcjVFbEllc0dnZTJCR1JWRV9IeldyUWo2UmE4Z2VReFdQNWZyek5qelVzV0dlaUtqbExvYTk4bzZjN19mVm5MWW1oUEtLYkEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upi14Hbj_7F4nuIz_Utwy65fnPCBcYO-pR97u6Nzb7XvSvTkhubPDKL3pbUYHX7xNYrsFiQJyDFm2yHzmZDJkN9ZbL1zJZG6Yhfraue6sJ5o34pB4s" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiUmVxdWlyZWQiLAogICAgImRlYnVnSW5mbyI6ICJjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1udWxsLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9YmFkUmVxdWVzdCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uUkVRVUlSRUQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPW51bGwsIGVycm9yUHJvdG9Db2RlPVJFUVVJUkVELCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5kZXN0aW5hdGlvbl9yZXNvdXJjZS5pZC5uYW1lLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkuZGVzdGluYXRpb25fcmVzb3VyY2UuaWQubmFtZSwgbWVzc2FnZT1SZXF1aXJlZCwgcmVhc29uPXJlcXVpcmVkLCBycGNDb2RlPTQwMH0gUmVxdWlyZWRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAwLAogICJtZXNzYWdlIjogIlJlcXVpcmVkIgogfQp9Cg==" + } + }, + { + "ID": "2a6af5d5914447a4", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=96beff29826c321cb036d06a5f110d104042a66bf17abc67a91bfb41bfd9" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "947842c916a0bd702dff8db8fd6448a2/16444362157315795728;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS05NmJlZmYyOTgyNmMzMjFjYjAzNmQwNmE1ZjExMGQxMDQwNDJhNjZiZjE3YWJjNjdhOTFiZmI0MWJmZDkNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNyYzMyYyI6ImNIK0Erdz09IiwibmFtZSI6Imhhc2hlc09uVXBsb2FkLTEifQoNCi0tOTZiZWZmMjk4MjZjMzIxY2IwMzZkMDZhNWYxMTBkMTA0MDQyYTY2YmYxN2FiYzY3YTkxYmZiNDFiZmQ5DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KSSBjYW4ndCB3YWl0IHRvIGJlIHZlcmlmaWVkDQotLTk2YmVmZjI5ODI2YzMyMWNiMDM2ZDA2YTVmMTEwZDEwNDA0MmE2NmJmMTdhYmM2N2E5MWJmYjQxYmZkOS0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3753" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:07 GMT" + ], + "Etag": [ + "CKPiqr+i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220507000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlj81:4352,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=r041W-_mHcW6N4SVrcgJ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/271:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWHJPNUkxYUpBc1pqVUJQQkxsWlpQQ0RaUURpeWtuNkMwRFJXbUlacDBSUHk2QVNxeXZvRkkyY3dMaklzQzEyUnhFeHJ6ajBSdDFJSWJ2cndIUldNOEhwekhyN0hFWnBUVERuSmk1Q29uQmZ1ODRtWGVPbnRyNzFzYzBFa00yU1RpMTZuTC1YZ0JiNkE0bWNSMXMtWjc2Y0dFSUV3OVJHbnlwQUdKZjhPSzZZZmNuS1drWW11OWJyQlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrRt6Av5wwAW_BdgwQaGw9vVQJr8N4dU764Hei0XufSFpz58s_O7o0ojuSj5J3kvHslZcwRw-hLB4v2TjkAvx-kopW5YGJJe728NzQTeb7hwLhx52s" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDc4NDU2NjciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9oYXNoZXNPblVwbG9hZC0xIiwKICJuYW1lIjogImhhc2hlc09uVXBsb2FkLTEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzg0NTY2NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNy44NDVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDcuODQ1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA3Ljg0NVoiLAogInNpemUiOiAiMjciLAogIm1kNUhhc2giOiAib2ZaakdsY1hQSmlHT0FmS0ZiSmwxUT09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTE/Z2VuZXJhdGlvbj0xNTMwMjIwMjA3ODQ1NjY3JmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDc4NDU2NjcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImhhc2hlc09uVXBsb2FkLTEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA3ODQ1NjY3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDS1BpcXIraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwNzg0NTY2Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzg0NTY2NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDS1BpcXIraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwNzg0NTY2Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzg0NTY2NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0tQaXFyK2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDc4NDU2NjcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vaGFzaGVzT25VcGxvYWQtMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNzg0NTY2NyIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ0tQaXFyK2k5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogImNIK0Erdz09IiwKICJldGFnIjogIkNLUGlxcitpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "fa070c41f0f81c3e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=26f5537ed8447d9dbf01da693f6419ef0741c7afdff00288bee5d00663c1" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "bbb2e4d8139e295b0aaddf78df0d5daf/17252070408995204383;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0yNmY1NTM3ZWQ4NDQ3ZDlkYmYwMWRhNjkzZjY0MTllZjA3NDFjN2FmZGZmMDAyODhiZWU1ZDAwNjYzYzENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNyYzMyYyI6ImNIK0EvQT09IiwibmFtZSI6Imhhc2hlc09uVXBsb2FkLTEifQoNCi0tMjZmNTUzN2VkODQ0N2Q5ZGJmMDFkYTY5M2Y2NDE5ZWYwNzQxYzdhZmRmZjAwMjg4YmVlNWQwMDY2M2MxDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KSSBjYW4ndCB3YWl0IHRvIGJlIHZlcmlmaWVkDQotLTI2ZjU1MzdlZDg0NDdkOWRiZjAxZGE2OTNmNjQxOWVmMDc0MWM3YWZkZmYwMDI4OGJlZTVkMDA2NjNjMS0tDQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "3339" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:07 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220507000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdv10:4003,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=r041W5yKO8aNhAT3nLboBA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/461:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWHJPNUkxYUpBc1pqVUJQQkxsWlpQQ0RaUURpeWtuNkMwRFJXbUlacDBSUHk2QVNxeXZvRkkyY3dMaklzQzEyUnhFeHJ6ajBSdDFJSWJ2cndIUldNOEhwekhyN0hFWnBUVERuSmk1Q29uQmZ1ODRtWGVPbnRyNzFzYzBFa00yU1RpMTZuTC1YZ0JiNkE0bWNSMXMtWjc2Y0dFSUV3OVJHbnlwQUdKZjhPSzZZZmNuS1drWW11OWJyQlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upz2Wf_831ck-W7BEMSRU2s_up5x3P6hVBLzoY7LKAd4fg2ijaW8RxC_NSthJl3CDa-Cp04SKitjCMs9ScKRqyDZ64BVdo6adNtYM5udyetJFx92Y4" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiaW52YWxpZCIsCiAgICAibWVzc2FnZSI6ICJQcm92aWRlZCBDUkMzMkMgXCJjSCtBL0E9PVwiIGRvZXNuJ3QgbWF0Y2ggY2FsY3VsYXRlZCBDUkMzMkMgXCJjSCtBK3c9PVwiLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1JTlZBTElEX1ZBTFVFLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89bnVsbCwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLklOVkFMSURfVkFMVUUsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPW51bGwsIGVycm9yUHJvdG9Db2RlPUlOVkFMSURfVkFMVUUsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlLmNyYzMyYywgbWVzc2FnZT1Qcm92aWRlZCBDUkMzMkMgXCJjSCtBL0E9PVwiIGRvZXNuJ3QgbWF0Y2ggY2FsY3VsYXRlZCBDUkMzMkMgXCJjSCtBK3c9PVwiLiwgdW5uYW1lZEFyZ3VtZW50cz1bY0grQS9BPT1dfSwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlLmNyYzMyYywgbWVzc2FnZT1Qcm92aWRlZCBDUkMzMkMgXCJjSCtBL0E9PVwiIGRvZXNuJ3QgbWF0Y2ggY2FsY3VsYXRlZCBDUkMzMkMgXCJjSCtBK3c9PVwiLiwgcmVhc29uPWludmFsaWQsIHJwY0NvZGU9NDAwfSBQcm92aWRlZCBDUkMzMkMgXCJjSCtBL0E9PVwiIGRvZXNuJ3QgbWF0Y2ggY2FsY3VsYXRlZCBDUkMzMkMgXCJjSCtBK3c9PVwiLlxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiUHJvdmlkZWQgQ1JDMzJDIFwiY0grQS9BPT1cIiBkb2Vzbid0IG1hdGNoIGNhbGN1bGF0ZWQgQ1JDMzJDIFwiY0grQSt3PT1cIi4iCiB9Cn0K" + } + }, + { + "ID": "822ec2bc6f2c2429", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=629b36eaa36d2dca8eaa8e28bc0c222a84b19ed52dfef21166cd09061b8a" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "646287bbf43f543d8de96219f105ba31/18059496090514730543;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS02MjliMzZlYWEzNmQyZGNhOGVhYThlMjhiYzBjMjIyYTg0YjE5ZWQ1MmRmZWYyMTE2NmNkMDkwNjFiOGENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJoYXNoZXNPblVwbG9hZC0xIn0KDQotLTYyOWIzNmVhYTM2ZDJkY2E4ZWFhOGUyOGJjMGMyMjJhODRiMTllZDUyZGZlZjIxMTY2Y2QwOTA2MWI4YQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCkkgY2FuJ3Qgd2FpdCB0byBiZSB2ZXJpZmllZA0KLS02MjliMzZlYWEzNmQyZGNhOGVhYThlMjhiYzBjMjIyYTg0YjE5ZWQ1MmRmZWYyMTE2NmNkMDkwNjFiOGEtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3753" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:08 GMT" + ], + "Etag": [ + "CNqsyL+i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220507000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmm2:4409,/bns/yw/borg/yw/bns/blobstore2/bitpusher/303.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=sE41W7qVAYn2hASxs6mIBQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/303.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/303:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWHJPNUkxYUpBc1pqVUJQQkxsWlpQQ0RaUURpeWtuNkMwRFJXbUlacDBSUHk2QVNxeXZvRkkyY3dMaklzQzEyUnhFeHJ6ajBSdDFJSWJ2cndIUldNOEhwekhyN0hFWnBUVERuSmk1Q29uQmZ1ODRtWGVPbnRyNzFzYzBFa00yU1RpMTZuTC1YZ0JiNkE0bWNSMXMtWjc2Y0dFSUV3OVJHbnlwQUdKZjhPSzZZZmNuS1drWW11OWJyQlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqA-tRHFuYdqCCgJq1hwfNPO_m48Am7CX8BLK4zVoP4FamfuRh5scrhuX6PzWkDC82Q5bz_XrPeJgSiE3GQdnQAQYcZm0rcrMhLlIyq6_2euasjq0M" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDgzMzAzMzAiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9oYXNoZXNPblVwbG9hZC0xIiwKICJuYW1lIjogImhhc2hlc09uVXBsb2FkLTEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODMzMDMzMCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowOC4zMjlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDguMzI5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA4LjMyOVoiLAogInNpemUiOiAiMjciLAogIm1kNUhhc2giOiAib2ZaakdsY1hQSmlHT0FmS0ZiSmwxUT09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTE/Z2VuZXJhdGlvbj0xNTMwMjIwMjA4MzMwMzMwJmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDgzMzAzMzAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImhhc2hlc09uVXBsb2FkLTEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA4MzMwMzMwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTnFzeUwraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwODMzMDMzMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODMzMDMzMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTnFzeUwraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwODMzMDMzMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODMzMDMzMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ05xc3lMK2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDgzMzAzMzAvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vaGFzaGVzT25VcGxvYWQtMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODMzMDMzMCIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ05xc3lMK2k5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogImNIK0Erdz09IiwKICJldGFnIjogIkNOcXN5TCtpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "bb653f4cd638f132", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=b440e66db07c5212504ab799a81480a630bf5c516aeef455bc4131892f86" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "06057a41303f934dca680e180ff98a23/420741743461232703;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1iNDQwZTY2ZGIwN2M1MjEyNTA0YWI3OTlhODE0ODBhNjMwYmY1YzUxNmFlZWY0NTViYzQxMzE4OTJmODYNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm1kNUhhc2giOiJvZlpqR2xjWFBKaUdPQWZLRmJKbDFRPT0iLCJuYW1lIjoiaGFzaGVzT25VcGxvYWQtMSJ9Cg0KLS1iNDQwZTY2ZGIwN2M1MjEyNTA0YWI3OTlhODE0ODBhNjMwYmY1YzUxNmFlZWY0NTViYzQxMzE4OTJmODYNCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA0KDQpJIGNhbid0IHdhaXQgdG8gYmUgdmVyaWZpZWQNCi0tYjQ0MGU2NmRiMDdjNTIxMjUwNGFiNzk5YTgxNDgwYTYzMGJmNWM1MTZhZWVmNDU1YmM0MTMxODkyZjg2LS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3753" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:08 GMT" + ], + "Etag": [ + "CMSq57+i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220508000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrgh6:4422,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=sE41W57tHorvhAT60qWICA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/4:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWHJPNUkxYUpBc1pqVUJQQkxsWlpQQ0RaUURpeWtuNkMwRFJXbUlacDBSUHk2QVNxeXZvRkkyY3dMaklzQzEyUnhFeHJ6ajBSdDFJSWJ2cndIUldNOEhwekhyN0hFWnBUVERuSmk1Q29uQmZ1ODRtWGVPbnRyNzFzYzBFa00yU1RpMTZuTC1YZ0JiNkE0bWNSMXMtWjc2Y0dFSUV3OVJHbnlwQUdKZjhPSzZZZmNuS1drWW11OWJyQlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrPyFzdsi76dubyuB_VkkaXbf9_fwqPZWSbzvwTwTMKtn5Lah3L1zgGQuWBu12TMEr1dhAC2CoHq6B0Mg9AzghzgKq5mrgVP8M6V3l-l9KcdyBBLq8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDg4Mzc5NTYiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9oYXNoZXNPblVwbG9hZC0xIiwKICJuYW1lIjogImhhc2hlc09uVXBsb2FkLTEiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODgzNzk1NiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowOC44MzdaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDguODM3WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA4LjgzN1oiLAogInNpemUiOiAiMjciLAogIm1kNUhhc2giOiAib2ZaakdsY1hQSmlHT0FmS0ZiSmwxUT09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTE/Z2VuZXJhdGlvbj0xNTMwMjIwMjA4ODM3OTU2JmFsdD1tZWRpYSIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDg4Mzc5NTYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImhhc2hlc09uVXBsb2FkLTEiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA4ODM3OTU2IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTVNxNTcraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwODgzNzk1Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODgzNzk1NiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTVNxNTcraTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwODgzNzk1Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2hhc2hlc09uVXBsb2FkLTEvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODgzNzk1NiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ01TcTU3K2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDg4Mzc5NTYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vaGFzaGVzT25VcGxvYWQtMS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwODgzNzk1NiIsCiAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ01TcTU3K2k5OXNDRUFFPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogImNIK0Erdz09IiwKICJldGFnIjogIkNNU3E1NytpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "203487c689494c8f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=b9429e76f2bf35c82a53f136cc12faf7e3e44309b84314030d566979e2b3" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "83228e9ff545cba9e4ea3d518be4be43/1156112025654407503;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1iOTQyOWU3NmYyYmYzNWM4MmE1M2YxMzZjYzEyZmFmN2UzZTQ0MzA5Yjg0MzE0MDMwZDU2Njk3OWUyYjMNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm1kNUhhc2giOiJvdlpqR2xjWFBKaUdPQWZLRmJKbDFRPT0iLCJuYW1lIjoiaGFzaGVzT25VcGxvYWQtMSJ9Cg0KLS1iOTQyOWU3NmYyYmYzNWM4MmE1M2YxMzZjYzEyZmFmN2UzZTQ0MzA5Yjg0MzE0MDMwZDU2Njk3OWUyYjMNCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA0KDQpJIGNhbid0IHdhaXQgdG8gYmUgdmVyaWZpZWQNCi0tYjk0MjllNzZmMmJmMzVjODJhNTNmMTM2Y2MxMmZhZjdlM2U0NDMwOWI4NDMxNDAzMGQ1NjY5NzllMmIzLS0NCg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "3553" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:08 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220507000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrv6:4082,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=sE41W7v3OsT2N87Rh_gH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/102:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWHJPNUkxYUpBc1pqVUJQQkxsWlpQQ0RaUURpeWtuNkMwRFJXbUlacDBSUHk2QVNxeXZvRkkyY3dMaklzQzEyUnhFeHJ6ajBSdDFJSWJ2cndIUldNOEhwekhyN0hFWnBUVERuSmk1Q29uQmZ1ODRtWGVPbnRyNzFzYzBFa00yU1RpMTZuTC1YZ0JiNkE0bWNSMXMtWjc2Y0dFSUV3OVJHbnlwQUdKZjhPSzZZZmNuS1drWW11OWJyQlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqFPTQ2IVam7a-ij-7toUScZglQ97-PsEbs8SqcUJdBl4Rb_EDnH56teEfdMaJ3td2xlIqy5q8eClBn5lqtlkM6gPW0QrRP-ijJyIlcxvO1Q68j3Mk" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiaW52YWxpZCIsCiAgICAibWVzc2FnZSI6ICJQcm92aWRlZCBNRDUgaGFzaCBcIm92WmpHbGNYUEppR09BZktGYkpsMVE9PVwiIGRvZXNuJ3QgbWF0Y2ggY2FsY3VsYXRlZCBNRDUgaGFzaCBcIm9mWmpHbGNYUEppR09BZktGYkpsMVE9PVwiLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1JTlZBTElEX1ZBTFVFLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89bnVsbCwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLklOVkFMSURfVkFMVUUsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPW51bGwsIGVycm9yUHJvdG9Db2RlPUlOVkFMSURfVkFMVUUsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlLm1kNV9oYXNoX2Jhc2U2NCwgbWVzc2FnZT1Qcm92aWRlZCBNRDUgaGFzaCBcIm92WmpHbGNYUEppR09BZktGYkpsMVE9PVwiIGRvZXNuJ3QgbWF0Y2ggY2FsY3VsYXRlZCBNRDUgaGFzaCBcIm9mWmpHbGNYUEppR09BZktGYkpsMVE9PVwiLiwgdW5uYW1lZEFyZ3VtZW50cz1bb3ZaakdsY1hQSmlHT0FmS0ZiSmwxUT09XX0sIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZS5tZDVfaGFzaF9iYXNlNjQsIG1lc3NhZ2U9UHJvdmlkZWQgTUQ1IGhhc2ggXCJvdlpqR2xjWFBKaUdPQWZLRmJKbDFRPT1cIiBkb2Vzbid0IG1hdGNoIGNhbGN1bGF0ZWQgTUQ1IGhhc2ggXCJvZlpqR2xjWFBKaUdPQWZLRmJKbDFRPT1cIi4sIHJlYXNvbj1pbnZhbGlkLCBycGNDb2RlPTQwMH0gUHJvdmlkZWQgTUQ1IGhhc2ggXCJvdlpqR2xjWFBKaUdPQWZLRmJKbDFRPT1cIiBkb2Vzbid0IG1hdGNoIGNhbGN1bGF0ZWQgTUQ1IGhhc2ggXCJvZlpqR2xjWFBKaUdPQWZLRmJKbDFRPT1cIi5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAwLAogICJtZXNzYWdlIjogIlByb3ZpZGVkIE1ENSBoYXNoIFwib3ZaakdsY1hQSmlHT0FmS0ZiSmwxUT09XCIgZG9lc24ndCBtYXRjaCBjYWxjdWxhdGVkIE1ENSBoYXNoIFwib2ZaakdsY1hQSmlHT0FmS0ZiSmwxUT09XCIuIgogfQp9Cg==" + } + }, + { + "ID": "a35ff976c976a510", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1f83551725982f9121622141542fbede/2771245958853342318;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "412" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:09 GMT" + ], + "Etag": [ + "CAk=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:09 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220509000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnr5:4245,/bns/yw/borg/yw/bns/blobstore2/bitpusher/473.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=sU41W97HBJbkhASluqCQBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/473.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/473:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWFhjbWo3TlFkeGxib3RjTGhzN0t6VWF5eFV5Z3g2VXBhWm01ZTlNeFVla0dPdFJqaWRsbk5PRUhuZlU4TjhwcHY3M05ZQWZoUWFIdW5OQ05sWDNTMW1wZ3J4Q2thMm45aXA3azJJSW1yWGR6RFBVUUliczFIejQxc3otUUsyWkRWS2Y4WE1yM3dMc1JLOEdhZjZjc2x0VEt6OURIU3RsUE4yYk1OS0c4azVINXVrc01oTDBsejRmM1UwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpvdHl3DCWn-2TCG5BNLdaEUJvXUICnT_4S38d_fSrt0zBcpaPKp7pdLXjhIb9vjxPWVxl0cy-YQRKdxn7RmdT9QDAECNzeAEAzzdFAH64fUGJJ2wc" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNwb2xpY3kiLAogInJlc291cmNlSWQiOiAicHJvamVjdHMvXy9idWNrZXRzL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAiYmluZGluZ3MiOiBbCiAgewogICAicm9sZSI6ICJyb2xlcy9zdG9yYWdlLmxlZ2FjeUJ1Y2tldE93bmVyIiwKICAgIm1lbWJlcnMiOiBbCiAgICAicHJvamVjdEVkaXRvcjpkdWxjZXQtcG9ydC03NjIiLAogICAgInByb2plY3RPd25lcjpkdWxjZXQtcG9ydC03NjIiCiAgIF0KICB9LAogIHsKICAgInJvbGUiOiAicm9sZXMvc3RvcmFnZS5sZWdhY3lCdWNrZXRSZWFkZXIiLAogICAibWVtYmVycyI6IFsKICAgICJwcm9qZWN0Vmlld2VyOmR1bGNldC1wb3J0LTc2MiIKICAgXQogIH0KIF0sCiAiZXRhZyI6ICJDQWs9Igp9Cg==" + } + }, + { + "ID": "cfa58786f61c5bca", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "317" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "94d2723a7718be6829265ac3a4beaa23/4386379892052211598;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJiaW5kaW5ncyI6W3sibWVtYmVycyI6WyJwcm9qZWN0RWRpdG9yOmR1bGNldC1wb3J0LTc2MiIsInByb2plY3RPd25lcjpkdWxjZXQtcG9ydC03NjIiXSwicm9sZSI6InJvbGVzL3N0b3JhZ2UubGVnYWN5QnVja2V0T3duZXIifSx7Im1lbWJlcnMiOlsicHJvamVjdFZpZXdlcjpkdWxjZXQtcG9ydC03NjIiXSwicm9sZSI6InJvbGVzL3N0b3JhZ2UubGVnYWN5QnVja2V0UmVhZGVyIn0seyJtZW1iZXJzIjpbInByb2plY3RWaWV3ZXI6ZHVsY2V0LXBvcnQtNzYyIl0sInJvbGUiOiJyb2xlcy9zdG9yYWdlLm9iamVjdFZpZXdlciJ9XSwiZXRhZyI6IkNBaz0ifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "519" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:10 GMT" + ], + "Etag": [ + "CAo=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220509000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vris8:4314,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=sU41W_btGIbRhQSY0oKoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/197:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWFhjbWo3TlFkeGxib3RjTGhzN0t6VWF5eFV5Z3g2VXBhWm01ZTlNeFVla0dPdFJqaWRsbk5PRUhuZlU4TjhwcHY3M05ZQWZoUWFIdW5OQ05sWDNTMW1wZ3J4Q2thMm45aXA3azJJSW1yWGR6RFBVUUliczFIejQxc3otUUsyWkRWS2Y4WE1yM3dMc1JLOEdhZjZjc2x0VEt6OURIU3RsUE4yYk1OS0c4azVINXVrc01oTDBsejRmM1UwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upq8wY51AAH2mDDRYuhnewlBTYZZhZtN8rakNmtQBddPTMzQvx_t-bm_M7UQy7Dl0xU2VkGE1CHz-QqqsPBfyh6dnaj4N_QVpPeWGy1OkRvjXHxzIQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNwb2xpY3kiLAogInJlc291cmNlSWQiOiAicHJvamVjdHMvXy9idWNrZXRzL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAiYmluZGluZ3MiOiBbCiAgewogICAicm9sZSI6ICJyb2xlcy9zdG9yYWdlLmxlZ2FjeUJ1Y2tldE93bmVyIiwKICAgIm1lbWJlcnMiOiBbCiAgICAicHJvamVjdEVkaXRvcjpkdWxjZXQtcG9ydC03NjIiLAogICAgInByb2plY3RPd25lcjpkdWxjZXQtcG9ydC03NjIiCiAgIF0KICB9LAogIHsKICAgInJvbGUiOiAicm9sZXMvc3RvcmFnZS5sZWdhY3lCdWNrZXRSZWFkZXIiLAogICAibWVtYmVycyI6IFsKICAgICJwcm9qZWN0Vmlld2VyOmR1bGNldC1wb3J0LTc2MiIKICAgXQogIH0sCiAgewogICAicm9sZSI6ICJyb2xlcy9zdG9yYWdlLm9iamVjdFZpZXdlciIsCiAgICJtZW1iZXJzIjogWwogICAgInByb2plY3RWaWV3ZXI6ZHVsY2V0LXBvcnQtNzYyIgogICBdCiAgfQogXSwKICJldGFnIjogIkNBbz0iCn0K" + } + }, + { + "ID": "8f6c55ece7b08942", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "27b885f98e0fb1616942aaed7f0b45e9/5929457330724846253;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "519" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:10 GMT" + ], + "Etag": [ + "CAo=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:10 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220510000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vru67:4406,/bns/yw/borg/yw/bns/blobstore2/bitpusher/168.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=sk41W_StIoSPN-3XkeAI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/168.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/168:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWFhjbWo3TlFkeGxib3RjTGhzN0t6VWF5eFV5Z3g2VXBhWm01ZTlNeFVla0dPdFJqaWRsbk5PRUhuZlU4TjhwcHY3M05ZQWZoUWFIdW5OQ05sWDNTMW1wZ3J4Q2thMm45aXA3azJJSW1yWGR6RFBVUUliczFIejQxc3otUUsyWkRWS2Y4WE1yM3dMc1JLOEdhZjZjc2x0VEt6OURIU3RsUE4yYk1OS0c4azVINXVrc01oTDBsejRmM1UwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq00A1wyFzFWwAIQP_t-Qy6ulXYshyjKDISFuUUA_JdSyU-Y4iKU5OPNRydlUsZXAaNafiyMtBCOcYFpAc6Ds4bUvN3Ciitd1xWesw8a4AFpOv491o" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNwb2xpY3kiLAogInJlc291cmNlSWQiOiAicHJvamVjdHMvXy9idWNrZXRzL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAiYmluZGluZ3MiOiBbCiAgewogICAicm9sZSI6ICJyb2xlcy9zdG9yYWdlLmxlZ2FjeUJ1Y2tldE93bmVyIiwKICAgIm1lbWJlcnMiOiBbCiAgICAicHJvamVjdEVkaXRvcjpkdWxjZXQtcG9ydC03NjIiLAogICAgInByb2plY3RPd25lcjpkdWxjZXQtcG9ydC03NjIiCiAgIF0KICB9LAogIHsKICAgInJvbGUiOiAicm9sZXMvc3RvcmFnZS5sZWdhY3lCdWNrZXRSZWFkZXIiLAogICAibWVtYmVycyI6IFsKICAgICJwcm9qZWN0Vmlld2VyOmR1bGNldC1wb3J0LTc2MiIKICAgXQogIH0sCiAgewogICAicm9sZSI6ICJyb2xlcy9zdG9yYWdlLm9iamVjdFZpZXdlciIsCiAgICJtZW1iZXJzIjogWwogICAgInByb2plY3RWaWV3ZXI6ZHVsY2V0LXBvcnQtNzYyIgogICBdCiAgfQogXSwKICJldGFnIjogIkNBbz0iCn0K" + } + }, + { + "ID": "b86cf8ebc91d4f7a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam/testPermissions?alt=json\u0026permissions=storage.buckets.get\u0026permissions=storage.buckets.delete", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "815342964fe43c3b5d348651edf499f8/7544592359123664333;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/iam/testPermissions?alt=json\u0026permissions=storage.buckets.get\u0026permissions=storage.buckets.delete" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "124" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:10 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:10 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220509000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcb124:4240,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=sk41W8yzL8HvhAS3rbPYCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/101:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWFhjbWo3TlFkeGxib3RjTGhzN0t6VWF5eFV5Z3g2VXBhWm01ZTlNeFVla0dPdFJqaWRsbk5PRUhuZlU4TjhwcHY3M05ZQWZoUWFIdW5OQ05sWDNTMW1wZ3J4Q2thMm45aXA3azJJSW1yWGR6RFBVUUliczFIejQxc3otUUsyWkRWS2Y4WE1yM3dMc1JLOEdhZjZjc2x0VEt6OURIU3RsUE4yYk1OS0c4azVINXVrc01oTDBsejRmM1UwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UozfiGqoVpp6MJB37vUXWGIMJvItUCsZqtVQwoK8pNza-SiGqySYQOQmYHrP4lSnUu2EfMHQa32tyQusbsiAVgPriUjMbquKjOtxZIDikYdz3LqYSE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSN0ZXN0SWFtUGVybWlzc2lvbnNSZXNwb25zZSIsCiAicGVybWlzc2lvbnMiOiBbCiAgInN0b3JhZ2UuYnVja2V0cy5nZXQiLAogICJzdG9yYWdlLmJ1Y2tldHMuZGVsZXRlIgogXQp9Cg==" + } + }, + { + "ID": "f56293ab80c4e14a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "93" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "98910b44687b8ef4e8e43e5da3a1f4b5/9087669797796298988;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJiaWxsaW5nIjp7InJlcXVlc3RlclBheXMiOnRydWV9LCJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIn0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "503" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:11 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrxx13:4296,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=s041W7TUBcLdhAT6tKGwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/625:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uoc3Jobdew6ZKQLb_yTpmbu5QRi6v0NMngiziYoE3JwYXaSbIagDYJTNf2gHF_NQ8WBxd9IMqxgNXAnqtUb0yUG-H9LKlubCOPGOrhL9Fg2P-ygOtc" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTEuNTU5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjExLjU1OVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImJpbGxpbmciOiB7CiAgInJlcXVlc3RlclBheXMiOiB0cnVlCiB9LAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "254dbae42b80f91a", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/user-integration%40gcloud-golang-firestore-tests.iam.gserviceaccount.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "159" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0eec1ded30e63cfa305e6bf6d882ed46/10702803730995168012;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/user-integration%40gcloud-golang-firestore-tests.iam.gserviceaccount.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwicm9sZSI6Ik9XTkVSIn0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "615" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:13 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrpw4:4066,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=s041W8iiO8LxhATkzq7YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/221:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uox0tWqvw02gze1TPmqnNmS4Y5JDdQiMAuKFpMqD_mBhFoe9uqbLXxSoy8D6vYwHU7O42ynSuV3oqzyNR1wnMIB_Z72Z2BvYWMolMIWhbMUMpfuFNo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogInJvbGUiOiAiT1dORVIiLAogImVtYWlsIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICJldGFnIjogIkNBST0iCn0K" + } + }, + { + "ID": "6c8f46049da55376", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d79926c10f4fbd24c2ce4cb365d8303b/12317656193512359467;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3412" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220513000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqp3:4354,/bns/yw/borg/yw/bns/blobstore2/bitpusher/325.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=tU41W_meLIyXhATojpP4BQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/325.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/325:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoKBxe2U6CACrf-cOSkm214HEoukP4hQLHrpp-16p2vno5Anh3HVtVucf_KynWA1dR14jLjkUYwQrBXPQ-Bz3Jo8TSvMxTWiwmkIKE4oTVPvmLSE5s" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTEuNTU5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjEzLjUxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2FjbC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiYmlsbGluZyI6IHsKICAicmVxdWVzdGVyUGF5cyI6IHRydWUKIH0sCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "3da307f0a2a334b2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f30af424781e36250d16458bcde99a27/13860734727384877387;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3412" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220514000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlf17:4159,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=tk41W7_nAcL7hQTg1oPgDg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/306:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoPoQKs4Fbo8-rogSBfxvUdbT0L6_4EN8sVXpaoWxctZAw2wXqcd80C2y3LzrGfJbB8v92kdLnqLkJtSlES24bcix298xrmSNoThrh7toxjN5WEs4Y" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTEuNTU5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjEzLjUxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2FjbC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiYmlsbGluZyI6IHsKICAicmVxdWVzdGVyUGF5cyI6IHRydWUKIH0sCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "076a2cf477151fe2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8333a3bd4ffc404e38306dfa8c5d952a/15475868660583812202;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12857" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220514000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrbs2:4372,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=tk41W9vpF4aIN5_KqfgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/157:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOIrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpmmKVxrBBS3p_c0sUCPxG9lnbG72s3AOqWwCGUcR1qlwkaQMKdA2u76LwoHQdaXuto4CadX8H8jveyf8YwYk9Stmd6ZenAPCtmN-vF47Ya2j8XS3w" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjEwMilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkdldEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0QnVja2V0LmphdmE6MzEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmdldChCdWNrZXRzRGVsZWdhdG9yLmphdmE6NzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YToxMDIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjMxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5nZXQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YToxMDIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjMxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5nZXQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YToxMDIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjMxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5nZXQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIKIH0KfQo=" + } + }, + { + "ID": "d00853883a2ca4e4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "935f75965cc214fd3bcafbec1048751b/17018946099256381322;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3412" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220514000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrat2:4108,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=tk41W7a5JsPrhAT61qjACQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/635:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOIrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrMgurT0pXNn820o2n77mZhyWnhCZIeMFkfDiSd1A0S4Z8AsjC1EgUx7nYx0kIZ6odOs4Iu6gV4Hm3QyiLkBt0BOU3fbcaJSQNbBfauuhdREixizhs" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTEuNTU5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjEzLjUxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2FjbC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiYmlsbGluZyI6IHsKICAicmVxdWVzdGVyUGF5cyI6IHRydWUKIH0sCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "a2b78339e45ec204", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9653d85d1963489b361af4a13e0db9bc/187617433722475177;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json\u0026projection=full\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13809" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:14 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220514000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrnv6:4101,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=tk41W-e8LMPdhQTRkIzQBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/178:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOIrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqN8gEiG829eXCdLN7O0ZTMhpPjaIw74hay0KljUAtauiQvONHyUkdLBvpak4vbQF0epc0xKhRTgrQ4iRvpCI6drRvQDBaU2sO4N_Fu9N111__N0rg" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YToxMDIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjMxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5nZXQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9Rk9SQklEREVOLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YToxMDIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjMxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5nZXQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9Zm9yYmlkZGVuLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5GT1JCSURERU4sIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkdldEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0QnVja2V0LmphdmE6MTAyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YTozMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IuZ2V0KEJ1Y2tldHNEZWxlZ2F0b3IuamF2YTo3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkdldEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0QnVja2V0LmphdmE6MTAyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YTozMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IuZ2V0KEJ1Y2tldHNEZWxlZ2F0b3IuamF2YTo3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDMsCiAgIm1lc3NhZ2UiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4iCiB9Cn0K" + } + }, + { + "ID": "63da1af8ebe97ab1", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=6ec6184745f623088539da6f403efefa8cb7834516496d589a9a1f0d200f" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "18844e280a5ab78fc2da54a0f856f5e2/995044214720074937;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS02ZWM2MTg0NzQ1ZjYyMzA4ODUzOWRhNmY0MDNlZmVmYThjYjc4MzQ1MTY0OTZkNTg5YTlhMWYwZDIwMGYNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJmb28ifQoNCi0tNmVjNjE4NDc0NWY2MjMwODg1MzlkYTZmNDAzZWZlZmE4Y2I3ODM0NTE2NDk2ZDU4OWE5YTFmMGQyMDBmDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW4NCg0KaGVsbG8NCi0tNmVjNjE4NDc0NWY2MjMwODg1MzlkYTZmNDAzZWZlZmE4Y2I3ODM0NTE2NDk2ZDU4OWE5YTFmMGQyMDBmLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3568" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:15 GMT" + ], + "Etag": [ + "CJi87sKi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrit75:4012,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=tk41W-iYOcfUN9mJjPgB" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/435:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpgDHZeRKk__D-1l5dfU4MGmQTu8M9Eiy_t5Tt4akfm21dBSFS1bfuj4Dg7HUg8AZynpFAXylCDZXI-YqjTjlZ_ApBexxmpAz1W13-XlKD9GKiaLDM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNTI0NjM2MCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNTI0NjM2MCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNS4yNDVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTUuMjQ1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE1LjI0NVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNTI0NjM2MCZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTUyNDYzNjAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNTI0NjM2MCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0ppODdzS2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNTI0NjM2MC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTUyNDYzNjAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0ppODdzS2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNTI0NjM2MC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTUyNDYzNjAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNKaTg3c0tpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTUyNDYzNjAvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTUyNDYzNjAiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNKaTg3c0tpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDSmk4N3NLaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "43765d8b1f7e791c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=8d079d613ea35805061825899e26e84a0148f0c34a881a37131d1dd19cce" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a7649fd09323bfd4db900532c9119a7e/1730695967594993097;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS04ZDA3OWQ2MTNlYTM1ODA1MDYxODI1ODk5ZTI2ZTg0YTAxNDhmMGMzNGE4ODFhMzcxMzFkMWRkMTljY2UNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJmb28ifQoNCi0tOGQwNzlkNjEzZWEzNTgwNTA2MTgyNTg5OWUyNmU4NGEwMTQ4ZjBjMzRhODgxYTM3MTMxZDFkZDE5Y2NlDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW4NCg0KaGVsbG8NCi0tOGQwNzlkNjEzZWEzNTgwNTA2MTgyNTg5OWUyNmU4NGEwMTQ4ZjBjMzRhODgxYTM3MTMxZDFkZDE5Y2NlLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3568" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:15 GMT" + ], + "Etag": [ + "CI2vhsOi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrx3:4290,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=t041W9L5FY7ShASVqYPICg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/111:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrpGOF-FAKIRqmfIc2tdZ6nwrxlN0HODuZXkFevgbaDAQWK-GfKw_Yqq-Hb1asq9wVNPB3SDXkSQ7-kv7eD32DqTsl-856_iLIw5LIy8FTX_jA9qrg" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNTYzNzkwMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNTYzNzkwMSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNS42MzdaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTUuNjM3WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE1LjYzN1oiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNTYzNzkwMSZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTU2Mzc5MDEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNTYzNzkwMSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0kydmhzT2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNTYzNzkwMS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTU2Mzc5MDEiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0kydmhzT2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNTYzNzkwMS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTU2Mzc5MDEiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNJMnZoc09pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTU2Mzc5MDEvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTU2Mzc5MDEiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNJMnZoc09pOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDSTJ2aHNPaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "f6dd652ef67aec41", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=a8d6c4c95945ccad252c84ee344324fb14107e1866372b316e1a2782678d" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8d3e926569a3fbd857f05b8f8b1c56de/2538121649097742297;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1hOGQ2YzRjOTU5NDVjY2FkMjUyYzg0ZWUzNDQzMjRmYjE0MTA3ZTE4NjYzNzJiMzE2ZTFhMjc4MjY3OGQNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJmb28ifQoNCi0tYThkNmM0Yzk1OTQ1Y2NhZDI1MmM4NGVlMzQ0MzI0ZmIxNDEwN2UxODY2MzcyYjMxNmUxYTI3ODI2NzhkDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW4NCg0KaGVsbG8NCi0tYThkNmM0Yzk1OTQ1Y2NhZDI1MmM4NGVlMzQ0MzI0ZmIxNDEwN2UxODY2MzcyYjMxNmUxYTI3ODI2NzhkLS0NCg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "12529" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:15 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220515000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrn19:4063,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=t041W82pLtOkhATsyYKoCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/521:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOMrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UryRWS9ZzLuKeR5RdgkO5odKdLIzjvxFbuQrsnAe-Lh_h47o9B5FuMCwpSQ3kFdkdk-WnIS1sa7d5Zt-gd1Lau3R6GPl3Ki_Zf4xLEuAm8G-No4US0" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "27d1576bed80ae31", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=eae2d5bb073df0bbe96a812a67f780df1ba65d35731d8a05940c5be97c14" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9b66cad0e78508f56af1136e71260c22/3345829900793927912;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1lYWUyZDViYjA3M2RmMGJiZTk2YTgxMmE2N2Y3ODBkZjFiYTY1ZDM1NzMxZDhhMDU5NDBjNWJlOTdjMTQNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJmb28ifQoNCi0tZWFlMmQ1YmIwNzNkZjBiYmU5NmE4MTJhNjdmNzgwZGYxYmE2NWQzNTczMWQ4YTA1OTQwYzViZTk3YzE0DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW4NCg0KaGVsbG8NCi0tZWFlMmQ1YmIwNzNkZjBiYmU5NmE4MTJhNjdmNzgwZGYxYmE2NWQzNTczMWQ4YTA1OTQwYzViZTk3YzE0LS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3523" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220515000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrw66:4310,/bns/yw/borg/yw/bns/blobstore2/bitpusher/617.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=t041W9S4OdW5hQTt_5nwBQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/617.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/617:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOMrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upbzhwbcit4Fs3rhm8baw7cJ0inJ_8WUe4PDz5RKgYVKDGUQCj-OrWzU7v5gnyGVzg6HKrWiXcivl3z1Onr81zRFaBB0idbsC73U0YPo-acJuy5B7o" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNi4wNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTYuMDQ5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE2LjA0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNjA1MDkwMiZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "33124363d24c10c2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=19587cf12768c8974266b5287f33e5a87f84de670a4390fabfd8684da171" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "666b5160a1d6ea8ee24f338f53aa2856/4153256681791527672;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xOTU4N2NmMTI3NjhjODk3NDI2NmI1Mjg3ZjMzZTVhODdmODRkZTY3MGE0MzkwZmFiZmQ4Njg0ZGExNzENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJmb28ifQoNCi0tMTk1ODdjZjEyNzY4Yzg5NzQyNjZiNTI4N2YzM2U1YTg3Zjg0ZGU2NzBhNDM5MGZhYmZkODY4NGRhMTcxDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW4NCg0KaGVsbG8NCi0tMTk1ODdjZjEyNzY4Yzg5NzQyNjZiNTI4N2YzM2U1YTg3Zjg0ZGU2NzBhNDM5MGZhYmZkODY4NGRhMTcxLS0NCg==" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13481" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220515000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrqp9:4465,/bns/yw/borg/yw/bns/blobstore2/bitpusher/291.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uE41W9S3CofTN_LZjLgD" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/291.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/291:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOMrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoUtHzivC_HU9AMj05A7N_EChEwwGC4Cd_uud1AQzPfRHOUnkCLSoCsDX_UB__9RklNn8VD9RkYh3mryNsUn30S2VWLDotTLEguQ3uuQpZ8uWq7h0Q" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "a928bb7bf8687dfb", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "08d1377ec8e17155c274fc194ad9f820/5696334120464096536;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "5" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Etag": [ + "\"5d41402abc4b2a76b9719d911017c592\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1530220216050902" + ], + "X-Goog-Hash": [ + "crc32c=mnG7TA==", + "md5=XUFAKrxLKna5cZ2REBfFkg==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "5" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/27,/bns/xg/borg/xg/bns/blobstore2/bitpusher/144.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uE41W5zyGc6z_QSq17aACQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/144.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/144:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up3PfCVQKPiR6DiCVpfwCJEz4vzo-RgveL_pez6n48ga9AxWksJfdjukF4CWg4cjMUDH105nDpYX-dZOrZQ3tIKAP66Pw" + ] + }, + "Body": "aGVsbG8=" + } + }, + { + "ID": "dd67652d41d2686b", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fd21f084b73d31df1bbdd72c616bcb92/7311468053663031351;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo" + ], + "X-Goog-User-Project": [ + "dulcet-port-762" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "5" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Etag": [ + "\"5d41402abc4b2a76b9719d911017c592\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1530220216050902" + ], + "X-Goog-Hash": [ + "crc32c=mnG7TA==", + "md5=XUFAKrxLKna5cZ2REBfFkg==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "5" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/0,/bns/xg/borg/xg/bns/blobstore2/bitpusher/16.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uE41W9-8Ic21_QTXxrTYCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/16.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/16:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UopOVmH-iqH_wuhhN37j3OGNCTOz1awBkR0BJweU-cnwgEot_jrU0AoU-djJaaH4ZF5Lzp2tiEtkweRHE7zDdPt0rHJ6w" + ] + }, + "Body": "aGVsbG8=" + } + }, + { + "ID": "db433d11c39a99dc", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4263b7e61fa6a42a4824946ddcdfd52d/8926601982566998871;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "266" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/57,/bns/xg/borg/xg/bns/blobstore2/bitpusher/27.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uE41W_yIJc2x_QSH3qPYCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/27.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/27:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo3nec4ennzHc49DG_-yuy9YmGv-DWUW2XecUT0b38Czq_VH1Vt4UwyXivL4rbEEbPoQ2GZIayrSVNjp3zsCIp3t-xs_w" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+VXNlclByb2plY3RNaXNzaW5nPC9Db2RlPjxNZXNzYWdlPkJ1Y2tldCBpcyBhIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLjwvTWVzc2FnZT48RGV0YWlscz5CdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci48L0RldGFpbHM+PC9FcnJvcj4=" + } + }, + { + "ID": "f1d7d281a173bbab", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1c9400b9342eb99e0b40f5859a7e6dfc/10469680520734484086;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo" + ], + "X-Goog-User-Project": [ + "gcloud-golang-firestore-tests" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "5" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Etag": [ + "\"5d41402abc4b2a76b9719d911017c592\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:10:16 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1530220216050902" + ], + "X-Goog-Hash": [ + "crc32c=mnG7TA==", + "md5=XUFAKrxLKna5cZ2REBfFkg==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "5" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/8,/bns/xg/borg/xg/bns/blobstore2/bitpusher/161.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uE41W-uLMou6_QSYkbfICw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/161.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/161:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpCMgVcXoWdrmzePVAThzJawO4WXMW0f8IMHdhJxPMTw2uNtnddA7McBGrH7IuoaKq9uwsy54mOWuHwLkYRMiYoumcDqg" + ] + }, + "Body": "aGVsbG8=" + } + }, + { + "ID": "42864a4ed49833e6", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a6d4423924680721fa9909d721a40b4a/12084814453933353366;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0002/foo" + ], + "X-Goog-User-Project": [ + "veener-jba" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "342" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/9,/bns/xg/borg/xg/bns/blobstore2/bitpusher/157.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uE41W-agN6u5_QTCxI6wCg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/157.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/157:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrDJqfXKAavivqZCnygzCrMWdDbIRSsy-nl53ETiaQLMmXx1BkgNhIHcuHdBUo6h2rvFN73wg_2ukCDORGwPNkwnyQ6Yg" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+VXNlclByb2plY3RBY2Nlc3NEZW5pZWQ8L0NvZGU+PE1lc3NhZ2U+UmVxdWVzdGVyIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBwZXJtaXNzaW9ucyBvbiB1c2VyIHByb2plY3QuPC9NZXNzYWdlPjxEZXRhaWxzPmludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuPC9EZXRhaWxzPjwvRXJyb3I+" + } + }, + { + "ID": "31408bf1488157e9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "46941ff997a590c2eedb3d891654ce25/13627891888311086261;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3523" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220513000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vry82:4465,/bns/yw/borg/yw/bns/blobstore2/bitpusher/293.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uU41W_7gB4SchwS8k4SIAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/293.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/293:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpGte3xsP3Zp7_HgydfXba1Xuq9gBP_nINu13UEfbbaTAYD4irZEUMqH_-NLMKDeRn-lmV7nt_VPrnu2UoEi1qVPlHBqKN2ogJ3X1XOhOXhLjgL8SA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNi4wNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTYuMDQ5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE2LjA0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNjA1MDkwMiZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "190cbc2c7fe0c559", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "72d8ae241932c1c8c3f789bde6064231/15242744350828212181;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3523" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220513000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbq124:4261,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uU41W5-YDYXGhQT1vofYAg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/288:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UorRQ8cwPhFKv7il_-XrPG0d6JrikWYUuaeEjglHgrPgNLYCfU9o6rJi2TW9K39ZqpZf_-NngBn6_hXEsFiSbo5O23gNXxujhMyfWT4tYHpz56uMPc" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNi4wNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTYuMDQ5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE2LjA0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNjA1MDkwMiZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "c0ea345eefed2670", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3325d76a9425b670cfd8c677f9b4c33c/16857879383521997556;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12469" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220514000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrrw17:4231,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uU41W_ShEoPThQS6q4CABQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/608:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOIrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UphlJBEP2iTABN3407ia-4ih9-oM7S9yL9TiHnjEjlvp-XZNPzQQFfwm4p9jZrBCCHlvxDzOW7m3XIXW1dO_KFbl09KQMN4iRfofotcSZeQ72NCRB0" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6MzMzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZ2V0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6MzMzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZ2V0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTozMzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5HZXRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldE9iamVjdC5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5nZXQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjgxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5HZXRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldE9iamVjdC5qYXZhOjMzMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmdldChPYmplY3RzRGVsZWdhdG9yLmphdmE6ODEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "00b270f78c67626c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "08a9a77c6de9dccf037e8b02c715d961/18400956822194566420;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3523" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220514000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrce128:4376,/bns/yw/borg/yw/bns/blobstore2/bitpusher/388.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uU41W_PTIIq0hQSgpJTwCw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/388.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/388:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOIrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uop-J8iBEhEnXgkz_MzPX9TCr42SEmTETdU5oCTEXa5qIXlaNqh_p1sQQEpXFUp2BAf0tAiWBeRH4A0pYKC0n9cAl98J6kkh1Sua2KxVm-cq9OEu0I" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNi4wNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTYuMDQ5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE2LjA0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNjA1MDkwMiZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "3142f930904cbbde", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8aa6a345da9a66a35c171d811cd12c58/1569628152365758515;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13421" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:17 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220514000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrdo186:4304,/bns/yw/borg/yw/bns/blobstore2/bitpusher/238.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uU41W53wJcGphQST_ovoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/238.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/238:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOIrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur4LNMxyGsvG7u9Lg--pnN3GYdVlXrL40RHbaNlsnAtOhWejHJlG6wYttZ12LHxIPap7ywyWu-8rEG88KRsBdVE5uZwYL5zL8IAiJImgiALLxuI5Yc" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5HZXRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldE9iamVjdC5qYXZhOjMzMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmdldChPYmplY3RzRGVsZWdhdG9yLmphdmE6ODEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6MzMzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZ2V0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkdldE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0T2JqZWN0LmphdmE6MzMzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZ2V0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuR2V0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRPYmplY3QuamF2YTozMzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5HZXRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldE9iamVjdC5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5nZXQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjgxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "100fe931303dceec", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "85" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4532ade6469dfc883b7b83b117ae24f5/3112705591038327635;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJjb250ZW50TGFuZ3VhZ2UiOiJlbiJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3549" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:18 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrkx5:4437,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uU41W_PKMsaNhQTT7aKgCA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/199:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpknFs9O5h99ip2tORJBwl0L7JgZZXUe3rGGiKJlDIi5ZxWBUpmSYuRbnBXRq_oUP_HATK7hpG_eWAEhSJw4Dan0DQ_6GIMiC3RsALhfls0gK4r-1M" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMiIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNi4wNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTcuOTE4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE2LjA0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNjA1MDkwMiZhbHQ9bWVkaWEiLAogImNvbnRlbnRMYW5ndWFnZSI6ICJlbiIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAidXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFJPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIm1uRzdUQT09IiwKICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBST0iCn0K" + } + }, + { + "ID": "9bbb7b21fd48e636", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "85" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3dd6f41b0e6263e4c3eceadd5175eca8/4727840623732113010;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJjb250ZW50TGFuZ3VhZ2UiOiJlbiJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3549" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:18 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbo2:4462,/bns/yw/borg/yw/bns/blobstore2/bitpusher/522.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uk41W9nRAsSnhQTgoLeoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/522.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/522:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoMQbhuYXuQ7GOff9UbKImJjMVhKo8y6MQHEeUTUJ5EH6RnSlppnY9sB5EqIsUBkPPMyAM6Ln5YjYwcNutRKulDH2LcSXifKwgjZ43Fh5LRqjcLQrY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMyIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNi4wNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTguMTg4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE2LjA0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNjA1MDkwMiZhbHQ9bWVkaWEiLAogImNvbnRlbnRMYW5ndWFnZSI6ICJlbiIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFNPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAidXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFNPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIm1uRzdUQT09IiwKICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBTT0iCn0K" + } + }, + { + "ID": "4dbc47f781b6c21c", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "85" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5da10d26a1ccd166b5f5f271680af74d/6270918062404682130;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJjb250ZW50TGFuZ3VhZ2UiOiJlbiJ9Cg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "12661" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:18 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrdf5:4210,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uk41W67MFYbUhQSJtoaQBA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/66:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur1qPgBQlfszVlaM3FT3ZbBU3ySCoXPQdwcGpR_OT7yLURvPVeMtSnKGNQiGe4dTEjGG4qD-tqBZbVA6b4gFVm88c6mtD1Gov_MNCZ9XIMP7nkWTM4" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjM0NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci51cGRhdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjM0NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci51cGRhdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuVXBkYXRlQW5kUGF0Y2hPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFuZFBhdGNoT2JqZWN0LmphdmE6MzQ1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuVXBkYXRlQW5kUGF0Y2hPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFuZFBhdGNoT2JqZWN0LmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLnVwZGF0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5VcGRhdGVBbmRQYXRjaE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQW5kUGF0Y2hPYmplY3QuamF2YTozNDUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5VcGRhdGVBbmRQYXRjaE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQW5kUGF0Y2hPYmplY3QuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IudXBkYXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMDYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "01128ee46ef74cf2", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "85" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "bacf82845c280e09dbee3cef35b7787f/7886051991308715185;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJjb250ZW50TGFuZ3VhZ2UiOiJlbiJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3549" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:18 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAQ=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrbd184:4417,/bns/yw/borg/yw/bns/blobstore2/bitpusher/331.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uk41W8OuIMKOhgT_2Z64Bg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/331.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/331:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrAXV5iYTuMuUUEzPacx6UnMeQlHjUqSuMGGixfMRpnH2sSWdNjcuyXYx7pYheIMTvkteiLeV-xEhvXMhB8NflmyPTS5PHqv3hy9bhXJ80ldJfN2xM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAibWV0YWdlbmVyYXRpb24iOiAiNCIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDoxNi4wNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MTguNjE2WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjE2LjA0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIxNjA1MDkwMiZhbHQ9bWVkaWEiLAogImNvbnRlbnRMYW5ndWFnZSI6ICJlbiIsCiAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAiYWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVE9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVE9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFRPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIxNjA1MDkwMi91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIxNjA1MDkwMiIsCiAgICJlbnRpdHkiOiAidXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgImVtYWlsIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFRPSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAidXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIKIH0sCiAiY3JjMzJjIjogIm1uRzdUQT09IiwKICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBUT0iCn0K" + } + }, + { + "ID": "00dae6ab172ea645", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "85" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "46ca805e584f6572ef53d8ca97d9997a/9501185924507584465;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026projection=full\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJjb250ZW50TGFuZ3VhZ2UiOiJlbiJ9Cg==" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13613" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:18 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrqe13:4484,/bns/yw/borg/yw/bns/blobstore2/bitpusher/115.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uk41W5ujLcXWhATtno6wAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/115.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/115:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpQmzUEoByvFITXR7T3N4j137soNyjFtaBEfAWR9Of14rus4CXBTwwBrmYmkj8jtkwQeR9IfFckazKCRTyYFzo4wDwEoHX-P0RWVksQ6h9Tz2ZN-yo" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5VcGRhdGVBbmRQYXRjaE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQW5kUGF0Y2hPYmplY3QuamF2YTozNDUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5VcGRhdGVBbmRQYXRjaE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQW5kUGF0Y2hPYmplY3QuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IudXBkYXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMDYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjM0NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci51cGRhdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjM0NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlVwZGF0ZUFuZFBhdGNoT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBbmRQYXRjaE9iamVjdC5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci51cGRhdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuVXBkYXRlQW5kUGF0Y2hPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFuZFBhdGNoT2JqZWN0LmphdmE6MzQ1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuVXBkYXRlQW5kUGF0Y2hPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFuZFBhdGNoT2JqZWN0LmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLnVwZGF0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "4a8b1b2718d9b064", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b0b9a3a93a81c40a7c7597fd3f20c02e/11043981892498475760;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "403" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:20 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbp125:4390,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=uk41W_fcONGChQTP5Cs" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/139:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpJycIVITYZYCSk73eOheXGapDPZ-dYKqC-WGASnlDXcm3VwZI4gice2InchwYM9MqWVAliUgjUwNUt2yEMmW6lix45IZkyuaKl8PtSVeNEishbrBA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZG9tYWluLWdvb2dsZS5jb20iLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQU09Igp9Cg==" + } + }, + { + "ID": "bb277ce379d81a6d", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "158d3e0dc393a56b52b071dc7eb7368b/12659116925192195344;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "403" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:20 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vria2:4265,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vE41W4alINDXN_6_oYgI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/129:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoljIuBxeSkz08rKihRr-DXUYHF_UMfzs-g9uQG7eEuIRyG-zOcBgYm9WVxZ2RPClRbnyKkaee1AvkuzSrkfjZQ9iquZQ7yrz7mWaHqDZD0nxQU4Ns" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZG9tYWluLWdvb2dsZS5jb20iLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQU09Igp9Cg==" + } + }, + { + "ID": "aaccc858a7b3541f", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b337f0690498d25811062520fbe0c456/14202194359569928239;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13913" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:20 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrpv11:4112,/bns/yw/borg/yw/bns/blobstore2/bitpusher/492.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vE41W4XiMYikhwST84aoBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/492.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/492:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoN0c7HMbwIpya1PLfc5N41t9a2inrwWt2CA3dhygca1rirFarzpuiFz26FA-Blem5qi4Tk78Pz7RU2yk_xRNF805Rm_28rTvyj7juQgrOPBNTILZ4" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6MTYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci51cGRhdGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIKIH0KfQo=" + } + }, + { + "ID": "5f988648496bbcc3", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d4352a964ac5b65aae79386f4c2f760e/15817328292768797519;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "403" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:21 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrbx62:4007,/bns/yw/borg/yw/bns/blobstore2/bitpusher/405.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vU41W9W2Acm8hgT99KeQAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/405.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/405:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqMXZhr3hO4vFLHQL4GL8F_so6-eqx4cdYM0PHUB4x0U5bZDO0CazPcp0d8zzFMvami2VDcYEtCwk0i4pvx1yDbIGfAz_KIKA87xcggmS-izaF4QKk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZG9tYWluLWdvb2dsZS5jb20iLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQU09Igp9Cg==" + } + }, + { + "ID": "f0aebd6ad8a4eae7", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f6591b10278450b95cd7cf26ad92bb25/17432462225967732334;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14865" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:21 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vry17:4479,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vU41W_LKBtbshgThiaaoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/372:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpdaeUA7XBhqwNUqeRKkK8UAmHkZu63mKLIQN2Aj8HutErUQgfwb8By1PNsjTaOGXC28dOJl5hJUBPZgmavGXqUifnLcneXxReF4QLuohFFz07EIEA" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9Rk9SQklEREVOLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9Zm9yYmlkZGVuLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5GT1JCSURERU4sIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDMsCiAgIm1lc3NhZ2UiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4iCiB9Cn0K" + } + }, + { + "ID": "458e8210b09b16fb", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "91803a2e383e5ef81fd9d1ca9e8102f0/529078165402311054;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2646" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:21 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:21 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmk1:4341,/bns/yw/borg/yw/bns/blobstore2/bitpusher/133.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vU41W_G4FI6IhQSY0ILACg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/133.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/133:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqS3u-ld30ynwDpV7zcUPHHOArUY_p75xyep3cdhVYPllTybL-UEMK7qQ83uhyuGnv9aiNjzXYisiIrAZOdUP7MkQUVMkXmFcXygYTQpR6hjLEaWsM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2RvbWFpbi1nb29nbGUuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ0FNPSIKICB9CiBdCn0K" + } + }, + { + "ID": "f0d818ac284ba140", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c95d1319c144ddc0a91eff30c1276bb5/2144212094306344109;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2646" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:21 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:21 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrir7:4320,/bns/yw/borg/yw/bns/blobstore2/bitpusher/58.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vU41W6b0JcbuN4yunbAI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/58.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/58:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur_BlKLKSgWSKxbKbZW-jjHfl6Hr8cX1JSUnIIvhcUTcpKboQp8oKodsBw20uzhuHZjVl3W2LtEE9rk3bSx95dkPASJwhHjWk2iDqdVHEReqp4epUc" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2RvbWFpbi1nb29nbGUuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ0FNPSIKICB9CiBdCn0K" + } + }, + { + "ID": "937a08b12197b6f3", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "627bf8da27c5e684bfbaa5a92a4246f0/3687289532978913229;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13869" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:22 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:22 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrhr6:4427,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vU41W-jdN4aihQTOw5HwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/68:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpuUNauXKg3Ngl0jvcCP7yD8D3qrBfEVMcjgdb2-6GMhXGQ2mxPBRa2XKcqRDn0I0x2H-geyMgBas1R59s0vUotK6pbd71664cpxfEL1xGd7-49nbE" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPVJFUVVJUkVELCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5saXN0KEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9YmFkUmVxdWVzdCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uUkVRVUlSRUQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAyMCBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAwLAogICJtZXNzYWdlIjogIkJ1Y2tldCBpcyByZXF1ZXN0ZXIgcGF5cyBidWNrZXQgYnV0IG5vIHVzZXIgcHJvamVjdCBwcm92aWRlZC4iCiB9Cn0K" + } + }, + { + "ID": "93bc95e34c46c800", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9a0b7d5457d211d3635424d7e82d042c/5302423466177848044;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2646" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:22 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:22 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrrw17:4231,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vk41W5LKBoH1N7-RjPgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/198:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upuql0SxcO1P3TXnE0zW9ljU8AAkwyAawXOZ2FMztEm05BVMyyqSr-v5byuoxrwV_NGdHbXkOmhvd2IIZj25v2m4qmld-RWrLAbU85NGQy1CaWVHE8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgImVudGl0eSI6ICJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2RvbWFpbi1nb29nbGUuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ0FNPSIKICB9CiBdCn0K" + } + }, + { + "ID": "a09dbd6357e3f4c8", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "75c24c2d2ad809e3640ad4611b02329b/6917277028189824268;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "14821" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:22 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:22 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrnu7:4402,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vk41W46QC8LvhASL0qegBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/59:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpdHFn0a0Asz41wuAiaZOOWj-LnFZCpUjTKoGI1d7SzCZAPe4W3Oz6bI2D5rt5HOwHqHsprZ0Y_ezUpujHP8HrO6edKNRiYo9c7s2VgpeSIScOp4eY" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUZPUkJJRERFTiwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5saXN0KEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAyMCBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1mb3JiaWRkZW4sIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLkZPUkJJRERFTiwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5saXN0KEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAyMCBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAzLAogICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIgogfQp9Cg==" + } + }, + { + "ID": "fbc52a6c389a327a", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "952a88b10bc7f24e3efaf29f2c73f7f3/8460354462567557163;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Etag": [ + "CAQ=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbf6:4100,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=vk41W_2tGsjPhASU8bPwBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/169:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoYqEBv06NMUWqJX-IfAipkY0rFewQ3lrbit50ZwlZjC8Qzv4iONrJ3AXg-Z2M21-l_cHX-ulHyuu-UfiBgOtikGA1x8278ZBCpi_y7RKEkXUvYbzw" + ] + }, + "Body": "" + } + }, + { + "ID": "3fdd03e9d6b5b0ff", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "618eee53a680675ff106ebebfd501390/10075488395766426443;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "2993" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnb20:4079,/bns/yw/borg/yw/bns/blobstore2/bitpusher/337.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wE41W-6iB8O3hgTcw4ywBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/337.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/337:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoItumeX5mVK2g5hUVKi9jnqjQuOfK5qLC2T-1bZgXoTTftZ-l35-kZwFcyK_07f9-6a8jU6FlWNEtCdKb_1nReAHb-_tk0V4TKTx-BirA1IZL4wR8" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPU5PVF9GT1VORCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPW51bGwsIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1udWxsLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLnNjb3BlLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQuc2NvcGUsIG1lc3NhZ2U9Tm90IEZvdW5kLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBOb3QgRm91bmRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDA0LAogICJtZXNzYWdlIjogIk5vdCBGb3VuZCIKIH0KfQo=" + } + }, + { + "ID": "9698f9263205e4b9", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5b501e82cbd8b72dbad13dfa95937747/11618565834439061098;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13913" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrv6:4082,/bns/yw/borg/yw/bns/blobstore2/bitpusher/114.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wE41W5vTGcbuhAS64LzQCA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/114.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/114:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqeAtjrK7v6Lv5JE_OUYpUc7hKowqZYBmq5NHZGX_gESV3njaV6VjNVcvmqEfEAZIH7tgXp0U5petPFcvFpT6qIJE0SILIUt-FlaWw3r4UOKxswzbM" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6NzEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5kZWxldGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIKIH0KfQo=" + } + }, + { + "ID": "e5422788c8be7feb", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1ab519a49f9a0a4f727917a7638238aa/13233700867132780938;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "2993" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrav7:4461,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wE41W_W1JsytN6euonA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/282:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo75imgt_-9FrD6hotGjGDtb85VHmnTdefVXP5OwDBMIO5v91tGvk4TCrYwf0EgUYkL7L5ZufyIokBjpNkmF5_SHr7F0SJePMeII-vwd91QwtPRSec" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPU5PVF9GT1VORCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPW51bGwsIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1udWxsLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLnNjb3BlLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQuc2NvcGUsIG1lc3NhZ2U9Tm90IEZvdW5kLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBOb3QgRm91bmRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDA0LAogICJtZXNzYWdlIjogIk5vdCBGb3VuZCIKIH0KfQo=" + } + }, + { + "ID": "feea6ad84d4cf5b0", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "25b20b856b43cbfeedd80d89c60bcc54/14776778301510513833;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/acl/domain-google.com?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "14865" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:24 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrdk9:4305,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wE41W6WaLYy7N5zWgrgN" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/575:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urs4YM3yELVVvMO7Wmy_BBgZDfKKBfSDStGaKA9jV2MafZ-JB-pHESIf-X6KZXX7wDVO6ZOOx-jLArnucnjvrIF7Od0hEaIjzq-lQeg8SqDwC41dII" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9Rk9SQklEREVOLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9Zm9yYmlkZGVuLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5GT1JCSURERU4sIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDMsCiAgIm1lc3NhZ2UiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4iCiB9Cn0K" + } + }, + { + "ID": "c70627e9809b0d39", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9c9b2f0c26a4a31bf6f8f5c0f0ae8fb3/16391912234709383113;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "136" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:26 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vraz63:4430,/bns/yw/borg/yw/bns/blobstore2/bitpusher/377.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wE41W7uiOYTRhATJ_5PIDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/377.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/377:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoYAFn9P2csXRnCf5z5FLufOA1YS1VltYtdgIOg4ziozdf7FXbbglqoHkLvwZFE3kV2G6cnP1E3INz2kltvAcBBcoKNiRX0hGwtz2Dtz7csloKajjE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQVU9Igp9Cg==" + } + }, + { + "ID": "45303e826009ee9e", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "079bac55b6087e1c6334e2946f429ae1/18007046167908317928;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "136" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:26 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrks2:4084,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wk41W6vAFIObhATkprbADg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/265:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up9kF55KkQr_A-9ifJBKopm66zibXjB1I-duEFFWWt-0LRHFRhdpUisD0x7krQNNq0ukewv6zF1sMIAeW9qJOZmYLbfviS2f1ydMPzLD2SPAOI7sRg" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQVU9Igp9Cg==" + } + }, + { + "ID": "560fd000a2b184ef", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "021c4caa7b26a729d81b8a4e89da4c74/1103662107342896392;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13913" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:26 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vred4:4217,/bns/yw/borg/yw/bns/blobstore2/bitpusher/661.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wk41W5X-JYjUhATjwKuwCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/661.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/661:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqSXOHMb3IaL1_jGdR_appBY6ZZxuFeFi3WatR7XvXrPLrg9jfdbXRrPS7Sal8dEHgDIvReTj4PIZ5fMGijNdrohAf58jdAbbeKZ67AUqmz4miSvX4" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6MTYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci51cGRhdGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIKIH0KfQo=" + } + }, + { + "ID": "625d38ead6d57211", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f442e9741438e713bd59d48de96ff2d5/2718514565565120552;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "136" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:26 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrzz3:4408,/bns/yw/borg/yw/bns/blobstore2/bitpusher/572.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wk41W7a1MoazhQSrsq7YAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/572.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/572:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoY7-_uVK6tYh9TCIgeU67bpqN7bdmwjmOuI8yy99mGEjbMcwBr-FplvK8naMRe2XqPyb2iJtw0Ayb2916U2OzI_Rk-Jwml-4zpOnowrrgOaMbSR04" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogInJvbGUiOiAiUkVBREVSIiwKICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAiZXRhZyI6ICJDQVU9Igp9Cg==" + } + }, + { + "ID": "1e496dd00c93a4f1", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "370c0dfaffe30b171306f581760a9ee1/4261592004237755207;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14865" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vryy6:4071,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=wk41W4m4OcqfhQTz8pv4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/574:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Upe1-0ylTp5-b-21FiLpGzJXkdbA7pPVode5OI-QP9qBSIpMuS-B2Y49-GBSp2hbm680TftkZwMGZm3H-bJJXJxHQl7JWOJ0bmOezNqFsgPG_FiUr8" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9Rk9SQklEREVOLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9Zm9yYmlkZGVuLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5GT1JCSURERU4sIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDMsCiAgIm1lc3NhZ2UiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4iCiB9Cn0K" + } + }, + { + "ID": "35be47c44ea71acf", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c993ee3962ecb716dadda6a8e2cfbd2d/5876725937436624487;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "860" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbp125:4390,/bns/yw/borg/yw/bns/blobstore2/bitpusher/627.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=w041W5mgCcfQhASqvYj4Bg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/627.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/627:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoMkuKiXV2A3JneMemU3VGfe2Z2aFJbA0wXPB58oanYsBkR_WTFsbOCnekkBNLq8oJYSIdGXq7VfUrXaE3H_Re00pXJzhWFRqS53L1pCsHF5k2L6bo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogICAicm9sZSI6ICJSRUFERVIiLAogICAiZG9tYWluIjogImdvb2dsZS5jb20iLAogICAiZXRhZyI6ICJDQVU9IgogIH0KIF0KfQo=" + } + }, + { + "ID": "56b3d6c47d3badc2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "443dca65ba3e2a526ac660dde1ab32e3/7491860970130409862;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "860" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbh10:4400,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=w041W8HCGcaJN5i7i4AF" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/488:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpNy4J8Z9KM0J7nAAQX8OGNFwJAvmnmfyzj7vnau1oXsc12Lf5-nsllNKgRqddfbHE0c7SDHPJEf9MBXcws_IuXdzWAWKoy4Swpwli3IhRE6-s8wEk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogICAicm9sZSI6ICJSRUFERVIiLAogICAiZG9tYWluIjogImdvb2dsZS5jb20iLAogICAiZXRhZyI6ICJDQVU9IgogIH0KIF0KfQo=" + } + }, + { + "ID": "9ae5ab018d8a5c06", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "074624ea76a8396a4d1f549d1b9386a1/9034938404508077222;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13869" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrqq2:4452,/bns/yw/borg/yw/bns/blobstore2/bitpusher/252.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=w041W67nK4G0N6SfkrgK" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/252.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/252:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpPjgyWszAcvAkfEUUUtjAOOj9qzEYs1DbdF8XZrwsR031AXg0_yYja4e93HzEtzwll8EJf0zgg6UL_Zq66tDcfkmVlsIojmi0tcfEIAVi1zeFSTmk" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPVJFUVVJUkVELCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5saXN0KEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9YmFkUmVxdWVzdCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uUkVRVUlSRUQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAyMCBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAwLAogICJtZXNzYWdlIjogIkJ1Y2tldCBpcyByZXF1ZXN0ZXIgcGF5cyBidWNrZXQgYnV0IG5vIHVzZXIgcHJvamVjdCBwcm92aWRlZC4iCiB9Cn0K" + } + }, + { + "ID": "6be1de723d8dae9d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5291755353d633ec141665ae7c2172ca/10650072337707012037;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "860" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Etag": [ + "CAU=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:27 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrlj81:4352,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=w041W4SpOIeHhgSr9ppQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/172:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urp7Vvi8IYFk9QxcNFZh-5EbifsyH9byCBP0ocDt5XNfblDj_OdhtWMFAP6dW1zdHIAnKEUFoiXcqwXeHGpI8mROtyzDwPX7ICFExJGp5CIDNHmYlw" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAiZG9tYWluLWdvb2dsZS5jb20iLAogICAicm9sZSI6ICJSRUFERVIiLAogICAiZG9tYWluIjogImdvb2dsZS5jb20iLAogICAiZXRhZyI6ICJDQVU9IgogIH0KIF0KfQo=" + } + }, + { + "ID": "7b5835e133162bd4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a92c8d44626abfdd51f6f495bb68ae7f/12193149776379581157;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "14821" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:28 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:28 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrgg74:4491,/bns/yw/borg/yw/bns/blobstore2/bitpusher/409.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xE41W60J1eSFBLenntgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/409.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/409:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uou4YsMXdxLcwXImBtLUQPcOTKhH8SC2ih_n6GFxlxAPOg41ej7bp-Ps-HH08w-21FYwkQVxoPBgUHyOcqipzuyuX32gok2S54XXLHiRBwz8iSsFD4" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUZPUkJJRERFTiwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5saXN0KEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAyMCBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1mb3JiaWRkZW4sIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLkZPUkJJRERFTiwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5saXN0KEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAyMCBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAzLAogICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIgogfQp9Cg==" + } + }, + { + "ID": "6b5518a66e07fbd5", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0583fcf1fe600d9de75852efa9335e5f/13808284809073366276;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:29 GMT" + ], + "Etag": [ + "CAY=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrkq1:4159,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xE41W42HDobJN_DHnpAH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/159.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/159:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqpXsnw1uvYh8JILOsT73sspD_a_tq93eIFIrm_RCKRexmy69ndxdPniHz0K-MzoyRLumQ252JRn-FNGc0P4vKtXdaY4CA3cCwBLZvXE2pSQzFowlg" + ] + }, + "Body": "" + } + }, + { + "ID": "2b7e9579327d3e1f", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fe625f527185441e57093fb8858b24a9/15351362243451033636;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "2993" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrhl10:4450,/bns/yw/borg/yw/bns/blobstore2/bitpusher/174.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xU41W_mlMoXbhQTUy7PoAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/174.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/174:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up6w0N_CPJvDHevqv1pBk9ng7WlafDMMCtvXHrZC_9PKIPHibYLHe1GwAhWtcWbjxQf1ZGtrjUBtLs5pXYeM_rFOrxwMRHW-FJnwqjQKeHdJjwtNRo" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPU5PVF9GT1VORCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPW51bGwsIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1udWxsLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLnNjb3BlLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQuc2NvcGUsIG1lc3NhZ2U9Tm90IEZvdW5kLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBOb3QgRm91bmRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDA0LAogICJtZXNzYWdlIjogIk5vdCBGb3VuZCIKIH0KfQo=" + } + }, + { + "ID": "62719b79fee0b2ec", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "44be004a7d33f6710d8936c9fd017a56/16966214705968225091;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13913" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrkk24:4320,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xk41W96KB8KKhATFuJ7wBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/94:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq1PUBAtIT-UcX7FSxeDSNpQ9fJoAMKIxO3PLJMS9E_1iRjPLxIatNI8rsCazXst0XNrEBUEXy_ln5cskGhP6s5_TlxtM-OJ-G-aWbEXKeEmLaLv80" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkQnVja2V0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzEwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6NzEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5kZWxldGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIKIH0KfQo=" + } + }, + { + "ID": "bed6bc10d14d4b6b", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "19b7cdbb8c03fdeb2f821aaa1357b673/134886040434253411;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "2993" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrmi15:4409,/bns/yw/borg/yw/bns/blobstore2/bitpusher/209.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xk41W47qE4KlN9Wct9gI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/209.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/209:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrvWShO17uIEiXuLbY9t-uBnK6KOQWQzxlAm9ra6-zJjKxCIPJb31RX0LAu8AC8gXVYbXx60WatRGRjuAg_T6ouE8KxRzy5lJBTLDB5kxDaqQ8fr5A" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPU5PVF9GT1VORCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPW51bGwsIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1udWxsLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLnNjb3BlLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQuc2NvcGUsIG1lc3NhZ2U9Tm90IEZvdW5kLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBOb3QgRm91bmRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDA0LAogICJtZXNzYWdlIjogIk5vdCBGb3VuZCIKIH0KfQo=" + } + }, + { + "ID": "1749c49109cda048", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2280d35755e4c218eef1f6bed37810ab/1677964578601738626;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/defaultObjectAcl/domain-google.com?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "14865" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrlm67:4174,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xk41W5mFGszuhATb8oXwCw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/87:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpbcOsH6F_SbC5-yMNxJ_9Ok2mnR0zDik8wBUYUl1CiWEF6jMQAgJXB73_nFpLAodg7gpCClxeb8j0QNpzpxn5kaf0MGN8378hpZuw465B9Bn8OZ64" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9Rk9SQklEREVOLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZEJ1Y2tldChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjc4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMjAgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9Zm9yYmlkZGVuLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5GT1JCSURERU4sIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRCdWNrZXQoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDIwIG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDMsCiAgIm1lc3NhZ2UiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4iCiB9Cn0K" + } + }, + { + "ID": "72a8a7e60f02f4c2", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5e54114cf61649b47fdcaf658abb8cb1/3293098507505706146;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "495" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:30 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAU=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrow2:4362,/bns/yw/borg/yw/bns/blobstore2/bitpusher/432.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xk41W5ePJ8aLhgTpw42oBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/432.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/432:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoY3c-V5W-Sg8JoVyzr3YRO3NFtMOLtyHz3fJLQHU9S1e9ye-2Itd4xxVMLPdokom_34JAotUyNT9CSos-7S7IpY_SR21x_JDJMy8yjOVOIRfUZaPM" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvZG9tYWluLWdvb2dsZS5jb20iLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICJvYmplY3QiOiAiZm9vIiwKICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogImVudGl0eSI6ICJkb21haW4tZ29vZ2xlLmNvbSIsCiAicm9sZSI6ICJSRUFERVIiLAogImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCn0K" + } + }, + { + "ID": "0957eb356dd897a9", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c8ceecb85fdac30058fb5eac3eae2852/4836175946178340801;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "495" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAU=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrk127:4117,/bns/yw/borg/yw/bns/blobstore2/bitpusher/138.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=xk41W4LlOoq-hQT_maLIAg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/138.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/138:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqmnwPWzcY1YY34nS9N8lj8o9htxD3Pp9aRh9abNpZuj3IAEa3luqfWTFm1uhNa1tZ2I5zV47G-s5KvYo5kZas8l1CAkqVrQDWWgQdZUN6z3tnKXCo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvZG9tYWluLWdvb2dsZS5jb20iLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICJvYmplY3QiOiAiZm9vIiwKICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogImVudGl0eSI6ICJkb21haW4tZ29vZ2xlLmNvbSIsCiAicm9sZSI6ICJSRUFERVIiLAogImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCn0K" + } + }, + { + "ID": "98f4847e810f1194", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "89805ae057d244a9d38c40a575af190b/6451309879377210081;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13525" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrnt4:4443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=x041W_GSA9PFhgTc2byYBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/485:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqVinm8lFRfJFROlJN5HhxwXaAdJ4swaHXj0y39A51OfJU-CmQws4OV2RmAEEpdyqZzmrtuo9qdsy2CgLmYWVm7LY0gSReK6d0jH--rFcPXQj6zhQQ" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZE9iamVjdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMzOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6MTYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci51cGRhdGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "c3bab13c287f1114", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f9495f7b59b27d93123f9b0711c8e1e4/8066444912070995200;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "495" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAU=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrlg23:4041,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=x041W_bIFIrvhAT60qWICA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/4:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqFcQTdv7OvEMZi6rGCdwB5hq9PhOQtIptiYa2bE8oUErjmDJPZzU-rKfRDNLmYPAmyU51LPVHo45Kje_mngyc0BkMeVAwEVG0-RZ7ZFBQUOssJWKY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvZG9tYWluLWdvb2dsZS5jb20iLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICJvYmplY3QiOiAiZm9vIiwKICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogImVudGl0eSI6ICJkb21haW4tZ29vZ2xlLmNvbSIsCiAicm9sZSI6ICJSRUFERVIiLAogImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCn0K" + } + }, + { + "ID": "2f140ccb6f61151d", + "Request": { + "Method": "PUT", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "107" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "42c83a43f5fa083b78fdb47dd0d5f66e/9609522346448662560;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLCJlbnRpdHkiOiJkb21haW4tZ29vZ2xlLmNvbSIsInJvbGUiOiJSRUFERVIifQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14477" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrw187:4237,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=x041W8CZG8GVhgSpl5CYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/577:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urz4vBseBymaCqTrhu8H6cGro42YRef42eJtcuu3FGuo5USl_61J__fIkbLskkY7_S1YLCMftbwr0box6zdsbuIDjSXDgc9TWKuv-PeQv-oV9J2H7Q" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5VcGRhdGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVBY2xzLmphdmE6MTYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci51cGRhdGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLlVwZGF0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUFjbHMuamF2YToxNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLnVwZGF0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZE9iamVjdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMzOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuVXBkYXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQWNscy5qYXZhOjE2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IudXBkYXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "0da7091949c63388", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4dc88f987047e04c32d69f9d521efd00/11224656279647597375;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3126" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAU=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmr18:4187,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=x041W8i-JsT2N87Rh_gH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/102:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urh3ZwDPOygQtjdI4qRMibFYA49VaMwoH3c9sO5GsTKJBtVDm7JJfZbM6eXk0r2OWB3JYg7OPdmPj2-hunAWCyhzNzN0HvuhxGRbhg2viIJZxBct88" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL2RvbWFpbi1nb29nbGUuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFVPSIKICB9CiBdCn0K" + } + }, + { + "ID": "b321faf31b2faf4c", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9dbf90fca1d0ad6c26dc22359284f0d3/12767452247638423135;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3126" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAU=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vru67:4406,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=x041W7PtK8KVN86YjeAJ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/211:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo7tDTxkV-kpNq3GEGahM2DGZ3XzH2sYJzfrwMykJL6MvhAc-g1xB1kP44H5DNYpJDa6PJtXRtyta48rJKLKuroVkd10-tV3-XI7P4vTT8DDBa1Lbc" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL2RvbWFpbi1nb29nbGUuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFVPSIKICB9CiBdCn0K" + } + }, + { + "ID": "a37e9848a20ca25f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8abe88cfbcababb89507265c626ce47d/14382587280332208510;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13481" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:31 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrt64:4383,/bns/yw/borg/yw/bns/blobstore2/bitpusher/410.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=x041W5rJMM2_N9XTiegC" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/410.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/410:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur5zXWAguS5SoACWWQX7hqJfB3CiqDKPVjTK0mhHsd5mnjAVKyjPaw6bgq_YfLWr9NvflZ0_7Osc93_DOcvosyOpGRawq4I7lN1UbftFNn3gaBoO6Q" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjg1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5saXN0KEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWJhZFJlcXVlc3QsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDAsCiAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIKIH0KfQo=" + } + }, + { + "ID": "1a88a0a08229a1c9", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d27ff18af2e24d008a3673ddcb9986f6/15925664714709875870;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "3126" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAU=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrnr3:4315,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yE41W6rSAcuGhASj3anICg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/615:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpXUB4RTGEHFQ2R-D70pybsb2mqhsxEyHrzcVZ0A_Tbc501yGq9dBk1pnQhlWOqETPhq4GpfRZ23wE1BNxbf4j0henI3QpAYOFUll5OGAy_ZGNrlvE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9scyIsCiAiaXRlbXMiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNOYkpuOE9pOTlzQ0VBVT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMTYwNTA5MDIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL3VzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgIm9iamVjdCI6ICJmb28iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjE2MDUwOTAyIiwKICAgImVudGl0eSI6ICJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTmJKbjhPaTk5c0NFQVU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2Zvby8xNTMwMjIwMjE2MDUwOTAyL2RvbWFpbi1nb29nbGUuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9mb28vYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMTYwNTA5MDIiLAogICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgImRvbWFpbiI6ICJnb29nbGUuY29tIiwKICAgImV0YWciOiAiQ05iSm44T2k5OXNDRUFVPSIKICB9CiBdCn0K" + } + }, + { + "ID": "b1251cf6b2d85f2d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0b6b58e009a9ac40dedd0ae2cd6732c9/17540798647908810685;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "14433" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrdh7:4070,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yE41W-_DBoyihwS80rTIBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/623:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqfKs9-M6MJ731w2jsn9HqFRwx6NCtvhcZbrz86rjL1W0W_OWYEhNCFUh1Pg1k4BjDqzUQ_yNuz2mqr5gHbcSOHPkkLn7Ys3hGCeQg-7Y2LIa86mVA" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9Rk9SQklEREVOLCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YTo4NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkxpc3RBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0QWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IubGlzdChBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9Zm9yYmlkZGVuLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5GT1JCSURERU4sIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZE9iamVjdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMzOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZE9iamVjdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMzOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuTGlzdEFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RBY2xzLmphdmE6ODUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5MaXN0QWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdEFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmxpc3QoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDMsCiAgIm1lc3NhZ2UiOiAiaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4iCiB9Cn0K" + } + }, + { + "ID": "5ac642be380c2150", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "69a90d83e9c250d327b520dd4af09178/709469982374839005;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Etag": [ + "CNbJn8Oi99sCEAY=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220517000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrar8:4264,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yE41W9-kFNWyhgSNxaeACw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/444:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpWTsX9f0N2y2eDw8SzYrmsvLkhTv0ihlB12ypt_ZsHWYz8pFlwEZtjtxJTG7waehWGjroxCwLqZkTLTmVTKN8l4NqXPknWKJQYtF1emY1pX5vbbdw" + ] + }, + "Body": "" + } + }, + { + "ID": "002f165e552d4076", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5e8fd5c515b2abbfc286a05fb2c4e03b/2252548520542324220;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "2993" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnx21:4420,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yE41W6m3JsLdhAT6tKGwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/625:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrONF3UrsFmA1l_RhmCckA6UA_NE6th4pnWXiZNclp6VHAlviMYmUaI7Iu9sl25pVkiNFGUaKfPzrmRe-D4n6W9Q9A7HBM-qGCzQR-JzXBy9xBl-Ng" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPU5PVF9GT1VORCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPW51bGwsIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1udWxsLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLnNjb3BlLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQuc2NvcGUsIG1lc3NhZ2U9Tm90IEZvdW5kLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBOb3QgRm91bmRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDA0LAogICJtZXNzYWdlIjogIk5vdCBGb3VuZCIKIH0KfQo=" + } + }, + { + "ID": "69c314ae145b1dd4", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "bcbeea5c1333ca3f4b4e29818d3f5589/3867682449446291484;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13525" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrjl71:4118,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yE41W8L9LMHvhAS3rbPYCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/101:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqWTOU2GbKcjzJxx6tAL3bmNe2mhEaR-zDvt7oDdNOXQwAMB_JtdTz_vKYz1_n8eHOf6egiarsoXJGhVEoiigbbXJ-Zs4si3DvZ7KfKWW2MRgYC3rs" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZE9iamVjdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMzOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6NzEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5kZWxldGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "a2ee16fd59be66da", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1bc23f3cd620b1290c566a7eee7858f0/5410759888118926139;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "2993" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:33 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:33 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrv63:4258,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yE41W6qNOML7hQTg1oPgDg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/306:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UptXr5UrqFieOAtpqaxjlHNagcyTOYLaybSTPfGRxT6kkZ1HH97rmy0J5eczKnvTdug2m6_IeLOCpagogzNF_Mf1Sqox0hxK0Au-usuKgh51YHxB1c" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPU5PVF9GT1VORCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPW51bGwsIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1ub3RGb3VuZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uTk9UX0ZPVU5ELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1udWxsLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLnNjb3BlLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1lbnRpdHkucmVzb3VyY2VfaWQuc2NvcGUsIG1lc3NhZ2U9Tm90IEZvdW5kLCByZWFzb249bm90Rm91bmQsIHJwY0NvZGU9NDA0fSBOb3QgRm91bmRcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDA0LAogICJtZXNzYWdlIjogIk5vdCBGb3VuZCIKIH0KfQo=" + } + }, + { + "ID": "e847ef7ec0d168dd", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "aea0b98a14e81b8d34485235c0c3a2db/7025893821317795419;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/acl/domain-google.com?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "14477" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:33 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:33 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220518000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrnm22:4040,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yU41W6KDAsW6N4SVrcgJ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/271:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATo_ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uoi_2taGcCTC655cDHGA2Y9KL78TyNz4Pvnd-6wVUgzyXgqITOy09UtwSN7XhHLCSig59xamIr7zmREqYFSX9KR0BUfekPuvxT7pb4Yhwng-Ej-IbQ" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5sb2FkT2JqZWN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6MzM5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIuZ2V0QWNsUmVzb3VyY2VGb3JSZXF1ZXN0KEFjY2Vzc0NvbnRyb2xzSGVscGVyLmphdmE6NjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6NzEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5EZWxldGVBY2xzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVBY2xzLmphdmE6MjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5BY2Nlc3NDb250cm9sc0RlbGVnYXRvci5kZWxldGUoQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuamF2YToxMDkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmxvYWRPYmplY3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTozMzkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYWNscy5BY2Nlc3NDb250cm9sc0hlbHBlci5nZXRBY2xSZXNvdXJjZUZvclJlcXVlc3QoQWNjZXNzQ29udHJvbHNIZWxwZXIuamF2YTo2OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YTo3MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkRlbGV0ZUFjbHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZUFjbHMuamF2YToyMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmRlbGV0ZShBY2Nlc3NDb250cm9sc0RlbGVnYXRvci5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE5IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuQWNjZXNzQ29udHJvbHNIZWxwZXIubG9hZE9iamVjdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjMzOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5hY2xzLkFjY2Vzc0NvbnRyb2xzSGVscGVyLmdldEFjbFJlc291cmNlRm9yUmVxdWVzdChBY2Nlc3NDb250cm9sc0hlbHBlci5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjcxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmFjbHMuRGVsZXRlQWNscy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlQWNscy5qYXZhOjIwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQWNjZXNzQ29udHJvbHNEZWxlZ2F0b3IuZGVsZXRlKEFjY2Vzc0NvbnRyb2xzRGVsZWdhdG9yLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "ac4b278f9dacff35", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1a2dd758404739caf673af3bb0949e09/8640747383329837434;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3809" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:33 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220533000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbx62:4007,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yU41W-WNDpLuhATIx7SoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/25:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up8Ugw3JLfdovqiwsiZqAa5Hqo9f58WngeftYgSEhsCPo57J8YCkAJcX2HDGASDMlMHMRWolubu6ZeouTft2M3QFa9yWl5VWsZTKDHj2GzDme3qZ7Y" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiNSIsCiAib2JqZWN0U2l6ZSI6ICI1IiwKICJkb25lIjogdHJ1ZSwKICJyZXNvdXJjZSI6IHsKICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9jb3B5LzE1MzAyMjAyMzM2MjkzMzkiLAogICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29weSIsCiAgIm5hbWUiOiAiY29weSIsCiAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzM2MjkzMzkiLAogICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzMuNjI4WiIsCiAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozMy42MjhaIiwKICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozMy42MjhaIiwKICAic2l6ZSI6ICI1IiwKICAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHk/Z2VuZXJhdGlvbj0xNTMwMjIwMjMzNjI5MzM5JmFsdD1tZWRpYSIsCiAgImNvbnRlbnRMYW5ndWFnZSI6ICJlbiIsCiAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICJhY2wiOiBbCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2NvcHkvMTUzMDIyMDIzMzYyOTMzOS9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgICJvYmplY3QiOiAiY29weSIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjMzNjI5MzM5IiwKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJvd25lcnMiCiAgICB9LAogICAgImV0YWciOiAiQ0p1OTBNdWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvY29weS8xNTMwMjIwMjMzNjI5MzM5L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzMzYyOTMzOSIsCiAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJPV05FUiIsCiAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICB9LAogICAgImV0YWciOiAiQ0p1OTBNdWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvY29weS8xNTMwMjIwMjMzNjI5MzM5L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzMzYyOTMzOSIsCiAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJSRUFERVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgfSwKICAgICJldGFnIjogIkNKdTkwTXVpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2NvcHkvMTUzMDIyMDIzMzYyOTMzOS91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29weS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzMzYyOTMzOSIsCiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJldGFnIjogIkNKdTkwTXVpOTlzQ0VBRT0iCiAgIH0KICBdLAogICJvd25lciI6IHsKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogIH0sCiAgImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAgImV0YWciOiAiQ0p1OTBNdWk5OXNDRUFFPSIKIH0KfQo=" + } + }, + { + "ID": "89250e5caad1818e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a78dd3f6a38d19abdfea270a42c9c113/10183824817707504794;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3809" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:34 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220533000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnv1:4379,/bns/yw/borg/yw/bns/blobstore2/bitpusher/541.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yU41W8bLLcmChQTn2LjwAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/541.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/541:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urag6p_vCyG1qVHWNKLI05A_JgL9zhVsDG7IX5pfn8V0MByBoAeYUGklwkZ3gEhn8wNwGpgyEV5QFJj1o2oBcKBVm_ZmXfoXH0FdSMJS0fEFOHC-j4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiNSIsCiAib2JqZWN0U2l6ZSI6ICI1IiwKICJkb25lIjogdHJ1ZSwKICJyZXNvdXJjZSI6IHsKICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9jb3B5LzE1MzAyMjAyMzQxMzg5ODMiLAogICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29weSIsCiAgIm5hbWUiOiAiY29weSIsCiAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzQxMzg5ODMiLAogICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzQuMTM2WiIsCiAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNC4xMzZaIiwKICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNC4xMzZaIiwKICAic2l6ZSI6ICI1IiwKICAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHk/Z2VuZXJhdGlvbj0xNTMwMjIwMjM0MTM4OTgzJmFsdD1tZWRpYSIsCiAgImNvbnRlbnRMYW5ndWFnZSI6ICJlbiIsCiAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICJhY2wiOiBbCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2NvcHkvMTUzMDIyMDIzNDEzODk4My9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgICJvYmplY3QiOiAiY29weSIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjM0MTM4OTgzIiwKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJvd25lcnMiCiAgICB9LAogICAgImV0YWciOiAiQ09mSzc4dWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvY29weS8xNTMwMjIwMjM0MTM4OTgzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNDEzODk4MyIsCiAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJPV05FUiIsCiAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICB9LAogICAgImV0YWciOiAiQ09mSzc4dWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvY29weS8xNTMwMjIwMjM0MTM4OTgzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNDEzODk4MyIsCiAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJSRUFERVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgfSwKICAgICJldGFnIjogIkNPZks3OHVpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2NvcHkvMTUzMDIyMDIzNDEzODk4My91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29weS9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNDEzODk4MyIsCiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJldGFnIjogIkNPZks3OHVpOTlzQ0VBRT0iCiAgIH0KICBdLAogICJvd25lciI6IHsKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogIH0sCiAgImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAgImV0YWciOiAiQ09mSzc4dWk5OXNDRUFFPSIKIH0KfQo=" + } + }, + { + "ID": "b6206031b9093a0c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f1651f9bd821d441c6a6bcc64b333360/11798958750906439609;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13465" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:34 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220534000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrmm2:4409,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yk41W-aHEMaNhAT3nLboBA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/461:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpCChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UozftxjltFoWyuSg5NaY06tPw1dobK-wKSbgUilnNcesPMhYIp6LQE3Dsg759OBvEdHf2AuisIyVG7U8ArBaFEFmc2ZoVhbifFHRQadOe8GinEicvo" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLkZyb250ZW5kUmV3cml0ZUFjdGlvbnMuZ2V0U291cmNlT2JqZWN0KEZyb250ZW5kUmV3cml0ZUFjdGlvbnMuamF2YToxMDkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3QoUmV3cml0ZXIuamF2YTozMjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LnJld3JpdGVJbkZyb250ZW5kKFJld3JpdGVPYmplY3QuamF2YToyMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6MjA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjUyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdC4uLiAxNCBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLkZyb250ZW5kUmV3cml0ZUFjdGlvbnMuZ2V0U291cmNlT2JqZWN0KEZyb250ZW5kUmV3cml0ZUFjdGlvbnMuamF2YToxMDkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3QoUmV3cml0ZXIuamF2YTozMjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LnJld3JpdGVJbkZyb250ZW5kKFJld3JpdGVPYmplY3QuamF2YToyMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6MjA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjUyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdC4uLiAxNCBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMxMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLnJld3JpdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyNClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuRnJvbnRlbmRSZXdyaXRlQWN0aW9ucy5nZXRTb3VyY2VPYmplY3QoRnJvbnRlbmRSZXdyaXRlQWN0aW9ucy5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUmV3cml0ZXIucmV3cml0ZU9iamVjdChSZXdyaXRlci5qYXZhOjMyOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QucmV3cml0ZUluRnJvbnRlbmQoUmV3cml0ZU9iamVjdC5qYXZhOjIyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YToyMDYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6NTIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0Li4uIDE0IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzExKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IucmV3cml0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5Gcm9udGVuZFJld3JpdGVBY3Rpb25zLmdldFNvdXJjZU9iamVjdChGcm9udGVuZFJld3JpdGVBY3Rpb25zLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0KFJld3JpdGVyLmphdmE6MzI4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5yZXdyaXRlSW5Gcm9udGVuZChSZXdyaXRlT2JqZWN0LmphdmE6MjI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjIwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YTo1Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHQuLi4gMTQgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "ca74f64fd8a64886", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c05f05c656a1b18af417a0fae07befce/13342036189579008729;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3764" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:34 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220534000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrqi23:4269,/bns/yw/borg/yw/bns/blobstore2/bitpusher/456.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yk41W_b-GcSGhAS92oPIAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/456.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/456:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpCChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrUoZe9KqVQdKaP-g1668bTKPxW2PfRGRRFpChF7xUSZvoxYkYDpZPpAW5D-T-znFmBr2oJAe8uVJRUA91fBWgupW0wyLMCFzGtIR4gr089Vvdmhx8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiNSIsCiAib2JqZWN0U2l6ZSI6ICI1IiwKICJkb25lIjogdHJ1ZSwKICJyZXNvdXJjZSI6IHsKICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9jb3B5LzE1MzAyMjAyMzQ1NDUxNzAiLAogICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29weSIsCiAgIm5hbWUiOiAiY29weSIsCiAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzQ1NDUxNzAiLAogICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzQuNTQ0WiIsCiAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNC41NDRaIiwKICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNC41NDRaIiwKICAic2l6ZSI6ICI1IiwKICAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHk/Z2VuZXJhdGlvbj0xNTMwMjIwMjM0NTQ1MTcwJmFsdD1tZWRpYSIsCiAgImNvbnRlbnRMYW5ndWFnZSI6ICJlbiIsCiAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICJhY2wiOiBbCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2NvcHkvMTUzMDIyMDIzNDU0NTE3MC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyIiwKICAgICJvYmplY3QiOiAiY29weSIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjM0NTQ1MTcwIiwKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJvd25lcnMiCiAgICB9LAogICAgImV0YWciOiAiQ0pLd2lNeWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvY29weS8xNTMwMjIwMjM0NTQ1MTcwL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNDU0NTE3MCIsCiAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJPV05FUiIsCiAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICB9LAogICAgImV0YWciOiAiQ0pLd2lNeWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvY29weS8xNTMwMjIwMjM0NTQ1MTcwL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2NvcHkvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNDU0NTE3MCIsCiAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJSRUFERVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgfSwKICAgICJldGFnIjogIkNKS3dpTXlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL2NvcHkvMTUzMDIyMDIzNDU0NTE3MC91c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29weS9hY2wvdXNlci1pbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICAib2JqZWN0IjogImNvcHkiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNDU0NTE3MCIsCiAgICAiZW50aXR5IjogInVzZXItaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgImVtYWlsIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJldGFnIjogIkNKS3dpTXlpOTlzQ0VBRT0iCiAgIH0KICBdLAogICJvd25lciI6IHsKICAgImVudGl0eSI6ICJ1c2VyLWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIgogIH0sCiAgImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAgImV0YWciOiAiQ0pLd2lNeWk5OXNDRUFFPSIKIH0KfQo=" + } + }, + { + "ID": "365b63c8d9880a1e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "be09dc9b1419febf621bd79059f1b3e4/14957171222272794104;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo/rewriteTo/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026projection=full\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "14417" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:35 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220534000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrbj65:4346,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=yk41W5TnL8PdhQTRkIzQBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/178:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpCChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UorBgrs1e8_luDHyRcoSgZKvuyOkUFJqdVH0NCFBOVfZGNAJnH-zGadT9y5NwWC-zJk1RgJebuCVCtZZShGX5PHPxAsD3_N5n0ywiXYEv-7P_Gy9Zs" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzExKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IucmV3cml0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLkZyb250ZW5kUmV3cml0ZUFjdGlvbnMuZ2V0U291cmNlT2JqZWN0KEZyb250ZW5kUmV3cml0ZUFjdGlvbnMuamF2YToxMDkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLnJld3JpdGVyLlJld3JpdGVyLnJld3JpdGVPYmplY3QoUmV3cml0ZXIuamF2YTozMjgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LnJld3JpdGVJbkZyb250ZW5kKFJld3JpdGVPYmplY3QuamF2YToyMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6MjA2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjUyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdC4uLiAxNCBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuRnJvbnRlbmRSZXdyaXRlQWN0aW9ucy5nZXRTb3VyY2VPYmplY3QoRnJvbnRlbmRSZXdyaXRlQWN0aW9ucy5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUmV3cml0ZXIucmV3cml0ZU9iamVjdChSZXdyaXRlci5qYXZhOjMyOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QucmV3cml0ZUluRnJvbnRlbmQoUmV3cml0ZU9iamVjdC5qYXZhOjIyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YToyMDYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6NTIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0Li4uIDE0IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMTEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5yZXdyaXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuRnJvbnRlbmRSZXdyaXRlQWN0aW9ucy5nZXRTb3VyY2VPYmplY3QoRnJvbnRlbmRSZXdyaXRlQWN0aW9ucy5qYXZhOjEwOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24ucmV3cml0ZXIuUmV3cml0ZXIucmV3cml0ZU9iamVjdChSZXdyaXRlci5qYXZhOjMyOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QucmV3cml0ZUluRnJvbnRlbmQoUmV3cml0ZU9iamVjdC5qYXZhOjIyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YToyMDYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5SZXdyaXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChSZXdyaXRlT2JqZWN0LmphdmE6NTIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0Li4uIDE0IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMxMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLnJld3JpdGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyNClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5Gcm9udGVuZFJld3JpdGVBY3Rpb25zLmdldFNvdXJjZU9iamVjdChGcm9udGVuZFJld3JpdGVBY3Rpb25zLmphdmE6MTA5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5yZXdyaXRlci5SZXdyaXRlci5yZXdyaXRlT2JqZWN0KFJld3JpdGVyLmphdmE6MzI4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5yZXdyaXRlSW5Gcm9udGVuZChSZXdyaXRlT2JqZWN0LmphdmE6MjI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuUmV3cml0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoUmV3cml0ZU9iamVjdC5qYXZhOjIwNilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLlJld3JpdGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFJld3JpdGVPYmplY3QuamF2YTo1Milcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHQuLi4gMTQgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "465270522ee5c580", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "127" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7330501283e8092f2ba79bb3d8ffd047/16572305151176761368;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImZvbyJ9LHsibmFtZSI6ImNvcHkifV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "792" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:35 GMT" + ], + "Etag": [ + "CMi4ucyi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220533000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrae2:4360,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=y041W_yTAobRhQSY0oKoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/197:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrfMxz_RCoMQ7S-kGhUs8-4CrBIwi2lTuJ67XKbCpCdJNcB8PDgV_8Akknij5sc6iULpMnNXF_zjVNPdnA5k_ncGEIxkhE0PVk1E_huxBmBnvI4Wtg" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9jb21wb3NlLzE1MzAyMjAyMzUzNDkwNjQiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9jb21wb3NlIiwKICJuYW1lIjogImNvbXBvc2UiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNTM0OTA2NCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNS4zNDhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzUuMzQ4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM1LjM0OFoiLAogInNpemUiOiAiMTAiLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29tcG9zZT9nZW5lcmF0aW9uPTE1MzAyMjAyMzUzNDkwNjQmYWx0PW1lZGlhIiwKICJjcmMzMmMiOiAiL1JDT2dnPT0iLAogImNvbXBvbmVudENvdW50IjogMiwKICJldGFnIjogIkNNaTR1Y3lpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "94059bcaf9c79866", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "127" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "251a80ef626fb5d5921e5bcee7033b80/18115382589849396023;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImZvbyJ9LHsibmFtZSI6ImNvcHkifV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "792" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:35 GMT" + ], + "Etag": [ + "CNuC2Myi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220533000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqq22:4024,/bns/yw/borg/yw/bns/blobstore2/bitpusher/450.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=y041W5jAHMSIN-P5taAD" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/450.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/450:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoQBr05iXRkcG0vfErkas0-U0dS7lPyRoIrHWEetI5OWSw6RbWsKftZz41eDxhuzGJ6P2B9bgOcW3hzLUY1mNhEzu69gcxrBs1VZoOH2cqaD-40CZQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9jb21wb3NlLzE1MzAyMjAyMzU4NTAwNzUiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9jb21wb3NlIiwKICJuYW1lIjogImNvbXBvc2UiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNTg1MDA3NSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNS44NDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzUuODQ5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM1Ljg0OVoiLAogInNpemUiOiAiMTAiLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29tcG9zZT9nZW5lcmF0aW9uPTE1MzAyMjAyMzU4NTAwNzUmYWx0PW1lZGlhIiwKICJjcmMzMmMiOiAiL1JDT2dnPT0iLAogImNvbXBvbmVudENvdW50IjogMiwKICJldGFnIjogIkNOdUMyTXlpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "ef1878d77aae18e2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "127" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "201ff419961c9f4391a9220a611b7a8a/1284053924315424343;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImZvbyJ9LHsibmFtZSI6ImNvcHkifV19Cg==" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "12553" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220534000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrks2:4084,/bns/yw/borg/yw/bns/blobstore2/bitpusher/355.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=y041W4CXO4uDhASy3KG4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/355.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/355:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpCChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqZpEkWwotz5OBvqhZOCW9rgQ-PqDxL5l9TrJZ4j-1_lAIXg3AXFsAj4oQtqkP1NPWykdQIZkdwhQ4PjJYbPMIpaT-Mv4CpuPShK_vV8fg3zdJ7FWQ" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuQ29tcG9zZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoQ29tcG9zZU9iamVjdC5qYXZhOjIwMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YTo0Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuY29tcG9zZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6MjAwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuQ29tcG9zZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoQ29tcG9zZU9iamVjdC5qYXZhOjQ3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5jb21wb3NlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "85d81132d4d39647", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "127" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4801b91349315d35c9089e135413e1d8/2827132462482909558;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImZvbyJ9LHsibmFtZSI6ImNvcHkifV19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "792" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Etag": [ + "CND/78yi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220534000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrkx5:4437,/bns/yw/borg/yw/bns/blobstore2/bitpusher/357.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zE41W_L5B8rIhgS6grugDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/357.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/357:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpCChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoAmuSd3zjt7Ha_MUONBGHNBdxCRGueZLLZbXOIbmYGineICy0eSq8j5eX8rY56fvFaZwtNj9kmA7gJDELmzXEYxHpgFmWiSaSiCZR3WABRXlD4OAg" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9jb21wb3NlLzE1MzAyMjAyMzYyNDI4OTYiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvby9jb21wb3NlIiwKICJuYW1lIjogImNvbXBvc2UiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNjI0Mjg5NiIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNi4yNDJaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzYuMjQyWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM2LjI0MloiLAogInNpemUiOiAiMTAiLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vY29tcG9zZT9nZW5lcmF0aW9uPTE1MzAyMjAyMzYyNDI4OTYmYWx0PW1lZGlhIiwKICJjcmMzMmMiOiAiL1JDT2dnPT0iLAogImNvbXBvbmVudENvdW50IjogMiwKICJldGFnIjogIkNORC83OHlpOTlzQ0VBRT0iCn0K" + } + }, + { + "ID": "dc740ade03352fae", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "127" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4165f243395521e58cb71687b04df475/4441984920705133718;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose/compose?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJkZXN0aW5hdGlvbiI6eyJidWNrZXQiOiJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIifSwic291cmNlT2JqZWN0cyI6W3sibmFtZSI6ImZvbyJ9LHsibmFtZSI6ImNvcHkifV19Cg==" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13505" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:36 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220534000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrav7:4461,/bns/yw/borg/yw/bns/blobstore2/bitpusher/502.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zE41W5WZHYf0N5mFp7gP" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/502.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/502:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpCChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqaohdLyAm4kT4LlcEJq0mBHlZMqRuabTaDYlovKgp7ZFGIYd3e0WC9jIC_ypHR3qksvE5MpLYCvGWMlj2zWpjKo1AC7zcV7c84T42CTntrwQPCeTY" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6MjAwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuQ29tcG9zZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoQ29tcG9zZU9iamVjdC5qYXZhOjQ3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5jb21wb3NlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YToyMDApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5Db21wb3NlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChDb21wb3NlT2JqZWN0LmphdmE6NDcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmNvbXBvc2UoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjEyOSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuQ29tcG9zZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoQ29tcG9zZU9iamVjdC5qYXZhOjIwMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkNvbXBvc2VPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKENvbXBvc2VPYmplY3QuamF2YTo0Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuY29tcG9zZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTI5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "026167780e8689fa", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=28995b27690fd982f961e9a7a573f73f7ce2f22e68534ed4b48a92db240c" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "40b7c6eb2ba09149588c3d209a1f99dc/5249692072906403238;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0yODk5NWIyNzY5MGZkOTgyZjk2MWU5YTdhNTczZjczZjdjZTJmMjJlNjg1MzRlZDRiNDhhOTJkYjI0MGMNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsIm5hbWUiOiJmb28ifQoNCi0tMjg5OTViMjc2OTBmZDk4MmY5NjFlOWE3YTU3M2Y3M2Y3Y2UyZjIyZTY4NTM0ZWQ0YjQ4YTkyZGIyNDBjDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KaGVsbG8NCi0tMjg5OTViMjc2OTBmZDk4MmY5NjFlOWE3YTU3M2Y3M2Y3Y2UyZjIyZTY4NTM0ZWQ0YjQ4YTkyZGIyNDBjLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3544" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:37 GMT" + ], + "Etag": [ + "CMW3oc2i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220536000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbq65:4491,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zE41W4_ALI7ShASVqYPICg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/111:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpPPJgdWUZb5UTTVCL3wqQcx8AsAVSJmcCdyEjQB__DKlhKM4Cr3McDYD0olf1UWx-x1fNmqjs07bApiBXgHXw61QDv_u244h_BoGBZRLUMJf9tXTs" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzNzA1Mjg2OSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNzA1Mjg2OSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNy4wNTJaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzcuMDUyWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM3LjA1MloiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIzNzA1Mjg2OSZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzcwNTI4NjkvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNzA1Mjg2OSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ01XM29jMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzNzA1Mjg2OS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzcwNTI4NjkiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ01XM29jMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzNzA1Mjg2OS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzcwNTI4NjkiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNNVzNvYzJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzcwNTI4NjkvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzcwNTI4NjkiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNNVzNvYzJpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDTVczb2MyaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "bc6392ccecc93e3b", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c34f4f87d73e13464b179e952ad69517/5985062359377768373;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:37 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220536000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrls9:4389,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zU41W9q9CsLxhATkzq7YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/221:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpSepvdmJ1LYIg5GyhYNH88jPho5IJXLl6t89wHNn2BU_uvtAonACCltjMqZPiV8z58BHiqQk7GP6krZJcREOgVIQpMgQu__LgMqJMRBiNjOre1peY" + ] + }, + "Body": "" + } + }, + { + "ID": "0845018c54a970e4", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=1b87bca80949ce78926ed126d633f3515e72f7965135faa49447acc18001" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2a015f40a5bf34068f2355f3f7484db8/6792770611073888453;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xYjg3YmNhODA5NDljZTc4OTI2ZWQxMjZkNjMzZjM1MTVlNzJmNzk2NTEzNWZhYTQ5NDQ3YWNjMTgwMDENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsIm5hbWUiOiJmb28ifQoNCi0tMWI4N2JjYTgwOTQ5Y2U3ODkyNmVkMTI2ZDYzM2YzNTE1ZTcyZjc5NjUxMzVmYWE0OTQ0N2FjYzE4MDAxDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KaGVsbG8NCi0tMWI4N2JjYTgwOTQ5Y2U3ODkyNmVkMTI2ZDYzM2YzNTE1ZTcyZjc5NjUxMzVmYWE0OTQ0N2FjYzE4MDAxLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3544" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:37 GMT" + ], + "Etag": [ + "CInQ0M2i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220536000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmv23:4472,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zU41W9nBIIaIN5_KqfgH" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/157:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpNX0dD7zhg6HA9uh0PGTufe9EoRW1HxPo_HJOinkT8gqAIjqNKFqswEMI92l5iaePioK-BFenArQwrhPo75LBJjr6uiNKvd1xDzcWrWA8zh1Hy5BU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzNzgyNjA1NyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNzgyNjA1NyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozNy44MjVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzcuODI1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM3LjgyNVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIzNzgyNjA1NyZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzc4MjYwNTcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzNzgyNjA1NyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0luUTBNMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzNzgyNjA1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzc4MjYwNTciLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0luUTBNMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzNzgyNjA1Ny9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzc4MjYwNTciLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNJblEwTTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzc4MjYwNTcvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzc4MjYwNTciLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNJblEwTTJpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDSW5RME0yaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "dcb772567c2b4831", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d994ee0fdddfcb6315228a4ace29c710/7600196292576637653;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:38 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbv13:4457,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zU41W9TwOcPrhAT61qjACQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/635:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpAtemcgY9wZzSJtdSShEnXPTRdw1nj-UvXsWfFCTHq_jgB_GngioPrI7SUs1QVp4rUIcTcDSxAuRGRYt9CanciFC5qwVodqfwqpKIszbrcja0y_DQ" + ] + }, + "Body": "" + } + }, + { + "ID": "ff711c00b4b61602", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=71e658c7978d5cfe2ee8761febdbe343541afb809d22e1bf499cccb1e06a" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b207047b26d9bbd11eb7bfbce0d098ba/8407904544272757733;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS03MWU2NThjNzk3OGQ1Y2ZlMmVlODc2MWZlYmRiZTM0MzU0MWFmYjgwOWQyMmUxYmY0OTljY2NiMWUwNmENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsIm5hbWUiOiJmb28ifQoNCi0tNzFlNjU4Yzc5NzhkNWNmZTJlZTg3NjFmZWJkYmUzNDM1NDFhZmI4MDlkMjJlMWJmNDk5Y2NjYjFlMDZhDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KaGVsbG8NCi0tNzFlNjU4Yzc5NzhkNWNmZTJlZTg3NjFmZWJkYmUzNDM1NDFhZmI4MDlkMjJlMWJmNDk5Y2NjYjFlMDZhLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3544" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:38 GMT" + ], + "Etag": [ + "CP2D/M2i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrgm184:4086,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zk41W7yHDoPThQS6q4CABQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/608:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpaJ15u7kWX_7iHK-rodjJ_6Xj-0L3e0E9DZIW-lXht9X85llaxg61iMXJ-nZqWwG7ECiQUjZ-FkhS3xjFGERFh96mNh00-DL9CHIHqsRai_cmnl94" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzODUzNzIxMyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzODUzNzIxMyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozOC41MzZaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzguNTM2WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM4LjUzNloiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIzODUzNzIxMyZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzg1MzcyMTMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzODUzNzIxMyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ1AyRC9NMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzODUzNzIxMy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzg1MzcyMTMiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ1AyRC9NMmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzODUzNzIxMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzg1MzcyMTMiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNQMkQvTTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzg1MzcyMTMvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzg1MzcyMTMiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNQMkQvTTJpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDUDJEL00yaTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "43db79285150c284", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "31f716b5ea3493ad5e75ef4bb2b2c5e8/9215330225775572468;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 400, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12529" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:38 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:38 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220538000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrcw62:4262,/bns/yw/borg/yw/bns/blobstore2/bitpusher/303.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zk41W_LgJ4n2hASxs6mIBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/303.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/303:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOMrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpjTgsdh2UhyBxXxrk1DVLLLMhZ3PJ8dXGtj0ZkBs6ndqqt94wkqXW6Y2Ak57mOuqMUn8hlGNMbjMZznUwzQnglC_-ynloMonyWvoBkjI_6-DKGTC0" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQnVja2V0IGlzIHJlcXVlc3RlciBwYXlzIGJ1Y2tldCBidXQgbm8gdXNlciBwcm9qZWN0IHByb3ZpZGVkLiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1iYWRSZXF1ZXN0LCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YTo4NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6MjcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmRlbGV0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTEzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBCdWNrZXQgaXMgUmVxdWVzdGVyIFBheXMgYnVja2V0IGJ1dCBubyBiaWxsaW5nIHByb2plY3QgaWQgcHJvdmlkZWQgZm9yIG5vbi1vd25lci5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1CdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAwfSBCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX01JU1NJTkc6IFVTRVJfUFJPSkVDVF9NSVNTSU5HOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogQnVja2V0IGlzIFJlcXVlc3RlciBQYXlzIGJ1Y2tldCBidXQgbm8gYmlsbGluZyBwcm9qZWN0IGlkIHByb3ZpZGVkIGZvciBub24tb3duZXIuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfTUlTU0lORzogVVNFUl9QUk9KRUNUX01JU1NJTkc6IEJ1Y2tldCBpcyBSZXF1ZXN0ZXIgUGF5cyBidWNrZXQgYnV0IG5vIGJpbGxpbmcgcHJvamVjdCBpZCBwcm92aWRlZCBmb3Igbm9uLW93bmVyLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMCwKICAibWVzc2FnZSI6ICJCdWNrZXQgaXMgcmVxdWVzdGVyIHBheXMgYnVja2V0IGJ1dCBubyB1c2VyIHByb2plY3QgcHJvdmlkZWQuIgogfQp9Cg==" + } + }, + { + "ID": "86cf3d32d972741a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=0ef666b03dc08fed51549b75f9ff0525adf4fcc606ba816165a036d45c01" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2705f678ea6dff44cdedf565eb8b11e0/9950981978650490372;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0wZWY2NjZiMDNkYzA4ZmVkNTE1NDliNzVmOWZmMDUyNWFkZjRmY2M2MDZiYTgxNjE2NWEwMzZkNDVjMDENCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsIm5hbWUiOiJmb28ifQoNCi0tMGVmNjY2YjAzZGMwOGZlZDUxNTQ5Yjc1ZjlmZjA1MjVhZGY0ZmNjNjA2YmE4MTYxNjVhMDM2ZDQ1YzAxDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KaGVsbG8NCi0tMGVmNjY2YjAzZGMwOGZlZDUxNTQ5Yjc1ZjlmZjA1MjVhZGY0ZmNjNjA2YmE4MTYxNjVhMDM2ZDQ1YzAxLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3544" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:39 GMT" + ], + "Etag": [ + "CK67oc6i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjk18:4091,/bns/yw/borg/yw/bns/blobstore2/bitpusher/473.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=zk41W72SMpbkhASluqCQBQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/473.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/473:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur23u_2pDginx6-588CqtXDXMu1CsuX1nE-KEUW0_g97_5JS3ptalEYKAJNTaig2uqJPJAGfstMhk4WwpMWCmeJd5xdhSZQCl7h2RAHfxxuo2LF9r8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzOTE1MDUxMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzOTE1MDUxMCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozOS4xNDlaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzkuMTQ5WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM5LjE0OVoiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIzOTE1MDUxMCZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzkxNTA1MTAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzOTE1MDUxMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0s2N29jNmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzOTE1MDUxMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzkxNTA1MTAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0s2N29jNmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzOTE1MDUxMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzkxNTA1MTAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNLNjdvYzZpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzkxNTA1MTAvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzkxNTA1MTAiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNLNjdvYzZpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDSzY3b2M2aTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "d54e3334f26de898", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026userProject=gcloud-golang-firestore-tests", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "88572579c523efd9d3a7991727907d68/10758408759648090132;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026userProject=gcloud-golang-firestore-tests" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:39 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220538000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrgr19:4021,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=z041W5zNEtbshgThiaaoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/372:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOMrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqzgBp-hadem2L8DgSACr_6OSEJxmds24WtJWhpBKREz4wlRq2agAct1DHHrgzgLnag8xmBsaXp7U3QjZuDs-MF-7kiXsUF9VUfJg4S3mPnjLwM2Sw" + ] + }, + "Body": "" + } + }, + { + "ID": "8f3ee1fdf17805f8", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=15231e77b504acd77db1197296a62a44a93e9b40abfad28fca9078b1e4d0" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "efcaa6518753832a00929329823d72bf/11565834441167616292;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0xNTIzMWU3N2I1MDRhY2Q3N2RiMTE5NzI5NmE2MmE0NGE5M2U5YjQwYWJmYWQyOGZjYTkwNzhiMWU0ZDANCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsIm5hbWUiOiJmb28ifQoNCi0tMTUyMzFlNzdiNTA0YWNkNzdkYjExOTcyOTZhNjJhNDRhOTNlOWI0MGFiZmFkMjhmY2E5MDc4YjFlNGQwDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KaGVsbG8NCi0tMTUyMzFlNzdiNTA0YWNkNzdkYjExOTcyOTZhNjJhNDRhOTNlOWI0MGFiZmFkMjhmY2E5MDc4YjFlNGQwLS0NCg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3544" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:39 GMT" + ], + "Etag": [ + "CMqZy86i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdk9:4305,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=z041W_GCH9OkhATsyYKoCg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/521:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrV1-1tibiNnDFk2IVjxLGS_Xyz7XrF__ZKcj9BIUDbdpiFYum0o-Vlym8ra9Q8l8vXf72Zr2swMYGKtNetTeYK6la8PZI5foSs6BZvw551ijovoTo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzOTgzNDMxNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2ZvbyIsCiAibmFtZSI6ICJmb28iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzOTgzNDMxNCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDozOS44MzJaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MzkuODMyWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjM5LjgzMloiLAogInNpemUiOiAiNSIsCiAibWQ1SGFzaCI6ICJYVUZBS3J4TEtuYTVjWjJSRUJmRmtnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vP2dlbmVyYXRpb249MTUzMDIyMDIzOTgzNDMxNCZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzk4MzQzMTQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMiIsCiAgICJvYmplY3QiOiAiZm9vIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIzOTgzNDMxNCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ01xWnk4Nmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzOTgzNDMxNC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzk4MzQzMTQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ01xWnk4Nmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9mb28vMTUzMDIyMDIzOTgzNDMxNC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMi9vL2Zvby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzk4MzQzMTQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNNcVp5ODZpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIvZm9vLzE1MzAyMjAyMzk4MzQzMTQvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAyL28vZm9vL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDIiLAogICAib2JqZWN0IjogImZvbyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMzk4MzQzMTQiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNNcVp5ODZpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJtbkc3VEE9PSIsCiAiZXRhZyI6ICJDTXFaeTg2aTk5c0NFQUU9Igp9Cg==" + } + }, + { + "ID": "2590c5af1e1399f8", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026userProject=veener-jba", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "71c42cfcf290e26daf644c095d0539f3/12373542692847024947;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json\u0026userProject=veener-jba" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13481" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:40 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:40 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220515000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "471383502717" + ], + "X-Google-Backends": [ + "vrak7:4363,/bns/yw/borg/yw/bns/blobstore2/bitpusher/533.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=z041W8KeOoS1hQSt54II" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/533.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/533:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CP3elYXcDRoCGAYoATpFChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGM6ropHgHCIVMTE3MjM4MDU4ODYxOTM3NTE5MTQ0MOArMOErMOMrSp8BEoIBeWEyOS5jLkVscm9CWXBPNWt3TjAxazRwdy1mTThCQlQ0Q1c1eWUwSldFUVR2d2VPbkdydnJBSzhlaXoyZVRjemFITlZSamgzMlpwNWNyaXhtVzFFNzhzaFNVb1VqaDJ4eV9kTGlYcHpCVmdxMG1CTmFoX0JKZ3cyeHJjQ25RbEtJSTAEOhZOT1RfQV9QRVJTSVNURU5UX1RPS0VO" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrmLrRY8eThNP3lsduLCGCE96NOUgsLaNWX60dBzcDs6RL6Ucw3Dd23E7EMRPMP0Xi1cbK0_-EV8A7-_jAEXRMb7-vzTRJIHKM0i0i8NeP8X7_2Wl4" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogImludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IGludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPWZvcmJpZGRlbiwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uRk9SQklEREVOLCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6VVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Rk9SQklEREVOLCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPW51bGwsIG1lc3NhZ2U9aW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPWludGVncmF0aW9uQGdjbG91ZC1nb2xhbmctZmlyZXN0b3JlLXRlc3RzLmlhbS5nc2VydmljZWFjY291bnQuY29tIGRvZXMgbm90IGhhdmUgc2VydmljZXVzYWdlLnNlcnZpY2VzLnVzZSBhY2Nlc3MgdG8gcHJvamVjdCA2NDIwODA5MTgxMDEuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gaW50ZWdyYXRpb25AZ2Nsb3VkLWdvbGFuZy1maXJlc3RvcmUtdGVzdHMuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20gZG9lcyBub3QgaGF2ZSBzZXJ2aWNldXNhZ2Uuc2VydmljZXMudXNlIGFjY2VzcyB0byBwcm9qZWN0IDY0MjA4MDkxODEwMS46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpVU0VSX1BST0pFQ1RfQUNDRVNTX0RFTklFRDogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YTo4NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6MjcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmRlbGV0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTEzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogVVNFUl9QUk9KRUNUX0FDQ0VTU19ERU5JRUQ6IFVTRVJfUFJPSkVDVF9BQ0NFU1NfREVOSUVEOiBpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJpbnRlZ3JhdGlvbkBnY2xvdWQtZ29sYW5nLWZpcmVzdG9yZS10ZXN0cy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSBkb2VzIG5vdCBoYXZlIHNlcnZpY2V1c2FnZS5zZXJ2aWNlcy51c2UgYWNjZXNzIHRvIHByb2plY3QgNjQyMDgwOTE4MTAxLiIKIH0KfQo=" + } + }, + { + "ID": "9a5f1369645e9728", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "536f725249c833e6a9653dfaf4c9ea60/13180969473861401667;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/foo?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:40 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220536000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrba191:4142,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0E41W5yPCMytN6euonA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/282:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqE0dtwCuQug3KHtrMWYgkEpeWBuGEizffKz-B-pzGs30cXR-xek5LkFW9V_VHf_myA_4IIOYB3jVL0SbF3NfhjS3zlgXWEO6C-WtNvYssySXEJvhE" + ] + }, + "Body": "" + } + }, + { + "ID": "827a28bca75c9330", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "25dadf84862f9c07725f3efec681eacf/13916620131519594067;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/copy?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:40 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220536000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrch67:4274,/bns/yw/borg/yw/bns/blobstore2/bitpusher/388.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0E41W8TiGoq0hQSgpJTwCw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/388.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/388:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq6Ik3kGpZKhklStbgKoXaZeJCwv7BMvr9d5u91bbaiBw3YTmbh7XfPvSb_oG7NxELrdoUxX1j6oPVC1MJYpR_SoIZBsOWWUC21aHVWwXJd75jG84A" + ] + }, + "Body": "" + } + }, + { + "ID": "9cc58ce4cdb9b8b8", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose?alt=json\u0026userProject=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fc91cc1415abf9aa7a5a81976105fbaa/14724046912533970787;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002/o/compose?alt=json\u0026userProject=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:41 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220511000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdh62:4480,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0E41W4jHLMfUN9mJjPgB" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/435:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urv3CdAezrB-H64m3d6jJsxb3f1ZVVzp7C0iyuOmjJIscPFh3xJi__lEoo-U8dRa8w8xtbP8_DQeSKCCEqJP5IlaA-ZLpLHCdT-OHF-eOtQlcAqGJQ" + ] + }, + "Body": "" + } + }, + { + "ID": "b7eb0c5e67be8cc4", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8880d7dcf9e5ec409a12dd0a211fa0ce/16339180841438003842;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0002?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:41 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220536000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrmr18:4187,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0U41W9iSAobUhQSJtoaQBA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/66.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/66:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVWJ4V2YyWHlVYzgzemRLbUdqV21SZDV4M095dm5qdGFFSlJUc1l2Qm4tUlBiakhUNVVZV1JTMmY2bFR2QURDV3hUblhZNHJQWVB1QkNjSWtBeTVQYjU0R1hhLS1WcXhvbEF1WGRHdkZNMUJGLW9OcDZGdFRCSW96aXRldVZNVVdJZ3RKVjdqSGhfSHNDdmRYMF9Hd2pqejlucWJzaVZCQUZBVDFWWTNsSEtGaE5KcjZtRGdIYVg0SlUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrOYrFKxzlUJl7VTGG-z9ULjH2DbTOlXpusezHNS0MmVBASWAUSUtRWZxlXdqFNuFW0PeUmDyVguIh1h2ceFYmAK15i00hW5SDaDVZ1xhpFJd6IWn0" + ] + }, + "Body": "" + } + }, + { + "ID": "6998e22e8000dbd7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "29664c6be16da0646573fb41931883a3/17882258280110572962;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "37" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:41 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:41 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220541000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vran64:4046,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0U41W93jIIaihQTOw5HwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/68:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBV0NZdmZMWHJDanpyaHBfZVhMX2hERW5UVFRYSVBFOUJudG1HMHBJaGpGMjE2TVhndVpVQ0JCcjdEajZuNUdLdlZKUDdZeXJFdmVQVFNOdDl3Q2RvZUd3YXJkbldhc0E0cU44VldjTkVjdFVtZ0tPQjBlNmNjRVl4WUZPSGw4ekxQbjNZT2Nuc1ljM0pDcGVrM2V0bDRyUENaVXI4X0w1eUFtSUx3di1zS3lrMUYzQnJEeWoyRTdEcHcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpHJShsnX2VLvKVyQYq2A3NIWpTRhb1vY8PiV1dVVq_Ktd0se5xwRJj6UnYTrVJsVLDswoXZPBPrqluGOuZ0UvHMC14EmEa-bYqpuRPP1GTqh6QPAA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNub3RpZmljYXRpb25zIgp9Cg==" + } + }, + { + "ID": "1124345a990c15d2", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "121" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "bce7f205f825691e476a0cfbbe01305e/1050930714071517377;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJwYXlsb2FkX2Zvcm1hdCI6Ik5PTkUiLCJ0b3BpYyI6Ii8vcHVic3ViLmdvb2dsZWFwaXMuY29tL3Byb2plY3RzL2R1bGNldC1wb3J0LTc2Mi90b3BpY3MvZ28tc3RvcmFnZS1ub3RpZmljYXRpb24tdGVzdCJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "302" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:42 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220541000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vru67:4406,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0U41W9SqLIXGhQT1vofYAg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/288:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBV0NZdmZMWHJDanpyaHBfZVhMX2hERW5UVFRYSVBFOUJudG1HMHBJaGpGMjE2TVhndVpVQ0JCcjdEajZuNUdLdlZKUDdZeXJFdmVQVFNOdDl3Q2RvZUd3YXJkbldhc0E0cU44VldjTkVjdFVtZ0tPQjBlNmNjRVl4WUZPSGw4ekxQbjNZT2Nuc1ljM0pDcGVrM2V0bDRyUENaVXI4X0w1eUFtSUx3di1zS3lrMUYzQnJEeWoyRTdEcHcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UovrMDyD-enwEErTeZcUIV4t88JSRXRkdt_hteUuXHJThwSG0EN8Bx5qjbH75dvpXo9677_QCTejEv7HM2iZrz7qaYkm9UyYYc5MM7Nq0m-eXxqJeo" + ] + }, + "Body": "ewogImlkIjogIjEwIiwKICJ0b3BpYyI6ICIvL3B1YnN1Yi5nb29nbGVhcGlzLmNvbS9wcm9qZWN0cy9kdWxjZXQtcG9ydC03NjIvdG9waWNzL2dvLXN0b3JhZ2Utbm90aWZpY2F0aW9uLXRlc3QiLAogInBheWxvYWRfZm9ybWF0IjogIk5PTkUiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm90aWZpY2F0aW9uQ29uZmlncy8xMCIsCiAia2luZCI6ICJzdG9yYWdlI25vdGlmaWNhdGlvbiIKfQo=" + } + }, + { + "ID": "442734abf7ab191f", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9e7bd443128653ecb716a01a54224a40/2666064647270386657;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "369" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:42 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:42 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220541000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vry82:4465,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0k41W-P2MNGChQTP5Cs" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/139:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBV0NZdmZMWHJDanpyaHBfZVhMX2hERW5UVFRYSVBFOUJudG1HMHBJaGpGMjE2TVhndVpVQ0JCcjdEajZuNUdLdlZKUDdZeXJFdmVQVFNOdDl3Q2RvZUd3YXJkbldhc0E0cU44VldjTkVjdFVtZ0tPQjBlNmNjRVl4WUZPSGw4ekxQbjNZT2Nuc1ljM0pDcGVrM2V0bDRyUENaVXI4X0w1eUFtSUx3di1zS3lrMUYzQnJEeWoyRTdEcHcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urw1Op2g8PaRk_EoKnUqHbFyEJmtNvsR4nLY0pwy9Aa__rosg8mf48hrXrCmOB3HB6CeO4WXxf6uUy8LEqoin6T3j3xJqspLOV_cIwDEr1cGHQGC30" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNub3RpZmljYXRpb25zIiwKICJpdGVtcyI6IFsKICB7CiAgICJpZCI6ICIxMCIsCiAgICJ0b3BpYyI6ICIvL3B1YnN1Yi5nb29nbGVhcGlzLmNvbS9wcm9qZWN0cy9kdWxjZXQtcG9ydC03NjIvdG9waWNzL2dvLXN0b3JhZ2Utbm90aWZpY2F0aW9uLXRlc3QiLAogICAicGF5bG9hZF9mb3JtYXQiOiAiTk9ORSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vdGlmaWNhdGlvbkNvbmZpZ3MvMTAiLAogICAia2luZCI6ICJzdG9yYWdlI25vdGlmaWNhdGlvbiIKICB9CiBdCn0K" + } + }, + { + "ID": "d3de8650df0d113d", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs/10?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "817dedccfaa9342e565a189b2e71803c/4209142081648119296;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs/10?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:43 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220543000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrv185:4062,/bns/yw/borg/yw/bns/blobstore2/bitpusher/617.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0041W7-oAdW5hQTt_5nwBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/617.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/617:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBV0NZdmZMWHJDanpyaHBfZVhMX2hERW5UVFRYSVBFOUJudG1HMHBJaGpGMjE2TVhndVpVQ0JCcjdEajZuNUdLdlZKUDdZeXJFdmVQVFNOdDl3Q2RvZUd3YXJkbldhc0E0cU44VldjTkVjdFVtZ0tPQjBlNmNjRVl4WUZPSGw4ekxQbjNZT2Nuc1ljM0pDcGVrM2V0bDRyUENaVXI4X0w1eUFtSUx3di1zS3lrMUYzQnJEeWoyRTdEcHcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpcNXjjPjkRGEzlMUdFBxOwoqiPtzbHpuIQ_ETtW_MAMx_k-QeMbBwebWzEkICnZE5359ZT6fgaPBu9sLuaFjE4PJFpuC-JIvbF_FgNAKR1R5ix-DA" + ] + }, + "Body": "" + } + }, + { + "ID": "bc496c5d408dd20a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "54754bbed2cf63cf3567090cced3168a/5824276014846988576;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/notificationConfigs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "37" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:43 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:43 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220541000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vreb9:4228,/bns/yw/borg/yw/bns/blobstore2/bitpusher/492.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0041W9uzLIikhwST84aoBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/492.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/492:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBV0NZdmZMWHJDanpyaHBfZVhMX2hERW5UVFRYSVBFOUJudG1HMHBJaGpGMjE2TVhndVpVQ0JCcjdEajZuNUdLdlZKUDdZeXJFdmVQVFNOdDl3Q2RvZUd3YXJkbldhc0E0cU44VldjTkVjdFVtZ0tPQjBlNmNjRVl4WUZPSGw4ekxQbjNZT2Nuc1ljM0pDcGVrM2V0bDRyUENaVXI4X0w1eUFtSUx3di1zS3lrMUYzQnJEeWoyRTdEcHcwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrOIjoDRorE_-8wjq65GcYze9vwQ2Y7rrhvFnMU-IXnjEI1DV-XnR583oS24rlDGwOUYJpO2V_UdIGkIPU8YCdU6uIELn1gy0bV-OklEZU-N-kQgQY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNub3RpZmljYXRpb25zIgp9Cg==" + } + }, + { + "ID": "7bc2c9477a1d659a", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "72e69bc3bd23262013da89a5867440e1/7367073082332730431;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "7903" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:43 GMT" + ], + "Etag": [ + "\"7a5fd4743bd647485f88496fadb05c51\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:43 GMT" + ], + "Last-Modified": [ + "Tue, 04 Oct 2016 16:42:07 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1475599327662000" + ], + "X-Goog-Hash": [ + "crc32c=PWBt8g==", + "md5=el/UdDvWR0hfiElvrbBcUQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "7903" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/28,/bns/xg/borg/xg/bns/blobstore2/bitpusher/146.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=0041W-i5N-q__QT18ZzYBw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "570399209098" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/146.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/146:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urf7gNgMJz1Lij9uAG96v8-gXZZfamdc5aFQMlrQhRoKGK0QdkUlX9mUme_aZ657FPx1n1Q2Hj6A08Ops2x4HvkMqr31A" + ] + }, + "Body": "R1JPVVAgPSBMMV9NRVRBREFUQV9GSUxFCiAgR1JPVVAgPSBNRVRBREFUQV9GSUxFX0lORk8KICAgIE9SSUdJTiA9ICJJbWFnZSBjb3VydGVzeSBvZiB0aGUgVS5TLiBHZW9sb2dpY2FsIFN1cnZleSIKICAgIFJFUVVFU1RfSUQgPSAiMDcwMTYwOTE5MTA1MV8wMDAwNCIKICAgIExBTkRTQVRfU0NFTkVfSUQgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwIgogICAgRklMRV9EQVRFID0gMjAxNi0wOS0yMFQwMzoxMzowMloKICAgIFNUQVRJT05fSUQgPSAiTEdOIgogICAgUFJPQ0VTU0lOR19TT0ZUV0FSRV9WRVJTSU9OID0gIkxQR1NfMi42LjIiCiAgRU5EX0dST1VQID0gTUVUQURBVEFfRklMRV9JTkZPCiAgR1JPVVAgPSBQUk9EVUNUX01FVEFEQVRBCiAgICBEQVRBX1RZUEUgPSAiTDFUIgogICAgRUxFVkFUSU9OX1NPVVJDRSA9ICJHTFMyMDAwIgogICAgT1VUUFVUX0ZPUk1BVCA9ICJHRU9USUZGIgogICAgU1BBQ0VDUkFGVF9JRCA9ICJMQU5EU0FUXzgiCiAgICBTRU5TT1JfSUQgPSAiT0xJX1RJUlMiCiAgICBXUlNfUEFUSCA9IDQ0CiAgICBXUlNfUk9XID0gMzQKICAgIE5BRElSX09GRk5BRElSID0gIk5BRElSIgogICAgVEFSR0VUX1dSU19QQVRIID0gNDQKICAgIFRBUkdFVF9XUlNfUk9XID0gMzQKICAgIERBVEVfQUNRVUlSRUQgPSAyMDE2LTA5LTE1CiAgICBTQ0VORV9DRU5URVJfVElNRSA9ICIxODo0NjoxOC42ODY3MzgwWiIKICAgIENPUk5FUl9VTF9MQVRfUFJPRFVDVCA9IDM4LjUyODE5CiAgICBDT1JORVJfVUxfTE9OX1BST0RVQ1QgPSAtMTIzLjQwODQzCiAgICBDT1JORVJfVVJfTEFUX1BST0RVQ1QgPSAzOC41MDc2NQogICAgQ09STkVSX1VSX0xPTl9QUk9EVUNUID0gLTEyMC43NjkzMwogICAgQ09STkVSX0xMX0xBVF9QUk9EVUNUID0gMzYuNDE2MzMKICAgIENPUk5FUl9MTF9MT05fUFJPRFVDVCA9IC0xMjMuMzk3MDkKICAgIENPUk5FUl9MUl9MQVRfUFJPRFVDVCA9IDM2LjM5NzI5CiAgICBDT1JORVJfTFJfTE9OX1BST0RVQ1QgPSAtMTIwLjgzMTE3CiAgICBDT1JORVJfVUxfUFJPSkVDVElPTl9YX1BST0RVQ1QgPSA0NjQ0MDAuMDAwCiAgICBDT1JORVJfVUxfUFJPSkVDVElPTl9ZX1BST0RVQ1QgPSA0MjY0NTAwLjAwMAogICAgQ09STkVSX1VSX1BST0pFQ1RJT05fWF9QUk9EVUNUID0gNjk0NTAwLjAwMAogICAgQ09STkVSX1VSX1BST0pFQ1RJT05fWV9QUk9EVUNUID0gNDI2NDUwMC4wMDAKICAgIENPUk5FUl9MTF9QUk9KRUNUSU9OX1hfUFJPRFVDVCA9IDQ2NDQwMC4wMDAKICAgIENPUk5FUl9MTF9QUk9KRUNUSU9OX1lfUFJPRFVDVCA9IDQwMzAyMDAuMDAwCiAgICBDT1JORVJfTFJfUFJPSkVDVElPTl9YX1BST0RVQ1QgPSA2OTQ1MDAuMDAwCiAgICBDT1JORVJfTFJfUFJPSkVDVElPTl9ZX1BST0RVQ1QgPSA0MDMwMjAwLjAwMAogICAgUEFOQ0hST01BVElDX0xJTkVTID0gMTU2MjEKICAgIFBBTkNIUk9NQVRJQ19TQU1QTEVTID0gMTUzNDEKICAgIFJFRkxFQ1RJVkVfTElORVMgPSA3ODExCiAgICBSRUZMRUNUSVZFX1NBTVBMRVMgPSA3NjcxCiAgICBUSEVSTUFMX0xJTkVTID0gNzgxMQogICAgVEhFUk1BTF9TQU1QTEVTID0gNzY3MQogICAgRklMRV9OQU1FX0JBTkRfMSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjEuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMiA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjIuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMyA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjMuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjQuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjUuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNiA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjYuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNyA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjcuVElGIgogICAgRklMRV9OQU1FX0JBTkRfOCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjguVElGIgogICAgRklMRV9OQU1FX0JBTkRfOSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjkuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMTAgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IxMC5USUYiCiAgICBGSUxFX05BTUVfQkFORF8xMSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjExLlRJRiIKICAgIEZJTEVfTkFNRV9CQU5EX1FVQUxJVFkgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0JRQS5USUYiCiAgICBNRVRBREFUQV9GSUxFX05BTUUgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX01UTC50eHQiCiAgICBCUEZfTkFNRV9PTEkgPSAiTE84QlBGMjAxNjA5MTUxODMwNTdfMjAxNjA5MTUyMDA5NTAuMDEiCiAgICBCUEZfTkFNRV9USVJTID0gIkxUOEJQRjIwMTYwOTAyMDg0MTIyXzIwMTYwOTE3MDc0MDI3LjAyIgogICAgQ1BGX05BTUUgPSAiTDhDUEYyMDE2MDcwMV8yMDE2MDkzMC4wMiIKICAgIFJMVVRfRklMRV9OQU1FID0gIkw4UkxVVDIwMTUwMzAzXzIwNDMxMjMxdjExLmg1IgogIEVORF9HUk9VUCA9IFBST0RVQ1RfTUVUQURBVEEKICBHUk9VUCA9IElNQUdFX0FUVFJJQlVURVMKICAgIENMT1VEX0NPVkVSID0gMjkuNTYKICAgIENMT1VEX0NPVkVSX0xBTkQgPSAzLjMzCiAgICBJTUFHRV9RVUFMSVRZX09MSSA9IDkKICAgIElNQUdFX1FVQUxJVFlfVElSUyA9IDkKICAgIFRJUlNfU1NNX01PREVMID0gIkZJTkFMIgogICAgVElSU19TU01fUE9TSVRJT05fU1RBVFVTID0gIkVTVElNQVRFRCIKICAgIFJPTExfQU5HTEUgPSAtMC4wMDEKICAgIFNVTl9BWklNVVRIID0gMTQ4LjQ4MDQ5Mzk2CiAgICBTVU5fRUxFVkFUSU9OID0gNTAuOTM3NjgzOTkKICAgIEVBUlRIX1NVTl9ESVNUQU5DRSA9IDEuMDA1Mzc1MgogICAgR1JPVU5EX0NPTlRST0xfUE9JTlRTX1ZFUlNJT04gPSA0CiAgICBHUk9VTkRfQ09OVFJPTF9QT0lOVFNfTU9ERUwgPSA1NDgKICAgIEdFT01FVFJJQ19STVNFX01PREVMID0gNS44NTcKICAgIEdFT01FVFJJQ19STVNFX01PREVMX1kgPSAzLjg0MQogICAgR0VPTUVUUklDX1JNU0VfTU9ERUxfWCA9IDQuNDIyCiAgICBHUk9VTkRfQ09OVFJPTF9QT0lOVFNfVkVSSUZZID0gMjI4CiAgICBHRU9NRVRSSUNfUk1TRV9WRVJJRlkgPSAzLjM4MgogIEVORF9HUk9VUCA9IElNQUdFX0FUVFJJQlVURVMKICBHUk9VUCA9IE1JTl9NQVhfUkFESUFOQ0UKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF8xID0gNzUxLjk1NzA5CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfMSA9IC02Mi4wOTY4NgogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzIgPSA3NzAuMDEzMTgKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8yID0gLTYzLjU4Nzk0CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDcwOS41NjA2MQogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzMgPSAtNTguNTk1NzUKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF80ID0gNTk4LjM0MTQ5CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNCA9IC00OS40MTEyMwogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzUgPSAzNjYuMTU1MTUKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF81ID0gLTMwLjIzNzIxCiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDkxLjA1OTQ2CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNiA9IC03LjUxOTcyCiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfNyA9IDMwLjY5MTkxCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNyA9IC0yLjUzNDU1CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfOCA9IDY3Ny4xNTc4NAogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzggPSAtNTUuOTE5OTIKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF85ID0gMTQzLjEwMTczCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfOSA9IC0xMS44MTczOQogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzEwID0gMjIuMDAxODAKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8xMCA9IDAuMTAwMzMKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF8xMSA9IDIyLjAwMTgwCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfMTEgPSAwLjEwMDMzCiAgRU5EX0dST1VQID0gTUlOX01BWF9SQURJQU5DRQogIEdST1VQID0gTUlOX01BWF9SRUZMRUNUQU5DRQogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzEgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzEgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF8yID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF8yID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfMyA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzQgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzQgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF81ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF81ID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfNiA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzcgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzcgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF84ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF84ID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfOSA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfOSA9IC0wLjA5OTk4MAogIEVORF9HUk9VUCA9IE1JTl9NQVhfUkVGTEVDVEFOQ0UKICBHUk9VUCA9IE1JTl9NQVhfUElYRUxfVkFMVUUKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8xID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF8xID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzIgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzIgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfMyA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMyA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF80ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF80ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzUgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzUgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfNiA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfNiA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF83ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF83ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzggPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzggPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfOSA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfOSA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8xMCA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMTAgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfMTEgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzExID0gMQogIEVORF9HUk9VUCA9IE1JTl9NQVhfUElYRUxfVkFMVUUKICBHUk9VUCA9IFJBRElPTUVUUklDX1JFU0NBTElORwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzEgPSAxLjI0MjJFLTAyCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfMiA9IDEuMjcyMEUtMDIKICAgIFJBRElBTkNFX01VTFRfQkFORF8zID0gMS4xNzIxRS0wMgogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzQgPSA5Ljg4NDJFLTAzCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfNSA9IDYuMDQ4N0UtMDMKICAgIFJBRElBTkNFX01VTFRfQkFORF82ID0gMS41MDQyRS0wMwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzcgPSA1LjA3MDFFLTA0CiAgICBSQURJQU5DRV9NVUxUX0JBTkRfOCA9IDEuMTE4NkUtMDIKICAgIFJBRElBTkNFX01VTFRfQkFORF85ID0gMi4zNjQwRS0wMwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzEwID0gMy4zNDIwRS0wNAogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzExID0gMy4zNDIwRS0wNAogICAgUkFESUFOQ0VfQUREX0JBTkRfMSA9IC02Mi4xMDkyOAogICAgUkFESUFOQ0VfQUREX0JBTkRfMiA9IC02My42MDA2NgogICAgUkFESUFOQ0VfQUREX0JBTkRfMyA9IC01OC42MDc0NwogICAgUkFESUFOQ0VfQUREX0JBTkRfNCA9IC00OS40MjExMgogICAgUkFESUFOQ0VfQUREX0JBTkRfNSA9IC0zMC4yNDMyNgogICAgUkFESUFOQ0VfQUREX0JBTkRfNiA9IC03LjUyMTIyCiAgICBSQURJQU5DRV9BRERfQkFORF83ID0gLTIuNTM1MDUKICAgIFJBRElBTkNFX0FERF9CQU5EXzggPSAtNTUuOTMxMTAKICAgIFJBRElBTkNFX0FERF9CQU5EXzkgPSAtMTEuODE5NzUKICAgIFJBRElBTkNFX0FERF9CQU5EXzEwID0gMC4xMDAwMAogICAgUkFESUFOQ0VfQUREX0JBTkRfMTEgPSAwLjEwMDAwCiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfMSA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF8yID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzMgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfNCA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF81ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzYgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfNyA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF84ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzkgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8xID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8yID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8zID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF80ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF81ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF82ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF83ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF84ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF85ID0gLTAuMTAwMDAwCiAgRU5EX0dST1VQID0gUkFESU9NRVRSSUNfUkVTQ0FMSU5HCiAgR1JPVVAgPSBUSVJTX1RIRVJNQUxfQ09OU1RBTlRTCiAgICBLMV9DT05TVEFOVF9CQU5EXzEwID0gNzc0Ljg4NTMKICAgIEsxX0NPTlNUQU5UX0JBTkRfMTEgPSA0ODAuODg4MwogICAgSzJfQ09OU1RBTlRfQkFORF8xMCA9IDEzMjEuMDc4OQogICAgSzJfQ09OU1RBTlRfQkFORF8xMSA9IDEyMDEuMTQ0MgogIEVORF9HUk9VUCA9IFRJUlNfVEhFUk1BTF9DT05TVEFOVFMKICBHUk9VUCA9IFBST0pFQ1RJT05fUEFSQU1FVEVSUwogICAgTUFQX1BST0pFQ1RJT04gPSAiVVRNIgogICAgREFUVU0gPSAiV0dTODQiCiAgICBFTExJUFNPSUQgPSAiV0dTODQiCiAgICBVVE1fWk9ORSA9IDEwCiAgICBHUklEX0NFTExfU0laRV9QQU5DSFJPTUFUSUMgPSAxNS4wMAogICAgR1JJRF9DRUxMX1NJWkVfUkVGTEVDVElWRSA9IDMwLjAwCiAgICBHUklEX0NFTExfU0laRV9USEVSTUFMID0gMzAuMDAKICAgIE9SSUVOVEFUSU9OID0gIk5PUlRIX1VQIgogICAgUkVTQU1QTElOR19PUFRJT04gPSAiQ1VCSUNfQ09OVk9MVVRJT04iCiAgRU5EX0dST1VQID0gUFJPSkVDVElPTl9QQVJBTUVURVJTCkVORF9HUk9VUCA9IEwxX01FVEFEQVRBX0ZJTEUKRU5ECg==" + } + }, + { + "ID": "5e5bb0c20796c3a2", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/gcp-public-data-landsat/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=LC08%2FPRE%2F044%2F034%2FLC80440342016259LGN00%2F\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2bb5c7b549db3cd0d8525d01815971de/8174780234517222991;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/gcp-public-data-landsat/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=LC08%2FPRE%2F044%2F034%2FLC80440342016259LGN00%2F\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "13825" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:44 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:44 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Backends": [ + "vrli86:4200,/bns/yw/borg/yw/bns/blobstore2/bitpusher/331.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1E41W43-AsKOhgT_2Z64Bg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/331.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/331:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "GgIYBiAB" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrunIw8lPRxhcbQkixkQeV3E_hlFlAfg6cxALlD_BPXzSPNMwwf5y6BYXl9YY3ycx_Ze47PMk92s7YIKp98aR9BDPqX98lYnnJoWgTCpGiNyLo54Pw" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0L0xDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9CMS5USUYvMTQ3NTU5OTE0NDU3OTAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvby9MQzA4JTJGUFJFJTJGMDQ0JTJGMDM0JTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwJTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IxLlRJRiIsCiAgICJuYW1lIjogIkxDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9CMS5USUYiLAogICAiYnVja2V0IjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0IiwKICAgImdlbmVyYXRpb24iOiAiMTQ3NTU5OTE0NDU3OTAwMCIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjM5OjA0LjU0NVoiLAogICAidXBkYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjM5OjA0LjU0NVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6Mzk6MDQuNTQ1WiIsCiAgICJzaXplIjogIjc0NzIxNzM2IiwKICAgIm1kNUhhc2giOiAiODM1TDZCNWZyQjB6Q0I2czIycjJTdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvby9MQzA4JTJGUFJFJTJGMDQ0JTJGMDM0JTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwJTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IxLlRJRj9nZW5lcmF0aW9uPTE0NzU1OTkxNDQ1NzkwMDAmYWx0PW1lZGlhIiwKICAgImNyYzMyYyI6ICI5MzRCcmc9PSIsCiAgICJldGFnIjogIkNMamYzNWJMd2M4Q0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0L0xDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9CMTAuVElGLzE0NzU1OTkzMTAwNDIwMDAiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2djcC1wdWJsaWMtZGF0YS1sYW5kc2F0L28vTEMwOCUyRlBSRSUyRjA0NCUyRjAzNCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMF9CMTAuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IxMC5USUYiLAogICAiYnVja2V0IjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0IiwKICAgImdlbmVyYXRpb24iOiAiMTQ3NTU5OTMxMDA0MjAwMCIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQxOjUwLjAwMloiLAogICAidXBkYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQxOjUwLjAwMloiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDE6NTAuMDAyWiIsCiAgICJzaXplIjogIjU4NjgxMjI4IiwKICAgIm1kNUhhc2giOiAiQlc2MjN4SGcxNUloVjI0bWJyTCtBdz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvby9MQzA4JTJGUFJFJTJGMDQ0JTJGMDM0JTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwJTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IxMC5USUY/Z2VuZXJhdGlvbj0xNDc1NTk5MzEwMDQyMDAwJmFsdD1tZWRpYSIsCiAgICJjcmMzMmMiOiAieHpWMmZnPT0iLAogICAiZXRhZyI6ICJDSkRuMHVYTHdjOENFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgICJpZCI6ICJnY3AtcHVibGljLWRhdGEtbGFuZHNhdC9MQzA4L1BSRS8wNDQvMDM0L0xDODA0NDAzNDIwMTYyNTlMR04wMC9MQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjExLlRJRi8xNDc1NTk5MzE5MTg4MDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjExLlRJRiIsCiAgICJuYW1lIjogIkxDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9CMTEuVElGIiwKICAgImJ1Y2tldCI6ICJnY3AtcHVibGljLWRhdGEtbGFuZHNhdCIsCiAgICJnZW5lcmF0aW9uIjogIjE0NzU1OTkzMTkxODgwMDAiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxNi0xMC0wNFQxNjo0MTo1OS4xNDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjo0MTo1OS4xNDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQxOjU5LjE0OVoiLAogICAic2l6ZSI6ICI1Njc5NjQzOSIsCiAgICJtZDVIYXNoIjogIkZPeGl5eEpYcUFmbFJUOGxGblNkT2c9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2djcC1wdWJsaWMtZGF0YS1sYW5kc2F0L28vTEMwOCUyRlBSRSUyRjA0NCUyRjAzNCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMF9CMTEuVElGP2dlbmVyYXRpb249MTQ3NTU5OTMxOTE4ODAwMCZhbHQ9bWVkaWEiLAogICAiY3JjMzJjIjogInAvSEZWdz09IiwKICAgImV0YWciOiAiQ0tDRWdlckx3YzhDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IyLlRJRi8xNDc1NTk5MTYxMjI0MDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjIuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IyLlRJRiIsCiAgICJidWNrZXQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNDc1NTk5MTYxMjI0MDAwIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTYtMTAtMDRUMTY6Mzk6MjEuMTYwWiIsCiAgICJ1cGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6Mzk6MjEuMTYwWiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjozOToyMS4xNjBaIiwKICAgInNpemUiOiAiNzcxNDk3NzEiLAogICAibWQ1SGFzaCI6ICJNUDIyempPbzJOczBpWTRNVFBKUndBPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjIuVElGP2dlbmVyYXRpb249MTQ3NTU5OTE2MTIyNDAwMCZhbHQ9bWVkaWEiLAogICAiY3JjMzJjIjogInJJOFlSZz09IiwKICAgImV0YWciOiAiQ01EVzE1N0x3YzhDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IzLlRJRi8xNDc1NTk5MTc4NDM1MDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjMuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IzLlRJRiIsCiAgICJidWNrZXQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNDc1NTk5MTc4NDM1MDAwIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTYtMTAtMDRUMTY6Mzk6MzguMzc2WiIsCiAgICJ1cGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6Mzk6MzguMzc2WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjozOTozOC4zNzZaIiwKICAgInNpemUiOiAiODAyOTM2ODciLAogICAibWQ1SGFzaCI6ICJ2UU1pR2VEdUJnNmNyM1hzZklFam9RPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjMuVElGP2dlbmVyYXRpb249MTQ3NTU5OTE3ODQzNTAwMCZhbHQ9bWVkaWEiLAogICAiY3JjMzJjIjogInVaQnJuQT09IiwKICAgImV0YWciOiAiQ0xpVDhxYkx3YzhDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I0LlRJRi8xNDc1NTk5MTk0MjY4MDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjQuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I0LlRJRiIsCiAgICJidWNrZXQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNDc1NTk5MTk0MjY4MDAwIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTYtMTAtMDRUMTY6Mzk6NTQuMjExWiIsCiAgICJ1cGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6Mzk6NTQuMjExWiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjozOTo1NC4yMTFaIiwKICAgInNpemUiOiAiODQ0OTQzNzUiLAogICAibWQ1SGFzaCI6ICJGV2VWQTAxWk8wK21BK0VSRmN6dWhBPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjQuVElGP2dlbmVyYXRpb249MTQ3NTU5OTE5NDI2ODAwMCZhbHQ9bWVkaWEiLAogICAiY3JjMzJjIjogIldlczVvUT09IiwKICAgImV0YWciOiAiQ09EQ3VLN0x3YzhDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I1LlRJRi8xNDc1NTk5MjAyOTc5MDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjUuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I1LlRJRiIsCiAgICJidWNrZXQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNDc1NTk5MjAyOTc5MDAwIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDA6MDIuOTM3WiIsCiAgICJ1cGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDA6MDIuOTM3WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjo0MDowMi45MzdaIiwKICAgInNpemUiOiAiODkzMTg0NjciLAogICAibWQ1SGFzaCI6ICJwNG95S0hBR281S3kzS2cxVEsxWlF3PT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjUuVElGP2dlbmVyYXRpb249MTQ3NTU5OTIwMjk3OTAwMCZhbHQ9bWVkaWEiLAogICAiY3JjMzJjIjogInBUWXV1dz09IiwKICAgImV0YWciOiAiQ0xpWnpMTEx3YzhDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I2LlRJRi8xNDc1NTk5MjMzNDgxMDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjYuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I2LlRJRiIsCiAgICJidWNrZXQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNDc1NTk5MjMzNDgxMDAwIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDA6MzMuMzQ5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDA6MzMuMzQ5WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjo0MDozMy4zNDlaIiwKICAgInNpemUiOiAiODk0NjU3NjciLAogICAibWQ1SGFzaCI6ICIyWjcyR1VPS3RsZ3pUOVZSU0dZWGpBPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjYuVElGP2dlbmVyYXRpb249MTQ3NTU5OTIzMzQ4MTAwMCZhbHQ9bWVkaWEiLAogICAiY3JjMzJjIjogIklOWEhiUT09IiwKICAgImV0YWciOiAiQ0tqeWtjSEx3YzhDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I3LlRJRi8xNDc1NTk5MjQxMDU1MDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjcuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I3LlRJRiIsCiAgICJidWNrZXQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNDc1NTk5MjQxMDU1MDAwIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDA6NDEuMDIxWiIsCiAgICJ1cGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDA6NDEuMDIxWiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjo0MDo0MS4wMjFaIiwKICAgInNpemUiOiAiODY0NjI2MTQiLAogICAibWQ1SGFzaCI6ICI4Z1BOUTdRWm9GMkNOWlo5RW1ybG9nPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjcuVElGP2dlbmVyYXRpb249MTQ3NTU5OTI0MTA1NTAwMCZhbHQ9bWVkaWEiLAogICAiY3JjMzJjIjogInV3Q0QrQT09IiwKICAgImV0YWciOiAiQ0ppVzRNVEx3YzhDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I4LlRJRi8xNDc1NTk5MjgxMzM4MDAwIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjguVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I4LlRJRiIsCiAgICJidWNrZXQiOiAiZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQiLAogICAiZ2VuZXJhdGlvbiI6ICIxNDc1NTk5MjgxMzM4MDAwIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDE6MjEuMzAwWiIsCiAgICJ1cGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDE6MjEuMzAwWiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxNi0xMC0wNFQxNjo0MToyMS4zMDBaIiwKICAgInNpemUiOiAiMzE4ODg3Nzc0IiwKICAgIm1kNUhhc2giOiAieTc5NUxyVXpCd2sydEw2UE0wMWNFQT09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvby9MQzA4JTJGUFJFJTJGMDQ0JTJGMDM0JTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwJTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I4LlRJRj9nZW5lcmF0aW9uPTE0NzU1OTkyODEzMzgwMDAmYWx0PW1lZGlhIiwKICAgImNyYzMyYyI6ICJaMytaaFE9PSIsCiAgICJldGFnIjogIkNKRHQrdGZMd2M4Q0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0L0xDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9COS5USUYvMTQ3NTU5OTI5MTQyNTAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvby9MQzA4JTJGUFJFJTJGMDQ0JTJGMDM0JTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwJTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I5LlRJRiIsCiAgICJuYW1lIjogIkxDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9COS5USUYiLAogICAiYnVja2V0IjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0IiwKICAgImdlbmVyYXRpb24iOiAiMTQ3NTU5OTI5MTQyNTAwMCIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQxOjMxLjM2MVoiLAogICAidXBkYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQxOjMxLjM2MVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDE6MzEuMzYxWiIsCiAgICJzaXplIjogIjQ0MzA4MjA1IiwKICAgIm1kNUhhc2giOiAiNUI0MUUyREJiWTUycFlQVUdWaDk1Zz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ2NwLXB1YmxpYy1kYXRhLWxhbmRzYXQvby9MQzA4JTJGUFJFJTJGMDQ0JTJGMDM0JTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwJTJGTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0I5LlRJRj9nZW5lcmF0aW9uPTE0NzU1OTkyOTE0MjUwMDAmYWx0PW1lZGlhIiwKICAgImNyYzMyYyI6ICJhME9EUXc9PSIsCiAgICJldGFnIjogIkNPakI0dHpMd2M4Q0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0L0xDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9CUUEuVElGLzE0NzU1OTkzMjcyMjIwMDAiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2djcC1wdWJsaWMtZGF0YS1sYW5kc2F0L28vTEMwOCUyRlBSRSUyRjA0NCUyRjAzNCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMF9CUUEuVElGIiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0JRQS5USUYiLAogICAiYnVja2V0IjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0IiwKICAgImdlbmVyYXRpb24iOiAiMTQ3NTU5OTMyNzIyMjAwMCIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQyOjA3LjE1OVoiLAogICAidXBkYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQyOjA3LjE1OVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDI6MDcuMTU5WiIsCiAgICJzaXplIjogIjMzNTQ3MTkiLAogICAibWQ1SGFzaCI6ICJ6cWlndmw1RW52bWkvR0xjOHlINTFBPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQlFBLlRJRj9nZW5lcmF0aW9uPTE0NzU1OTkzMjcyMjIwMDAmYWx0PW1lZGlhIiwKICAgImNyYzMyYyI6ICJXT0JnS0E9PSIsCiAgICJldGFnIjogIkNQQ3g2KzNMd2M4Q0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0L0xDMDgvUFJFLzA0NC8wMzQvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwL0xDODA0NDAzNDIwMTYyNTlMR04wMF9NVEwudHh0LzE0NzU1OTkzMjc2NjIwMDAiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2djcC1wdWJsaWMtZGF0YS1sYW5kc2F0L28vTEMwOCUyRlBSRSUyRjA0NCUyRjAzNCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMCUyRkxDODA0NDAzNDIwMTYyNTlMR04wMF9NVEwudHh0IiwKICAgIm5hbWUiOiAiTEMwOC9QUkUvMDQ0LzAzNC9MQzgwNDQwMzQyMDE2MjU5TEdOMDAvTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX01UTC50eHQiLAogICAiYnVja2V0IjogImdjcC1wdWJsaWMtZGF0YS1sYW5kc2F0IiwKICAgImdlbmVyYXRpb24iOiAiMTQ3NTU5OTMyNzY2MjAwMCIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQyOjA3LjYxOFoiLAogICAidXBkYXRlZCI6ICIyMDE2LTEwLTA0VDE2OjQyOjA3LjYxOFoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTYtMTAtMDRUMTY6NDI6MDcuNjE4WiIsCiAgICJzaXplIjogIjc5MDMiLAogICAibWQ1SGFzaCI6ICJlbC9VZER2V1IwaGZpRWx2cmJCY1VRPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nY3AtcHVibGljLWRhdGEtbGFuZHNhdC9vL0xDMDglMkZQUkUlMkYwNDQlMkYwMzQlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDAlMkZMQzgwNDQwMzQyMDE2MjU5TEdOMDBfTVRMLnR4dD9nZW5lcmF0aW9uPTE0NzU1OTkzMjc2NjIwMDAmYWx0PW1lZGlhIiwKICAgImNyYzMyYyI6ICJQV0J0OGc9PSIsCiAgICJldGFnIjogIkNMQ2ZodTdMd2M4Q0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "764021e93620b9eb", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/noauth", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "da88ce6cc5bd0d1f42ba572cbbeef0df/9789914167716092271;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/noauth" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "247" + ], + "Content-Type": [ + "application/xml; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:44 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:44 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/4,/bns/xg/borg/xg/bns/blobstore2/bitpusher/39.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1E41W-vqE-Sx_QTLmYTIBg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/39.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/39:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoBoQW-kDuFdMDIZS3N-CMa3TEIZAnB5tIMoGs5CL9Hg_BrcexuTyRUbDXNnGGWNPhSrIhSyNkSf6sx93NaQn8yMcaTEQ" + ] + }, + "Body": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48RXJyb3I+PENvZGU+QWNjZXNzRGVuaWVkPC9Db2RlPjxNZXNzYWdlPkFjY2VzcyBkZW5pZWQuPC9NZXNzYWdlPjxEZXRhaWxzPkFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuZ2V0IGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLjwvRGV0YWlscz48L0Vycm9yPg==" + } + }, + { + "ID": "d2d396fad8aaf80a", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "multipart/related; boundary=e56b32213fdbed4ca1f1746d339782c9aee65039a09d8614685f16451c95" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fb0f5c917d126b8f95e3691e3b4fac93/10525284449909332606;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1lNTZiMzIyMTNmZGJlZDRjYTFmMTc0NmQzMzk3ODJjOWFlZTY1MDM5YTA5ZDg2MTQ2ODVmMTY0NTFjOTUNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJub2F1dGgifQoNCi0tZTU2YjMyMjEzZmRiZWQ0Y2ExZjE3NDZkMzM5NzgyYzlhZWU2NTAzOWEwOWQ4NjE0Njg1ZjE2NDUxYzk1DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW4NCg0KYg0KLS1lNTZiMzIyMTNmZGJlZDRjYTFmMTc0NmQzMzk3ODJjOWFlZTY1MDM5YTA5ZDg2MTQ2ODVmMTY0NTFjOTUtLQ0K" + }, + "Response": { + "StatusCode": 401, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "30809" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:44 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "Www-Authenticate": [ + "Bearer realm=\"https://accounts.google.com/\"" + ], + "X-Google-Backends": [ + "vrlk21:4108,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1E41W8bLJYy7N5zWgrgN" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/575:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "GgIYBiAB" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur7eHt_dtNs6w2VUmvmVVua67CJ0nu-Vna3XEcScfB8hski2ToweWug8lvPprHDjH4COQWQXI91genZjriTqHgFh_zkEuDKT_R626W4RtIHBw28etU" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAicmVxdWlyZWQiLAogICAgIm1lc3NhZ2UiOiAiQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguIiwKICAgICJsb2NhdGlvblR5cGUiOiAiaGVhZGVyIiwKICAgICJsb2NhdGlvbiI6ICJBdXRob3JpemF0aW9uIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1MT0dJTl9SRVFVSVJFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9Y29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRmF1bHQ6IEltbXV0YWJsZUVycm9yRGVmaW5pdGlvbntiYXNlPUxPR0lOX1JFUVVJUkVELCBjYXRlZ29yeT1VU0VSX0VSUk9SLCBjYXVzZT1udWxsLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz11bmF1dGhvcml6ZWQsIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLlJFUVVJUkVELCBjcmVhdGVkQnlCYWNrZW5kPXRydWUsIGRlYnVnTWVzc2FnZT1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9UkVRVUlSRUQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LmF1dGhlbnRpY2F0ZWRfdXNlciwgbWVzc2FnZT1Bbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC4sIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1oZWFkZXJzLkF1dGhvcml6YXRpb24sIG1lc3NhZ2U9QW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguLCByZWFzb249cmVxdWlyZWQsIHJwY0NvZGU9NDAxfSBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBkZWJ1Z0luZm89Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17V1dXLUF1dGhlbnRpY2F0ZT1bQmVhcmVyIHJlYWxtPVwiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL1wiXX0sIGh0dHBTdGF0dXM9dW5hdXRob3JpemVkLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5SRVFVSVJFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPVJFUVVJUkVELCBlcnJvclByb3RvRG9tYWluPWdkYXRhLkNvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5hdXRoZW50aWNhdGVkX3VzZXIsIG1lc3NhZ2U9QW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249aGVhZGVycy5BdXRob3JpemF0aW9uLCBtZXNzYWdlPUFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLiwgcmVhc29uPXJlcXVpcmVkLCBycGNDb2RlPTQwMX0gQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuYXV0aC5BdXRoZW50aWNhdG9ySW50ZXJjZXB0b3IuYWRkQ2hhbGxlbmdlSGVhZGVyKEF1dGhlbnRpY2F0b3JJbnRlcmNlcHRvci5qYXZhOjI2NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmF1dGguQXV0aGVudGljYXRvckludGVyY2VwdG9yLnByb2Nlc3NFcnJvclJlc3BvbnNlKEF1dGhlbnRpY2F0b3JJbnRlcmNlcHRvci5qYXZhOjIzMSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmF1dGguR2FpYU1pbnRJbnRlcmNlcHRvci5wcm9jZXNzRXJyb3JSZXNwb25zZShHYWlhTWludEludGVyY2VwdG9yLmphdmE6NzQ3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuQXJvdW5kSW50ZXJjZXB0b3JXcmFwcGVyLnByb2Nlc3NFcnJvclJlc3BvbnNlKEFyb3VuZEludGVyY2VwdG9yV3JhcHBlci5qYXZhOjI4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc3RhdHMuU3RhdHNCb290c3RyYXAkSW50ZXJjZXB0b3JTdGF0c1JlY29yZGVyLnByb2Nlc3NFcnJvclJlc3BvbnNlKFN0YXRzQm9vdHN0cmFwLmphdmE6MzEyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuSW50ZXJjZXB0aW9ucyRBcm91bmRJbnRlcmNlcHRpb24uaGFuZGxlRXJyb3JSZXNwb25zZShJbnRlcmNlcHRpb25zLmphdmE6MjAyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuSW50ZXJjZXB0aW9ucyRBcm91bmRJbnRlcmNlcHRpb24uYWNjZXNzJDIwMChJbnRlcmNlcHRpb25zLmphdmE6MTAzKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5pbnRlcmNlcHQuSW50ZXJjZXB0aW9ucyRBcm91bmRJbnRlcmNlcHRpb24kMS5jYWxsKEludGVyY2VwdGlvbnMuamF2YToxNDQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLmludGVyY2VwdC5JbnRlcmNlcHRpb25zJEFyb3VuZEludGVyY2VwdGlvbiQxLmNhbGwoSW50ZXJjZXB0aW9ucy5qYXZhOjEzNylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0RXhjZXB0aW9uKEFic3RyYWN0RnV0dXJlLmphdmE6NzE2KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjY4KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9TE9HSU5fUkVRVUlSRUQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjI3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6NjEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmluc2VydChPYmplY3RzRGVsZWdhdG9yLmphdmE6OTUpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPXVuYXV0aG9yaXplZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uUkVRVUlSRUQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBBbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkluc2VydE9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0T2JqZWN0LmphdmE6Mjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YTo2MSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuaW5zZXJ0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo5NSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBlcnJvclByb3RvQ29kZT1SRVFVSVJFRCwgZXJyb3JQcm90b0RvbWFpbj1nZGF0YS5Db3JlRXJyb3JEb21haW4sIGZpbHRlcmVkTWVzc2FnZT1udWxsLCBsb2NhdGlvbj1lbnRpdHkuYXV0aGVudGljYXRlZF91c2VyLCBtZXNzYWdlPUFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLiwgdW5uYW1lZEFyZ3VtZW50cz1bXX0sIGxvY2F0aW9uPWhlYWRlcnMuQXV0aG9yaXphdGlvbiwgbWVzc2FnZT1Bbm9ueW1vdXMgY2FsbGVyIGRvZXMgbm90IGhhdmUgc3RvcmFnZS5vYmplY3RzLmNyZWF0ZSBhY2Nlc3MgdG8gZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL25vYXV0aC4sIHJlYXNvbj1yZXF1aXJlZCwgcnBjQ29kZT00MDF9IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuSW5zZXJ0T2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRPYmplY3QuamF2YToyNzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5JbnNlcnRPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydE9iamVjdC5qYXZhOjYxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5pbnNlcnQoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjk1KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQW5vbnltb3VzIGNhbGxlciBkb2VzIG5vdCBoYXZlIHN0b3JhZ2Uub2JqZWN0cy5jcmVhdGUgYWNjZXNzIHRvIGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9ub2F1dGguXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdC4uLiAxOSBtb3JlXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDAxLAogICJtZXNzYWdlIjogIkFub255bW91cyBjYWxsZXIgZG9lcyBub3QgaGF2ZSBzdG9yYWdlLm9iamVjdHMuY3JlYXRlIGFjY2VzcyB0byBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvbm9hdXRoLiIKIH0KfQo=" + } + }, + { + "ID": "e75f23df89243dfc", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "797b24837800d6e63c59101acbe1e954/12140418383108201886;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "7903" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "\"7a5fd4743bd647485f88496fadb05c51\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 04 Oct 2016 16:42:07 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1475599327662000" + ], + "X-Goog-Hash": [ + "crc32c=PWBt8g==", + "md5=el/UdDvWR0hfiElvrbBcUQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "7903" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/9,/bns/xg/borg/xg/bns/blobstore2/bitpusher/10.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1E41W9utOuqy_QSHmIa4Dg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "570399209098" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/10.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/10:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrmIqndPdGu2TCqIhuZVI7wBHjGYcb5dgXBcYtUCAzOQUwcWVrz-dLXL9Scslzf6N9kLMj59YMl5y45RkLFfDeGKdbMsw" + ] + }, + "Body": "R1JPVVAgPSBMMV9NRVRBREFUQV9GSUxFCiAgR1JPVVAgPSBNRVRBREFUQV9GSUxFX0lORk8KICAgIE9SSUdJTiA9ICJJbWFnZSBjb3VydGVzeSBvZiB0aGUgVS5TLiBHZW9sb2dpY2FsIFN1cnZleSIKICAgIFJFUVVFU1RfSUQgPSAiMDcwMTYwOTE5MTA1MV8wMDAwNCIKICAgIExBTkRTQVRfU0NFTkVfSUQgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwIgogICAgRklMRV9EQVRFID0gMjAxNi0wOS0yMFQwMzoxMzowMloKICAgIFNUQVRJT05fSUQgPSAiTEdOIgogICAgUFJPQ0VTU0lOR19TT0ZUV0FSRV9WRVJTSU9OID0gIkxQR1NfMi42LjIiCiAgRU5EX0dST1VQID0gTUVUQURBVEFfRklMRV9JTkZPCiAgR1JPVVAgPSBQUk9EVUNUX01FVEFEQVRBCiAgICBEQVRBX1RZUEUgPSAiTDFUIgogICAgRUxFVkFUSU9OX1NPVVJDRSA9ICJHTFMyMDAwIgogICAgT1VUUFVUX0ZPUk1BVCA9ICJHRU9USUZGIgogICAgU1BBQ0VDUkFGVF9JRCA9ICJMQU5EU0FUXzgiCiAgICBTRU5TT1JfSUQgPSAiT0xJX1RJUlMiCiAgICBXUlNfUEFUSCA9IDQ0CiAgICBXUlNfUk9XID0gMzQKICAgIE5BRElSX09GRk5BRElSID0gIk5BRElSIgogICAgVEFSR0VUX1dSU19QQVRIID0gNDQKICAgIFRBUkdFVF9XUlNfUk9XID0gMzQKICAgIERBVEVfQUNRVUlSRUQgPSAyMDE2LTA5LTE1CiAgICBTQ0VORV9DRU5URVJfVElNRSA9ICIxODo0NjoxOC42ODY3MzgwWiIKICAgIENPUk5FUl9VTF9MQVRfUFJPRFVDVCA9IDM4LjUyODE5CiAgICBDT1JORVJfVUxfTE9OX1BST0RVQ1QgPSAtMTIzLjQwODQzCiAgICBDT1JORVJfVVJfTEFUX1BST0RVQ1QgPSAzOC41MDc2NQogICAgQ09STkVSX1VSX0xPTl9QUk9EVUNUID0gLTEyMC43NjkzMwogICAgQ09STkVSX0xMX0xBVF9QUk9EVUNUID0gMzYuNDE2MzMKICAgIENPUk5FUl9MTF9MT05fUFJPRFVDVCA9IC0xMjMuMzk3MDkKICAgIENPUk5FUl9MUl9MQVRfUFJPRFVDVCA9IDM2LjM5NzI5CiAgICBDT1JORVJfTFJfTE9OX1BST0RVQ1QgPSAtMTIwLjgzMTE3CiAgICBDT1JORVJfVUxfUFJPSkVDVElPTl9YX1BST0RVQ1QgPSA0NjQ0MDAuMDAwCiAgICBDT1JORVJfVUxfUFJPSkVDVElPTl9ZX1BST0RVQ1QgPSA0MjY0NTAwLjAwMAogICAgQ09STkVSX1VSX1BST0pFQ1RJT05fWF9QUk9EVUNUID0gNjk0NTAwLjAwMAogICAgQ09STkVSX1VSX1BST0pFQ1RJT05fWV9QUk9EVUNUID0gNDI2NDUwMC4wMDAKICAgIENPUk5FUl9MTF9QUk9KRUNUSU9OX1hfUFJPRFVDVCA9IDQ2NDQwMC4wMDAKICAgIENPUk5FUl9MTF9QUk9KRUNUSU9OX1lfUFJPRFVDVCA9IDQwMzAyMDAuMDAwCiAgICBDT1JORVJfTFJfUFJPSkVDVElPTl9YX1BST0RVQ1QgPSA2OTQ1MDAuMDAwCiAgICBDT1JORVJfTFJfUFJPSkVDVElPTl9ZX1BST0RVQ1QgPSA0MDMwMjAwLjAwMAogICAgUEFOQ0hST01BVElDX0xJTkVTID0gMTU2MjEKICAgIFBBTkNIUk9NQVRJQ19TQU1QTEVTID0gMTUzNDEKICAgIFJFRkxFQ1RJVkVfTElORVMgPSA3ODExCiAgICBSRUZMRUNUSVZFX1NBTVBMRVMgPSA3NjcxCiAgICBUSEVSTUFMX0xJTkVTID0gNzgxMQogICAgVEhFUk1BTF9TQU1QTEVTID0gNzY3MQogICAgRklMRV9OQU1FX0JBTkRfMSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjEuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMiA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjIuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMyA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjMuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjQuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjUuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNiA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjYuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNyA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjcuVElGIgogICAgRklMRV9OQU1FX0JBTkRfOCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjguVElGIgogICAgRklMRV9OQU1FX0JBTkRfOSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjkuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMTAgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IxMC5USUYiCiAgICBGSUxFX05BTUVfQkFORF8xMSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjExLlRJRiIKICAgIEZJTEVfTkFNRV9CQU5EX1FVQUxJVFkgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0JRQS5USUYiCiAgICBNRVRBREFUQV9GSUxFX05BTUUgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX01UTC50eHQiCiAgICBCUEZfTkFNRV9PTEkgPSAiTE84QlBGMjAxNjA5MTUxODMwNTdfMjAxNjA5MTUyMDA5NTAuMDEiCiAgICBCUEZfTkFNRV9USVJTID0gIkxUOEJQRjIwMTYwOTAyMDg0MTIyXzIwMTYwOTE3MDc0MDI3LjAyIgogICAgQ1BGX05BTUUgPSAiTDhDUEYyMDE2MDcwMV8yMDE2MDkzMC4wMiIKICAgIFJMVVRfRklMRV9OQU1FID0gIkw4UkxVVDIwMTUwMzAzXzIwNDMxMjMxdjExLmg1IgogIEVORF9HUk9VUCA9IFBST0RVQ1RfTUVUQURBVEEKICBHUk9VUCA9IElNQUdFX0FUVFJJQlVURVMKICAgIENMT1VEX0NPVkVSID0gMjkuNTYKICAgIENMT1VEX0NPVkVSX0xBTkQgPSAzLjMzCiAgICBJTUFHRV9RVUFMSVRZX09MSSA9IDkKICAgIElNQUdFX1FVQUxJVFlfVElSUyA9IDkKICAgIFRJUlNfU1NNX01PREVMID0gIkZJTkFMIgogICAgVElSU19TU01fUE9TSVRJT05fU1RBVFVTID0gIkVTVElNQVRFRCIKICAgIFJPTExfQU5HTEUgPSAtMC4wMDEKICAgIFNVTl9BWklNVVRIID0gMTQ4LjQ4MDQ5Mzk2CiAgICBTVU5fRUxFVkFUSU9OID0gNTAuOTM3NjgzOTkKICAgIEVBUlRIX1NVTl9ESVNUQU5DRSA9IDEuMDA1Mzc1MgogICAgR1JPVU5EX0NPTlRST0xfUE9JTlRTX1ZFUlNJT04gPSA0CiAgICBHUk9VTkRfQ09OVFJPTF9QT0lOVFNfTU9ERUwgPSA1NDgKICAgIEdFT01FVFJJQ19STVNFX01PREVMID0gNS44NTcKICAgIEdFT01FVFJJQ19STVNFX01PREVMX1kgPSAzLjg0MQogICAgR0VPTUVUUklDX1JNU0VfTU9ERUxfWCA9IDQuNDIyCiAgICBHUk9VTkRfQ09OVFJPTF9QT0lOVFNfVkVSSUZZID0gMjI4CiAgICBHRU9NRVRSSUNfUk1TRV9WRVJJRlkgPSAzLjM4MgogIEVORF9HUk9VUCA9IElNQUdFX0FUVFJJQlVURVMKICBHUk9VUCA9IE1JTl9NQVhfUkFESUFOQ0UKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF8xID0gNzUxLjk1NzA5CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfMSA9IC02Mi4wOTY4NgogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzIgPSA3NzAuMDEzMTgKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8yID0gLTYzLjU4Nzk0CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDcwOS41NjA2MQogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzMgPSAtNTguNTk1NzUKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF80ID0gNTk4LjM0MTQ5CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNCA9IC00OS40MTEyMwogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzUgPSAzNjYuMTU1MTUKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF81ID0gLTMwLjIzNzIxCiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDkxLjA1OTQ2CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNiA9IC03LjUxOTcyCiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfNyA9IDMwLjY5MTkxCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNyA9IC0yLjUzNDU1CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfOCA9IDY3Ny4xNTc4NAogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzggPSAtNTUuOTE5OTIKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF85ID0gMTQzLjEwMTczCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfOSA9IC0xMS44MTczOQogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzEwID0gMjIuMDAxODAKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8xMCA9IDAuMTAwMzMKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF8xMSA9IDIyLjAwMTgwCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfMTEgPSAwLjEwMDMzCiAgRU5EX0dST1VQID0gTUlOX01BWF9SQURJQU5DRQogIEdST1VQID0gTUlOX01BWF9SRUZMRUNUQU5DRQogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzEgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzEgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF8yID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF8yID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfMyA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzQgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzQgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF81ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF81ID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfNiA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzcgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzcgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF84ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF84ID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfOSA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfOSA9IC0wLjA5OTk4MAogIEVORF9HUk9VUCA9IE1JTl9NQVhfUkVGTEVDVEFOQ0UKICBHUk9VUCA9IE1JTl9NQVhfUElYRUxfVkFMVUUKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8xID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF8xID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzIgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzIgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfMyA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMyA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF80ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF80ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzUgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzUgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfNiA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfNiA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF83ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF83ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzggPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzggPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfOSA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfOSA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8xMCA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMTAgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfMTEgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzExID0gMQogIEVORF9HUk9VUCA9IE1JTl9NQVhfUElYRUxfVkFMVUUKICBHUk9VUCA9IFJBRElPTUVUUklDX1JFU0NBTElORwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzEgPSAxLjI0MjJFLTAyCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfMiA9IDEuMjcyMEUtMDIKICAgIFJBRElBTkNFX01VTFRfQkFORF8zID0gMS4xNzIxRS0wMgogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzQgPSA5Ljg4NDJFLTAzCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfNSA9IDYuMDQ4N0UtMDMKICAgIFJBRElBTkNFX01VTFRfQkFORF82ID0gMS41MDQyRS0wMwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzcgPSA1LjA3MDFFLTA0CiAgICBSQURJQU5DRV9NVUxUX0JBTkRfOCA9IDEuMTE4NkUtMDIKICAgIFJBRElBTkNFX01VTFRfQkFORF85ID0gMi4zNjQwRS0wMwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzEwID0gMy4zNDIwRS0wNAogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzExID0gMy4zNDIwRS0wNAogICAgUkFESUFOQ0VfQUREX0JBTkRfMSA9IC02Mi4xMDkyOAogICAgUkFESUFOQ0VfQUREX0JBTkRfMiA9IC02My42MDA2NgogICAgUkFESUFOQ0VfQUREX0JBTkRfMyA9IC01OC42MDc0NwogICAgUkFESUFOQ0VfQUREX0JBTkRfNCA9IC00OS40MjExMgogICAgUkFESUFOQ0VfQUREX0JBTkRfNSA9IC0zMC4yNDMyNgogICAgUkFESUFOQ0VfQUREX0JBTkRfNiA9IC03LjUyMTIyCiAgICBSQURJQU5DRV9BRERfQkFORF83ID0gLTIuNTM1MDUKICAgIFJBRElBTkNFX0FERF9CQU5EXzggPSAtNTUuOTMxMTAKICAgIFJBRElBTkNFX0FERF9CQU5EXzkgPSAtMTEuODE5NzUKICAgIFJBRElBTkNFX0FERF9CQU5EXzEwID0gMC4xMDAwMAogICAgUkFESUFOQ0VfQUREX0JBTkRfMTEgPSAwLjEwMDAwCiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfMSA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF8yID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzMgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfNCA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF81ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzYgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfNyA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF84ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzkgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8xID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8yID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8zID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF80ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF81ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF82ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF83ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF84ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF85ID0gLTAuMTAwMDAwCiAgRU5EX0dST1VQID0gUkFESU9NRVRSSUNfUkVTQ0FMSU5HCiAgR1JPVVAgPSBUSVJTX1RIRVJNQUxfQ09OU1RBTlRTCiAgICBLMV9DT05TVEFOVF9CQU5EXzEwID0gNzc0Ljg4NTMKICAgIEsxX0NPTlNUQU5UX0JBTkRfMTEgPSA0ODAuODg4MwogICAgSzJfQ09OU1RBTlRfQkFORF8xMCA9IDEzMjEuMDc4OQogICAgSzJfQ09OU1RBTlRfQkFORF8xMSA9IDEyMDEuMTQ0MgogIEVORF9HUk9VUCA9IFRJUlNfVEhFUk1BTF9DT05TVEFOVFMKICBHUk9VUCA9IFBST0pFQ1RJT05fUEFSQU1FVEVSUwogICAgTUFQX1BST0pFQ1RJT04gPSAiVVRNIgogICAgREFUVU0gPSAiV0dTODQiCiAgICBFTExJUFNPSUQgPSAiV0dTODQiCiAgICBVVE1fWk9ORSA9IDEwCiAgICBHUklEX0NFTExfU0laRV9QQU5DSFJPTUFUSUMgPSAxNS4wMAogICAgR1JJRF9DRUxMX1NJWkVfUkVGTEVDVElWRSA9IDMwLjAwCiAgICBHUklEX0NFTExfU0laRV9USEVSTUFMID0gMzAuMDAKICAgIE9SSUVOVEFUSU9OID0gIk5PUlRIX1VQIgogICAgUkVTQU1QTElOR19PUFRJT04gPSAiQ1VCSUNfQ09OVk9MVVRJT04iCiAgRU5EX0dST1VQID0gUFJPSkVDVElPTl9QQVJBTUVURVJTCkVORF9HUk9VUCA9IEwxX01FVEFEQVRBX0ZJTEUKRU5ECg==" + } + }, + { + "ID": "24893384f5f5f4b6", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7677b42b76ca1c3f25515d3073059443/13755553415801987261;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "7903" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "\"7a5fd4743bd647485f88496fadb05c51\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 04 Oct 2016 16:42:07 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1475599327662000" + ], + "X-Goog-Hash": [ + "crc32c=PWBt8g==", + "md5=el/UdDvWR0hfiElvrbBcUQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "7903" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/64,/bns/xg/borg/xg/bns/blobstore2/bitpusher/40.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W7DOAue3_QS0wLWABw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "570399209098" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/40.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/40:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpsxU6E96rFeQIdjKxqWIMvOLq1qwVeADCBNGKaF3h2uHyIycPME7UeWBscS7gYtJaBR7iid4ZqEWrTTltQivfD_IN-dg" + ] + }, + "Body": "R1JPVVAgPSBMMV9NRVRBREFUQV9GSUxFCiAgR1JPVVAgPSBNRVRBREFUQV9GSUxFX0lORk8KICAgIE9SSUdJTiA9ICJJbWFnZSBjb3VydGVzeSBvZiB0aGUgVS5TLiBHZW9sb2dpY2FsIFN1cnZleSIKICAgIFJFUVVFU1RfSUQgPSAiMDcwMTYwOTE5MTA1MV8wMDAwNCIKICAgIExBTkRTQVRfU0NFTkVfSUQgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwIgogICAgRklMRV9EQVRFID0gMjAxNi0wOS0yMFQwMzoxMzowMloKICAgIFNUQVRJT05fSUQgPSAiTEdOIgogICAgUFJPQ0VTU0lOR19TT0ZUV0FSRV9WRVJTSU9OID0gIkxQR1NfMi42LjIiCiAgRU5EX0dST1VQID0gTUVUQURBVEFfRklMRV9JTkZPCiAgR1JPVVAgPSBQUk9EVUNUX01FVEFEQVRBCiAgICBEQVRBX1RZUEUgPSAiTDFUIgogICAgRUxFVkFUSU9OX1NPVVJDRSA9ICJHTFMyMDAwIgogICAgT1VUUFVUX0ZPUk1BVCA9ICJHRU9USUZGIgogICAgU1BBQ0VDUkFGVF9JRCA9ICJMQU5EU0FUXzgiCiAgICBTRU5TT1JfSUQgPSAiT0xJX1RJUlMiCiAgICBXUlNfUEFUSCA9IDQ0CiAgICBXUlNfUk9XID0gMzQKICAgIE5BRElSX09GRk5BRElSID0gIk5BRElSIgogICAgVEFSR0VUX1dSU19QQVRIID0gNDQKICAgIFRBUkdFVF9XUlNfUk9XID0gMzQKICAgIERBVEVfQUNRVUlSRUQgPSAyMDE2LTA5LTE1CiAgICBTQ0VORV9DRU5URVJfVElNRSA9ICIxODo0NjoxOC42ODY3MzgwWiIKICAgIENPUk5FUl9VTF9MQVRfUFJPRFVDVCA9IDM4LjUyODE5CiAgICBDT1JORVJfVUxfTE9OX1BST0RVQ1QgPSAtMTIzLjQwODQzCiAgICBDT1JORVJfVVJfTEFUX1BST0RVQ1QgPSAzOC41MDc2NQogICAgQ09STkVSX1VSX0xPTl9QUk9EVUNUID0gLTEyMC43NjkzMwogICAgQ09STkVSX0xMX0xBVF9QUk9EVUNUID0gMzYuNDE2MzMKICAgIENPUk5FUl9MTF9MT05fUFJPRFVDVCA9IC0xMjMuMzk3MDkKICAgIENPUk5FUl9MUl9MQVRfUFJPRFVDVCA9IDM2LjM5NzI5CiAgICBDT1JORVJfTFJfTE9OX1BST0RVQ1QgPSAtMTIwLjgzMTE3CiAgICBDT1JORVJfVUxfUFJPSkVDVElPTl9YX1BST0RVQ1QgPSA0NjQ0MDAuMDAwCiAgICBDT1JORVJfVUxfUFJPSkVDVElPTl9ZX1BST0RVQ1QgPSA0MjY0NTAwLjAwMAogICAgQ09STkVSX1VSX1BST0pFQ1RJT05fWF9QUk9EVUNUID0gNjk0NTAwLjAwMAogICAgQ09STkVSX1VSX1BST0pFQ1RJT05fWV9QUk9EVUNUID0gNDI2NDUwMC4wMDAKICAgIENPUk5FUl9MTF9QUk9KRUNUSU9OX1hfUFJPRFVDVCA9IDQ2NDQwMC4wMDAKICAgIENPUk5FUl9MTF9QUk9KRUNUSU9OX1lfUFJPRFVDVCA9IDQwMzAyMDAuMDAwCiAgICBDT1JORVJfTFJfUFJPSkVDVElPTl9YX1BST0RVQ1QgPSA2OTQ1MDAuMDAwCiAgICBDT1JORVJfTFJfUFJPSkVDVElPTl9ZX1BST0RVQ1QgPSA0MDMwMjAwLjAwMAogICAgUEFOQ0hST01BVElDX0xJTkVTID0gMTU2MjEKICAgIFBBTkNIUk9NQVRJQ19TQU1QTEVTID0gMTUzNDEKICAgIFJFRkxFQ1RJVkVfTElORVMgPSA3ODExCiAgICBSRUZMRUNUSVZFX1NBTVBMRVMgPSA3NjcxCiAgICBUSEVSTUFMX0xJTkVTID0gNzgxMQogICAgVEhFUk1BTF9TQU1QTEVTID0gNzY3MQogICAgRklMRV9OQU1FX0JBTkRfMSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjEuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMiA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjIuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMyA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjMuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjQuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjUuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNiA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjYuVElGIgogICAgRklMRV9OQU1FX0JBTkRfNyA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjcuVElGIgogICAgRklMRV9OQU1FX0JBTkRfOCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjguVElGIgogICAgRklMRV9OQU1FX0JBTkRfOSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjkuVElGIgogICAgRklMRV9OQU1FX0JBTkRfMTAgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0IxMC5USUYiCiAgICBGSUxFX05BTUVfQkFORF8xMSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjExLlRJRiIKICAgIEZJTEVfTkFNRV9CQU5EX1FVQUxJVFkgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX0JRQS5USUYiCiAgICBNRVRBREFUQV9GSUxFX05BTUUgPSAiTEM4MDQ0MDM0MjAxNjI1OUxHTjAwX01UTC50eHQiCiAgICBCUEZfTkFNRV9PTEkgPSAiTE84QlBGMjAxNjA5MTUxODMwNTdfMjAxNjA5MTUyMDA5NTAuMDEiCiAgICBCUEZfTkFNRV9USVJTID0gIkxUOEJQRjIwMTYwOTAyMDg0MTIyXzIwMTYwOTE3MDc0MDI3LjAyIgogICAgQ1BGX05BTUUgPSAiTDhDUEYyMDE2MDcwMV8yMDE2MDkzMC4wMiIKICAgIFJMVVRfRklMRV9OQU1FID0gIkw4UkxVVDIwMTUwMzAzXzIwNDMxMjMxdjExLmg1IgogIEVORF9HUk9VUCA9IFBST0RVQ1RfTUVUQURBVEEKICBHUk9VUCA9IElNQUdFX0FUVFJJQlVURVMKICAgIENMT1VEX0NPVkVSID0gMjkuNTYKICAgIENMT1VEX0NPVkVSX0xBTkQgPSAzLjMzCiAgICBJTUFHRV9RVUFMSVRZX09MSSA9IDkKICAgIElNQUdFX1FVQUxJVFlfVElSUyA9IDkKICAgIFRJUlNfU1NNX01PREVMID0gIkZJTkFMIgogICAgVElSU19TU01fUE9TSVRJT05fU1RBVFVTID0gIkVTVElNQVRFRCIKICAgIFJPTExfQU5HTEUgPSAtMC4wMDEKICAgIFNVTl9BWklNVVRIID0gMTQ4LjQ4MDQ5Mzk2CiAgICBTVU5fRUxFVkFUSU9OID0gNTAuOTM3NjgzOTkKICAgIEVBUlRIX1NVTl9ESVNUQU5DRSA9IDEuMDA1Mzc1MgogICAgR1JPVU5EX0NPTlRST0xfUE9JTlRTX1ZFUlNJT04gPSA0CiAgICBHUk9VTkRfQ09OVFJPTF9QT0lOVFNfTU9ERUwgPSA1NDgKICAgIEdFT01FVFJJQ19STVNFX01PREVMID0gNS44NTcKICAgIEdFT01FVFJJQ19STVNFX01PREVMX1kgPSAzLjg0MQogICAgR0VPTUVUUklDX1JNU0VfTU9ERUxfWCA9IDQuNDIyCiAgICBHUk9VTkRfQ09OVFJPTF9QT0lOVFNfVkVSSUZZID0gMjI4CiAgICBHRU9NRVRSSUNfUk1TRV9WRVJJRlkgPSAzLjM4MgogIEVORF9HUk9VUCA9IElNQUdFX0FUVFJJQlVURVMKICBHUk9VUCA9IE1JTl9NQVhfUkFESUFOQ0UKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF8xID0gNzUxLjk1NzA5CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfMSA9IC02Mi4wOTY4NgogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzIgPSA3NzAuMDEzMTgKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8yID0gLTYzLjU4Nzk0CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDcwOS41NjA2MQogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzMgPSAtNTguNTk1NzUKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF80ID0gNTk4LjM0MTQ5CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNCA9IC00OS40MTEyMwogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzUgPSAzNjYuMTU1MTUKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF81ID0gLTMwLjIzNzIxCiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDkxLjA1OTQ2CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNiA9IC03LjUxOTcyCiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfNyA9IDMwLjY5MTkxCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfNyA9IC0yLjUzNDU1CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfOCA9IDY3Ny4xNTc4NAogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzggPSAtNTUuOTE5OTIKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF85ID0gMTQzLjEwMTczCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfOSA9IC0xMS44MTczOQogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzEwID0gMjIuMDAxODAKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8xMCA9IDAuMTAwMzMKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF8xMSA9IDIyLjAwMTgwCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfMTEgPSAwLjEwMDMzCiAgRU5EX0dST1VQID0gTUlOX01BWF9SQURJQU5DRQogIEdST1VQID0gTUlOX01BWF9SRUZMRUNUQU5DRQogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzEgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzEgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF8yID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF8yID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfMyA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzQgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzQgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF81ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF81ID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfNiA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzcgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzcgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF84ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF84ID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfOSA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfOSA9IC0wLjA5OTk4MAogIEVORF9HUk9VUCA9IE1JTl9NQVhfUkVGTEVDVEFOQ0UKICBHUk9VUCA9IE1JTl9NQVhfUElYRUxfVkFMVUUKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8xID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF8xID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzIgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzIgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfMyA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMyA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF80ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF80ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzUgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzUgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfNiA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfNiA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF83ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF83ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzggPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzggPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfOSA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfOSA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8xMCA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMTAgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfMTEgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzExID0gMQogIEVORF9HUk9VUCA9IE1JTl9NQVhfUElYRUxfVkFMVUUKICBHUk9VUCA9IFJBRElPTUVUUklDX1JFU0NBTElORwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzEgPSAxLjI0MjJFLTAyCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfMiA9IDEuMjcyMEUtMDIKICAgIFJBRElBTkNFX01VTFRfQkFORF8zID0gMS4xNzIxRS0wMgogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzQgPSA5Ljg4NDJFLTAzCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfNSA9IDYuMDQ4N0UtMDMKICAgIFJBRElBTkNFX01VTFRfQkFORF82ID0gMS41MDQyRS0wMwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzcgPSA1LjA3MDFFLTA0CiAgICBSQURJQU5DRV9NVUxUX0JBTkRfOCA9IDEuMTE4NkUtMDIKICAgIFJBRElBTkNFX01VTFRfQkFORF85ID0gMi4zNjQwRS0wMwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzEwID0gMy4zNDIwRS0wNAogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzExID0gMy4zNDIwRS0wNAogICAgUkFESUFOQ0VfQUREX0JBTkRfMSA9IC02Mi4xMDkyOAogICAgUkFESUFOQ0VfQUREX0JBTkRfMiA9IC02My42MDA2NgogICAgUkFESUFOQ0VfQUREX0JBTkRfMyA9IC01OC42MDc0NwogICAgUkFESUFOQ0VfQUREX0JBTkRfNCA9IC00OS40MjExMgogICAgUkFESUFOQ0VfQUREX0JBTkRfNSA9IC0zMC4yNDMyNgogICAgUkFESUFOQ0VfQUREX0JBTkRfNiA9IC03LjUyMTIyCiAgICBSQURJQU5DRV9BRERfQkFORF83ID0gLTIuNTM1MDUKICAgIFJBRElBTkNFX0FERF9CQU5EXzggPSAtNTUuOTMxMTAKICAgIFJBRElBTkNFX0FERF9CQU5EXzkgPSAtMTEuODE5NzUKICAgIFJBRElBTkNFX0FERF9CQU5EXzEwID0gMC4xMDAwMAogICAgUkFESUFOQ0VfQUREX0JBTkRfMTEgPSAwLjEwMDAwCiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfMSA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF8yID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzMgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfNCA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF81ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzYgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfNyA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF84ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzkgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8xID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8yID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF8zID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF80ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF81ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF82ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF83ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF84ID0gLTAuMTAwMDAwCiAgICBSRUZMRUNUQU5DRV9BRERfQkFORF85ID0gLTAuMTAwMDAwCiAgRU5EX0dST1VQID0gUkFESU9NRVRSSUNfUkVTQ0FMSU5HCiAgR1JPVVAgPSBUSVJTX1RIRVJNQUxfQ09OU1RBTlRTCiAgICBLMV9DT05TVEFOVF9CQU5EXzEwID0gNzc0Ljg4NTMKICAgIEsxX0NPTlNUQU5UX0JBTkRfMTEgPSA0ODAuODg4MwogICAgSzJfQ09OU1RBTlRfQkFORF8xMCA9IDEzMjEuMDc4OQogICAgSzJfQ09OU1RBTlRfQkFORF8xMSA9IDEyMDEuMTQ0MgogIEVORF9HUk9VUCA9IFRJUlNfVEhFUk1BTF9DT05TVEFOVFMKICBHUk9VUCA9IFBST0pFQ1RJT05fUEFSQU1FVEVSUwogICAgTUFQX1BST0pFQ1RJT04gPSAiVVRNIgogICAgREFUVU0gPSAiV0dTODQiCiAgICBFTExJUFNPSUQgPSAiV0dTODQiCiAgICBVVE1fWk9ORSA9IDEwCiAgICBHUklEX0NFTExfU0laRV9QQU5DSFJPTUFUSUMgPSAxNS4wMAogICAgR1JJRF9DRUxMX1NJWkVfUkVGTEVDVElWRSA9IDMwLjAwCiAgICBHUklEX0NFTExfU0laRV9USEVSTUFMID0gMzAuMDAKICAgIE9SSUVOVEFUSU9OID0gIk5PUlRIX1VQIgogICAgUkVTQU1QTElOR19PUFRJT04gPSAiQ1VCSUNfQ09OVk9MVVRJT04iCiAgRU5EX0dST1VQID0gUFJPSkVDVElPTl9QQVJBTUVURVJTCkVORF9HUk9VUCA9IEwxX01FVEFEQVRBX0ZJTEUKRU5ECg==" + } + }, + { + "ID": "cfb3645473b7cc10", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt", + "Proto": "HTTP/1.1", + "Header": { + "Range": [ + "bytes=1-" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8361cfd1b52fad7e007f8adcab97a388/15298630854474556381;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 206, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "7902" + ], + "Content-Range": [ + "bytes 1-7902/7903" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "\"7a5fd4743bd647485f88496fadb05c51\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 04 Oct 2016 16:42:07 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1475599327662000" + ], + "X-Goog-Hash": [ + "crc32c=PWBt8g==", + "md5=el/UdDvWR0hfiElvrbBcUQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "7903" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/9,/bns/xg/borg/xg/bns/blobstore2/bitpusher/105.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W5H9Bou1_QScq6bYDA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "570399209098" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/105.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/105:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqcXPD8mn_2Hnw7NABsHk_ITjMI3csvpn1MJdTSfq7aAF-R21IjXbmLq7Cq3n7XtYQ-Q7o8EG5CLkjb8i7ORy4ZGV7-bQ" + ] + }, + "Body": "Uk9VUCA9IEwxX01FVEFEQVRBX0ZJTEUKICBHUk9VUCA9IE1FVEFEQVRBX0ZJTEVfSU5GTwogICAgT1JJR0lOID0gIkltYWdlIGNvdXJ0ZXN5IG9mIHRoZSBVLlMuIEdlb2xvZ2ljYWwgU3VydmV5IgogICAgUkVRVUVTVF9JRCA9ICIwNzAxNjA5MTkxMDUxXzAwMDA0IgogICAgTEFORFNBVF9TQ0VORV9JRCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDAiCiAgICBGSUxFX0RBVEUgPSAyMDE2LTA5LTIwVDAzOjEzOjAyWgogICAgU1RBVElPTl9JRCA9ICJMR04iCiAgICBQUk9DRVNTSU5HX1NPRlRXQVJFX1ZFUlNJT04gPSAiTFBHU18yLjYuMiIKICBFTkRfR1JPVVAgPSBNRVRBREFUQV9GSUxFX0lORk8KICBHUk9VUCA9IFBST0RVQ1RfTUVUQURBVEEKICAgIERBVEFfVFlQRSA9ICJMMVQiCiAgICBFTEVWQVRJT05fU09VUkNFID0gIkdMUzIwMDAiCiAgICBPVVRQVVRfRk9STUFUID0gIkdFT1RJRkYiCiAgICBTUEFDRUNSQUZUX0lEID0gIkxBTkRTQVRfOCIKICAgIFNFTlNPUl9JRCA9ICJPTElfVElSUyIKICAgIFdSU19QQVRIID0gNDQKICAgIFdSU19ST1cgPSAzNAogICAgTkFESVJfT0ZGTkFESVIgPSAiTkFESVIiCiAgICBUQVJHRVRfV1JTX1BBVEggPSA0NAogICAgVEFSR0VUX1dSU19ST1cgPSAzNAogICAgREFURV9BQ1FVSVJFRCA9IDIwMTYtMDktMTUKICAgIFNDRU5FX0NFTlRFUl9USU1FID0gIjE4OjQ2OjE4LjY4NjczODBaIgogICAgQ09STkVSX1VMX0xBVF9QUk9EVUNUID0gMzguNTI4MTkKICAgIENPUk5FUl9VTF9MT05fUFJPRFVDVCA9IC0xMjMuNDA4NDMKICAgIENPUk5FUl9VUl9MQVRfUFJPRFVDVCA9IDM4LjUwNzY1CiAgICBDT1JORVJfVVJfTE9OX1BST0RVQ1QgPSAtMTIwLjc2OTMzCiAgICBDT1JORVJfTExfTEFUX1BST0RVQ1QgPSAzNi40MTYzMwogICAgQ09STkVSX0xMX0xPTl9QUk9EVUNUID0gLTEyMy4zOTcwOQogICAgQ09STkVSX0xSX0xBVF9QUk9EVUNUID0gMzYuMzk3MjkKICAgIENPUk5FUl9MUl9MT05fUFJPRFVDVCA9IC0xMjAuODMxMTcKICAgIENPUk5FUl9VTF9QUk9KRUNUSU9OX1hfUFJPRFVDVCA9IDQ2NDQwMC4wMDAKICAgIENPUk5FUl9VTF9QUk9KRUNUSU9OX1lfUFJPRFVDVCA9IDQyNjQ1MDAuMDAwCiAgICBDT1JORVJfVVJfUFJPSkVDVElPTl9YX1BST0RVQ1QgPSA2OTQ1MDAuMDAwCiAgICBDT1JORVJfVVJfUFJPSkVDVElPTl9ZX1BST0RVQ1QgPSA0MjY0NTAwLjAwMAogICAgQ09STkVSX0xMX1BST0pFQ1RJT05fWF9QUk9EVUNUID0gNDY0NDAwLjAwMAogICAgQ09STkVSX0xMX1BST0pFQ1RJT05fWV9QUk9EVUNUID0gNDAzMDIwMC4wMDAKICAgIENPUk5FUl9MUl9QUk9KRUNUSU9OX1hfUFJPRFVDVCA9IDY5NDUwMC4wMDAKICAgIENPUk5FUl9MUl9QUk9KRUNUSU9OX1lfUFJPRFVDVCA9IDQwMzAyMDAuMDAwCiAgICBQQU5DSFJPTUFUSUNfTElORVMgPSAxNTYyMQogICAgUEFOQ0hST01BVElDX1NBTVBMRVMgPSAxNTM0MQogICAgUkVGTEVDVElWRV9MSU5FUyA9IDc4MTEKICAgIFJFRkxFQ1RJVkVfU0FNUExFUyA9IDc2NzEKICAgIFRIRVJNQUxfTElORVMgPSA3ODExCiAgICBUSEVSTUFMX1NBTVBMRVMgPSA3NjcxCiAgICBGSUxFX05BTUVfQkFORF8xID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CMS5USUYiCiAgICBGSUxFX05BTUVfQkFORF8yID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CMi5USUYiCiAgICBGSUxFX05BTUVfQkFORF8zID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CMy5USUYiCiAgICBGSUxFX05BTUVfQkFORF80ID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CNC5USUYiCiAgICBGSUxFX05BTUVfQkFORF81ID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CNS5USUYiCiAgICBGSUxFX05BTUVfQkFORF82ID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CNi5USUYiCiAgICBGSUxFX05BTUVfQkFORF83ID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CNy5USUYiCiAgICBGSUxFX05BTUVfQkFORF84ID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9COC5USUYiCiAgICBGSUxFX05BTUVfQkFORF85ID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9COS5USUYiCiAgICBGSUxFX05BTUVfQkFORF8xMCA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQjEwLlRJRiIKICAgIEZJTEVfTkFNRV9CQU5EXzExID0gIkxDODA0NDAzNDIwMTYyNTlMR04wMF9CMTEuVElGIgogICAgRklMRV9OQU1FX0JBTkRfUVVBTElUWSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfQlFBLlRJRiIKICAgIE1FVEFEQVRBX0ZJTEVfTkFNRSA9ICJMQzgwNDQwMzQyMDE2MjU5TEdOMDBfTVRMLnR4dCIKICAgIEJQRl9OQU1FX09MSSA9ICJMTzhCUEYyMDE2MDkxNTE4MzA1N18yMDE2MDkxNTIwMDk1MC4wMSIKICAgIEJQRl9OQU1FX1RJUlMgPSAiTFQ4QlBGMjAxNjA5MDIwODQxMjJfMjAxNjA5MTcwNzQwMjcuMDIiCiAgICBDUEZfTkFNRSA9ICJMOENQRjIwMTYwNzAxXzIwMTYwOTMwLjAyIgogICAgUkxVVF9GSUxFX05BTUUgPSAiTDhSTFVUMjAxNTAzMDNfMjA0MzEyMzF2MTEuaDUiCiAgRU5EX0dST1VQID0gUFJPRFVDVF9NRVRBREFUQQogIEdST1VQID0gSU1BR0VfQVRUUklCVVRFUwogICAgQ0xPVURfQ09WRVIgPSAyOS41NgogICAgQ0xPVURfQ09WRVJfTEFORCA9IDMuMzMKICAgIElNQUdFX1FVQUxJVFlfT0xJID0gOQogICAgSU1BR0VfUVVBTElUWV9USVJTID0gOQogICAgVElSU19TU01fTU9ERUwgPSAiRklOQUwiCiAgICBUSVJTX1NTTV9QT1NJVElPTl9TVEFUVVMgPSAiRVNUSU1BVEVEIgogICAgUk9MTF9BTkdMRSA9IC0wLjAwMQogICAgU1VOX0FaSU1VVEggPSAxNDguNDgwNDkzOTYKICAgIFNVTl9FTEVWQVRJT04gPSA1MC45Mzc2ODM5OQogICAgRUFSVEhfU1VOX0RJU1RBTkNFID0gMS4wMDUzNzUyCiAgICBHUk9VTkRfQ09OVFJPTF9QT0lOVFNfVkVSU0lPTiA9IDQKICAgIEdST1VORF9DT05UUk9MX1BPSU5UU19NT0RFTCA9IDU0OAogICAgR0VPTUVUUklDX1JNU0VfTU9ERUwgPSA1Ljg1NwogICAgR0VPTUVUUklDX1JNU0VfTU9ERUxfWSA9IDMuODQxCiAgICBHRU9NRVRSSUNfUk1TRV9NT0RFTF9YID0gNC40MjIKICAgIEdST1VORF9DT05UUk9MX1BPSU5UU19WRVJJRlkgPSAyMjgKICAgIEdFT01FVFJJQ19STVNFX1ZFUklGWSA9IDMuMzgyCiAgRU5EX0dST1VQID0gSU1BR0VfQVRUUklCVVRFUwogIEdST1VQID0gTUlOX01BWF9SQURJQU5DRQogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzEgPSA3NTEuOTU3MDkKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8xID0gLTYyLjA5Njg2CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfMiA9IDc3MC4wMTMxOAogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzIgPSAtNjMuNTg3OTQKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF8zID0gNzA5LjU2MDYxCiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfMyA9IC01OC41OTU3NQogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzQgPSA1OTguMzQxNDkKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF80ID0gLTQ5LjQxMTIzCiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfNSA9IDM2Ni4xNTUxNQogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzUgPSAtMzAuMjM3MjEKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF82ID0gOTEuMDU5NDYKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF82ID0gLTcuNTE5NzIKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF83ID0gMzAuNjkxOTEKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF83ID0gLTIuNTM0NTUKICAgIFJBRElBTkNFX01BWElNVU1fQkFORF84ID0gNjc3LjE1Nzg0CiAgICBSQURJQU5DRV9NSU5JTVVNX0JBTkRfOCA9IC01NS45MTk5MgogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzkgPSAxNDMuMTAxNzMKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF85ID0gLTExLjgxNzM5CiAgICBSQURJQU5DRV9NQVhJTVVNX0JBTkRfMTAgPSAyMi4wMDE4MAogICAgUkFESUFOQ0VfTUlOSU1VTV9CQU5EXzEwID0gMC4xMDAzMwogICAgUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzExID0gMjIuMDAxODAKICAgIFJBRElBTkNFX01JTklNVU1fQkFORF8xMSA9IDAuMTAwMzMKICBFTkRfR1JPVVAgPSBNSU5fTUFYX1JBRElBTkNFCiAgR1JPVVAgPSBNSU5fTUFYX1JFRkxFQ1RBTkNFCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfMSA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfMSA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzIgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzIgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF8zID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF8zID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNCA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfNCA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzUgPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzUgPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF82ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF82ID0gLTAuMDk5OTgwCiAgICBSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNyA9IDEuMjEwNzAwCiAgICBSRUZMRUNUQU5DRV9NSU5JTVVNX0JBTkRfNyA9IC0wLjA5OTk4MAogICAgUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzggPSAxLjIxMDcwMAogICAgUkVGTEVDVEFOQ0VfTUlOSU1VTV9CQU5EXzggPSAtMC4wOTk5ODAKICAgIFJFRkxFQ1RBTkNFX01BWElNVU1fQkFORF85ID0gMS4yMTA3MDAKICAgIFJFRkxFQ1RBTkNFX01JTklNVU1fQkFORF85ID0gLTAuMDk5OTgwCiAgRU5EX0dST1VQID0gTUlOX01BWF9SRUZMRUNUQU5DRQogIEdST1VQID0gTUlOX01BWF9QSVhFTF9WQUxVRQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzEgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzEgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfMiA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMiA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8zID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF8zID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzQgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzQgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfNSA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfNSA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF82ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF82ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzcgPSA2NTUzNQogICAgUVVBTlRJWkVfQ0FMX01JTl9CQU5EXzcgPSAxCiAgICBRVUFOVElaRV9DQUxfTUFYX0JBTkRfOCA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfOCA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF85ID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF85ID0gMQogICAgUVVBTlRJWkVfQ0FMX01BWF9CQU5EXzEwID0gNjU1MzUKICAgIFFVQU5USVpFX0NBTF9NSU5fQkFORF8xMCA9IDEKICAgIFFVQU5USVpFX0NBTF9NQVhfQkFORF8xMSA9IDY1NTM1CiAgICBRVUFOVElaRV9DQUxfTUlOX0JBTkRfMTEgPSAxCiAgRU5EX0dST1VQID0gTUlOX01BWF9QSVhFTF9WQUxVRQogIEdST1VQID0gUkFESU9NRVRSSUNfUkVTQ0FMSU5HCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfMSA9IDEuMjQyMkUtMDIKICAgIFJBRElBTkNFX01VTFRfQkFORF8yID0gMS4yNzIwRS0wMgogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzMgPSAxLjE3MjFFLTAyCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfNCA9IDkuODg0MkUtMDMKICAgIFJBRElBTkNFX01VTFRfQkFORF81ID0gNi4wNDg3RS0wMwogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzYgPSAxLjUwNDJFLTAzCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfNyA9IDUuMDcwMUUtMDQKICAgIFJBRElBTkNFX01VTFRfQkFORF84ID0gMS4xMTg2RS0wMgogICAgUkFESUFOQ0VfTVVMVF9CQU5EXzkgPSAyLjM2NDBFLTAzCiAgICBSQURJQU5DRV9NVUxUX0JBTkRfMTAgPSAzLjM0MjBFLTA0CiAgICBSQURJQU5DRV9NVUxUX0JBTkRfMTEgPSAzLjM0MjBFLTA0CiAgICBSQURJQU5DRV9BRERfQkFORF8xID0gLTYyLjEwOTI4CiAgICBSQURJQU5DRV9BRERfQkFORF8yID0gLTYzLjYwMDY2CiAgICBSQURJQU5DRV9BRERfQkFORF8zID0gLTU4LjYwNzQ3CiAgICBSQURJQU5DRV9BRERfQkFORF80ID0gLTQ5LjQyMTEyCiAgICBSQURJQU5DRV9BRERfQkFORF81ID0gLTMwLjI0MzI2CiAgICBSQURJQU5DRV9BRERfQkFORF82ID0gLTcuNTIxMjIKICAgIFJBRElBTkNFX0FERF9CQU5EXzcgPSAtMi41MzUwNQogICAgUkFESUFOQ0VfQUREX0JBTkRfOCA9IC01NS45MzExMAogICAgUkFESUFOQ0VfQUREX0JBTkRfOSA9IC0xMS44MTk3NQogICAgUkFESUFOQ0VfQUREX0JBTkRfMTAgPSAwLjEwMDAwCiAgICBSQURJQU5DRV9BRERfQkFORF8xMSA9IDAuMTAwMDAKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF8xID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzIgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfMyA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF80ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzUgPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfNiA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX01VTFRfQkFORF83ID0gMi4wMDAwRS0wNQogICAgUkVGTEVDVEFOQ0VfTVVMVF9CQU5EXzggPSAyLjAwMDBFLTA1CiAgICBSRUZMRUNUQU5DRV9NVUxUX0JBTkRfOSA9IDIuMDAwMEUtMDUKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzEgPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzIgPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzMgPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzQgPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzUgPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzYgPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzcgPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzggPSAtMC4xMDAwMDAKICAgIFJFRkxFQ1RBTkNFX0FERF9CQU5EXzkgPSAtMC4xMDAwMDAKICBFTkRfR1JPVVAgPSBSQURJT01FVFJJQ19SRVNDQUxJTkcKICBHUk9VUCA9IFRJUlNfVEhFUk1BTF9DT05TVEFOVFMKICAgIEsxX0NPTlNUQU5UX0JBTkRfMTAgPSA3NzQuODg1MwogICAgSzFfQ09OU1RBTlRfQkFORF8xMSA9IDQ4MC44ODgzCiAgICBLMl9DT05TVEFOVF9CQU5EXzEwID0gMTMyMS4wNzg5CiAgICBLMl9DT05TVEFOVF9CQU5EXzExID0gMTIwMS4xNDQyCiAgRU5EX0dST1VQID0gVElSU19USEVSTUFMX0NPTlNUQU5UUwogIEdST1VQID0gUFJPSkVDVElPTl9QQVJBTUVURVJTCiAgICBNQVBfUFJPSkVDVElPTiA9ICJVVE0iCiAgICBEQVRVTSA9ICJXR1M4NCIKICAgIEVMTElQU09JRCA9ICJXR1M4NCIKICAgIFVUTV9aT05FID0gMTAKICAgIEdSSURfQ0VMTF9TSVpFX1BBTkNIUk9NQVRJQyA9IDE1LjAwCiAgICBHUklEX0NFTExfU0laRV9SRUZMRUNUSVZFID0gMzAuMDAKICAgIEdSSURfQ0VMTF9TSVpFX1RIRVJNQUwgPSAzMC4wMAogICAgT1JJRU5UQVRJT04gPSAiTk9SVEhfVVAiCiAgICBSRVNBTVBMSU5HX09QVElPTiA9ICJDVUJJQ19DT05WT0xVVElPTiIKICBFTkRfR1JPVVAgPSBQUk9KRUNUSU9OX1BBUkFNRVRFUlMKRU5EX0dST1VQID0gTDFfTUVUQURBVEFfRklMRQpFTkQK" + } + }, + { + "ID": "1c2a404568c311dc", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt", + "Proto": "HTTP/1.1", + "Header": { + "Range": [ + "bytes=0-17" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "323f9c45abecbc36987e7dd3c87e69b4/16913764783378589436;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/gcp-public-data-landsat/LC08/PRE/044/034/LC80440342016259LGN00/LC80440342016259LGN00_MTL.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 206, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "18" + ], + "Content-Range": [ + "bytes 0-17/7903" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "\"7a5fd4743bd647485f88496fadb05c51\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 04 Oct 2016 16:42:07 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1475599327662000" + ], + "X-Goog-Hash": [ + "crc32c=PWBt8g==", + "md5=el/UdDvWR0hfiElvrbBcUQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "7903" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/28,/bns/xg/borg/xg/bns/blobstore2/bitpusher/0.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W8e2C6-0_QTHpLiQDQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "570399209098" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/0.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/0:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrsCsgEPCohbb8tsYVC-K83-Df66RFc8ZHf64YopdIWg2s2HL-nlU1voXhHeLfNqQICaoYRPGywiJE8N2k6dZ79TrgITQ" + ] + }, + "Body": "R1JPVVAgPSBMMV9NRVRBREFU" + } + }, + { + "ID": "83acd83e324698db", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "cc22eb0dcc869daa28a3c4c8519bebca/10379623318317340;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "\"c6117833aa4d1510d09ef69144d56790\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 14 Nov 2017 13:07:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Accept-Encoding" + ], + "X-Goog-Generation": [ + "1510664852486988" + ], + "X-Goog-Hash": [ + "crc32c=T1s5RQ==", + "md5=xhF4M6pNFRDQnvaRRNVnkA==" + ], + "X-Goog-Metageneration": [ + "2" + ], + "X-Goog-Storage-Class": [ + "MULTI_REGIONAL" + ], + "X-Goog-Stored-Content-Encoding": [ + "gzip" + ], + "X-Goog-Stored-Content-Length": [ + "31" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/32,/bns/xg/borg/xg/bns/blobstore2/bitpusher/7.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W5vED864_QSK_4_4Cw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "149776848335" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/7.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/7:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqIRWqYIhs6N5xvWO1KLlU3DKSG7X8omOlNnqixq4b8ABXldSLwn_z5cSIt3rDdZJDCbXArgmXD2np51NnhNUnCC1o3xg" + ] + }, + "Body": "H4sIAAAAAAAAC8tIzcnJVyjPL8pJAQCFEUoNCwAAAA==" + } + }, + { + "ID": "43f7d9c2773cae0a", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "911b098cf30bb2d2db6373f690e96455/1625514656012102715;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "\"c6117833aa4d1510d09ef69144d56790\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 14 Nov 2017 13:07:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Accept-Encoding" + ], + "X-Goog-Generation": [ + "1510664852486988" + ], + "X-Goog-Hash": [ + "crc32c=T1s5RQ==", + "md5=xhF4M6pNFRDQnvaRRNVnkA==" + ], + "X-Goog-Metageneration": [ + "2" + ], + "X-Goog-Storage-Class": [ + "MULTI_REGIONAL" + ], + "X-Goog-Stored-Content-Encoding": [ + "gzip" + ], + "X-Goog-Stored-Content-Length": [ + "31" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/10,/bns/xg/borg/xg/bns/blobstore2/bitpusher/11.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W_6qH4q-_QTkpILQCw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "149776848335" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/11.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/11:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uqs2j5-lfjUTeRpZH8gaopVXoxDd8H3Ru3KOG4fh1RxSF_rO2AjgfH-O4t4tESE-fa_ppR9QDwc4YVOfxP9CzRlvTrmcQ" + ] + }, + "Body": "H4sIAAAAAAAAC8tIzcnJVyjPL8pJAQCFEUoNCwAAAA==" + } + }, + { + "ID": "145a5b07cb7cee22", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt", + "Proto": "HTTP/1.1", + "Header": { + "Range": [ + "bytes=1-8" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c8ebc936d6a1701bda2ec20a597d0efa/3240367118529228635;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "11" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "W/\"c6117833aa4d1510d09ef69144d56790\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 14 Nov 2017 13:07:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Accept-Encoding" + ], + "Warning": [ + "214 UploadServer gunzipped" + ], + "X-Goog-Generation": [ + "1510664852486988" + ], + "X-Goog-Hash": [ + "crc32c=T1s5RQ==", + "md5=xhF4M6pNFRDQnvaRRNVnkA==" + ], + "X-Goog-Metageneration": [ + "2" + ], + "X-Goog-Storage-Class": [ + "MULTI_REGIONAL" + ], + "X-Goog-Stored-Content-Encoding": [ + "gzip" + ], + "X-Goog-Stored-Content-Length": [ + "31" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/51,/bns/xg/borg/xg/bns/blobstore2/bitpusher/56.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W9KYIq-5_QT08q2oCA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "149776848335" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/56.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/56:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Response-Body-Transformations": [ + "gunzipped" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UowTICIKtzeRG9Ou_PSvDwVQg4nVkNPdHQ2KfvMkcv8d9Boi4SBVjVIcIRIRAYrFKw-LFZvQS9x3yeu6Sbv2jEbML9q4g" + ] + }, + "Body": "aGVsbG8gd29ybGQ=" + } + }, + { + "ID": "02f9306edf1966b4", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Range": [ + "bytes=1-8" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "71757057b8103659305baba67138c95f/4783444552906961530;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/storage-library-test-bucket/gzipped-text.txt" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 206, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Range": [ + "bytes 1-8/31" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:45 GMT" + ], + "Etag": [ + "\"c6117833aa4d1510d09ef69144d56790\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:10:45 GMT" + ], + "Last-Modified": [ + "Tue, 14 Nov 2017 13:07:32 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Accept-Encoding" + ], + "X-Goog-Generation": [ + "1510664852486988" + ], + "X-Goog-Hash": [ + "crc32c=T1s5RQ==", + "md5=xhF4M6pNFRDQnvaRRNVnkA==" + ], + "X-Goog-Metageneration": [ + "2" + ], + "X-Goog-Storage-Class": [ + "MULTI_REGIONAL" + ], + "X-Goog-Stored-Content-Encoding": [ + "gzip" + ], + "X-Goog-Stored-Content-Length": [ + "31" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/37,/bns/xg/borg/xg/bns/blobstore2/bitpusher/89.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W8TBJc61_QS8-5yoAw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "149776848335" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/89.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Body-Transformations": [ + "chunked" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/89:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpBtSxrfke14g0eI3Rrgg8bRTCWRAVT2Q59YpYtBr0cBURMme2SCzToajLFWSGJGX9AAbS1bz4EKFodpsW29B7-es95Iw" + ] + }, + "Body": "iwgAAAAAAAA=" + } + }, + { + "ID": "268ea6db77b72076", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "168" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4c4dd613d708454328d916ba0881b33b/7134230243258873770;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb3JzIjpbeyJtYXhBZ2VTZWNvbmRzIjozNjAwLCJtZXRob2QiOlsiUE9TVCJdLCJvcmlnaW4iOlsic29tZS1vcmlnaW4uY29tIl0sInJlc3BvbnNlSGVhZGVyIjpbImZvby1iYXIiXX1dLCJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAzIn0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "627" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:46 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vriq19:4119,/bns/yw/borg/yw/bns/blobstore2/bitpusher/114.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1U41W6zdNMbuhAS64LzQCA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/114.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/114:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq4Onx-4cRLHYYRXXNMvQIDn6B0iyDSS-F6PipLYkG9i3fAtHYQM2xhiJUnt3p_peAPcarq9z7RKoUnwHUj2YqiRKZlOKi0YKg5V51LrnnYxRukU14" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NDYuNTAyWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjQ2LjUwMloiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJjb3JzIjogWwogIHsKICAgIm9yaWdpbiI6IFsKICAgICJzb21lLW9yaWdpbi5jb20iCiAgIF0sCiAgICJtZXRob2QiOiBbCiAgICAiUE9TVCIKICAgXSwKICAgInJlc3BvbnNlSGVhZGVyIjogWwogICAgImZvby1iYXIiCiAgIF0sCiAgICJtYXhBZ2VTZWNvbmRzIjogMzYwMAogIH0KIF0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBRT0iCn0K" + } + }, + { + "ID": "4cb246f313286458", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0003?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "99" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "883dabfb34d5b26b50efeea55487bbbd/8749364176457808585;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0003?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb3JzIjpbeyJtYXhBZ2VTZWNvbmRzIjozNjAwLCJtZXRob2QiOlsiR0VUIl0sIm9yaWdpbiI6WyIqIl0sInJlc3BvbnNlSGVhZGVyIjpbInNvbWUtaGVhZGVyIl19XX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2889" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:48 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220546000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vril66:4366,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=1k41W4mHLsjPhASU8bPwBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/169.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/169:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo7m-xZz50gtnRt891bK3lgdR_dGpL5JIPWosajwMbRvImrVsYI6PYn4DKC9MYZtg08iKhyCIw-N_1JrQHwPTHPZ9TVsXHy8YA_xBqcN6ov6zVkNuY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NDYuNTAyWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjQ4LjEyMVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogImNvcnMiOiBbCiAgewogICAib3JpZ2luIjogWwogICAgIioiCiAgIF0sCiAgICJtZXRob2QiOiBbCiAgICAiR0VUIgogICBdLAogICAicmVzcG9uc2VIZWFkZXIiOiBbCiAgICAic29tZS1oZWFkZXIiCiAgIF0sCiAgICJtYXhBZ2VTZWNvbmRzIjogMzYwMAogIH0KIF0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBST0iCn0K" + } + }, + { + "ID": "947a85d333eb78ab", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0003?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "46d1bdbdbfc5baf93cb221fbec1c260b/10364216634680032745;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0003?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2889" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:48 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:48 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220548000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrt3:4280,/bns/yw/borg/yw/bns/blobstore2/bitpusher/405.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=2E41W6SKFMm8hgT99KeQAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/405.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/405:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur7qBaV4i39ojtesbk-AtbUm0q_JUDQVag_cTkslTbDSNPf6tuI-2XIjbPx9mwudHlVsM3htH6RWMRWStzURg4GyS3lFRXtPPAxjuHe2qxHP8fJqt8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NDYuNTAyWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjQ4LjEyMVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDMiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogImNvcnMiOiBbCiAgewogICAib3JpZ2luIjogWwogICAgIioiCiAgIF0sCiAgICJtZXRob2QiOiBbCiAgICAiR0VUIgogICBdLAogICAicmVzcG9uc2VIZWFkZXIiOiBbCiAgICAic29tZS1oZWFkZXIiCiAgIF0sCiAgICJtYXhBZ2VTZWNvbmRzIjogMzYwMAogIH0KIF0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBST0iCn0K" + } + }, + { + "ID": "dbb882d38af1813c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "168" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "e1cabbeb4a90c87ebddf822ac341096a/11907295172847517704;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb3JzIjpbeyJtYXhBZ2VTZWNvbmRzIjozNjAwLCJtZXRob2QiOlsiUE9TVCJdLCJvcmlnaW4iOlsic29tZS1vcmlnaW4uY29tIl0sInJlc3BvbnNlSGVhZGVyIjpbImZvby1iYXIiXX1dLCJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDA0In0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "627" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:49 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrae22:4190,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=2E41W9e-JYH1N7-RjPgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/198:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpZOTGjibBD0rvBslgaTaEJ9o_Sy5GW2VXGH4JzXJN0bl6UOBEQlY9cacgtR06QP-Wmpo0taJPHP-s1JFkzsTe6_omACuBc6g0IciedK459Ddk4Ssk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NDkuMDU5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjQ5LjA1OVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJjb3JzIjogWwogIHsKICAgIm9yaWdpbiI6IFsKICAgICJzb21lLW9yaWdpbi5jb20iCiAgIF0sCiAgICJtZXRob2QiOiBbCiAgICAiUE9TVCIKICAgXSwKICAgInJlc3BvbnNlSGVhZGVyIjogWwogICAgImZvby1iYXIiCiAgIF0sCiAgICJtYXhBZ2VTZWNvbmRzIjogMzYwMAogIH0KIF0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBRT0iCn0K" + } + }, + { + "ID": "bbae204dbd958f9e", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0004?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "12" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f5e64adf249836a263f5d09d09a41814/13522429106046386984;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0004?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb3JzIjpbXX0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:51 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220546000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrs185:4159,/bns/yw/borg/yw/bns/blobstore2/bitpusher/115.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=2U41W4LYGsXWhATtno6wAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/115.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/115:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrCqp4L1JuW26kiBMA8COS4w6IZOmPbn7y1H1LaZ9AG7x3Ypv1476BYk6tJHdzSxJZLHZ1eC_iyH6BnpTM6wAj1qXfOtil9KwkKgG2KPZsR4vKFsNQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NDkuMDU5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjUwLjkxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "8639aed730f03dad", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0004?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c44412bf67411dd69a04f668760de2a3/15065506544719021639;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0004?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:51 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:51 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220548000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrax68:4128,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=2041W-zeB4ObhATkprbADg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/265.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/265:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrwoyX3T2WXJcc4Ht2xdeTF5XSKDy1n29qRYuN81V6EN5fAHk5ZS4xK9L4y0-yz1KkGH-2zjq_uXr0nK-5djS81tD7vv_2X4ZL3I5ex3k0sFZGBKHI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NDkuMDU5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjUwLjkxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "c4c831f520d76d93", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "168" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9b1d83e851510db4389b4bf60a8f7779/16680640473622989159;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJjb3JzIjpbeyJtYXhBZ2VTZWNvbmRzIjozNjAwLCJtZXRob2QiOlsiUE9TVCJdLCJvcmlnaW4iOlsic29tZS1vcmlnaW4uY29tIl0sInJlc3BvbnNlSGVhZGVyIjpbImZvby1iYXIiXX1dLCJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDA1In0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "627" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:52 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220551000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrry15:4156,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=2041W4myGcaNhQTT7aKgCA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/199.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/199:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoiWY7uHo9ilGRcMQJ86JwZ5asP_jbetW-SbtzRmPtyEFoe_8vw_KL9wOv6vdOKc3Si9YGux0uuMk5HDcjcVDxdnL9dPGKeYSCQCO7qeXIQ2vp9BAk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTEuOTkzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjUxLjk5M1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJjb3JzIjogWwogIHsKICAgIm9yaWdpbiI6IFsKICAgICJzb21lLW9yaWdpbi5jb20iCiAgIF0sCiAgICJtZXRob2QiOiBbCiAgICAiUE9TVCIKICAgXSwKICAgInJlc3BvbnNlSGVhZGVyIjogWwogICAgImZvby1iYXIiCiAgIF0sCiAgICJtYXhBZ2VTZWNvbmRzIjogMzYwMAogIH0KIF0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBRT0iCn0K" + } + }, + { + "ID": "f8a83548454d59b8", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0005?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b35076728b8187b57d670e2cf40e525b/18295775506316774534;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0005?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2900" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:53 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220552000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrrr4:4094,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=3E41W56eEdDXN_6_oYgI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/129.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/129:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrrJkpkE-yxgPAo1Pf1SOq3kiLRIADADX7-Uk_Qdg2pbJU5jj5iYdjRCtzltysEfVfynH9AAJ_hYhv5X2wpbD70q6FGoyvAFR_3hajmVE39HLVULlk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTEuOTkzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjUzLjcxOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogImNvcnMiOiBbCiAgewogICAib3JpZ2luIjogWwogICAgInNvbWUtb3JpZ2luLmNvbSIKICAgXSwKICAgIm1ldGhvZCI6IFsKICAgICJQT1NUIgogICBdLAogICAicmVzcG9uc2VIZWFkZXIiOiBbCiAgICAiZm9vLWJhciIKICAgXSwKICAgIm1heEFnZVNlY29uZHMiOiAzNjAwCiAgfQogXSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FJPSIKfQo=" + } + }, + { + "ID": "3c73fe4d0f16e403", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0005?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9f2b51f0560db7e7b096aa86cb4afee6/1392390346256502694;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0005?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2900" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:54 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:54 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220548000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlz2:4372,/bns/yw/borg/yw/bns/blobstore2/bitpusher/39.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=3U41W-fhOIW8hQTu37noBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/39.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/39:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq7EeWPow9d7IWJg4b9S7d-C_tiM9thZiDkzYr5FNl6cCMMl4Rv25ZJv4Nmcd09o1LarcyT_Smgba6ILQ5Q5UCdfKeHLza1Y3cd4MEzf2Ax2jFxgMI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTEuOTkzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjUzLjcxOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDUiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogImNvcnMiOiBbCiAgewogICAib3JpZ2luIjogWwogICAgInNvbWUtb3JpZ2luLmNvbSIKICAgXSwKICAgIm1ldGhvZCI6IFsKICAgICJQT1NUIgogICBdLAogICAicmVzcG9uc2VIZWFkZXIiOiBbCiAgICAiZm9vLWJhciIKICAgXSwKICAgIm1heEFnZVNlY29uZHMiOiAzNjAwCiAgfQogXSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FJPSIKfQo=" + } + }, + { + "ID": "41130fe5b74f7c64", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0005?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c9837f53a03814db146bcb58f258b2a6/3007524279455437509;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0005?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:54 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdc6:4144,/bns/yw/borg/yw/bns/blobstore2/bitpusher/337.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=3k41W4aTDcO3hgTcw4ywBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/337.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/337:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpmSTsCDZ0sJownQpHbf3ZUxwi4Vf3v12gJC1rxDEaPZqfjvjDGJ2yBoA6NGO-lR3C992BQhT0wh7CyALJf2e33IwdDnjYZnS00Q8I2SAh-ufV1lkw" + ] + }, + "Body": "" + } + }, + { + "ID": "8d88b9e640b8cbec", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0004?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "726cbb3f58e9ac35a2e38556891b81f8/4550601713833104869;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0004?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:55 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrrv12:4154,/bns/yw/borg/yw/bns/blobstore2/bitpusher/238.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=3k41W6PmKMGphQST_ovoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/238.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/238:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoW4TUTHRneZLCkFQW_ZSNl11jjABQTfQA9Woyn0kdW-TIN5zX4COhbw-wsHURQWOGN9iqN660NdsbCQVw9AeBivY7WkgxjmiugiVcE5yZw1i05QV0" + ] + }, + "Body": "" + } + }, + { + "ID": "c6be8f87a468933c", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0003?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7a8bdb615b9cfec4840844068ce88e98/6165455275845146628;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0003?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:55 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220545000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrit75:4012,/bns/yw/borg/yw/bns/blobstore2/bitpusher/661.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=3041W7OeCojUhATjwKuwCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/661.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/661:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVmF2Y05Pc2tNdDViTUNrMXpNUEVOOGo1ME96ZGNjTU9aRU9GVkh3Yl9CdHR3ZEphMkRZSHNuRXZrV3Y0Z19ER09VeWRsX0pPUTlxTHNZbmdJTkJjZ1VrQmFDWExBbmw2ZlozSmZXWDVIODJXVGFzVGRYNzhmblpTUGdfYnB5UDY5OGNXRzRCVWNyZkQtUzNzaVNSaWNhVUlrdUVDbTVFRXVpUWJuMkVSZEtud202ejc4SjFhbEQzZVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqCT4VZb1zHvRALQS-Ng_LW7m6gaMoGnMB86fc-Zn1F25YivcV_521pTccJvzEUusvdbAqSVANw-FuYABuhCDhSXHMyTb5WmHRnTV5DR61WzkTYd40" + ] + }, + "Body": "" + } + }, + { + "ID": "52c0d71b13f8a38c", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "103" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d642c7eba29a43099d2c667c025e1220/7780589209044015908;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDA2IiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjYwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:56 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220555000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcf9:4485,/bns/yw/borg/yw/bns/blobstore2/bitpusher/58.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=3041W7jVK8buN4yunbAI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/58.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/58:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urpr6NwLBiv_mi4hVIHN0W3DgBpYwVus_4EYG21qrqPL7Vs-9DPsVicyurPOZMo_A5xnDoBb4HDpg8xo3Q2pFNFsZii3fz7wrn4dSAIsF_AEoAFpIs" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTYuMTQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjU2LjE0NFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI2MCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMDo1Ni4xNDRaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "26d6bf2f903ed22c", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0006?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "47" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d6f5bf961980b01c74104b1b54d167fa/9323666647716650563;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0006?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyZXRlbnRpb25Qb2xpY3kiOnsicmV0ZW50aW9uUGVyaW9kIjoiMzYwMCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2835" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:57 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220556000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vris2:4169,/bns/yw/borg/yw/bns/blobstore2/bitpusher/291.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=4E41W8vLFIfTN_LZjLgD" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/291.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/291:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ure6RRaV5wED5l3KxpFnRa2qW8KCECHH3NxRtwE0JPu-4RI5Nbec6zeS-DA8yv00dSX4ryKrGy-u3pDE2utfLri_d3aAqnB3xZ4sVuyssYLHJQ7RZs" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTYuMTQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjU3LjczOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInJldGVudGlvblBvbGljeSI6IHsKICAicmV0ZW50aW9uUGVyaW9kIjogIjM2MDAiLAogICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMjE6MTA6NTYuMTQ0WiIKIH0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBST0iCn0K" + } + }, + { + "ID": "9708af716949fd4b", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0006?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d9e51e2be6a1ea0f9b23a002747a76bf/10938800576620618083;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0006?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2835" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:58 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:10:58 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220556000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjp69:4089,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=4U41W9fxOcT2N87Rh_gH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/102.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/102:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uox2FFuYlPP_4mF8cjGGl150GbWNXtgSflouRjXbVt-y7avH7MgOmsf3D2XO_-FoY5dTif6rouYGb_R1-UCbvwcT7aCZ60VJBgKKZDAnW8wWwjafNo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTYuMTQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjU3LjczOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDYiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInJldGVudGlvblBvbGljeSI6IHsKICAicmV0ZW50aW9uUGVyaW9kIjogIjM2MDAiLAogICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMjE6MTA6NTYuMTQ0WiIKIH0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBST0iCn0K" + } + }, + { + "ID": "7594ffa01886bd65", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "103" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "511de0a8e8c41f0ce6960f34b4ede02a/12481879114788103298;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDA3IiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjYwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:10:58 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220555000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrhr11:4176,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=4k41W-6FDcaJN5i7i4AF" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/488.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/488:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up-H_uoxDJZsZo1-oX3pPE11qOanTaEFh2OcgRmZNB-lWjZJKMrRuH0BcFrpTITHwqHcGFKSnHdoGm7C_wwY6XWy1LFzDR_ufcgiklQj9fO1tvnp6I" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDciLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTguNjY2WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjU4LjY2NloiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI2MCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMDo1OC42NjZaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "90da3e36482c8eda", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0007?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "47" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a6b134a8266b675d58a222ef75d7f7d2/14097013047986972578;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0007?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyZXRlbnRpb25Qb2xpY3kiOnsicmV0ZW50aW9uUGVyaW9kIjoiMzYwMCJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2835" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:00 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220556000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnv1:4379,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=4k41W7_vM8LvhASL0qegBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/59.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/59:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrzdInRx0WzC3iUw9uEKEqZ9jOMz_mKCd0H48Z-ndgf9KTg8vXSxDmIECtq-nv6exa8vwaWzYL0AAY6u6FtkjYIBi1gi3Q8IDgJAxrjAmU163r15qw" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDciLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTguNjY2WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjAwLjEzN1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDciLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDciLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInJldGVudGlvblBvbGljeSI6IHsKICAicmV0ZW50aW9uUGVyaW9kIjogIjM2MDAiLAogICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMjE6MTA6NTguNjY2WiIKIH0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBST0iCn0K" + } + }, + { + "ID": "b09b667761978e68", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0007?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fa425123d62904d2a1fa781227fef29b/15640090486659607233;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0007?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2835" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:00 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:00 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220560000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrm187:4148,/bns/yw/borg/yw/bns/blobstore2/bitpusher/572.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=5E41W66QFYazhQSrsq7YAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/572.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/572:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrniaI_VKLPTei_C4td-u-2yomhp8wti-YTNRtiqKust62BK6yLRH5HbYULjVzpO9SWS-ryEVqeO3bR1R4ti0wi0YMOHbl-BJj-yLfDarFWCFoRIGA" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDciLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6NTguNjY2WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjAwLjEzN1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDcvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNyIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDciLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwNy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDciLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInJldGVudGlvblBvbGljeSI6IHsKICAicmV0ZW50aW9uUGVyaW9kIjogIjM2MDAiLAogICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMjE6MTA6NTguNjY2WiIKIH0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBST0iCn0K" + } + }, + { + "ID": "b9a23098ac835b41", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "103" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "31eae1967b91e55b82b99988f857f531/17255224415563574753;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDA4IiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjYwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:01 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220555000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vri10:4099,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=5E41W4WdJorvhAT60qWICA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/4.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/4:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqV7M7654M2n0B-GGyg3YCtX-cXgXCgbiSLht3y8wcxeUG6x-qoQJul3uVXJ7wncmxBk3cRyNJSj-aY5rNL9_Nl_HLGSvMVarA8paPypxktksC4DSE" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDEuMjg5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjAxLjI4OVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI2MCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMTowMS4yODlaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "09931345e25ba5da", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0008?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "25" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b4ca478689cc1a5f6bfc36621c933444/423896849524518912;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0008?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyZXRlbnRpb25Qb2xpY3kiOm51bGx9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:03 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220556000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlz2:4372,/bns/yw/borg/yw/bns/blobstore2/bitpusher/209.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=5U41W73RHoKlN9Wct9gI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/209.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/209:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UolvMtErawf4IvGZWGlgEHzDvsk80zLE8ByShOgb3swvY6w3huI2ETBvmFzwE8tuQmAoraTY89Zw3RXMd685uQaoBZr7i_ZycXMwVyNR5Lyyog-Af8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDEuMjg5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjAzLjAyMVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "b402a162f98073ea", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0008?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "386c626a6e20a3a6597b25de0950a66e/1966692817515344672;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0008?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:03 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:03 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220560000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqe5:4085,/bns/yw/borg/yw/bns/blobstore2/bitpusher/293.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=5041W9ODDoSchwS8k4SIAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/293.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/293:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uoec1_FK40UQCsecZLs1kYZXuNN-v4z5In_JAnffngxL5QgrDtI0FlaUv1ujlnPJuyHTtcNnPMZ9N6ypdT9CsMIQ-w_FNNfAirYzARvsQaTbgtmJCs" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDEuMjg5WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjAzLjAyMVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDgiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "12a6b9f8257e556f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "103" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "54a5f098f2074bf5d63808179d4eba41/3581826750714279487;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDA5IiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjYwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:04 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220563000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbd3:4081,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=5041W6anH4eHhgSr9ppQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/172.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/172:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uprqw8GusuJjXhMAo_zptR--uK9myvu3RRV5wGRNaQtzMUAFEL6SkQUWIpYzEg4uqRBi78_Suxnfl1rEPhupZmF_vX2OVYbvEY57igtPJcmoY05j4s" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDMuODQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjAzLjg0NFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI2MCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMTowMy44NDRaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "26af0c62dc79d11c", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0009?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "25" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "76fb3c82f8ea9c11d4312c333ab74c02/5124904185091946847;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0009?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyZXRlbnRpb25Qb2xpY3kiOm51bGx9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:05 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220564000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqm20:4158,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=6E41W8flA8KKhATFuJ7wBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/94.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/94:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq9EKwLaNflgRqGvRYy9ocWGIV5OvaWHTmIKVwpHE1wFVjo6-1_m_yq7o9PWrpX-NNyrZYVVSweJVlw9Z71EjtG1Nt6FE2q4RnIXe9NByPAuBgpbYU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDMuODQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjA1LjQxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "fbddae685ed8f722", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0009?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d4e9f7085cf1a1a7ae335d1406e3680e/6740039217785732222;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0009?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:05 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:05 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220556000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrna5:4014,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=6U41W4i3JsPdhQTRkIzQBQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/178.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/178:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpyuBgTMq7_GG5fwJaD3g-BEwoAuqapUGr3zDzReNZim5cve6RZx1V8d7SOug3EMgsML0GDLEB-uIx2JtHbs3ypFg0h0yQ5JwBzm_VI898H_FIlntU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDMuODQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjA1LjQxOVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwOS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDkiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "fab04c573ffac79d", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "103" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "91f76af722f16dfc5a27ccd171fe4727/8355173150984601502;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEwIiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjYwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:06 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220563000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrgg82:4016,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=6U41W8LwN8LdhAT6tKGwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/625.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/625:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpQ3Ue3oHlNWHfIvMnZtmXM95LP9VDlQdWN8U4sQyg0mUQUuxEToufPFKshrHK_6w7a6LtproJ5Lqm98l2V7jrGMDLC8N7UCP66m_DMXPnuDzfc0ps" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDYuMzcxWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjA2LjM3MVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI2MCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMTowNi4zNzFaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "cc46f59d7dc5e04f", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0010?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3f4cdb6e2d990943b284bbbfe6b0a9a3/9898250589657170622;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0010?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2833" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:08 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220564000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcw125:4019,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=6k41W_qIKcHvhAS3rbPYCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/101.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/101:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpQzNwoQrzldBbB0_lq3Rm2I2IWOFeYeLEa9Zl82V95ltGiblfV4yGaGBWQuIaR9nq691jFwlzNnBaLKIDBODym3hSDB5dGk8_FifYWAHGd5mK5ykg" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDYuMzcxWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjA4LjAyOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInJldGVudGlvblBvbGljeSI6IHsKICAicmV0ZW50aW9uUGVyaW9kIjogIjYwIiwKICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI4VDIxOjExOjA2LjM3MVoiCiB9LAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "020c14e4c847c525", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0010?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fa3cf0aeb127aabad02fcd0e03c808a3/11513384518561203677;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0010?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2833" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:08 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:08 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220560000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrms12:4089,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7E41W4axDszuhATb8oXwCw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/87.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/87:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpDo2YXmdY8LXZgzw90YCh4ruc7jVdPwkGatU2ygOs7lv7PpIe50EzzdE0ir43B9lSNp4MYtRC0WIGK4KjL74CAxujyJwFJBJgLZMCsfjFJlOoBTAk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MDYuMzcxWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjA4LjAyOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTAiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInJldGVudGlvblBvbGljeSI6IHsKICAicmV0ZW50aW9uUGVyaW9kIjogIjYwIiwKICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI4VDIxOjExOjA2LjM3MVoiCiB9LAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "78baf9bd42e6dadb", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0010?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fa6006d43944a1bdbdeb9f06c5bcb5c2/13056463056728623357;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0010?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:08 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220555000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdb67:4086,/bns/yw/borg/yw/bns/blobstore2/bitpusher/138.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7E41W9nVH4q-hQT_maLIAg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/138.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/138:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpaQBfELX-AGaPgUjCl61XVyTHCgPsbtpYKZ9CkZxzr19CeT64V7_poTQs-sIir3c8PqQMhOOm8yOxmDLoYC6zCn7gYaRntTLKiQpBjwFyNJ7G2dwo" + ] + }, + "Body": "" + } + }, + { + "ID": "71b3d9210b0ac960", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0009?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a960bda1b822a3b99005d12c8d7ee8e2/14671596989927557916;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0009?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:09 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220563000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcb124:4240,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7E41W8nQOo7ShASVqYPICg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/111.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/111:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoWx74Ykze9njYOJZLA52xxtJR7WjYWjK248WffqZQyeGjRYuC3pH31VBRMT8uJ70wIpXzJm2BlEGffJVFvB8hojkvY86-7RIeP9xuRSKvuIO9Y7qY" + ] + }, + "Body": "" + } + }, + { + "ID": "a384a50051fd0b8d", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0008?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d92b2c5e8b82ddd9f846c8e1529a8d63/16214392957918383676;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0008?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:09 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220563000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vroa22:4081,/bns/yw/borg/yw/bns/blobstore2/bitpusher/522.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7U41W6q4HcSnhQTgoLeoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/522.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/522:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur2FexzNqLVfy-G_2zzu6xDMld1C9_zlwSCB26hohfJnjFBC4dNoAp9JSbAlTQm7VK_F3U7PUOh2qvHvGNw1NuZZZvEY_A1zL0VDwrC8NgXgH_CzOU" + ] + }, + "Body": "" + } + }, + { + "ID": "7f59eed97fe3a6a0", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0007?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "44b1c14459990dd486c6fedebca6e062/17829526886822416731;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0007?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:10 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220563000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdh62:4480,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7U41W-L0OsL7hQTg1oPgDg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/306.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/306:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo7fpi3AHC9xQncR9d8booud6w31tXdGf8BnkMfCLHbA0jH2sZme9rgP5pH57vgK-muFnwf0mlaXJ8O0qHR7c7jth6k92WaDXoj5cYEoWIle96wZEw" + ] + }, + "Body": "" + } + }, + { + "ID": "d256b404f8656a9a", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0006?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "91db821c91014cca6364debe4aec1b38/998199320783295611;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0006?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:10 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220563000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrls9:4389,/bns/yw/borg/yw/bns/blobstore2/bitpusher/409.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7k41W4XEF9XkhQS3p57YBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/409.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/409:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVW5odFUyclFOb01LNnE4NFFRRTIzS2VTMEs0dERWOFB4YnpYZVZ3dUlfU0dQMjNxa3pUSUdHOE5xSXFWTVFLenBoQmRHSjJyZFpwV3dmQTZKb1FUVmpKRjI0a0lOOFJjQl9IQ1N4R2JMRlBPdGVGb2JtZ2RsWTlCUHJfRl8xR0pRMEdkOTBEV2NjSDlMQTYyUmJsVTJVM1Zrd0JKS2pCZUw0anRXX2k5cHR1dzItaU16b2lVVzJUVlEwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpQkCxoyGu5VVxQDyNDHt2nEK3hEWoamYzfQ2RjTWXtwRFXZF5eNe5W5-5t_VOnsr_Zuqn4LDYQWs40FJbLjluNuavTYwumDqJoDA27wNjbakqhNVs" + ] + }, + "Body": "" + } + }, + { + "ID": "0690b920cefb3cfd", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "106" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "33d59f7369af3145b96e39338cdbe201/2541276759455930266;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExIiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjkwMDAwIn19Cg==" + }, + "Response": { + "StatusCode": 429, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13051" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:11 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220570000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrm187:4148,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7k41W8vLOcGVhgSpl5CYAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/577.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/577:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqmRCekZyikqumQ4nZhwOwy9K7rPyy6F6_HToH4lBG_A6403yPtOMa0Nd5dN-iPWoMXoDxzNTa8rhXm3r9_uKX_1Tsx9zqC4pO3znn8BNkSX9QUOXE" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAidXNhZ2VMaW1pdHMiLAogICAgInJlYXNvbiI6ICJyYXRlTGltaXRFeGNlZWRlZCIsCiAgICAibWVzc2FnZSI6ICJUaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjE2Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmluc2VydChCdWNrZXRzRGVsZWdhdG9yLmphdmE6ODYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1UT09fTUFOWV9SRVFVRVNUUywgY2F0ZWdvcnk9UVVPVEFfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogQ3JlYXRpbmcgYnVja2V0cyB0b28gcXVpY2tseSwgcGxlYXNlIHNsb3cgZG93blxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MTYzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuSW5zZXJ0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRCdWNrZXQuamF2YTozOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IuaW5zZXJ0KEJ1Y2tldHNEZWxlZ2F0b3IuamF2YTo4Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZG9tYWluPXVzYWdlTGltaXRzLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9dG9vTWFueVJlcXVlc3RzLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLkNMSUVOVF9RVU9UQV9FWENFRURFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjE2Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmluc2VydChCdWNrZXRzRGVsZWdhdG9yLmphdmE6ODYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPUNMSUVOVF9RVU9UQV9FWENFRURFRCwgZXJyb3JQcm90b0RvbWFpbj1jbG91ZC5iaWdzdG9yZS5hcGkuQmlnc3RvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZS5idWNrZXQubmFtZSwgbWVzc2FnZT1UaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlLmJ1Y2tldC5uYW1lLCBtZXNzYWdlPVRoZSBwcm9qZWN0IGV4Y2VlZGVkIHRoZSByYXRlIGxpbWl0IGZvciBjcmVhdGluZyBhbmQgZGVsZXRpbmcgYnVja2V0cy4sIHJlYXNvbj1yYXRlTGltaXRFeGNlZWRlZCwgcnBjQ29kZT00Mjl9IFRoZSBwcm9qZWN0IGV4Y2VlZGVkIHRoZSByYXRlIGxpbWl0IGZvciBjcmVhdGluZyBhbmQgZGVsZXRpbmcgYnVja2V0cy46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuSW5zZXJ0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRCdWNrZXQuamF2YToxNjMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjM4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5pbnNlcnQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjg2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogQ3JlYXRpbmcgYnVja2V0cyB0b28gcXVpY2tseSwgcGxlYXNlIHNsb3cgZG93blxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQyOSwKICAibWVzc2FnZSI6ICJUaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuIgogfQp9Cg==" + } + }, + { + "ID": "babd9716cca9191f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "106" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "33d59f7369af3145b96e39338cdbe201/3348983911657199786;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExIiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjkwMDAwIn19Cg==" + }, + "Response": { + "StatusCode": 429, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13051" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:12 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220571000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vras29:4472,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=7041W4CeKsPrhAT61qjACQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/635.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/635:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqGAa2_wjRYB6hOeJ07xbnGGJKEzLNh1mcacNIteqJKt9vPlZ1KDzIBD1Vtn8LpuKueqRaClc_vHV4YXkvghV-x-2WY8DAUn_GfgJVLggZc2WC_GDw" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAidXNhZ2VMaW1pdHMiLAogICAgInJlYXNvbiI6ICJyYXRlTGltaXRFeGNlZWRlZCIsCiAgICAibWVzc2FnZSI6ICJUaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjE2Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmluc2VydChCdWNrZXRzRGVsZWdhdG9yLmphdmE6ODYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1UT09fTUFOWV9SRVFVRVNUUywgY2F0ZWdvcnk9UVVPVEFfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogQ3JlYXRpbmcgYnVja2V0cyB0b28gcXVpY2tseSwgcGxlYXNlIHNsb3cgZG93blxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MTYzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuSW5zZXJ0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRCdWNrZXQuamF2YTozOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IuaW5zZXJ0KEJ1Y2tldHNEZWxlZ2F0b3IuamF2YTo4Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZG9tYWluPXVzYWdlTGltaXRzLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9dG9vTWFueVJlcXVlc3RzLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLkNMSUVOVF9RVU9UQV9FWENFRURFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjE2Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmluc2VydChCdWNrZXRzRGVsZWdhdG9yLmphdmE6ODYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPUNMSUVOVF9RVU9UQV9FWENFRURFRCwgZXJyb3JQcm90b0RvbWFpbj1jbG91ZC5iaWdzdG9yZS5hcGkuQmlnc3RvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZS5idWNrZXQubmFtZSwgbWVzc2FnZT1UaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlLmJ1Y2tldC5uYW1lLCBtZXNzYWdlPVRoZSBwcm9qZWN0IGV4Y2VlZGVkIHRoZSByYXRlIGxpbWl0IGZvciBjcmVhdGluZyBhbmQgZGVsZXRpbmcgYnVja2V0cy4sIHJlYXNvbj1yYXRlTGltaXRFeGNlZWRlZCwgcnBjQ29kZT00Mjl9IFRoZSBwcm9qZWN0IGV4Y2VlZGVkIHRoZSByYXRlIGxpbWl0IGZvciBjcmVhdGluZyBhbmQgZGVsZXRpbmcgYnVja2V0cy46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuSW5zZXJ0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRCdWNrZXQuamF2YToxNjMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjM4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5pbnNlcnQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjg2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogQ3JlYXRpbmcgYnVja2V0cyB0b28gcXVpY2tseSwgcGxlYXNlIHNsb3cgZG93blxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQyOSwKICAibWVzc2FnZSI6ICJUaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuIgogfQp9Cg==" + } + }, + { + "ID": "b7ae48d0879d448e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "106" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "33d59f7369af3145b96e39338cdbe201/4156410692654799546;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExIiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjkwMDAwIn19Cg==" + }, + "Response": { + "StatusCode": 429, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13051" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:13 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220570000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlj81:4352,/bns/yw/borg/yw/bns/blobstore2/bitpusher/252.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=8E41W83TKoG0N6SfkrgK" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/252.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/252:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqNthFRhRte4BKdbqCJo3AyjoGRI6QEN_HH1J6rQMQIhG9-BLJ_lgSpcOG0XUMhh5iLV_j948WAJ8OP8I4JsQerRlQMVV4R5NxiMtGO4EZIAVjhr5E" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAidXNhZ2VMaW1pdHMiLAogICAgInJlYXNvbiI6ICJyYXRlTGltaXRFeGNlZWRlZCIsCiAgICAibWVzc2FnZSI6ICJUaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjE2Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmluc2VydChCdWNrZXRzRGVsZWdhdG9yLmphdmE6ODYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1UT09fTUFOWV9SRVFVRVNUUywgY2F0ZWdvcnk9UVVPVEFfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogQ3JlYXRpbmcgYnVja2V0cyB0b28gcXVpY2tseSwgcGxlYXNlIHNsb3cgZG93blxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MTYzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuSW5zZXJ0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRCdWNrZXQuamF2YTozOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IuaW5zZXJ0KEJ1Y2tldHNEZWxlZ2F0b3IuamF2YTo4Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MjczKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZG9tYWluPXVzYWdlTGltaXRzLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9dG9vTWFueVJlcXVlc3RzLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWNsb3VkLmJpZ3N0b3JlLmFwaS5CaWdzdG9yZUVycm9yRG9tYWluLkNMSUVOVF9RVU9UQV9FWENFRURFRCwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IFBST0pFQ1RfQlVDS0VUX09QX1JBVEVfVE9PX0hJR0g6IENyZWF0aW5nIGJ1Y2tldHMgdG9vIHF1aWNrbHksIHBsZWFzZSBzbG93IGRvd25cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjE2Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkluc2VydEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoSW5zZXJ0QnVja2V0LmphdmE6MzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmluc2VydChCdWNrZXRzRGVsZWdhdG9yLmphdmE6ODYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPUNMSUVOVF9RVU9UQV9FWENFRURFRCwgZXJyb3JQcm90b0RvbWFpbj1jbG91ZC5iaWdzdG9yZS5hcGkuQmlnc3RvcmVFcnJvckRvbWFpbiwgZmlsdGVyZWRNZXNzYWdlPW51bGwsIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZS5idWNrZXQubmFtZSwgbWVzc2FnZT1UaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlLmJ1Y2tldC5uYW1lLCBtZXNzYWdlPVRoZSBwcm9qZWN0IGV4Y2VlZGVkIHRoZSByYXRlIGxpbWl0IGZvciBjcmVhdGluZyBhbmQgZGVsZXRpbmcgYnVja2V0cy4sIHJlYXNvbj1yYXRlTGltaXRFeGNlZWRlZCwgcnBjQ29kZT00Mjl9IFRoZSBwcm9qZWN0IGV4Y2VlZGVkIHRoZSByYXRlIGxpbWl0IGZvciBjcmVhdGluZyBhbmQgZGVsZXRpbmcgYnVja2V0cy46IGNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBQUk9KRUNUX0JVQ0tFVF9PUF9SQVRFX1RPT19ISUdIOiBDcmVhdGluZyBidWNrZXRzIHRvbyBxdWlja2x5LCBwbGVhc2Ugc2xvdyBkb3duXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuSW5zZXJ0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChJbnNlcnRCdWNrZXQuamF2YToxNjMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5JbnNlcnRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEluc2VydEJ1Y2tldC5qYXZhOjM4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5pbnNlcnQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjg2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogUFJPSkVDVF9CVUNLRVRfT1BfUkFURV9UT09fSElHSDogQ3JlYXRpbmcgYnVja2V0cyB0b28gcXVpY2tseSwgcGxlYXNlIHNsb3cgZG93blxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQyOSwKICAibWVzc2FnZSI6ICJUaGUgcHJvamVjdCBleGNlZWRlZCB0aGUgcmF0ZSBsaW1pdCBmb3IgY3JlYXRpbmcgYW5kIGRlbGV0aW5nIGJ1Y2tldHMuIgogfQp9Cg==" + } + }, + { + "ID": "4981a73c6d7e88a3", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "106" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "33d59f7369af3145b96e39338cdbe201/4963837473669241801;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExIiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjkwMDAwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "563" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:16 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220571000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbh11:4232,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=8041W5LQOobRhQSY0oKoCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/197.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/197:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqKCokoH0ZPjYud9zX-vaUcb1d2jseeL2ex2pZk3exW3SgL9GnZRFygJJvNaYy6dZzkJB9ohvGY6QcazLRNhA7G6_LgntLMOIiFv2TQnRJVtiPyUqU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MTYuNDcxWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjE2LjQ3MVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMToxNi40NzFaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "c71c8031e10d9e16", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0011/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=f6f006ab13a852a6df0838509aee3e0fd7586f73e5759c0afb32ab78e180" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c6bed3d5d042d967ebde2c72e1195fdf/5699488127032532441;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0011/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1mNmYwMDZhYjEzYTg1MmE2ZGYwODM4NTA5YWVlM2UwZmQ3NTg2ZjczZTU3NTljMGFmYjMyYWI3OGUxODANCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMSIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJzb21lLW9iamVjdCJ9Cg0KLS1mNmYwMDZhYjEzYTg1MmE2ZGYwODM4NTA5YWVlM2UwZmQ3NTg2ZjczZTU3NTljMGFmYjMyYWI3OGUxODANCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbg0KDQpoZWxsbyB3b3JsZA0KLS1mNmYwMDZhYjEzYTg1MmE2ZGYwODM4NTA5YWVlM2UwZmQ3NTg2ZjczZTU3NTljMGFmYjMyYWI3OGUxODAtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3753" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:17 GMT" + ], + "Etag": [ + "CPjTr+Ci99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220570000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrat2:4108,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9E41W_KPMsaNhAT3nLboBA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/461.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/461:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrUprorqrrtnSYhlv1fVmHILLW469ZsqDLuMt2uduopgcTYta6pgxm95BRZyBkQNfl1pFRvWWWIcSS8ldGEorAJTnH6k2d1fwBgy6e-PvYNJ3FOcdo" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9zb21lLW9iamVjdC8xNTMwMjIwMjc3MTMxNzY4IiwKICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExL28vc29tZS1vYmplY3QiLAogIm5hbWUiOiAic29tZS1vYmplY3QiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDI3NzEzMTc2OCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMToxNy4xMzFaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MTcuMTMxWiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjE3LjEzMVoiLAogInNpemUiOiAiMTEiLAogIm1kNUhhc2giOiAiWHJZN3UrQWU3dENUeXlLN2oxck53dz09IiwKICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9vL3NvbWUtb2JqZWN0P2dlbmVyYXRpb249MTUzMDIyMDI3NzEzMTc2OCZhbHQ9bWVkaWEiLAogImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEvc29tZS1vYmplY3QvMTUzMDIyMDI3NzEzMTc2OC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExL28vc29tZS1vYmplY3QvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEiLAogICAib2JqZWN0IjogInNvbWUtb2JqZWN0IiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI3NzEzMTc2OCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ1BqVHIrQ2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9zb21lLW9iamVjdC8xNTMwMjIwMjc3MTMxNzY4L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExL28vc29tZS1vYmplY3QvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExIiwKICAgIm9iamVjdCI6ICJzb21lLW9iamVjdCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyNzcxMzE3NjgiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ1BqVHIrQ2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9zb21lLW9iamVjdC8xNTMwMjIwMjc3MTMxNzY4L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExL28vc29tZS1vYmplY3QvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExIiwKICAgIm9iamVjdCI6ICJzb21lLW9iamVjdCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyNzcxMzE3NjgiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNQalRyK0NpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEvc29tZS1vYmplY3QvMTUzMDIyMDI3NzEzMTc2OC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEvby9zb21lLW9iamVjdC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExIiwKICAgIm9iamVjdCI6ICJzb21lLW9iamVjdCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyNzcxMzE3NjgiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNQalRyK0NpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJ5WlJscWc9PSIsCiAiZXRhZyI6ICJDUGpUcitDaTk5c0NFQUU9IiwKICJyZXRlbnRpb25FeHBpcmF0aW9uVGltZSI6ICIyMDE4LTA2LTI5VDIyOjExOjE3LjEzMVoiCn0K" + } + }, + { + "ID": "16440491587ae189", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011/o/some-object?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "1436f680b067919580b7f1fc4107d866/6506914908046909161;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011/o/some-object?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "13204" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:17 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:17 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220571000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqh26:4149,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9U41W821F5LuhATIx7SoDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/25.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/25:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpUAh4jFLBaChGoYdE19CRz9QIxtsZVSZanEfW24Ti03P69gzLlVmU_-_3iCpj6zZ7i9uy83BJGOICbkjBOoVB1OHH1xQLvBNB06I-jWJK8Sz9rahA" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogIkZvcmJpZGRlbiIsCiAgICAiZGVidWdJbmZvIjogImNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpSRVRFTlRJT05fUE9MSUNZX05PVF9NRVQ6IFJFVEVOVElPTl9QT0xJQ1lfTk9UX01FVDogUkVURU5USU9OX1BPTElDWV9OT1RfTUVUOiBPYmplY3QgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9zb21lLW9iamVjdCcgaXMgc3ViamVjdCB0byBidWNrZXQncyByZXRlbnRpb24gcG9saWN5IGFuZCBjYW5ub3QgYmUgZGVsZXRlZCBvciBvdmVyd3JpdHRlbiB1bnRpbCAyMDE4LTA2LTI5VDE1OjExOjE3LjEzMTM3MjAzMS0wNzowMFxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YTo4NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6MjcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmRlbGV0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTEzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUkVURU5USU9OX1BPTElDWV9OT1RfTUVUOiBSRVRFTlRJT05fUE9MSUNZX05PVF9NRVQ6IE9iamVjdCAnZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExL3NvbWUtb2JqZWN0JyBpcyBzdWJqZWN0IHRvIGJ1Y2tldCdzIHJldGVudGlvbiBwb2xpY3kgYW5kIGNhbm5vdCBiZSBkZWxldGVkIG9yIG92ZXJ3cml0dGVuIHVudGlsIDIwMTgtMDYtMjlUMTU6MTE6MTcuMTMxMzcyMDMxLTA3OjAwXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6UkVURU5USU9OX1BPTElDWV9OT1RfTUVUOiBSRVRFTlRJT05fUE9MSUNZX05PVF9NRVQ6IFJFVEVOVElPTl9QT0xJQ1lfTk9UX01FVDogT2JqZWN0ICdnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEvc29tZS1vYmplY3QnIGlzIHN1YmplY3QgdG8gYnVja2V0J3MgcmV0ZW50aW9uIHBvbGljeSBhbmQgY2Fubm90IGJlIGRlbGV0ZWQgb3Igb3ZlcndyaXR0ZW4gdW50aWwgMjAxOC0wNi0yOVQxNToxMToxNy4xMzEzNzIwMzEtMDc6MDBcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6ODQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjI3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5kZWxldGUoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjExMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IFJFVEVOVElPTl9QT0xJQ1lfTk9UX01FVDogUkVURU5USU9OX1BPTElDWV9OT1RfTUVUOiBPYmplY3QgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9zb21lLW9iamVjdCcgaXMgc3ViamVjdCB0byBidWNrZXQncyByZXRlbnRpb24gcG9saWN5IGFuZCBjYW5ub3QgYmUgZGVsZXRlZCBvciBvdmVyd3JpdHRlbiB1bnRpbCAyMDE4LTA2LTI5VDE1OjExOjE3LjEzMTM3MjAzMS0wNzowMFxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTcgbW9yZVxuLCBkb21haW49Z2xvYmFsLCBleHRlbmRlZEhlbHA9bnVsbCwgaHR0cEhlYWRlcnM9e30sIGh0dHBTdGF0dXM9Zm9yYmlkZGVuLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5GT1JCSURERU4sIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpSRVRFTlRJT05fUE9MSUNZX05PVF9NRVQ6IFJFVEVOVElPTl9QT0xJQ1lfTk9UX01FVDogUkVURU5USU9OX1BPTElDWV9OT1RfTUVUOiBPYmplY3QgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9zb21lLW9iamVjdCcgaXMgc3ViamVjdCB0byBidWNrZXQncyByZXRlbnRpb24gcG9saWN5IGFuZCBjYW5ub3QgYmUgZGVsZXRlZCBvciBvdmVyd3JpdHRlbiB1bnRpbCAyMDE4LTA2LTI5VDE1OjExOjE3LjEzMTM3MjAzMS0wNzowMFxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YTo4NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkRlbGV0ZU9iamVjdC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoRGVsZXRlT2JqZWN0LmphdmE6MjcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmRlbGV0ZShPYmplY3RzRGVsZWdhdG9yLmphdmE6MTEzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogUkVURU5USU9OX1BPTElDWV9OT1RfTUVUOiBSRVRFTlRJT05fUE9MSUNZX05PVF9NRVQ6IE9iamVjdCAnZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExL3NvbWUtb2JqZWN0JyBpcyBzdWJqZWN0IHRvIGJ1Y2tldCdzIHJldGVudGlvbiBwb2xpY3kgYW5kIGNhbm5vdCBiZSBkZWxldGVkIG9yIG92ZXJ3cml0dGVuIHVudGlsIDIwMTgtMDYtMjlUMTU6MTE6MTcuMTMxMzcyMDMxLTA3OjAwXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxNyBtb3JlXG4sIGVycm9yUHJvdG9Db2RlPUZPUkJJRERFTiwgZXJyb3JQcm90b0RvbWFpbj1nZGF0YS5Db3JlRXJyb3JEb21haW4sIGZpbHRlcmVkTWVzc2FnZT1udWxsLCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPW51bGwsIHVubmFtZWRBcmd1bWVudHM9W119LCBsb2NhdGlvbj1udWxsLCBtZXNzYWdlPUZvcmJpZGRlbiwgcmVhc29uPWZvcmJpZGRlbiwgcnBjQ29kZT00MDN9IEZvcmJpZGRlbjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OlJFVEVOVElPTl9QT0xJQ1lfTk9UX01FVDogUkVURU5USU9OX1BPTElDWV9OT1RfTUVUOiBSRVRFTlRJT05fUE9MSUNZX05PVF9NRVQ6IE9iamVjdCAnZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDExL3NvbWUtb2JqZWN0JyBpcyBzdWJqZWN0IHRvIGJ1Y2tldCdzIHJldGVudGlvbiBwb2xpY3kgYW5kIGNhbm5vdCBiZSBkZWxldGVkIG9yIG92ZXJ3cml0dGVuIHVudGlsIDIwMTgtMDYtMjlUMTU6MTE6MTcuMTMxMzcyMDMxLTA3OjAwXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5EZWxldGVPYmplY3QuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKERlbGV0ZU9iamVjdC5qYXZhOjg0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuRGVsZXRlT2JqZWN0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChEZWxldGVPYmplY3QuamF2YToyNylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IuZGVsZXRlKE9iamVjdHNEZWxlZ2F0b3IuamF2YToxMTMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBSRVRFTlRJT05fUE9MSUNZX05PVF9NRVQ6IFJFVEVOVElPTl9QT0xJQ1lfTk9UX01FVDogT2JqZWN0ICdnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEvc29tZS1vYmplY3QnIGlzIHN1YmplY3QgdG8gYnVja2V0J3MgcmV0ZW50aW9uIHBvbGljeSBhbmQgY2Fubm90IGJlIGRlbGV0ZWQgb3Igb3ZlcndyaXR0ZW4gdW50aWwgMjAxOC0wNi0yOVQxNToxMToxNy4xMzEzNzIwMzEtMDc6MDBcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE3IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDMsCiAgIm1lc3NhZ2UiOiAiRm9yYmlkZGVuIgogfQp9Cg==" + } + }, + { + "ID": "02f2cf1213b93166", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "25" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d77f3076b53fca38a492f0841b0b1d4c/8122048841245843720;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyZXRlbnRpb25Qb2xpY3kiOm51bGx9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:19 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220577000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqv23:4141,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9U41W_6DKMW6N4SVrcgJ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/271.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/271:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq5DkWX9AyKU3I3AawbQ_ThIbJvrd38UJjkN4tt-iduqEOMEcz1bM8uSAzZ9MVaiDqM6KJxAE1kmF1864a1VSUJ5EjzmQabtWoo2vCOhprVd5oXw6I" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMSIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MTYuNDcxWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjE4LjkxOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTEiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "7d9e44f015812e97", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011/o/some-object?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8d5d740f9fed56c3f41c999c40d440f2/8929757092925186840;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011/o/some-object?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:19 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220570000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbc189:4377,/bns/yw/borg/yw/bns/blobstore2/bitpusher/432.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9041W-fbB8aLhgTpw42oBw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/432.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/432:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoJAg-mnP-i7L85z4rz0ojbIc83vnbrjfbVBesbO_CnUqs_wlm2JV0gW9DbY38Miv4xoZP18co9KOlI0EI1GtCylTasRk_udaUNE4S1NGWeIZ1mzvs" + ] + }, + "Body": "" + } + }, + { + "ID": "1352005774b8c470", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "907fc07295d8f5d2c9e079cc710811f9/10472834531597755960;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0011?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:19 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220571000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vruu1:4251,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=9041W6a4IsuGhASj3anICg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/615.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/615:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVJFZWVGd1ZTMFY1dElId1RrOUpFWlVKMkppLTk1VktLVGZVVVIxM0ROQjIwbjBOV3hwcFhQekRiTTgtQnNUanlZN0tTRk1YbVU3aVFrV2VPd2RoUE9nTTNOa2dzLW80Z3BKR3pjY2hnZDIzN2czSjBwLTdqRVlWRTRMVjBzTndlYWduaG4tY1BJcUtlMnFyOHdwU2JSZTMzYk00RVQxdWxNTW95cVg4VXFKc1RPd09sQjM5NFlGSVUwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpIBTCVrDDAk_GVIKRxl5B6mNWCndBNT4ZJFGHEQk5u0gQzxE1KKLxoNTVjD5y_KBr3oJJw-gX2rlvfAlt9wjC8CKR_oar4cq9tzaD5D7C8OSvOly8" + ] + }, + "Body": "" + } + }, + { + "ID": "45b23549874e8ae9", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "106" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b3691797f8d3655b1ffdba0d25304d42/12087686989820045655;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEyIiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjkwMDAwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "563" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:20 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220580000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrch2:4197,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-E41W83ZAtWyhgSNxaeACw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/444.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/444:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWDBCR1ExX3FULWNJSFZoRV9WUFhuYUlIUFFvNDE5ZEdVRjBuaXhrLVoxeGRGY3l0T29VSDNCWTVSQjV3ZHZsSUhZRjJnMlctUl91dGNzX2dQRHZfOFZ0TU9uX0VpalVvQWhHSkFQQzRTRW9GczBUdzNzV3BwcldMaXVva1F3THBTYlNSQThYYlZCVEtydGg1X3UwMXlUckx5RVFnSzdGUkE3eDV0QlhTX250aE5iTHdIUEpKN1dPOU0wBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoCgsbR8kYCXJeWPS9qex_k1FOxPuD1RH28xp3hs2pd2Acri9QfZZ36FPomW6qSO7IX6tvGnPFoG6Pw135A3j5h7AAF_mXctMElgRyIHTtx-xdyln0" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MjAuNDQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjIwLjQ0NFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMToyMC40NDRaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "89f99f436bbb207e", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0012?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "dc0fcc5349fc150cfef21785dbaed7fb/13630764428492614775;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0012?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2836" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:21 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:21 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220580000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbp126:4246,/bns/yw/borg/yw/bns/blobstore2/bitpusher/357.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-E41W-7nKcrIhgS6grugDA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/357.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/357:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWDBCR1ExX3FULWNJSFZoRV9WUFhuYUlIUFFvNDE5ZEdVRjBuaXhrLVoxeGRGY3l0T29VSDNCWTVSQjV3ZHZsSUhZRjJnMlctUl91dGNzX2dQRHZfOFZ0TU9uX0VpalVvQWhHSkFQQzRTRW9GczBUdzNzV3BwcldMaXVva1F3THBTYlNSQThYYlZCVEtydGg1X3UwMXlUckx5RVFnSzdGUkE3eDV0QlhTX250aE5iTHdIUEpKN1dPOU0wBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq7wnR70mJiHSwYOt-i2JfkeeRxxE-94Yc04RSSHPCaq15FvZ2G7o9cKZDNOF8zZ94RX1tPLrJJHKCDSiLEImViAqLbMvBalhbRyxPBKlUk7aXzwdU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MjAuNDQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjIwLjQ0NFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBRT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInJldGVudGlvblBvbGljeSI6IHsKICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI4VDIxOjExOjIwLjQ0NFoiCiB9LAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUU9Igp9Cg==" + } + }, + { + "ID": "e2c6bcf418defa80", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0012/lockRetentionPolicy?alt=json\u0026ifMetagenerationMatch=1", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "0" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3f7ee2129d5ab03640ed460bae01f60a/14438472680188800390;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0012/lockRetentionPolicy?alt=json\u0026ifMetagenerationMatch=1" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "640" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:22 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220580000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqf18:4270,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-U41W47_BsKVN86YjeAJ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/211.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/211:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWDBCR1ExX3FULWNJSFZoRV9WUFhuYUlIUFFvNDE5ZEdVRjBuaXhrLVoxeGRGY3l0T29VSDNCWTVSQjV3ZHZsSUhZRjJnMlctUl91dGNzX2dQRHZfOFZ0TU9uX0VpalVvQWhHSkFQQzRTRW9GczBUdzNzV3BwcldMaXVva1F3THBTYlNSQThYYlZCVEtydGg1X3UwMXlUckx5RVFnSzdGUkE3eDV0QlhTX250aE5iTHdIUEpKN1dPOU0wBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo1PHv5yytKZaAFCeefVpoU4uybeINu99DM7EBAVmpKDLha2TC8brWIjMy8-spOA4LfwWIwSBCO3olCAnAIjjjcINnVSM0sPFTv1GAbKbG6UJDdpu4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MjAuNDQ0WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjIyLjQzM1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogIm93bmVyIjogewogICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiB9LAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMToyMC40NDRaIiwKICAiaXNMb2NrZWQiOiB0cnVlCiB9LAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "01dbd286bdf4ebf4", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0012?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "47" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "709abd0fc19307cb027d2bb5538b2efc/16053606613387669670;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0012?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJyZXRlbnRpb25Qb2xpY3kiOnsicmV0ZW50aW9uUGVyaW9kIjoiMzYwMCJ9fQo=" + }, + "Response": { + "StatusCode": 403, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13924" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:23 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220582000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbf20:4478,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-k41W5CkJ8LxhATkzq7YCQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/221.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/221:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBWDBCR1ExX3FULWNJSFZoRV9WUFhuYUlIUFFvNDE5ZEdVRjBuaXhrLVoxeGRGY3l0T29VSDNCWTVSQjV3ZHZsSUhZRjJnMlctUl91dGNzX2dQRHZfOFZ0TU9uX0VpalVvQWhHSkFQQzRTRW9GczBUdzNzV3BwcldMaXVva1F3THBTYlNSQThYYlZCVEtydGg1X3UwMXlUckx5RVFnSzdGUkE3eDV0QlhTX250aE5iTHdIUEpKN1dPOU0wBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrYkUd_dElz4aoQ12AeDvuyb0H5hADoh6JxWT-g0T-trkMJcc07NxYfSH4lQXiQB3h_Q2Wpy29PeLuKaqznGIfPaPMT1hoRK9WzLaDzLRClcGOgWMY" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiZm9yYmlkZGVuIiwKICAgICJtZXNzYWdlIjogIkNhbm5vdCByZWR1Y2UgcmV0ZW50aW9uIGR1cmF0aW9uIG9mIGEgbG9ja2VkIFJldGVudGlvbiBQb2xpY3kgZm9yIGJ1Y2tldCAnZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEyJy4iLAogICAgImRlYnVnSW5mbyI6ICJjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQ2Fubm90IHJlZHVjZSByZXRlbnRpb24gZHVyYXRpb24gb2YgYSBsb2NrZWQgUmV0ZW50aW9uIFBvbGljeSBmb3IgYnVja2V0ICdnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTInLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLlVwZGF0ZUFuZFBhdGNoQnVja2V0LnVwZGF0ZUJ1Y2tldChVcGRhdGVBbmRQYXRjaEJ1Y2tldC5qYXZhOjk2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuVXBkYXRlQnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVCdWNrZXQuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLlVwZGF0ZUJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQnVja2V0LmphdmE6MTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLnVwZGF0ZShCdWNrZXRzRGVsZWdhdG9yLmphdmE6OTQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBDYW5ub3QgcmVkdWNlIHJldGVudGlvbiBkdXJhdGlvbiBvZiBhIGxvY2tlZCBSZXRlbnRpb24gUG9saWN5IGZvciBidWNrZXQgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMicuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1GT1JCSURERU4sIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQ2Fubm90IHJlZHVjZSByZXRlbnRpb24gZHVyYXRpb24gb2YgYSBsb2NrZWQgUmV0ZW50aW9uIFBvbGljeSBmb3IgYnVja2V0ICdnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTInLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLlVwZGF0ZUFuZFBhdGNoQnVja2V0LnVwZGF0ZUJ1Y2tldChVcGRhdGVBbmRQYXRjaEJ1Y2tldC5qYXZhOjk2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuVXBkYXRlQnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChVcGRhdGVCdWNrZXQuamF2YTo3OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLlVwZGF0ZUJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQnVja2V0LmphdmE6MTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLnVwZGF0ZShCdWNrZXRzRGVsZWdhdG9yLmphdmE6OTQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBBQ0NFU1NfREVOSUVEOiBBQ0NFU1NfREVOSUVEOiBDYW5ub3QgcmVkdWNlIHJldGVudGlvbiBkdXJhdGlvbiBvZiBhIGxvY2tlZCBSZXRlbnRpb24gUG9saWN5IGZvciBidWNrZXQgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMicuXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Mylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOSBtb3JlXG4sIGRvbWFpbj1nbG9iYWwsIGV4dGVuZGVkSGVscD1udWxsLCBodHRwSGVhZGVycz17fSwgaHR0cFN0YXR1cz1mb3JiaWRkZW4sIGludGVybmFsUmVhc29uPVJlYXNvbnthcmd1bWVudHM9e30sIGNhdXNlPW51bGwsIGNvZGU9Z2RhdGEuQ29yZUVycm9yRG9tYWluLkZPUkJJRERFTiwgY3JlYXRlZEJ5QmFja2VuZD10cnVlLCBkZWJ1Z01lc3NhZ2U9Y29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IENhbm5vdCByZWR1Y2UgcmV0ZW50aW9uIGR1cmF0aW9uIG9mIGEgbG9ja2VkIFJldGVudGlvbiBQb2xpY3kgZm9yIGJ1Y2tldCAnZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEyJy5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5VcGRhdGVBbmRQYXRjaEJ1Y2tldC51cGRhdGVCdWNrZXQoVXBkYXRlQW5kUGF0Y2hCdWNrZXQuamF2YTo5Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLlVwZGF0ZUJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQnVja2V0LmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5VcGRhdGVCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUJ1Y2tldC5qYXZhOjE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci51cGRhdGUoQnVja2V0c0RlbGVnYXRvci5qYXZhOjk0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQ2Fubm90IHJlZHVjZSByZXRlbnRpb24gZHVyYXRpb24gb2YgYSBsb2NrZWQgUmV0ZW50aW9uIFBvbGljeSBmb3IgYnVja2V0ICdnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTInLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuLCBlcnJvclByb3RvQ29kZT1GT1JCSURERU4sIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1DYW5ub3QgcmVkdWNlIHJldGVudGlvbiBkdXJhdGlvbiBvZiBhIGxvY2tlZCBSZXRlbnRpb24gUG9saWN5IGZvciBidWNrZXQgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMicuLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1DYW5ub3QgcmVkdWNlIHJldGVudGlvbiBkdXJhdGlvbiBvZiBhIGxvY2tlZCBSZXRlbnRpb24gUG9saWN5IGZvciBidWNrZXQgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMicuLCByZWFzb249Zm9yYmlkZGVuLCBycGNDb2RlPTQwM30gQ2Fubm90IHJlZHVjZSByZXRlbnRpb24gZHVyYXRpb24gb2YgYSBsb2NrZWQgUmV0ZW50aW9uIFBvbGljeSBmb3IgYnVja2V0ICdnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTInLjogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IEFDQ0VTU19ERU5JRUQ6IENhbm5vdCByZWR1Y2UgcmV0ZW50aW9uIGR1cmF0aW9uIG9mIGEgbG9ja2VkIFJldGVudGlvbiBQb2xpY3kgZm9yIGJ1Y2tldCAnZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEyJy5cblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5VcGRhdGVBbmRQYXRjaEJ1Y2tldC51cGRhdGVCdWNrZXQoVXBkYXRlQW5kUGF0Y2hCdWNrZXQuamF2YTo5Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLlVwZGF0ZUJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoVXBkYXRlQnVja2V0LmphdmE6NzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5VcGRhdGVCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKFVwZGF0ZUJ1Y2tldC5qYXZhOjE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci51cGRhdGUoQnVja2V0c0RlbGVnYXRvci5qYXZhOjk0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQUNDRVNTX0RFTklFRDogQUNDRVNTX0RFTklFRDogQ2Fubm90IHJlZHVjZSByZXRlbnRpb24gZHVyYXRpb24gb2YgYSBsb2NrZWQgUmV0ZW50aW9uIFBvbGljeSBmb3IgYnVja2V0ICdnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTInLlxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTkgbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQwMywKICAibWVzc2FnZSI6ICJDYW5ub3QgcmVkdWNlIHJldGVudGlvbiBkdXJhdGlvbiBvZiBhIGxvY2tlZCBSZXRlbnRpb24gUG9saWN5IGZvciBidWNrZXQgJ2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMicuIgogfQp9Cg==" + } + }, + { + "ID": "d3232b1ceef0b48f", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "106" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a7269b59cf0be008c3892ffdf2547e5a/17596685147260253125;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJuYW1lIjoiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEzIiwicmV0ZW50aW9uUG9saWN5Ijp7InJldGVudGlvblBlcmlvZCI6IjkwMDAwIn19Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "563" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:24 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220583000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrnh11:4427,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=-041W5ngOMytN6euonA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/282.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/282:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWFJIMHFFZEJ6V29DMXc5NjlVYjcxak1qb2RHR29CYmhJdHhmUkJoVlBLMzJnNEVJT0pxbjRtcHdYWUctVndZdmprOWRWTU4zdm14TFZMcWVBeGd3bzdRMzAtZURLdjJNRlQtLVJoQlR0Zjl4T1JWM05hYlc4d18xbU5HamRLU3lDZmFLYlpCRnhBRV9qZWg2emtld0xDWUVscnV4M2ZNa1lvOUdZbFZoZkpBaFU1N2pXRTIzcWJSOWswBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urm9vdxpF88E2XlxhyXDsLeenagnBf2F1Cdk1BoNhVi056hxBdwVN7v0g5pvktUDICgetBnV5tfZDHjWcNwvIuL4OqKdEUeWj83gqwUqDM4Zv9Wwko" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMyIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMyIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTMiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MjQuNTg3WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjI0LjU4N1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImxvY2F0aW9uIjogIlVTIiwKICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMToyNC41ODdaIgogfSwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "69cd7991b1a2c977", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0013/lockRetentionPolicy?alt=json\u0026ifMetagenerationMatch=0", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "0" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "308308b9122c01111a40409706e33d38/18404110828763002325;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0013/lockRetentionPolicy?alt=json\u0026ifMetagenerationMatch=0" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 412, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Content-Length": [ + "13081" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:26 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220583000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlv18:4344,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_E41W76BL4yihwS80rTIBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/623.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/623:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWFJIMHFFZEJ6V29DMXc5NjlVYjcxak1qb2RHR29CYmhJdHhmUkJoVlBLMzJnNEVJT0pxbjRtcHdYWUctVndZdmprOWRWTU4zdm14TFZMcWVBeGd3bzdRMzAtZURLdjJNRlQtLVJoQlR0Zjl4T1JWM05hYlc4d18xbU5HamRLU3lDZmFLYlpCRnhBRV9qZWg2emtld0xDWUVscnV4M2ZNa1lvOUdZbFZoZkpBaFU1N2pXRTIzcWJSOWswBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrgND6A5vhpPan7MUDTo9_hSj52zajV4kuJXK-L3S7s3uUkQuV4yafzYBUj1yF8UgUvglt3DaKOjUs9Jn2dYSqpj0I7FaV1UwE3ui_Ct3Ph_MKA8mQ" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAiY29uZGl0aW9uTm90TWV0IiwKICAgICJtZXNzYWdlIjogIlByZWNvbmRpdGlvbiBGYWlsZWQiLAogICAgImxvY2F0aW9uVHlwZSI6ICJoZWFkZXIiLAogICAgImxvY2F0aW9uIjogIklmLU1hdGNoIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OklOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IGV4cGVjdGVkIEJ1Y2tldE1ldGFkYXRhLm1ldGFkYXRhX2dlbmVyYXRpb246IDAgYWN0dWFsOiAxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuTG9ja1JldGVudGlvblBvbGljeS5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTG9ja1JldGVudGlvblBvbGljeS5qYXZhOjIxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkxvY2tSZXRlbnRpb25Qb2xpY3kuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExvY2tSZXRlbnRpb25Qb2xpY3kuamF2YTo1OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IubG9ja1JldGVudGlvblBvbGljeShCdWNrZXRzRGVsZWdhdG9yLmphdmE6MTAwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBleHBlY3RlZCBCdWNrZXRNZXRhZGF0YS5tZXRhZGF0YV9nZW5lcmF0aW9uOiAwIGFjdHVhbDogMVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuXG5jb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5GYXVsdDogSW1tdXRhYmxlRXJyb3JEZWZpbml0aW9ue2Jhc2U9UFJFQ09ORElUSU9OX0ZBSUxFRCwgY2F0ZWdvcnk9VVNFUl9FUlJPUiwgY2F1c2U9bnVsbCwgZGVidWdJbmZvPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBleHBlY3RlZCBCdWNrZXRNZXRhZGF0YS5tZXRhZGF0YV9nZW5lcmF0aW9uOiAwIGFjdHVhbDogMVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkxvY2tSZXRlbnRpb25Qb2xpY3kuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExvY2tSZXRlbnRpb25Qb2xpY3kuamF2YToyMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5Mb2NrUmV0ZW50aW9uUG9saWN5LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMb2NrUmV0ZW50aW9uUG9saWN5LmphdmE6NTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmxvY2tSZXRlbnRpb25Qb2xpY3koQnVja2V0c0RlbGVnYXRvci5qYXZhOjEwMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogZXhwZWN0ZWQgQnVja2V0TWV0YWRhdGEubWV0YWRhdGFfZ2VuZXJhdGlvbjogMCBhY3R1YWw6IDFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPXByZWNvbmRpdGlvbkZhaWxlZCwgaW50ZXJuYWxSZWFzb249UmVhc29ue2FyZ3VtZW50cz17fSwgY2F1c2U9bnVsbCwgY29kZT1nZGF0YS5Db3JlRXJyb3JEb21haW4uQ09ORElUSU9OX05PVF9NRVQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBleHBlY3RlZCBCdWNrZXRNZXRhZGF0YS5tZXRhZGF0YV9nZW5lcmF0aW9uOiAwIGFjdHVhbDogMVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkxvY2tSZXRlbnRpb25Qb2xpY3kuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExvY2tSZXRlbnRpb25Qb2xpY3kuamF2YToyMTApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5Mb2NrUmV0ZW50aW9uUG9saWN5LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMb2NrUmV0ZW50aW9uUG9saWN5LmphdmE6NTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmxvY2tSZXRlbnRpb25Qb2xpY3koQnVja2V0c0RlbGVnYXRvci5qYXZhOjEwMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogZXhwZWN0ZWQgQnVja2V0TWV0YWRhdGEubWV0YWRhdGFfZ2VuZXJhdGlvbjogMCBhY3R1YWw6IDFcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZXJyb3JQcm90b0NvZGU9Q09ORElUSU9OX05PVF9NRVQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249bnVsbCwgbWVzc2FnZT1udWxsLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249aGVhZGVycy5JZi1NYXRjaCwgbWVzc2FnZT1QcmVjb25kaXRpb24gRmFpbGVkLCByZWFzb249Y29uZGl0aW9uTm90TWV0LCBycGNDb2RlPTQxMn0gUHJlY29uZGl0aW9uIEZhaWxlZDogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OklOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBJTkNPUlJFQ1RfTUVUQV9HRU5FUkFUSU9OX1NQRUNJRklFRDogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IGV4cGVjdGVkIEJ1Y2tldE1ldGFkYXRhLm1ldGFkYXRhX2dlbmVyYXRpb246IDAgYWN0dWFsOiAxXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuTG9ja1JldGVudGlvblBvbGljeS5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTG9ja1JldGVudGlvblBvbGljeS5qYXZhOjIxMClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkxvY2tSZXRlbnRpb25Qb2xpY3kuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExvY2tSZXRlbnRpb25Qb2xpY3kuamF2YTo1OClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IubG9ja1JldGVudGlvblBvbGljeShCdWNrZXRzRGVsZWdhdG9yLmphdmE6MTAwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogSU5DT1JSRUNUX01FVEFfR0VORVJBVElPTl9TUEVDSUZJRUQ6IElOQ09SUkVDVF9NRVRBX0dFTkVSQVRJT05fU1BFQ0lGSUVEOiBleHBlY3RlZCBCdWNrZXRNZXRhZGF0YS5tZXRhZGF0YV9nZW5lcmF0aW9uOiAwIGFjdHVhbDogMVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkVycm9yQ29sbGVjdG9yLnRvRmF1bHQoRXJyb3JDb2xsZWN0b3IuamF2YTo1NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lFcnJvckNvbnZlcnRlci50b0ZhdWx0KFJvc3lFcnJvckNvbnZlcnRlci5qYXZhOjY3KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjU4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUhhbmRsZXIkMi5jYWxsKFJvc3lIYW5kbGVyLmphdmE6MjM4KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnRocmVhZC5UaHJlYWRUcmFja2VycyRUaHJlYWRUcmFja2luZ1J1bm5hYmxlLnJ1bihUaHJlYWRUcmFja2Vycy5qYXZhOjEyNilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6NDU1KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuc2VydmVyLkNvbW1vbk1vZHVsZSRDb250ZXh0Q2FycnlpbmdFeGVjdXRvclNlcnZpY2UkMS5ydW5JbkNvbnRleHQoQ29tbW9uTW9kdWxlLmphdmE6ODQ2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlJDEucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ2Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoVHJhY2VDb250ZXh0LmphdmE6MzIxKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjMxMylcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDU5KVxuXHRhdCBjb20uZ29vZ2xlLmdzZS5pbnRlcm5hbC5EaXNwYXRjaFF1ZXVlSW1wbCRXb3JrZXJUaHJlYWQucnVuKERpc3BhdGNoUXVldWVJbXBsLmphdmE6NDAzKVxuIgogICB9CiAgXSwKICAiY29kZSI6IDQxMiwKICAibWVzc2FnZSI6ICJQcmVjb25kaXRpb24gRmFpbGVkIgogfQp9Cg==" + } + }, + { + "ID": "969b3088c4502b9d", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026kmsKeyName=projects%2Fdulcet-port-762%2Flocations%2Fus%2FkeyRings%2Fgo-integration-test%2FcryptoKeys%2Fkey1\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=d2d25e5f94ce189cfaf17a0f4bce36bb87e15af84d96fb9f53b4bc954995" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "96859440a2d2c84084883bdc6fb923d5/765075011044538085;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026kmsKeyName=projects%2Fdulcet-port-762%2Flocations%2Fus%2FkeyRings%2Fgo-integration-test%2FcryptoKeys%2Fkey1\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS1kMmQyNWU1Zjk0Y2UxODljZmFmMTdhMGY0YmNlMzZiYjg3ZTE1YWY4NGQ5NmZiOWY1M2I0YmM5NTQ5OTUNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJrbXMifQoNCi0tZDJkMjVlNWY5NGNlMTg5Y2ZhZjE3YTBmNGJjZTM2YmI4N2UxNWFmODRkOTZmYjlmNTNiNGJjOTU0OTk1DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KbXkgc2VjcmV0DQotLWQyZDI1ZTVmOTRjZTE4OWNmYWYxN2EwZjRiY2UzNmJiODdlMTVhZjg0ZDk2ZmI5ZjUzYjRiYzk1NDk5NS0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3665" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:28 GMT" + ], + "Etag": [ + "CJ3x2uWi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220586000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrk4:4290,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=_k41W5-GKdbshgThiaaoCQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/372.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/372:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpuXFLQWTkYV8mHRH1pUbX48SVryy2vZfWkeC66a9EbNlhWXT7CJydejejocEYsWBNDBcEaATe-UGOBMNe8affxTeHS2932d4ZvznUvqSPHa4ZveYU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9rbXMvMTUzMDIyMDI4ODMyNTc4OSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcyIsCiAibmFtZSI6ICJrbXMiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDI4ODMyNTc4OSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMToyOC4zMjVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MjguMzI1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjI4LjMyNVoiLAogInNpemUiOiAiOSIsCiAibWQ1SGFzaCI6ICJBQVBRUzQ2VHJuTVlucWlLQWJhZ3RRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28va21zP2dlbmVyYXRpb249MTUzMDIyMDI4ODMyNTc4OSZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAva21zLzE1MzAyMjAyODgzMjU3ODkvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAia21zIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI4ODMyNTc4OSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0ozeDJ1V2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9rbXMvMTUzMDIyMDI4ODMyNTc4OS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyODgzMjU3ODkiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0ozeDJ1V2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9rbXMvMTUzMDIyMDI4ODMyNTc4OS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyODgzMjU3ODkiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNKM3gydVdpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAva21zLzE1MzAyMjAyODgzMjU3ODkvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28va21zL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyODgzMjU3ODkiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNKM3gydVdpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJVSTc4NUE9PSIsCiAiZXRhZyI6ICJDSjN4MnVXaTk5c0NFQUU9IiwKICJrbXNLZXlOYW1lIjogInByb2plY3RzL2R1bGNldC1wb3J0LTc2Mi9sb2NhdGlvbnMvdXMva2V5UmluZ3MvZ28taW50ZWdyYXRpb24tdGVzdC9jcnlwdG9LZXlzL2tleTEvY3J5cHRvS2V5VmVyc2lvbnMvMSIKfQo=" + } + }, + { + "ID": "f9a26e520ca61b77", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/kms", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f9684eb1e1d05e8da167a9862727f845/2308152449717172484;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/kms" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "9" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:29 GMT" + ], + "Etag": [ + "\"-CJ3x2uWi99sCEAE=\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:11:28 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Encryption-Kms-Key-Name": [ + "projects/dulcet-port-762/locations/us/keyRings/go-integration-test/cryptoKeys/key1/cryptoKeyVersions/1" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:11:28 GMT" + ], + "X-Goog-Generation": [ + "1530220288325789" + ], + "X-Goog-Hash": [ + "crc32c=UI785A==", + "md5=AAPQS46TrnMYnqiKAbagtQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "9" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/59,/bns/xg/borg/xg/bns/blobstore2/bitpusher/150.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=AE81W-ObIom0_QTNlKSoCQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/150.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/150:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoAMUppDLWcJZwslJKEnw2WDtpUnpIvyEkRPHR7FFoflCerdGPmQOiB5rze6KOPsiLdnGEF9l5xazrkfmV9iCL6aFr-mA" + ] + }, + "Body": "bXkgc2VjcmV0" + } + }, + { + "ID": "0412ebd3de62015a", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/kms?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "8a383d34cb15ab0c64bcaf3a2c46c1e9/3923286382916041764;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/kms?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3665" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:31 GMT" + ], + "Etag": [ + "CJ3x2uWi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220590000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbo126:4288,/bns/yw/borg/yw/bns/blobstore2/bitpusher/410.scotty,acatli15:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ak81W8v-Ac2_N9XTiegC" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/410.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/410:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo9m0lK3Up2BsDb8sf07LFwtO0lVJr26y1_oYG4D4rWutvIvkZDOlLUTLVyT7_aGm8scE8uhNT7LTwgHrwsCnVMiIbYJhhgb0wmSwFZu8w6g0r7-fY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9rbXMvMTUzMDIyMDI4ODMyNTc4OSIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcyIsCiAibmFtZSI6ICJrbXMiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDI4ODMyNTc4OSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMToyOC4zMjVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MjguMzI1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjI4LjMyNVoiLAogInNpemUiOiAiOSIsCiAibWQ1SGFzaCI6ICJBQVBRUzQ2VHJuTVlucWlLQWJhZ3RRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28va21zP2dlbmVyYXRpb249MTUzMDIyMDI4ODMyNTc4OSZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAva21zLzE1MzAyMjAyODgzMjU3ODkvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAia21zIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI4ODMyNTc4OSIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0ozeDJ1V2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9rbXMvMTUzMDIyMDI4ODMyNTc4OS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyODgzMjU3ODkiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0ozeDJ1V2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9rbXMvMTUzMDIyMDI4ODMyNTc4OS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2ttcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyODgzMjU3ODkiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNKM3gydVdpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAva21zLzE1MzAyMjAyODgzMjU3ODkvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28va21zL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyODgzMjU3ODkiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNKM3gydVdpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJVSTc4NUE9PSIsCiAiZXRhZyI6ICJDSjN4MnVXaTk5c0NFQUU9IiwKICJrbXNLZXlOYW1lIjogInByb2plY3RzL2R1bGNldC1wb3J0LTc2Mi9sb2NhdGlvbnMvdXMva2V5UmluZ3MvZ28taW50ZWdyYXRpb24tdGVzdC9jcnlwdG9LZXlzL2tleTEvY3J5cHRvS2V5VmVyc2lvbnMvMSIKfQo=" + } + }, + { + "ID": "7e6dcf022410ae4e", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/kms?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7715569a23d3dcf334875c78ff127c2e/4730994634595384884;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/kms?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:31 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220586000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrv125:4223,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=A081W4-XJdPFhgTc2byYBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/485.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/485:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UruJgQ3oLkFnOHqqZNwrk8FxKelE7Vqqd1n11OuF4bT7MBVdjK00NNb5z-xOGFk1dk_dMPHVPOEGMZIRFjuguHcQn-leHE4uWhBLF5_YLgG-RvidOY" + ] + }, + "Body": "" + } + }, + { + "ID": "633aa593fcc27744", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=3de98368efbc140f1e180d7747415edbff8d42e68ba830111d5c3216c21c" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "ec02136f82e58d4bf42d03b3ce9d4bbf/5538421411314925379;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Encryption-Key-Sha256": [ + "Io4lnOPU+EThO0X0nq7mNEXB1rWxZsBI4L37pBmyfDc=" + ] + }, + "Body": "LS0zZGU5ODM2OGVmYmMxNDBmMWUxODBkNzc0NzQxNWVkYmZmOGQ0MmU2OGJhODMwMTExZDVjMzIxNmMyMWMNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsImNhY2hlQ29udHJvbCI6InB1YmxpYywgbWF4LWFnZT02MCIsImNvbnRlbnRUeXBlIjoidGV4dC9wbGFpbiIsIm5hbWUiOiJjc2VrIn0KDQotLTNkZTk4MzY4ZWZiYzE0MGYxZTE4MGQ3NzQ3NDE1ZWRiZmY4ZDQyZTY4YmE4MzAxMTFkNWMzMjE2YzIxYw0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNCm15IHNlY3JldA0KLS0zZGU5ODM2OGVmYmMxNDBmMWUxODBkNzc0NzQxNWVkYmZmOGQ0MmU2OGJhODMwMTExZDVjMzIxNmMyMWMtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3710" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:32 GMT" + ], + "Etag": [ + "CLP9uuei99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220586000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vran9:4304,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=A081W8_ZN8qfhQTz8pv4CA" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/574.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/574:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrZIsUm9YSwh1A5EgsdyUuknjbu2J9Vm4tqTDRwu0EM2AnP4Yueq5xLQOTRqHcW7MZ1ZdCltRXjl0LMzXuB2-2muVYffIgY6b06E8SFrTpGw9_JuYQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jc2VrLzE1MzAyMjAyOTE5OTczNjMiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jc2VrIiwKICJuYW1lIjogImNzZWsiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5MTk5NzM2MyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMTozMS45OTVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzEuOTk1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjMxLjk5NVoiLAogInNpemUiOiAiOSIsCiAibWQ1SGFzaCI6ICJBQVBRUzQ2VHJuTVlucWlLQWJhZ3RRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3Nlaz9nZW5lcmF0aW9uPTE1MzAyMjAyOTE5OTczNjMmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NzZWsvMTUzMDIyMDI5MTk5NzM2My9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3Nlay9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY3NlayIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTE5OTczNjMiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNMUDl1dWVpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3Nlay8xNTMwMjIwMjkxOTk3MzYzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3Nlay9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNzZWsiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjkxOTk3MzYzIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNMUDl1dWVpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3Nlay8xNTMwMjIwMjkxOTk3MzYzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3Nlay9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNzZWsiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjkxOTk3MzYzIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDTFA5dXVlaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NzZWsvMTUzMDIyMDI5MTk5NzM2My91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jc2VrL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNzZWsiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjkxOTk3MzYzIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDTFA5dXVlaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiVUk3ODVBPT0iLAogImV0YWciOiAiQ0xQOXV1ZWk5OXNDRUFFPSIsCiAiY3VzdG9tZXJFbmNyeXB0aW9uIjogewogICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgImtleVNoYTI1NiI6ICJJbzRsbk9QVStFVGhPMFgwbnE3bU5FWEIxcld4WnNCSTRMMzdwQm15ZkRjPSIKIH0KfQo=" + } + }, + { + "ID": "4fb40e9e0680264e", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/csek/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/cmek?alt=json\u0026destinationKmsKeyName=projects%2Fdulcet-port-762%2Flocations%2Fus%2FkeyRings%2Fgo-integration-test%2FcryptoKeys%2Fkey1\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "3" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3de8250c20d101ba4cfe3f60d2f174bc/7081498849987494499;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/csek/rewriteTo/b/go-integration-test-20180628-76155232935339-0000/o/cmek?alt=json\u0026destinationKmsKeyName=projects%2Fdulcet-port-762%2Flocations%2Fus%2FkeyRings%2Fgo-integration-test%2FcryptoKeys%2Fkey1\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ], + "X-Goog-Copy-Source-Encryption-Algorithm": [ + "AES256" + ], + "X-Goog-Copy-Source-Encryption-Key": [ + "REDACTED" + ], + "X-Goog-Copy-Source-Encryption-Key-Sha256": [ + "Io4lnOPU+EThO0X0nq7mNEXB1rWxZsBI4L37pBmyfDc=" + ] + }, + "Body": "e30K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3904" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:33 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220592000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrae2:4360,/bns/yw/borg/yw/bns/blobstore2/bitpusher/168.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=BE81W5zUBoSPN-3XkeAI" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/168.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/168:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp0ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4StK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqQhzsgMbzMRcW2A3AmALzM9Exy4kQfBQBm9vkwQpAEINHfk9lRmU8tuSH-rI2guum1BXiU3kLJGad5aRzAtyrv9AywstiSbX08t3LCsn8Rl8ksyeI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNyZXdyaXRlUmVzcG9uc2UiLAogInRvdGFsQnl0ZXNSZXdyaXR0ZW4iOiAiOSIsCiAib2JqZWN0U2l6ZSI6ICI5IiwKICJkb25lIjogdHJ1ZSwKICJyZXNvdXJjZSI6IHsKICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jbWVrLzE1MzAyMjAyOTM0OTU3MjciLAogICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY21layIsCiAgIm5hbWUiOiAiY21layIsCiAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTM0OTU3MjciLAogICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzMuNDk1WiIsCiAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMTozMy40OTVaIiwKICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMTozMy40OTVaIiwKICAic2l6ZSI6ICI5IiwKICAibWQ1SGFzaCI6ICJBQVBRUzQ2VHJuTVlucWlLQWJhZ3RRPT0iLAogICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NtZWs/Z2VuZXJhdGlvbj0xNTMwMjIwMjkzNDk1NzI3JmFsdD1tZWRpYSIsCiAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICJhY2wiOiBbCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NtZWsvMTUzMDIyMDI5MzQ5NTcyNy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NtZWsvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICJvYmplY3QiOiAiY21layIsCiAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjkzNDk1NzI3IiwKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJvd25lcnMiCiAgICB9LAogICAgImV0YWciOiAiQ0srM2x1aWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY21lay8xNTMwMjIwMjkzNDk1NzI3L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NtZWsvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogImNtZWsiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5MzQ5NTcyNyIsCiAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJPV05FUiIsCiAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICB9LAogICAgImV0YWciOiAiQ0srM2x1aWk5OXNDRUFFPSIKICAgfSwKICAgewogICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY21lay8xNTMwMjIwMjkzNDk1NzI3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NtZWsvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogImNtZWsiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5MzQ5NTcyNyIsCiAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAicm9sZSI6ICJSRUFERVIiLAogICAgInByb2plY3RUZWFtIjogewogICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgfSwKICAgICJldGFnIjogIkNLKzNsdWlpOTlzQ0VBRT0iCiAgIH0sCiAgIHsKICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NtZWsvMTUzMDIyMDI5MzQ5NTcyNy91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY21lay9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAib2JqZWN0IjogImNtZWsiLAogICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5MzQ5NTcyNyIsCiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgInJvbGUiOiAiT1dORVIiLAogICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJldGFnIjogIkNLKzNsdWlpOTlzQ0VBRT0iCiAgIH0KICBdLAogICJvd25lciI6IHsKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogIH0sCiAgImNyYzMyYyI6ICJVSTc4NUE9PSIsCiAgImV0YWciOiAiQ0srM2x1aWk5OXNDRUFFPSIsCiAgImttc0tleU5hbWUiOiAicHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2xvY2F0aW9ucy91cy9rZXlSaW5ncy9nby1pbnRlZ3JhdGlvbi10ZXN0L2NyeXB0b0tleXMva2V5MS9jcnlwdG9LZXlWZXJzaW9ucy8xIgogfQp9Cg==" + } + }, + { + "ID": "10b258aedc47e787", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/cmek", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9965197569e03b08192cd806f1e8dc07/8696632783186363779;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/cmek" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "9" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:33 GMT" + ], + "Etag": [ + "\"-CK+3luii99sCEAE=\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:11:33 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Encryption-Kms-Key-Name": [ + "projects/dulcet-port-762/locations/us/keyRings/go-integration-test/cryptoKeys/key1/cryptoKeyVersions/1" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:11:33 GMT" + ], + "X-Goog-Generation": [ + "1530220293495727" + ], + "X-Goog-Hash": [ + "crc32c=UI785A==", + "md5=AAPQS46TrnMYnqiKAbagtQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "9" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/0,/bns/xg/borg/xg/bns/blobstore2/bitpusher/123.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=BU81W_2JKeW9_QTcj5HYDw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/123.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/123:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Url1VekzaPQc_kBlAgBYW3U5lVnnY0WMmbOQGINsumGFJMRiaT8nYXsnOW9sHF2IQBksZ1xSOQOjEvw9UTWApB9duDfWA" + ] + }, + "Body": "bXkgc2VjcmV0" + } + }, + { + "ID": "ed523e10269abde4", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/cmek?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "79408e37a31c4897da25aacbe786fc40/10239710221858998434;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/cmek?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3705" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:35 GMT" + ], + "Etag": [ + "CK+3luii99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220590000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbc189:4377,/bns/yw/borg/yw/bns/blobstore2/bitpusher/541.scotty,acatli15:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Bk81W52HA8mChQTn2LjwAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/541.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/541:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoFyLBJ6JesuPIsSsBQeLiXaJdaSFHMj9lybgxfR7cIqMx90UTsaaUSyR1hI_v8WCLbWd110uwe3XDPzWROY9GhEItqW7PkezaSiEOXh89KmHR66W4" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jbWVrLzE1MzAyMjAyOTM0OTU3MjciLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jbWVrIiwKICJuYW1lIjogImNtZWsiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5MzQ5NTcyNyIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMTozMy40OTVaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzMuNDk1WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjMzLjQ5NVoiLAogInNpemUiOiAiOSIsCiAibWQ1SGFzaCI6ICJBQVBRUzQ2VHJuTVlucWlLQWJhZ3RRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY21laz9nZW5lcmF0aW9uPTE1MzAyMjAyOTM0OTU3MjcmYWx0PW1lZGlhIiwKICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NtZWsvMTUzMDIyMDI5MzQ5NTcyNy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY21lay9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiY21layIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTM0OTU3MjciLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNLKzNsdWlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY21lay8xNTMwMjIwMjkzNDk1NzI3L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY21lay9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNtZWsiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjkzNDk1NzI3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNLKzNsdWlpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY21lay8xNTMwMjIwMjkzNDk1NzI3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY21lay9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNtZWsiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjkzNDk1NzI3IiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDSyszbHVpaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NtZWsvMTUzMDIyMDI5MzQ5NTcyNy91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jbWVrL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogImNtZWsiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjkzNDk1NzI3IiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDSyszbHVpaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiVUk3ODVBPT0iLAogImV0YWciOiAiQ0srM2x1aWk5OXNDRUFFPSIsCiAia21zS2V5TmFtZSI6ICJwcm9qZWN0cy9kdWxjZXQtcG9ydC03NjIvbG9jYXRpb25zL3VzL2tleVJpbmdzL2dvLWludGVncmF0aW9uLXRlc3QvY3J5cHRvS2V5cy9rZXkxL2NyeXB0b0tleVZlcnNpb25zLzEiCn0K" + } + }, + { + "ID": "0ba8a56c598e4f64", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/csek?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5872c8c38ee2968e250c907440e78caa/11047137002856598194;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/csek?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:35 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220586000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlq72:4470,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=B081W6iDH4aihQTOw5HwBg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/68.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/68:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uol_Nd6vDCRHiR4dZ6hfayYOJjMe9M5IPoEUlUQu-y6dN3UlpnFKkLdIrv5vCJXsHA3rrwsOO0GwFMm7w-Nt7ZXrnXQP0hj5RReldOk5K31bxO_weI" + ] + }, + "Body": "" + } + }, + { + "ID": "b3417d30188f7be1", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/cmek?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "3634b9003ebe4fd230bf7e8a6728b936/11854845250257816514;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/cmek?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:35 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220586000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vri10:4099,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=B081W8qAKoPThQS6q4CABQ" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/608.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/608:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqAvxEqKRxLF-EvEoFIKun1FVzQu2-v0QGwbyz76LFsPX2598zOVUCO3QzXQoP62uID5CzQtS1Bhke7WjLnTiEHaVXo8ohIpGTlvHA2wYSA1jqH_Qk" + ] + }, + "Body": "" + } + }, + { + "ID": "51f3b75ec05d1e39", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "196" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6c4dfdf6426ced45c5e20b5d4b4a85b1/13469979183456751329;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026project=dulcet-port-762" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJlbmNyeXB0aW9uIjp7ImRlZmF1bHRLbXNLZXlOYW1lIjoicHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2xvY2F0aW9ucy91cy9rZXlSaW5ncy9nby1pbnRlZ3JhdGlvbi10ZXN0L2NyeXB0b0tleXMva2V5MSJ9LCJsb2NhdGlvbiI6IlVTIiwibmFtZSI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCJ9Cg==" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "590" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:36 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220595000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrqh26:4149,/bns/yw/borg/yw/bns/blobstore2/bitpusher/355.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=B081W43eNIuDhASy3KG4CA" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/355.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/355:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up5facbLg-t46aj-SpMoDGLr39B0exeTDcwQcvrV7xcNr1_687_hIantVgPyvSswFmhgTRrfftx8yoEenyPnMKy_LZ0ZI6JN1WUZugXLUsPxkQH1TI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzYuMzg1WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjM2LjM4NVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImVuY3J5cHRpb24iOiB7CiAgImRlZmF1bHRLbXNLZXlOYW1lIjogInByb2plY3RzL2R1bGNldC1wb3J0LTc2Mi9sb2NhdGlvbnMvdXMva2V5UmluZ3MvZ28taW50ZWdyYXRpb24tdGVzdC9jcnlwdG9LZXlzL2tleTEiCiB9LAogImxvY2F0aW9uIjogIlVTIiwKICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogImV0YWciOiAiQ0FFPSIKfQo=" + } + }, + { + "ID": "734ba218ec75ead1", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "84c9ec4586ba3b95e0c17f966096f8e2/15012775151447576833;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2863" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:36 GMT" + ], + "Etag": [ + "CAE=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:36 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220590000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrw187:4237,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=CE81W6rHI9GChQTP5Cs" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/139.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/139:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoKHsKupRqdR06Ie7XG2fY3d8FhVpKeURODp478yEf59dRq0PVmMxmMDYTz-pKJYCsHHI3FYSy8C_HlXER1s_Vo_yXnRU36PzYiI1cCAL5TM99GKsk" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzYuMzg1WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjM2LjM4NVoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBRT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUU9IgogIH0KIF0sCiAiZW5jcnlwdGlvbiI6IHsKICAiZGVmYXVsdEttc0tleU5hbWUiOiAicHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2xvY2F0aW9ucy91cy9rZXlSaW5ncy9nby1pbnRlZ3JhdGlvbi10ZXN0L2NyeXB0b0tleXMva2V5MSIKIH0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUU9Igp9Cg==" + } + }, + { + "ID": "07c5adb8b95cb120", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0014/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=970d1214c5fa9441981e0ff11181b3867adcb94c8a1caeb3e816a8449fe8" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f432548c4efaf8d76cda2d9a7ebdaf6f/15820483403126985488;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0014/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS05NzBkMTIxNGM1ZmE5NDQxOTgxZTBmZjExMTgxYjM4NjdhZGNiOTRjOGExY2FlYjNlODE2YTg0NDlmZTgNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsIm5hbWUiOiJrbXMifQoNCi0tOTcwZDEyMTRjNWZhOTQ0MTk4MWUwZmYxMTE4MWIzODY3YWRjYjk0YzhhMWNhZWIzZTgxNmE4NDQ5ZmU4DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KbXkgc2VjcmV0DQotLTk3MGQxMjE0YzVmYTk0NDE5ODFlMGZmMTExODFiMzg2N2FkY2I5NGM4YTFjYWViM2U4MTZhODQ0OWZlOC0tDQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3665" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:38 GMT" + ], + "Etag": [ + "CML6xeqi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220586000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrt64:4383,/bns/yw/borg/yw/bns/blobstore2/bitpusher/174.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=CE81W92UOIXbhQTUy7PoAQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/174.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/174:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo1U7rwyBJmMiw8V_ZP5Wm0bd0JQ0PtzQQaaU9TAOjNzQE8z6N-71DPKwbjT6QSms0YHiLmmPgPug-ZlFqA9HdL7x3gJJXtTXFIQo8nf4L86iiABfU" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9rbXMvMTUzMDIyMDI5ODQ2ODY3NCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcyIsCiAibmFtZSI6ICJrbXMiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5ODQ2ODY3NCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMTozOC40NjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzguNDY4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjM4LjQ2OFoiLAogInNpemUiOiAiOSIsCiAibWQ1SGFzaCI6ICJBQVBRUzQ2VHJuTVlucWlLQWJhZ3RRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE0L28va21zP2dlbmVyYXRpb249MTUzMDIyMDI5ODQ2ODY3NCZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQva21zLzE1MzAyMjAyOTg0Njg2NzQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAgICJvYmplY3QiOiAia21zIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5ODQ2ODY3NCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ01MNnhlcWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9rbXMvMTUzMDIyMDI5ODQ2ODY3NC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTg0Njg2NzQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ01MNnhlcWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9rbXMvMTUzMDIyMDI5ODQ2ODY3NC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTg0Njg2NzQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNNTDZ4ZXFpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQva21zLzE1MzAyMjAyOTg0Njg2NzQvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE0L28va21zL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTg0Njg2NzQiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNNTDZ4ZXFpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJVSTc4NUE9PSIsCiAiZXRhZyI6ICJDTUw2eGVxaTk5c0NFQUU9IiwKICJrbXNLZXlOYW1lIjogInByb2plY3RzL2R1bGNldC1wb3J0LTc2Mi9sb2NhdGlvbnMvdXMva2V5UmluZ3MvZ28taW50ZWdyYXRpb24tdGVzdC9jcnlwdG9LZXlzL2tleTEvY3J5cHRvS2V5VmVyc2lvbnMvMSIKfQo=" + } + }, + { + "ID": "886edd5b7cdc99fd", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0014/kms", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "152834c589e924a1092675a002f3aa8d/17435617336325854768;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0014/kms" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "9" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:38 GMT" + ], + "Etag": [ + "\"-CML6xeqi99sCEAE=\"" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:11:38 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Encryption-Kms-Key-Name": [ + "projects/dulcet-port-762/locations/us/keyRings/go-integration-test/cryptoKeys/key1/cryptoKeyVersions/1" + ], + "X-Goog-Generation": [ + "1530220298468674" + ], + "X-Goog-Hash": [ + "crc32c=UI785A==", + "md5=AAPQS46TrnMYnqiKAbagtQ==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "9" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/15,/bns/xg/borg/xg/bns/blobstore2/bitpusher/115.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ck81W6fWI8-5_QSv76r4Dw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/115.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/115:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoFKgIGqweOR0IZjXNJ80XPpbsC6KBPtJgE08OqphqxZabR2ym_MK0SoVE5FayEz8WNkGBq0mpHCZA_ndlcYF6dguvA0g" + ] + }, + "Body": "bXkgc2VjcmV0" + } + }, + { + "ID": "e828719bfa8f598d", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014/o/kms?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d58b2a1fb8f624ab198028501b5f937c/532232171970746703;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014/o/kms?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3665" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:38 GMT" + ], + "Etag": [ + "CML6xeqi99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220590000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjo3:4099,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ck81W5fYNNOkhATsyYKoCg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/521.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/521:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpTmBrHuhWqa5u4tmkK6YcFAx3SuPkD9MuB_8X2bGQHRqztfxRyR2YKnQdKy1ajHrXHmu0mGupzbn1qHDYgbq1J2aeq_2UxWrAy5Uom3-p-gIniM6k" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9rbXMvMTUzMDIyMDI5ODQ2ODY3NCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcyIsCiAibmFtZSI6ICJrbXMiLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5ODQ2ODY3NCIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMTozOC40NjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzguNDY4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjM4LjQ2OFoiLAogInNpemUiOiAiOSIsCiAibWQ1SGFzaCI6ICJBQVBRUzQ2VHJuTVlucWlLQWJhZ3RRPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE0L28va21zP2dlbmVyYXRpb249MTUzMDIyMDI5ODQ2ODY3NCZhbHQ9bWVkaWEiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQva21zLzE1MzAyMjAyOTg0Njg2NzQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAgICJvYmplY3QiOiAia21zIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDI5ODQ2ODY3NCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ01MNnhlcWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9rbXMvMTUzMDIyMDI5ODQ2ODY3NC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTg0Njg2NzQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ01MNnhlcWk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9rbXMvMTUzMDIyMDI5ODQ2ODY3NC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9vL2ttcy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTg0Njg2NzQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNNTDZ4ZXFpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQva21zLzE1MzAyMjAyOTg0Njg2NzQvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE0L28va21zL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAib2JqZWN0IjogImttcyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyOTg0Njg2NzQiLAogICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICJldGFnIjogIkNNTDZ4ZXFpOTlzQ0VBRT0iCiAgfQogXSwKICJvd25lciI6IHsKICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiB9LAogImNyYzMyYyI6ICJVSTc4NUE9PSIsCiAiZXRhZyI6ICJDTUw2eGVxaTk5c0NFQUU9IiwKICJrbXNLZXlOYW1lIjogInByb2plY3RzL2R1bGNldC1wb3J0LTc2Mi9sb2NhdGlvbnMvdXMva2V5UmluZ3MvZ28taW50ZWdyYXRpb24tdGVzdC9jcnlwdG9LZXlzL2tleTEvY3J5cHRvS2V5VmVyc2lvbnMvMSIKfQo=" + } + }, + { + "ID": "5ae6c7af1624be28", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014/o/kms?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "69f40d54336587fa430e7f6708aa2a73/1339658952985123423;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014/o/kms?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:39 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220595000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrss4:4278,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Ck81W4DTPIaIN5_KqfgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/157.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/157:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqCMMx8UJ06wKt2-5YnqdryzZXrRzU7LFtw1XbJglioPHYegazcgX9W_DW7tr33UnaQfF-UnzWQLJAC177CV3vbBuAeSeJC_HXJdMc02Eh6-PYNeRQ" + ] + }, + "Body": "" + } + }, + { + "ID": "b0e29fb8deab476f", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "122" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "e81d0b0afb20039a77f9775c2a152358/2882736391657692543;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJlbmNyeXB0aW9uIjp7ImRlZmF1bHRLbXNLZXlOYW1lIjoicHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2xvY2F0aW9ucy91cy9rZXlSaW5ncy9nby1pbnRlZ3JhdGlvbi10ZXN0L2NyeXB0b0tleXMva2V5MiJ9fQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2863" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:40 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220599000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcf9:4485,/bns/yw/borg/yw/bns/blobstore2/bitpusher/395.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=C081W7_kE8LBhQTk4ZewAw" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/395.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/395:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoB_5FtQd4d7VxKk4tFU6AEmxRIYMp3dXMN9jZsCC6ahLlFZRAHySdM00_xOXlc0BwVgB3aN8DGGmIO39Rb_n7nG1t5bqNViiUuo8JN3ii4RyjmLEI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzYuMzg1WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjQwLjYxN1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAiZW5jcnlwdGlvbiI6IHsKICAiZGVmYXVsdEttc0tleU5hbWUiOiAicHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2xvY2F0aW9ucy91cy9rZXlSaW5ncy9nby1pbnRlZ3JhdGlvbi10ZXN0L2NyeXB0b0tleXMva2V5MiIKIH0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "0045e42f95bfa051", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fa6988bb926d3b54c2c5afca944c6ff2/4497870324856627358;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2863" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:41 GMT" + ], + "Etag": [ + "CAI=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:41 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220600000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrd4:4404,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=DE81W7fpM4H1N7-RjPgH" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/198.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/198:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrviBH6w3eQrxbrJbdI5roLf-Z43peyXKcawyB3_keVh2XotWdmVMQikTinEmBXWOml3Kfiuh2cPmUIz_CjV7Zw2gj-X2q1aZr5cw3h5HxLWEFYWwY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzYuMzg1WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjQwLjYxN1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBST0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQUk9IgogIH0KIF0sCiAiZW5jcnlwdGlvbiI6IHsKICAiZGVmYXVsdEttc0tleU5hbWUiOiAicHJvamVjdHMvZHVsY2V0LXBvcnQtNzYyL2xvY2F0aW9ucy91cy9rZXlSaW5ncy9nby1pbnRlZ3JhdGlvbi10ZXN0L2NyeXB0b0tleXMva2V5MiIKIH0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQUk9Igp9Cg==" + } + }, + { + "ID": "70e1da18e7a00bb1", + "Request": { + "Method": "PATCH", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Length": [ + "20" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "66684a1fc181903effbfe0f689ace285/6113005353255445438;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "eyJlbmNyeXB0aW9uIjpudWxsfQo=" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "2734" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:42 GMT" + ], + "Etag": [ + "CAM=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220601000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdn3:4337,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=DU81W__0BsfUN9mJjPgB" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/435.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/435:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATpxChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4CtK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpBzKmsTzUSdbavCHYO8tudKiIyGhZNjT6FaVROMxhEEctBLfXYGyyuBX-FTxm2CKeBYbnJHQzGAbnrkD-j8OkJteesxUTH2CVK-I_o706IFNL9bzI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6MzYuMzg1WiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjQyLjcxOFoiLAogIm1ldGFnZW5lcmF0aW9uIjogIjMiLAogImFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0FNPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FNPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTQiLAogICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIlJFQURFUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgIH0sCiAgICJldGFnIjogIkNBTT0iCiAgfQogXSwKICJkZWZhdWx0T2JqZWN0QWNsIjogWwogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAib3duZXJzIgogICB9LAogICAiZXRhZyI6ICJDQU09IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJyb2xlIjogIk9XTkVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAiZWRpdG9ycyIKICAgfSwKICAgImV0YWciOiAiQ0FNPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQU09IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKIH0sCiAibG9jYXRpb24iOiAiVVMiLAogInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAiZXRhZyI6ICJDQU09Igp9Cg==" + } + }, + { + "ID": "5caf3c5dd237839c", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "83cc19b85d39d5c67d62712e56a08e0f/7656082791928080093;o=0" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0014?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220586000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrii75:4136,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Dk81W4DIOIy7N5zWgrgN" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/575.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/575:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBVVdDcWRTdDJLV0ZNb2J2X2hBTTVsbGlPQV9aQVNvVnFKckhLUTR3UnZRNTNkckh2azdXWXZERFR1V3d2U2tVZks0azJEbTMtbF9IYTRwd05tR0xGS2pVMVA2QmlWV01yVWFsYXY4QUp3OGxTOFFVN3VYeTFIdWl2eEtObFg5bzB1RUhCOEdnb3VZbW1HOHVBVHVpZ2ZuQllEXzlyTk5wTU9OVXA4dEh4UUtFXzYybExrdHlneHB5d3cwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up7H8NuvZUo6ZFGXnwQBBX5Vi-UGGeEGby8YC80LoXt2QfQ1E3TwRaLfCffl1hbw60SSAbr5HK1lUeZ_wX7pebf5DcVYtTjxUxNjNBh-VogZ_GQ5dk" + ] + }, + "Body": "" + } + }, + { + "ID": "09e2c95b43d436ab", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "a908f397ddcb46462c7368f3d5ac308d/11621720944797183532;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "2952" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Etag": [ + "CAw=" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220603000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vraz190:4268,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D081W8nGG4XGhQT1vofYAg" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/288.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/288:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEdYV3VTR2RESGI3UjZIOE1WOEhIU204SEN4MkpOOXpJdXRSSFNqUXFkNW14cnRVaXJYWG1nd1RFUGxVbDlxbE9xZjVydnBVeElid3Z0Nl9BekRaVDl2T0VOLWZBYmlsb2JXUURiSjQ5cGw0azZORFJFUW1wcFNaMS1XMndMcThQZXM2ZWExd3Axc2JGWXVITTBtOHljaHlZWjl0azhvWmVyaW53Y1FUOUg4UW5Na2E5WjcwNUZTSEkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Urke2Ehi0gEOjUyMCPa5bdXfek_9vxnYgCOdQmE42yDCJw11w1yOCEKWbhHce-O-fjTH2B0h5WdEjuj24NfHQvYM2DNEhpnXTQm5pgtIF6H1B5XLKs" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MTYuMzYzWiIsCiAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjQzLjUxN1oiLAogIm1ldGFnZW5lcmF0aW9uIjogIjEyIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJlZGl0b3JzIgogICB9LAogICAiZXRhZyI6ICJDQXc9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNBdz0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDQXc9IgogIH0KIF0sCiAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogIm93bmVycyIKICAgfSwKICAgImV0YWciOiAiQ0F3PSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNBdz0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiUkVBREVSIiwKICAgInByb2plY3RUZWFtIjogewogICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgInRlYW0iOiAidmlld2VycyIKICAgfSwKICAgImV0YWciOiAiQ0F3PSIKICB9CiBdLAogIm93bmVyIjogewogICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiB9LAogImxvY2F0aW9uIjogIlVTIiwKICJ2ZXJzaW9uaW5nIjogewogICJlbmFibGVkIjogZmFsc2UKIH0sCiAibGlmZWN5Y2xlIjogewogICJydWxlIjogWwogICB7CiAgICAiYWN0aW9uIjogewogICAgICJ0eXBlIjogIkRlbGV0ZSIKICAgIH0sCiAgICAiY29uZGl0aW9uIjogewogICAgICJhZ2UiOiAzMAogICAgfQogICB9CiAgXQogfSwKICJsYWJlbHMiOiB7CiAgImwxIjogInYyIiwKICAibmV3IjogIm5ldyIKIH0sCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJldGFnIjogIkNBdz0iCn0K" + } + }, + { + "ID": "05180cbd256a8a72", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/storage-library-test-bucket/Caf%C3%A9", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "10f82ece6f4b035efd0b8a995bd57964/3870836460309629149;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/storage-library-test-bucket/Caf%C3%A9" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "20" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Etag": [ + "\"ade43306cb39336d630e101af5fb51b4\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:11:43 GMT" + ], + "Last-Modified": [ + "Fri, 24 Mar 2017 20:04:38 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1490385878535828" + ], + "X-Goog-Hash": [ + "crc32c=fN3yZg==", + "md5=reQzBss5M21jDhAa9ftRtA==" + ], + "X-Goog-Metageneration": [ + "2" + ], + "X-Goog-Storage-Class": [ + "MULTI_REGIONAL" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "20" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/1,/bns/xg/borg/xg/bns/blobstore2/bitpusher/67.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D081W6izKO-5_QSkq6PACQ" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "149776848335" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/67.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/67:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoIga7kkwZmScwCxNNTyIpUoXivZ13tCdPbTshzlreAJsMGonpNiIK_FuHEiaTOIkavHPvz7RhHhVc3mWQGWm_YbSHPGg" + ] + }, + "Body": "Tm9ybWFsaXphdGlvbiBGb3JtIEM=" + } + }, + { + "ID": "5f7eebff3ddfa531", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0015?alt=json\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "36ee8e5a99ebc6e83381d98a814e41e8/4678263241324005869;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0015?alt=json\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12271" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220603000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrna13:4488,/bns/yw/borg/yw/bns/blobstore2/bitpusher/450.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D081W9eDKcSIN-P5taAD" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/450.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/450:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEdYV3VTR2RESGI3UjZIOE1WOEhIU204SEN4MkpOOXpJdXRSSFNqUXFkNW14cnRVaXJYWG1nd1RFUGxVbDlxbE9xZjVydnBVeElid3Z0Nl9BekRaVDl2T0VOLWZBYmlsb2JXUURiSjQ5cGw0azZORFJFUW1wcFNaMS1XMndMcThQZXM2ZWExd3Axc2JGWXVITTBtOHljaHlZWjl0azhvWmVyaW53Y1FUOUg4UW5Na2E5WjcwNUZTSEkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrBntH1cCTXRHeLtJyhcrP0CaesAsffft-_NYJ2XyivVT_Y5YklJcudNIj5s3BVtXCjCnq7cjGzsYwtb0C9GApHdFDNLU5Ri5bW_NxZgfQXt73SB9E" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkJVQ0tFVF9OT1RfRk9VTkQ6IEJVQ0tFVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggYnVja2V0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTVcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjEwMilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkdldEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0QnVja2V0LmphdmE6MzEpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5CdWNrZXRzRGVsZWdhdG9yLmdldChCdWNrZXRzRGVsZWdhdG9yLmphdmE6NzQpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBCVUNLRVRfTk9UX0ZPVU5EOiBObyBzdWNoIGJ1Y2tldDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE1XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1OT1RfRk9VTkQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QlVDS0VUX05PVF9GT1VORDogQlVDS0VUX05PVF9GT1VORDogTm8gc3VjaCBidWNrZXQ6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkdldEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0QnVja2V0LmphdmE6MTAyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YTozMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IuZ2V0KEJ1Y2tldHNEZWxlZ2F0b3IuamF2YTo3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEJVQ0tFVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggYnVja2V0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTVcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPW5vdEZvdW5kLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5OT1RfRk9VTkQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpCVUNLRVRfTk9UX0ZPVU5EOiBCVUNLRVRfTk9UX0ZPVU5EOiBObyBzdWNoIGJ1Y2tldDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE1XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YToxMDIpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMuYnVja2V0cy5HZXRCdWNrZXQuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKEdldEJ1Y2tldC5qYXZhOjMxKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uQnVja2V0c0RlbGVnYXRvci5nZXQoQnVja2V0c0RlbGVnYXRvci5qYXZhOjc0KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQlVDS0VUX05PVF9GT1VORDogTm8gc3VjaCBidWNrZXQ6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LnJlc291cmNlX2lkLm5hbWUsIG1lc3NhZ2U9bnVsbCwgdW5uYW1lZEFyZ3VtZW50cz1bXX0sIGxvY2F0aW9uPWVudGl0eS5yZXNvdXJjZV9pZC5uYW1lLCBtZXNzYWdlPU5vdCBGb3VuZCwgcmVhc29uPW5vdEZvdW5kLCBycGNDb2RlPTQwNH0gTm90IEZvdW5kOiBjb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QlVDS0VUX05PVF9GT1VORDogQlVDS0VUX05PVF9GT1VORDogTm8gc3VjaCBidWNrZXQ6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5idWNrZXRzLkdldEJ1Y2tldC5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoR2V0QnVja2V0LmphdmE6MTAyKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLmJ1Y2tldHMuR2V0QnVja2V0LmhhbmRsZVJlcXVlc3RSZWNlaXZlZChHZXRCdWNrZXQuamF2YTozMSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLkJ1Y2tldHNEZWxlZ2F0b3IuZ2V0KEJ1Y2tldHNEZWxlZ2F0b3IuamF2YTo3NClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEJVQ0tFVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggYnVja2V0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTVcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcblxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS5FcnJvckNvbGxlY3Rvci50b0ZhdWx0KEVycm9yQ29sbGVjdG9yLmphdmE6NTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5RXJyb3JDb252ZXJ0ZXIudG9GYXVsdChSb3N5RXJyb3JDb252ZXJ0ZXIuamF2YTo2Nylcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjI1OClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnJlc3QuYWRhcHRlci5yb3N5LlJvc3lIYW5kbGVyJDIuY2FsbChSb3N5SGFuZGxlci5qYXZhOjIzOClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuTW9yZUV4ZWN1dG9ycyREaXJlY3RFeGVjdXRvci5leGVjdXRlKE1vcmVFeGVjdXRvcnMuamF2YTo0MDIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5leGVjdXRlTGlzdGVuZXIoQWJzdHJhY3RGdXR1cmUuamF2YToxMDI5KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuY29tcGxldGUoQWJzdHJhY3RGdXR1cmUuamF2YTo4NzEpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5zZXQoQWJzdHJhY3RGdXR1cmUuamF2YTo2OTQpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci50aHJlYWQuVGhyZWFkVHJhY2tlcnMkVGhyZWFkVHJhY2tpbmdSdW5uYWJsZS5ydW4oVGhyZWFkVHJhY2tlcnMuamF2YToxMjYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KFRyYWNlQ29udGV4dC5qYXZhOjQ1NSlcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLnNlcnZlci5Db21tb25Nb2R1bGUkQ29udGV4dENhcnJ5aW5nRXhlY3V0b3JTZXJ2aWNlJDEucnVuSW5Db250ZXh0KENvbW1vbk1vZHVsZS5qYXZhOjg0Nilcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRUcmFjZUNvbnRleHRSdW5uYWJsZSQxLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NjIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKFRyYWNlQ29udGV4dC5qYXZhOjMyMSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLlRyYWNlQ29udGV4dCRBYnN0cmFjdFRyYWNlQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTozMTMpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUucnVuKFRyYWNlQ29udGV4dC5qYXZhOjQ1OSlcblx0YXQgY29tLmdvb2dsZS5nc2UuaW50ZXJuYWwuRGlzcGF0Y2hRdWV1ZUltcGwkV29ya2VyVGhyZWFkLnJ1bihEaXNwYXRjaFF1ZXVlSW1wbC5qYXZhOjQwMylcbiIKICAgfQogIF0sCiAgImNvZGUiOiA0MDQsCiAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIgogfQp9Cg==" + } + }, + { + "ID": "c43f1bd50fa61a57", + "Request": { + "Method": "POST", + "URL": "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "Content-Type": [ + "multipart/related; boundary=2bc79de2a79c362dad599d516755d389d0bd86e52f94845621fe06b300ae" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7e85676cff9e30b80bba41052c188a1f/5413632428300454909;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/upload/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026projection=full\u0026uploadType=multipart" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "LS0yYmM3OWRlMmE3OWMzNjJkYWQ1OTlkNTE2NzU1ZDM4OWQwYmQ4NmU1MmY5NDg0NTYyMWZlMDZiMzAwYWUNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbg0KDQp7ImJ1Y2tldCI6ImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsIm5hbWUiOiJ6ZXJvIn0KDQotLTJiYzc5ZGUyYTc5YzM2MmRhZDU5OWQ1MTY3NTVkMzg5ZDBiZDg2ZTUyZjk0ODQ1NjIxZmUwNmIzMDBhZQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQoNCg0KLS0yYmM3OWRlMmE3OWMzNjJkYWQ1OTlkNTE2NzU1ZDM4OWQwYmQ4NmU1MmY5NDg0NTYyMWZlMDZiMzAwYWUtLQ0K" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "3560" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Etag": [ + "CIfKj+2i99sCEAE=" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220603000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlf10:4457,/bns/yw/borg/yw/bns/blobstore2/bitpusher/456.scotty,acatli15:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D081W_2NL8SGhAS92oPIAw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "acatli15:443,/bns/yw/borg/yw/bns/blobstore2/bitpusher/456.scotty,acatli15:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yw/borg/yw/bns/blobstore2/bitpusher/456:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEdYV3VTR2RESGI3UjZIOE1WOEhIU204SEN4MkpOOXpJdXRSSFNqUXFkNW14cnRVaXJYWG1nd1RFUGxVbDlxbE9xZjVydnBVeElid3Z0Nl9BekRaVDl2T0VOLWZBYmlsb2JXUURiSjQ5cGw0azZORFJFUW1wcFNaMS1XMndMcThQZXM2ZWExd3Axc2JGWXVITTBtOHljaHlZWjl0azhvWmVyaW53Y1FUOUg4UW5Na2E5WjcwNUZTSEkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_single_post_uploads" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uoyzk0DF9SBps-iwu1yUs4n7G014Hz9ZkBTIhigAphBuZAry7k2vpmxZrLqsEwMyZmoLZqcB8nyzTj9j3Bo637tbYw6-AjWanp6XF_JG-InKHp99bI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC96ZXJvLzE1MzAyMjAzMDM4NjkxOTEiLAogInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby96ZXJvIiwKICJuYW1lIjogInplcm8iLAogImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogImdlbmVyYXRpb24iOiAiMTUzMDIyMDMwMzg2OTE5MSIsCiAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMTo0My44NjhaIiwKICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTE6NDMuODY4WiIsCiAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjExOjQzLjg2OFoiLAogInNpemUiOiAiMCIsCiAibWQ1SGFzaCI6ICIxQjJNMlk4QXNnVHBnQW1ZN1BoQ2ZnPT0iLAogIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vemVybz9nZW5lcmF0aW9uPTE1MzAyMjAzMDM4NjkxOTEmYWx0PW1lZGlhIiwKICJhY2wiOiBbCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3plcm8vMTUzMDIyMDMwMzg2OTE5MS9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vemVyby9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJvYmplY3QiOiAiemVybyIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAzMDM4NjkxOTEiLAogICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgInJvbGUiOiAiT1dORVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJvd25lcnMiCiAgIH0sCiAgICJldGFnIjogIkNJZktqKzJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvemVyby8xNTMwMjIwMzAzODY5MTkxL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vemVyby9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInplcm8iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMzAzODY5MTkxIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJPV05FUiIsCiAgICJwcm9qZWN0VGVhbSI6IHsKICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgIH0sCiAgICJldGFnIjogIkNJZktqKzJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvemVyby8xNTMwMjIwMzAzODY5MTkxL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vemVyby9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInplcm8iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMzAzODY5MTkxIiwKICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAicm9sZSI6ICJSRUFERVIiLAogICAicHJvamVjdFRlYW0iOiB7CiAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICB9LAogICAiZXRhZyI6ICJDSWZLaisyaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3plcm8vMTUzMDIyMDMwMzg2OTE5MS91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby96ZXJvL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAib2JqZWN0IjogInplcm8iLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMzAzODY5MTkxIiwKICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgInJvbGUiOiAiT1dORVIiLAogICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAiZXRhZyI6ICJDSWZLaisyaTk5c0NFQUU9IgogIH0KIF0sCiAib3duZXIiOiB7CiAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogfSwKICJjcmMzMmMiOiAiQUFBQUFBPT0iLAogImV0YWciOiAiQ0lmS2orMmk5OXNDRUFFPSIKfQo=" + } + }, + { + "ID": "94a3065edb567ff7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0015/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5f5a2f07064707cd37c7634ef43f8d3d/6221340679996574733;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0015/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 404, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "12287" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220603000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrv6:4082,/bns/yv/borg/yv/bns/blobstore2/bitpusher/501.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D081W6StL4GJggS6haTgCw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/501.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/501:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEdYV3VTR2RESGI3UjZIOE1WOEhIU204SEN4MkpOOXpJdXRSSFNqUXFkNW14cnRVaXJYWG1nd1RFUGxVbDlxbE9xZjVydnBVeElid3Z0Nl9BekRaVDl2T0VOLWZBYmlsb2JXUURiSjQ5cGw0azZORFJFUW1wcFNaMS1XMndMcThQZXM2ZWExd3Axc2JGWXVITTBtOHljaHlZWjl0azhvWmVyaW53Y1FUOUg4UW5Na2E5WjcwNUZTSEkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "agent_rejected" + ], + "X-Guploader-Upload-Result": [ + "agent_rejected" + ], + "X-Guploader-Uploadid": [ + "AEnB2UomILTgAgwPo3qpH_zk--6cVZWm0RqGNTELpNt-JvKWd1Vd0gBUQdpzUOd7nYZVWA1IOO2TVg-sz4pbitGbRjS94Xr9yV3tDbSaMQfCA4W353Stu1s" + ] + }, + "Body": "ewogImVycm9yIjogewogICJlcnJvcnMiOiBbCiAgIHsKICAgICJkb21haW4iOiAiZ2xvYmFsIiwKICAgICJyZWFzb24iOiAibm90Rm91bmQiLAogICAgIm1lc3NhZ2UiOiAiTm90IEZvdW5kIiwKICAgICJkZWJ1Z0luZm8iOiAiY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkJVQ0tFVF9OT1RfRk9VTkQ6IEJVQ0tFVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggYnVja2V0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTVcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5MaXN0T2JqZWN0cy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdE9iamVjdHMuamF2YToxNjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5MaXN0T2JqZWN0cy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdE9iamVjdHMuamF2YTozOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IubGlzdChPYmplY3RzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBCVUNLRVRfTk9UX0ZPVU5EOiBObyBzdWNoIGJ1Y2tldDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE1XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG5cbmNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLkZhdWx0OiBJbW11dGFibGVFcnJvckRlZmluaXRpb257YmFzZT1OT1RfRk9VTkQsIGNhdGVnb3J5PVVTRVJfRVJST1IsIGNhdXNlPW51bGwsIGRlYnVnSW5mbz1jb20uZ29vZ2xlLm5ldC5ycGMzLlJwY0V4Y2VwdGlvbjogY2xvdWQuYmlnc3RvcmUuUmVzcG9uc2VDb2RlLkVycm9yQ29kZTo6QlVDS0VUX05PVF9GT1VORDogQlVDS0VUX05PVF9GT1VORDogTm8gc3VjaCBidWNrZXQ6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50b1JwYzNFeGNlcHRpb24oQmlnc3RvcmVFeGNlcHRpb24uamF2YToxMTgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjk3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo1Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkxpc3RPYmplY3RzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0T2JqZWN0cy5qYXZhOjE2MClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5oYW5kbGVycy5vYmplY3RzLkxpc3RPYmplY3RzLmhhbmRsZVJlcXVlc3RSZWNlaXZlZChMaXN0T2JqZWN0cy5qYXZhOjM4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5SZXF1ZXN0SGFuZGxlci5oYW5kbGUoUmVxdWVzdEhhbmRsZXIuamF2YTozMDMpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uT2JqZWN0c0RlbGVnYXRvci5saXN0KE9iamVjdHNEZWxlZ2F0b3IuamF2YTo4OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uUnBjUmVjZWl2ZXIubGFtYmRhJHByb2Nlc3NSZXF1ZXN0QXN5bmMkMyhScGNSZWNlaXZlci5qYXZhOjE2OSlcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5pc29sYXRpb24uQXN5bmNFeGVjdXRvci5sYW1iZGEkc3VibWl0JDAoQXN5bmNFeGVjdXRvci5qYXZhOjI0Mylcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuSW5Db250ZXh0KENvbnRleHRSdW5uYWJsZS5qYXZhOjUwKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZSQxLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozOSlcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkN1cnJlbnRDb250ZXh0LnJ1bkluQ29udGV4dChDdXJyZW50Q29udGV4dC5qYXZhOjMyMClcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NzIpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dChHZW5lcmljQ29udGV4dENhbGxiYWNrLmphdmE6NjQpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bihDb250ZXh0UnVubmFibGUuamF2YTozNilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRXhlY3V0b3JzJFJ1bm5hYmxlQWRhcHRlci5jYWxsKEV4ZWN1dG9ycy5qYXZhOjUxMSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuRnV0dXJlVGFzay5ydW4oRnV0dXJlVGFzay5qYXZhOjI2Nilcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yLnJ1bldvcmtlcihUaHJlYWRQb29sRXhlY3V0b3IuamF2YToxMTQ5KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IkV29ya2VyLnJ1bihUaHJlYWRQb29sRXhlY3V0b3IuamF2YTo2MjQpXG5cdGF0IGphdmEubGFuZy5UaHJlYWQucnVuKFRocmVhZC5qYXZhOjc0OClcbkNhdXNlZCBieTogY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb246IEJVQ0tFVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggYnVja2V0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTVcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udGhyb3dPbkVycm9yKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6Mjc2KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Mylcblx0Li4uIDE4IG1vcmVcbiwgZG9tYWluPWdsb2JhbCwgZXh0ZW5kZWRIZWxwPW51bGwsIGh0dHBIZWFkZXJzPXt9LCBodHRwU3RhdHVzPW5vdEZvdW5kLCBpbnRlcm5hbFJlYXNvbj1SZWFzb257YXJndW1lbnRzPXt9LCBjYXVzZT1udWxsLCBjb2RlPWdkYXRhLkNvcmVFcnJvckRvbWFpbi5OT1RfRk9VTkQsIGNyZWF0ZWRCeUJhY2tlbmQ9dHJ1ZSwgZGVidWdNZXNzYWdlPWNvbS5nb29nbGUubmV0LnJwYzMuUnBjRXhjZXB0aW9uOiBjbG91ZC5iaWdzdG9yZS5SZXNwb25zZUNvZGUuRXJyb3JDb2RlOjpCVUNLRVRfTk9UX0ZPVU5EOiBCVUNLRVRfTk9UX0ZPVU5EOiBObyBzdWNoIGJ1Y2tldDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE1XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRvUnBjM0V4Y2VwdGlvbihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjExOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjU3KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuTGlzdE9iamVjdHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RPYmplY3RzLmphdmE6MTYwKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmhhbmRsZXJzLm9iamVjdHMuTGlzdE9iamVjdHMuaGFuZGxlUmVxdWVzdFJlY2VpdmVkKExpc3RPYmplY3RzLmphdmE6MzgpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLlJlcXVlc3RIYW5kbGVyLmhhbmRsZShSZXF1ZXN0SGFuZGxlci5qYXZhOjMwMylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5PYmplY3RzRGVsZWdhdG9yLmxpc3QoT2JqZWN0c0RlbGVnYXRvci5qYXZhOjg5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5ScGNSZWNlaXZlci5sYW1iZGEkcHJvY2Vzc1JlcXVlc3RBc3luYyQzKFJwY1JlY2VpdmVyLmphdmE6MTY5KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmlzb2xhdGlvbi5Bc3luY0V4ZWN1dG9yLmxhbWJkYSRzdWJtaXQkMChBc3luY0V4ZWN1dG9yLmphdmE6MjQzKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW5JbkNvbnRleHQoQ29udGV4dFJ1bm5hYmxlLmphdmE6NTApXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlJDEucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM5KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHROb1VucmVmKEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo3Milcblx0YXQgY29tLmdvb2dsZS50cmFjaW5nLkdlbmVyaWNDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0KEdlbmVyaWNDb250ZXh0Q2FsbGJhY2suamF2YTo2NClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUucnVuKENvbnRleHRSdW5uYWJsZS5qYXZhOjM2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5FeGVjdXRvcnMkUnVubmFibGVBZGFwdGVyLmNhbGwoRXhlY3V0b3JzLmphdmE6NTExKVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5GdXR1cmVUYXNrLnJ1bihGdXR1cmVUYXNrLmphdmE6MjY2KVxuXHRhdCBqYXZhLnV0aWwuY29uY3VycmVudC5UaHJlYWRQb29sRXhlY3V0b3IucnVuV29ya2VyKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjExNDkpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvciRXb3JrZXIucnVuKFRocmVhZFBvb2xFeGVjdXRvci5qYXZhOjYyNClcblx0YXQgamF2YS5sYW5nLlRocmVhZC5ydW4oVGhyZWFkLmphdmE6NzQ4KVxuQ2F1c2VkIGJ5OiBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbjogQlVDS0VUX05PVF9GT1VORDogTm8gc3VjaCBidWNrZXQ6IGdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxNVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmNvbW1vbi5CaWdzdG9yZUV4Y2VwdGlvbi50aHJvd09uRXJyb3IoQmlnc3RvcmVFeGNlcHRpb24uamF2YToyNzYpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uZnJhbWV3b3JrLkJhY2tlbmRDYWxsVXRpbC5jYWxsKEJhY2tlbmRDYWxsVXRpbC5qYXZhOjkzKVxuXHQuLi4gMTggbW9yZVxuLCBlcnJvclByb3RvQ29kZT1OT1RfRk9VTkQsIGVycm9yUHJvdG9Eb21haW49Z2RhdGEuQ29yZUVycm9yRG9tYWluLCBmaWx0ZXJlZE1lc3NhZ2U9bnVsbCwgbG9jYXRpb249ZW50aXR5LmJ1Y2tldCwgbWVzc2FnZT1udWxsLCB1bm5hbWVkQXJndW1lbnRzPVtdfSwgbG9jYXRpb249ZW50aXR5LmJ1Y2tldCwgbWVzc2FnZT1Ob3QgRm91bmQsIHJlYXNvbj1ub3RGb3VuZCwgcnBjQ29kZT00MDR9IE5vdCBGb3VuZDogY29tLmdvb2dsZS5uZXQucnBjMy5ScGNFeGNlcHRpb246IGNsb3VkLmJpZ3N0b3JlLlJlc3BvbnNlQ29kZS5FcnJvckNvZGU6OkJVQ0tFVF9OT1RfRk9VTkQ6IEJVQ0tFVF9OT1RfRk9VTkQ6IE5vIHN1Y2ggYnVja2V0OiBnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTVcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5jb21tb24uQmlnc3RvcmVFeGNlcHRpb24udG9ScGMzRXhjZXB0aW9uKEJpZ3N0b3JlRXhjZXB0aW9uLmphdmE6MTE4KVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLmZyYW1ld29yay5CYWNrZW5kQ2FsbFV0aWwuY2FsbChCYWNrZW5kQ2FsbFV0aWwuamF2YTo5Nylcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6NTcpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5MaXN0T2JqZWN0cy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdE9iamVjdHMuamF2YToxNjApXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuYXBpLmpzb24uaGFuZGxlcnMub2JqZWN0cy5MaXN0T2JqZWN0cy5oYW5kbGVSZXF1ZXN0UmVjZWl2ZWQoTGlzdE9iamVjdHMuamF2YTozOClcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuUmVxdWVzdEhhbmRsZXIuaGFuZGxlKFJlcXVlc3RIYW5kbGVyLmphdmE6MzAzKVxuXHRhdCBjb20uZ29vZ2xlLmNsb3VkLmJpZ3N0b3JlLmFwaS5qc29uLk9iamVjdHNEZWxlZ2F0b3IubGlzdChPYmplY3RzRGVsZWdhdG9yLmphdmE6ODkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLlJwY1JlY2VpdmVyLmxhbWJkYSRwcm9jZXNzUmVxdWVzdEFzeW5jJDMoUnBjUmVjZWl2ZXIuamF2YToxNjkpXG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuaXNvbGF0aW9uLkFzeW5jRXhlY3V0b3IubGFtYmRhJHN1Ym1pdCQwKEFzeW5jRXhlY3V0b3IuamF2YToyNDMpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLmNvbnRleHQuQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChDb250ZXh0UnVubmFibGUuamF2YTo1MClcblx0YXQgY29tLmdvb2dsZS5jb21tb24uY29udGV4dC5Db250ZXh0UnVubmFibGUkMS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzkpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5DdXJyZW50Q29udGV4dC5ydW5JbkNvbnRleHQoQ3VycmVudENvbnRleHQuamF2YTozMjApXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5HZW5lcmljQ29udGV4dENhbGxiYWNrLnJ1bkluSW5oZXJpdGVkQ29udGV4dE5vVW5yZWYoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjcyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuR2VuZXJpY0NvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoR2VuZXJpY0NvbnRleHRDYWxsYmFjay5qYXZhOjY0KVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi5jb250ZXh0LkNvbnRleHRSdW5uYWJsZS5ydW4oQ29udGV4dFJ1bm5hYmxlLmphdmE6MzYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkV4ZWN1dG9ycyRSdW5uYWJsZUFkYXB0ZXIuY2FsbChFeGVjdXRvcnMuamF2YTo1MTEpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LkZ1dHVyZVRhc2sucnVuKEZ1dHVyZVRhc2suamF2YToyNjYpXG5cdGF0IGphdmEudXRpbC5jb25jdXJyZW50LlRocmVhZFBvb2xFeGVjdXRvci5ydW5Xb3JrZXIoVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6MTE0OSlcblx0YXQgamF2YS51dGlsLmNvbmN1cnJlbnQuVGhyZWFkUG9vbEV4ZWN1dG9yJFdvcmtlci5ydW4oVGhyZWFkUG9vbEV4ZWN1dG9yLmphdmE6NjI0KVxuXHRhdCBqYXZhLmxhbmcuVGhyZWFkLnJ1bihUaHJlYWQuamF2YTo3NDgpXG5DYXVzZWQgYnk6IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uOiBCVUNLRVRfTk9UX0ZPVU5EOiBObyBzdWNoIGJ1Y2tldDogZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDE1XG5cdGF0IGNvbS5nb29nbGUuY2xvdWQuYmlnc3RvcmUuY29tbW9uLkJpZ3N0b3JlRXhjZXB0aW9uLnRocm93T25FcnJvcihCaWdzdG9yZUV4Y2VwdGlvbi5qYXZhOjI3Nilcblx0YXQgY29tLmdvb2dsZS5jbG91ZC5iaWdzdG9yZS5hcGkuanNvbi5mcmFtZXdvcmsuQmFja2VuZENhbGxVdGlsLmNhbGwoQmFja2VuZENhbGxVdGlsLmphdmE6OTMpXG5cdC4uLiAxOCBtb3JlXG5cblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUuRXJyb3JDb2xsZWN0b3IudG9GYXVsdChFcnJvckNvbGxlY3Rvci5qYXZhOjU0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIucmVzdC5hZGFwdGVyLnJvc3kuUm9zeUVycm9yQ29udmVydGVyLnRvRmF1bHQoUm9zeUVycm9yQ29udmVydGVyLmphdmE6NjcpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyNTgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5yZXN0LmFkYXB0ZXIucm9zeS5Sb3N5SGFuZGxlciQyLmNhbGwoUm9zeUhhbmRsZXIuamF2YToyMzgpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5jb3JlLnV0aWwuQ2FsbGFibGVGdXR1cmUucnVuKENhbGxhYmxlRnV0dXJlLmphdmE6NjIpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5Nb3JlRXhlY3V0b3JzJERpcmVjdEV4ZWN1dG9yLmV4ZWN1dGUoTW9yZUV4ZWN1dG9ycy5qYXZhOjQwMilcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmV4ZWN1dGVMaXN0ZW5lcihBYnN0cmFjdEZ1dHVyZS5qYXZhOjEwMjkpXG5cdGF0IGNvbS5nb29nbGUuY29tbW9uLnV0aWwuY29uY3VycmVudC5BYnN0cmFjdEZ1dHVyZS5jb21wbGV0ZShBYnN0cmFjdEZ1dHVyZS5qYXZhOjg3MSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLnNldChBYnN0cmFjdEZ1dHVyZS5qYXZhOjY5NClcblx0YXQgY29tLmdvb2dsZS5hcGkuc2VydmVyLmNvcmUudXRpbC5DYWxsYWJsZUZ1dHVyZS5ydW4oQ2FsbGFibGVGdXR1cmUuamF2YTo2Milcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50Lk1vcmVFeGVjdXRvcnMkRGlyZWN0RXhlY3V0b3IuZXhlY3V0ZShNb3JlRXhlY3V0b3JzLmphdmE6NDAyKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuZXhlY3V0ZUxpc3RlbmVyKEFic3RyYWN0RnV0dXJlLmphdmE6MTAyOSlcblx0YXQgY29tLmdvb2dsZS5jb21tb24udXRpbC5jb25jdXJyZW50LkFic3RyYWN0RnV0dXJlLmNvbXBsZXRlKEFic3RyYWN0RnV0dXJlLmphdmE6ODcxKVxuXHRhdCBjb20uZ29vZ2xlLmNvbW1vbi51dGlsLmNvbmN1cnJlbnQuQWJzdHJhY3RGdXR1cmUuc2V0KEFic3RyYWN0RnV0dXJlLmphdmE6Njk0KVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIuY29yZS51dGlsLkNhbGxhYmxlRnV0dXJlLnJ1bihDYWxsYWJsZUZ1dHVyZS5qYXZhOjYyKVxuXHRhdCBjb20uZ29vZ2xlLmFwaS5zZXJ2ZXIudGhyZWFkLlRocmVhZFRyYWNrZXJzJFRocmVhZFRyYWNraW5nUnVubmFibGUucnVuKFRocmVhZFRyYWNrZXJzLmphdmE6MTI2KVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bkluQ29udGV4dChUcmFjZUNvbnRleHQuamF2YTo0NTUpXG5cdGF0IGNvbS5nb29nbGUuYXBpLnNlcnZlci5zZXJ2ZXIuQ29tbW9uTW9kdWxlJENvbnRleHRDYXJyeWluZ0V4ZWN1dG9yU2VydmljZSQxLnJ1bkluQ29udGV4dChDb21tb25Nb2R1bGUuamF2YTo4NDYpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkVHJhY2VDb250ZXh0UnVubmFibGUkMS5ydW4oVHJhY2VDb250ZXh0LmphdmE6NDYyKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuQ3VycmVudENvbnRleHQucnVuSW5Db250ZXh0KEN1cnJlbnRDb250ZXh0LmphdmE6MzIwKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JEFic3RyYWN0VHJhY2VDb250ZXh0Q2FsbGJhY2sucnVuSW5Jbmhlcml0ZWRDb250ZXh0Tm9VbnJlZihUcmFjZUNvbnRleHQuamF2YTozMjEpXG5cdGF0IGNvbS5nb29nbGUudHJhY2luZy5UcmFjZUNvbnRleHQkQWJzdHJhY3RUcmFjZUNvbnRleHRDYWxsYmFjay5ydW5JbkluaGVyaXRlZENvbnRleHQoVHJhY2VDb250ZXh0LmphdmE6MzEzKVxuXHRhdCBjb20uZ29vZ2xlLnRyYWNpbmcuVHJhY2VDb250ZXh0JFRyYWNlQ29udGV4dFJ1bm5hYmxlLnJ1bihUcmFjZUNvbnRleHQuamF2YTo0NTkpXG5cdGF0IGNvbS5nb29nbGUuZ3NlLmludGVybmFsLkRpc3BhdGNoUXVldWVJbXBsJFdvcmtlclRocmVhZC5ydW4oRGlzcGF0Y2hRdWV1ZUltcGwuamF2YTo0MDMpXG4iCiAgIH0KICBdLAogICJjb2RlIjogNDA0LAogICJtZXNzYWdlIjogIk5vdCBGb3VuZCIKIH0KfQo=" + } + }, + { + "ID": "a51e4f8fd1aa155e", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/storage-library-test-bucket/Cafe%CC%81", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5882c263f815f3fa0d532302e1d78edb/7836474608900607788;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/storage-library-test-bucket/Cafe%CC%81" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "public, max-age=3600" + ], + "Content-Length": [ + "20" + ], + "Content-Type": [ + "text/plain" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Etag": [ + "\"df597679bac7c6150429ad80a1a05680\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 22:11:43 GMT" + ], + "Last-Modified": [ + "Fri, 24 Mar 2017 20:04:37 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Generation": [ + "1490385877705600" + ], + "X-Goog-Hash": [ + "crc32c=qBeWjQ==", + "md5=31l2ebrHxhUEKa2AoaBWgA==" + ], + "X-Goog-Metageneration": [ + "2" + ], + "X-Goog-Storage-Class": [ + "MULTI_REGIONAL" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "20" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/3,/bns/xg/borg/xg/bns/blobstore2/bitpusher/126.scotty,aclgal16:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D081W9GgL46y_QTR64OQBw" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "149776848335" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/126.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/126:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uo7uK4P-lh9sM5z2ufgHpSKo6MTssEOrLpV8wrY55uZfq7GmxX21MpEQp8VhexqnenqSbZXqviNviKbM-w71IA-hoAdAw" + ] + }, + "Body": "Tm9ybWFsaXphdGlvbiBGb3JtIEQ=" + } + }, + { + "ID": "1d928209553b71d8", + "Request": { + "Method": "GET", + "URL": "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/zero", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2303236367f5366b2f537746157615f7/9379553147068027468;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "storage.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://storage.googleapis.com/go-integration-test-20180628-76155232935339-0000/zero" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Accept-Ranges": [ + "bytes" + ], + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:44 GMT" + ], + "Etag": [ + "\"d41d8cd98f00b204e9800998ecf8427e\"" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:44 GMT" + ], + "Last-Modified": [ + "Thu, 28 Jun 2018 21:11:43 GMT" + ], + "Server": [ + "UploadServer" + ], + "X-Goog-Expiration": [ + "Sat, 28 Jul 2018 21:11:43 GMT" + ], + "X-Goog-Generation": [ + "1530220303869191" + ], + "X-Goog-Hash": [ + "crc32c=AAAAAA==", + "md5=1B2M2Y8AsgTpgAmY7PhCfg==" + ], + "X-Goog-Metageneration": [ + "1" + ], + "X-Goog-Storage-Class": [ + "STANDARD" + ], + "X-Goog-Stored-Content-Encoding": [ + "identity" + ], + "X-Goog-Stored-Content-Length": [ + "0" + ], + "X-Google-Backends": [ + "/bns/xg/borg/xg/bns/cloud-storage/prod-cloud-storage-frontend.frontend/41,/bns/xg/borg/xg/bns/blobstore2/bitpusher/132.scotty,aclgal16:443" + ], + "X-Google-Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=D081W5_IO-6x_QSbt4LwBg" + ], + "X-Google-Gfe-Cloud-Project-Number": [ + "36639933145" + ], + "X-Google-Gfe-Request-Trace": [ + "aclgal16:443,/bns/xg/borg/xg/bns/blobstore2/bitpusher/132.scotty,aclgal16:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-cloud-storage" + ], + "X-Google-Netmon-Label": [ + "/bns/xg/borg/xg/bns/blobstore2/bitpusher/132:caf3" + ], + "X-Google-Service": [ + "bitpusher-cloud-storage" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Google-Storage-Location": [ + "US" + ], + "X-Guploader-Customer": [ + "cloud-storage" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoraO6dMdtdulcNnw0WQQz1WipozkujHGICzDQJkQEsRf8505hb9E1bfr2pZwvzOjb78SWhKA88C147GbE8Yv7333_C2g" + ] + }, + "Body": "" + } + }, + { + "ID": "e33713414f77589a", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/zero?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9852675efe12879bbe9e39edf341e33c/10186978828570842203;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/zero?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:44 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220603000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrn2:4338,/bns/yv/borg/yv/bns/blobstore2/bitpusher/375.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=EE81W5PRApSCggTOibPoAg" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/375.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/375:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEdYV3VTR2RESGI3UjZIOE1WOEhIU204SEN4MkpOOXpJdXRSSFNqUXFkNW14cnRVaXJYWG1nd1RFUGxVbDlxbE9xZjVydnBVeElid3Z0Nl9BekRaVDl2T0VOLWZBYmlsb2JXUURiSjQ5cGw0azZORFJFUW1wcFNaMS1XMndMcThQZXM2ZWExd3Axc2JGWXVITTBtOHljaHlZWjl0azhvWmVyaW53Y1FUOUg4UW5Na2E5WjcwNUZTSEkwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqmBdJ5ddrlefVHZ0yOGkiK-95lMEKR8ukUhVT69UAHZ-KAiftKS1pDWokMBvjU6KawImME2aHOod0ZiC-8T4nomvchCopph2pZzIkEFP7EGEbikss" + ] + }, + "Body": "" + } + }, + { + "ID": "2ca3a59c5509ff73", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "dd49136754e3d6ee4c3cc24e1601a059/10994687080266962283;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "72775" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:47 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:47 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vru127:4240,/bns/yv/borg/yv/bns/blobstore2/bitpusher/134.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E081W62dBYbAggSqio6oAw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/134.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/134:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UphSMwktmPWdDMG4Lnhu0yTc4QLdwlWbiUAGNRy8qmwV8O45ori6YvqogDN0hOZd5cN9uMtm7692DcMP2FxQnwQeRbEMne4KBSifJLbBr14r_eary8" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wxLzE1MzAyMjAxODcxNDAwMDciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDEiLAogICAibmFtZSI6ICJhY2wxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3MTQwMDA3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDcuMTM5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDguMTQ0WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0Ny4xMzlaIiwKICAgInNpemUiOiAiMTYiLAogICAibWQ1SGFzaCI6ICJoMWNsc0Y0K0FCVGRvbTFjc04wUWlnPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hY2wxP2dlbmVyYXRpb249MTUzMDIyMDE4NzE0MDAwNyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDEvMTUzMDIyMDE4NzE0MDAwNy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hY2wxL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiYWNsMSIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzE0MDAwNyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDS2YvdXJXaTk5c0NFQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMS8xNTMwMjIwMTg3MTQwMDA3L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hY2wxL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImFjbDEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODcxNDAwMDciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDS2YvdXJXaTk5c0NFQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMS8xNTMwMjIwMTg3MTQwMDA3L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9hY2wxL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImFjbDEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODcxNDAwMDciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0tmL3VyV2k5OXNDRUFJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDEvMTUzMDIyMDE4NzE0MDAwNy91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDEvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImFjbDEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODcxNDAwMDciLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ0tmL3VyV2k5OXNDRUFJPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogICB9LAogICAiY3JjMzJjIjogIks2Ty9Ydz09IiwKICAgImV0YWciOiAiQ0tmL3VyV2k5OXNDRUFJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDIvMTUzMDIyMDE4NzczNjI5OCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYWNsMiIsCiAgICJuYW1lIjogImFjbDIiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODc3MzYyOTgiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0Ny43MzVaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0Ny43MzVaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQ3LjczNVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIllQb3NsQnhZYWE4dWZTc0d1MCsxSlE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDI/Z2VuZXJhdGlvbj0xNTMwMjIwMTg3NzM2Mjk4JmFsdD1tZWRpYSIsCiAgICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMi8xNTMwMjIwMTg3NzM2Mjk4L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJhY2wyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3NzM2Mjk4IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNPcXgzN1dpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wyLzE1MzAyMjAxODc3MzYyOTgvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiYWNsMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzczNjI5OCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNPcXgzN1dpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9hY2wyLzE1MzAyMjAxODc3MzYyOTgvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiYWNsMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NzczNjI5OCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDT3F4MzdXaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvYWNsMi8xNTMwMjIwMTg3NzM2Mjk4L2RvbWFpbi1nb29nbGUuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDIvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJhY2wyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg3NzM2Mjk4IiwKICAgICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAgICAgImV0YWciOiAiQ09xeDM3V2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2FjbDIvMTUzMDIyMDE4NzczNjI5OC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2FjbDIvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImFjbDIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODc3MzYyOTgiLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ09xeDM3V2k5OXNDRUFFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogICB9LAogICAiY3JjMzJjIjogIlhYTUFVZz09IiwKICAgImV0YWciOiAiQ09xeDM3V2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2J1Y2tldEluQ29weUF0dHJzLzE1MzAyMjAyMDcyMzMxOTAiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2J1Y2tldEluQ29weUF0dHJzIiwKICAgIm5hbWUiOiAiYnVja2V0SW5Db3B5QXR0cnMiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDcyMzMxOTAiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDcuMjMxWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDcuMjMxWiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNy4yMzFaIiwKICAgInNpemUiOiAiMyIsCiAgICJtZDVIYXNoIjogInJMMFkyMHpDK0Z6dDcyVlB6TVNrMkE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2J1Y2tldEluQ29weUF0dHJzP2dlbmVyYXRpb249MTUzMDIyMDIwNzIzMzE5MCZhbHQ9bWVkaWEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9idWNrZXRJbkNvcHlBdHRycy8xNTMwMjIwMjA3MjMzMTkwL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2J1Y2tldEluQ29weUF0dHJzL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiYnVja2V0SW5Db3B5QXR0cnMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDcyMzMxOTAiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0theGhiK2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2J1Y2tldEluQ29weUF0dHJzLzE1MzAyMjAyMDcyMzMxOTAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2J1Y2tldEluQ29weUF0dHJzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImJ1Y2tldEluQ29weUF0dHJzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA3MjMzMTkwIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0theGhiK2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2J1Y2tldEluQ29weUF0dHJzLzE1MzAyMjAyMDcyMzMxOTAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2J1Y2tldEluQ29weUF0dHJzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImJ1Y2tldEluQ29weUF0dHJzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA3MjMzMTkwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNLYXhoYitpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9idWNrZXRJbkNvcHlBdHRycy8xNTMwMjIwMjA3MjMzMTkwL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vYnVja2V0SW5Db3B5QXR0cnMvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImJ1Y2tldEluQ29weUF0dHJzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA3MjMzMTkwIiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNLYXhoYitpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJ6OFN1SFE9PSIsCiAgICJldGFnIjogIkNLYXhoYitpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jaGVja3N1bS1vYmplY3QvMTUzMDIyMDE3OTIzNTI1MiIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY2hlY2tzdW0tb2JqZWN0IiwKICAgIm5hbWUiOiAiY2hlY2tzdW0tb2JqZWN0IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5MjM1MjUyIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjM5LjIzM1oiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjM5LjIzM1oiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzkuMjMzWiIsCiAgICJzaXplIjogIjEwIiwKICAgIm1kNUhhc2giOiAiL0Y0RGpUaWxjRElJVkVIbi9uQVFzQT09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY2hlY2tzdW0tb2JqZWN0P2dlbmVyYXRpb249MTUzMDIyMDE3OTIzNTI1MiZhbHQ9bWVkaWEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jaGVja3N1bS1vYmplY3QvMTUzMDIyMDE3OTIzNTI1Mi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jaGVja3N1bS1vYmplY3QvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjaGVja3N1bS1vYmplY3QiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNzkyMzUyNTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xURDJMR2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NoZWNrc3VtLW9iamVjdC8xNTMwMjIwMTc5MjM1MjUyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jaGVja3N1bS1vYmplY3QvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY2hlY2tzdW0tb2JqZWN0IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5MjM1MjUyIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0xURDJMR2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NoZWNrc3VtLW9iamVjdC8xNTMwMjIwMTc5MjM1MjUyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jaGVja3N1bS1vYmplY3QvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY2hlY2tzdW0tb2JqZWN0IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5MjM1MjUyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNMVEQyTEdpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jaGVja3N1bS1vYmplY3QvMTUzMDIyMDE3OTIzNTI1Mi91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NoZWNrc3VtLW9iamVjdC9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY2hlY2tzdW0tb2JqZWN0IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5MjM1MjUyIiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMVEQyTEdpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJWc3UwZ0E9PSIsCiAgICJldGFnIjogIkNMVEQyTEdpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb21wb3NlZDEvMTUzMDIyMDE4MTg2NjYxMSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29tcG9zZWQxIiwKICAgIm5hbWUiOiAiY29tcG9zZWQxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgxODY2NjExIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0MS44NjVaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0MS44NjVaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQxLjg2NVoiLAogICAic2l6ZSI6ICI0OCIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbXBvc2VkMT9nZW5lcmF0aW9uPTE1MzAyMjAxODE4NjY2MTEmYWx0PW1lZGlhIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29tcG9zZWQxLzE1MzAyMjAxODE4NjY2MTEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29tcG9zZWQxL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY29tcG9zZWQxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgxODY2NjExIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNQT1ErYktpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb21wb3NlZDEvMTUzMDIyMDE4MTg2NjYxMS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29tcG9zZWQxL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImNvbXBvc2VkMSIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MTg2NjYxMSIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNQT1ErYktpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb21wb3NlZDEvMTUzMDIyMDE4MTg2NjYxMS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29tcG9zZWQxL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImNvbXBvc2VkMSIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MTg2NjYxMSIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDUE9RK2JLaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29tcG9zZWQxLzE1MzAyMjAxODE4NjY2MTEvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb21wb3NlZDEvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImNvbXBvc2VkMSIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MTg2NjYxMSIsCiAgICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiZXRhZyI6ICJDUE9RK2JLaTk5c0NFQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiAgIH0sCiAgICJjcmMzMmMiOiAib3VJdTRRPT0iLAogICAiY29tcG9uZW50Q291bnQiOiAzLAogICAiZXRhZyI6ICJDUE9RK2JLaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY29tcG9zZWQyLzE1MzAyMjAxODIzODU2NDIiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbXBvc2VkMiIsCiAgICJuYW1lIjogImNvbXBvc2VkMiIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MjM4NTY0MiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvanNvbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQyLjM4NVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQyLjM4NVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDIuMzg1WiIsCiAgICJzaXplIjogIjQ4IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29tcG9zZWQyP2dlbmVyYXRpb249MTUzMDIyMDE4MjM4NTY0MiZhbHQ9bWVkaWEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb21wb3NlZDIvMTUzMDIyMDE4MjM4NTY0Mi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb21wb3NlZDIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjb21wb3NlZDIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODIzODU2NDIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ09ybm1MT2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbXBvc2VkMi8xNTMwMjIwMTgyMzg1NjQyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb21wb3NlZDIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY29tcG9zZWQyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgyMzg1NjQyIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ09ybm1MT2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbXBvc2VkMi8xNTMwMjIwMTgyMzg1NjQyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb21wb3NlZDIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY29tcG9zZWQyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgyMzg1NjQyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNPcm5tTE9pOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb21wb3NlZDIvMTUzMDIyMDE4MjM4NTY0Mi91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbXBvc2VkMi9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY29tcG9zZWQyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgyMzg1NjQyIiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNPcm5tTE9pOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJvdUl1NFE9PSIsCiAgICJjb21wb25lbnRDb3VudCI6IDMsCiAgICJldGFnIjogIkNPcm5tTE9pOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2NvbnRlbnQiLAogICAibmFtZSI6ICJjb250ZW50IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4ODMwODQxIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiaW1hZ2UvanBlZyIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU4LjgyOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU4LjgyOVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTguODI5WiIsCiAgICJzaXplIjogIjU0IiwKICAgIm1kNUhhc2giOiAiTjhwOC9zOUZ3ZEFBbmx2ci9sRUFqUT09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudD9nZW5lcmF0aW9uPTE1MzAyMjAxOTg4MzA4NDEmYWx0PW1lZGlhIiwKICAgImNhY2hlQ29udHJvbCI6ICJwdWJsaWMsIG1heC1hZ2U9NjAiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImNvbnRlbnQiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTg4MzA4NDEiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ1BuRmhMdWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODgzMDg0MS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4ODMwODQxIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ1BuRmhMdWk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2NvbnRlbnQvMTUzMDIyMDE5ODgzMDg0MS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY29udGVudC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4ODMwODQxIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNQbkZoTHVpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jb250ZW50LzE1MzAyMjAxOTg4MzA4NDEvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jb250ZW50L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjb250ZW50IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk4ODMwODQxIiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNQbkZoTHVpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJHb1Vic1E9PSIsCiAgICJldGFnIjogIkNQbkZoTHVpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLzE1MzAyMjAxOTk0MzAwMTgiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24iLAogICAibmFtZSI6ICJjdXN0b21lci1lbmNyeXB0aW9uIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTk5NDMwMDE4IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjMiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU5LjQyOFoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjU5Ljk4M1oiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NTkuNDI4WiIsCiAgICJzaXplIjogIjExIiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbj9nZW5lcmF0aW9uPTE1MzAyMjAxOTk0MzAwMTgmYWx0PW1lZGlhIiwKICAgImNvbnRlbnRMYW5ndWFnZSI6ICJlbiIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE5OTQzMDAxOCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQU09IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQU09IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi8xNTMwMjIwMTk5NDMwMDE4L3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFNPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24vMTUzMDIyMDE5OTQzMDAxOC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24vYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24iLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxOTk0MzAwMTgiLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ0lLUHFidWk5OXNDRUFNPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogICB9LAogICAiZXRhZyI6ICJDSUtQcWJ1aTk5c0NFQU09IiwKICAgImN1c3RvbWVyRW5jcnlwdGlvbiI6IHsKICAgICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgICAia2V5U2hhMjU2IjogIkgrTG1uWGhSb2VJNlRNVzVic1Y2SHlVazZweUdjMklNYnFZYkFYQmNwczA9IgogICB9CiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwNDQ4Njc5NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgIm5hbWUiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA0NDg2Nzk1IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOCIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA0LjQ4NloiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjEwOjA0LjQ4NloiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDQuNDg2WiIsCiAgICJzaXplIjogIjExIiwKICAgIm1kNUhhc2giOiAieHdXTkZhMFZkWFBtbEF3cmxjQUpjZz09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yP2dlbmVyYXRpb249MTUzMDIyMDIwNDQ4Njc5NSZhbHQ9bWVkaWEiLAogICAiY29udGVudExhbmd1YWdlIjogImVuIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0yLzE1MzAyMjAyMDQ0ODY3OTUvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiY3VzdG9tZXItZW5jcnlwdGlvbi0yIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA0NDg2Nzk1IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNJdmgzYjJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwNDQ4Njc5NS9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24tMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNDQ4Njc5NSIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNJdmgzYjJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9jdXN0b21lci1lbmNyeXB0aW9uLTIvMTUzMDIyMDIwNDQ4Njc5NS9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0yL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24tMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNDQ4Njc5NSIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDSXZoM2IyaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0yLzE1MzAyMjAyMDQ0ODY3OTUvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uLTIvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24tMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNDQ4Njc5NSIsCiAgICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiZXRhZyI6ICJDSXZoM2IyaTk5c0NFQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiAgIH0sCiAgICJjcmMzMmMiOiAicjBOR3JnPT0iLAogICAiZXRhZyI6ICJDSXZoM2IyaTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0zLzE1MzAyMjAyMDM3NjM3MzUiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMyIsCiAgICJuYW1lIjogImN1c3RvbWVyLWVuY3J5cHRpb24tMyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMzc2MzczNSIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDMuNzYzWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDMuNzYzWiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowMy43NjNaIiwKICAgInNpemUiOiAiMjIiLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9jdXN0b21lci1lbmNyeXB0aW9uLTM/Z2VuZXJhdGlvbj0xNTMwMjIwMjAzNzYzNzM1JmFsdD1tZWRpYSIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMy8xNTMwMjIwMjAzNzYzNzM1L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImN1c3RvbWVyLWVuY3J5cHRpb24tMyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwMzc2MzczNSIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDSmZRc2IyaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0zLzE1MzAyMjAyMDM3NjM3MzUvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDM3NjM3MzUiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDSmZRc2IyaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvY3VzdG9tZXItZW5jcnlwdGlvbi0zLzE1MzAyMjAyMDM3NjM3MzUvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2N1c3RvbWVyLWVuY3J5cHRpb24tMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDM3NjM3MzUiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0pmUXNiMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2N1c3RvbWVyLWVuY3J5cHRpb24tMy8xNTMwMjIwMjAzNzYzNzM1L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vY3VzdG9tZXItZW5jcnlwdGlvbi0zL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJjdXN0b21lci1lbmNyeXB0aW9uLTMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDM3NjM3MzUiLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ0pmUXNiMmk5OXNDRUFFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogICB9LAogICAiY29tcG9uZW50Q291bnQiOiAyLAogICAiZXRhZyI6ICJDSmZRc2IyaTk5c0NFQUU9IiwKICAgImN1c3RvbWVyRW5jcnlwdGlvbiI6IHsKICAgICJlbmNyeXB0aW9uQWxnb3JpdGhtIjogIkFFUzI1NiIsCiAgICAia2V5U2hhMjU2IjogIkgrTG1uWGhSb2VJNlRNVzVic1Y2SHlVazZweUdjMklNYnFZYkFYQmNwczA9IgogICB9CiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9nemlwLXRlc3QvMTUzMDIyMDE4MzAzMDMzNSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vZ3ppcC10ZXN0IiwKICAgIm5hbWUiOiAiZ3ppcC10ZXN0IiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTgzMDMwMzM1IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAiYXBwbGljYXRpb24veC1nemlwIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDMuMDI5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDMuMDI5WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0My4wMjlaIiwKICAgInNpemUiOiAiMjciLAogICAibWQ1SGFzaCI6ICJPdEN3K2FSUklScUtHRkFFT2F4K3F3PT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9nemlwLXRlc3Q/Z2VuZXJhdGlvbj0xNTMwMjIwMTgzMDMwMzM1JmFsdD1tZWRpYSIsCiAgICJjb250ZW50RW5jb2RpbmciOiAiZ3ppcCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2d6aXAtdGVzdC8xNTMwMjIwMTgzMDMwMzM1L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2d6aXAtdGVzdC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImd6aXAtdGVzdCIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4MzAzMDMzNSIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDTCtVd0xPaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvZ3ppcC10ZXN0LzE1MzAyMjAxODMwMzAzMzUvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2d6aXAtdGVzdC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJnemlwLXRlc3QiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODMwMzAzMzUiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDTCtVd0xPaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvZ3ppcC10ZXN0LzE1MzAyMjAxODMwMzAzMzUvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL2d6aXAtdGVzdC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJnemlwLXRlc3QiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODMwMzAzMzUiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wrVXdMT2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2d6aXAtdGVzdC8xNTMwMjIwMTgzMDMwMzM1L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vZ3ppcC10ZXN0L2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJnemlwLXRlc3QiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODMwMzAzMzUiLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ0wrVXdMT2k5OXNDRUFFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogICB9LAogICAiY3JjMzJjIjogIjlEaHdCQT09IiwKICAgImV0YWciOiAiQ0wrVXdMT2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwODgzNzk1NiIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vaGFzaGVzT25VcGxvYWQtMSIsCiAgICJuYW1lIjogImhhc2hlc09uVXBsb2FkLTEiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDg4Mzc5NTYiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDguODM3WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDguODM3WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowOC44MzdaIiwKICAgInNpemUiOiAiMjciLAogICAibWQ1SGFzaCI6ICJvZlpqR2xjWFBKaUdPQWZLRmJKbDFRPT0iLAogICAibWVkaWFMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2Rvd25sb2FkL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9oYXNoZXNPblVwbG9hZC0xP2dlbmVyYXRpb249MTUzMDIyMDIwODgzNzk1NiZhbHQ9bWVkaWEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDg4Mzc5NTYvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vaGFzaGVzT25VcGxvYWQtMS9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogImhhc2hlc09uVXBsb2FkLTEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDg4Mzc5NTYiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ01TcTU3K2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwODgzNzk1Ni9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vaGFzaGVzT25VcGxvYWQtMS9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA4ODM3OTU2IiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ01TcTU3K2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL2hhc2hlc09uVXBsb2FkLTEvMTUzMDIyMDIwODgzNzk1Ni9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vaGFzaGVzT25VcGxvYWQtMS9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA4ODM3OTU2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNNU3E1NytpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9oYXNoZXNPblVwbG9hZC0xLzE1MzAyMjAyMDg4Mzc5NTYvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9oYXNoZXNPblVwbG9hZC0xL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJoYXNoZXNPblVwbG9hZC0xIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA4ODM3OTU2IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNNU3E1NytpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJjSCtBK3c9PSIsCiAgICJldGFnIjogIkNNU3E1NytpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzIiwKICAgIm5hbWUiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS41NDlaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjU0OVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjY1akFkdktmRzZHaGFTNVcrRXpnNEE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzP2dlbmVyYXRpb249MTUzMDIyMDE2OTU0OTc1NyZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmolMkZ3aXRoJTJGc2xhc2hlcy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqJTJGd2l0aCUyRnNsYXNoZXMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqL3dpdGgvc2xhc2hlcyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTU0OTc1NyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmovd2l0aC9zbGFzaGVzLzE1MzAyMjAxNjk1NDk3NTcvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iai93aXRoL3NsYXNoZXMiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjk1NDk3NTciLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0wydmlhMmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iai93aXRoL3NsYXNoZXMvMTUzMDIyMDE2OTU0OTc1Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iaiUyRndpdGglMkZzbGFzaGVzL2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmovd2l0aC9zbGFzaGVzIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5NTQ5NzU3IiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIremlJOFE9PSIsCiAgICJldGFnIjogIkNMMnZpYTJpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoxLzE1MzAyMjAxNjg1MzE4OTciLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEiLAogICAibmFtZSI6ICJvYmoxIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjQiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOC41MjlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTo0MC4xMzBaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI4LjUyOVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogIjBRODIyN3NlVkw2QlVPTXNLY0ZvOXc9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajE/Z2VuZXJhdGlvbj0xNTMwMjIwMTY4NTMxODk3JmFsdD1tZWRpYSIsCiAgICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMS8xNTMwMjIwMTY4NTMxODk3L2RvbWFpbi1nb29nbGUuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL2RvbWFpbi1nb29nbGUuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoxIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY4NTMxODk3IiwKICAgICAiZW50aXR5IjogImRvbWFpbi1nb29nbGUuY29tIiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJkb21haW4iOiAiZ29vZ2xlLmNvbSIsCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFRPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajEvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogIm9iajEiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNjg1MzE4OTciLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ0xtZnk2eWk5OXNDRUFRPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL29iajEvMTUzMDIyMDE2ODUzMTg5Ny9hbGxVc2VycyIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoxL2FjbC9hbGxVc2VycyIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqMSIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2ODUzMTg5NyIsCiAgICAgImVudGl0eSI6ICJhbGxVc2VycyIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQVE9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiAgIH0sCiAgICJjcmMzMmMiOiAiMFNHNGl3PT0iLAogICAiZXRhZyI6ICJDTG1meTZ5aTk5c0NFQVE9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMi8xNTMwMjIwMTY5MDIxODY2IiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9vYmoyIiwKICAgIm5hbWUiOiAib2JqMiIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImNvbnRlbnRUeXBlIjogInRleHQvcGxhaW4iLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS4wMjFaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOToyOS4wMjFaIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJ0aW1lU3RvcmFnZUNsYXNzVXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjI5LjAyMVoiLAogICAic2l6ZSI6ICIxNiIsCiAgICJtZDVIYXNoIjogImRTQ09FeEExMS9NMGdMWXh1N1RBWHc9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajI/Z2VuZXJhdGlvbj0xNTMwMjIwMTY5MDIxODY2JmFsdD1tZWRpYSIsCiAgICJjYWNoZUNvbnRyb2wiOiAicHVibGljLCBtYXgtYWdlPTYwIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMi8xNTMwMjIwMTY5MDIxODY2L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJvYmoyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTY5MDIxODY2IiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNLcVQ2YXlpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vYmoyLzE1MzAyMjAxNjkwMjE4NjYvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL29iajIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDS3FUNmF5aTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvb2JqMi8xNTMwMjIwMTY5MDIxODY2L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vb2JqMi9hY2wvdXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAib2JqMiIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE2OTAyMTg2NiIsCiAgICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgImVtYWlsIjogIjM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiZXRhZyI6ICJDS3FUNmF5aTk5c0NFQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iCiAgIH0sCiAgICJjcmMzMmMiOiAiTkV4Q3d3PT0iLAogICAiZXRhZyI6ICJDS3FUNmF5aTk5c0NFQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI29iamVjdCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcG9zYy8xNTMwMjIwMjA2MjM5OTkzIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjIiwKICAgIm5hbWUiOiAicG9zYyIsCiAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNjIzOTk5MyIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDYuMjM5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDYuMjM5WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiTVVMVElfUkVHSU9OQUwiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNi4yMzlaIiwKICAgInNpemUiOiAiMyIsCiAgICJtZDVIYXNoIjogInJMMFkyMHpDK0Z6dDcyVlB6TVNrMkE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2M/Z2VuZXJhdGlvbj0xNTMwMjIwMjA2MjM5OTkzJmFsdD1tZWRpYSIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNjIzOTk5My9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAicG9zYyIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDIwNjIzOTk5MyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDUG5oeUw2aTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcG9zYy8xNTMwMjIwMjA2MjM5OTkzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInBvc2MiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDYyMzk5OTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDUG5oeUw2aTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvcG9zYy8xNTMwMjIwMjA2MjM5OTkzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvby9wb3NjL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInBvc2MiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDYyMzk5OTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ1BuaHlMNmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MvMTUzMDIyMDIwNjIzOTk5My91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInBvc2MiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDYyMzk5OTMiLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ1BuaHlMNmk5OXNDRUFFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogICB9LAogICAiY3JjMzJjIjogIno4U3VIUT09IiwKICAgImV0YWciOiAiQ1BuaHlMNmk5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MyLzE1MzAyMjAyMDY2NTQ2NjEiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MyIiwKICAgIm5hbWUiOiAicG9zYzIiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDY2NTQ2NjEiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDYuNjU0WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MTA6MDYuNjU0WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiTVVMVElfUkVHSU9OQUwiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMDowNi42NTRaIiwKICAgInNpemUiOiAiMyIsCiAgICJtZDVIYXNoIjogIjlXR3E5dThMOFUxQ0NMdEdwTXl6clE9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MyP2dlbmVyYXRpb249MTUzMDIyMDIwNjY1NDY2MSZhbHQ9bWVkaWEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjMi8xNTMwMjIwMjA2NjU0NjYxL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MyL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAicG9zYzIiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAyMDY2NTQ2NjEiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ01XSjRyNmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MyLzE1MzAyMjAyMDY2NTQ2NjEvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MyL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInBvc2MyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA2NjU0NjYxIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ01XSjRyNmk5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3Bvc2MyLzE1MzAyMjAyMDY2NTQ2NjEvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3Bvc2MyL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInBvc2MyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA2NjU0NjYxIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNNV0o0cjZpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9wb3NjMi8xNTMwMjIwMjA2NjU0NjYxL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vcG9zYzIvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInBvc2MyIiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMjA2NjU0NjYxIiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNNV0o0cjZpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICIxN3FBQlE9PSIsCiAgICJldGFnIjogIkNNV0o0cjZpOTlzQ0VBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9zaWduZWRVUkwvMTUzMDIyMDE4NDAzNjM3NSIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vc2lnbmVkVVJMIiwKICAgIm5hbWUiOiAic2lnbmVkVVJMIiwKICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTg0MDM2Mzc1IiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiY29udGVudFR5cGUiOiAidGV4dC9wbGFpbiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQ0LjAzNVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDIxOjA5OjQ0LjAzNVoiLAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgInRpbWVTdG9yYWdlQ2xhc3NVcGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6NDQuMDM1WiIsCiAgICJzaXplIjogIjI5IiwKICAgIm1kNUhhc2giOiAiSnl4dmd3bTluMk1zckdUTVBiTWVZQT09IiwKICAgIm1lZGlhTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9kb3dubG9hZC9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vc2lnbmVkVVJMP2dlbmVyYXRpb249MTUzMDIyMDE4NDAzNjM3NSZhbHQ9bWVkaWEiLAogICAiY2FjaGVDb250cm9sIjogInB1YmxpYywgbWF4LWFnZT02MCIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3NpZ25lZFVSTC8xNTMwMjIwMTg0MDM2Mzc1L3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3NpZ25lZFVSTC9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInNpZ25lZFVSTCIsCiAgICAgImdlbmVyYXRpb24iOiAiMTUzMDIyMDE4NDAzNjM3NSIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDSmZJL2JPaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvc2lnbmVkVVJMLzE1MzAyMjAxODQwMzYzNzUvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3NpZ25lZFVSTC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJzaWduZWRVUkwiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODQwMzYzNzUiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDSmZJL2JPaTk5c0NFQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAvc2lnbmVkVVJMLzE1MzAyMjAxODQwMzYzNzUvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3NpZ25lZFVSTC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJzaWduZWRVUkwiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODQwMzYzNzUiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0pmSS9iT2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3NpZ25lZFVSTC8xNTMwMjIwMTg0MDM2Mzc1L3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vc2lnbmVkVVJML2FjbC91c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICAgIm9iamVjdCI6ICJzaWduZWRVUkwiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxODQwMzYzNzUiLAogICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJlbWFpbCI6ICIzNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiAgICAgImV0YWciOiAiQ0pmSS9iT2k5OXNDRUFFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJ1c2VyLTM2NjM5OTMzMTQ1LWIxOHQwMW9tdDlhMjc5a2MzZ2NnaXFocWtsOGJvYmh1QGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIgogICB9LAogICAiY3JjMzJjIjogIlpUcUFMdz09IiwKICAgImV0YWciOiAiQ0pmSS9iT2k5OXNDRUFFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3QiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3plcm8tb2JqZWN0LzE1MzAyMjAxNzk4NjY4MzMiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3plcm8tb2JqZWN0IiwKICAgIm5hbWUiOiAiemVyby1vYmplY3QiLAogICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMCIsCiAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNzk4NjY4MzMiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJjb250ZW50VHlwZSI6ICJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04IiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzkuODY2WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMjE6MDk6MzkuODY2WiIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAidGltZVN0b3JhZ2VDbGFzc1VwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMTowOTozOS44NjZaIiwKICAgInNpemUiOiAiMCIsCiAgICJtZDVIYXNoIjogIjFCMk0yWThBc2dUcGdBbVk3UGhDZmc9PSIsCiAgICJtZWRpYUxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vZG93bmxvYWQvc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3plcm8tb2JqZWN0P2dlbmVyYXRpb249MTUzMDIyMDE3OTg2NjgzMyZhbHQ9bWVkaWEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC96ZXJvLW9iamVjdC8xNTMwMjIwMTc5ODY2ODMzL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3plcm8tb2JqZWN0L2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMDAiLAogICAgICJvYmplY3QiOiAiemVyby1vYmplY3QiLAogICAgICJnZW5lcmF0aW9uIjogIjE1MzAyMjAxNzk4NjY4MzMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ05HSi83R2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3plcm8tb2JqZWN0LzE1MzAyMjAxNzk4NjY4MzMvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3plcm8tb2JqZWN0L2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInplcm8tb2JqZWN0IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5ODY2ODMzIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ05HSi83R2k5OXNDRUFFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL3plcm8tb2JqZWN0LzE1MzAyMjAxNzk4NjY4MzMvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC9vL3plcm8tb2JqZWN0L2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInplcm8tb2JqZWN0IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5ODY2ODMzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNOR0ovN0dpOTlzQ0VBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAwMC96ZXJvLW9iamVjdC8xNTMwMjIwMTc5ODY2ODMzL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwL28vemVyby1vYmplY3QvYWNsL3VzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDAwIiwKICAgICAib2JqZWN0IjogInplcm8tb2JqZWN0IiwKICAgICAiZ2VuZXJhdGlvbiI6ICIxNTMwMjIwMTc5ODY2ODMzIiwKICAgICAiZW50aXR5IjogInVzZXItMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAiZW1haWwiOiAiMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgICJldGFnIjogIkNOR0ovN0dpOTlzQ0VBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAidXNlci0zNjYzOTkzMzE0NS1iMTh0MDFvbXQ5YTI3OWtjM2djZ2lxaHFrbDhib2JodUBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIKICAgfSwKICAgImNyYzMyYyI6ICJBQUFBQUE9PSIsCiAgICJldGFnIjogIkNOR0ovN0dpOTlzQ0VBRT0iCiAgfQogXQp9Cg==" + } + }, + { + "ID": "99da45a535dfcc9e", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "608dd18fa70204d640b4386f61e1ceb5/11802112761769711483;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:47 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrss4:4278,/bns/yv/borg/yv/bns/blobstore2/bitpusher/449.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E081W7rcFsfEgATc4JOACA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/449.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/449:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq3t6RHhh0Er51BktLVQipa3rKMQQ_zyFfTN02dZ0Opd35TNoZQ2zhg0wgdO1H8DUmDZcC0VNnJ6f0uRAZ1IBxloVwUGR0b8Dd2kA6XOJAuFPs35RA" + ] + }, + "Body": "" + } + }, + { + "ID": "674fdd70a65d9b0c", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "5e6a869933fb11f255140b0427d6b790/12609539542784088203;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/acl2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:47 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrde17:4368,/bns/yv/borg/yv/bns/blobstore2/bitpusher/347.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E081W5W_IYrLggS1_56wAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/347.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/347:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq-CRUi-5EsXazFQ454fFcBjl09Y1uii3jmShyTm0re9IFi-dQGjisRL7nQo6JAGM_B48749aI8uqNSXN8jkbBrT3xra8tCN4__7EQ9OhwmFHwmgHg" + ] + }, + "Body": "" + } + }, + { + "ID": "9751fac08ed3d2b4", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/bucketInCopyAttrs?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6ce25178e6eafe63d2f3958aebbcbff1/13345191295642294938;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/bucketInCopyAttrs?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:47 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrkb18:4263,/bns/yv/borg/yv/bns/blobstore2/bitpusher/438.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E081W_eOLJDzgQTov4DoBw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/438.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/438:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrW146LeKyvkTfXelvpNzDxpFwRW_l9_-BOM1Zj50_HcVXwM_5_4tcqlBtjyfKbYss2rS7agMkxzi1MsglfIvDRfZMbI4rbQ7U4Z9ZI2uORYUNc11Q" + ] + }, + "Body": "" + } + }, + { + "ID": "0a1fc0dcbb7bd603", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/checksum-object?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "cc030a96e48c78e565c7090c230c2330/14152616977161821098;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/checksum-object?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:48 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrna5:4014,/bns/yv/borg/yv/bns/blobstore2/bitpusher/992.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=E081W5vCNs-NgQTlgp7gAg" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/992.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/992:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrbF34gEFtIaYF5hDBng4rcMmk-Kgr9SkFjcOt6YKzbvRXgiPHHqUYvg9CZp1RE8WkCxuB8JycfwI_pXfRE0QJvK88lT2qWTqWBI8WwSxoNpKczCi0" + ] + }, + "Body": "" + } + }, + { + "ID": "3f44ca719bbbef0e", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d23f5a58aba3f35442898f79836abfbc/14960325228841164218;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:48 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrae22:4190,/bns/yv/borg/yv/bns/blobstore2/bitpusher/365.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FE81W9bpBoSOggTG3pLAAg" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/365.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/365:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur4rSMY4KaBrnrBgCgyfP4uWofPSRerp2zhOs3flZp0qkWu_D_TRL4x_RU2Ei5_akUAorSv856n8NiM6c6q3AtugIqdPXQq3zsZfL6IvXeGU17bLhA" + ] + }, + "Body": "" + } + }, + { + "ID": "0b071fb24fc3fcc1", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "83182f25d90fb5ad1fc17cd935f62bd6/15767752009855540938;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/composed2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:48 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrhm7:4367,/bns/yv/borg/yv/bns/blobstore2/bitpusher/953.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FE81W6HQEcrYgQTUhLqADw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/953.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/953:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrJgpjtairKPdhvXmzYv-MkBDa-tVyTB1JqVXOMRpoMv8IKY1wisFCSjGWGGU1KXSCUnDrou9MmovhbiMPU6lk8wa0scw88S6z3gQkN8AgUE345u9Y" + ] + }, + "Body": "" + } + }, + { + "ID": "01d09ce74da19cc0", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "b99cec3bbd0a67624c24e0fe3d87b8fb/16503402667513798873;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/content?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:48 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrfn12:4237,/bns/yv/borg/yv/bns/blobstore2/bitpusher/371.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FE81W_zoI4HMgQSH7pz4Aw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/371.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/371:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqdnIZxv_LoKM4wv-zOvNSyDb4WPv98lYHPRRK6b3uoB2ytHakHqW3Y9EELijP_p02hHqls8VrlPbVdlCMpTIbxH-t4GK4KFcZZkeANpRxdQElZJ04" + ] + }, + "Body": "" + } + }, + { + "ID": "4706e83991b3d511", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "448c389cfaf76c1ea190b016851299b2/17310829448528175593;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:48 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrar3:4282,/bns/yv/borg/yv/bns/blobstore2/bitpusher/304.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FE81W9eaL86gggSLx7LIDg" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/304.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/304:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UqfPIkjSxcfmR1udreZ1nfIDUVif6NXwqoHIu6srUKigANDtAXmsYy3dnadUZi7pRATMeS6d32YYFn1K3LwY7LxEoYHOw0hISFg3uMthuqrGn7WrAs" + ] + }, + "Body": "" + } + }, + { + "ID": "b75004afb49f4a60", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "d5680956f98168526456cdee469a205e/18118536600712668153;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:49 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrcb9:4119,/bns/yv/borg/yv/bns/blobstore2/bitpusher/879.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FE81W8CTOpPvgQTx_ILgCw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/879.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/879:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uoktpj__NgESZGfT9NWpMbYc5sOevTkUNILVa5VRCqaRc9JF-RK5vvaRZmLzeYZsSu6YagJ1eMMQS8QLAXnrOV5j2WjFtvQkGTvNfaBlJIqB-kgq4s" + ] + }, + "Body": "" + } + }, + { + "ID": "1d68f72e75cc1bbe", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6da7c341016883fdba2b498b26eb3d60/479500782994203657;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/customer-encryption-3?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:49 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrbj127:4134,/bns/yv/borg/yv/bns/blobstore2/bitpusher/709.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FU81W9epDYXPgQTy4byIDA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/709.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/709:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uox22PlRYbJr1cxKCZMtn679GcYH3E8FL_3vDsyataBh0OrVxTmUw5redH7RvtKIu45yHYaJ6w-3_9zis5wE5bRBI-BRZ9hjLSL79W9WDPL7vhjSes" + ] + }, + "Body": "" + } + }, + { + "ID": "e7062eb2267a4be7", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/gzip-test?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "386b2071c0e43962acd1a0fd3d4fb104/1286927559696967192;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/gzip-test?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:49 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrax68:4128,/bns/yv/borg/yv/bns/blobstore2/bitpusher/876.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FU81W_baF5PrgQTw24awDA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/876.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/876:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoxK0H-jyFlwpwubBx8Mxudipkeka-syXtXIqn-XeyL8IC-4Uqi2707T6p9WziguGrdElAZSuRzj9aeU_V_P9Cz0aACpkidQexZDY8jpctiUT0-sYk" + ] + }, + "Body": "" + } + }, + { + "ID": "c33e5f22f3db2450", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/hashesOnUpload-1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "871a1b16ecd9b52935f09baed95d9c02/2022578217371936552;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/hashesOnUpload-1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:49 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrlk21:4108,/bns/yv/borg/yv/bns/blobstore2/bitpusher/624.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FU81W7qNLY_iggTIoLzACA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/624.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/624:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UocuTT1I_KYJzU4r6AuKdcQCrL7FXV3uoofO1YsIPuiwR4tjs5QFE5srG27tiGBF_NcdeI0VE_XcY0iNu7qDzYBo9rAZzWSDW7qT0wTV_I9jvygsB8" + ] + }, + "Body": "" + } + }, + { + "ID": "62d700bdc068c567", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj%2Fwith%2Fslashes?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4c8a9c74ac0754415bf19e09d8c4d7f9/2830004998369536312;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj%2Fwith%2Fslashes?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:50 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vroa22:4081,/bns/yv/borg/yv/bns/blobstore2/bitpusher/681.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=FU81W8TtOdTLgATElKDYCA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/681.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/681:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoUtZrt6UXQ6KdXeslkKuukWgGgtprBb9OTh9ry2T1N6_PKbP4__C8pnHOpf0c2Un_aPmZQvF64CgQo6JKg43k3wbmtsj-zVvnZ9OP-sa_UFdezmf4" + ] + }, + "Body": "" + } + }, + { + "ID": "383ef4a6fcac1f9d", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2d0e93d1c1226d49179bd8b588fb16d2/3637713250065656392;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj1?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:50 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vruu1:4251,/bns/yv/borg/yv/bns/blobstore2/bitpusher/172.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Fk81W62WCcjigwTbnoCQDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/172.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/172:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UoCXQKnrr4K9hkXaq6pLeMd3w_R1jLvIMEn0CbB-h1IBGiFdYaRM7CkokBI3GdX3_uAGCximfhCGYuGPHqlTHAv50edhTpnS3I91tVNLMedvRgcLFE" + ] + }, + "Body": "" + } + }, + { + "ID": "98bb1efd62eee4aa", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "fd7ec434f24f91015a9a57173a4bcfbe/4445138931568471127;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/obj2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:50 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrns11:4000,/bns/yv/borg/yv/bns/blobstore2/bitpusher/64.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Fk81W7LpE5DuggSQnrGQDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/64.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/64:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrFo7VIoudKyDe0k9iB2mmF3iXe6HapY6vIYSm7GwMGsJTQj3O1_MkIvCyafIXI67YDpziFRof58iBbeOKiudXu0mC-YqS-oQukYXh49mgrxHX5Uz8" + ] + }, + "Body": "" + } + }, + { + "ID": "2864574fde06ad30", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "f04f700bc32639adc6cd3a4d75b4ef2c/5252847183264591207;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:50 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdv2:4288,/bns/yv/borg/yv/bns/blobstore2/bitpusher/233.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Fk81W4ulHtDqgASVw5yIDg" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/233.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/233:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UrMi8tXywghy7YH-xwJ6uwTxRncglFNSEstbB2woGYRFtugfWjC4pgAGuEVN6BKVHTv_DbrffPkL4P-KLnjJmKUKM0Lew" + ] + }, + "Body": "" + } + }, + { + "ID": "0b38bc202e8eb3aa", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc2?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "7107db2416933ec17ad3253d0e3fe39b/5988216370241040247;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/posc2?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:50 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrry5:4201,/bns/yv/borg/yv/bns/blobstore2/bitpusher/342.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Fk81W9TGKMnRgQSt85KoAw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/342.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/342:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UovZ9RSmw0HjJzn4ImzHNUEpMZDv4_4sv-6gthxJwLWYs0ruEoguDSDF-1GEq3doTRW9xLWCzk7lthkpUU-6MrQgxeYMA" + ] + }, + "Body": "" + } + }, + { + "ID": "673aba3e99cca295", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/signedURL?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "86a6d01c653eb58ec17bcfb62af090aa/6795924621937160327;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/signedURL?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:51 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrd1:4007,/bns/yv/borg/yv/bns/blobstore2/bitpusher/495.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=Fk81W6PtM42YgQTBnbXIDA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/495.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/495:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpZQQ-3o4Y5mirs3sMMwCsfvLQ_VSTlAP6-3oE64QLTWAnz-tvz85JBpv5W8tCPSBy7sJENU3jjpp4hL2Vlg4NYzEwM8g" + ] + }, + "Body": "" + } + }, + { + "ID": "0d82e2e35e856663", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/zero-object?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "233be859e998acc62b1e37122d42a35d/7603351398639923862;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000/o/zero-object?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:51 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrdb67:4086,/bns/yv/borg/yv/bns/blobstore2/bitpusher/243.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=F081W9fUAYaYgwT8mZroBA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/243.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/243:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur_4VFSCBrHWJ_5apya96vZMMUwhDB8iRoBGY6ipXh22BbCccc3B0oJnITh9A_kT1JVFOvpGiERhtfVd0lj9Ub8NyJuZ0VrXEdvcHjeK16BMLCOvfI" + ] + }, + "Body": "" + } + }, + { + "ID": "1d01c1ab0a81e7e1", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "e763b9bfdc100972cdb67400cc6fb82e/9218485331838793142;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180628-76155232935339-0000?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:51 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjn1:4228,/bns/yv/borg/yv/bns/blobstore2/bitpusher/642.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=F081W5XCDI2kggSh2J_QCA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/642.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/642:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uov7Uxd_3MsyBSW8-Yy4K4JgzlaJ5lRKPqp5ep2nDc7kAfSxd6c-Ft0JDTDtsLyevMqSg2PFk3uerMzKS5_0ggKFSGGBDQPSAF-jU1xtUgt6XkDcGw" + ] + }, + "Body": "" + } + }, + { + "ID": "3857ae628bc8a644", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b?alt=json\u0026pageToken=\u0026prefix=go-integration-test\u0026project=dulcet-port-762\u0026projection=full", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "2edcbdafb98ffee681c853966bbafe2c/9953855618326869702;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b?alt=json\u0026pageToken=\u0026prefix=go-integration-test\u0026project=dulcet-port-762\u0026projection=full" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "129365" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:51 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:51 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrk71:4158,/bns/yv/borg/yv/bns/blobstore2/bitpusher/534.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=F081W5ukIMSigQSG64bwDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/534.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/534:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Up3RLMLpxfMIUg6cJCHgoGzsiq3iN-T8erFRhkRpBIbCLCecb2J6tpqNu7oq1S08ngJzxw3SvasBgy8ocL7qwNWNcXi05imqSDs1n8oOwsJQIn4-hg" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNidWNrZXRzIiwKICJpdGVtcyI6IFsKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMiIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy02NDU3ODEwMTI4ODcxNy0wMDEyIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTY0NTc4MTAxMjg4NzE3LTAwMTIiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yN1QxNzo1ODoyOS40ODRaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yN1QxNzo1ODozMS42MjJaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTY0NTc4MTAxMjg4NzE3LTAwMTIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTY0NTc4MTAxMjg4NzE3LTAwMTIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy02NDU3ODEwMTI4ODcxNy0wMDEyL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy02NDU3ODEwMTI4ODcxNy0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yN1QxNzo1ODoyOS40ODRaIiwKICAgICJpc0xvY2tlZCI6IHRydWUKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMyIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy02NDU3ODEwMTI4ODcxNy0wMDEzIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTY0NTc4MTAxMjg4NzE3LTAwMTMiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yN1QxNzo1ODozMy45ODRaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yN1QxNzo1ODozMy45ODRaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTY0NTc4MTAxMjg4NzE3LTAwMTMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTY0NTc4MTAxMjg4NzE3LTAwMTMvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNjQ1NzgxMDEyODg3MTctMDAxMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy02NDU3ODEwMTI4ODcxNy0wMDEzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy02NDU3ODEwMTI4ODcxNy0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yN1QxNzo1ODozMy45ODRaIgogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEyIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc1ODMzNDM0Nzk4NjgzLTAwMTIiLAogICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICJuYW1lIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzU4MzM0MzQ3OTg2ODMtMDAxMiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjA2OjA3LjUwMFoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjA2OjA5LjcyMFoiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMiIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzU4MzM0MzQ3OTg2ODMtMDAxMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzU4MzM0MzQ3OTg2ODMtMDAxMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEyL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc1ODMzNDM0Nzk4NjgzLTAwMTIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc1ODMzNDM0Nzk4NjgzLTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI3VDIxOjA2OjA3LjUwMFoiLAogICAgImlzTG9ja2VkIjogdHJ1ZQogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEzIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc1ODMzNDM0Nzk4NjgzLTAwMTMiLAogICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICJuYW1lIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzU4MzM0MzQ3OTg2ODMtMDAxMyIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjA2OjEyLjA4NFoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjA2OjEyLjA4NFoiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEzL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzU4MzM0MzQ3OTg2ODMtMDAxMy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzU4MzM0MzQ3OTg2ODMtMDAxMy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NTgzMzQzNDc5ODY4My0wMDEzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc1ODMzNDM0Nzk4NjgzLTAwMTMvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc1ODMzNDM0Nzk4NjgzLTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI3VDIxOjA2OjEyLjA4NFoiCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTIiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzYzNTEwNjgxNzY2NzQtMDAxMiIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NjM1MTA2ODE3NjY3NC0wMDEyIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjdUMjE6MTQ6NDQuMzgyWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjdUMjE6MTQ6NDYuNjE5WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NjM1MTA2ODE3NjY3NC0wMDEyL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NjM1MTA2ODE3NjY3NC0wMDEyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzYzNTEwNjgxNzY2NzQtMDAxMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzYzNTEwNjgxNzY2NzQtMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjdUMjE6MTQ6NDQuMzgyWiIsCiAgICAiaXNMb2NrZWQiOiB0cnVlCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTMiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzYzNTEwNjgxNzY2NzQtMDAxMyIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NjM1MTA2ODE3NjY3NC0wMDEzIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjdUMjE6MTQ6NDguOTY0WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjdUMjE6MTQ6NDguOTY0WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NjM1MTA2ODE3NjY3NC0wMDEzL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NjM1MTA2ODE3NjY3NC0wMDEzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2MzUxMDY4MTc2Njc0LTAwMTMvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzYzNTEwNjgxNzY2NzQtMDAxMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzYzNTEwNjgxNzY2NzQtMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjdUMjE6MTQ6NDguOTY0WiIKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMiIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03Njg3MTIyODA1NTg4NC0wMDEyIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2ODcxMjI4MDU1ODg0LTAwMTIiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yN1QyMToyMzozMi45MDVaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yN1QyMToyMzozNS4yNDRaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2ODcxMjI4MDU1ODg0LTAwMTIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2ODcxMjI4MDU1ODg0LTAwMTIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03Njg3MTIyODA1NTg4NC0wMDEyL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03Njg3MTIyODA1NTg4NC0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yN1QyMToyMzozMi45MDVaIiwKICAgICJpc0xvY2tlZCI6IHRydWUKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMyIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03Njg3MTIyODA1NTg4NC0wMDEzIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2ODcxMjI4MDU1ODg0LTAwMTMiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yN1QyMToyMzozOC4wMDRaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yN1QyMToyMzozOC4wMDRaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2ODcxMjI4MDU1ODg0LTAwMTMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc2ODcxMjI4MDU1ODg0LTAwMTMvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzY4NzEyMjgwNTU4ODQtMDAxMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03Njg3MTIyODA1NTg4NC0wMDEzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03Njg3MTIyODA1NTg4NC0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yN1QyMToyMzozOC4wMDRaIgogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEyIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc3NDQ0Njc3MzU1Nzg3LTAwMTIiLAogICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICJuYW1lIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzc0NDQ2NzczNTU3ODctMDAxMiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjMzOjAxLjYwNFoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjMzOjAzLjc0MloiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMiIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzc0NDQ2NzczNTU3ODctMDAxMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzc0NDQ2NzczNTU3ODctMDAxMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEyL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc3NDQ0Njc3MzU1Nzg3LTAwMTIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc3NDQ0Njc3MzU1Nzg3LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI3VDIxOjMzOjAxLjYwNFoiLAogICAgImlzTG9ja2VkIjogdHJ1ZQogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEzIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc3NDQ0Njc3MzU1Nzg3LTAwMTMiLAogICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICJuYW1lIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzc0NDQ2NzczNTU3ODctMDAxMyIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjMzOjA2LjIwOVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI3VDIxOjMzOjA2LjIwOVoiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEzL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzc0NDQ2NzczNTU3ODctMDAxMy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzc0NDQ2NzczNTU3ODctMDAxMy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03NzQ0NDY3NzM1NTc4Ny0wMDEzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc3NDQ0Njc3MzU1Nzg3LTAwMTMvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc3NDQ0Njc3MzU1Nzg3LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI3VDIxOjMzOjA2LjIwOVoiCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NjY2MTczMTkyMTI5LTAwMDAiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzg2NjYxNzMxOTIxMjktMDAwMCIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03ODY2NjE3MzE5MjEyOS0wMDAwIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjdUMjE6NTE6MDcuNDY4WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjdUMjE6NTE6MDcuNDY4WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NjY2MTczMTkyMTI5LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03ODY2NjE3MzE5MjEyOS0wMDAwL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NjY2MTczMTkyMTI5LTAwMDAiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03ODY2NjE3MzE5MjEyOS0wMDAwL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NjY2MTczMTkyMTI5LTAwMDAvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NjY2MTczMTkyMTI5LTAwMDAiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NjY2MTczMTkyMTI5LTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzg2NjYxNzMxOTIxMjktMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzg2NjYxNzMxOTIxMjktMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NzU4Mzg2MzEyNzg4LTAwMDAiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzg3NTgzODYzMTI3ODgtMDAwMCIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03ODc1ODM4NjMxMjc4OC0wMDAwIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjdUMjE6NTI6MzkuNjAyWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjdUMjE6NTI6MzkuNjAyWiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NzU4Mzg2MzEyNzg4LTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03ODc1ODM4NjMxMjc4OC0wMDAwL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NzU4Mzg2MzEyNzg4LTAwMDAiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03ODc1ODM4NjMxMjc4OC0wMDAwL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NzU4Mzg2MzEyNzg4LTAwMDAvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NzU4Mzg2MzEyNzg4LTAwMDAiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc4NzU4Mzg2MzEyNzg4LTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzg3NTgzODYzMTI3ODgtMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzg3NTgzODYzMTI3ODgtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc5MDAyNzI1NTA0ODYzLTAwMDAiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzkwMDI3MjU1MDQ4NjMtMDAwMCIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03OTAwMjcyNTUwNDg2My0wMDAwIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjdUMjE6NTY6NDQuMTA0WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjdUMjE6NTY6NDQuMTA0WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc5MDAyNzI1NTA0ODYzLTAwMDAvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03OTAwMjcyNTUwNDg2My0wMDAwL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc5MDAyNzI1NTA0ODYzLTAwMDAiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy03OTAwMjcyNTUwNDg2My0wMDAwL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc5MDAyNzI1NTA0ODYzLTAwMDAvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc5MDAyNzI1NTA0ODYzLTAwMDAiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTc5MDAyNzI1NTA0ODYzLTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzkwMDI3MjU1MDQ4NjMtMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctNzkwMDI3MjU1MDQ4NjMtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTIiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODUxNTkzMjA2NTQyNTctMDAxMiIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTE1OTMyMDY1NDI1Ny0wMDEyIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjdUMjM6NDE6MjguMjY2WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjdUMjM6NDE6MzAuMzkwWiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTE1OTMyMDY1NDI1Ny0wMDEyL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTE1OTMyMDY1NDI1Ny0wMDEyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODUxNTkzMjA2NTQyNTctMDAxMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODUxNTkzMjA2NTQyNTctMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjdUMjM6NDE6MjguMjY2WiIsCiAgICAiaXNMb2NrZWQiOiB0cnVlCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTMiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODUxNTkzMjA2NTQyNTctMDAxMyIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTE1OTMyMDY1NDI1Ny0wMDEzIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjdUMjM6NDE6MzIuNzU3WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjdUMjM6NDE6MzIuNzU3WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTE1OTMyMDY1NDI1Ny0wMDEzL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTE1OTMyMDY1NDI1Ny0wMDEzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1MTU5MzIwNjU0MjU3LTAwMTMvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODUxNTkzMjA2NTQyNTctMDAxMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODUxNTkzMjA2NTQyNTctMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjdUMjM6NDE6MzIuNzU3WiIKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMiIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTYxNzU0NzkyMTM3Mi0wMDEyIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1NjE3NTQ3OTIxMzcyLTAwMTIiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yN1QyMzo0OTowNy4zODNaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yN1QyMzo0OTowOS4zMjNaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1NjE3NTQ3OTIxMzcyLTAwMTIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1NjE3NTQ3OTIxMzcyLTAwMTIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTYxNzU0NzkyMTM3Mi0wMDEyL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTYxNzU0NzkyMTM3Mi0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yN1QyMzo0OTowNy4zODNaIiwKICAgICJpc0xvY2tlZCI6IHRydWUKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMyIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTYxNzU0NzkyMTM3Mi0wMDEzIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1NjE3NTQ3OTIxMzcyLTAwMTMiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yN1QyMzo0OToxMS43OTJaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yN1QyMzo0OToxMS43OTJaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1NjE3NTQ3OTIxMzcyLTAwMTMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg1NjE3NTQ3OTIxMzcyLTAwMTMvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODU2MTc1NDc5MjEzNzItMDAxMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTYxNzU0NzkyMTM3Mi0wMDEzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NTYxNzU0NzkyMTM3Mi0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yN1QyMzo0OToxMS43OTJaIgogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEyIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg2MTA1NDU3MDg3NDM0LTAwMTIiLAogICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICJuYW1lIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODYxMDU0NTcwODc0MzQtMDAxMiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI3VDIzOjU3OjIzLjQ4OVoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI3VDIzOjU3OjI1Ljc0MloiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMiIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODYxMDU0NTcwODc0MzQtMDAxMi9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODYxMDU0NTcwODc0MzQtMDAxMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEyL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg2MTA1NDU3MDg3NDM0LTAwMTIvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg2MTA1NDU3MDg3NDM0LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI3VDIzOjU3OjIzLjQ4OVoiLAogICAgImlzTG9ja2VkIjogdHJ1ZQogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEzIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg2MTA1NDU3MDg3NDM0LTAwMTMiLAogICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICJuYW1lIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODYxMDU0NTcwODc0MzQtMDAxMyIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI3VDIzOjU3OjI4LjAwMloiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI3VDIzOjU3OjI4LjAwMloiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEzL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODYxMDU0NTcwODc0MzQtMDAxMy9hY2wvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjctODYxMDU0NTcwODc0MzQtMDAxMy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEzL2FjbC9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyNy04NjEwNTQ1NzA4NzQzNC0wMDEzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg2MTA1NDU3MDg3NDM0LTAwMTMvYWNsL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI3LTg2MTA1NDU3MDg3NDM0LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI3VDIzOjU3OjI4LjAwMloiCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUU9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEyIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEyIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEyIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMDA6MDU6NTkuODY5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMDA6MDY6MDIuMDI3WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEyL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMjI3OTI2NzM1NTEzLTAwMTIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMjI3OTI2NzM1NTEzLTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0yMjc5MjY3MzU1MTMtMDAxMi9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0yMjc5MjY3MzU1MTMtMDAxMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMjI3OTI2NzM1NTEzLTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEyL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEyL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0yMjc5MjY3MzU1MTMtMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMDA6MDU6NTkuODY5WiIsCiAgICAiaXNMb2NrZWQiOiB0cnVlCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEzIiwKICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEzIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEzIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMDA6MDY6MDQuMjAwWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMDA6MDY6MDQuMjAwWiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEzL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMjI3OTI2NzM1NTEzLTAwMTMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMjI3OTI2NzM1NTEzLTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0yMjc5MjY3MzU1MTMtMDAxMy9wcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0yMjc5MjY3MzU1MTMtMDAxMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMjI3OTI2NzM1NTEzLTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEzL3Byb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTIyNzkyNjczNTUxMy0wMDEzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0yMjc5MjY3MzU1MTMtMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMDA6MDY6MDQuMjAwWiIKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzI5NDE0MTA5NDg1ODUtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMjk0MTQxMDk0ODU4NS0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMyOTQxNDEwOTQ4NTg1LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQwOTowOTowMi41NTZaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQwOTowOTowMi41NTZaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzI5NDE0MTA5NDg1ODUtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMyOTQxNDEwOTQ4NTg1LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzI5NDE0MTA5NDg1ODUtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMyOTQxNDEwOTQ4NTg1LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzI5NDE0MTA5NDg1ODUtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzI5NDE0MTA5NDg1ODUtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzI5NDE0MTA5NDg1ODUtMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMjk0MTQxMDk0ODU4NS0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMjk0MTQxMDk0ODU4NS0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMyNjc4MjUzNjEzMTItMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzI2NzgyNTM2MTMxMi0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMjY3ODI1MzYxMzEyLTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxNDoyOC45NTdaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxNDoyOC45NTdaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMyNjc4MjUzNjEzMTItMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMjY3ODI1MzYxMzEyLTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMyNjc4MjUzNjEzMTItMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMjY3ODI1MzYxMzEyLTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMyNjc4MjUzNjEzMTItMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMyNjc4MjUzNjEzMTItMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMyNjc4MjUzNjEzMTItMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzI2NzgyNTM2MTMxMi0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzI2NzgyNTM2MTMxMi0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzMDYzNjA1ODYzOTgtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzMwNjM2MDU4NjM5OC0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMzA2MzYwNTg2Mzk4LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxNTowNy42MDFaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxNTowNy42MDFaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzMDYzNjA1ODYzOTgtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMzA2MzYwNTg2Mzk4LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzMDYzNjA1ODYzOTgtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMzA2MzYwNTg2Mzk4LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzMDYzNjA1ODYzOTgtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzMDYzNjA1ODYzOTgtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzMDYzNjA1ODYzOTgtMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzMwNjM2MDU4NjM5OC0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzMwNjM2MDU4NjM5OC0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzNDUyMjAxNDgyMjMtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzM0NTIyMDE0ODIyMy0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMzQ1MjIwMTQ4MjIzLTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxNTo0Ni41MDBaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxNTo0Ni41MDBaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzNDUyMjAxNDgyMjMtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMzQ1MjIwMTQ4MjIzLTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzNDUyMjAxNDgyMjMtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzMzQ1MjIwMTQ4MjIzLTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzNDUyMjAxNDgyMjMtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzNDUyMjAxNDgyMjMtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzMzNDUyMjAxNDgyMjMtMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzM0NTIyMDE0ODIyMy0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzM0NTIyMDE0ODIyMy0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzQxMjkzNDcwMzkyNi0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxNjo1NC4xNjlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQwOToxODoyMi4xMThaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBdz0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQXc9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQXc9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0F3PSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0F3PSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBdz0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJ2ZXJzaW9uaW5nIjogewogICAgImVuYWJsZWQiOiBmYWxzZQogICB9LAogICAibGlmZWN5Y2xlIjogewogICAgInJ1bGUiOiBbCiAgICAgewogICAgICAiYWN0aW9uIjogewogICAgICAgInR5cGUiOiAiRGVsZXRlIgogICAgICB9LAogICAgICAiY29uZGl0aW9uIjogewogICAgICAgImFnZSI6IDMwCiAgICAgIH0KICAgICB9CiAgICBdCiAgIH0sCiAgICJsYWJlbHMiOiB7CiAgICAibDEiOiAidjIiLAogICAgIm5ldyI6ICJuZXciCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQXc9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTIiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAxMiIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzQxMjkzNDcwMzkyNi0wMDEyIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMDk6MTg6NTQuMjAzWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMDk6MTg6NTYuNDMyWiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzQxMjkzNDcwMzkyNi0wMDEyL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzQxMjkzNDcwMzkyNi0wMDEyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAxMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMDk6MTg6NTQuMjAzWiIsCiAgICAiaXNMb2NrZWQiOiB0cnVlCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTMiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAxMyIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzQxMjkzNDcwMzkyNi0wMDEzIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMDk6MTg6NTguNjAwWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMDk6MTg6NTguNjAwWiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzQxMjkzNDcwMzkyNi0wMDEzL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzQxMjkzNDcwMzkyNi0wMDEzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzNDEyOTM0NzAzOTI2LTAwMTMvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAxMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM0MTI5MzQ3MDM5MjYtMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMDk6MTg6NTguNjAwWiIKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM5NDk5NDAzOTQxMzctMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzk0OTk0MDM5NDEzNy0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzOTQ5OTQwMzk0MTM3LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQwOToyNTo1MS4xMDdaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQwOToyNTo1MS4xMDdaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM5NDk5NDAzOTQxMzctMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzOTQ5OTQwMzk0MTM3LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM5NDk5NDAzOTQxMzctMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTMzOTQ5OTQwMzk0MTM3LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM5NDk5NDAzOTQxMzctMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM5NDk5NDAzOTQxMzctMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzM5NDk5NDAzOTQxMzctMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzk0OTk0MDM5NDEzNy0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zMzk0OTk0MDM5NDEzNy0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzU1Njg4MjY1OTY1MzctMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zNTU2ODgyNjU5NjUzNy0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM1NTY4ODI2NTk2NTM3LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQwOTo1Mjo1MC4xODdaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQwOTo1Mjo1MC4xODdaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzU1Njg4MjY1OTY1MzctMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM1NTY4ODI2NTk2NTM3LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzU1Njg4MjY1OTY1MzctMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM1NTY4ODI2NTk2NTM3LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzU1Njg4MjY1OTY1MzctMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzU1Njg4MjY1OTY1MzctMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzU1Njg4MjY1OTY1MzctMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zNTU2ODgyNjU5NjUzNy0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zNTU2ODgyNjU5NjUzNy0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxNjQ5NjQwNDMwMDktMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODE2NDk2NDA0MzAwOS0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MTY0OTY0MDQzMDA5LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQxMDozNjowNi4wODhaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQxMDozNjowNi4wODhaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxNjQ5NjQwNDMwMDktMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MTY0OTY0MDQzMDA5LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxNjQ5NjQwNDMwMDktMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MTY0OTY0MDQzMDA5LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxNjQ5NjQwNDMwMDktMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxNjQ5NjQwNDMwMDktMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxNjQ5NjQwNDMwMDktMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODE2NDk2NDA0MzAwOS0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODE2NDk2NDA0MzAwOS0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxOTA1MzQ2NjAyMDUtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODE5MDUzNDY2MDIwNS0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MTkwNTM0NjYwMjA1LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQxMDozNjozMS44NTZaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQxMDozNjozMS44NTZaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxOTA1MzQ2NjAyMDUtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MTkwNTM0NjYwMjA1LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxOTA1MzQ2NjAyMDUtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MTkwNTM0NjYwMjA1LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxOTA1MzQ2NjAyMDUtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxOTA1MzQ2NjAyMDUtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgxOTA1MzQ2NjAyMDUtMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODE5MDUzNDY2MDIwNS0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODE5MDUzNDY2MDIwNS0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODI0MjExMjE0ODgxNC0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQxMDozNzoyMy4yODdaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQxMDozODo1Mi4xMTdaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBdz0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQXc9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQXc9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0F3PSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0F3PSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBdz0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJ2ZXJzaW9uaW5nIjogewogICAgImVuYWJsZWQiOiBmYWxzZQogICB9LAogICAibGlmZWN5Y2xlIjogewogICAgInJ1bGUiOiBbCiAgICAgewogICAgICAiYWN0aW9uIjogewogICAgICAgInR5cGUiOiAiRGVsZXRlIgogICAgICB9LAogICAgICAiY29uZGl0aW9uIjogewogICAgICAgImFnZSI6IDMwCiAgICAgIH0KICAgICB9CiAgICBdCiAgIH0sCiAgICJsYWJlbHMiOiB7CiAgICAibDEiOiAidjIiLAogICAgIm5ldyI6ICJuZXciCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQXc9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTIiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAxMiIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODI0MjExMjE0ODgxNC0wMDEyIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMTA6Mzk6MjguMDkxWiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMTA6Mzk6MzAuNDQ0WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODI0MjExMjE0ODgxNC0wMDEyL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODI0MjExMjE0ODgxNC0wMDEyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAxMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMTA6Mzk6MjguMDkxWiIsCiAgICAiaXNMb2NrZWQiOiB0cnVlCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTMiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAxMyIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODI0MjExMjE0ODgxNC0wMDEzIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMTA6Mzk6MzIuNzk5WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMTA6Mzk6MzIuNzk5WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODI0MjExMjE0ODgxNC0wMDEzL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zODI0MjExMjE0ODgxNC0wMDEzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM4MjQyMTEyMTQ4ODE0LTAwMTMvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAxMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzgyNDIxMTIxNDg4MTQtMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMTA6Mzk6MzIuNzk5WiIKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzNjg4MzgxOTM0NjEtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM2ODgzODE5MzQ2MS0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5MzY4ODM4MTkzNDYxLTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQxMDo1NjowOS43MzlaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQxMDo1NjowOS43MzlaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzNjg4MzgxOTM0NjEtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5MzY4ODM4MTkzNDYxLTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzNjg4MzgxOTM0NjEtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5MzY4ODM4MTkzNDYxLTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzNjg4MzgxOTM0NjEtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzNjg4MzgxOTM0NjEtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzNjg4MzgxOTM0NjEtMDAwMC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM2ODgzODE5MzQ2MS0wMDAwL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM2ODgzODE5MzQ2MS0wMDAwIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAwMCIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM5NzIxODM0NTAzOC0wMDAwIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMDAiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQxMDo1NjozOC40NzRaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQxMDo1ODoxNC4yMTZaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMDAvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAwMC9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBdz0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAwMC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMDAvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQXc9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMDAvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAwMC9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAwMCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQXc9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0F3PSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0F3PSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBdz0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJ2ZXJzaW9uaW5nIjogewogICAgImVuYWJsZWQiOiBmYWxzZQogICB9LAogICAibGlmZWN5Y2xlIjogewogICAgInJ1bGUiOiBbCiAgICAgewogICAgICAiYWN0aW9uIjogewogICAgICAgInR5cGUiOiAiRGVsZXRlIgogICAgICB9LAogICAgICAiY29uZGl0aW9uIjogewogICAgICAgImFnZSI6IDMwCiAgICAgIH0KICAgICB9CiAgICBdCiAgIH0sCiAgICJsYWJlbHMiOiB7CiAgICAibDEiOiAidjIiLAogICAgIm5ldyI6ICJuZXciCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQXc9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTIiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAxMiIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM5NzIxODM0NTAzOC0wMDEyIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMTA6NTg6NTAuNzg4WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMTA6NTg6NTMuMTMyWiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIyIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTIvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM5NzIxODM0NTAzOC0wMDEyL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM5NzIxODM0NTAzOC0wMDEyL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTIvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAxMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMTA6NTg6NTAuNzg4WiIsCiAgICAiaXNMb2NrZWQiOiB0cnVlCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUk9IgogIH0sCiAgewogICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldCIsCiAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTMiLAogICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAxMyIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM5NzIxODM0NTAzOC0wMDEzIiwKICAgInRpbWVDcmVhdGVkIjogIjIwMTgtMDYtMjhUMTA6NTg6NTUuMjc3WiIsCiAgICJ1cGRhdGVkIjogIjIwMTgtMDYtMjhUMTA6NTg6NTUuMjc3WiIsCiAgICJtZXRhZ2VuZXJhdGlvbiI6ICIxIiwKICAgImFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTMvcHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM5NzIxODM0NTAzOC0wMDEzL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC0zOTM5NzIxODM0NTAzOC0wMDEzL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTM5Mzk3MjE4MzQ1MDM4LTAwMTMvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAxMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtMzkzOTcyMTgzNDUwMzgtMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAiZGVmYXVsdE9iamVjdEFjbCI6IFsKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAib3duZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJvd25lciI6IHsKICAgICJlbnRpdHkiOiAicHJvamVjdC1vd25lcnMtMzY2Mzk5MzMxNDUiCiAgIH0sCiAgICJsb2NhdGlvbiI6ICJVUyIsCiAgICJyZXRlbnRpb25Qb2xpY3kiOiB7CiAgICAicmV0ZW50aW9uUGVyaW9kIjogIjkwMDAwIiwKICAgICJlZmZlY3RpdmVUaW1lIjogIjIwMTgtMDYtMjhUMTA6NTg6NTUuMjc3WiIKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBRT0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEyIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMToyMC40NDRaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMToyMi40MzNaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjIiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMiIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEyL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMToyMC40NDRaIiwKICAgICJpc0xvY2tlZCI6IHRydWUKICAgfSwKICAgInN0b3JhZ2VDbGFzcyI6ICJTVEFOREFSRCIsCiAgICJldGFnIjogIkNBST0iCiAgfSwKICB7CiAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0IiwKICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMyIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEzIiwKICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAibmFtZSI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTMiLAogICAidGltZUNyZWF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMToyNC41ODdaIiwKICAgInVwZGF0ZWQiOiAiMjAxOC0wNi0yOFQyMToxMToyNC41ODdaIiwKICAgIm1ldGFnZW5lcmF0aW9uIjogIjEiLAogICAiYWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTMvYWNsL3Byb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI2J1Y2tldEFjY2Vzc0NvbnRyb2wiLAogICAgICJpZCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc2MTU1MjMyOTM1MzM5LTAwMTMvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMy9hY2wvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMyIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzYxNTUyMzI5MzUzMzktMDAxMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEzL2FjbC9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJidWNrZXQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NjE1NTIzMjkzNTMzOS0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtdmlld2Vycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiUkVBREVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAidmlld2VycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9CiAgIF0sCiAgICJkZWZhdWx0T2JqZWN0QWNsIjogWwogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJvd25lcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogImVkaXRvcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfSwKICAgIHsKICAgICAia2luZCI6ICJzdG9yYWdlI29iamVjdEFjY2Vzc0NvbnRyb2wiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgIm93bmVyIjogewogICAgImVudGl0eSI6ICJwcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIKICAgfSwKICAgImxvY2F0aW9uIjogIlVTIiwKICAgInJldGVudGlvblBvbGljeSI6IHsKICAgICJyZXRlbnRpb25QZXJpb2QiOiAiOTAwMDAiLAogICAgImVmZmVjdGl2ZVRpbWUiOiAiMjAxOC0wNi0yOFQyMToxMToyNC41ODdaIgogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FFPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMiIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMiIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMiIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDAwOjE1OjAzLjMwNloiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDAwOjE1OjA1LjY0MloiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMiIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMi9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc3NDg2NzM4NzQ1Ni0wMDEyL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc3NDg2NzM4NzQ1Ni0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzc0ODY3Mzg3NDU2LTAwMTIvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzc0ODY3Mzg3NDU2LTAwMTIvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc3NDg2NzM4NzQ1Ni0wMDEyIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMi9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMi9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzc0ODY3Mzg3NDU2LTAwMTIiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FJPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBST0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUk9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI4VDAwOjE1OjAzLjMwNloiLAogICAgImlzTG9ja2VkIjogdHJ1ZQogICB9LAogICAic3RvcmFnZUNsYXNzIjogIlNUQU5EQVJEIiwKICAgImV0YWciOiAiQ0FJPSIKICB9LAogIHsKICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXQiLAogICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMyIsCiAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMyIsCiAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgIm5hbWUiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMyIsCiAgICJ0aW1lQ3JlYXRlZCI6ICIyMDE4LTA2LTI4VDAwOjE1OjA3LjgwMFoiLAogICAidXBkYXRlZCI6ICIyMDE4LTA2LTI4VDAwOjE1OjA3LjgwMFoiLAogICAibWV0YWdlbmVyYXRpb24iOiAiMSIsCiAgICJhY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMy9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgInNlbGZMaW5rIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3N0b3JhZ2UvdjEvYi9nby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc3NDg2NzM4NzQ1Ni0wMDEzL2FjbC9wcm9qZWN0LW93bmVycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc3NDg2NzM4NzQ1Ni0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2UjYnVja2V0QWNjZXNzQ29udHJvbCIsCiAgICAgImlkIjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzc0ODY3Mzg3NDU2LTAwMTMvcHJvamVjdC1lZGl0b3JzLTM2NjM5OTMzMTQ1IiwKICAgICAic2VsZkxpbmsiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vc3RvcmFnZS92MS9iL2dvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzc0ODY3Mzg3NDU2LTAwMTMvYWNsL3Byb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgImJ1Y2tldCI6ICJnby1pbnRlZ3JhdGlvbi10ZXN0LTIwMTgwNjI4LTc3NDg2NzM4NzQ1Ni0wMDEzIiwKICAgICAiZW50aXR5IjogInByb2plY3QtZWRpdG9ycy0zNjYzOTkzMzE0NSIsCiAgICAgInJvbGUiOiAiT1dORVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJlZGl0b3JzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0sCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNidWNrZXRBY2Nlc3NDb250cm9sIiwKICAgICAiaWQiOiAiZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMy9wcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJzZWxmTGluayI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9zdG9yYWdlL3YxL2IvZ28taW50ZWdyYXRpb24tdGVzdC0yMDE4MDYyOC03NzQ4NjczODc0NTYtMDAxMy9hY2wvcHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAiYnVja2V0IjogImdvLWludGVncmF0aW9uLXRlc3QtMjAxODA2MjgtNzc0ODY3Mzg3NDU2LTAwMTMiLAogICAgICJlbnRpdHkiOiAicHJvamVjdC12aWV3ZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJSRUFERVIiLAogICAgICJwcm9qZWN0VGVhbSI6IHsKICAgICAgInByb2plY3ROdW1iZXIiOiAiMzY2Mzk5MzMxNDUiLAogICAgICAidGVhbSI6ICJ2aWV3ZXJzIgogICAgIH0sCiAgICAgImV0YWciOiAiQ0FFPSIKICAgIH0KICAgXSwKICAgImRlZmF1bHRPYmplY3RBY2wiOiBbCiAgICB7CiAgICAgImtpbmQiOiAic3RvcmFnZSNvYmplY3RBY2Nlc3NDb250cm9sIiwKICAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IiwKICAgICAicm9sZSI6ICJPV05FUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogIm93bmVycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LWVkaXRvcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIk9XTkVSIiwKICAgICAicHJvamVjdFRlYW0iOiB7CiAgICAgICJwcm9qZWN0TnVtYmVyIjogIjM2NjM5OTMzMTQ1IiwKICAgICAgInRlYW0iOiAiZWRpdG9ycyIKICAgICB9LAogICAgICJldGFnIjogIkNBRT0iCiAgICB9LAogICAgewogICAgICJraW5kIjogInN0b3JhZ2Ujb2JqZWN0QWNjZXNzQ29udHJvbCIsCiAgICAgImVudGl0eSI6ICJwcm9qZWN0LXZpZXdlcnMtMzY2Mzk5MzMxNDUiLAogICAgICJyb2xlIjogIlJFQURFUiIsCiAgICAgInByb2plY3RUZWFtIjogewogICAgICAicHJvamVjdE51bWJlciI6ICIzNjYzOTkzMzE0NSIsCiAgICAgICJ0ZWFtIjogInZpZXdlcnMiCiAgICAgfSwKICAgICAiZXRhZyI6ICJDQUU9IgogICAgfQogICBdLAogICAib3duZXIiOiB7CiAgICAiZW50aXR5IjogInByb2plY3Qtb3duZXJzLTM2NjM5OTMzMTQ1IgogICB9LAogICAibG9jYXRpb24iOiAiVVMiLAogICAicmV0ZW50aW9uUG9saWN5IjogewogICAgInJldGVudGlvblBlcmlvZCI6ICI5MDAwMCIsCiAgICAiZWZmZWN0aXZlVGltZSI6ICIyMDE4LTA2LTI4VDAwOjE1OjA3LjgwMFoiCiAgIH0sCiAgICJzdG9yYWdlQ2xhc3MiOiAiU1RBTkRBUkQiLAogICAiZXRhZyI6ICJDQUU9IgogIH0KIF0KfQo=" + } + }, + { + "ID": "ff43a73f40bf3372", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0012/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "eb2ec52c69b2d4ad7a84e6e020f094b0/10761562770511427797;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0012/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "31" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:52 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:52 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjo3:4099,/bns/yv/borg/yv/bns/blobstore2/bitpusher/833.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=F081W6_MPMSEggSEz7ngBg" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/833.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/833:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpG2yNf3WZQD7fTjw5CBMK8fIyYjxkpVOhlvByO2h3AgeLpCFV3NqKOu93F6J8ZQACgMKktdbzf0v5OFO7zghDO8Ik7rFc7s2v_5-BTISsExKP5sXY" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIgp9Cg==" + } + }, + { + "ID": "93e25768947f298f", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0012?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "c7229284c23cffc8580d9fb1ded30942/12376696703710297077;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0012?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:52 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrrr1:4228,/bns/yv/borg/yv/bns/blobstore2/bitpusher/911.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GE81W53ZDM6fgQT0pK3wDA" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/911.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/911:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpRX42gflr0_tdFDa5BqfChfcv5btRUhRVFA38rZrPyFr-hjmHcI0ZCEHRYNdUfF-Jt3L3j6_lqh3yDkKQn_bJTkI-REpM_Gc5emeeRMsQ36ptQGrI" + ] + }, + "Body": "" + } + }, + { + "ID": "890adf016aca6ef7", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0013/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9f1d7b46f3892a3a85d2fd062c2828dc/13184123484724673541;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0013/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "31" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:52 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:52 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vree2:4061,/bns/yv/borg/yv/bns/blobstore2/bitpusher/300.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GE81W9mUIpbLggSio7fwDg" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/300.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/300:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Ur6yI-5-iIWgSLsncrRsVpuQN5bUSy-48dG0Y2ZAczsWxR5J5rIGeIxpp82CuKkkbDptwxHQbQ8GnEvQmxJZZ1oQYJUgtXeiS-vWEY_t5VYUcK0SBQ" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIgp9Cg==" + } + }, + { + "ID": "e9636835c8c03f3f", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0013?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "faeda15f89a7b3341adacb13ebd167b2/14727200919102406436;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-64578101288717-0013?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:53 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrkw75:4001,/bns/yv/borg/yv/bns/blobstore2/bitpusher/761.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GE81W--qMYT4gQTxpIKAAQ" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/761.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/761:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uob2KwX0-qi1vYsvWnxXGn7PxY_0DghiIQ3_F01gr8IeU8n8pYTEIXd-gHfYKf2mOO4NLR2W9CrHHbqsehNTn_NNSBDEW1Zj-ITOMv0UVu3X8aBH_A" + ] + }, + "Body": "" + } + }, + { + "ID": "583e9ad50717be12", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0012/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "0b42658f5438c69d046fc0e8d93ef1d0/15534627700100006196;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0012/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "31" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:53 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:53 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrk71:4158,/bns/yv/borg/yv/bns/blobstore2/bitpusher/284.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GU81W9DICJGvgASSkZWIDQ" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/284.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/284:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UpaLTahTavEGo1WUwv21q9W44dfY5M4FwuOeQh8s1g_OiqSlRr0sbr36yVevYpA4bCAS-8GdRfIEGqyKwlZurbPhGRQf4c4kpPwHzNn2UtaFXp-Z5Q" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIgp9Cg==" + } + }, + { + "ID": "360876fc6e05a92a", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0012?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "6f6848b118702925dabf2f092db0cdfb/17077705138772575316;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0012?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:53 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrgm125:4144,/bns/yv/borg/yv/bns/blobstore2/bitpusher/773.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GU81W631GIi0gQSqyJ_4DQ" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/773.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/773:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2UowYxFDBOKl19fI3oq6WuOdLCmCBSw7vgibYT_tsVfWMnvNX87uj5njyfmguwimz25xrRF2YxsY1Gym39PuLwhDYkIxeVlC0HM4a2mjuMtE5MdZ854" + ] + }, + "Body": "" + } + }, + { + "ID": "03214a84d67fe129", + "Request": { + "Method": "GET", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0013/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "4309b8485eb74db44e17c0377a79d541/17885413390468760931;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0013/o?alt=json\u0026delimiter=\u0026pageToken=\u0026prefix=\u0026projection=full\u0026versions=false" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 200, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "private, max-age=0, must-revalidate, no-transform" + ], + "Content-Length": [ + "31" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:53 GMT" + ], + "Expires": [ + "Thu, 28 Jun 2018 21:11:53 GMT" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.read_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrns12:4246,/bns/yv/borg/yv/bns/blobstore2/bitpusher/738.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GU81W7GxK8WhgQSkk5G4Cw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/738.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/738:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4itK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq-26mV4XCP1ZF6TrJbI3x_RoorYJx6xod5vWuSmo9CnNBxqZTcT3mPEEL1zrnqylNmbSokUqcJ3VVxnre5EBwErkTYbMLH0Qy2LpWKpWSPt2vvrnI" + ] + }, + "Body": "ewogImtpbmQiOiAic3RvcmFnZSNvYmplY3RzIgp9Cg==" + } + }, + { + "ID": "001fb38821dc2cfd", + "Request": { + "Method": "DELETE", + "URL": "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0013?alt=json", + "Proto": "HTTP/1.1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Authorization": [ + "REDACTED" + ], + "User-Agent": [ + "google-api-go-client/0.5" + ], + "Via": [ + "1.1 httpr-8adfd5c90fff562f61d6" + ], + "X-Cloud-Trace-Context": [ + "9c26a269125cb20c48b9243e692fb0ac/1054084724934789251;o=1" + ], + "X-Forwarded-For": [ + "::1" + ], + "X-Forwarded-Host": [ + "www.googleapis.com" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Forwarded-Url": [ + "https://www.googleapis.com/storage/v1/b/go-integration-test-20180627-75833434798683-0013?alt=json" + ], + "X-Goog-Api-Client": [ + "gl-go/1.10.3 gccl/20180226" + ] + }, + "Body": "" + }, + "Response": { + "StatusCode": 204, + "Proto": "HTTP/1.1", + "Header": { + "Alt-Svc": [ + "quic=\":443\"; ma=2592000; v=\"43,42,41,39,35\"" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Content-Length": [ + "0" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Thu, 28 Jun 2018 21:11:54 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Pragma": [ + "no-cache" + ], + "Server": [ + "UploadServer" + ], + "Vary": [ + "Origin", + "X-Origin" + ], + "X-Google-Apiary-Auth-Expires": [ + "1530220607000" + ], + "X-Google-Apiary-Auth-Scopes": [ + "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/devstorage.write_only" + ], + "X-Google-Apiary-Auth-User": [ + "998958384336" + ], + "X-Google-Backends": [ + "vrjd6:4010,/bns/yv/borg/yv/bns/blobstore2/bitpusher/492.scotty,yabq4-v6:443" + ], + "X-Google-Dos-Service-Trace": [ + "main:apps-upload-cloud-storage-unified" + ], + "X-Google-Gfe-Backend-Request-Info": [ + "eid=GU81W4rCN5G2gQT8qJmACw" + ], + "X-Google-Gfe-Request-Trace": [ + "yabq4-v6:443,/bns/yv/borg/yv/bns/blobstore2/bitpusher/492.scotty,yabq4-v6:443" + ], + "X-Google-Gfe-Response-Code-Details-Trace": [ + "response_code_set_by_backend" + ], + "X-Google-Gfe-Service-Trace": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Netmon-Label": [ + "/bns/yv/borg/yv/bns/blobstore2/bitpusher/492:caf3" + ], + "X-Google-Service": [ + "bitpusher-gcs-apiary" + ], + "X-Google-Session-Info": [ + "CNCJvbSJHRoCGAYoATp3ChJjbG91ZC1zdG9yYWdlLXJvc3kSCGJpZ3N0b3JlGNmFpL-IASJHMzY2Mzk5MzMxNDUtYjE4dDAxb210OWEyNzlrYzNnY2dpcWhxa2w4Ym9iaHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20w4Csw4Ssw4ytK4wESxgF5YTI5LmMuRW93QjZBWEQzamVjdmNLTTQ5bXh3NjZJRjJSQm9xbmlMSGN0RnJPSTZVRDVWT0paMTJNLW1hSEpoUFBueU9aZncyb2c3aDh5LXFkMVBjekxPNV9HUW1NbC1aT2twcHpWR3pYb3c2OHRSOGZsdDZzNUowSGx6M0x4ZWlaVHhQOW5zUEFNc1pkNWQ5MVowbG1KbjU3UmVQbktDU0dhNkR3eGY1X0FQOG1FUEZ1RmRDdzhWZzY4ZFVUYmhCTk00STAwBDoWTk9UX0FfUEVSU0lTVEVOVF9UT0tFTg" + ], + "X-Google-Shellfish-Status": [ + "CA0gBEBG" + ], + "X-Guploader-Customer": [ + "apiary_cloudstorage_metadata" + ], + "X-Guploader-Request-Result": [ + "success" + ], + "X-Guploader-Upload-Result": [ + "success" + ], + "X-Guploader-Uploadid": [ + "AEnB2Uq1DO0Vl260ZjCOVTrGhgDDOZ5lQPIYmrK6mwKKLGTuDtY5LTDBJD4s1FHWoajL27SPNXPPxhtj6_j95UhaQjhY909A4bykI6nSNP5HGFc88bK2XvQ" + ] + }, + "Body": "" + } + } + ] +} \ No newline at end of file diff --git a/vendor/cloud.google.com/go/storage/storage_test.go b/vendor/cloud.google.com/go/storage/storage_test.go new file mode 100644 index 0000000000..c97fa587b7 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/storage_test.go @@ -0,0 +1,874 @@ +// 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 storage + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "net/http/httptest" + "net/url" + "regexp" + "strings" + "testing" + "time" + + "cloud.google.com/go/iam" + "cloud.google.com/go/internal/testutil" + + "golang.org/x/net/context" + "google.golang.org/api/googleapi" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + raw "google.golang.org/api/storage/v1" +) + +func TestHeaderSanitization(t *testing.T) { + t.Parallel() + var tests = []struct { + desc string + in []string + want []string + }{ + { + desc: "already sanitized headers should not be modified", + in: []string{"x-goog-header1:true", "x-goog-header2:0"}, + want: []string{"x-goog-header1:true", "x-goog-header2:0"}, + }, + { + desc: "sanitized headers should be sorted", + in: []string{"x-goog-header2:0", "x-goog-header1:true"}, + want: []string{"x-goog-header1:true", "x-goog-header2:0"}, + }, + { + desc: "non-canonical headers should be removed", + in: []string{"x-goog-header1:true", "x-goog-no-value", "non-canonical-header:not-of-use"}, + want: []string{"x-goog-header1:true"}, + }, + { + desc: "excluded canonical headers should be removed", + in: []string{"x-goog-header1:true", "x-goog-encryption-key:my_key", "x-goog-encryption-key-sha256:my_sha256"}, + want: []string{"x-goog-header1:true"}, + }, + { + desc: "dirty headers should be formatted correctly", + in: []string{" x-goog-header1 : \textra-spaces ", "X-Goog-Header2:CamelCaseValue"}, + want: []string{"x-goog-header1:extra-spaces", "x-goog-header2:CamelCaseValue"}, + }, + { + desc: "duplicate headers should be merged", + in: []string{"x-goog-header1:value1", "X-Goog-Header1:value2"}, + want: []string{"x-goog-header1:value1,value2"}, + }, + } + for _, test := range tests { + got := sanitizeHeaders(test.in) + if !testutil.Equal(got, test.want) { + t.Errorf("%s: got %v, want %v", test.desc, got, test.want) + } + } +} + +func TestSignedURL(t *testing.T) { + t.Parallel() + expires, _ := time.Parse(time.RFC3339, "2002-10-02T10:00:00-05:00") + url, err := SignedURL("bucket-name", "object-name", &SignedURLOptions{ + GoogleAccessID: "xxx@clientid", + PrivateKey: dummyKey("rsa"), + Method: "GET", + MD5: "ICy5YqxZB1uWSwcVLSNLcA==", + Expires: expires, + ContentType: "application/json", + Headers: []string{"x-goog-header1:true", "x-goog-header2:false"}, + }) + if err != nil { + t.Error(err) + } + want := "https://storage.googleapis.com/bucket-name/object-name?" + + "Expires=1033570800&GoogleAccessId=xxx%40clientid&Signature=" + + "RfsHlPtbB2JUYjzCgNr2Mi%2BjggdEuL1V7E6N9o6aaqwVLBDuTv3I0%2B9" + + "x94E6rmmr%2FVgnmZigkIUxX%2Blfl7LgKf30uPGLt0mjKGH2p7r9ey1ONJ" + + "%2BhVec23FnTRcSgopglvHPuCMWU2oNJE%2F1y8EwWE27baHrG1RhRHbLVF" + + "bPpLZ9xTRFK20pluIkfHV00JGljB1imqQHXM%2B2XPWqBngLr%2FwqxLN7i" + + "FcUiqR8xQEOHF%2F2e7fbkTHPNq4TazaLZ8X0eZ3eFdJ55A5QmNi8atlN4W" + + "5q7Hvs0jcxElG3yqIbx439A995BkspLiAcA%2Fo4%2BxAwEMkGLICdbvakq" + + "3eEprNCojw%3D%3D" + if url != want { + t.Fatalf("Unexpected signed URL; found %v", url) + } +} + +func TestSignedURL_PEMPrivateKey(t *testing.T) { + t.Parallel() + expires, _ := time.Parse(time.RFC3339, "2002-10-02T10:00:00-05:00") + url, err := SignedURL("bucket-name", "object-name", &SignedURLOptions{ + GoogleAccessID: "xxx@clientid", + PrivateKey: dummyKey("pem"), + Method: "GET", + MD5: "ICy5YqxZB1uWSwcVLSNLcA==", + Expires: expires, + ContentType: "application/json", + Headers: []string{"x-goog-header1:true", "x-goog-header2:false"}, + }) + if err != nil { + t.Error(err) + } + want := "https://storage.googleapis.com/bucket-name/object-name?" + + "Expires=1033570800&GoogleAccessId=xxx%40clientid&Signature=" + + "TiyKD%2FgGb6Kh0kkb2iF%2FfF%2BnTx7L0J4YiZua8AcTmnidutePEGIU5" + + "NULYlrGl6l52gz4zqFb3VFfIRTcPXMdXnnFdMCDhz2QuJBUpsU1Ai9zlyTQ" + + "dkb6ShG03xz9%2BEXWAUQO4GBybJw%2FULASuv37xA00SwLdkqj8YdyS5II" + + "1lro%3D" + if url != want { + t.Fatalf("Unexpected signed URL; found %v", url) + } +} + +func TestSignedURL_SignBytes(t *testing.T) { + t.Parallel() + expires, _ := time.Parse(time.RFC3339, "2002-10-02T10:00:00-05:00") + url, err := SignedURL("bucket-name", "object-name", &SignedURLOptions{ + GoogleAccessID: "xxx@clientid", + SignBytes: func(b []byte) ([]byte, error) { + return []byte("signed"), nil + }, + Method: "GET", + MD5: "ICy5YqxZB1uWSwcVLSNLcA==", + Expires: expires, + ContentType: "application/json", + Headers: []string{"x-goog-header1:true", "x-goog-header2:false"}, + }) + if err != nil { + t.Error(err) + } + want := "https://storage.googleapis.com/bucket-name/object-name?" + + "Expires=1033570800&GoogleAccessId=xxx%40clientid&Signature=" + + "c2lnbmVk" // base64('signed') == 'c2lnbmVk' + if url != want { + t.Fatalf("Unexpected signed URL\ngot: %q\nwant: %q", url, want) + } +} + +func TestSignedURL_URLUnsafeObjectName(t *testing.T) { + t.Parallel() + expires, _ := time.Parse(time.RFC3339, "2002-10-02T10:00:00-05:00") + url, err := SignedURL("bucket-name", "object name界", &SignedURLOptions{ + GoogleAccessID: "xxx@clientid", + PrivateKey: dummyKey("pem"), + Method: "GET", + MD5: "ICy5YqxZB1uWSwcVLSNLcA==", + Expires: expires, + ContentType: "application/json", + Headers: []string{"x-goog-header1:true", "x-goog-header2:false"}, + }) + if err != nil { + t.Error(err) + } + want := "https://storage.googleapis.com/bucket-name/object%20name%E7%95%8C?" + + "Expires=1033570800&GoogleAccessId=xxx%40clientid&Signature=bxVH1%2Bl%2" + + "BSxpnj3XuqKz6mOFk6M94Y%2B4w85J6FCmJan%2FNhGSpndP6fAw1uLHlOn%2F8xUaY%2F" + + "SfZ5GzcQ%2BbxOL1WA37yIwZ7xgLYlO%2ByAi3GuqMUmHZiNCai28emODXQ8RtWHvgv6dE" + + "SQ%2F0KpDMIWW7rYCaUa63UkUyeSQsKhrVqkIA%3D" + if url != want { + t.Fatalf("Unexpected signed URL; found %v", url) + } +} + +func TestSignedURL_MissingOptions(t *testing.T) { + t.Parallel() + pk := dummyKey("rsa") + expires, _ := time.Parse(time.RFC3339, "2002-10-02T10:00:00-05:00") + var tests = []struct { + opts *SignedURLOptions + errMsg string + }{ + { + &SignedURLOptions{}, + "missing required GoogleAccessID", + }, + { + &SignedURLOptions{GoogleAccessID: "access_id"}, + "exactly one of PrivateKey or SignedBytes must be set", + }, + { + &SignedURLOptions{ + GoogleAccessID: "access_id", + SignBytes: func(b []byte) ([]byte, error) { return b, nil }, + PrivateKey: pk, + }, + "exactly one of PrivateKey or SignedBytes must be set", + }, + { + &SignedURLOptions{ + GoogleAccessID: "access_id", + PrivateKey: pk, + }, + "missing required method", + }, + { + &SignedURLOptions{ + GoogleAccessID: "access_id", + SignBytes: func(b []byte) ([]byte, error) { return b, nil }, + }, + "missing required method", + }, + { + &SignedURLOptions{ + GoogleAccessID: "access_id", + PrivateKey: pk, + Method: "PUT", + }, + "missing required expires", + }, + { + &SignedURLOptions{ + GoogleAccessID: "access_id", + PrivateKey: pk, + Method: "PUT", + Expires: expires, + MD5: "invalid", + }, + "invalid MD5 checksum", + }, + } + for _, test := range tests { + _, err := SignedURL("bucket", "name", test.opts) + if !strings.Contains(err.Error(), test.errMsg) { + t.Errorf("expected err: %v, found: %v", test.errMsg, err) + } + } +} + +func dummyKey(kind string) []byte { + slurp, err := ioutil.ReadFile(fmt.Sprintf("./testdata/dummy_%s", kind)) + if err != nil { + log.Fatal(err) + } + return slurp +} + +func TestObjectNames(t *testing.T) { + t.Parallel() + // Naming requirements: https://cloud.google.com/storage/docs/bucket-naming + const maxLegalLength = 1024 + + type testT struct { + name, want string + } + tests := []testT{ + // Embedded characters important in URLs. + {"foo % bar", "foo%20%25%20bar"}, + {"foo ? bar", "foo%20%3F%20bar"}, + {"foo / bar", "foo%20/%20bar"}, + {"foo %?/ bar", "foo%20%25%3F/%20bar"}, + + // Non-Roman scripts + {"타코", "%ED%83%80%EC%BD%94"}, + {"世界", "%E4%B8%96%E7%95%8C"}, + + // Longest legal name + {strings.Repeat("a", maxLegalLength), strings.Repeat("a", maxLegalLength)}, + + // Line terminators besides CR and LF: https://en.wikipedia.org/wiki/Newline#Unicode + {"foo \u000b bar", "foo%20%0B%20bar"}, + {"foo \u000c bar", "foo%20%0C%20bar"}, + {"foo \u0085 bar", "foo%20%C2%85%20bar"}, + {"foo \u2028 bar", "foo%20%E2%80%A8%20bar"}, + {"foo \u2029 bar", "foo%20%E2%80%A9%20bar"}, + + // Null byte. + {"foo \u0000 bar", "foo%20%00%20bar"}, + + // Non-control characters that are discouraged, but not forbidden, according to the documentation. + {"foo # bar", "foo%20%23%20bar"}, + {"foo []*? bar", "foo%20%5B%5D%2A%3F%20bar"}, + + // Angstrom symbol singleton and normalized forms: http://unicode.org/reports/tr15/ + {"foo \u212b bar", "foo%20%E2%84%AB%20bar"}, + {"foo \u0041\u030a bar", "foo%20A%CC%8A%20bar"}, + {"foo \u00c5 bar", "foo%20%C3%85%20bar"}, + + // Hangul separating jamo: http://www.unicode.org/versions/Unicode7.0.0/ch18.pdf (Table 18-10) + {"foo \u3131\u314f bar", "foo%20%E3%84%B1%E3%85%8F%20bar"}, + {"foo \u1100\u1161 bar", "foo%20%E1%84%80%E1%85%A1%20bar"}, + {"foo \uac00 bar", "foo%20%EA%B0%80%20bar"}, + } + + // C0 control characters not forbidden by the docs. + var runes []rune + for r := rune(0x01); r <= rune(0x1f); r++ { + if r != '\u000a' && r != '\u000d' { + runes = append(runes, r) + } + } + tests = append(tests, testT{fmt.Sprintf("foo %s bar", string(runes)), "foo%20%01%02%03%04%05%06%07%08%09%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20bar"}) + + // C1 control characters, plus DEL. + runes = nil + for r := rune(0x7f); r <= rune(0x9f); r++ { + runes = append(runes, r) + } + tests = append(tests, testT{fmt.Sprintf("foo %s bar", string(runes)), "foo%20%7F%C2%80%C2%81%C2%82%C2%83%C2%84%C2%85%C2%86%C2%87%C2%88%C2%89%C2%8A%C2%8B%C2%8C%C2%8D%C2%8E%C2%8F%C2%90%C2%91%C2%92%C2%93%C2%94%C2%95%C2%96%C2%97%C2%98%C2%99%C2%9A%C2%9B%C2%9C%C2%9D%C2%9E%C2%9F%20bar"}) + + opts := &SignedURLOptions{ + GoogleAccessID: "xxx@clientid", + PrivateKey: dummyKey("rsa"), + Method: "GET", + MD5: "ICy5YqxZB1uWSwcVLSNLcA==", + Expires: time.Date(2002, time.October, 2, 10, 0, 0, 0, time.UTC), + ContentType: "application/json", + Headers: []string{"x-goog-header1", "x-goog-header2"}, + } + + for _, test := range tests { + g, err := SignedURL("bucket-name", test.name, opts) + if err != nil { + t.Errorf("SignedURL(%q) err=%v, want nil", test.name, err) + } + if w := "/bucket-name/" + test.want; !strings.Contains(g, w) { + t.Errorf("SignedURL(%q)=%q, want substring %q", test.name, g, w) + } + } +} + +func TestCondition(t *testing.T) { + t.Parallel() + gotReq := make(chan *http.Request, 1) + hc, close := newTestServer(func(w http.ResponseWriter, r *http.Request) { + io.Copy(ioutil.Discard, r.Body) + gotReq <- r + w.WriteHeader(200) + }) + defer close() + ctx := context.Background() + c, err := NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatal(err) + } + + obj := c.Bucket("buck").Object("obj") + dst := c.Bucket("dstbuck").Object("dst") + tests := []struct { + fn func() + want string + }{ + { + func() { obj.Generation(1234).NewReader(ctx) }, + "GET /buck/obj?generation=1234", + }, + { + func() { obj.If(Conditions{GenerationMatch: 1234}).NewReader(ctx) }, + "GET /buck/obj?ifGenerationMatch=1234", + }, + { + func() { obj.If(Conditions{GenerationNotMatch: 1234}).NewReader(ctx) }, + "GET /buck/obj?ifGenerationNotMatch=1234", + }, + { + func() { obj.If(Conditions{MetagenerationMatch: 1234}).NewReader(ctx) }, + "GET /buck/obj?ifMetagenerationMatch=1234", + }, + { + func() { obj.If(Conditions{MetagenerationNotMatch: 1234}).NewReader(ctx) }, + "GET /buck/obj?ifMetagenerationNotMatch=1234", + }, + { + func() { obj.If(Conditions{MetagenerationNotMatch: 1234}).Attrs(ctx) }, + "GET /storage/v1/b/buck/o/obj?alt=json&ifMetagenerationNotMatch=1234&projection=full", + }, + + { + func() { obj.If(Conditions{MetagenerationMatch: 1234}).Update(ctx, ObjectAttrsToUpdate{}) }, + "PATCH /storage/v1/b/buck/o/obj?alt=json&ifMetagenerationMatch=1234&projection=full", + }, + { + func() { obj.Generation(1234).Delete(ctx) }, + "DELETE /storage/v1/b/buck/o/obj?alt=json&generation=1234", + }, + { + func() { + w := obj.If(Conditions{GenerationMatch: 1234}).NewWriter(ctx) + w.ContentType = "text/plain" + w.Close() + }, + "POST /upload/storage/v1/b/buck/o?alt=json&ifGenerationMatch=1234&projection=full&uploadType=multipart", + }, + { + func() { + w := obj.If(Conditions{DoesNotExist: true}).NewWriter(ctx) + w.ContentType = "text/plain" + w.Close() + }, + "POST /upload/storage/v1/b/buck/o?alt=json&ifGenerationMatch=0&projection=full&uploadType=multipart", + }, + { + func() { + dst.If(Conditions{MetagenerationMatch: 5678}).CopierFrom(obj.If(Conditions{GenerationMatch: 1234})).Run(ctx) + }, + "POST /storage/v1/b/buck/o/obj/rewriteTo/b/dstbuck/o/dst?alt=json&ifMetagenerationMatch=5678&ifSourceGenerationMatch=1234&projection=full", + }, + } + + for i, tt := range tests { + tt.fn() + select { + case r := <-gotReq: + got := r.Method + " " + r.RequestURI + if got != tt.want { + t.Errorf("%d. RequestURI = %q; want %q", i, got, tt.want) + } + case <-time.After(5 * time.Second): + t.Fatalf("%d. timeout", i) + } + if err != nil { + t.Fatal(err) + } + } + + // Test an error, too: + err = obj.Generation(1234).NewWriter(ctx).Close() + if err == nil || !strings.Contains(err.Error(), "NewWriter: generation not supported") { + t.Errorf("want error about unsupported generation; got %v", err) + } +} + +func TestConditionErrors(t *testing.T) { + t.Parallel() + for _, conds := range []Conditions{ + {GenerationMatch: 0}, + {DoesNotExist: false}, // same as above, actually + {GenerationMatch: 1, GenerationNotMatch: 2}, + {GenerationNotMatch: 2, DoesNotExist: true}, + {MetagenerationMatch: 1, MetagenerationNotMatch: 2}, + } { + if err := conds.validate(""); err == nil { + t.Errorf("%+v: got nil, want error", conds) + } + } +} + +// Test object compose. +func TestObjectCompose(t *testing.T) { + t.Parallel() + gotURL := make(chan string, 1) + gotBody := make(chan []byte, 1) + hc, close := newTestServer(func(w http.ResponseWriter, r *http.Request) { + body, _ := ioutil.ReadAll(r.Body) + gotURL <- r.URL.String() + gotBody <- body + w.Write([]byte("{}")) + }) + defer close() + ctx := context.Background() + c, err := NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatal(err) + } + + testCases := []struct { + desc string + dst *ObjectHandle + srcs []*ObjectHandle + attrs *ObjectAttrs + wantReq raw.ComposeRequest + wantURL string + wantErr bool + }{ + { + desc: "basic case", + dst: c.Bucket("foo").Object("bar"), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object("baz"), + c.Bucket("foo").Object("quux"), + }, + wantURL: "/storage/v1/b/foo/o/bar/compose?alt=json", + wantReq: raw.ComposeRequest{ + Destination: &raw.Object{Bucket: "foo"}, + SourceObjects: []*raw.ComposeRequestSourceObjects{ + {Name: "baz"}, + {Name: "quux"}, + }, + }, + }, + { + desc: "with object attrs", + dst: c.Bucket("foo").Object("bar"), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object("baz"), + c.Bucket("foo").Object("quux"), + }, + attrs: &ObjectAttrs{ + Name: "not-bar", + ContentType: "application/json", + }, + wantURL: "/storage/v1/b/foo/o/bar/compose?alt=json", + wantReq: raw.ComposeRequest{ + Destination: &raw.Object{ + Bucket: "foo", + Name: "not-bar", + ContentType: "application/json", + }, + SourceObjects: []*raw.ComposeRequestSourceObjects{ + {Name: "baz"}, + {Name: "quux"}, + }, + }, + }, + { + desc: "with conditions", + dst: c.Bucket("foo").Object("bar").If(Conditions{ + GenerationMatch: 12, + MetagenerationMatch: 34, + }), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object("baz").Generation(56), + c.Bucket("foo").Object("quux").If(Conditions{GenerationMatch: 78}), + }, + wantURL: "/storage/v1/b/foo/o/bar/compose?alt=json&ifGenerationMatch=12&ifMetagenerationMatch=34", + wantReq: raw.ComposeRequest{ + Destination: &raw.Object{Bucket: "foo"}, + SourceObjects: []*raw.ComposeRequestSourceObjects{ + { + Name: "baz", + Generation: 56, + }, + { + Name: "quux", + ObjectPreconditions: &raw.ComposeRequestSourceObjectsObjectPreconditions{ + IfGenerationMatch: 78, + }, + }, + }, + }, + }, + { + desc: "no sources", + dst: c.Bucket("foo").Object("bar"), + wantErr: true, + }, + { + desc: "destination, no bucket", + dst: c.Bucket("").Object("bar"), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object("baz"), + }, + wantErr: true, + }, + { + desc: "destination, no object", + dst: c.Bucket("foo").Object(""), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object("baz"), + }, + wantErr: true, + }, + { + desc: "source, different bucket", + dst: c.Bucket("foo").Object("bar"), + srcs: []*ObjectHandle{ + c.Bucket("otherbucket").Object("baz"), + }, + wantErr: true, + }, + { + desc: "source, no object", + dst: c.Bucket("foo").Object("bar"), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object(""), + }, + wantErr: true, + }, + { + desc: "destination, bad condition", + dst: c.Bucket("foo").Object("bar").Generation(12), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object("baz"), + }, + wantErr: true, + }, + { + desc: "source, bad condition", + dst: c.Bucket("foo").Object("bar"), + srcs: []*ObjectHandle{ + c.Bucket("foo").Object("baz").If(Conditions{MetagenerationMatch: 12}), + }, + wantErr: true, + }, + } + + for _, tt := range testCases { + composer := tt.dst.ComposerFrom(tt.srcs...) + if tt.attrs != nil { + composer.ObjectAttrs = *tt.attrs + } + _, err := composer.Run(ctx) + if gotErr := err != nil; gotErr != tt.wantErr { + t.Errorf("%s: got error %v; want err %t", tt.desc, err, tt.wantErr) + continue + } + if tt.wantErr { + continue + } + url, body := <-gotURL, <-gotBody + if url != tt.wantURL { + t.Errorf("%s: request URL\ngot %q\nwant %q", tt.desc, url, tt.wantURL) + } + var req raw.ComposeRequest + if err := json.Unmarshal(body, &req); err != nil { + t.Errorf("%s: json.Unmarshal %v (body %s)", tt.desc, err, body) + } + if !testutil.Equal(req, tt.wantReq) { + // Print to JSON. + wantReq, _ := json.Marshal(tt.wantReq) + t.Errorf("%s: request body\ngot %s\nwant %s", tt.desc, body, wantReq) + } + } +} + +// Test that ObjectIterator's Next and NextPage methods correctly terminate +// if there is nothing to iterate over. +func TestEmptyObjectIterator(t *testing.T) { + t.Parallel() + hClient, close := newTestServer(func(w http.ResponseWriter, r *http.Request) { + io.Copy(ioutil.Discard, r.Body) + fmt.Fprintf(w, "{}") + }) + defer close() + ctx := context.Background() + client, err := NewClient(ctx, option.WithHTTPClient(hClient)) + if err != nil { + t.Fatal(err) + } + it := client.Bucket("b").Objects(ctx, nil) + _, err = it.Next() + if err != iterator.Done { + t.Errorf("got %v, want Done", err) + } +} + +// Test that BucketIterator's Next method correctly terminates if there is +// nothing to iterate over. +func TestEmptyBucketIterator(t *testing.T) { + t.Parallel() + hClient, close := newTestServer(func(w http.ResponseWriter, r *http.Request) { + io.Copy(ioutil.Discard, r.Body) + fmt.Fprintf(w, "{}") + }) + defer close() + ctx := context.Background() + client, err := NewClient(ctx, option.WithHTTPClient(hClient)) + if err != nil { + t.Fatal(err) + } + it := client.Buckets(ctx, "project") + _, err = it.Next() + if err != iterator.Done { + t.Errorf("got %v, want Done", err) + } + +} + +func TestCodecUint32(t *testing.T) { + t.Parallel() + for _, u := range []uint32{0, 1, 256, 0xFFFFFFFF} { + s := encodeUint32(u) + d, err := decodeUint32(s) + if err != nil { + t.Fatal(err) + } + if d != u { + t.Errorf("got %d, want input %d", d, u) + } + } +} + +func TestBucketAttrs(t *testing.T) { + for _, c := range []struct { + attrs BucketAttrs + raw raw.Bucket + }{{ + attrs: BucketAttrs{ + Lifecycle: Lifecycle{ + Rules: []LifecycleRule{{ + Action: LifecycleAction{ + Type: SetStorageClassAction, + StorageClass: "NEARLINE", + }, + Condition: LifecycleCondition{ + AgeInDays: 10, + Liveness: Live, + CreatedBefore: time.Date(2017, 1, 2, 3, 4, 5, 6, time.UTC), + MatchesStorageClasses: []string{"MULTI_REGIONAL", "REGIONAL", "STANDARD"}, + NumNewerVersions: 3, + }, + }, { + Action: LifecycleAction{ + Type: DeleteAction, + }, + Condition: LifecycleCondition{ + AgeInDays: 30, + Liveness: Live, + CreatedBefore: time.Date(2017, 1, 2, 3, 4, 5, 6, time.UTC), + MatchesStorageClasses: []string{"NEARLINE"}, + NumNewerVersions: 10, + }, + }, { + Action: LifecycleAction{ + Type: DeleteAction, + }, + Condition: LifecycleCondition{ + Liveness: Archived, + }, + }}, + }, + }, + raw: raw.Bucket{ + Lifecycle: &raw.BucketLifecycle{ + Rule: []*raw.BucketLifecycleRule{{ + Action: &raw.BucketLifecycleRuleAction{ + Type: SetStorageClassAction, + StorageClass: "NEARLINE", + }, + Condition: &raw.BucketLifecycleRuleCondition{ + Age: 10, + IsLive: googleapi.Bool(true), + CreatedBefore: "2017-01-02", + MatchesStorageClass: []string{"MULTI_REGIONAL", "REGIONAL", "STANDARD"}, + NumNewerVersions: 3, + }, + }, { + Action: &raw.BucketLifecycleRuleAction{ + Type: DeleteAction, + }, + Condition: &raw.BucketLifecycleRuleCondition{ + Age: 30, + IsLive: googleapi.Bool(true), + CreatedBefore: "2017-01-02", + MatchesStorageClass: []string{"NEARLINE"}, + NumNewerVersions: 10, + }, + }, { + Action: &raw.BucketLifecycleRuleAction{ + Type: DeleteAction, + }, + Condition: &raw.BucketLifecycleRuleCondition{ + IsLive: googleapi.Bool(false), + }, + }}, + }, + }, + }} { + if got := c.attrs.toRawBucket(); !testutil.Equal(*got, c.raw) { + t.Errorf("toRawBucket: got %v, want %v", *got, c.raw) + } + } +} + +func TestUserProject(t *testing.T) { + // Verify that the userProject query param is sent. + t.Parallel() + ctx := context.Background() + gotURL := make(chan *url.URL, 1) + hClient, close := newTestServer(func(w http.ResponseWriter, r *http.Request) { + io.Copy(ioutil.Discard, r.Body) + gotURL <- r.URL + if strings.Contains(r.URL.String(), "/rewriteTo/") { + res := &raw.RewriteResponse{Done: true} + bytes, err := res.MarshalJSON() + if err != nil { + t.Fatal(err) + } + w.Write(bytes) + } else { + fmt.Fprintf(w, "{}") + } + }) + defer close() + client, err := NewClient(ctx, option.WithHTTPClient(hClient)) + if err != nil { + t.Fatal(err) + } + + re := regexp.MustCompile(`\buserProject=p\b`) + b := client.Bucket("b").UserProject("p") + o := b.Object("o") + + check := func(msg string, f func()) { + f() + select { + case u := <-gotURL: + if !re.MatchString(u.RawQuery) { + t.Errorf("%s: query string %q does not contain userProject", msg, u.RawQuery) + } + case <-time.After(2 * time.Second): + t.Errorf("%s: timed out", msg) + } + } + + check("buckets.delete", func() { b.Delete(ctx) }) + check("buckets.get", func() { b.Attrs(ctx) }) + check("buckets.patch", func() { b.Update(ctx, BucketAttrsToUpdate{}) }) + check("storage.objects.compose", func() { o.ComposerFrom(b.Object("x")).Run(ctx) }) + check("storage.objects.delete", func() { o.Delete(ctx) }) + check("storage.objects.get", func() { o.Attrs(ctx) }) + check("storage.objects.insert", func() { o.NewWriter(ctx).Close() }) + check("storage.objects.list", func() { b.Objects(ctx, nil).Next() }) + check("storage.objects.patch", func() { o.Update(ctx, ObjectAttrsToUpdate{}) }) + check("storage.objects.rewrite", func() { o.CopierFrom(b.Object("x")).Run(ctx) }) + check("storage.objectAccessControls.list", func() { o.ACL().List(ctx) }) + check("storage.objectAccessControls.update", func() { o.ACL().Set(ctx, "", "") }) + check("storage.objectAccessControls.delete", func() { o.ACL().Delete(ctx, "") }) + check("storage.bucketAccessControls.list", func() { b.ACL().List(ctx) }) + check("storage.bucketAccessControls.update", func() { b.ACL().Set(ctx, "", "") }) + check("storage.bucketAccessControls.delete", func() { b.ACL().Delete(ctx, "") }) + check("storage.defaultObjectAccessControls.list", + func() { b.DefaultObjectACL().List(ctx) }) + check("storage.defaultObjectAccessControls.update", + func() { b.DefaultObjectACL().Set(ctx, "", "") }) + check("storage.defaultObjectAccessControls.delete", + func() { b.DefaultObjectACL().Delete(ctx, "") }) + check("buckets.getIamPolicy", func() { b.IAM().Policy(ctx) }) + check("buckets.setIamPolicy", func() { + p := &iam.Policy{} + p.Add("m", iam.Owner) + b.IAM().SetPolicy(ctx, p) + }) + check("buckets.testIamPermissions", func() { b.IAM().TestPermissions(ctx, nil) }) + check("storage.notifications.insert", func() { + b.AddNotification(ctx, &Notification{TopicProjectID: "p", TopicID: "t"}) + }) + check("storage.notifications.delete", func() { b.DeleteNotification(ctx, "n") }) + check("storage.notifications.list", func() { b.Notifications(ctx) }) +} + +func newTestServer(handler func(w http.ResponseWriter, r *http.Request)) (*http.Client, func()) { + ts := httptest.NewTLSServer(http.HandlerFunc(handler)) + tlsConf := &tls.Config{InsecureSkipVerify: true} + tr := &http.Transport{ + TLSClientConfig: tlsConf, + DialTLS: func(netw, addr string) (net.Conn, error) { + return tls.Dial("tcp", ts.Listener.Addr().String(), tlsConf) + }, + } + return &http.Client{Transport: tr}, func() { + tr.CloseIdleConnections() + ts.Close() + } +} diff --git a/vendor/cloud.google.com/go/storage/testdata/dummy_pem b/vendor/cloud.google.com/go/storage/testdata/dummy_pem new file mode 100644 index 0000000000..3428d4497c --- /dev/null +++ b/vendor/cloud.google.com/go/storage/testdata/dummy_pem @@ -0,0 +1,39 @@ +Bag Attributes + friendlyName: privatekey + localKeyID: 54 69 6D 65 20 31 34 31 36 38 35 32 30 30 34 37 37 32 +Key Attributes: +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCtCWMoJ2Bok2QoGFyU7A6IlGprO9QfUTT0jNrLkIbM5OWNIuDx +64+PEaTS5g5m+2Hz/lmd5jJKanAH4dY9LZzsaYAPq1K17Gcmg1hEisYeKsgOcjYY +kwRkV+natCTsC+tfWmS0voRh0jA1rI1J4MikceoHtgWdEuoHrrptRVpWKwIDAQAB +AoGAKp3uQvx3vSnX+BwP6Um+RpsvHpwMoW3xue1bEdnVqW8SrlERz+NxZw40ZxDs +KSbuuBZD4iTI7BUM5JQVnNm4FQY1YrPlWZLyI73Bj8RKTXrPdJheM/0r7xjiIXbQ +7w4cUSM9rVugnI/rxF2kPIQTGYI+EG/6+P+k6VvgPmC0T/ECQQDUPskiS18WaY+i +Koalbrb3GakaBoHrC1b4ln4CAv7fq7H4WvFvqi/2rxLhHYq31iwxYy8s7J7Sba1+ +5vwJ2TxZAkEA0LVfs3Q2VWZ+cM3bv0aYTalMXg6wT+LoNvk9HnOb0zQYajF3qm4G +ZFdfEqvOkje0zQ4fcihARKyda/VY84UGIwJBAIZa0FvjNmgrnn7bSKzEbxHwrnkJ +EYjGfuGR8mY3mzvfpiM+/oLfSslvfhX+62cALq18yco4ZzlxsFgaxAU//NECQDcS +NN94YcHlGqYPW9W7/gI4EwOaoqFhwV6II71+SfbP/0U+KlJZV+xwNZEKrqZcdqPI +/zkzL8ovNha/laokRrsCQQCyoPHGcBWj+VFbNoyQnX4tghc6rOY7n4pmpgQvU825 +TAM9vnYtSkKK/V56kEDNBO5LwiRsir95IUNclqqMKR1C +-----END RSA PRIVATE KEY----- +Bag Attributes + friendlyName: privatekey + localKeyID: 54 69 6D 65 20 31 34 31 36 38 35 32 30 30 34 37 37 32 +subject=/CN=1079432350659-nvog0vmn9s6pqr3kr4v2avbc7nkhoa11.apps.googleusercontent.com +issuer=/CN=1079432350659-nvog0vmn9s6pqr3kr4v2avbc7nkhoa11.apps.googleusercontent.com +-----BEGIN CERTIFICATE----- +MIICXTCCAcagAwIBAgIIHxTMQUVJRZ0wDQYJKoZIhvcNAQEFBQAwVDFSMFAGA1UE +AxNJMTA3OTQzMjM1MDY1OS1udm9nMHZtbjlzNnBxcjNrcjR2MmF2YmM3bmtob2Ex +MS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTAeFw0xNDExMjQxODAwMDRaFw0y +NDExMjExODAwMDRaMFQxUjBQBgNVBAMTSTEwNzk0MzIzNTA2NTktbnZvZzB2bW45 +czZwcXIza3I0djJhdmJjN25raG9hMTEuYXBwcy5nb29nbGV1c2VyY29udGVudC5j +b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK0JYygnYGiTZCgYXJTsDoiU +ams71B9RNPSM2suQhszk5Y0i4PHrj48RpNLmDmb7YfP+WZ3mMkpqcAfh1j0tnOxp +gA+rUrXsZyaDWESKxh4qyA5yNhiTBGRX6dq0JOwL619aZLS+hGHSMDWsjUngyKRx +6ge2BZ0S6geuum1FWlYrAgMBAAGjODA2MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/ +BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4GB +ACVvKkZkomHq3uffOQwdZ4VJYuxrvDGnZu/ExW9WngO2teEsjxABL41TNnRYHN5T +lMC19poFA2tR/DySDLJ2XNs/hSvyQUL6HHCncVdR4Srpie88j48peY1MZSMP51Jv +qagbbP5K5DSEu02/zZaV0kaCvLEN0KAtj/noDuOOnQU2 +-----END CERTIFICATE----- diff --git a/vendor/cloud.google.com/go/storage/testdata/dummy_rsa b/vendor/cloud.google.com/go/storage/testdata/dummy_rsa new file mode 100644 index 0000000000..4ce6678dbd --- /dev/null +++ b/vendor/cloud.google.com/go/storage/testdata/dummy_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE +DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY +fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK +1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr +k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9 +/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt +3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn +2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3 +nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK +6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf +5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e +DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1 +M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g +z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y +1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK +J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U +f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx +QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA +cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr +Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw +5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg +KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84 +OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd +mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ +5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg== +-----END RSA PRIVATE KEY----- diff --git a/vendor/cloud.google.com/go/storage/writer.go b/vendor/cloud.google.com/go/storage/writer.go new file mode 100644 index 0000000000..93597c8d6d --- /dev/null +++ b/vendor/cloud.google.com/go/storage/writer.go @@ -0,0 +1,224 @@ +// 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 storage + +import ( + "encoding/base64" + "errors" + "fmt" + "io" + "sync" + "unicode/utf8" + + "golang.org/x/net/context" + "google.golang.org/api/googleapi" + raw "google.golang.org/api/storage/v1" +) + +// A Writer writes a Cloud Storage object. +type Writer struct { + // ObjectAttrs are optional attributes to set on the object. Any attributes + // must be initialized before the first Write call. Nil or zero-valued + // attributes are ignored. + ObjectAttrs + + // SendCRC specifies whether to transmit a CRC32C field. It should be set + // to true in addition to setting the Writer's CRC32C field, because zero + // is a valid CRC and normally a zero would not be transmitted. + // If a CRC32C is sent, and the data written does not match the checksum, + // the write will be rejected. + SendCRC32C bool + + // ChunkSize controls the maximum number of bytes of the object that the + // Writer will attempt to send to the server in a single request. Objects + // smaller than the size will be sent in a single request, while larger + // objects will be split over multiple requests. The size will be rounded up + // to the nearest multiple of 256K. If zero, chunking will be disabled and + // the object will be uploaded in a single request. + // + // ChunkSize will default to a reasonable value. If you perform many concurrent + // writes of small objects, you may wish set ChunkSize to a value that matches + // your objects' sizes to avoid consuming large amounts of memory. + // + // ChunkSize must be set before the first Write call. + ChunkSize int + + // ProgressFunc can be used to monitor the progress of a large write. + // operation. If ProgressFunc is not nil and writing requires multiple + // calls to the underlying service (see + // https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload), + // then ProgressFunc will be invoked after each call with the number of bytes of + // content copied so far. + // + // ProgressFunc should return quickly without blocking. + ProgressFunc func(int64) + + ctx context.Context + o *ObjectHandle + + opened bool + pw *io.PipeWriter + + donec chan struct{} // closed after err and obj are set. + obj *ObjectAttrs + + mu sync.Mutex + err error +} + +func (w *Writer) open() error { + attrs := w.ObjectAttrs + // Check the developer didn't change the object Name (this is unfortunate, but + // we don't want to store an object under the wrong name). + if attrs.Name != w.o.object { + return fmt.Errorf("storage: Writer.Name %q does not match object name %q", attrs.Name, w.o.object) + } + if !utf8.ValidString(attrs.Name) { + return fmt.Errorf("storage: object name %q is not valid UTF-8", attrs.Name) + } + if attrs.KMSKeyName != "" && w.o.encryptionKey != nil { + return errors.New("storage: cannot use KMSKeyName with a customer-supplied encryption key") + } + pr, pw := io.Pipe() + w.pw = pw + w.opened = true + + if w.ChunkSize < 0 { + return errors.New("storage: Writer.ChunkSize must be non-negative") + } + mediaOpts := []googleapi.MediaOption{ + googleapi.ChunkSize(w.ChunkSize), + } + if c := attrs.ContentType; c != "" { + mediaOpts = append(mediaOpts, googleapi.ContentType(c)) + } + + go func() { + defer close(w.donec) + + rawObj := attrs.toRawObject(w.o.bucket) + if w.SendCRC32C { + rawObj.Crc32c = encodeUint32(attrs.CRC32C) + } + if w.MD5 != nil { + rawObj.Md5Hash = base64.StdEncoding.EncodeToString(w.MD5) + } + call := w.o.c.raw.Objects.Insert(w.o.bucket, rawObj). + Media(pr, mediaOpts...). + Projection("full"). + Context(w.ctx) + if w.ProgressFunc != nil { + call.ProgressUpdater(func(n, _ int64) { w.ProgressFunc(n) }) + } + if attrs.KMSKeyName != "" { + call.KmsKeyName(attrs.KMSKeyName) + } + if err := setEncryptionHeaders(call.Header(), w.o.encryptionKey, false); err != nil { + w.mu.Lock() + w.err = err + w.mu.Unlock() + pr.CloseWithError(err) + return + } + var resp *raw.Object + err := applyConds("NewWriter", w.o.gen, w.o.conds, call) + if err == nil { + if w.o.userProject != "" { + call.UserProject(w.o.userProject) + } + setClientHeader(call.Header()) + // If the chunk size is zero, then no chunking is done on the Reader, + // which means we cannot retry: the first call will read the data, and if + // it fails, there is no way to re-read. + if w.ChunkSize == 0 { + resp, err = call.Do() + } else { + // We will only retry here if the initial POST, which obtains a URI for + // the resumable upload, fails with a retryable error. The upload itself + // has its own retry logic. + err = runWithRetry(w.ctx, func() error { + var err2 error + resp, err2 = call.Do() + return err2 + }) + } + } + if err != nil { + w.mu.Lock() + w.err = err + w.mu.Unlock() + pr.CloseWithError(err) + return + } + w.obj = newObject(resp) + }() + return nil +} + +// Write appends to w. It implements the io.Writer interface. +// +// Since writes happen asynchronously, Write may return a nil +// error even though the write failed (or will fail). Always +// use the error returned from Writer.Close to determine if +// the upload was successful. +func (w *Writer) Write(p []byte) (n int, err error) { + w.mu.Lock() + werr := w.err + w.mu.Unlock() + if werr != nil { + return 0, werr + } + if !w.opened { + if err := w.open(); err != nil { + return 0, err + } + } + return w.pw.Write(p) +} + +// Close completes the write operation and flushes any buffered data. +// If Close doesn't return an error, metadata about the written object +// can be retrieved by calling Attrs. +func (w *Writer) Close() error { + if !w.opened { + if err := w.open(); err != nil { + return err + } + } + if err := w.pw.Close(); err != nil { + return err + } + <-w.donec + w.mu.Lock() + defer w.mu.Unlock() + return w.err +} + +// CloseWithError aborts the write operation with the provided error. +// CloseWithError always returns nil. +// +// Deprecated: cancel the context passed to NewWriter instead. +func (w *Writer) CloseWithError(err error) error { + if !w.opened { + return nil + } + return w.pw.CloseWithError(err) +} + +// Attrs returns metadata about a successfully-written object. +// It's only valid to call it after Close returns nil. +func (w *Writer) Attrs() *ObjectAttrs { + return w.obj +} diff --git a/vendor/cloud.google.com/go/storage/writer_test.go b/vendor/cloud.google.com/go/storage/writer_test.go new file mode 100644 index 0000000000..5c07c33a84 --- /dev/null +++ b/vendor/cloud.google.com/go/storage/writer_test.go @@ -0,0 +1,193 @@ +// 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 storage + +import ( + "crypto/sha256" + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" + "testing" + + "cloud.google.com/go/internal/testutil" + + "golang.org/x/net/context" + + "google.golang.org/api/googleapi" + "google.golang.org/api/option" +) + +var testEncryptionKey = []byte("secret-key-that-is-32-bytes-long") + +type fakeTransport struct { + gotReq *http.Request + gotBody []byte + results []transportResult +} + +type transportResult struct { + res *http.Response + err error +} + +func (t *fakeTransport) addResult(res *http.Response, err error) { + t.results = append(t.results, transportResult{res, err}) +} + +func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) { + t.gotReq = req + t.gotBody = nil + if req.Body != nil { + bytes, err := ioutil.ReadAll(req.Body) + if err != nil { + return nil, err + } + t.gotBody = bytes + } + if len(t.results) == 0 { + return nil, fmt.Errorf("error handling request") + } + result := t.results[0] + t.results = t.results[1:] + return result.res, result.err +} + +func TestErrorOnObjectsInsertCall(t *testing.T) { + t.Parallel() + ctx := context.Background() + const contents = "hello world" + + doWrite := func(hc *http.Client) *Writer { + client, err := NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatalf("error when creating client: %v", err) + } + wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx) + wc.ContentType = "text/plain" + + // We can't check that the Write fails, since it depends on the write to the + // underling fakeTransport failing which is racy. + wc.Write([]byte(contents)) + return wc + } + + wc := doWrite(&http.Client{Transport: &fakeTransport{}}) + // Close must always return an error though since it waits for the transport to + // have closed. + if err := wc.Close(); err == nil { + t.Errorf("expected error on close, got nil") + } + + // Retry on 5xx + ft := &fakeTransport{} + ft.addResult(&http.Response{StatusCode: 503, Body: bodyReader("")}, nil) + ft.addResult(&http.Response{StatusCode: 200, Body: bodyReader("{}")}, nil) + + wc = doWrite(&http.Client{Transport: ft}) + if err := wc.Close(); err != nil { + t.Errorf("got %v, want nil", err) + } + got := string(ft.gotBody) + if !strings.Contains(got, contents) { + t.Errorf("got body %q, which does not contain %q", got, contents) + } +} + +func TestEncryption(t *testing.T) { + t.Parallel() + ctx := context.Background() + ft := &fakeTransport{} + ft.addResult(&http.Response{StatusCode: 200, Body: bodyReader("{}")}, nil) + hc := &http.Client{Transport: ft} + client, err := NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatalf("error when creating client: %v", err) + } + obj := client.Bucket("bucketname").Object("filename1") + wc := obj.Key(testEncryptionKey).NewWriter(ctx) + if _, err := wc.Write([]byte("hello world")); err != nil { + t.Fatal(err) + } + if err := wc.Close(); err != nil { + t.Fatal(err) + } + if got, want := ft.gotReq.Header.Get("x-goog-encryption-algorithm"), "AES256"; got != want { + t.Errorf("algorithm: got %q, want %q", got, want) + } + gotKey, err := base64.StdEncoding.DecodeString(ft.gotReq.Header.Get("x-goog-encryption-key")) + if err != nil { + t.Fatalf("decoding key: %v", err) + } + if !testutil.Equal(gotKey, testEncryptionKey) { + t.Errorf("key: got %v, want %v", gotKey, testEncryptionKey) + } + wantHash := sha256.Sum256(testEncryptionKey) + gotHash, err := base64.StdEncoding.DecodeString(ft.gotReq.Header.Get("x-goog-encryption-key-sha256")) + if err != nil { + t.Fatalf("decoding hash: %v", err) + } + if !testutil.Equal(gotHash, wantHash[:]) { // wantHash is an array + t.Errorf("hash: got\n%v, want\n%v", gotHash, wantHash) + } + + // Using a customer-supplied encryption key and a KMS key together is an error. + checkKMSError := func(msg string, err error) { + if err == nil { + t.Errorf("%s: got nil, want error", msg) + } else if !strings.Contains(err.Error(), "KMS") { + t.Errorf(`%s: got %q, want it to contain "KMS"`, msg, err) + } + } + + wc = obj.Key(testEncryptionKey).NewWriter(ctx) + wc.KMSKeyName = "key" + _, err = wc.Write([]byte{}) + checkKMSError("Write", err) + checkKMSError("Close", wc.Close()) +} + +// This test demonstrates the data race on Writer.err that can happen when the +// Writer's context is cancelled. To see the race, comment out the w.mu.Lock/Unlock +// lines in writer.go and run this test with -race. +func TestRaceOnCancel(t *testing.T) { + ctx := context.Background() + ft := &fakeTransport{} + hc := &http.Client{Transport: ft} + client, err := NewClient(ctx, option.WithHTTPClient(hc)) + if err != nil { + t.Fatalf("error when creating client: %v", err) + } + + cctx, cancel := context.WithCancel(ctx) + w := client.Bucket("b").Object("o").NewWriter(cctx) + w.ChunkSize = googleapi.MinUploadChunkSize + buf := make([]byte, w.ChunkSize) + // This Write starts the goroutine in Writer.open. That reads the first chunk in its entirety + // before sending the request (see google.golang.org/api/gensupport.PrepareUpload), + // so to exhibit the race we must provide ChunkSize bytes. The goroutine then makes the RPC (L137). + w.Write(buf) + // Canceling the context causes the call to return context.Canceled, which makes the open goroutine + // write to w.err (L151). + cancel() + // This call to Write concurrently reads w.err (L169). + w.Write([]byte(nil)) +} + +func bodyReader(s string) io.ReadCloser { + return ioutil.NopCloser(strings.NewReader(s)) +} diff --git a/vendor/cloud.google.com/go/texttospeech/apiv1/doc.go b/vendor/cloud.google.com/go/texttospeech/apiv1/doc.go new file mode 100644 index 0000000000..b313cf8cdf --- /dev/null +++ b/vendor/cloud.google.com/go/texttospeech/apiv1/doc.go @@ -0,0 +1,47 @@ +// 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 texttospeech is an auto-generated package for the +// Cloud Text-to-Speech API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Synthesizes natural-sounding speech by applying powerful neural network +// models. +package texttospeech // import "cloud.google.com/go/texttospeech/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/texttospeech/apiv1/mock_test.go b/vendor/cloud.google.com/go/texttospeech/apiv1/mock_test.go new file mode 100644 index 0000000000..aef64ba922 --- /dev/null +++ b/vendor/cloud.google.com/go/texttospeech/apiv1/mock_test.go @@ -0,0 +1,232 @@ +// 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 texttospeech + +import ( + texttospeechpb "google.golang.org/genproto/googleapis/cloud/texttospeech/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockTextToSpeechServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + texttospeechpb.TextToSpeechServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockTextToSpeechServer) ListVoices(ctx context.Context, req *texttospeechpb.ListVoicesRequest) (*texttospeechpb.ListVoicesResponse, 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].(*texttospeechpb.ListVoicesResponse), nil +} + +func (s *mockTextToSpeechServer) SynthesizeSpeech(ctx context.Context, req *texttospeechpb.SynthesizeSpeechRequest) (*texttospeechpb.SynthesizeSpeechResponse, 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].(*texttospeechpb.SynthesizeSpeechResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockTextToSpeech mockTextToSpeechServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + texttospeechpb.RegisterTextToSpeechServer(serv, &mockTextToSpeech) + + 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 TestTextToSpeechListVoices(t *testing.T) { + var expectedResponse *texttospeechpb.ListVoicesResponse = &texttospeechpb.ListVoicesResponse{} + + mockTextToSpeech.err = nil + mockTextToSpeech.reqs = nil + + mockTextToSpeech.resps = append(mockTextToSpeech.resps[:0], expectedResponse) + + var request *texttospeechpb.ListVoicesRequest = &texttospeechpb.ListVoicesRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListVoices(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockTextToSpeech.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 TestTextToSpeechListVoicesError(t *testing.T) { + errCode := codes.PermissionDenied + mockTextToSpeech.err = gstatus.Error(errCode, "test error") + + var request *texttospeechpb.ListVoicesRequest = &texttospeechpb.ListVoicesRequest{} + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListVoices(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 TestTextToSpeechSynthesizeSpeech(t *testing.T) { + var audioContent []byte = []byte("16") + var expectedResponse = &texttospeechpb.SynthesizeSpeechResponse{ + AudioContent: audioContent, + } + + mockTextToSpeech.err = nil + mockTextToSpeech.reqs = nil + + mockTextToSpeech.resps = append(mockTextToSpeech.resps[:0], expectedResponse) + + var input *texttospeechpb.SynthesisInput = &texttospeechpb.SynthesisInput{} + var voice *texttospeechpb.VoiceSelectionParams = &texttospeechpb.VoiceSelectionParams{} + var audioConfig *texttospeechpb.AudioConfig = &texttospeechpb.AudioConfig{} + var request = &texttospeechpb.SynthesizeSpeechRequest{ + Input: input, + Voice: voice, + AudioConfig: audioConfig, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SynthesizeSpeech(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockTextToSpeech.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 TestTextToSpeechSynthesizeSpeechError(t *testing.T) { + errCode := codes.PermissionDenied + mockTextToSpeech.err = gstatus.Error(errCode, "test error") + + var input *texttospeechpb.SynthesisInput = &texttospeechpb.SynthesisInput{} + var voice *texttospeechpb.VoiceSelectionParams = &texttospeechpb.VoiceSelectionParams{} + var audioConfig *texttospeechpb.AudioConfig = &texttospeechpb.AudioConfig{} + var request = &texttospeechpb.SynthesizeSpeechRequest{ + Input: input, + Voice: voice, + AudioConfig: audioConfig, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.SynthesizeSpeech(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/vendor/cloud.google.com/go/texttospeech/apiv1/text_to_speech_client.go b/vendor/cloud.google.com/go/texttospeech/apiv1/text_to_speech_client.go new file mode 100644 index 0000000000..c69cf252e7 --- /dev/null +++ b/vendor/cloud.google.com/go/texttospeech/apiv1/text_to_speech_client.go @@ -0,0 +1,154 @@ +// 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 texttospeech + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + texttospeechpb "google.golang.org/genproto/googleapis/cloud/texttospeech/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 { + ListVoices []gax.CallOption + SynthesizeSpeech []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("texttospeech.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{ + ListVoices: retry[[2]string{"default", "idempotent"}], + SynthesizeSpeech: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Cloud Text-to-Speech 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 texttospeechpb.TextToSpeechClient + + // 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 text to speech client. +// +// Service that implements Google Cloud Text-to-Speech API. +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: texttospeechpb.NewTextToSpeechClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// ListVoices returns a list of [Voice][google.cloud.texttospeech.v1.Voice] +// supported for synthesis. +func (c *Client) ListVoices(ctx context.Context, req *texttospeechpb.ListVoicesRequest, opts ...gax.CallOption) (*texttospeechpb.ListVoicesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListVoices[0:len(c.CallOptions.ListVoices):len(c.CallOptions.ListVoices)], opts...) + var resp *texttospeechpb.ListVoicesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.ListVoices(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// SynthesizeSpeech synthesizes speech synchronously: receive results after all text input +// has been processed. +func (c *Client) SynthesizeSpeech(ctx context.Context, req *texttospeechpb.SynthesizeSpeechRequest, opts ...gax.CallOption) (*texttospeechpb.SynthesizeSpeechResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.SynthesizeSpeech[0:len(c.CallOptions.SynthesizeSpeech):len(c.CallOptions.SynthesizeSpeech)], opts...) + var resp *texttospeechpb.SynthesizeSpeechResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.SynthesizeSpeech(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/texttospeech/apiv1/text_to_speech_client_example_test.go b/vendor/cloud.google.com/go/texttospeech/apiv1/text_to_speech_client_example_test.go new file mode 100644 index 0000000000..cb9e9a7772 --- /dev/null +++ b/vendor/cloud.google.com/go/texttospeech/apiv1/text_to_speech_client_example_test.go @@ -0,0 +1,69 @@ +// 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 texttospeech_test + +import ( + "cloud.google.com/go/texttospeech/apiv1" + "golang.org/x/net/context" + texttospeechpb "google.golang.org/genproto/googleapis/cloud/texttospeech/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := texttospeech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_ListVoices() { + ctx := context.Background() + c, err := texttospeech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &texttospeechpb.ListVoicesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.ListVoices(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_SynthesizeSpeech() { + ctx := context.Background() + c, err := texttospeech.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &texttospeechpb.SynthesizeSpeechRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.SynthesizeSpeech(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/trace/apiv1/ListTraces_smoke_test.go b/vendor/cloud.google.com/go/trace/apiv1/ListTraces_smoke_test.go new file mode 100644 index 0000000000..1619ca19bb --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv1/ListTraces_smoke_test.go @@ -0,0 +1,67 @@ +// 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 trace + +import ( + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestTraceServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var projectId2 string = projectId + var request = &cloudtracepb.ListTracesRequest{ + ProjectId: projectId2, + } + + iter := c.ListTraces(ctx, request) + if _, err := iter.Next(); err != nil && err != iterator.Done { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/trace/apiv1/doc.go b/vendor/cloud.google.com/go/trace/apiv1/doc.go new file mode 100644 index 0000000000..29024af50d --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv1/doc.go @@ -0,0 +1,54 @@ +// 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 trace is an auto-generated package for the +// Stackdriver Trace API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Sends application trace data to Stackdriver Trace for viewing. Trace data +// is +// collected for all App Engine applications by default. Trace data from +// other +// applications can be provided using this API. +// +// Use the client at cloud.google.com/go/trace in preference to this. +package trace // import "cloud.google.com/go/trace/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + } +} diff --git a/vendor/cloud.google.com/go/trace/apiv1/mock_test.go b/vendor/cloud.google.com/go/trace/apiv1/mock_test.go new file mode 100644 index 0000000000..631281f40e --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv1/mock_test.go @@ -0,0 +1,321 @@ +// 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 trace + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockTraceServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + cloudtracepb.TraceServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockTraceServer) ListTraces(ctx context.Context, req *cloudtracepb.ListTracesRequest) (*cloudtracepb.ListTracesResponse, 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].(*cloudtracepb.ListTracesResponse), nil +} + +func (s *mockTraceServer) GetTrace(ctx context.Context, req *cloudtracepb.GetTraceRequest) (*cloudtracepb.Trace, 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].(*cloudtracepb.Trace), nil +} + +func (s *mockTraceServer) PatchTraces(ctx context.Context, req *cloudtracepb.PatchTracesRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockTrace mockTraceServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + cloudtracepb.RegisterTraceServiceServer(serv, &mockTrace) + + 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 TestTraceServicePatchTraces(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockTrace.err = nil + mockTrace.reqs = nil + + mockTrace.resps = append(mockTrace.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var traces *cloudtracepb.Traces = &cloudtracepb.Traces{} + var request = &cloudtracepb.PatchTracesRequest{ + ProjectId: projectId, + Traces: traces, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.PatchTraces(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockTrace.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestTraceServicePatchTracesError(t *testing.T) { + errCode := codes.PermissionDenied + mockTrace.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var traces *cloudtracepb.Traces = &cloudtracepb.Traces{} + var request = &cloudtracepb.PatchTracesRequest{ + ProjectId: projectId, + Traces: traces, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.PatchTraces(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 TestTraceServiceGetTrace(t *testing.T) { + var projectId2 string = "projectId2939242356" + var traceId2 string = "traceId2987826376" + var expectedResponse = &cloudtracepb.Trace{ + ProjectId: projectId2, + TraceId: traceId2, + } + + mockTrace.err = nil + mockTrace.reqs = nil + + mockTrace.resps = append(mockTrace.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var traceId string = "traceId1270300245" + var request = &cloudtracepb.GetTraceRequest{ + ProjectId: projectId, + TraceId: traceId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTrace(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockTrace.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 TestTraceServiceGetTraceError(t *testing.T) { + errCode := codes.PermissionDenied + mockTrace.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var traceId string = "traceId1270300245" + var request = &cloudtracepb.GetTraceRequest{ + ProjectId: projectId, + TraceId: traceId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.GetTrace(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 TestTraceServiceListTraces(t *testing.T) { + var nextPageToken string = "" + var tracesElement *cloudtracepb.Trace = &cloudtracepb.Trace{} + var traces = []*cloudtracepb.Trace{tracesElement} + var expectedResponse = &cloudtracepb.ListTracesResponse{ + NextPageToken: nextPageToken, + Traces: traces, + } + + mockTrace.err = nil + mockTrace.reqs = nil + + mockTrace.resps = append(mockTrace.resps[:0], expectedResponse) + + var projectId string = "projectId-1969970175" + var request = &cloudtracepb.ListTracesRequest{ + ProjectId: projectId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTraces(context.Background(), request).Next() + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockTrace.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + + want := (interface{})(expectedResponse.Traces[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 TestTraceServiceListTracesError(t *testing.T) { + errCode := codes.PermissionDenied + mockTrace.err = gstatus.Error(errCode, "test error") + + var projectId string = "projectId-1969970175" + var request = &cloudtracepb.ListTracesRequest{ + ProjectId: projectId, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.ListTraces(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 +} diff --git a/vendor/cloud.google.com/go/trace/apiv1/trace_client.go b/vendor/cloud.google.com/go/trace/apiv1/trace_client.go new file mode 100644 index 0000000000..90b33c510e --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv1/trace_client.go @@ -0,0 +1,240 @@ +// 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 trace + +import ( + "math" + "time" + + "cloud.google.com/go/internal/version" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + "google.golang.org/api/transport" + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/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 { + PatchTraces []gax.CallOption + GetTrace []gax.CallOption + ListTraces []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("cloudtrace.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: 1000 * time.Millisecond, + Multiplier: 1.2, + }) + }), + }, + } + return &CallOptions{ + PatchTraces: retry[[2]string{"default", "idempotent"}], + GetTrace: retry[[2]string{"default", "idempotent"}], + ListTraces: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Stackdriver Trace 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 cloudtracepb.TraceServiceClient + + // 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 trace service client. +// +// This file describes an API for collecting and viewing traces and spans +// within a trace. A Trace is a collection of spans corresponding to a single +// operation or set of operations for an application. A span is an individual +// timed event which forms a node of the trace tree. Spans for a single trace +// may span multiple services. +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: cloudtracepb.NewTraceServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// PatchTraces sends new traces to Stackdriver Trace or updates existing traces. If the ID +// of a trace that you send matches that of an existing trace, any fields +// in the existing trace and its spans are overwritten by the provided values, +// and any new fields provided are merged with the existing trace data. If the +// ID does not match, a new trace is created. +func (c *Client) PatchTraces(ctx context.Context, req *cloudtracepb.PatchTracesRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.PatchTraces[0:len(c.CallOptions.PatchTraces):len(c.CallOptions.PatchTraces)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.PatchTraces(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// GetTrace gets a single trace by its ID. +func (c *Client) GetTrace(ctx context.Context, req *cloudtracepb.GetTraceRequest, opts ...gax.CallOption) (*cloudtracepb.Trace, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.GetTrace[0:len(c.CallOptions.GetTrace):len(c.CallOptions.GetTrace)], opts...) + var resp *cloudtracepb.Trace + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.GetTrace(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// ListTraces returns of a list of traces that match the specified filter conditions. +func (c *Client) ListTraces(ctx context.Context, req *cloudtracepb.ListTracesRequest, opts ...gax.CallOption) *TraceIterator { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.ListTraces[0:len(c.CallOptions.ListTraces):len(c.CallOptions.ListTraces)], opts...) + it := &TraceIterator{} + req = proto.Clone(req).(*cloudtracepb.ListTracesRequest) + it.InternalFetch = func(pageSize int, pageToken string) ([]*cloudtracepb.Trace, string, error) { + var resp *cloudtracepb.ListTracesResponse + 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.ListTraces(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, "", err + } + return resp.Traces, 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 +} + +// TraceIterator manages a stream of *cloudtracepb.Trace. +type TraceIterator struct { + items []*cloudtracepb.Trace + 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 []*cloudtracepb.Trace, nextPageToken string, err error) +} + +// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. +func (it *TraceIterator) 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 *TraceIterator) Next() (*cloudtracepb.Trace, error) { + var item *cloudtracepb.Trace + if err := it.nextFunc(); err != nil { + return item, err + } + item = it.items[0] + it.items = it.items[1:] + return item, nil +} + +func (it *TraceIterator) bufLen() int { + return len(it.items) +} + +func (it *TraceIterator) takeBuf() interface{} { + b := it.items + it.items = nil + return b +} diff --git a/vendor/cloud.google.com/go/trace/apiv1/trace_client_example_test.go b/vendor/cloud.google.com/go/trace/apiv1/trace_client_example_test.go new file mode 100644 index 0000000000..2fe01b061c --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv1/trace_client_example_test.go @@ -0,0 +1,92 @@ +// 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 trace_test + +import ( + "cloud.google.com/go/trace/apiv1" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := trace.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_PatchTraces() { + ctx := context.Background() + c, err := trace.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &cloudtracepb.PatchTracesRequest{ + // TODO: Fill request struct fields. + } + err = c.PatchTraces(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_GetTrace() { + ctx := context.Background() + c, err := trace.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &cloudtracepb.GetTraceRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.GetTrace(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleClient_ListTraces() { + ctx := context.Background() + c, err := trace.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &cloudtracepb.ListTracesRequest{ + // TODO: Fill request struct fields. + } + it := c.ListTraces(ctx, req) + for { + resp, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp + } +} diff --git a/vendor/cloud.google.com/go/trace/apiv2/BatchWriteSpans_smoke_test.go b/vendor/cloud.google.com/go/trace/apiv2/BatchWriteSpans_smoke_test.go new file mode 100644 index 0000000000..0c0c118833 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv2/BatchWriteSpans_smoke_test.go @@ -0,0 +1,66 @@ +// 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 trace + +import ( + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestTraceServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var formattedName string = fmt.Sprintf("projects/%s", projectId) + var request = &cloudtracepb.BatchWriteSpansRequest{ + Name: formattedName, + } + + if err := c.BatchWriteSpans(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/trace/apiv2/doc.go b/vendor/cloud.google.com/go/trace/apiv2/doc.go new file mode 100644 index 0000000000..2f7ffdc01c --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv2/doc.go @@ -0,0 +1,51 @@ +// 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 trace is an auto-generated package for the +// Stackdriver Trace API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Sends application trace data to Stackdriver Trace for viewing. Trace data +// is +// collected for all App Engine applications by default. Trace data from +// other +// applications can be provided using this API. +package trace // import "cloud.google.com/go/trace/apiv2" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/trace.append", + } +} diff --git a/vendor/cloud.google.com/go/trace/apiv2/mock_test.go b/vendor/cloud.google.com/go/trace/apiv2/mock_test.go new file mode 100644 index 0000000000..c7fff5c495 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv2/mock_test.go @@ -0,0 +1,252 @@ +// 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 trace + +import ( + emptypb "github.com/golang/protobuf/ptypes/empty" + timestamppb "github.com/golang/protobuf/ptypes/timestamp" + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockTraceServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + cloudtracepb.TraceServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockTraceServer) BatchWriteSpans(ctx context.Context, req *cloudtracepb.BatchWriteSpansRequest) (*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 *mockTraceServer) CreateSpan(ctx context.Context, req *cloudtracepb.Span) (*cloudtracepb.Span, 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].(*cloudtracepb.Span), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockTrace mockTraceServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + cloudtracepb.RegisterTraceServiceServer(serv, &mockTrace) + + 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 TestTraceServiceBatchWriteSpans(t *testing.T) { + var expectedResponse *emptypb.Empty = &emptypb.Empty{} + + mockTrace.err = nil + mockTrace.reqs = nil + + mockTrace.resps = append(mockTrace.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var spans []*cloudtracepb.Span = nil + var request = &cloudtracepb.BatchWriteSpansRequest{ + Name: formattedName, + Spans: spans, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.BatchWriteSpans(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockTrace.reqs[0]; !proto.Equal(want, got) { + t.Errorf("wrong request %q, want %q", got, want) + } + +} + +func TestTraceServiceBatchWriteSpansError(t *testing.T) { + errCode := codes.PermissionDenied + mockTrace.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s", "[PROJECT]") + var spans []*cloudtracepb.Span = nil + var request = &cloudtracepb.BatchWriteSpansRequest{ + Name: formattedName, + Spans: spans, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + err = c.BatchWriteSpans(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 TestTraceServiceCreateSpan(t *testing.T) { + var name2 string = "name2-1052831874" + var spanId2 string = "spanId2-643891741" + var parentSpanId string = "parentSpanId-1757797477" + var expectedResponse = &cloudtracepb.Span{ + Name: name2, + SpanId: spanId2, + ParentSpanId: parentSpanId, + } + + mockTrace.err = nil + mockTrace.reqs = nil + + mockTrace.resps = append(mockTrace.resps[:0], expectedResponse) + + var formattedName string = fmt.Sprintf("projects/%s/traces/%s/spans/%s", "[PROJECT]", "[TRACE]", "[SPAN]") + var spanId string = "spanId-2011840976" + var displayName *cloudtracepb.TruncatableString = &cloudtracepb.TruncatableString{} + var startTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var endTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var request = &cloudtracepb.Span{ + Name: formattedName, + SpanId: spanId, + DisplayName: displayName, + StartTime: startTime, + EndTime: endTime, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSpan(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockTrace.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 TestTraceServiceCreateSpanError(t *testing.T) { + errCode := codes.PermissionDenied + mockTrace.err = gstatus.Error(errCode, "test error") + + var formattedName string = fmt.Sprintf("projects/%s/traces/%s/spans/%s", "[PROJECT]", "[TRACE]", "[SPAN]") + var spanId string = "spanId-2011840976" + var displayName *cloudtracepb.TruncatableString = &cloudtracepb.TruncatableString{} + var startTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var endTime *timestamppb.Timestamp = ×tamppb.Timestamp{} + var request = &cloudtracepb.Span{ + Name: formattedName, + SpanId: spanId, + DisplayName: displayName, + StartTime: startTime, + EndTime: endTime, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.CreateSpan(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/vendor/cloud.google.com/go/trace/apiv2/path_funcs.go b/vendor/cloud.google.com/go/trace/apiv2/path_funcs.go new file mode 100644 index 0000000000..80b8d40b58 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv2/path_funcs.go @@ -0,0 +1,43 @@ +// 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. + +package trace + +// ProjectPath returns the path for the project resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s", project) +// instead. +func ProjectPath(project string) string { + return "" + + "projects/" + + project + + "" +} + +// SpanPath returns the path for the span resource. +// +// Deprecated: Use +// fmt.Sprintf("projects/%s/traces/%s/spans/%s", project, trace, span) +// instead. +func SpanPath(project, trace, span string) string { + return "" + + "projects/" + + project + + "/traces/" + + trace + + "/spans/" + + span + + "" +} diff --git a/vendor/cloud.google.com/go/trace/apiv2/trace_client.go b/vendor/cloud.google.com/go/trace/apiv2/trace_client.go new file mode 100644 index 0000000000..27a016218f --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv2/trace_client.go @@ -0,0 +1,153 @@ +// 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 trace + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2" + "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 { + BatchWriteSpans []gax.CallOption + CreateSpan []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("cloudtrace.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: 1000 * time.Millisecond, + Multiplier: 1.2, + }) + }), + }, + } + return &CallOptions{ + BatchWriteSpans: retry[[2]string{"default", "non_idempotent"}], + CreateSpan: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Stackdriver Trace 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 cloudtracepb.TraceServiceClient + + // 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 trace service client. +// +// This file describes an API for collecting and viewing traces and spans +// within a trace. A Trace is a collection of spans corresponding to a single +// operation or set of operations for an application. A span is an individual +// timed event which forms a node of the trace tree. A single trace may +// contain span(s) from multiple services. +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: cloudtracepb.NewTraceServiceClient(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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// BatchWriteSpans sends new spans to new or existing traces. You cannot update +// existing spans. +func (c *Client) BatchWriteSpans(ctx context.Context, req *cloudtracepb.BatchWriteSpansRequest, opts ...gax.CallOption) error { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchWriteSpans[0:len(c.CallOptions.BatchWriteSpans):len(c.CallOptions.BatchWriteSpans)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = c.client.BatchWriteSpans(ctx, req, settings.GRPC...) + return err + }, opts...) + return err +} + +// CreateSpan creates a new span. +func (c *Client) CreateSpan(ctx context.Context, req *cloudtracepb.Span, opts ...gax.CallOption) (*cloudtracepb.Span, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.CreateSpan[0:len(c.CallOptions.CreateSpan):len(c.CallOptions.CreateSpan)], opts...) + var resp *cloudtracepb.Span + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.CreateSpan(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/trace/apiv2/trace_client_example_test.go b/vendor/cloud.google.com/go/trace/apiv2/trace_client_example_test.go new file mode 100644 index 0000000000..d070ccf6f6 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/apiv2/trace_client_example_test.go @@ -0,0 +1,67 @@ +// 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 trace_test + +import ( + "cloud.google.com/go/trace/apiv2" + "golang.org/x/net/context" + cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := trace.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_BatchWriteSpans() { + ctx := context.Background() + c, err := trace.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &cloudtracepb.BatchWriteSpansRequest{ + // TODO: Fill request struct fields. + } + err = c.BatchWriteSpans(ctx, req) + if err != nil { + // TODO: Handle error. + } +} + +func ExampleClient_CreateSpan() { + ctx := context.Background() + c, err := trace.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &cloudtracepb.Span{ + // TODO: Fill request struct fields. + } + resp, err := c.CreateSpan(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/trace/grpc.go b/vendor/cloud.google.com/go/trace/grpc.go new file mode 100644 index 0000000000..c21c053d67 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/grpc.go @@ -0,0 +1,108 @@ +// Copyright 2017 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 trace + +import ( + "encoding/hex" + "fmt" + + "cloud.google.com/go/internal/tracecontext" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +const grpcMetadataKey = "grpc-trace-bin" + +// GRPCClientInterceptor returns a grpc.UnaryClientInterceptor that traces all outgoing requests from a gRPC client. +// The calling context should already have a *trace.Span; a child span will be +// created for the outgoing gRPC call. If the calling context doesn't have a span, +// the call will not be traced. If the client is nil, then the interceptor just +// passes through the request. +// +// The functionality in gRPC that this feature relies on is currently experimental. +func (c *Client) GRPCClientInterceptor() grpc.UnaryClientInterceptor { + if c == nil { + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + return invoker(ctx, method, req, reply, cc, opts...) + } + } + return grpc.UnaryClientInterceptor(c.grpcUnaryInterceptor) +} + +func (c *Client) grpcUnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + // TODO: also intercept streams. + span := FromContext(ctx).NewChild(method) + if span == nil { + span = c.NewSpan(method) + } + defer span.Finish() + + traceContext := make([]byte, tracecontext.Len) + // traceID is a hex-encoded 128-bit value. + // TODO(jbd): Decode trace IDs upon arrival and + // represent trace IDs with 16 bytes internally. + tid, err := hex.DecodeString(span.trace.traceID) + if err != nil { + return invoker(ctx, method, req, reply, cc, opts...) + } + tracecontext.Encode(traceContext, tid, span.span.SpanId, byte(span.trace.globalOptions)) + md, ok := metadata.FromOutgoingContext(ctx) + if !ok { + md = metadata.Pairs(grpcMetadataKey, string(traceContext)) + } else { + md = md.Copy() // metadata is immutable, copy. + md[grpcMetadataKey] = []string{string(traceContext)} + } + ctx = metadata.NewOutgoingContext(ctx, md) + + err = invoker(ctx, method, req, reply, cc, opts...) + if err != nil { + // TODO: standardize gRPC label names? + span.SetLabel("error", err.Error()) + } + return err +} + +// GRPCServerInterceptor returns a grpc.UnaryServerInterceptor that enables the tracing of the incoming +// gRPC calls. Incoming call's context can be used to extract the span on servers that enabled this option: +// +// span := trace.FromContext(ctx) +// +// If the client is nil, then the interceptor just invokes the handler. +// +// The functionality in gRPC that this feature relies on is currently experimental. +func (c *Client) GRPCServerInterceptor() grpc.UnaryServerInterceptor { + if c == nil { + return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + return handler(ctx, req) + } + } + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + md, _ := metadata.FromIncomingContext(ctx) + var traceHeader string + if header, ok := md[grpcMetadataKey]; ok { + traceID, spanID, opts, ok := tracecontext.Decode([]byte(header[0])) + if ok { + // TODO(jbd): Generate a span directly from string(traceID), spanID and opts. + traceHeader = fmt.Sprintf("%x/%d;o=%d", traceID, spanID, opts) + } + } + span := c.SpanFromHeader(info.FullMethod, traceHeader) + defer span.Finish() + ctx = NewContext(ctx, span) + return handler(ctx, req) + } +} diff --git a/vendor/cloud.google.com/go/trace/grpc_test.go b/vendor/cloud.google.com/go/trace/grpc_test.go new file mode 100644 index 0000000000..ca7f53e121 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/grpc_test.go @@ -0,0 +1,180 @@ +// Copyright 2017 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 trace + +import ( + "io/ioutil" + "log" + "net" + "net/http" + "strings" + "testing" + + pb "cloud.google.com/go/trace/testdata/helloworld" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +func TestGRPCInterceptors(t *testing.T) { + t.Skip("hangs forever for go < 1.9") + + tc := newTestClient(&noopTransport{}) + + // default sampling with global=1. + parent := tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1") + testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) { + if in == nil { + t.Fatalf("missing span in the incoming context") + } + if got, want := in.TraceID(), out.TraceID(); got != want { + t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want) + } + if !in.Traced() { + t.Errorf("incoming span is not traced; want traced") + } + }) + + // default sampling with global=0. + parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=0") + testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) { + if in == nil { + t.Fatalf("missing span in the incoming context") + } + if got, want := in.TraceID(), out.TraceID(); got != want { + t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want) + } + if in.Traced() { + t.Errorf("incoming span is traced; want not traced") + } + }) + + // sampling all with global=1. + all, _ := NewLimitedSampler(1.0, 1<<32) + tc.SetSamplingPolicy(all) + parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1") + testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) { + if in == nil { + t.Fatalf("missing span in the incoming context") + } + if got, want := in.TraceID(), out.TraceID(); got != want { + t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want) + } + if !in.Traced() { + t.Errorf("incoming span is not traced; want traced") + } + }) + + // sampling none with global=1. + none, _ := NewLimitedSampler(0, 0) + tc.SetSamplingPolicy(none) + parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1") + testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) { + if in == nil { + t.Fatalf("missing span in the incoming context") + } + if got, want := in.TraceID(), out.TraceID(); got != want { + t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want) + } + if in.Traced() { + t.Errorf("incoming span is traced; want not traced") + } + }) + + // sampling all with no parent span. + tc.SetSamplingPolicy(all) + testGRPCInterceptor(t, tc, nil, func(t *testing.T, out, in *Span) { + if in == nil { + t.Fatalf("missing span in the incoming context") + } + if in.TraceID() == "" { + t.Errorf("incoming call TraceID is empty") + } + if !in.Traced() { + t.Errorf("incoming span is not traced; want traced") + } + }) + + // sampling none with no parent span. + tc.SetSamplingPolicy(none) + testGRPCInterceptor(t, tc, nil, func(t *testing.T, out, in *Span) { + if in == nil { + t.Fatalf("missing span in the incoming context") + } + if in.TraceID() == "" { + t.Errorf("incoming call TraceID is empty") + } + if in.Traced() { + t.Errorf("incoming span is traced; want not traced") + } + }) +} + +func testGRPCInterceptor(t *testing.T, tc *Client, parent *Span, assert func(t *testing.T, out, in *Span)) { + incomingCh := make(chan *Span, 1) + addrCh := make(chan net.Addr, 1) + go func() { + lis, err := net.Listen("tcp", "") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + addrCh <- lis.Addr() + + s := grpc.NewServer(grpc.UnaryInterceptor(tc.GRPCServerInterceptor())) + pb.RegisterGreeterServer(s, &grpcServer{ + fn: func(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { + incomingCh <- FromContext(ctx) + return &pb.HelloReply{}, nil + }, + }) + if err := s.Serve(lis); err != nil { + t.Fatalf("Failed to serve: %v", err) + } + }() + + addr := <-addrCh + conn, err := grpc.Dial(addr.String(), grpc.WithInsecure(), grpc.WithBlock(), grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor())) + if err != nil { + t.Fatalf("Did not connect: %v", err) + } + defer conn.Close() + c := pb.NewGreeterClient(conn) + + outgoingCtx := NewContext(context.Background(), parent) + _, err = c.SayHello(outgoingCtx, &pb.HelloRequest{}) + if err != nil { + log.Fatalf("Could not SayHello: %v", err) + } + + assert(t, parent, <-incomingCh) +} + +type noopTransport struct{} + +func (rt *noopTransport) RoundTrip(req *http.Request) (*http.Response, error) { + resp := &http.Response{ + Status: "200 OK", + StatusCode: 200, + Body: ioutil.NopCloser(strings.NewReader("{}")), + } + return resp, nil +} + +type grpcServer struct { + fn func(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) +} + +func (s *grpcServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { + return s.fn(ctx, in) +} diff --git a/vendor/cloud.google.com/go/trace/http.go b/vendor/cloud.google.com/go/trace/http.go new file mode 100644 index 0000000000..0a88a73f27 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/http.go @@ -0,0 +1,107 @@ +// Copyright 2017 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 go1.7 + +package trace + +import ( + "net/http" +) + +// Transport is an http.RoundTripper that traces the outgoing requests. +// +// Transport is safe for concurrent usage. +type Transport struct { + // Base is the base http.RoundTripper to be used to do the actual request. + // + // Optional. If nil, http.DefaultTransport is used. + Base http.RoundTripper +} + +// RoundTrip creates a trace.Span and inserts it into the outgoing request's headers. +// The created span can follow a parent span, if a parent is presented in +// the request's context. +func (t Transport) RoundTrip(req *http.Request) (*http.Response, error) { + span := FromContext(req.Context()).NewRemoteChild(req) + resp, err := t.base().RoundTrip(req) + + // TODO(jbd): Is it possible to defer the span.Finish? + // In cases where RoundTrip panics, we still can finish the span. + span.Finish(WithResponse(resp)) + return resp, err +} + +// CancelRequest cancels an in-flight request by closing its connection. +func (t Transport) CancelRequest(req *http.Request) { + type canceler interface { + CancelRequest(*http.Request) + } + if cr, ok := t.base().(canceler); ok { + cr.CancelRequest(req) + } +} + +func (t Transport) base() http.RoundTripper { + if t.Base != nil { + return t.Base + } + return http.DefaultTransport +} + +// HTTPHandler returns a http.Handler from the given handler +// that is aware of the incoming request's span. +// The span can be extracted from the incoming request in handler +// functions from incoming request's context: +// +// span := trace.FromContext(r.Context()) +// +// The span will be auto finished by the handler. +func (c *Client) HTTPHandler(h http.Handler) http.Handler { + if c == nil { + return h + } + return &handler{traceClient: c, handler: h} +} + +type handler struct { + traceClient *Client + handler http.Handler +} + +func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + traceID, parentSpanID, options, optionsOk, ok := traceInfoFromHeader(r.Header.Get(httpHeader)) + if !ok { + traceID = nextTraceID() + } + t := &trace{ + traceID: traceID, + client: h.traceClient, + globalOptions: options, + localOptions: options, + } + span := startNewChildWithRequest(r, t, parentSpanID) + span.span.Kind = spanKindServer + span.rootSpan = true + configureSpanFromPolicy(span, h.traceClient.policy, ok) + defer span.Finish() + + r = r.WithContext(NewContext(r.Context(), span)) + if ok && !optionsOk { + // Inject the trace context back to the response with the sampling options. + // TODO(jbd): Remove when there is a better way to report the client's sampling. + w.Header().Set(httpHeader, spanHeader(traceID, parentSpanID, span.trace.localOptions)) + } + h.handler.ServeHTTP(w, r) +} diff --git a/vendor/cloud.google.com/go/trace/http_test.go b/vendor/cloud.google.com/go/trace/http_test.go new file mode 100644 index 0000000000..bf8ebd3c49 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/http_test.go @@ -0,0 +1,151 @@ +// Copyright 2017 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 go1.7 + +package trace + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +type recorderTransport struct { + ch chan *http.Request +} + +func (rt *recorderTransport) RoundTrip(req *http.Request) (*http.Response, error) { + rt.ch <- req + resp := &http.Response{ + Status: "200 OK", + StatusCode: 200, + Body: ioutil.NopCloser(strings.NewReader("{}")), + } + return resp, nil +} + +func TestNewHTTPClient(t *testing.T) { + rt := &recorderTransport{ + ch: make(chan *http.Request, 1), + } + + tc := newTestClient(&noopTransport{}) + client := &http.Client{ + Transport: &Transport{ + Base: rt, + }, + } + req, _ := http.NewRequest("GET", "http://example.com", nil) + + t.Run("NoTrace", func(t *testing.T) { + _, err := client.Do(req) + if err != nil { + t.Error(err) + } + outgoing := <-rt.ch + if got, want := outgoing.Header.Get(httpHeader), ""; want != got { + t.Errorf("got trace header = %q; want none", got) + } + }) + + t.Run("Trace", func(t *testing.T) { + span := tc.NewSpan("/foo") + + req = req.WithContext(NewContext(req.Context(), span)) + _, err := client.Do(req) + if err != nil { + t.Error(err) + } + outgoing := <-rt.ch + + s := tc.SpanFromHeader("/foo", outgoing.Header.Get(httpHeader)) + if got, want := s.TraceID(), span.TraceID(); got != want { + t.Errorf("trace ID = %q; want %q", got, want) + } + }) +} + +func TestHTTPHandlerNoTrace(t *testing.T) { + tc := newTestClient(&noopTransport{}) + client := &http.Client{ + Transport: &Transport{}, + } + handler := tc.HTTPHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + span := FromContext(r.Context()) + if span == nil { + t.Errorf("span is nil; want non-nil span") + } + })) + + ts := httptest.NewServer(handler) + defer ts.Close() + + req, _ := http.NewRequest("GET", ts.URL, nil) + _, err := client.Do(req) + if err != nil { + t.Fatal(err) + } +} + +func TestHTTPHandler_response(t *testing.T) { + tc := newTestClient(&noopTransport{}) + p, _ := NewLimitedSampler(1, 1<<32) // all + tc.SetSamplingPolicy(p) + handler := tc.HTTPHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + ts := httptest.NewServer(handler) + defer ts.Close() + + tests := []struct { + name string + traceHeader string + wantTraceHeader string + }{ + { + name: "no global", + traceHeader: "0123456789ABCDEF0123456789ABCDEF/123", + wantTraceHeader: "0123456789ABCDEF0123456789ABCDEF/123;o=1", + }, + { + name: "global=1", + traceHeader: "0123456789ABCDEF0123456789ABCDEF/123;o=1", + wantTraceHeader: "", + }, + { + name: "global=0", + traceHeader: "0123456789ABCDEF0123456789ABCDEF/123;o=0", + wantTraceHeader: "", + }, + { + name: "no trace context", + traceHeader: "", + wantTraceHeader: "", + }, + } + + for _, tt := range tests { + req, _ := http.NewRequest("GET", ts.URL, nil) + req.Header.Set(httpHeader, tt.traceHeader) + + res, err := http.DefaultClient.Do(req) + if err != nil { + t.Errorf("failed to request: %v", err) + } + if got, want := res.Header.Get(httpHeader), tt.wantTraceHeader; got != want { + t.Errorf("%v: response context header = %q; want %q", tt.name, got, want) + } + } +} diff --git a/vendor/cloud.google.com/go/trace/httpexample_test.go b/vendor/cloud.google.com/go/trace/httpexample_test.go new file mode 100644 index 0000000000..4eaee09e0f --- /dev/null +++ b/vendor/cloud.google.com/go/trace/httpexample_test.go @@ -0,0 +1,57 @@ +// Copyright 2017 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 go1.7 + +package trace_test + +import ( + "log" + "net/http" + + "cloud.google.com/go/trace" +) + +var traceClient *trace.Client + +func ExampleHTTPClient_Do() { + client := http.Client{ + Transport: &trace.Transport{}, + } + span := traceClient.NewSpan("/foo") // traceClient is a *trace.Client + + req, _ := http.NewRequest("GET", "https://metadata/users", nil) + req = req.WithContext(trace.NewContext(req.Context(), span)) + + if _, err := client.Do(req); err != nil { + log.Fatal(err) + } +} + +func ExampleClient_HTTPHandler() { + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := http.Client{ + Transport: &trace.Transport{}, + } + + req, _ := http.NewRequest("GET", "https://metadata/users", nil) + req = req.WithContext(r.Context()) + + // The outgoing request will be traced with r's trace ID. + if _, err := client.Do(req); err != nil { + log.Fatal(err) + } + }) + http.Handle("/foo", traceClient.HTTPHandler(handler)) // traceClient is a *trace.Client +} diff --git a/vendor/cloud.google.com/go/trace/sampling.go b/vendor/cloud.google.com/go/trace/sampling.go new file mode 100644 index 0000000000..c4afb78f40 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/sampling.go @@ -0,0 +1,117 @@ +// 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 trace + +import ( + crand "crypto/rand" + "encoding/binary" + "fmt" + "math/rand" + "sync" + "time" + + "golang.org/x/time/rate" +) + +type SamplingPolicy interface { + // Sample returns a Decision. + // If Trace is false in the returned Decision, then the Decision should be + // the zero value. + Sample(p Parameters) Decision +} + +// Parameters contains the values passed to a SamplingPolicy's Sample method. +type Parameters struct { + HasTraceHeader bool // whether the incoming request has a valid X-Cloud-Trace-Context header. +} + +// Decision is the value returned by a call to a SamplingPolicy's Sample method. +type Decision struct { + Trace bool // Whether to trace the request. + Sample bool // Whether the trace is included in the random sample. + Policy string // Name of the sampling policy. + Weight float64 // Sample weight to be used in statistical calculations. +} + +type sampler struct { + fraction float64 + skipped float64 + *rate.Limiter + *rand.Rand + sync.Mutex +} + +func (s *sampler) Sample(p Parameters) Decision { + s.Lock() + x := s.Float64() + d := s.sample(p, time.Now(), x) + s.Unlock() + return d +} + +// sample contains the a deterministic, time-independent logic of Sample. +func (s *sampler) sample(p Parameters, now time.Time, x float64) (d Decision) { + d.Sample = x < s.fraction + d.Trace = p.HasTraceHeader || d.Sample + if !d.Trace { + // We have no reason to trace this request. + return Decision{} + } + // We test separately that the rate limit is not tiny before calling AllowN, + // because of overflow problems in x/time/rate. + if s.Limit() < 1e-9 || !s.AllowN(now, 1) { + // Rejected by the rate limit. + if d.Sample { + s.skipped++ + } + return Decision{} + } + if d.Sample { + d.Policy, d.Weight = "default", (1.0+s.skipped)/s.fraction + s.skipped = 0.0 + } + return +} + +// NewLimitedSampler returns a sampling policy that randomly samples a given +// fraction of requests. It also enforces a limit on the number of traces per +// second. It tries to trace every request with a trace header, but will not +// exceed the qps limit to do it. +func NewLimitedSampler(fraction, maxqps float64) (SamplingPolicy, error) { + if !(fraction >= 0) { + return nil, fmt.Errorf("invalid fraction %f", fraction) + } + if !(maxqps >= 0) { + return nil, fmt.Errorf("invalid maxqps %f", maxqps) + } + // Set a limit on the number of accumulated "tokens", to limit bursts of + // traced requests. Use one more than a second's worth of tokens, or 100, + // whichever is smaller. + // See https://godoc.org/golang.org/x/time/rate#NewLimiter. + maxTokens := 100 + if maxqps < 99.0 { + maxTokens = 1 + int(maxqps) + } + var seed int64 + if err := binary.Read(crand.Reader, binary.LittleEndian, &seed); err != nil { + seed = time.Now().UnixNano() + } + s := sampler{ + fraction: fraction, + Limiter: rate.NewLimiter(rate.Limit(maxqps), maxTokens), + Rand: rand.New(rand.NewSource(seed)), + } + return &s, nil +} diff --git a/vendor/cloud.google.com/go/trace/testdata/helloworld/helloworld.pb.go b/vendor/cloud.google.com/go/trace/testdata/helloworld/helloworld.pb.go new file mode 100644 index 0000000000..96307c6e14 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/testdata/helloworld/helloworld.pb.go @@ -0,0 +1,161 @@ +// Copyright 2017 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 helloworld is a generated protocol buffer package. + +It is generated from these files: + helloworld.proto + +It has these top-level messages: + HelloRequest + HelloReply +*/ +package helloworld + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// The request message containing the user's name. +type HelloRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *HelloRequest) Reset() { *m = HelloRequest{} } +func (m *HelloRequest) String() string { return proto.CompactTextString(m) } +func (*HelloRequest) ProtoMessage() {} +func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +// The response message containing the greetings +type HelloReply struct { + Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` +} + +func (m *HelloReply) Reset() { *m = HelloReply{} } +func (m *HelloReply) String() string { return proto.CompactTextString(m) } +func (*HelloReply) ProtoMessage() {} +func (*HelloReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func init() { + proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest") + proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Greeter service + +type GreeterClient interface { + // Sends a greeting + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) +} + +type greeterClient struct { + cc *grpc.ClientConn +} + +func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { + return &greeterClient{cc} +} + +func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { + out := new(HelloReply) + err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Greeter service + +type GreeterServer interface { + // Sends a greeting + SayHello(context.Context, *HelloRequest) (*HelloReply, error) +} + +func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { + s.RegisterService(&_Greeter_serviceDesc, srv) +} + +func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/helloworld.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Greeter_serviceDesc = grpc.ServiceDesc{ + ServiceName: "helloworld.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _Greeter_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "helloworld.proto", +} + +func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 174 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9, + 0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88, + 0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42, + 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92, + 0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71, + 0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a, + 0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64, + 0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x00, 0xad, 0x50, 0x62, 0x70, 0x32, 0xe0, 0x92, 0xce, 0xcc, 0xd7, + 0x4b, 0x2f, 0x2a, 0x48, 0xd6, 0x4b, 0xad, 0x48, 0xcc, 0x2d, 0xc8, 0x49, 0x2d, 0x46, 0x52, 0xeb, + 0xc4, 0x0f, 0x56, 0x1c, 0x0e, 0x62, 0x07, 0x80, 0xbc, 0x14, 0xc0, 0x98, 0xc4, 0x06, 0xf6, 0x9b, + 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00, +} diff --git a/vendor/cloud.google.com/go/trace/testdata/helloworld/helloworld.proto b/vendor/cloud.google.com/go/trace/testdata/helloworld/helloworld.proto new file mode 100644 index 0000000000..bffe980e30 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/testdata/helloworld/helloworld.proto @@ -0,0 +1,37 @@ +// Copyright 2017 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. + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/vendor/cloud.google.com/go/trace/trace.go b/vendor/cloud.google.com/go/trace/trace.go new file mode 100644 index 0000000000..75bc012ba1 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/trace.go @@ -0,0 +1,845 @@ +// 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. + +// This package is OBSOLETE. See https://godoc.org/go.opencensus.io/trace; and use +// OpenCensus Stackdriver exporter, https://godoc.org/contrib.go.opencensus.io/exporter/stackdriver. +// +// Package trace is a Google Stackdriver Trace library. +// +// This package is still experimental and subject to change. +// See https://cloud.google.com/trace/api/#data_model for a discussion of traces +// and spans. +// +// To initialize a client that connects to the Stackdriver Trace server, use the +// NewClient function. Generally you will want to do this on program +// initialization. +// +// import "cloud.google.com/go/trace" +// ... +// traceClient, err = trace.NewClient(ctx, projectID) +// +// Calling SpanFromRequest will create a new trace span for an incoming HTTP +// request. If the request contains a trace context header, it is used to +// determine the trace ID. Otherwise, a new trace ID is created. +// +// func handler(w http.ResponseWriter, r *http.Request) { +// span := traceClient.SpanFromRequest(r) +// defer span.Finish() +// ... +// } +// +// SpanFromRequest and NewSpan returns nil if the *Client is nil, so you can disable +// tracing by not initializing your *Client variable. All of the exported +// functions on *Span do nothing when the *Span is nil. +// +// If you need to start traces that don't correspond to an incoming HTTP request, +// you can use NewSpan to create a root-level span. +// +// span := traceClient.NewSpan("span name") +// defer span.Finish() +// +// Although a trace span object is created for every request, only a subset of +// traces are uploaded to the server, for efficiency. By default, the requests +// that are traced are those with the tracing bit set in the options field of +// the trace context header. Ideally, you should override this behaviour by +// calling SetSamplingPolicy. NewLimitedSampler returns an implementation of +// SamplingPolicy which traces requests that have the tracing bit set, and also +// randomly traces a specified fraction of requests. Additionally, it sets a +// limit on the number of requests traced per second. The following example +// traces one in every thousand requests, up to a limit of 5 per second. +// +// p, err := trace.NewLimitedSampler(0.001, 5) +// traceClient.SetSamplingPolicy(p) +// +// You can create a new span as a child of an existing span with NewChild. +// +// childSpan := span.NewChild(name) +// ... +// childSpan.Finish() +// +// When sending an HTTP request to another server, NewRemoteChild will create +// a span to represent the time the current program waits for the request to +// complete, and attach a header to the outgoing request so that the trace will +// be propagated to the destination server. +// +// childSpan := span.NewRemoteChild(&httpRequest) +// ... +// childSpan.Finish() +// +// Alternatively, if you have access to the X-Cloud-Trace-Context header value +// but not the underlying HTTP request (this can happen if you are using a +// different transport or messaging protocol, such as gRPC), you can use +// SpanFromHeader instead of SpanFromRequest. In that case, you will need to +// specify the span name explicility, since it cannot be constructed from the +// HTTP request's URL and method. +// +// func handler(r *somepkg.Request) { +// span := traceClient.SpanFromHeader("span name", r.TraceContext()) +// defer span.Finish() +// ... +// } +// +// Spans can contain a map from keys to values that have useful information +// about the span. The elements of this map are called labels. Some labels, +// whose keys all begin with the string "trace.cloud.google.com/", are set +// automatically in the following ways: +// +// - SpanFromRequest sets some labels to data about the incoming request. +// +// - NewRemoteChild sets some labels to data about the outgoing request. +// +// - Finish sets a label to a stack trace, if the stack trace option is enabled +// in the incoming trace header. +// +// - The WithResponse option sets some labels to data about a response. +// You can also set labels using SetLabel. If a label is given a value +// automatically and by SetLabel, the automatically-set value is used. +// +// span.SetLabel(key, value) +// +// The WithResponse option can be used when Finish is called. +// +// childSpan := span.NewRemoteChild(outgoingReq) +// resp, err := http.DefaultClient.Do(outgoingReq) +// ... +// childSpan.Finish(trace.WithResponse(resp)) +// +// When a span created by SpanFromRequest or SpanFromHeader is finished, the +// finished spans in the corresponding trace -- the span itself and its +// descendants -- are uploaded to the Stackdriver Trace server using the +// *Client that created the span. Finish returns immediately, and uploading +// occurs asynchronously. You can use the FinishWait function instead to wait +// until uploading has finished. +// +// err := span.FinishWait() +// +// Using contexts to pass *trace.Span objects through your program will often +// be a better approach than passing them around explicitly. This allows trace +// spans, and other request-scoped or part-of-request-scoped values, to be +// easily passed through API boundaries. Various Google Cloud libraries will +// retrieve trace spans from contexts and automatically create child spans for +// API requests. +// See https://blog.golang.org/context for more discussion of contexts. +// A derived context containing a trace span can be created using NewContext. +// +// span := traceClient.SpanFromRequest(r) +// ctx = trace.NewContext(ctx, span) +// +// The span can be retrieved from a context elsewhere in the program using +// FromContext. +// +// func foo(ctx context.Context) { +// span := trace.FromContext(ctx).NewChild("in foo") +// defer span.Finish() +// ... +// } +// +package trace // import "cloud.google.com/go/trace" + +import ( + "crypto/rand" + "encoding/binary" + "encoding/json" + "fmt" + "log" + "net/http" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "golang.org/x/net/context" + api "google.golang.org/api/cloudtrace/v1" + "google.golang.org/api/gensupport" + "google.golang.org/api/option" + "google.golang.org/api/support/bundler" + htransport "google.golang.org/api/transport/http" +) + +const ( + httpHeader = `X-Cloud-Trace-Context` + userAgent = `gcloud-golang-trace/20160501` + cloudPlatformScope = `https://www.googleapis.com/auth/cloud-platform` + spanKindClient = `RPC_CLIENT` + spanKindServer = `RPC_SERVER` + spanKindUnspecified = `SPAN_KIND_UNSPECIFIED` + maxStackFrames = 20 + labelAgent = `trace.cloud.google.com/agent` +) + +// Stackdriver Trace API predefined labels. +const ( + LabelComponent = `trace.cloud.google.com/component` + LabelErrorMessage = `trace.cloud.google.com/error/message` + LabelErrorName = `trace.cloud.google.com/error/name` + LabelHTTPClientCity = `trace.cloud.google.com/http/client_city` + LabelHTTPClientCountry = `trace.cloud.google.com/http/client_country` + LabelHTTPClientProtocol = `trace.cloud.google.com/http/client_protocol` + LabelHTTPClientRegion = `trace.cloud.google.com/http/client_region` + LabelHTTPHost = `trace.cloud.google.com/http/host` + LabelHTTPMethod = `trace.cloud.google.com/http/method` + LabelHTTPRedirectedURL = `trace.cloud.google.com/http/redirected_url` + LabelHTTPRequestSize = `trace.cloud.google.com/http/request/size` + LabelHTTPResponseSize = `trace.cloud.google.com/http/response/size` + LabelHTTPStatusCode = `trace.cloud.google.com/http/status_code` + LabelHTTPURL = `trace.cloud.google.com/http/url` + LabelHTTPUserAgent = `trace.cloud.google.com/http/user_agent` + LabelPID = `trace.cloud.google.com/pid` + LabelSamplingPolicy = `trace.cloud.google.com/sampling_policy` + LabelSamplingWeight = `trace.cloud.google.com/sampling_weight` + LabelStackTrace = `trace.cloud.google.com/stacktrace` + LabelTID = `trace.cloud.google.com/tid` +) + +const ( + // ScopeTraceAppend grants permissions to write trace data for a project. + ScopeTraceAppend = "https://www.googleapis.com/auth/trace.append" + + // ScopeCloudPlatform grants permissions to view and manage your data + // across Google Cloud Platform services. + ScopeCloudPlatform = "https://www.googleapis.com/auth/cloud-platform" +) + +type contextKey struct{} + +type stackLabelValue struct { + Frames []stackFrame `json:"stack_frame"` +} + +type stackFrame struct { + Class string `json:"class_name,omitempty"` + Method string `json:"method_name"` + Filename string `json:"file_name"` + Line int64 `json:"line_number"` +} + +var ( + spanIDCounter uint64 + spanIDIncrement uint64 +) + +func init() { + // Set spanIDCounter and spanIDIncrement to random values. nextSpanID will + // return an arithmetic progression using these values, skipping zero. We set + // the LSB of spanIDIncrement to 1, so that the cycle length is 2^64. + binary.Read(rand.Reader, binary.LittleEndian, &spanIDCounter) + binary.Read(rand.Reader, binary.LittleEndian, &spanIDIncrement) + spanIDIncrement |= 1 + // Attach hook for autogenerated Google API calls. This will automatically + // create trace spans for API calls if there is a trace in the context. + gensupport.RegisterHook(requestHook) +} + +func requestHook(ctx context.Context, req *http.Request) func(resp *http.Response) { + span := FromContext(ctx) + if span == nil || req == nil { + return nil + } + span = span.NewRemoteChild(req) + return func(resp *http.Response) { + if resp != nil { + span.Finish(WithResponse(resp)) + } else { + span.Finish() + } + } +} + +// nextSpanID returns a new span ID. It will never return zero. +func nextSpanID() uint64 { + var id uint64 + for id == 0 { + id = atomic.AddUint64(&spanIDCounter, spanIDIncrement) + } + return id +} + +// nextTraceID returns a new trace ID. +func nextTraceID() string { + id1 := nextSpanID() + id2 := nextSpanID() + return fmt.Sprintf("%016x%016x", id1, id2) +} + +// Client is a client for uploading traces to the Google Stackdriver Trace service. +// A nil Client will no-op for all of its methods. +type Client struct { + service *api.Service + projectID string + policy SamplingPolicy + bundler *bundler.Bundler +} + +// NewClient creates a new Google Stackdriver Trace client. +func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) { + o := []option.ClientOption{ + option.WithScopes(cloudPlatformScope), + option.WithUserAgent(userAgent), + } + o = append(o, opts...) + hc, basePath, err := htransport.NewClient(ctx, o...) + if err != nil { + return nil, fmt.Errorf("creating HTTP client for Google Stackdriver Trace API: %v", err) + } + apiService, err := api.New(hc) + if err != nil { + return nil, fmt.Errorf("creating Google Stackdriver Trace API client: %v", err) + } + if basePath != "" { + // An option set a basepath, so override api.New's default. + apiService.BasePath = basePath + } + c := &Client{ + service: apiService, + projectID: projectID, + } + bundler := bundler.NewBundler((*api.Trace)(nil), func(bundle interface{}) { + traces := bundle.([]*api.Trace) + err := c.upload(traces) + if err != nil { + log.Printf("failed to upload %d traces to the Cloud Trace server: %v", len(traces), err) + } + }) + bundler.DelayThreshold = 2 * time.Second + bundler.BundleCountThreshold = 100 + // We're not measuring bytes here, we're counting traces and spans as one "byte" each. + bundler.BundleByteThreshold = 1000 + bundler.BundleByteLimit = 1000 + bundler.BufferedByteLimit = 10000 + c.bundler = bundler + return c, nil +} + +// SetSamplingPolicy sets the SamplingPolicy that determines how often traces +// are initiated by this client. +func (c *Client) SetSamplingPolicy(p SamplingPolicy) { + if c != nil { + c.policy = p + } +} + +// SpanFromHeader returns a new trace span based on a provided request header +// value or nil iff the client is nil. +// +// The trace information and identifiers will be read from the header value. +// Otherwise, a new trace ID is made and the parent span ID is zero. +// For the exact format of the header value, see +// https://cloud.google.com/trace/docs/support#how_do_i_force_a_request_to_be_traced +// +// The name of the new span is provided as an argument. +// +// If a non-nil sampling policy has been set in the client, it can override +// the options set in the header and choose whether to trace the request. +// +// If the header doesn't have existing tracing information, then a *Span is +// returned anyway, but it will not be uploaded to the server, just as when +// calling SpanFromRequest on an untraced request. +// +// Most users using HTTP should use SpanFromRequest, rather than +// SpanFromHeader, since it provides additional functionality for HTTP +// requests. In particular, it will set various pieces of request information +// as labels on the *Span, which is not available from the header alone. +func (c *Client) SpanFromHeader(name string, header string) *Span { + if c == nil { + return nil + } + traceID, parentSpanID, options, _, ok := traceInfoFromHeader(header) + if !ok { + traceID = nextTraceID() + } + t := &trace{ + traceID: traceID, + client: c, + globalOptions: options, + localOptions: options, + } + span := startNewChild(name, t, parentSpanID) + span.span.Kind = spanKindServer + span.rootSpan = true + configureSpanFromPolicy(span, c.policy, ok) + return span +} + +// SpanFromRequest returns a new trace span for an HTTP request or nil +// iff the client is nil. +// +// If the incoming HTTP request contains a trace context header, the trace ID, +// parent span ID, and tracing options will be read from that header. +// Otherwise, a new trace ID is made and the parent span ID is zero. +// +// If a non-nil sampling policy has been set in the client, it can override the +// options set in the header and choose whether to trace the request. +// +// If the request is not being traced, then a *Span is returned anyway, but it +// will not be uploaded to the server -- it is only useful for propagating +// trace context to child requests and for getting the TraceID. All its +// methods can still be called -- the Finish, FinishWait, and SetLabel methods +// do nothing. NewChild does nothing, and returns the same *Span. TraceID +// works as usual. +func (c *Client) SpanFromRequest(r *http.Request) *Span { + if c == nil { + return nil + } + traceID, parentSpanID, options, _, ok := traceInfoFromHeader(r.Header.Get(httpHeader)) + if !ok { + traceID = nextTraceID() + } + t := &trace{ + traceID: traceID, + client: c, + globalOptions: options, + localOptions: options, + } + span := startNewChildWithRequest(r, t, parentSpanID) + span.span.Kind = spanKindServer + span.rootSpan = true + configureSpanFromPolicy(span, c.policy, ok) + return span +} + +// NewSpan returns a new trace span with the given name or nil iff the +// client is nil. +// +// A new trace and span ID is generated to trace the span. +// Returned span need to be finished by calling Finish or FinishWait. +func (c *Client) NewSpan(name string) *Span { + if c == nil { + return nil + } + t := &trace{ + traceID: nextTraceID(), + client: c, + localOptions: optionTrace, + globalOptions: optionTrace, + } + span := startNewChild(name, t, 0) + span.span.Kind = spanKindUnspecified + span.rootSpan = true + configureSpanFromPolicy(span, c.policy, false) + return span +} + +func configureSpanFromPolicy(s *Span, p SamplingPolicy, ok bool) { + if p == nil { + return + } + d := p.Sample(Parameters{HasTraceHeader: ok}) + if d.Trace { + // Turn on tracing locally, and in child requests. + s.trace.localOptions |= optionTrace + s.trace.globalOptions |= optionTrace + } else { + // Turn off tracing locally. + s.trace.localOptions = 0 + return + } + if d.Sample { + // This trace is in the random sample, so set the labels. + s.SetLabel(LabelSamplingPolicy, d.Policy) + s.SetLabel(LabelSamplingWeight, fmt.Sprint(d.Weight)) + } +} + +// NewContext returns a derived context containing the span. +func NewContext(ctx context.Context, s *Span) context.Context { + if s == nil { + return ctx + } + return context.WithValue(ctx, contextKey{}, s) +} + +// FromContext returns the span contained in the context, or nil. +func FromContext(ctx context.Context) *Span { + s, _ := ctx.Value(contextKey{}).(*Span) + return s +} + +func traceInfoFromHeader(h string) (traceID string, spanID uint64, options optionFlags, optionsOk bool, ok bool) { + // See https://cloud.google.com/trace/docs/faq for the header format. + // Return if the header is empty or missing, or if the header is unreasonably + // large, to avoid making unnecessary copies of a large string. + if h == "" || len(h) > 200 { + return "", 0, 0, false, false + + } + + // Parse the trace id field. + slash := strings.Index(h, `/`) + if slash == -1 { + return "", 0, 0, false, false + + } + traceID, h = h[:slash], h[slash+1:] + + // Parse the span id field. + spanstr := h + semicolon := strings.Index(h, `;`) + if semicolon != -1 { + spanstr, h = h[:semicolon], h[semicolon+1:] + } + spanID, err := strconv.ParseUint(spanstr, 10, 64) + if err != nil { + return "", 0, 0, false, false + + } + + // Parse the options field, options field is optional. + if !strings.HasPrefix(h, "o=") { + return traceID, spanID, 0, false, true + + } + o, err := strconv.ParseUint(h[2:], 10, 64) + if err != nil { + return "", 0, 0, false, false + + } + options = optionFlags(o) + return traceID, spanID, options, true, true +} + +type optionFlags uint32 + +const ( + optionTrace optionFlags = 1 << iota + optionStack +) + +type trace struct { + mu sync.Mutex + client *Client + traceID string + globalOptions optionFlags // options that will be passed to any child requests + localOptions optionFlags // options applied in this server + spans []*Span // finished spans for this trace. +} + +// finish appends s to t.spans. If s is the root span, uploads the trace to the +// server. +func (t *trace) finish(s *Span, wait bool, opts ...FinishOption) error { + for _, o := range opts { + o.modifySpan(s) + } + s.end = time.Now() + t.mu.Lock() + t.spans = append(t.spans, s) + spans := t.spans + t.mu.Unlock() + if s.rootSpan { + if wait { + return t.client.upload([]*api.Trace{t.constructTrace(spans)}) + } + go func() { + tr := t.constructTrace(spans) + err := t.client.bundler.Add(tr, 1+len(spans)) + if err == bundler.ErrOversizedItem { + err = t.client.upload([]*api.Trace{tr}) + } + if err != nil { + log.Println("error uploading trace:", err) + } + }() + } + return nil +} + +func (t *trace) constructTrace(spans []*Span) *api.Trace { + apiSpans := make([]*api.TraceSpan, len(spans)) + for i, sp := range spans { + sp.span.StartTime = sp.start.In(time.UTC).Format(time.RFC3339Nano) + sp.span.EndTime = sp.end.In(time.UTC).Format(time.RFC3339Nano) + if t.localOptions&optionStack != 0 { + sp.setStackLabel() + } + if sp.host != "" { + sp.SetLabel(LabelHTTPHost, sp.host) + } + if sp.url != "" { + sp.SetLabel(LabelHTTPURL, sp.url) + } + if sp.method != "" { + sp.SetLabel(LabelHTTPMethod, sp.method) + } + if sp.statusCode != 0 { + sp.SetLabel(LabelHTTPStatusCode, strconv.Itoa(sp.statusCode)) + } + sp.SetLabel(labelAgent, userAgent) + apiSpans[i] = &sp.span + } + + return &api.Trace{ + ProjectId: t.client.projectID, + TraceId: t.traceID, + Spans: apiSpans, + } +} + +func (c *Client) upload(traces []*api.Trace) error { + _, err := c.service.Projects.PatchTraces(c.projectID, &api.Traces{Traces: traces}).Do() + return err +} + +// Span contains information about one span of a trace. +type Span struct { + trace *trace + + spanMu sync.Mutex // guards span.Labels + span api.TraceSpan + + start time.Time + end time.Time + rootSpan bool + stack [maxStackFrames]uintptr + host string + method string + url string + statusCode int +} + +// Traced reports whether the current span is sampled to be traced. +func (s *Span) Traced() bool { + if s == nil { + return false + } + return s.trace.localOptions&optionTrace != 0 +} + +// NewChild creates a new span with the given name as a child of s. +// If s is nil, does nothing and returns nil. +func (s *Span) NewChild(name string) *Span { + if s == nil { + return nil + } + if !s.Traced() { + // TODO(jbd): Document this behavior in godoc here and elsewhere. + return s + } + return startNewChild(name, s.trace, s.span.SpanId) +} + +// NewRemoteChild creates a new span as a child of s. +// +// Some labels in the span are set from the outgoing *http.Request r. +// +// A header is set in r so that the trace context is propagated to the +// destination. The parent span ID in that header is set as follows: +// - If the request is being traced, then the ID of s is used. +// - If the request is not being traced, but there was a trace context header +// in the incoming request for this trace (the request passed to +// SpanFromRequest), the parent span ID in that header is used. +// - Otherwise, the parent span ID is zero. +// The tracing bit in the options is set if tracing is enabled, or if it was +// set in the incoming request. +// +// If s is nil, does nothing and returns nil. +func (s *Span) NewRemoteChild(r *http.Request) *Span { + if s == nil { + return nil + } + if !s.Traced() { + r.Header[httpHeader] = []string{spanHeader(s.trace.traceID, s.span.ParentSpanId, s.trace.globalOptions)} + return s + } + newSpan := startNewChildWithRequest(r, s.trace, s.span.SpanId) + r.Header[httpHeader] = []string{spanHeader(s.trace.traceID, newSpan.span.SpanId, s.trace.globalOptions)} + return newSpan +} + +// Header returns the value of the X-Cloud-Trace-Context header that +// should be used to propagate the span. This is the inverse of +// SpanFromHeader. +// +// Most users should use NewRemoteChild unless they have specific +// propagation needs or want to control the naming of their span. +// Header() does not create a new span. +func (s *Span) Header() string { + if s == nil { + return "" + } + return spanHeader(s.trace.traceID, s.span.SpanId, s.trace.globalOptions) +} + +func startNewChildWithRequest(r *http.Request, trace *trace, parentSpanID uint64) *Span { + name := r.URL.Host + r.URL.Path // drop scheme and query params + newSpan := startNewChild(name, trace, parentSpanID) + if r.Host == "" { + newSpan.host = r.URL.Host + } else { + newSpan.host = r.Host + } + newSpan.method = r.Method + newSpan.url = r.URL.String() + return newSpan +} + +func startNewChild(name string, trace *trace, parentSpanID uint64) *Span { + spanID := nextSpanID() + for spanID == parentSpanID { + spanID = nextSpanID() + } + newSpan := &Span{ + trace: trace, + span: api.TraceSpan{ + Kind: spanKindClient, + Name: name, + ParentSpanId: parentSpanID, + SpanId: spanID, + }, + start: time.Now(), + } + if trace.localOptions&optionStack != 0 { + _ = runtime.Callers(1, newSpan.stack[:]) + } + return newSpan +} + +// TraceID returns the ID of the trace to which s belongs. +func (s *Span) TraceID() string { + if s == nil { + return "" + } + return s.trace.traceID +} + +// SetLabel sets the label for the given key to the given value. +// If the value is empty, the label for that key is deleted. +// If a label is given a value automatically and by SetLabel, the +// automatically-set value is used. +// If s is nil, does nothing. +// +// SetLabel shouldn't be called after Finish or FinishWait. +func (s *Span) SetLabel(key, value string) { + if s == nil { + return + } + if !s.Traced() { + return + } + s.spanMu.Lock() + defer s.spanMu.Unlock() + + if value == "" { + if s.span.Labels != nil { + delete(s.span.Labels, key) + } + return + } + if s.span.Labels == nil { + s.span.Labels = make(map[string]string) + } + s.span.Labels[key] = value +} + +type FinishOption interface { + modifySpan(s *Span) +} + +type withResponse struct { + *http.Response +} + +// WithResponse returns an option that can be passed to Finish that indicates +// that some labels for the span should be set using the given *http.Response. +func WithResponse(resp *http.Response) FinishOption { + return withResponse{resp} +} +func (u withResponse) modifySpan(s *Span) { + if u.Response != nil { + s.statusCode = u.StatusCode + } +} + +// Finish declares that the span has finished. +// +// If s is nil, Finish does nothing and returns nil. +// +// If the option trace.WithResponse(resp) is passed, then some labels are set +// for s using information in the given *http.Response. This is useful when the +// span is for an outgoing http request; s will typically have been created by +// NewRemoteChild in this case. +// +// If s is a root span (one created by SpanFromRequest) then s, and all its +// descendant spans that have finished, are uploaded to the Google Stackdriver +// Trace server asynchronously. +func (s *Span) Finish(opts ...FinishOption) { + if s == nil { + return + } + if !s.Traced() { + return + } + s.trace.finish(s, false, opts...) +} + +// FinishWait is like Finish, but if s is a root span, it waits until uploading +// is finished, then returns an error if one occurred. +func (s *Span) FinishWait(opts ...FinishOption) error { + if s == nil { + return nil + } + if !s.Traced() { + return nil + } + return s.trace.finish(s, true, opts...) +} + +func spanHeader(traceID string, spanID uint64, options optionFlags) string { + // See https://cloud.google.com/trace/docs/faq for the header format. + return fmt.Sprintf("%s/%d;o=%d", traceID, spanID, options) +} + +func (s *Span) setStackLabel() { + var stack stackLabelValue + lastSigPanic, inTraceLibrary := false, true + for _, pc := range s.stack { + if pc == 0 { + break + } + if !lastSigPanic { + pc-- + } + fn := runtime.FuncForPC(pc) + file, line := fn.FileLine(pc) + // Name has one of the following forms: + // path/to/package.Foo + // path/to/package.(Type).Foo + // For the first form, we store the whole name in the Method field of the + // stack frame. For the second form, we set the Method field to "Foo" and + // the Class field to "path/to/package.(Type)". + name := fn.Name() + if inTraceLibrary && !strings.HasPrefix(name, "cloud.google.com/go/trace.") { + inTraceLibrary = false + } + var class string + if i := strings.Index(name, ")."); i != -1 { + class, name = name[:i+1], name[i+2:] + } + frame := stackFrame{ + Class: class, + Method: name, + Filename: file, + Line: int64(line), + } + if inTraceLibrary && len(stack.Frames) == 1 { + stack.Frames[0] = frame + } else { + stack.Frames = append(stack.Frames, frame) + } + lastSigPanic = fn.Name() == "runtime.sigpanic" + } + if label, err := json.Marshal(stack); err == nil { + s.SetLabel(LabelStackTrace, string(label)) + } +} diff --git a/vendor/cloud.google.com/go/trace/trace_test.go b/vendor/cloud.google.com/go/trace/trace_test.go new file mode 100644 index 0000000000..71975203b0 --- /dev/null +++ b/vendor/cloud.google.com/go/trace/trace_test.go @@ -0,0 +1,969 @@ +// 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 trace + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "math/rand" + "net/http" + "regexp" + "strings" + "sync" + "testing" + "time" + + "cloud.google.com/go/datastore" + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/storage" + "golang.org/x/net/context" + api "google.golang.org/api/cloudtrace/v1" + compute "google.golang.org/api/compute/v1" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + dspb "google.golang.org/genproto/googleapis/datastore/v1" + "google.golang.org/grpc" +) + +const testProjectID = "testproject" + +type fakeRoundTripper struct { + reqc chan *http.Request +} + +func newFakeRoundTripper() *fakeRoundTripper { + return &fakeRoundTripper{reqc: make(chan *http.Request)} +} + +func (rt *fakeRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { + rt.reqc <- r + resp := &http.Response{ + Status: "200 OK", + StatusCode: 200, + Body: ioutil.NopCloser(strings.NewReader("{}")), + } + return resp, nil +} + +func newTestClient(rt http.RoundTripper) *Client { + t, err := NewClient(context.Background(), testProjectID, option.WithHTTPClient(&http.Client{Transport: rt})) + if err != nil { + panic(err) + } + return t +} + +type fakeDatastoreServer struct { + dspb.DatastoreServer + fail bool +} + +func (f *fakeDatastoreServer) Lookup(ctx context.Context, req *dspb.LookupRequest) (*dspb.LookupResponse, error) { + if f.fail { + return nil, errors.New("lookup failed") + } + return &dspb.LookupResponse{}, nil +} + +// makeRequests makes some requests. +// span is the root span. rt is the trace client's http client's transport. +// This is used to retrieve the trace uploaded by the client, if any. If +// expectTrace is true, we expect a trace will be uploaded. If synchronous is +// true, the call to Finish is expected not to return before the client has +// uploaded any traces. +func makeRequests(t *testing.T, span *Span, rt *fakeRoundTripper, synchronous bool, expectTrace bool) *http.Request { + ctx := NewContext(context.Background(), span) + tc := newTestClient(&noopTransport{}) + + // An HTTP request. + { + req2, err := http.NewRequest("GET", "http://example.com/bar", nil) + if err != nil { + t.Fatal(err) + } + resp := &http.Response{StatusCode: 200} + s := span.NewRemoteChild(req2) + s.Finish(WithResponse(resp)) + } + + // An autogenerated API call. + { + rt := &fakeRoundTripper{reqc: make(chan *http.Request, 1)} + hc := &http.Client{Transport: rt} + computeClient, err := compute.New(hc) + if err != nil { + t.Fatal(err) + } + _, err = computeClient.Zones.List(testProjectID).Context(ctx).Do() + if err != nil { + t.Fatal(err) + } + } + + // A cloud library call that uses the autogenerated API. + { + rt := &fakeRoundTripper{reqc: make(chan *http.Request, 1)} + hc := &http.Client{Transport: rt} + storageClient, err := storage.NewClient(context.Background(), option.WithHTTPClient(hc)) + if err != nil { + t.Fatal(err) + } + var objAttrsList []*storage.ObjectAttrs + it := storageClient.Bucket("testbucket").Objects(ctx, nil) + for { + objAttrs, err := it.Next() + if err != nil && err != iterator.Done { + t.Fatal(err) + } + if err == iterator.Done { + break + } + objAttrsList = append(objAttrsList, objAttrs) + } + } + + // A cloud library call that uses grpc internally. + for _, fail := range []bool{false, true} { + srv, err := testutil.NewServer() + if err != nil { + t.Fatalf("creating test datastore server: %v", err) + } + dspb.RegisterDatastoreServer(srv.Gsrv, &fakeDatastoreServer{fail: fail}) + srv.Start() + conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure(), grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor())) + if err != nil { + t.Fatalf("connecting to test datastore server: %v", err) + } + datastoreClient, err := datastore.NewClient(ctx, testProjectID, option.WithGRPCConn(conn)) + if err != nil { + t.Fatalf("creating datastore client: %v", err) + } + k := datastore.NameKey("Entity", "stringID", nil) + e := new(datastore.Entity) + datastoreClient.Get(ctx, k, e) + } + + done := make(chan struct{}) + go func() { + if synchronous { + err := span.FinishWait() + if err != nil { + t.Errorf("Unexpected error from span.FinishWait: %v", err) + } + } else { + span.Finish() + } + done <- struct{}{} + }() + if !expectTrace { + <-done + select { + case <-rt.reqc: + t.Errorf("Got a trace, expected none.") + case <-time.After(5 * time.Millisecond): + } + return nil + } else if !synchronous { + <-done + return <-rt.reqc + } else { + select { + case <-done: + t.Errorf("Synchronous Finish didn't wait for trace upload.") + return <-rt.reqc + case <-time.After(5 * time.Millisecond): + r := <-rt.reqc + <-done + return r + } + } +} + +func TestHeader(t *testing.T) { + tests := []struct { + header string + wantTraceID string + wantSpanID uint64 + wantOpts optionFlags + wantOK bool + }{ + { + header: "0123456789ABCDEF0123456789ABCDEF/1;o=1", + wantTraceID: "0123456789ABCDEF0123456789ABCDEF", + wantSpanID: 1, + wantOpts: 1, + wantOK: true, + }, + { + header: "0123456789ABCDEF0123456789ABCDEF/1;o=0", + wantTraceID: "0123456789ABCDEF0123456789ABCDEF", + wantSpanID: 1, + wantOpts: 0, + wantOK: true, + }, + { + header: "0123456789ABCDEF0123456789ABCDEF/1", + wantTraceID: "0123456789ABCDEF0123456789ABCDEF", + wantSpanID: 1, + wantOpts: 0, + wantOK: true, + }, + { + header: "", + wantTraceID: "", + wantSpanID: 0, + wantOpts: 0, + wantOK: false, + }, + } + for _, tt := range tests { + traceID, parentSpanID, opts, _, ok := traceInfoFromHeader(tt.header) + if got, want := traceID, tt.wantTraceID; got != want { + t.Errorf("TraceID(%v) = %q; want %q", tt.header, got, want) + } + if got, want := parentSpanID, tt.wantSpanID; got != want { + t.Errorf("SpanID(%v) = %v; want %v", tt.header, got, want) + } + if got, want := opts, tt.wantOpts; got != want { + t.Errorf("Options(%v) = %v; want %v", tt.header, got, want) + } + if got, want := ok, tt.wantOK; got != want { + t.Errorf("Header exists (%v) = %v; want %v", tt.header, got, want) + } + } +} + +func TestOutgoingReqHeader(t *testing.T) { + all, _ := NewLimitedSampler(1, 1<<16) // trace every request + + tests := []struct { + desc string + traceHeader string + samplingPolicy SamplingPolicy + + wantHeaderRe *regexp.Regexp + }{ + { + desc: "Parent span without sampling options, client samples all", + traceHeader: "0123456789ABCDEF0123456789ABCDEF/1", + samplingPolicy: all, + wantHeaderRe: regexp.MustCompile("0123456789ABCDEF0123456789ABCDEF/\\d+;o=1"), + }, + { + desc: "Parent span without sampling options, without client sampling", + traceHeader: "0123456789ABCDEF0123456789ABCDEF/1", + samplingPolicy: nil, + wantHeaderRe: regexp.MustCompile("0123456789ABCDEF0123456789ABCDEF/\\d+;o=0"), + }, + { + desc: "Parent span with o=1, client samples none", + traceHeader: "0123456789ABCDEF0123456789ABCDEF/1;o=1", + samplingPolicy: nil, + wantHeaderRe: regexp.MustCompile("0123456789ABCDEF0123456789ABCDEF/\\d+;o=1"), + }, + { + desc: "Parent span with o=0, without client sampling", + traceHeader: "0123456789ABCDEF0123456789ABCDEF/1;o=0", + samplingPolicy: nil, + wantHeaderRe: regexp.MustCompile("0123456789ABCDEF0123456789ABCDEF/\\d+;o=0"), + }, + } + + tc := newTestClient(nil) + for _, tt := range tests { + tc.SetSamplingPolicy(tt.samplingPolicy) + span := tc.SpanFromHeader("/foo", tt.traceHeader) + + req, _ := http.NewRequest("GET", "http://localhost", nil) + span.NewRemoteChild(req) + + if got, re := req.Header.Get(httpHeader), tt.wantHeaderRe; !re.MatchString(got) { + t.Errorf("%v (parent=%q): got header %q; want in format %q", tt.desc, tt.traceHeader, got, re) + } + } +} + +func TestTrace(t *testing.T) { + t.Parallel() + testTrace(t, false, true) +} + +func TestTraceWithWait(t *testing.T) { + testTrace(t, true, true) +} + +func TestTraceFromHeader(t *testing.T) { + t.Parallel() + testTrace(t, false, false) +} + +func TestTraceFromHeaderWithWait(t *testing.T) { + testTrace(t, false, true) +} + +func TestNewSpan(t *testing.T) { + t.Skip("flaky") + const traceID = "0123456789ABCDEF0123456789ABCDEF" + + rt := newFakeRoundTripper() + traceClient := newTestClient(rt) + span := traceClient.NewSpan("/foo") + span.trace.traceID = traceID + + uploaded := makeRequests(t, span, rt, true, true) + + if uploaded == nil { + t.Fatalf("No trace uploaded, expected one.") + } + + expected := api.Traces{ + Traces: []*api.Trace{ + { + ProjectId: testProjectID, + Spans: []*api.TraceSpan{ + { + Kind: "RPC_CLIENT", + Labels: map[string]string{ + "trace.cloud.google.com/http/host": "example.com", + "trace.cloud.google.com/http/method": "GET", + "trace.cloud.google.com/http/status_code": "200", + "trace.cloud.google.com/http/url": "http://example.com/bar", + }, + Name: "example.com/bar", + }, + { + Kind: "RPC_CLIENT", + Labels: map[string]string{ + "trace.cloud.google.com/http/host": "www.googleapis.com", + "trace.cloud.google.com/http/method": "GET", + "trace.cloud.google.com/http/status_code": "200", + "trace.cloud.google.com/http/url": "https://www.googleapis.com/compute/v1/projects/testproject/zones", + }, + Name: "www.googleapis.com/compute/v1/projects/testproject/zones", + }, + { + Kind: "RPC_CLIENT", + Labels: map[string]string{ + "trace.cloud.google.com/http/host": "www.googleapis.com", + "trace.cloud.google.com/http/method": "GET", + "trace.cloud.google.com/http/status_code": "200", + "trace.cloud.google.com/http/url": "https://www.googleapis.com/storage/v1/b/testbucket/o", + }, + Name: "www.googleapis.com/storage/v1/b/testbucket/o", + }, + &api.TraceSpan{ + Kind: "RPC_CLIENT", + Labels: nil, + Name: "/google.datastore.v1.Datastore/Lookup", + }, + &api.TraceSpan{ + Kind: "RPC_CLIENT", + Labels: map[string]string{"error": "rpc error: code = Unknown desc = lookup failed"}, + Name: "/google.datastore.v1.Datastore/Lookup", + }, + { + Kind: "SPAN_KIND_UNSPECIFIED", + Labels: map[string]string{}, + Name: "/foo", + }, + }, + TraceId: traceID, + }, + }, + } + + body, err := ioutil.ReadAll(uploaded.Body) + if err != nil { + t.Fatal(err) + } + var patch api.Traces + err = json.Unmarshal(body, &patch) + if err != nil { + t.Fatal(err) + } + + checkTraces(t, patch, expected) + + n := len(patch.Traces[0].Spans) + rootSpan := patch.Traces[0].Spans[n-1] + for i, s := range patch.Traces[0].Spans { + if a, b := s.StartTime, s.EndTime; a > b { + t.Errorf("span %d start time is later than its end time (%q, %q)", i, a, b) + } + if a, b := rootSpan.StartTime, s.StartTime; a > b { + t.Errorf("trace start time is later than span %d start time (%q, %q)", i, a, b) + } + if a, b := s.EndTime, rootSpan.EndTime; a > b { + t.Errorf("span %d end time is later than trace end time (%q, %q)", i, a, b) + } + if i > 1 && i < n-1 { + if a, b := patch.Traces[0].Spans[i-1].EndTime, s.StartTime; a > b { + t.Errorf("span %d end time is later than span %d start time (%q, %q)", i-1, i, a, b) + } + } + } + + if x := rootSpan.ParentSpanId; x != 0 { + t.Errorf("Incorrect ParentSpanId: got %d want %d", x, 0) + } + for i, s := range patch.Traces[0].Spans { + if x, y := rootSpan.SpanId, s.ParentSpanId; i < n-1 && x != y { + t.Errorf("Incorrect ParentSpanId in span %d: got %d want %d", i, y, x) + } + } + for i, s := range patch.Traces[0].Spans { + s.EndTime = "" + labels := &expected.Traces[0].Spans[i].Labels + for key, value := range *labels { + if v, ok := s.Labels[key]; !ok { + t.Errorf("Span %d is missing Label %q:%q", i, key, value) + } else if key == "trace.cloud.google.com/http/url" { + if !strings.HasPrefix(v, value) { + t.Errorf("Span %d Label %q: got value %q want prefix %q", i, key, v, value) + } + } else if v != value { + t.Errorf("Span %d Label %q: got value %q want %q", i, key, v, value) + } + } + for key := range s.Labels { + if _, ok := (*labels)[key]; key != "trace.cloud.google.com/stacktrace" && !ok { + t.Errorf("Span %d: unexpected label %q", i, key) + } + } + *labels = nil + s.Labels = nil + s.ParentSpanId = 0 + if s.SpanId == 0 { + t.Errorf("Incorrect SpanId: got 0 want nonzero") + } + s.SpanId = 0 + s.StartTime = "" + } + if !testutil.Equal(patch, expected) { + got, _ := json.Marshal(patch) + want, _ := json.Marshal(expected) + t.Errorf("PatchTraces request: got %s want %s", got, want) + } +} + +func testTrace(t *testing.T, synchronous bool, fromRequest bool) { + t.Skip("flaky") + const header = `0123456789ABCDEF0123456789ABCDEF/42;o=3` + rt := newFakeRoundTripper() + traceClient := newTestClient(rt) + + span := traceClient.SpanFromHeader("/foo", header) + headerOrReqLabels := map[string]string{} + headerOrReqName := "/foo" + + if fromRequest { + req, err := http.NewRequest("GET", "http://example.com/foo", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("X-Cloud-Trace-Context", header) + span = traceClient.SpanFromRequest(req) + headerOrReqLabels = map[string]string{ + "trace.cloud.google.com/http/host": "example.com", + "trace.cloud.google.com/http/method": "GET", + "trace.cloud.google.com/http/url": "http://example.com/foo", + } + headerOrReqName = "example.com/foo" + } + + uploaded := makeRequests(t, span, rt, synchronous, true) + if uploaded == nil { + t.Fatalf("No trace uploaded, expected one.") + } + + expected := api.Traces{ + Traces: []*api.Trace{ + { + ProjectId: testProjectID, + Spans: []*api.TraceSpan{ + { + Kind: "RPC_CLIENT", + Labels: map[string]string{ + "trace.cloud.google.com/http/host": "example.com", + "trace.cloud.google.com/http/method": "GET", + "trace.cloud.google.com/http/status_code": "200", + "trace.cloud.google.com/http/url": "http://example.com/bar", + }, + Name: "example.com/bar", + }, + { + Kind: "RPC_CLIENT", + Labels: map[string]string{ + "trace.cloud.google.com/http/host": "www.googleapis.com", + "trace.cloud.google.com/http/method": "GET", + "trace.cloud.google.com/http/status_code": "200", + "trace.cloud.google.com/http/url": "https://www.googleapis.com/compute/v1/projects/testproject/zones", + }, + Name: "www.googleapis.com/compute/v1/projects/testproject/zones", + }, + { + Kind: "RPC_CLIENT", + Labels: map[string]string{ + "trace.cloud.google.com/http/host": "www.googleapis.com", + "trace.cloud.google.com/http/method": "GET", + "trace.cloud.google.com/http/status_code": "200", + "trace.cloud.google.com/http/url": "https://www.googleapis.com/storage/v1/b/testbucket/o", + }, + Name: "www.googleapis.com/storage/v1/b/testbucket/o", + }, + &api.TraceSpan{ + Kind: "RPC_CLIENT", + Labels: nil, + Name: "/google.datastore.v1.Datastore/Lookup", + }, + &api.TraceSpan{ + Kind: "RPC_CLIENT", + Labels: map[string]string{"error": "rpc error: code = Unknown desc = lookup failed"}, + Name: "/google.datastore.v1.Datastore/Lookup", + }, + { + Kind: "RPC_SERVER", + Labels: headerOrReqLabels, + Name: headerOrReqName, + }, + }, + TraceId: "0123456789ABCDEF0123456789ABCDEF", + }, + }, + } + + body, err := ioutil.ReadAll(uploaded.Body) + if err != nil { + t.Fatal(err) + } + var patch api.Traces + err = json.Unmarshal(body, &patch) + if err != nil { + t.Fatal(err) + } + + checkTraces(t, patch, expected) + + n := len(patch.Traces[0].Spans) + rootSpan := patch.Traces[0].Spans[n-1] + for i, s := range patch.Traces[0].Spans { + if a, b := s.StartTime, s.EndTime; a > b { + t.Errorf("span %d start time is later than its end time (%q, %q)", i, a, b) + } + if a, b := rootSpan.StartTime, s.StartTime; a > b { + t.Errorf("trace start time is later than span %d start time (%q, %q)", i, a, b) + } + if a, b := s.EndTime, rootSpan.EndTime; a > b { + t.Errorf("span %d end time is later than trace end time (%q, %q)", i, a, b) + } + if i > 1 && i < n-1 { + if a, b := patch.Traces[0].Spans[i-1].EndTime, s.StartTime; a > b { + t.Errorf("span %d end time is later than span %d start time (%q, %q)", i-1, i, a, b) + } + } + } + + if x := rootSpan.ParentSpanId; x != 42 { + t.Errorf("Incorrect ParentSpanId: got %d want %d", x, 42) + } + for i, s := range patch.Traces[0].Spans { + if x, y := rootSpan.SpanId, s.ParentSpanId; i < n-1 && x != y { + t.Errorf("Incorrect ParentSpanId in span %d: got %d want %d", i, y, x) + } + } + for i, s := range patch.Traces[0].Spans { + s.EndTime = "" + labels := &expected.Traces[0].Spans[i].Labels + for key, value := range *labels { + if v, ok := s.Labels[key]; !ok { + t.Errorf("Span %d is missing Label %q:%q", i, key, value) + } else if key == "trace.cloud.google.com/http/url" { + if !strings.HasPrefix(v, value) { + t.Errorf("Span %d Label %q: got value %q want prefix %q", i, key, v, value) + } + } else if v != value { + t.Errorf("Span %d Label %q: got value %q want %q", i, key, v, value) + } + } + for key := range s.Labels { + if _, ok := (*labels)[key]; key != "trace.cloud.google.com/stacktrace" && !ok { + t.Errorf("Span %d: unexpected label %q", i, key) + } + } + *labels = nil + s.Labels = nil + s.ParentSpanId = 0 + if s.SpanId == 0 { + t.Errorf("Incorrect SpanId: got 0 want nonzero") + } + s.SpanId = 0 + s.StartTime = "" + } + if !testutil.Equal(patch, expected) { + got, _ := json.Marshal(patch) + want, _ := json.Marshal(expected) + t.Errorf("PatchTraces request: got %s \n\n want %s", got, want) + } +} + +func TestNoTrace(t *testing.T) { + testNoTrace(t, false, true) +} + +func TestNoTraceWithWait(t *testing.T) { + testNoTrace(t, true, true) +} + +func TestNoTraceFromHeader(t *testing.T) { + testNoTrace(t, false, false) +} + +func TestNoTraceFromHeaderWithWait(t *testing.T) { + testNoTrace(t, true, false) +} + +func testNoTrace(t *testing.T, synchronous bool, fromRequest bool) { + for _, header := range []string{ + `0123456789ABCDEF0123456789ABCDEF/42;o=2`, + `0123456789ABCDEF0123456789ABCDEF/42;o=0`, + `0123456789ABCDEF0123456789ABCDEF/42`, + `0123456789ABCDEF0123456789ABCDEF`, + ``, + } { + rt := newFakeRoundTripper() + traceClient := newTestClient(rt) + var span *Span + if fromRequest { + req, err := http.NewRequest("GET", "http://example.com/foo", nil) + if header != "" { + req.Header.Set("X-Cloud-Trace-Context", header) + } + if err != nil { + t.Fatal(err) + } + span = traceClient.SpanFromRequest(req) + } else { + span = traceClient.SpanFromHeader("/foo", header) + } + uploaded := makeRequests(t, span, rt, synchronous, false) + if uploaded != nil { + t.Errorf("Got a trace, expected none.") + } + } +} + +func TestSample(t *testing.T) { + // A deterministic test of the sampler logic. + type testCase struct { + rate float64 + maxqps float64 + want int + } + const delta = 25 * time.Millisecond + for _, test := range []testCase{ + // qps won't matter, so we will sample half of the 79 calls + {0.50, 100, 40}, + // with 1 qps and a burst of 2, we will sample twice in second #1, once in the partial second #2 + {0.50, 1, 3}, + } { + sp, err := NewLimitedSampler(test.rate, test.maxqps) + if err != nil { + t.Fatal(err) + } + s := sp.(*sampler) + sampled := 0 + tm := time.Now() + for i := 0; i < 80; i++ { + if s.sample(Parameters{}, tm, float64(i%2)).Sample { + sampled++ + } + tm = tm.Add(delta) + } + if sampled != test.want { + t.Errorf("rate=%f, maxqps=%f: got %d samples, want %d", test.rate, test.maxqps, sampled, test.want) + } + } +} + +func TestSampling(t *testing.T) { + t.Parallel() + // This scope tests sampling in a larger context, with real time and randomness. + wg := sync.WaitGroup{} + type testCase struct { + rate float64 + maxqps float64 + expectedRange [2]int + } + for _, test := range []testCase{ + {0, 5, [2]int{0, 0}}, + {5, 0, [2]int{0, 0}}, + {0.50, 100, [2]int{20, 60}}, + {0.50, 1, [2]int{3, 4}}, // Windows, with its less precise clock, sometimes gives 4. + } { + wg.Add(1) + go func(test testCase) { + rt := newFakeRoundTripper() + traceClient := newTestClient(rt) + traceClient.bundler.BundleByteLimit = 1 + p, err := NewLimitedSampler(test.rate, test.maxqps) + if err != nil { + t.Fatalf("NewLimitedSampler: %v", err) + } + traceClient.SetSamplingPolicy(p) + ticker := time.NewTicker(25 * time.Millisecond) + sampled := 0 + for i := 0; i < 79; i++ { + req, err := http.NewRequest("GET", "http://example.com/foo", nil) + if err != nil { + t.Fatal(err) + } + span := traceClient.SpanFromRequest(req) + span.Finish() + select { + case <-rt.reqc: + <-ticker.C + sampled++ + case <-ticker.C: + } + } + ticker.Stop() + if test.expectedRange[0] > sampled || sampled > test.expectedRange[1] { + t.Errorf("rate=%f, maxqps=%f: got %d samples want ∈ %v", test.rate, test.maxqps, sampled, test.expectedRange) + } + wg.Done() + }(test) + } + wg.Wait() +} + +func TestBundling(t *testing.T) { + t.Parallel() + rt := newFakeRoundTripper() + traceClient := newTestClient(rt) + traceClient.bundler.DelayThreshold = time.Second / 2 + traceClient.bundler.BundleCountThreshold = 10 + p, err := NewLimitedSampler(1, 99) // sample every request. + if err != nil { + t.Fatalf("NewLimitedSampler: %v", err) + } + traceClient.SetSamplingPolicy(p) + + for i := 0; i < 35; i++ { + go func() { + req, err := http.NewRequest("GET", "http://example.com/foo", nil) + if err != nil { + t.Fatal(err) + } + span := traceClient.SpanFromRequest(req) + span.Finish() + }() + } + + // Read the first three bundles. + <-rt.reqc + <-rt.reqc + <-rt.reqc + + // Test that the fourth bundle isn't sent early. + select { + case <-rt.reqc: + t.Errorf("bundle sent too early") + case <-time.After(time.Second / 4): + <-rt.reqc + } + + // Test that there aren't extra bundles. + select { + case <-rt.reqc: + t.Errorf("too many bundles sent") + case <-time.After(time.Second): + } +} + +func TestWeights(t *testing.T) { + const ( + expectedNumTraced = 10100 + numTracedEpsilon = 100 + expectedTotalWeight = 50000 + totalWeightEpsilon = 5000 + ) + rng := rand.New(rand.NewSource(1)) + const delta = 2 * time.Millisecond + for _, headerRate := range []float64{0.0, 0.5, 1.0} { + // Simulate 10 seconds of requests arriving at 500qps. + // + // The sampling policy tries to sample 25% of them, but has a qps limit of + // 100, so it will not be able to. The returned weight should be higher + // for some sampled requests to compensate. + // + // headerRate is the fraction of incoming requests that have a trace header + // set. The qps limit should not be exceeded, even if headerRate is high. + sp, err := NewLimitedSampler(0.25, 100) + if err != nil { + t.Fatal(err) + } + s := sp.(*sampler) + tm := time.Now() + totalWeight := 0.0 + numTraced := 0 + seenLargeWeight := false + for i := 0; i < 50000; i++ { + d := s.sample(Parameters{HasTraceHeader: rng.Float64() < headerRate}, tm, rng.Float64()) + if d.Trace { + numTraced++ + } + if d.Sample { + totalWeight += d.Weight + if x := int(d.Weight) / 4; x <= 0 || x >= 100 || d.Weight != float64(x)*4.0 { + t.Errorf("weight: got %f, want a small positive multiple of 4", d.Weight) + } + if d.Weight > 4 { + seenLargeWeight = true + } + } + tm = tm.Add(delta) + } + if !seenLargeWeight { + t.Errorf("headerRate %f: never saw sample weight higher than 4.", headerRate) + } + if numTraced < expectedNumTraced-numTracedEpsilon || expectedNumTraced+numTracedEpsilon < numTraced { + t.Errorf("headerRate %f: got %d traced requests, want ∈ [%d, %d]", headerRate, numTraced, expectedNumTraced-numTracedEpsilon, expectedNumTraced+numTracedEpsilon) + } + if totalWeight < expectedTotalWeight-totalWeightEpsilon || expectedTotalWeight+totalWeightEpsilon < totalWeight { + t.Errorf("headerRate %f: got total weight %f want ∈ [%d, %d]", headerRate, totalWeight, expectedTotalWeight-totalWeightEpsilon, expectedTotalWeight+totalWeightEpsilon) + } + } +} + +type alwaysTrace struct{} + +func (a alwaysTrace) Sample(p Parameters) Decision { + return Decision{Trace: true} +} + +type neverTrace struct{} + +func (a neverTrace) Sample(p Parameters) Decision { + return Decision{Trace: false} +} + +func TestPropagation(t *testing.T) { + rt := newFakeRoundTripper() + traceClient := newTestClient(rt) + for _, header := range []string{ + `0123456789ABCDEF0123456789ABCDEF/42;o=0`, + `0123456789ABCDEF0123456789ABCDEF/42;o=1`, + `0123456789ABCDEF0123456789ABCDEF/42;o=2`, + `0123456789ABCDEF0123456789ABCDEF/42;o=3`, + `0123456789ABCDEF0123456789ABCDEF/0;o=0`, + `0123456789ABCDEF0123456789ABCDEF/0;o=1`, + `0123456789ABCDEF0123456789ABCDEF/0;o=2`, + `0123456789ABCDEF0123456789ABCDEF/0;o=3`, + ``, + } { + for _, policy := range []SamplingPolicy{ + nil, + alwaysTrace{}, + neverTrace{}, + } { + traceClient.SetSamplingPolicy(policy) + req, err := http.NewRequest("GET", "http://example.com/foo", nil) + if err != nil { + t.Fatal(err) + } + if header != "" { + req.Header.Set("X-Cloud-Trace-Context", header) + } + + span := traceClient.SpanFromRequest(req) + + req2, err := http.NewRequest("GET", "http://example.com/bar", nil) + if err != nil { + t.Fatal(err) + } + req3, err := http.NewRequest("GET", "http://example.com/baz", nil) + if err != nil { + t.Fatal(err) + } + span.NewRemoteChild(req2) + span.NewRemoteChild(req3) + + var ( + t1, t2, t3 string + s1, s2, s3 uint64 + o1, o2, o3 uint64 + ) + fmt.Sscanf(header, "%32s/%d;o=%d", &t1, &s1, &o1) + fmt.Sscanf(req2.Header.Get("X-Cloud-Trace-Context"), "%32s/%d;o=%d", &t2, &s2, &o2) + fmt.Sscanf(req3.Header.Get("X-Cloud-Trace-Context"), "%32s/%d;o=%d", &t3, &s3, &o3) + + if header == "" { + if t2 != t3 { + t.Errorf("expected the same trace ID in child requests, got %q %q", t2, t3) + } + } else { + if t2 != t1 || t3 != t1 { + t.Errorf("trace IDs should be passed to child requests") + } + } + trace := policy == alwaysTrace{} || policy == nil && (o1&1) != 0 + if header == "" { + if trace && (s2 == 0 || s3 == 0) { + t.Errorf("got span IDs %d %d in child requests, want nonzero", s2, s3) + } + if trace && s2 == s3 { + t.Errorf("got span IDs %d %d in child requests, should be different", s2, s3) + } + if !trace && (s2 != 0 || s3 != 0) { + t.Errorf("got span IDs %d %d in child requests, want zero", s2, s3) + } + } else { + if trace && (s2 == s1 || s3 == s1 || s2 == s3) { + t.Errorf("parent span IDs in input and outputs should be all different, got %d %d %d", s1, s2, s3) + } + if !trace && (s2 != s1 || s3 != s1) { + t.Errorf("parent span ID in input, %d, should have been equal to parent span IDs in output: %d %d", s1, s2, s3) + } + } + expectTraceOption := policy == alwaysTrace{} || (o1&1) != 0 + if expectTraceOption != ((o2&1) != 0) || expectTraceOption != ((o3&1) != 0) { + t.Errorf("tracing flag in child requests should be %t, got options %d %d", expectTraceOption, o2, o3) + } + } + } +} + +func BenchmarkSpanFromHeader(b *testing.B) { + const header = `0123456789ABCDEF0123456789ABCDEF/42;o=0` + const name = "/foo" + + rt := newFakeRoundTripper() + traceClient := newTestClient(rt) + for n := 0; n < b.N; n++ { + traceClient.SpanFromHeader(name, header) + } +} + +func checkTraces(t *testing.T, patch, expected api.Traces) { + if len(patch.Traces) != len(expected.Traces) || len(patch.Traces[0].Spans) != len(expected.Traces[0].Spans) { + diff := testutil.Diff(patch.Traces, expected.Traces) + t.Logf("diff:\n%s", diff) + got, _ := json.Marshal(patch) + want, _ := json.Marshal(expected) + t.Fatalf("PatchTraces request: got %s want %s", got, want) + } +} diff --git a/vendor/cloud.google.com/go/translate/examples_test.go b/vendor/cloud.google.com/go/translate/examples_test.go new file mode 100644 index 0000000000..2fbf4ad95f --- /dev/null +++ b/vendor/cloud.google.com/go/translate/examples_test.go @@ -0,0 +1,81 @@ +// 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 translate_test + +import ( + "fmt" + + "cloud.google.com/go/translate" + "golang.org/x/net/context" + "golang.org/x/text/language" +) + +func Example_NewClient() { + ctx := context.Background() + client, err := translate.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + // Use the client. + + // Close the client when finished. + if err := client.Close(); err != nil { + // TODO: handle error. + } +} + +func Example_Translate() { + ctx := context.Background() + client, err := translate.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + translations, err := client.Translate(ctx, + []string{"Le singe est sur la branche"}, language.English, + &translate.Options{ + Source: language.French, + Format: translate.Text, + }) + if err != nil { + // TODO: handle error. + } + fmt.Println(translations[0].Text) +} + +func Example_DetectLanguage() { + ctx := context.Background() + client, err := translate.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + ds, err := client.DetectLanguage(ctx, []string{"Today is Monday"}) + if err != nil { + // TODO: handle error. + } + fmt.Println(ds) +} + +func Example_SupportedLanguages() { + ctx := context.Background() + client, err := translate.NewClient(ctx) + if err != nil { + // TODO: handle error. + } + langs, err := client.SupportedLanguages(ctx, language.English) + if err != nil { + // TODO: handle error. + } + fmt.Println(langs) +} diff --git a/vendor/cloud.google.com/go/translate/internal/translate/v2/README b/vendor/cloud.google.com/go/translate/internal/translate/v2/README new file mode 100644 index 0000000000..a4f22c6f99 --- /dev/null +++ b/vendor/cloud.google.com/go/translate/internal/translate/v2/README @@ -0,0 +1,12 @@ +translate-nov2016-api.json is a hand-modified version of translate-api.json. +It correctly reflects the API as of 2016-11-15. + +Differences: + +- Change to base URL +- Addition of OAuth scopes + +To generate: + + + diff --git a/vendor/cloud.google.com/go/translate/internal/translate/v2/regen.sh b/vendor/cloud.google.com/go/translate/internal/translate/v2/regen.sh new file mode 100755 index 0000000000..dc8fd9e104 --- /dev/null +++ b/vendor/cloud.google.com/go/translate/internal/translate/v2/regen.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e + + +(cd $GOPATH/src/google.golang.org/api; make generator) + +$GOPATH/bin/google-api-go-generator \ + -api_json_file translate-nov2016-api.json \ + -api_pkg_base cloud.google.com/go/translate/internal \ + -output translate-nov2016-gen.nolicense + +cat - translate-nov2016-gen.nolicense > translate-nov2016-gen.go <" + s + "" + } + tr = translate(htmlify(test.input), test.target, nil) + if got, want := tr.Text, htmlify(test.output); got != want { + t.Errorf("html: got %q, want %q", got, want) + } + // Using the HTML format behaves the same. + tr = translate(htmlify(test.input), test.target, &Options{Format: HTML}) + if got, want := tr.Text, htmlify(test.output); got != want { + t.Errorf("html: got %q, want %q", got, want) + } + } +} + +// This tests the beta "nmt" model. +func TestTranslateModel(t *testing.T) { + ctx := context.Background() + c := initTest(ctx, t) + defer c.Close() + + trs, err := c.Translate(ctx, []string{"Hello"}, language.French, &Options{Model: "nmt"}) + if err != nil { + t.Fatal(err) + } + if len(trs) != 1 { + t.Fatalf("wanted one Translation, got %d", len(trs)) + } + tr := trs[0] + if got, want := tr.Text, "Bonjour"; got != want { + t.Errorf("text: got %q, want %q", got, want) + } + if got, want := tr.Model, "nmt"; got != want { + t.Errorf("model: got %q, want %q", got, want) + } +} + +func TestTranslateMultipleInputs(t *testing.T) { + ctx := context.Background() + c := initTest(ctx, t) + defer c.Close() + + inputs := []string{ + "When you're a Jet, you're a Jet all the way", + "From your first cigarette to your last dying day", + "When you're a Jet if the spit hits the fan", + "You got brothers around, you're a family man", + } + ts, err := c.Translate(ctx, inputs, language.French, nil) + if err != nil { + t.Fatal(err) + } + if got, want := len(ts), len(inputs); got != want { + t.Fatalf("got %d Translations, wanted %d", got, want) + } +} + +func TestTranslateErrors(t *testing.T) { + ctx := context.Background() + c := initTest(ctx, t) + defer c.Close() + + for _, test := range []struct { + ctx context.Context + target language.Tag + inputs []string + opts *Options + }{ + {ctx, language.English, nil, nil}, + {ctx, language.Und, []string{"input"}, nil}, + {ctx, language.English, []string{}, nil}, + {ctx, language.English, []string{"input"}, &Options{Format: "random"}}, + } { + _, err := c.Translate(test.ctx, test.inputs, test.target, test.opts) + if err == nil { + t.Errorf("%+v: got nil, want error", test) + } + } +} + +func TestDetectLanguage(t *testing.T) { + ctx := context.Background() + c := initTest(ctx, t) + defer c.Close() + ds, err := c.DetectLanguage(ctx, []string{ + "Today is Monday", + "Aujourd'hui est lundi", + }) + if err != nil { + t.Fatal(err) + } + if len(ds) != 2 { + t.Fatalf("got %d detection lists, want 2", len(ds)) + } + checkDetections(t, ds[0], language.English) + checkDetections(t, ds[1], language.French) +} + +func checkDetections(t *testing.T, ds []Detection, want language.Tag) { + for _, d := range ds { + if d.Language == want { + return + } + } + t.Errorf("%v: missing %s", ds, want) +} + +// A small subset of the supported languages. +var supportedLangs = []Language{ + {Name: "Danish", Tag: language.Danish}, + {Name: "English", Tag: language.English}, + {Name: "French", Tag: language.French}, + {Name: "German", Tag: language.German}, + {Name: "Greek", Tag: language.Greek}, + {Name: "Hindi", Tag: language.Hindi}, + {Name: "Hungarian", Tag: language.Hungarian}, + {Name: "Italian", Tag: language.Italian}, + {Name: "Russian", Tag: language.Russian}, + {Name: "Turkish", Tag: language.Turkish}, +} + +func TestSupportedLanguages(t *testing.T) { + ctx := context.Background() + c := initTest(ctx, t) + defer c.Close() + got, err := c.SupportedLanguages(ctx, language.English) + if err != nil { + t.Fatal(err) + } + want := map[language.Tag]Language{} + for _, sl := range supportedLangs { + want[sl.Tag] = sl + } + for _, g := range got { + w, ok := want[g.Tag] + if !ok { + continue + } + if g != w { + t.Errorf("got %+v, want %+v", g, w) + } + delete(want, g.Tag) + } + if len(want) > 0 { + t.Errorf("missing: %+v", want) + } +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1/AnnotateVideo_smoke_test.go b/vendor/cloud.google.com/go/videointelligence/apiv1/AnnotateVideo_smoke_test.go new file mode 100644 index 0000000000..01aa8d432f --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1/AnnotateVideo_smoke_test.go @@ -0,0 +1,69 @@ +// 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 videointelligence + +import ( + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestVideoIntelligenceServiceSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var inputUri string = "gs://demomaker/cat.mp4" + var featuresElement videointelligencepb.Feature = videointelligencepb.Feature_LABEL_DETECTION + var features = []videointelligencepb.Feature{featuresElement} + var request = &videointelligencepb.AnnotateVideoRequest{ + InputUri: inputUri, + Features: features, + } + + if _, err := c.AnnotateVideo(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1/doc.go b/vendor/cloud.google.com/go/videointelligence/apiv1/doc.go new file mode 100644 index 0000000000..cf0c5bff93 --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1/doc.go @@ -0,0 +1,46 @@ +// 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 videointelligence is an auto-generated package for the +// Cloud Video Intelligence API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Cloud Video Intelligence API. +package videointelligence // import "cloud.google.com/go/videointelligence/apiv1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1/mock_test.go b/vendor/cloud.google.com/go/videointelligence/apiv1/mock_test.go new file mode 100644 index 0000000000..b306b9304b --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1/mock_test.go @@ -0,0 +1,192 @@ +// 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 videointelligence + +import ( + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockVideoIntelligenceServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + videointelligencepb.VideoIntelligenceServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockVideoIntelligenceServer) AnnotateVideo(ctx context.Context, req *videointelligencepb.AnnotateVideoRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockVideoIntelligence mockVideoIntelligenceServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + videointelligencepb.RegisterVideoIntelligenceServiceServer(serv, &mockVideoIntelligence) + + 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 TestVideoIntelligenceServiceAnnotateVideo(t *testing.T) { + var expectedResponse *videointelligencepb.AnnotateVideoResponse = &videointelligencepb.AnnotateVideoResponse{} + + mockVideoIntelligence.err = nil + mockVideoIntelligence.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockVideoIntelligence.resps = append(mockVideoIntelligence.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var inputUri string = "gs://demomaker/cat.mp4" + var featuresElement videointelligencepb.Feature = videointelligencepb.Feature_LABEL_DETECTION + var features = []videointelligencepb.Feature{featuresElement} + var request = &videointelligencepb.AnnotateVideoRequest{ + InputUri: inputUri, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AnnotateVideo(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, mockVideoIntelligence.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 TestVideoIntelligenceServiceAnnotateVideoError(t *testing.T) { + errCode := codes.PermissionDenied + mockVideoIntelligence.err = nil + mockVideoIntelligence.resps = append(mockVideoIntelligence.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var inputUri string = "gs://demomaker/cat.mp4" + var featuresElement videointelligencepb.Feature = videointelligencepb.Feature_LABEL_DETECTION + var features = []videointelligencepb.Feature{featuresElement} + var request = &videointelligencepb.AnnotateVideoRequest{ + InputUri: inputUri, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AnnotateVideo(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 +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1/video_intelligence_client.go b/vendor/cloud.google.com/go/videointelligence/apiv1/video_intelligence_client.go new file mode 100644 index 0000000000..47b7029dd0 --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1/video_intelligence_client.go @@ -0,0 +1,227 @@ +// 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 videointelligence + +import ( + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "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 { + AnnotateVideo []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("videointelligence.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: 1000 * time.Millisecond, + Max: 120000 * time.Millisecond, + Multiplier: 2.5, + }) + }), + }, + } + return &CallOptions{ + AnnotateVideo: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Cloud Video Intelligence 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 videointelligencepb.VideoIntelligenceServiceClient + + // 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 video intelligence service client. +// +// Service that implements Google Cloud Video Intelligence API. +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: videointelligencepb.NewVideoIntelligenceServiceClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + 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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// AnnotateVideo performs asynchronous video annotation. Progress and results can be +// retrieved through the google.longrunning.Operations interface. +// Operation.metadata contains AnnotateVideoProgress (progress). +// Operation.response contains AnnotateVideoResponse (results). +func (c *Client) AnnotateVideo(ctx context.Context, req *videointelligencepb.AnnotateVideoRequest, opts ...gax.CallOption) (*AnnotateVideoOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnnotateVideo[0:len(c.CallOptions.AnnotateVideo):len(c.CallOptions.AnnotateVideo)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnnotateVideo(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &AnnotateVideoOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// AnnotateVideoOperation manages a long-running operation from AnnotateVideo. +type AnnotateVideoOperation struct { + lro *longrunning.Operation +} + +// AnnotateVideoOperation returns a new AnnotateVideoOperation from a given name. +// The name must be that of a previously created AnnotateVideoOperation, possibly from a different process. +func (c *Client) AnnotateVideoOperation(name string) *AnnotateVideoOperation { + return &AnnotateVideoOperation{ + 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 *AnnotateVideoOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*videointelligencepb.AnnotateVideoResponse, error) { + var resp videointelligencepb.AnnotateVideoResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *AnnotateVideoOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*videointelligencepb.AnnotateVideoResponse, error) { + var resp videointelligencepb.AnnotateVideoResponse + 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 *AnnotateVideoOperation) Metadata() (*videointelligencepb.AnnotateVideoProgress, error) { + var meta videointelligencepb.AnnotateVideoProgress + 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 *AnnotateVideoOperation) 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 *AnnotateVideoOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1/video_intelligence_client_example_test.go b/vendor/cloud.google.com/go/videointelligence/apiv1/video_intelligence_client_example_test.go new file mode 100644 index 0000000000..25cd76187c --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1/video_intelligence_client_example_test.go @@ -0,0 +1,56 @@ +// 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 videointelligence_test + +import ( + "cloud.google.com/go/videointelligence/apiv1" + "golang.org/x/net/context" + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := videointelligence.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_AnnotateVideo() { + ctx := context.Background() + c, err := videointelligence.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &videointelligencepb.AnnotateVideoRequest{ + // TODO: Fill request struct fields. + } + op, err := c.AnnotateVideo(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta1/doc.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/doc.go new file mode 100644 index 0000000000..f003315b0c --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/doc.go @@ -0,0 +1,46 @@ +// 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 videointelligence is an auto-generated package for the +// Google Cloud Video Intelligence API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Google Cloud Video Intelligence API. +package videointelligence // import "cloud.google.com/go/videointelligence/apiv1beta1" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta1/mock_test.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/mock_test.go new file mode 100644 index 0000000000..bb9f3c2ccd --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/mock_test.go @@ -0,0 +1,192 @@ +// 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 videointelligence + +import ( + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockVideoIntelligenceServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + videointelligencepb.VideoIntelligenceServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockVideoIntelligenceServer) AnnotateVideo(ctx context.Context, req *videointelligencepb.AnnotateVideoRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockVideoIntelligence mockVideoIntelligenceServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + videointelligencepb.RegisterVideoIntelligenceServiceServer(serv, &mockVideoIntelligence) + + 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 TestVideoIntelligenceServiceAnnotateVideo(t *testing.T) { + var expectedResponse *videointelligencepb.AnnotateVideoResponse = &videointelligencepb.AnnotateVideoResponse{} + + mockVideoIntelligence.err = nil + mockVideoIntelligence.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockVideoIntelligence.resps = append(mockVideoIntelligence.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var inputUri string = "gs://demomaker/cat.mp4" + var featuresElement videointelligencepb.Feature = videointelligencepb.Feature_LABEL_DETECTION + var features = []videointelligencepb.Feature{featuresElement} + var request = &videointelligencepb.AnnotateVideoRequest{ + InputUri: inputUri, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AnnotateVideo(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, mockVideoIntelligence.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 TestVideoIntelligenceServiceAnnotateVideoError(t *testing.T) { + errCode := codes.PermissionDenied + mockVideoIntelligence.err = nil + mockVideoIntelligence.resps = append(mockVideoIntelligence.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var inputUri string = "gs://demomaker/cat.mp4" + var featuresElement videointelligencepb.Feature = videointelligencepb.Feature_LABEL_DETECTION + var features = []videointelligencepb.Feature{featuresElement} + var request = &videointelligencepb.AnnotateVideoRequest{ + InputUri: inputUri, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AnnotateVideo(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 +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta1/video_intelligence_client.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/video_intelligence_client.go new file mode 100644 index 0000000000..581ebb4fc9 --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/video_intelligence_client.go @@ -0,0 +1,227 @@ +// 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 videointelligence + +import ( + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1beta1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "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 { + AnnotateVideo []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("videointelligence.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: 1000 * time.Millisecond, + Max: 120000 * time.Millisecond, + Multiplier: 2.5, + }) + }), + }, + } + return &CallOptions{ + AnnotateVideo: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud Video Intelligence 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 videointelligencepb.VideoIntelligenceServiceClient + + // 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 video intelligence service client. +// +// Service that implements Google Cloud Video Intelligence API. +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: videointelligencepb.NewVideoIntelligenceServiceClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + 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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// AnnotateVideo performs asynchronous video annotation. Progress and results can be +// retrieved through the google.longrunning.Operations interface. +// Operation.metadata contains AnnotateVideoProgress (progress). +// Operation.response contains AnnotateVideoResponse (results). +func (c *Client) AnnotateVideo(ctx context.Context, req *videointelligencepb.AnnotateVideoRequest, opts ...gax.CallOption) (*AnnotateVideoOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnnotateVideo[0:len(c.CallOptions.AnnotateVideo):len(c.CallOptions.AnnotateVideo)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnnotateVideo(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &AnnotateVideoOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// AnnotateVideoOperation manages a long-running operation from AnnotateVideo. +type AnnotateVideoOperation struct { + lro *longrunning.Operation +} + +// AnnotateVideoOperation returns a new AnnotateVideoOperation from a given name. +// The name must be that of a previously created AnnotateVideoOperation, possibly from a different process. +func (c *Client) AnnotateVideoOperation(name string) *AnnotateVideoOperation { + return &AnnotateVideoOperation{ + 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 *AnnotateVideoOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*videointelligencepb.AnnotateVideoResponse, error) { + var resp videointelligencepb.AnnotateVideoResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *AnnotateVideoOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*videointelligencepb.AnnotateVideoResponse, error) { + var resp videointelligencepb.AnnotateVideoResponse + 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 *AnnotateVideoOperation) Metadata() (*videointelligencepb.AnnotateVideoProgress, error) { + var meta videointelligencepb.AnnotateVideoProgress + 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 *AnnotateVideoOperation) 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 *AnnotateVideoOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta1/video_intelligence_client_example_test.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/video_intelligence_client_example_test.go new file mode 100644 index 0000000000..1c693ffa5d --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta1/video_intelligence_client_example_test.go @@ -0,0 +1,56 @@ +// 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 videointelligence_test + +import ( + "cloud.google.com/go/videointelligence/apiv1beta1" + "golang.org/x/net/context" + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1beta1" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := videointelligence.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_AnnotateVideo() { + ctx := context.Background() + c, err := videointelligence.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &videointelligencepb.AnnotateVideoRequest{ + // TODO: Fill request struct fields. + } + op, err := c.AnnotateVideo(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta2/doc.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/doc.go new file mode 100644 index 0000000000..654549374a --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/doc.go @@ -0,0 +1,46 @@ +// 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 videointelligence is an auto-generated package for the +// Google Cloud Video Intelligence API. +// +// NOTE: This package is in alpha. It is not stable, and is likely to change. +// +// Google Cloud Video Intelligence API. +package videointelligence // import "cloud.google.com/go/videointelligence/apiv1beta2" + +import ( + "golang.org/x/net/context" + "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", + } +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta2/mock_test.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/mock_test.go new file mode 100644 index 0000000000..a6494a3b90 --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/mock_test.go @@ -0,0 +1,192 @@ +// 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 videointelligence + +import ( + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1beta2" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockVideoIntelligenceServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + videointelligencepb.VideoIntelligenceServiceServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockVideoIntelligenceServer) AnnotateVideo(ctx context.Context, req *videointelligencepb.AnnotateVideoRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockVideoIntelligence mockVideoIntelligenceServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + videointelligencepb.RegisterVideoIntelligenceServiceServer(serv, &mockVideoIntelligence) + + 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 TestVideoIntelligenceServiceAnnotateVideo(t *testing.T) { + var expectedResponse *videointelligencepb.AnnotateVideoResponse = &videointelligencepb.AnnotateVideoResponse{} + + mockVideoIntelligence.err = nil + mockVideoIntelligence.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockVideoIntelligence.resps = append(mockVideoIntelligence.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var inputUri string = "gs://demomaker/cat.mp4" + var featuresElement videointelligencepb.Feature = videointelligencepb.Feature_LABEL_DETECTION + var features = []videointelligencepb.Feature{featuresElement} + var request = &videointelligencepb.AnnotateVideoRequest{ + InputUri: inputUri, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AnnotateVideo(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, mockVideoIntelligence.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 TestVideoIntelligenceServiceAnnotateVideoError(t *testing.T) { + errCode := codes.PermissionDenied + mockVideoIntelligence.err = nil + mockVideoIntelligence.resps = append(mockVideoIntelligence.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var inputUri string = "gs://demomaker/cat.mp4" + var featuresElement videointelligencepb.Feature = videointelligencepb.Feature_LABEL_DETECTION + var features = []videointelligencepb.Feature{featuresElement} + var request = &videointelligencepb.AnnotateVideoRequest{ + InputUri: inputUri, + Features: features, + } + + c, err := NewClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AnnotateVideo(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 +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta2/video_intelligence_client.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/video_intelligence_client.go new file mode 100644 index 0000000000..78b36417ec --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/video_intelligence_client.go @@ -0,0 +1,227 @@ +// 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 videointelligence + +import ( + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1beta2" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "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 { + AnnotateVideo []gax.CallOption +} + +func defaultClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("videointelligence.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: 1000 * time.Millisecond, + Max: 120000 * time.Millisecond, + Multiplier: 2.5, + }) + }), + }, + } + return &CallOptions{ + AnnotateVideo: retry[[2]string{"default", "idempotent"}], + } +} + +// Client is a client for interacting with Google Cloud Video Intelligence 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 videointelligencepb.VideoIntelligenceServiceClient + + // 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 video intelligence service client. +// +// Service that implements Google Cloud Video Intelligence API. +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: videointelligencepb.NewVideoIntelligenceServiceClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + 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", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// AnnotateVideo performs asynchronous video annotation. Progress and results can be +// retrieved through the google.longrunning.Operations interface. +// Operation.metadata contains AnnotateVideoProgress (progress). +// Operation.response contains AnnotateVideoResponse (results). +func (c *Client) AnnotateVideo(ctx context.Context, req *videointelligencepb.AnnotateVideoRequest, opts ...gax.CallOption) (*AnnotateVideoOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AnnotateVideo[0:len(c.CallOptions.AnnotateVideo):len(c.CallOptions.AnnotateVideo)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.client.AnnotateVideo(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &AnnotateVideoOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// AnnotateVideoOperation manages a long-running operation from AnnotateVideo. +type AnnotateVideoOperation struct { + lro *longrunning.Operation +} + +// AnnotateVideoOperation returns a new AnnotateVideoOperation from a given name. +// The name must be that of a previously created AnnotateVideoOperation, possibly from a different process. +func (c *Client) AnnotateVideoOperation(name string) *AnnotateVideoOperation { + return &AnnotateVideoOperation{ + 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 *AnnotateVideoOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*videointelligencepb.AnnotateVideoResponse, error) { + var resp videointelligencepb.AnnotateVideoResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *AnnotateVideoOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*videointelligencepb.AnnotateVideoResponse, error) { + var resp videointelligencepb.AnnotateVideoResponse + 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 *AnnotateVideoOperation) Metadata() (*videointelligencepb.AnnotateVideoProgress, error) { + var meta videointelligencepb.AnnotateVideoProgress + 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 *AnnotateVideoOperation) 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 *AnnotateVideoOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta2/video_intelligence_client_example_test.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/video_intelligence_client_example_test.go new file mode 100644 index 0000000000..6a8024fd00 --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/video_intelligence_client_example_test.go @@ -0,0 +1,56 @@ +// 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 videointelligence_test + +import ( + "cloud.google.com/go/videointelligence/apiv1beta2" + "golang.org/x/net/context" + videointelligencepb "google.golang.org/genproto/googleapis/cloud/videointelligence/v1beta2" +) + +func ExampleNewClient() { + ctx := context.Background() + c, err := videointelligence.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleClient_AnnotateVideo() { + ctx := context.Background() + c, err := videointelligence.NewClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &videointelligencepb.AnnotateVideoRequest{ + // TODO: Fill request struct fields. + } + op, err := c.AnnotateVideo(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/videointelligence/apiv1beta2/whitelist.go b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/whitelist.go new file mode 100644 index 0000000000..d0e057e700 --- /dev/null +++ b/vendor/cloud.google.com/go/videointelligence/apiv1beta2/whitelist.go @@ -0,0 +1,16 @@ +// Copyright 2017, Google Inc. All rights reserved. +// +// 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. + +// THIS API IS CURRENTLY UNDER WHITELIST. +package videointelligence diff --git a/vendor/cloud.google.com/go/vision/apiv1/BatchAnnotateImages_smoke_test.go b/vendor/cloud.google.com/go/vision/apiv1/BatchAnnotateImages_smoke_test.go new file mode 100644 index 0000000000..606c7113a6 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/BatchAnnotateImages_smoke_test.go @@ -0,0 +1,82 @@ +// 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 vision + +import ( + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestImageAnnotatorSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewImageAnnotatorClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var gcsImageUri string = "gs://gapic-toolkit/President_Barack_Obama.jpg" + var source = &visionpb.ImageSource{ + GcsImageUri: gcsImageUri, + } + var image = &visionpb.Image{ + Source: source, + } + var type_ visionpb.Feature_Type = visionpb.Feature_FACE_DETECTION + var featuresElement = &visionpb.Feature{ + Type: type_, + } + var features = []*visionpb.Feature{featuresElement} + var requestsElement = &visionpb.AnnotateImageRequest{ + Image: image, + Features: features, + } + var requests = []*visionpb.AnnotateImageRequest{requestsElement} + var request = &visionpb.BatchAnnotateImagesRequest{ + Requests: requests, + } + + if _, err := c.BatchAnnotateImages(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/README.md b/vendor/cloud.google.com/go/vision/apiv1/README.md new file mode 100644 index 0000000000..bcfa08dd0c --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/README.md @@ -0,0 +1,9 @@ +Auto-generated vision v1 clients +================================= + +This package includes auto-generated clients for the vision v1 API. + +Use the handwritten client (in the parent directory, +cloud.google.com/go/vision) in preference to this. + +This code is EXPERIMENTAL and subject to CHANGE AT ANY TIME. diff --git a/vendor/cloud.google.com/go/vision/apiv1/client.go b/vendor/cloud.google.com/go/vision/apiv1/client.go new file mode 100644 index 0000000000..f8a655cbfa --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/client.go @@ -0,0 +1,151 @@ +// Copyright 2017, 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 vision + +import ( + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/cloud/vision/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// AnnotateImage runs image detection and annotation for a single image. +func (c *ImageAnnotatorClient) AnnotateImage(ctx context.Context, req *pb.AnnotateImageRequest, opts ...gax.CallOption) (*pb.AnnotateImageResponse, error) { + res, err := c.BatchAnnotateImages(ctx, &pb.BatchAnnotateImagesRequest{ + Requests: []*pb.AnnotateImageRequest{req}, + }, opts...) + if err != nil { + return nil, err + } + return res.Responses[0], nil +} + +// Called for a single image and a single feature. +func (c *ImageAnnotatorClient) annotateOne(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, ftype pb.Feature_Type, maxResults int, opts []gax.CallOption) (*pb.AnnotateImageResponse, error) { + res, err := c.AnnotateImage(ctx, &pb.AnnotateImageRequest{ + Image: img, + ImageContext: ictx, + Features: []*pb.Feature{{Type: ftype, MaxResults: int32(maxResults)}}, + }, opts...) + if err != nil { + return nil, err + } + // When there is only one image and one feature, the response's Error field is + // unambiguously about that one detection, so we "promote" it to the error return + // value. + // res.Error is a google.rpc.Status. Convert to a Go error. Use a gRPC + // error because it preserves the code as a separate field. + // TODO(jba): preserve the details field. + if res.Error != nil { + return nil, status.Errorf(codes.Code(res.Error.Code), "%s", res.Error.Message) + } + return res, nil +} + +// DetectFaces performs face detection on the image. +// At most maxResults results are returned. +func (c *ImageAnnotatorClient) DetectFaces(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.FaceAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_FACE_DETECTION, maxResults, opts) + if err != nil { + return nil, err + } + return res.FaceAnnotations, nil +} + +// DetectLandmarks performs landmark detection on the image. +// At most maxResults results are returned. +func (c *ImageAnnotatorClient) DetectLandmarks(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_LANDMARK_DETECTION, maxResults, opts) + if err != nil { + return nil, err + } + return res.LandmarkAnnotations, nil +} + +// DetectLogos performs logo detection on the image. +// At most maxResults results are returned. +func (c *ImageAnnotatorClient) DetectLogos(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_LOGO_DETECTION, maxResults, opts) + if err != nil { + return nil, err + } + return res.LogoAnnotations, nil +} + +// DetectLabels performs label detection on the image. +// At most maxResults results are returned. +func (c *ImageAnnotatorClient) DetectLabels(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_LABEL_DETECTION, maxResults, opts) + if err != nil { + return nil, err + } + return res.LabelAnnotations, nil +} + +// DetectTexts performs text detection on the image. +// At most maxResults results are returned. +func (c *ImageAnnotatorClient) DetectTexts(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_TEXT_DETECTION, maxResults, opts) + if err != nil { + return nil, err + } + return res.TextAnnotations, nil +} + +// DetectDocumentText performs full text (OCR) detection on the image. +func (c *ImageAnnotatorClient) DetectDocumentText(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.TextAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_DOCUMENT_TEXT_DETECTION, 0, opts) + if err != nil { + return nil, err + } + return res.FullTextAnnotation, nil +} + +// DetectSafeSearch performs safe-search detection on the image. +func (c *ImageAnnotatorClient) DetectSafeSearch(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.SafeSearchAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_SAFE_SEARCH_DETECTION, 0, opts) + if err != nil { + return nil, err + } + return res.SafeSearchAnnotation, nil +} + +// DetectImageProperties computes properties of the image. +func (c *ImageAnnotatorClient) DetectImageProperties(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.ImageProperties, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_IMAGE_PROPERTIES, 0, opts) + if err != nil { + return nil, err + } + return res.ImagePropertiesAnnotation, nil +} + +// DetectWeb computes a web annotation on the image. +func (c *ImageAnnotatorClient) DetectWeb(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.WebDetection, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_WEB_DETECTION, 0, opts) + if err != nil { + return nil, err + } + return res.WebDetection, nil +} + +// CropHints computes crop hints for the image. +func (c *ImageAnnotatorClient) CropHints(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.CropHintsAnnotation, error) { + res, err := c.annotateOne(ctx, img, ictx, pb.Feature_CROP_HINTS, 0, opts) + if err != nil { + return nil, err + } + return res.CropHintsAnnotation, nil +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/client_test.go b/vendor/cloud.google.com/go/vision/apiv1/client_test.go new file mode 100644 index 0000000000..c7cda368ad --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/client_test.go @@ -0,0 +1,200 @@ +// Copyright 2017, 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 vision + +import ( + "fmt" + "reflect" + "testing" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/cloud/vision/v1" + "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var batchResponse = &pb.BatchAnnotateImagesResponse{ + Responses: []*pb.AnnotateImageResponse{{ + FaceAnnotations: []*pb.FaceAnnotation{ + {RollAngle: 1}, {RollAngle: 2}}, + LandmarkAnnotations: []*pb.EntityAnnotation{{Mid: "landmark"}}, + LogoAnnotations: []*pb.EntityAnnotation{{Mid: "logo"}}, + LabelAnnotations: []*pb.EntityAnnotation{{Mid: "label"}}, + TextAnnotations: []*pb.EntityAnnotation{{Mid: "text"}}, + FullTextAnnotation: &pb.TextAnnotation{Text: "full"}, + SafeSearchAnnotation: &pb.SafeSearchAnnotation{Spoof: pb.Likelihood_POSSIBLE}, + ImagePropertiesAnnotation: &pb.ImageProperties{DominantColors: &pb.DominantColorsAnnotation{}}, + CropHintsAnnotation: &pb.CropHintsAnnotation{CropHints: []*pb.CropHint{{Confidence: 0.5}}}, + WebDetection: &pb.WebDetection{WebEntities: []*pb.WebDetection_WebEntity{{EntityId: "web"}}}, + }}, +} + +// Verify that all the "shortcut" methods use the underlying +// BatchAnnotateImages RPC correctly. +func TestClientMethods(t *testing.T) { + ctx := context.Background() + c, err := NewImageAnnotatorClient(ctx, clientOpt) + if err != nil { + t.Fatal(err) + } + + mockImageAnnotator.resps = []proto.Message{batchResponse} + img := &pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}} + ictx := &pb.ImageContext{LanguageHints: []string{"en", "fr"}} + req := &pb.AnnotateImageRequest{ + Image: img, + ImageContext: ictx, + Features: []*pb.Feature{ + {Type: pb.Feature_LABEL_DETECTION, MaxResults: 3}, + {Type: pb.Feature_FACE_DETECTION, MaxResults: 4}, + }, + } + + for i, test := range []struct { + call func() (interface{}, error) + wantFeatures []*pb.Feature + wantRes interface{} + }{ + { + func() (interface{}, error) { return c.AnnotateImage(ctx, req) }, + req.Features, batchResponse.Responses[0], + }, + { + func() (interface{}, error) { return c.DetectFaces(ctx, img, ictx, 2) }, + []*pb.Feature{{Type: pb.Feature_FACE_DETECTION, MaxResults: 2}}, + batchResponse.Responses[0].FaceAnnotations, + }, + { + func() (interface{}, error) { return c.DetectLandmarks(ctx, img, ictx, 2) }, + []*pb.Feature{{Type: pb.Feature_LANDMARK_DETECTION, MaxResults: 2}}, + batchResponse.Responses[0].LandmarkAnnotations, + }, + { + func() (interface{}, error) { return c.DetectLogos(ctx, img, ictx, 2) }, + []*pb.Feature{{Type: pb.Feature_LOGO_DETECTION, MaxResults: 2}}, + batchResponse.Responses[0].LogoAnnotations, + }, + { + func() (interface{}, error) { return c.DetectLabels(ctx, img, ictx, 2) }, + []*pb.Feature{{Type: pb.Feature_LABEL_DETECTION, MaxResults: 2}}, + batchResponse.Responses[0].LabelAnnotations, + }, + { + func() (interface{}, error) { return c.DetectTexts(ctx, img, ictx, 2) }, + []*pb.Feature{{Type: pb.Feature_TEXT_DETECTION, MaxResults: 2}}, + batchResponse.Responses[0].TextAnnotations, + }, + { + func() (interface{}, error) { return c.DetectDocumentText(ctx, img, ictx) }, + []*pb.Feature{{Type: pb.Feature_DOCUMENT_TEXT_DETECTION, MaxResults: 0}}, + batchResponse.Responses[0].FullTextAnnotation, + }, + { + func() (interface{}, error) { return c.DetectSafeSearch(ctx, img, ictx) }, + []*pb.Feature{{Type: pb.Feature_SAFE_SEARCH_DETECTION, MaxResults: 0}}, + batchResponse.Responses[0].SafeSearchAnnotation, + }, + { + func() (interface{}, error) { return c.DetectImageProperties(ctx, img, ictx) }, + []*pb.Feature{{Type: pb.Feature_IMAGE_PROPERTIES, MaxResults: 0}}, + batchResponse.Responses[0].ImagePropertiesAnnotation, + }, + { + func() (interface{}, error) { return c.DetectWeb(ctx, img, ictx) }, + []*pb.Feature{{Type: pb.Feature_WEB_DETECTION, MaxResults: 0}}, + batchResponse.Responses[0].WebDetection, + }, + { + func() (interface{}, error) { return c.CropHints(ctx, img, ictx) }, + []*pb.Feature{{Type: pb.Feature_CROP_HINTS, MaxResults: 0}}, + batchResponse.Responses[0].CropHintsAnnotation, + }, + } { + mockImageAnnotator.reqs = nil + res, err := test.call() + if err != nil { + t.Fatal(err) + } + got := mockImageAnnotator.reqs[0] + want := &pb.BatchAnnotateImagesRequest{ + Requests: []*pb.AnnotateImageRequest{{ + Image: img, + ImageContext: ictx, + Features: test.wantFeatures, + }}, + } + if !testEqual(got, want) { + t.Errorf("#%d:\ngot %v\nwant %v", i, got, want) + } + if got, want := res, test.wantRes; !testEqual(got, want) { + t.Errorf("#%d:\ngot %v\nwant %v", i, got, want) + } + } + +} + +func testEqual(a, b interface{}) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + t := reflect.TypeOf(a) + if t != reflect.TypeOf(b) { + return false + } + if am, ok := a.(proto.Message); ok { + return proto.Equal(am, b.(proto.Message)) + } + if t.Kind() != reflect.Slice { + panic(fmt.Sprintf("testEqual can only handle proto.Message and slices, got %s", t)) + } + va := reflect.ValueOf(a) + vb := reflect.ValueOf(b) + if va.Len() != vb.Len() { + return false + } + for i := 0; i < va.Len(); i++ { + if !testEqual(va.Index(i).Interface(), vb.Index(i).Interface()) { + return false + } + } + return true +} + +func TestAnnotateOneError(t *testing.T) { + ctx := context.Background() + c, err := NewImageAnnotatorClient(ctx, clientOpt) + if err != nil { + t.Fatal(err) + } + mockImageAnnotator.resps = []proto.Message{ + &pb.BatchAnnotateImagesResponse{ + Responses: []*pb.AnnotateImageResponse{{ + Error: &status.Status{Code: int32(codes.NotFound), Message: "not found"}, + }}, + }, + } + + _, err = c.annotateOne(ctx, + &pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}}, + nil, pb.Feature_LOGO_DETECTION, 1, nil) + if c := grpc.Code(err); c != codes.NotFound { + t.Errorf("got %v, want NotFound", c) + } +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/doc.go b/vendor/cloud.google.com/go/vision/apiv1/doc.go new file mode 100644 index 0000000000..e94d383e0a --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/doc.go @@ -0,0 +1,49 @@ +// 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 vision is an auto-generated package for the +// Cloud Vision API. + +// +// Integrates Google Vision features, including image labeling, face, logo, +// and +// landmark detection, optical character recognition (OCR), and detection of +// explicit content, into applications. +package vision // import "cloud.google.com/go/vision/apiv1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/cloud-vision", + } +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/examples_test.go b/vendor/cloud.google.com/go/vision/apiv1/examples_test.go new file mode 100644 index 0000000000..280cc83786 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/examples_test.go @@ -0,0 +1,92 @@ +// 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 vision_test + +import ( + "fmt" + "os" + + vision "cloud.google.com/go/vision/apiv1" + "golang.org/x/net/context" + pb "google.golang.org/genproto/googleapis/cloud/vision/v1" +) + +func Example_NewImageFromReader() { + f, err := os.Open("path/to/image.jpg") + if err != nil { + // TODO: handle error. + } + img, err := vision.NewImageFromReader(f) + if err != nil { + // TODO: handle error. + } + fmt.Println(img) +} + +func Example_NewImageFromURI() { + img := vision.NewImageFromURI("gs://my-bucket/my-image.png") + fmt.Println(img) +} + +func ExampleImageAnnotatorClient_AnnotateImage() { + ctx := context.Background() + c, err := vision.NewImageAnnotatorClient(ctx) + if err != nil { + // TODO: Handle error. + } + res, err := c.AnnotateImage(ctx, &pb.AnnotateImageRequest{ + Image: vision.NewImageFromURI("gs://my-bucket/my-image.png"), + Features: []*pb.Feature{ + {Type: pb.Feature_LANDMARK_DETECTION, MaxResults: 5}, + {Type: pb.Feature_LABEL_DETECTION, MaxResults: 3}, + }, + }) + if err != nil { + // TODO: Handle error. + } + // TODO: Use res. + _ = res +} + +func Example_FaceFromLandmarks() { + ctx := context.Background() + c, err := vision.NewImageAnnotatorClient(ctx) + if err != nil { + // TODO: Handle error. + } + resp, err := c.BatchAnnotateImages(ctx, &pb.BatchAnnotateImagesRequest{ + Requests: []*pb.AnnotateImageRequest{ + { + Image: vision.NewImageFromURI("gs://bucket/image.jpg"), + Features: []*pb.Feature{{ + Type: pb.Feature_FACE_DETECTION, + MaxResults: 5, + }}, + }, + }, + }) + if err != nil { + // TODO: Handle error. + } + res := resp.Responses[0] + if res.Error != nil { + // TODO: Handle error. + } + for _, a := range res.FaceAnnotations { + face := vision.FaceFromLandmarks(a.Landmarks) + fmt.Println(face.Nose.Tip) + fmt.Println(face.Eyes.Left.Pupil) + } +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/face.go b/vendor/cloud.google.com/go/vision/apiv1/face.go new file mode 100644 index 0000000000..6758121bad --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/face.go @@ -0,0 +1,153 @@ +// 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 vision + +import ( + "log" + + pb "google.golang.org/genproto/googleapis/cloud/vision/v1" +) + +// FaceLandmarks contains the positions of facial features detected by the service. +type FaceLandmarks struct { + Eyebrows Eyebrows + Eyes Eyes + Ears Ears + Nose Nose + Mouth Mouth + Chin Chin + Forehead *pb.Position +} + +// Eyebrows represents a face's eyebrows. +type Eyebrows struct { + Left, Right Eyebrow +} + +// Eyebrow represents a face's eyebrow. +type Eyebrow struct { + Top, Left, Right *pb.Position +} + +// Eyes represents a face's eyes. +type Eyes struct { + Left, Right Eye +} + +// Eye represents a face's eye. +type Eye struct { + Left, Right, Top, Bottom, Center, Pupil *pb.Position +} + +// Ears represents a face's ears. +type Ears struct { + Left, Right *pb.Position +} + +// Nose represents a face's nose. +type Nose struct { + Left, Right, Top, Bottom, Tip *pb.Position +} + +// Mouth represents a face's mouth. +type Mouth struct { + Left, Center, Right, UpperLip, LowerLip *pb.Position +} + +// Chin represents a face's chin. +type Chin struct { + Left, Center, Right *pb.Position +} + +// FaceFromLandmarks converts the list of face landmarks returned by the service +// to a FaceLandmarks struct. +func FaceFromLandmarks(landmarks []*pb.FaceAnnotation_Landmark) *FaceLandmarks { + face := &FaceLandmarks{} + for _, lm := range landmarks { + switch lm.Type { + case pb.FaceAnnotation_Landmark_LEFT_OF_LEFT_EYEBROW: + face.Eyebrows.Left.Left = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_OF_LEFT_EYEBROW: + face.Eyebrows.Left.Right = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_OF_RIGHT_EYEBROW: + face.Eyebrows.Right.Left = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_OF_RIGHT_EYEBROW: + face.Eyebrows.Right.Right = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EYEBROW_UPPER_MIDPOINT: + face.Eyebrows.Left.Top = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EYEBROW_UPPER_MIDPOINT: + face.Eyebrows.Right.Top = lm.Position + case pb.FaceAnnotation_Landmark_MIDPOINT_BETWEEN_EYES: + face.Nose.Top = lm.Position + case pb.FaceAnnotation_Landmark_NOSE_TIP: + face.Nose.Tip = lm.Position + case pb.FaceAnnotation_Landmark_UPPER_LIP: + face.Mouth.UpperLip = lm.Position + case pb.FaceAnnotation_Landmark_LOWER_LIP: + face.Mouth.LowerLip = lm.Position + case pb.FaceAnnotation_Landmark_MOUTH_LEFT: + face.Mouth.Left = lm.Position + case pb.FaceAnnotation_Landmark_MOUTH_RIGHT: + face.Mouth.Right = lm.Position + case pb.FaceAnnotation_Landmark_MOUTH_CENTER: + face.Mouth.Center = lm.Position + case pb.FaceAnnotation_Landmark_NOSE_BOTTOM_RIGHT: + face.Nose.Right = lm.Position + case pb.FaceAnnotation_Landmark_NOSE_BOTTOM_LEFT: + face.Nose.Left = lm.Position + case pb.FaceAnnotation_Landmark_NOSE_BOTTOM_CENTER: + face.Nose.Bottom = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EYE: + face.Eyes.Left.Center = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EYE: + face.Eyes.Right.Center = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EYE_TOP_BOUNDARY: + face.Eyes.Left.Top = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EYE_RIGHT_CORNER: + face.Eyes.Left.Right = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EYE_BOTTOM_BOUNDARY: + face.Eyes.Left.Bottom = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EYE_LEFT_CORNER: + face.Eyes.Left.Left = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EYE_TOP_BOUNDARY: + face.Eyes.Right.Top = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EYE_RIGHT_CORNER: + face.Eyes.Right.Right = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EYE_BOTTOM_BOUNDARY: + face.Eyes.Right.Bottom = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EYE_LEFT_CORNER: + face.Eyes.Right.Left = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EYE_PUPIL: + face.Eyes.Left.Pupil = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EYE_PUPIL: + face.Eyes.Right.Pupil = lm.Position + case pb.FaceAnnotation_Landmark_LEFT_EAR_TRAGION: + face.Ears.Left = lm.Position + case pb.FaceAnnotation_Landmark_RIGHT_EAR_TRAGION: + face.Ears.Right = lm.Position + case pb.FaceAnnotation_Landmark_FOREHEAD_GLABELLA: + face.Forehead = lm.Position + case pb.FaceAnnotation_Landmark_CHIN_GNATHION: + face.Chin.Center = lm.Position + case pb.FaceAnnotation_Landmark_CHIN_LEFT_GONION: + face.Chin.Left = lm.Position + case pb.FaceAnnotation_Landmark_CHIN_RIGHT_GONION: + face.Chin.Right = lm.Position + default: + log.Printf("vision: ignoring unknown face annotation landmark %s", lm.Type) + } + } + return face +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/face_test.go b/vendor/cloud.google.com/go/vision/apiv1/face_test.go new file mode 100644 index 0000000000..060e0675dd --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/face_test.go @@ -0,0 +1,225 @@ +// 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 vision + +import ( + "testing" + + "cloud.google.com/go/internal/testutil" + + pb "google.golang.org/genproto/googleapis/cloud/vision/v1" +) + +func TestFaceFromLandmarks(t *testing.T) { + landmarks := []*pb.FaceAnnotation_Landmark{ + { + Type: pb.FaceAnnotation_Landmark_LEFT_EYE, + Position: &pb.Position{X: 1192, Y: 575, Z: 0}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EYE, + Position: &pb.Position{X: 1479, Y: 571, Z: -9}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_OF_LEFT_EYEBROW, + Position: &pb.Position{X: 1097, Y: 522, Z: 27}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_OF_LEFT_EYEBROW, + Position: &pb.Position{X: 1266, Y: 521, Z: -61}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_OF_RIGHT_EYEBROW, + Position: &pb.Position{X: 1402, Y: 520, Z: -66}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_OF_RIGHT_EYEBROW, + Position: &pb.Position{X: 1571, Y: 519, Z: 10}, + }, + { + Type: pb.FaceAnnotation_Landmark_MIDPOINT_BETWEEN_EYES, + Position: &pb.Position{X: 1331, Y: 566, Z: -66}, + }, + { + Type: pb.FaceAnnotation_Landmark_NOSE_TIP, + Position: &pb.Position{X: 1329, Y: 743, Z: -137}, + }, + { + Type: pb.FaceAnnotation_Landmark_UPPER_LIP, + Position: &pb.Position{X: 1330, Y: 836, Z: -66}, + }, + { + Type: pb.FaceAnnotation_Landmark_LOWER_LIP, + Position: &pb.Position{X: 1334, Y: 954, Z: -36}, + }, + { + Type: pb.FaceAnnotation_Landmark_MOUTH_LEFT, + Position: &pb.Position{X: 1186, Y: 867, Z: 27}, + }, + { + Type: pb.FaceAnnotation_Landmark_MOUTH_RIGHT, + Position: &pb.Position{X: 1484, Y: 857, Z: 19}, + }, + { + Type: pb.FaceAnnotation_Landmark_MOUTH_CENTER, + Position: &pb.Position{X: 1332, Y: 894, Z: -41}, + }, + { + Type: pb.FaceAnnotation_Landmark_NOSE_BOTTOM_RIGHT, + Position: &pb.Position{X: 1432, Y: 750, Z: -26}, + }, + { + Type: pb.FaceAnnotation_Landmark_NOSE_BOTTOM_LEFT, + Position: &pb.Position{X: 1236, Y: 755, Z: -20}, + }, + { + Type: pb.FaceAnnotation_Landmark_NOSE_BOTTOM_CENTER, + Position: &pb.Position{X: 1332, Y: 783, Z: -70}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_EYE_TOP_BOUNDARY, + Position: &pb.Position{X: 1193, Y: 561, Z: -20}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_EYE_RIGHT_CORNER, + Position: &pb.Position{X: 1252, Y: 581, Z: -1}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_EYE_BOTTOM_BOUNDARY, + Position: &pb.Position{X: 1190, Y: 593, Z: -1}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_EYE_LEFT_CORNER, + Position: &pb.Position{X: 1133, Y: 584, Z: 28}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_EYE_PUPIL, + Position: &pb.Position{X: 1189, Y: 580, Z: -8}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EYE_TOP_BOUNDARY, + Position: &pb.Position{X: 1474, Y: 561, Z: -30}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EYE_RIGHT_CORNER, + Position: &pb.Position{X: 1536, Y: 581, Z: 15}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EYE_BOTTOM_BOUNDARY, + Position: &pb.Position{X: 1481, Y: 590, Z: -11}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EYE_LEFT_CORNER, + Position: &pb.Position{X: 1424, Y: 579, Z: -6}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EYE_PUPIL, + Position: &pb.Position{X: 1478, Y: 580, Z: -18}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_EYEBROW_UPPER_MIDPOINT, + Position: &pb.Position{X: 1181, Y: 482, Z: -40}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EYEBROW_UPPER_MIDPOINT, + Position: &pb.Position{X: 1485, Y: 482, Z: -50}, + }, + { + Type: pb.FaceAnnotation_Landmark_LEFT_EAR_TRAGION, + Position: &pb.Position{X: 1027, Y: 696, Z: 361}, + }, + { + Type: pb.FaceAnnotation_Landmark_RIGHT_EAR_TRAGION, + Position: &pb.Position{X: 1666, Y: 695, Z: 339}, + }, + { + Type: pb.FaceAnnotation_Landmark_FOREHEAD_GLABELLA, + Position: &pb.Position{X: 1332, Y: 514, Z: -75}, + }, + { + Type: pb.FaceAnnotation_Landmark_CHIN_GNATHION, + Position: &pb.Position{X: 1335, Y: 1058, Z: 6}, + }, + { + Type: pb.FaceAnnotation_Landmark_CHIN_LEFT_GONION, + Position: &pb.Position{X: 1055, Y: 882, Z: 257}, + }, + { + Type: pb.FaceAnnotation_Landmark_CHIN_RIGHT_GONION, + Position: &pb.Position{X: 1631, Y: 881, Z: 238}, + }, + } + want := &FaceLandmarks{ + Eyebrows: Eyebrows{ + Left: Eyebrow{ + Top: &pb.Position{X: 1181, Y: 482, Z: -40}, + Left: &pb.Position{X: 1097, Y: 522, Z: 27}, + Right: &pb.Position{X: 1266, Y: 521, Z: -61}, + }, + Right: Eyebrow{ + Top: &pb.Position{X: 1485, Y: 482, Z: -50}, + Left: &pb.Position{X: 1402, Y: 520, Z: -66}, + Right: &pb.Position{X: 1571, Y: 519, Z: 10}, + }, + }, + Eyes: Eyes{ + Left: Eye{ + Left: &pb.Position{X: 1133, Y: 584, Z: 28}, + Right: &pb.Position{X: 1252, Y: 581, Z: -1}, + Top: &pb.Position{X: 1193, Y: 561, Z: -20}, + Bottom: &pb.Position{X: 1190, Y: 593, Z: -1}, + Center: &pb.Position{X: 1192, Y: 575, Z: 0}, + Pupil: &pb.Position{X: 1189, Y: 580, Z: -8}, + }, + Right: Eye{ + Left: &pb.Position{X: 1424, Y: 579, Z: -6}, + Right: &pb.Position{X: 1536, Y: 581, Z: 15}, + Top: &pb.Position{X: 1474, Y: 561, Z: -30}, + Bottom: &pb.Position{X: 1481, Y: 590, Z: -11}, + Center: &pb.Position{X: 1479, Y: 571, Z: -9}, + Pupil: &pb.Position{X: 1478, Y: 580, Z: -18}, + }, + }, + Ears: Ears{ + Left: &pb.Position{X: 1027, Y: 696, Z: 361}, + Right: &pb.Position{X: 1666, Y: 695, Z: 339}, + }, + Nose: Nose{ + Left: &pb.Position{X: 1236, Y: 755, Z: -20}, + Right: &pb.Position{X: 1432, Y: 750, Z: -26}, + Top: &pb.Position{X: 1331, Y: 566, Z: -66}, + Bottom: &pb.Position{X: 1332, Y: 783, Z: -70}, + Tip: &pb.Position{X: 1329, Y: 743, Z: -137}, + }, + Mouth: Mouth{ + Left: &pb.Position{X: 1186, Y: 867, Z: 27}, + Center: &pb.Position{X: 1332, Y: 894, Z: -41}, + Right: &pb.Position{X: 1484, Y: 857, Z: 19}, + UpperLip: &pb.Position{X: 1330, Y: 836, Z: -66}, + LowerLip: &pb.Position{X: 1334, Y: 954, Z: -36}, + }, + Chin: Chin{ + Left: &pb.Position{X: 1055, Y: 882, Z: 257}, + Center: &pb.Position{X: 1335, Y: 1058, Z: 6}, + Right: &pb.Position{X: 1631, Y: 881, Z: 238}, + }, + Forehead: &pb.Position{X: 1332, Y: 514, Z: -75}, + } + + got := FaceFromLandmarks(landmarks) + if diff := testutil.Diff(got, want); diff != "" { + t.Error(diff) + } +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/image.go b/vendor/cloud.google.com/go/vision/apiv1/image.go new file mode 100644 index 0000000000..fd6d9a77b0 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/image.go @@ -0,0 +1,37 @@ +// Copyright 2017, 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 vision + +import ( + "io" + "io/ioutil" + + pb "google.golang.org/genproto/googleapis/cloud/vision/v1" +) + +// NewImageFromReader reads the bytes of an image from r. +func NewImageFromReader(r io.Reader) (*pb.Image, error) { + bytes, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + return &pb.Image{Content: bytes}, nil +} + +// NewImageFromURI returns an image that refers to an object in Google Cloud Storage +// (when the uri is of the form "gs://BUCKET/OBJECT") or at a public URL. +func NewImageFromURI(uri string) *pb.Image { + return &pb.Image{Source: &pb.ImageSource{ImageUri: uri}} +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/image_annotator_client.go b/vendor/cloud.google.com/go/vision/apiv1/image_annotator_client.go new file mode 100644 index 0000000000..901e53f796 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/image_annotator_client.go @@ -0,0 +1,249 @@ +// 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 vision + +import ( + "time" + + "cloud.google.com/go/internal/version" + "cloud.google.com/go/longrunning" + lroauto "cloud.google.com/go/longrunning/autogen" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ImageAnnotatorCallOptions contains the retry settings for each method of ImageAnnotatorClient. +type ImageAnnotatorCallOptions struct { + BatchAnnotateImages []gax.CallOption + AsyncBatchAnnotateFiles []gax.CallOption +} + +func defaultImageAnnotatorClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("vision.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultImageAnnotatorCallOptions() *ImageAnnotatorCallOptions { + 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 &ImageAnnotatorCallOptions{ + BatchAnnotateImages: retry[[2]string{"default", "idempotent"}], + AsyncBatchAnnotateFiles: retry[[2]string{"default", "idempotent"}], + } +} + +// ImageAnnotatorClient is a client for interacting with Cloud Vision API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ImageAnnotatorClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + imageAnnotatorClient visionpb.ImageAnnotatorClient + + // 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 *ImageAnnotatorCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewImageAnnotatorClient creates a new image annotator client. +// +// Service that performs Google Cloud Vision API detection tasks over client +// images, such as face, landmark, logo, label, and text detection. The +// ImageAnnotator service returns detected entities from the images. +func NewImageAnnotatorClient(ctx context.Context, opts ...option.ClientOption) (*ImageAnnotatorClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultImageAnnotatorClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ImageAnnotatorClient{ + conn: conn, + CallOptions: defaultImageAnnotatorCallOptions(), + + imageAnnotatorClient: visionpb.NewImageAnnotatorClient(conn), + } + c.setGoogleClientInfo() + + c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn)) + if err != nil { + // This error "should not happen", since we are just reusing old connection + // and never actually need to dial. + // If this does happen, we could leak conn. However, we cannot close conn: + // If the user invoked the function with option.WithGRPCConn, + // we would close a connection that's still in use. + // TODO(pongad): investigate error conditions. + return nil, err + } + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ImageAnnotatorClient) 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 *ImageAnnotatorClient) 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 *ImageAnnotatorClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// BatchAnnotateImages run image detection and annotation for a batch of images. +func (c *ImageAnnotatorClient) BatchAnnotateImages(ctx context.Context, req *visionpb.BatchAnnotateImagesRequest, opts ...gax.CallOption) (*visionpb.BatchAnnotateImagesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchAnnotateImages[0:len(c.CallOptions.BatchAnnotateImages):len(c.CallOptions.BatchAnnotateImages)], opts...) + var resp *visionpb.BatchAnnotateImagesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.imageAnnotatorClient.BatchAnnotateImages(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} + +// AsyncBatchAnnotateFiles run asynchronous image detection and annotation for a list of generic +// files, such as PDF files, which may contain multiple pages and multiple +// images per page. Progress and results can be retrieved through the +// google.longrunning.Operations interface. +// Operation.metadata contains OperationMetadata (metadata). +// Operation.response contains AsyncBatchAnnotateFilesResponse (results). +func (c *ImageAnnotatorClient) AsyncBatchAnnotateFiles(ctx context.Context, req *visionpb.AsyncBatchAnnotateFilesRequest, opts ...gax.CallOption) (*AsyncBatchAnnotateFilesOperation, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.AsyncBatchAnnotateFiles[0:len(c.CallOptions.AsyncBatchAnnotateFiles):len(c.CallOptions.AsyncBatchAnnotateFiles)], opts...) + var resp *longrunningpb.Operation + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.imageAnnotatorClient.AsyncBatchAnnotateFiles(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return &AsyncBatchAnnotateFilesOperation{ + lro: longrunning.InternalNewOperation(c.LROClient, resp), + }, nil +} + +// AsyncBatchAnnotateFilesOperation manages a long-running operation from AsyncBatchAnnotateFiles. +type AsyncBatchAnnotateFilesOperation struct { + lro *longrunning.Operation +} + +// AsyncBatchAnnotateFilesOperation returns a new AsyncBatchAnnotateFilesOperation from a given name. +// The name must be that of a previously created AsyncBatchAnnotateFilesOperation, possibly from a different process. +func (c *ImageAnnotatorClient) AsyncBatchAnnotateFilesOperation(name string) *AsyncBatchAnnotateFilesOperation { + return &AsyncBatchAnnotateFilesOperation{ + 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 *AsyncBatchAnnotateFilesOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*visionpb.AsyncBatchAnnotateFilesResponse, error) { + var resp visionpb.AsyncBatchAnnotateFilesResponse + if err := op.lro.WaitWithInterval(ctx, &resp, 45000*time.Millisecond, 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 *AsyncBatchAnnotateFilesOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*visionpb.AsyncBatchAnnotateFilesResponse, error) { + var resp visionpb.AsyncBatchAnnotateFilesResponse + 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 *AsyncBatchAnnotateFilesOperation) Metadata() (*visionpb.OperationMetadata, error) { + var meta visionpb.OperationMetadata + 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 *AsyncBatchAnnotateFilesOperation) 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 *AsyncBatchAnnotateFilesOperation) Name() string { + return op.lro.Name() +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/image_annotator_client_example_test.go b/vendor/cloud.google.com/go/vision/apiv1/image_annotator_client_example_test.go new file mode 100644 index 0000000000..83de6c9829 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/image_annotator_client_example_test.go @@ -0,0 +1,74 @@ +// 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 vision_test + +import ( + "cloud.google.com/go/vision/apiv1" + "golang.org/x/net/context" + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1" +) + +func ExampleNewImageAnnotatorClient() { + ctx := context.Background() + c, err := vision.NewImageAnnotatorClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleImageAnnotatorClient_BatchAnnotateImages() { + ctx := context.Background() + c, err := vision.NewImageAnnotatorClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &visionpb.BatchAnnotateImagesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.BatchAnnotateImages(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} + +func ExampleImageAnnotatorClient_AsyncBatchAnnotateFiles() { + ctx := context.Background() + c, err := vision.NewImageAnnotatorClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &visionpb.AsyncBatchAnnotateFilesRequest{ + // TODO: Fill request struct fields. + } + op, err := c.AsyncBatchAnnotateFiles(ctx, req) + if err != nil { + // TODO: Handle error. + } + + resp, err := op.Wait(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/vision/apiv1/mock_test.go b/vendor/cloud.google.com/go/vision/apiv1/mock_test.go new file mode 100644 index 0000000000..b9fa1a0e31 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1/mock_test.go @@ -0,0 +1,254 @@ +// 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 vision + +import ( + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1" + longrunningpb "google.golang.org/genproto/googleapis/longrunning" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockImageAnnotatorServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + visionpb.ImageAnnotatorServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockImageAnnotatorServer) BatchAnnotateImages(ctx context.Context, req *visionpb.BatchAnnotateImagesRequest) (*visionpb.BatchAnnotateImagesResponse, 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].(*visionpb.BatchAnnotateImagesResponse), nil +} + +func (s *mockImageAnnotatorServer) AsyncBatchAnnotateFiles(ctx context.Context, req *visionpb.AsyncBatchAnnotateFilesRequest) (*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 +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockImageAnnotator mockImageAnnotatorServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + visionpb.RegisterImageAnnotatorServer(serv, &mockImageAnnotator) + + 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 TestImageAnnotatorBatchAnnotateImages(t *testing.T) { + var expectedResponse *visionpb.BatchAnnotateImagesResponse = &visionpb.BatchAnnotateImagesResponse{} + + mockImageAnnotator.err = nil + mockImageAnnotator.reqs = nil + + mockImageAnnotator.resps = append(mockImageAnnotator.resps[:0], expectedResponse) + + var requests []*visionpb.AnnotateImageRequest = nil + var request = &visionpb.BatchAnnotateImagesRequest{ + Requests: requests, + } + + c, err := NewImageAnnotatorClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BatchAnnotateImages(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockImageAnnotator.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 TestImageAnnotatorBatchAnnotateImagesError(t *testing.T) { + errCode := codes.PermissionDenied + mockImageAnnotator.err = gstatus.Error(errCode, "test error") + + var requests []*visionpb.AnnotateImageRequest = nil + var request = &visionpb.BatchAnnotateImagesRequest{ + Requests: requests, + } + + c, err := NewImageAnnotatorClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BatchAnnotateImages(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 TestImageAnnotatorAsyncBatchAnnotateFiles(t *testing.T) { + var expectedResponse *visionpb.AsyncBatchAnnotateFilesResponse = &visionpb.AsyncBatchAnnotateFilesResponse{} + + mockImageAnnotator.err = nil + mockImageAnnotator.reqs = nil + + any, err := ptypes.MarshalAny(expectedResponse) + if err != nil { + t.Fatal(err) + } + mockImageAnnotator.resps = append(mockImageAnnotator.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Response{Response: any}, + }) + + var requests []*visionpb.AsyncAnnotateFileRequest = nil + var request = &visionpb.AsyncBatchAnnotateFilesRequest{ + Requests: requests, + } + + c, err := NewImageAnnotatorClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AsyncBatchAnnotateFiles(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, mockImageAnnotator.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 TestImageAnnotatorAsyncBatchAnnotateFilesError(t *testing.T) { + errCode := codes.PermissionDenied + mockImageAnnotator.err = nil + mockImageAnnotator.resps = append(mockImageAnnotator.resps[:0], &longrunningpb.Operation{ + Name: "longrunning-test", + Done: true, + Result: &longrunningpb.Operation_Error{ + Error: &status.Status{ + Code: int32(errCode), + Message: "test error", + }, + }, + }) + + var requests []*visionpb.AsyncAnnotateFileRequest = nil + var request = &visionpb.AsyncBatchAnnotateFilesRequest{ + Requests: requests, + } + + c, err := NewImageAnnotatorClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + respLRO, err := c.AsyncBatchAnnotateFiles(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 +} diff --git a/vendor/cloud.google.com/go/vision/apiv1p1beta1/BatchAnnotateImages_smoke_test.go b/vendor/cloud.google.com/go/vision/apiv1p1beta1/BatchAnnotateImages_smoke_test.go new file mode 100644 index 0000000000..50f637649c --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1p1beta1/BatchAnnotateImages_smoke_test.go @@ -0,0 +1,82 @@ +// 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 vision + +import ( + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1p1beta1" +) + +import ( + "fmt" + "strconv" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "golang.org/x/net/context" + "google.golang.org/api/iterator" + "google.golang.org/api/option" +) + +var _ = fmt.Sprintf +var _ = iterator.Done +var _ = strconv.FormatUint +var _ = time.Now + +func TestImageAnnotatorSmoke(t *testing.T) { + if testing.Short() { + t.Skip("skipping smoke test in short mode") + } + ctx := context.Background() + ts := testutil.TokenSource(ctx, DefaultAuthScopes()...) + if ts == nil { + t.Skip("Integration tests skipped. See CONTRIBUTING.md for details") + } + + projectId := testutil.ProjID() + _ = projectId + + c, err := NewImageAnnotatorClient(ctx, option.WithTokenSource(ts)) + if err != nil { + t.Fatal(err) + } + + var gcsImageUri string = "gs://gapic-toolkit/President_Barack_Obama.jpg" + var source = &visionpb.ImageSource{ + GcsImageUri: gcsImageUri, + } + var image = &visionpb.Image{ + Source: source, + } + var type_ visionpb.Feature_Type = visionpb.Feature_FACE_DETECTION + var featuresElement = &visionpb.Feature{ + Type: type_, + } + var features = []*visionpb.Feature{featuresElement} + var requestsElement = &visionpb.AnnotateImageRequest{ + Image: image, + Features: features, + } + var requests = []*visionpb.AnnotateImageRequest{requestsElement} + var request = &visionpb.BatchAnnotateImagesRequest{ + Requests: requests, + } + + if _, err := c.BatchAnnotateImages(ctx, request); err != nil { + t.Error(err) + } +} diff --git a/vendor/cloud.google.com/go/vision/apiv1p1beta1/doc.go b/vendor/cloud.google.com/go/vision/apiv1p1beta1/doc.go new file mode 100644 index 0000000000..f25ee7a3c2 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1p1beta1/doc.go @@ -0,0 +1,50 @@ +// 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 vision is an auto-generated package for the +// Google Cloud Vision API. +// +// NOTE: This package is in beta. It is not stable, and may be subject to changes. +// +// Integrates Google Vision features, including image labeling, face, logo, +// and +// landmark detection, optical character recognition (OCR), and detection of +// explicit content, into applications. +package vision // import "cloud.google.com/go/vision/apiv1p1beta1" + +import ( + "golang.org/x/net/context" + "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", + "https://www.googleapis.com/auth/cloud-vision", + } +} diff --git a/vendor/cloud.google.com/go/vision/apiv1p1beta1/image_annotator_client.go b/vendor/cloud.google.com/go/vision/apiv1p1beta1/image_annotator_client.go new file mode 100644 index 0000000000..1a446e473b --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1p1beta1/image_annotator_client.go @@ -0,0 +1,136 @@ +// 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 vision + +import ( + "time" + + "cloud.google.com/go/internal/version" + gax "github.com/googleapis/gax-go" + "golang.org/x/net/context" + "google.golang.org/api/option" + "google.golang.org/api/transport" + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1p1beta1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" +) + +// ImageAnnotatorCallOptions contains the retry settings for each method of ImageAnnotatorClient. +type ImageAnnotatorCallOptions struct { + BatchAnnotateImages []gax.CallOption +} + +func defaultImageAnnotatorClientOptions() []option.ClientOption { + return []option.ClientOption{ + option.WithEndpoint("vision.googleapis.com:443"), + option.WithScopes(DefaultAuthScopes()...), + } +} + +func defaultImageAnnotatorCallOptions() *ImageAnnotatorCallOptions { + 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 &ImageAnnotatorCallOptions{ + BatchAnnotateImages: retry[[2]string{"default", "idempotent"}], + } +} + +// ImageAnnotatorClient is a client for interacting with Google Cloud Vision API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type ImageAnnotatorClient struct { + // The connection to the service. + conn *grpc.ClientConn + + // The gRPC API client. + imageAnnotatorClient visionpb.ImageAnnotatorClient + + // The call options for this service. + CallOptions *ImageAnnotatorCallOptions + + // The x-goog-* metadata to be sent with each request. + xGoogMetadata metadata.MD +} + +// NewImageAnnotatorClient creates a new image annotator client. +// +// Service that performs Google Cloud Vision API detection tasks over client +// images, such as face, landmark, logo, label, and text detection. The +// ImageAnnotator service returns detected entities from the images. +func NewImageAnnotatorClient(ctx context.Context, opts ...option.ClientOption) (*ImageAnnotatorClient, error) { + conn, err := transport.DialGRPC(ctx, append(defaultImageAnnotatorClientOptions(), opts...)...) + if err != nil { + return nil, err + } + c := &ImageAnnotatorClient{ + conn: conn, + CallOptions: defaultImageAnnotatorCallOptions(), + + imageAnnotatorClient: visionpb.NewImageAnnotatorClient(conn), + } + c.setGoogleClientInfo() + return c, nil +} + +// Connection returns the client's connection to the API service. +func (c *ImageAnnotatorClient) 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 *ImageAnnotatorClient) 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 *ImageAnnotatorClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", version.Go()}, keyval...) + kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) +} + +// BatchAnnotateImages run image detection and annotation for a batch of images. +func (c *ImageAnnotatorClient) BatchAnnotateImages(ctx context.Context, req *visionpb.BatchAnnotateImagesRequest, opts ...gax.CallOption) (*visionpb.BatchAnnotateImagesResponse, error) { + ctx = insertMetadata(ctx, c.xGoogMetadata) + opts = append(c.CallOptions.BatchAnnotateImages[0:len(c.CallOptions.BatchAnnotateImages):len(c.CallOptions.BatchAnnotateImages)], opts...) + var resp *visionpb.BatchAnnotateImagesResponse + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + resp, err = c.imageAnnotatorClient.BatchAnnotateImages(ctx, req, settings.GRPC...) + return err + }, opts...) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/cloud.google.com/go/vision/apiv1p1beta1/image_annotator_client_example_test.go b/vendor/cloud.google.com/go/vision/apiv1p1beta1/image_annotator_client_example_test.go new file mode 100644 index 0000000000..ed5aae6fc5 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1p1beta1/image_annotator_client_example_test.go @@ -0,0 +1,51 @@ +// 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 vision_test + +import ( + "cloud.google.com/go/vision/apiv1p1beta1" + "golang.org/x/net/context" + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1p1beta1" +) + +func ExampleNewImageAnnotatorClient() { + ctx := context.Background() + c, err := vision.NewImageAnnotatorClient(ctx) + if err != nil { + // TODO: Handle error. + } + // TODO: Use client. + _ = c +} + +func ExampleImageAnnotatorClient_BatchAnnotateImages() { + ctx := context.Background() + c, err := vision.NewImageAnnotatorClient(ctx) + if err != nil { + // TODO: Handle error. + } + + req := &visionpb.BatchAnnotateImagesRequest{ + // TODO: Fill request struct fields. + } + resp, err := c.BatchAnnotateImages(ctx, req) + if err != nil { + // TODO: Handle error. + } + // TODO: Use resp. + _ = resp +} diff --git a/vendor/cloud.google.com/go/vision/apiv1p1beta1/mock_test.go b/vendor/cloud.google.com/go/vision/apiv1p1beta1/mock_test.go new file mode 100644 index 0000000000..5f330cff60 --- /dev/null +++ b/vendor/cloud.google.com/go/vision/apiv1p1beta1/mock_test.go @@ -0,0 +1,159 @@ +// 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 vision + +import ( + visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1p1beta1" +) + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "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 mockImageAnnotatorServer struct { + // Embed for forward compatibility. + // Tests will keep working if more methods are added + // in the future. + visionpb.ImageAnnotatorServer + + reqs []proto.Message + + // If set, all calls return this error. + err error + + // responses to return if err == nil + resps []proto.Message +} + +func (s *mockImageAnnotatorServer) BatchAnnotateImages(ctx context.Context, req *visionpb.BatchAnnotateImagesRequest) (*visionpb.BatchAnnotateImagesResponse, 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].(*visionpb.BatchAnnotateImagesResponse), nil +} + +// clientOpt is the option tests should use to connect to the test server. +// It is initialized by TestMain. +var clientOpt option.ClientOption + +var ( + mockImageAnnotator mockImageAnnotatorServer +) + +func TestMain(m *testing.M) { + flag.Parse() + + serv := grpc.NewServer() + visionpb.RegisterImageAnnotatorServer(serv, &mockImageAnnotator) + + 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 TestImageAnnotatorBatchAnnotateImages(t *testing.T) { + var expectedResponse *visionpb.BatchAnnotateImagesResponse = &visionpb.BatchAnnotateImagesResponse{} + + mockImageAnnotator.err = nil + mockImageAnnotator.reqs = nil + + mockImageAnnotator.resps = append(mockImageAnnotator.resps[:0], expectedResponse) + + var requests []*visionpb.AnnotateImageRequest = nil + var request = &visionpb.BatchAnnotateImagesRequest{ + Requests: requests, + } + + c, err := NewImageAnnotatorClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BatchAnnotateImages(context.Background(), request) + + if err != nil { + t.Fatal(err) + } + + if want, got := request, mockImageAnnotator.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 TestImageAnnotatorBatchAnnotateImagesError(t *testing.T) { + errCode := codes.PermissionDenied + mockImageAnnotator.err = gstatus.Error(errCode, "test error") + + var requests []*visionpb.AnnotateImageRequest = nil + var request = &visionpb.BatchAnnotateImagesRequest{ + Requests: requests, + } + + c, err := NewImageAnnotatorClient(context.Background(), clientOpt) + if err != nil { + t.Fatal(err) + } + + resp, err := c.BatchAnnotateImages(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/vendor/github.com/howeyc/gopass/LICENSE.txt b/vendor/github.com/howeyc/gopass/LICENSE.txt new file mode 100644 index 0000000000..14f74708a4 --- /dev/null +++ b/vendor/github.com/howeyc/gopass/LICENSE.txt @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012 Chris Howey + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/howeyc/gopass/OPENSOLARIS.LICENSE b/vendor/github.com/howeyc/gopass/OPENSOLARIS.LICENSE new file mode 100644 index 0000000000..da23621dc8 --- /dev/null +++ b/vendor/github.com/howeyc/gopass/OPENSOLARIS.LICENSE @@ -0,0 +1,384 @@ +Unless otherwise noted, all files in this distribution are released +under the Common Development and Distribution License (CDDL). +Exceptions are noted within the associated source files. + +-------------------------------------------------------------------- + + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates + or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), + and the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing + Original Software with files containing Modifications, in + each case including portions thereof. + + 1.4. "Executable" means the Covered Software in any form other + than Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this + License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed + herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original + Software or previous Modifications; + + B. Any new file that contains any part of the Original + Software or previous Modifications; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable + form of computer software code that is originally released + under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, + process, and apparatus claims, in any patent Licensable by + grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms + of, this License. For legal entities, "You" includes any + entity which controls, is controlled by, or is under common + control with You. For purposes of this definition, + "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the Initial + Developer hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, + reproduce, modify, display, perform, sublicense and + distribute the Original Software (or portions thereof), + with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or + selling of Original Software, to make, have made, use, + practice, sell, and offer for sale, and/or otherwise + dispose of the Original Software (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are + effective on the date Initial Developer first distributes + or otherwise makes the Original Software available to a + third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original + Software, or (2) for infringements caused by: (i) the + modification of the Original Software, or (ii) the + combination of the Original Software with other software + or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, + modify, display, perform, sublicense and distribute the + Modifications created by such Contributor (or portions + thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a + Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either + alone and/or in combination with its Contributor Version + (or portions of such combination), to make, use, sell, + offer for sale, have made, and/or otherwise dispose of: + (1) Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions + of such combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first distributes or + otherwise makes the Modifications available to a third + party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted + from the Contributor Version; (2) for infringements caused + by: (i) third party modifications of Contributor Version, + or (ii) the combination of Modifications made by that + Contributor with other software (except as part of the + Contributor Version) or other devices; or (3) under Patent + Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in Source + Code form and that Source Code form must be distributed only under + the terms of this License. You must include a copy of this + License with every copy of the Source Code form of the Covered + Software You distribute or otherwise make available. You must + inform recipients of any such Covered Software in Executable form + as to how they can obtain such Covered Software in Source Code + form in a reasonable manner on or through a medium customarily + used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or + You have sufficient rights to grant the rights conveyed by this + License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may + not remove or alter any copyright, patent or trademark notices + contained within the Covered Software, or any notices of licensing + or any descriptive text giving attribution to any Contributor or + the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version + of this License or the recipients' rights hereunder. You may + choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, you may do so only on Your own behalf, + and not on behalf of the Initial Developer or any Contributor. + You must make it absolutely clear that any such warranty, support, + indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software + under the terms of this License or under the terms of a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the + Covered Software in Executable form under a different license, You + must make it absolutely clear that any terms which differ from + this License are offered by You alone, not by the Initial + Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of any + such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, + You must make sure the requirements of this License are fulfilled + for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and may + publish revised and/or new versions of this License from time to + time. Each version will be given a distinguishing version number. + Except as provided in Section 4.3, no one other than the license + steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. + If the Initial Developer includes a notice in the Original + Software prohibiting it from being distributed or otherwise made + available under any subsequent version of the License, You must + distribute and make the Covered Software available under the terms + of the version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to use, + distribute or otherwise make the Covered Software available under + the terms of any subsequent version of the License published by + the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license + and remove any references to the name of the license steward + (except to note that the license differs from this License); and + (b) otherwise make it clear that the license contains terms which + differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond + the termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that + the Participant Software (meaning the Contributor Version where + the Participant is a Contributor or the Original Software where + the Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if + the Initial Developer is not the Participant) and all Contributors + under Sections 2.1 and/or 2.2 of this License shall, upon 60 days + notice from Participant terminate prospectively and automatically + at the expiration of such 60 day notice period, unless if within + such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally + or pursuant to a written agreement with Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial + computer software" (as that term is defined at 48 + C.F.R. 252.227-7014(a)(1)) and "commercial computer software + documentation" as such terms are used in 48 C.F.R. 12.212 + (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 + C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all + U.S. Government End Users acquire Covered Software with only those + rights set forth herein. This U.S. Government Rights clause is in + lieu of, and supersedes, any other FAR, DFAR, or other clause or + provision that addresses Government rights in computer software + under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed + by the law of the jurisdiction specified in a notice contained + within the Original Software (except to the extent applicable law, + if any, provides otherwise), excluding such jurisdiction's + conflict-of-law provisions. Any litigation relating to this + License shall be subject to the jurisdiction of the courts located + in the jurisdiction and venue specified in a notice contained + within the Original Software, with the losing party responsible + for costs, including, without limitation, court costs and + reasonable attorneys' fees and expenses. The application of the + United Nations Convention on Contracts for the International Sale + of Goods is expressly excluded. Any law or regulation which + provides that the language of a contract shall be construed + against the drafter shall not apply to this License. You agree + that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. + +-------------------------------------------------------------------- + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND +DISTRIBUTION LICENSE (CDDL) + +For Covered Software in this distribution, this License shall +be governed by the laws of the State of California (excluding +conflict-of-law provisions). + +Any litigation relating to this License shall be subject to the +jurisdiction of the Federal Courts of the Northern District of +California and the state courts of the State of California, with +venue lying in Santa Clara County, California. diff --git a/vendor/github.com/howeyc/gopass/README.md b/vendor/github.com/howeyc/gopass/README.md new file mode 100644 index 0000000000..2d6a4e72c9 --- /dev/null +++ b/vendor/github.com/howeyc/gopass/README.md @@ -0,0 +1,27 @@ +# getpasswd in Go [![GoDoc](https://godoc.org/github.com/howeyc/gopass?status.svg)](https://godoc.org/github.com/howeyc/gopass) [![Build Status](https://secure.travis-ci.org/howeyc/gopass.png?branch=master)](http://travis-ci.org/howeyc/gopass) + +Retrieve password from user terminal or piped input without echo. + +Verified on BSD, Linux, and Windows. + +Example: +```go +package main + +import "fmt" +import "github.com/howeyc/gopass" + +func main() { + fmt.Printf("Password: ") + + // Silent. For printing *'s use gopass.GetPasswdMasked() + pass, err := gopass.GetPasswd() + if err != nil { + // Handle gopass.ErrInterrupted or getch() read error + } + + // Do something with pass +} +``` + +Caution: Multi-byte characters not supported! diff --git a/vendor/github.com/howeyc/gopass/pass.go b/vendor/github.com/howeyc/gopass/pass.go new file mode 100644 index 0000000000..f5bd5a51a8 --- /dev/null +++ b/vendor/github.com/howeyc/gopass/pass.go @@ -0,0 +1,110 @@ +package gopass + +import ( + "errors" + "fmt" + "io" + "os" +) + +type FdReader interface { + io.Reader + Fd() uintptr +} + +var defaultGetCh = func(r io.Reader) (byte, error) { + buf := make([]byte, 1) + if n, err := r.Read(buf); n == 0 || err != nil { + if err != nil { + return 0, err + } + return 0, io.EOF + } + return buf[0], nil +} + +var ( + maxLength = 512 + ErrInterrupted = errors.New("interrupted") + ErrMaxLengthExceeded = fmt.Errorf("maximum byte limit (%v) exceeded", maxLength) + + // Provide variable so that tests can provide a mock implementation. + getch = defaultGetCh +) + +// getPasswd returns the input read from terminal. +// If prompt is not empty, it will be output as a prompt to the user +// If masked is true, typing will be matched by asterisks on the screen. +// Otherwise, typing will echo nothing. +func getPasswd(prompt string, masked bool, r FdReader, w io.Writer) ([]byte, error) { + var err error + var pass, bs, mask []byte + if masked { + bs = []byte("\b \b") + mask = []byte("*") + } + + if isTerminal(r.Fd()) { + if oldState, err := makeRaw(r.Fd()); err != nil { + return pass, err + } else { + defer func() { + restore(r.Fd(), oldState) + fmt.Fprintln(w) + }() + } + } + + if prompt != "" { + fmt.Fprint(w, prompt) + } + + // Track total bytes read, not just bytes in the password. This ensures any + // errors that might flood the console with nil or -1 bytes infinitely are + // capped. + var counter int + for counter = 0; counter <= maxLength; counter++ { + if v, e := getch(r); e != nil { + err = e + break + } else if v == 127 || v == 8 { + if l := len(pass); l > 0 { + pass = pass[:l-1] + fmt.Fprint(w, string(bs)) + } + } else if v == 13 || v == 10 { + break + } else if v == 3 { + err = ErrInterrupted + break + } else if v != 0 { + pass = append(pass, v) + fmt.Fprint(w, string(mask)) + } + } + + if counter > maxLength { + err = ErrMaxLengthExceeded + } + + return pass, err +} + +// GetPasswd returns the password read from the terminal without echoing input. +// The returned byte array does not include end-of-line characters. +func GetPasswd() ([]byte, error) { + return getPasswd("", false, os.Stdin, os.Stdout) +} + +// GetPasswdMasked returns the password read from the terminal, echoing asterisks. +// The returned byte array does not include end-of-line characters. +func GetPasswdMasked() ([]byte, error) { + return getPasswd("", true, os.Stdin, os.Stdout) +} + +// GetPasswdPrompt prompts the user and returns the password read from the terminal. +// If mask is true, then asterisks are echoed. +// The returned byte array does not include end-of-line characters. +func GetPasswdPrompt(prompt string, mask bool, r FdReader, w io.Writer) ([]byte, error) { + return getPasswd(prompt, mask, r, w) +} diff --git a/vendor/github.com/howeyc/gopass/pass_test.go b/vendor/github.com/howeyc/gopass/pass_test.go new file mode 100644 index 0000000000..7ac3151357 --- /dev/null +++ b/vendor/github.com/howeyc/gopass/pass_test.go @@ -0,0 +1,225 @@ +package gopass + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "testing" + "time" +) + +// TestGetPasswd tests the password creation and output based on a byte buffer +// as input to mock the underlying getch() methods. +func TestGetPasswd(t *testing.T) { + type testData struct { + input []byte + + // Due to how backspaces are written, it is easier to manually write + // each expected output for the masked cases. + masked string + password string + byesLeft int + reason string + } + + ds := []testData{ + testData{[]byte("abc\n"), "***", "abc", 0, "Password parsing should stop at \\n"}, + testData{[]byte("abc\r"), "***", "abc", 0, "Password parsing should stop at \\r"}, + testData{[]byte("a\nbc\n"), "*", "a", 3, "Password parsing should stop at \\n"}, + testData{[]byte("*!]|\n"), "****", "*!]|", 0, "Special characters shouldn't affect the password."}, + + testData{[]byte("abc\r\n"), "***", "abc", 1, + "Password parsing should stop at \\r; Windows LINE_MODE should be unset so \\r is not converted to \\r\\n."}, + + testData{[]byte{'a', 'b', 'c', 8, '\n'}, "***\b \b", "ab", 0, "Backspace byte should remove the last read byte."}, + testData{[]byte{'a', 'b', 127, 'c', '\n'}, "**\b \b*", "ac", 0, "Delete byte should remove the last read byte."}, + testData{[]byte{'a', 'b', 127, 'c', 8, 127, '\n'}, "**\b \b*\b \b\b \b", "", 0, "Successive deletes continue to delete."}, + testData{[]byte{8, 8, 8, '\n'}, "", "", 0, "Deletes before characters are noops."}, + testData{[]byte{8, 8, 8, 'a', 'b', 'c', '\n'}, "***", "abc", 0, "Deletes before characters are noops."}, + + testData{[]byte{'a', 'b', 0, 'c', '\n'}, "***", "abc", 0, + "Nil byte should be ignored due; may get unintended nil bytes from syscalls on Windows."}, + } + + // Redirecting output for tests as they print to os.Stdout but we want to + // capture and test the output. + for _, masked := range []bool{true, false} { + for _, d := range ds { + pipeBytesToStdin(d.input) + + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err.Error()) + } + + result, err := getPasswd("", masked, os.Stdin, w) + if err != nil { + t.Errorf("Error getting password: %s", err.Error()) + } + leftOnBuffer := flushStdin() + + // Test output (masked and unmasked). Delete/backspace actually + // deletes, overwrites and deletes again. As a result, we need to + // remove those from the pipe afterwards to mimic the console's + // interpretation of those bytes. + w.Close() + output, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err.Error()) + } + var expectedOutput []byte + if masked { + expectedOutput = []byte(d.masked) + } else { + expectedOutput = []byte("") + } + if bytes.Compare(expectedOutput, output) != 0 { + t.Errorf("Expected output to equal %v (%q) but got %v (%q) instead when masked=%v. %s", expectedOutput, string(expectedOutput), output, string(output), masked, d.reason) + } + + if string(result) != d.password { + t.Errorf("Expected %q but got %q instead when masked=%v. %s", d.password, result, masked, d.reason) + } + + if leftOnBuffer != d.byesLeft { + t.Errorf("Expected %v bytes left on buffer but instead got %v when masked=%v. %s", d.byesLeft, leftOnBuffer, masked, d.reason) + } + } + } +} + +// TestPipe ensures we get our expected pipe behavior. +func TestPipe(t *testing.T) { + type testData struct { + input string + password string + expError error + } + ds := []testData{ + testData{"abc", "abc", io.EOF}, + testData{"abc\n", "abc", nil}, + testData{"abc\r", "abc", nil}, + testData{"abc\r\n", "abc", nil}, + } + + for _, d := range ds { + _, err := pipeToStdin(d.input) + if err != nil { + t.Log("Error writing input to stdin:", err) + t.FailNow() + } + pass, err := GetPasswd() + if string(pass) != d.password { + t.Errorf("Expected %q but got %q instead.", d.password, string(pass)) + } + if err != d.expError { + t.Errorf("Expected %v but got %q instead.", d.expError, err) + } + } +} + +// flushStdin reads from stdin for .5 seconds to ensure no bytes are left on +// the buffer. Returns the number of bytes read. +func flushStdin() int { + ch := make(chan byte) + go func(ch chan byte) { + reader := bufio.NewReader(os.Stdin) + for { + b, err := reader.ReadByte() + if err != nil { // Maybe log non io.EOF errors, if you want + close(ch) + return + } + ch <- b + } + close(ch) + }(ch) + + numBytes := 0 + for { + select { + case _, ok := <-ch: + if !ok { + return numBytes + } + numBytes++ + case <-time.After(500 * time.Millisecond): + return numBytes + } + } + return numBytes +} + +// pipeToStdin pipes the given string onto os.Stdin by replacing it with an +// os.Pipe. The write end of the pipe is closed so that EOF is read after the +// final byte. +func pipeToStdin(s string) (int, error) { + pipeReader, pipeWriter, err := os.Pipe() + if err != nil { + fmt.Println("Error getting os pipes:", err) + os.Exit(1) + } + os.Stdin = pipeReader + w, err := pipeWriter.WriteString(s) + pipeWriter.Close() + return w, err +} + +func pipeBytesToStdin(b []byte) (int, error) { + return pipeToStdin(string(b)) +} + +// TestGetPasswd_Err tests errors are properly handled from getch() +func TestGetPasswd_Err(t *testing.T) { + var inBuffer *bytes.Buffer + getch = func(io.Reader) (byte, error) { + b, err := inBuffer.ReadByte() + if err != nil { + return 13, err + } + if b == 'z' { + return 'z', fmt.Errorf("Forced error; byte returned should not be considered accurate.") + } + return b, nil + } + defer func() { getch = defaultGetCh }() + + for input, expectedPassword := range map[string]string{"abc": "abc", "abzc": "ab"} { + inBuffer = bytes.NewBufferString(input) + p, err := GetPasswdMasked() + if string(p) != expectedPassword { + t.Errorf("Expected %q but got %q instead.", expectedPassword, p) + } + if err == nil { + t.Errorf("Expected error to be returned.") + } + } +} + +func TestMaxPasswordLength(t *testing.T) { + type testData struct { + input []byte + expectedErr error + + // Helper field to output in case of failure; rather than hundreds of + // bytes. + inputDesc string + } + + ds := []testData{ + testData{append(bytes.Repeat([]byte{'a'}, maxLength), '\n'), nil, fmt.Sprintf("%v 'a' bytes followed by a newline", maxLength)}, + testData{append(bytes.Repeat([]byte{'a'}, maxLength+1), '\n'), ErrMaxLengthExceeded, fmt.Sprintf("%v 'a' bytes followed by a newline", maxLength+1)}, + testData{append(bytes.Repeat([]byte{0x00}, maxLength+1), '\n'), ErrMaxLengthExceeded, fmt.Sprintf("%v 0x00 bytes followed by a newline", maxLength+1)}, + } + + for _, d := range ds { + pipeBytesToStdin(d.input) + _, err := GetPasswd() + if err != d.expectedErr { + t.Errorf("Expected error to be %v; isntead got %v from %v", d.expectedErr, err, d.inputDesc) + } + } +} diff --git a/vendor/github.com/howeyc/gopass/terminal.go b/vendor/github.com/howeyc/gopass/terminal.go new file mode 100644 index 0000000000..0835641462 --- /dev/null +++ b/vendor/github.com/howeyc/gopass/terminal.go @@ -0,0 +1,25 @@ +// +build !solaris + +package gopass + +import "golang.org/x/crypto/ssh/terminal" + +type terminalState struct { + state *terminal.State +} + +func isTerminal(fd uintptr) bool { + return terminal.IsTerminal(int(fd)) +} + +func makeRaw(fd uintptr) (*terminalState, error) { + state, err := terminal.MakeRaw(int(fd)) + + return &terminalState{ + state: state, + }, err +} + +func restore(fd uintptr, oldState *terminalState) error { + return terminal.Restore(int(fd), oldState.state) +} diff --git a/vendor/github.com/howeyc/gopass/terminal_solaris.go b/vendor/github.com/howeyc/gopass/terminal_solaris.go new file mode 100644 index 0000000000..257e1b4e81 --- /dev/null +++ b/vendor/github.com/howeyc/gopass/terminal_solaris.go @@ -0,0 +1,69 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +// Below is derived from Solaris source, so CDDL license is included. + +package gopass + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +type terminalState struct { + state *unix.Termios +} + +// isTerminal returns true if there is a terminal attached to the given +// file descriptor. +// Source: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c +func isTerminal(fd uintptr) bool { + var termio unix.Termio + err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) + return err == nil +} + +// makeRaw puts the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +// Source: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c +func makeRaw(fd uintptr) (*terminalState, error) { + oldTermiosPtr, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) + if err != nil { + return nil, err + } + oldTermios := *oldTermiosPtr + + newTermios := oldTermios + newTermios.Lflag &^= syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL + if err := unix.IoctlSetTermios(int(fd), unix.TCSETS, &newTermios); err != nil { + return nil, err + } + + return &terminalState{ + state: oldTermiosPtr, + }, nil +} + +func restore(fd uintptr, oldState *terminalState) error { + return unix.IoctlSetTermios(int(fd), unix.TCSETS, oldState.state) +} diff --git a/vendor/github.com/imdario/mergo/.gitignore b/vendor/github.com/imdario/mergo/.gitignore new file mode 100644 index 0000000000..529c3412ba --- /dev/null +++ b/vendor/github.com/imdario/mergo/.gitignore @@ -0,0 +1,33 @@ +#### joe made this: http://goel.io/joe + +#### go #### +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ + +#### vim #### +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] + +# Session +Session.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags diff --git a/vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md b/vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..469b44907a --- /dev/null +++ b/vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at i@dario.im. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/imdario/mergo/LICENSE b/vendor/github.com/imdario/mergo/LICENSE new file mode 100644 index 0000000000..686680298d --- /dev/null +++ b/vendor/github.com/imdario/mergo/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2013 Dario Castañé. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/imdario/mergo/README.md b/vendor/github.com/imdario/mergo/README.md new file mode 100644 index 0000000000..d1cefa8718 --- /dev/null +++ b/vendor/github.com/imdario/mergo/README.md @@ -0,0 +1,222 @@ +# Mergo + +A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. + +Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. + +## Status + +It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild). + +[![GoDoc][3]][4] +[![GoCard][5]][6] +[![Build Status][1]][2] +[![Coverage Status][7]][8] +[![Sourcegraph][9]][10] + +[1]: https://travis-ci.org/imdario/mergo.png +[2]: https://travis-ci.org/imdario/mergo +[3]: https://godoc.org/github.com/imdario/mergo?status.svg +[4]: https://godoc.org/github.com/imdario/mergo +[5]: https://goreportcard.com/badge/imdario/mergo +[6]: https://goreportcard.com/report/github.com/imdario/mergo +[7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master +[8]: https://coveralls.io/github/imdario/mergo?branch=master +[9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg +[10]: https://sourcegraph.com/github.com/imdario/mergo?badge + +### Latest release + +[Release v0.3.4](https://github.com/imdario/mergo/releases/tag/v0.3.4). + +### Important note + +Please keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2) Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code. + +If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0). + +### Donations + +If Mergo is useful to you, consider buying me a coffee, a beer or making a monthly donation so I can keep building great free software. :heart_eyes: + +Buy Me a Coffee at ko-fi.com +[![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo) +[![Beerpay](https://beerpay.io/imdario/mergo/make-wish.svg)](https://beerpay.io/imdario/mergo) +Donate using Liberapay + +### Mergo in the wild + +- [moby/moby](https://github.com/moby/moby) +- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes) +- [vmware/dispatch](https://github.com/vmware/dispatch) +- [Shopify/themekit](https://github.com/Shopify/themekit) +- [imdario/zas](https://github.com/imdario/zas) +- [matcornic/hermes](https://github.com/matcornic/hermes) +- [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go) +- [kataras/iris](https://github.com/kataras/iris) +- [michaelsauter/crane](https://github.com/michaelsauter/crane) +- [go-task/task](https://github.com/go-task/task) +- [sensu/uchiwa](https://github.com/sensu/uchiwa) +- [ory/hydra](https://github.com/ory/hydra) +- [sisatech/vcli](https://github.com/sisatech/vcli) +- [dairycart/dairycart](https://github.com/dairycart/dairycart) +- [projectcalico/felix](https://github.com/projectcalico/felix) +- [resin-os/balena](https://github.com/resin-os/balena) +- [go-kivik/kivik](https://github.com/go-kivik/kivik) +- [Telefonica/govice](https://github.com/Telefonica/govice) +- [supergiant/supergiant](supergiant/supergiant) +- [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce) +- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy) +- [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel) +- [EagerIO/Stout](https://github.com/EagerIO/Stout) +- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api) +- [russross/canvasassignments](https://github.com/russross/canvasassignments) +- [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api) +- [casualjim/exeggutor](https://github.com/casualjim/exeggutor) +- [divshot/gitling](https://github.com/divshot/gitling) +- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl) +- [andrerocker/deploy42](https://github.com/andrerocker/deploy42) +- [elwinar/rambler](https://github.com/elwinar/rambler) +- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman) +- [jfbus/impressionist](https://github.com/jfbus/impressionist) +- [Jmeyering/zealot](https://github.com/Jmeyering/zealot) +- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host) +- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go) +- [thoas/picfit](https://github.com/thoas/picfit) +- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server) +- [jnuthong/item_search](https://github.com/jnuthong/item_search) +- [bukalapak/snowboard](https://github.com/bukalapak/snowboard) + +## Installation + + go get github.com/imdario/mergo + + // use in your .go code + import ( + "github.com/imdario/mergo" + ) + +## Usage + +You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are not considered zero values](https://golang.org/ref/spec#The_zero_value) either. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). + +```go +if err := mergo.Merge(&dst, src); err != nil { + // ... +} +``` + +Also, you can merge overwriting values using the transformer `WithOverride`. + +```go +if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { + // ... +} +``` + +Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field. + +```go +if err := mergo.Map(&dst, srcMap); err != nil { + // ... +} +``` + +Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values. + +More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo). + +### Nice example + +```go +package main + +import ( + "fmt" + "github.com/imdario/mergo" +) + +type Foo struct { + A string + B int64 +} + +func main() { + src := Foo{ + A: "one", + B: 2, + } + dest := Foo{ + A: "two", + } + mergo.Merge(&dest, src) + fmt.Println(dest) + // Will print + // {two 2} +} +``` + +Note: if test are failing due missing package, please execute: + + go get gopkg.in/yaml.v2 + +### Transformers + +Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`? + +```go +package main + +import ( + "fmt" + "github.com/imdario/mergo" + "reflect" + "time" +) + +type timeTransfomer struct { +} + +func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { + if typ == reflect.TypeOf(time.Time{}) { + return func(dst, src reflect.Value) error { + if dst.CanSet() { + isZero := dst.MethodByName("IsZero") + result := isZero.Call([]reflect.Value{}) + if result[0].Bool() { + dst.Set(src) + } + } + return nil + } + } + return nil +} + +type Snapshot struct { + Time time.Time + // ... +} + +func main() { + src := Snapshot{time.Now()} + dest := Snapshot{} + mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{})) + fmt.Println(dest) + // Will print + // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } +} +``` + + +## Contact me + +If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario) + +## About + +Written by [Dario Castañé](http://dario.im). + +## License + +[BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE). diff --git a/vendor/github.com/imdario/mergo/doc.go b/vendor/github.com/imdario/mergo/doc.go new file mode 100644 index 0000000000..6e9aa7baf3 --- /dev/null +++ b/vendor/github.com/imdario/mergo/doc.go @@ -0,0 +1,44 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mergo merges same-type structs and maps by setting default values in zero-value fields. + +Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). + +Usage + +From my own work-in-progress project: + + type networkConfig struct { + Protocol string + Address string + ServerType string `json: "server_type"` + Port uint16 + } + + type FssnConfig struct { + Network networkConfig + } + + var fssnDefault = FssnConfig { + networkConfig { + "tcp", + "127.0.0.1", + "http", + 31560, + }, + } + + // Inside a function [...] + + if err := mergo.Merge(&config, fssnDefault); err != nil { + log.Fatal(err) + } + + // More code [...] + +*/ +package mergo diff --git a/vendor/github.com/imdario/mergo/issue17_test.go b/vendor/github.com/imdario/mergo/issue17_test.go new file mode 100644 index 0000000000..f9de805ab7 --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue17_test.go @@ -0,0 +1,25 @@ +package mergo + +import ( + "encoding/json" + "testing" +) + +var ( + request = `{"timestamp":null, "name": "foo"}` + maprequest = map[string]interface{}{ + "timestamp": nil, + "name": "foo", + "newStuff": "foo", + } +) + +func TestIssue17MergeWithOverwrite(t *testing.T) { + var something map[string]interface{} + if err := json.Unmarshal([]byte(request), &something); err != nil { + t.Errorf("Error while Unmarshalling maprequest: %s", err) + } + if err := MergeWithOverwrite(&something, maprequest); err != nil { + t.Errorf("Error while merging: %s", err) + } +} diff --git a/vendor/github.com/imdario/mergo/issue23_test.go b/vendor/github.com/imdario/mergo/issue23_test.go new file mode 100644 index 0000000000..283f8c6a3f --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue23_test.go @@ -0,0 +1,27 @@ +package mergo + +import ( + "testing" + "time" +) + +type document struct { + Created *time.Time +} + +func TestIssue23MergeWithOverwrite(t *testing.T) { + now := time.Now() + dst := document{ + &now, + } + expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + src := document{ + &expected, + } + if err := MergeWithOverwrite(&dst, src); err != nil { + t.Errorf("Error while merging %s", err) + } + if !dst.Created.Equal(*src.Created) { //--> https://golang.org/pkg/time/#pkg-overview + t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created) + } +} diff --git a/vendor/github.com/imdario/mergo/issue33_test.go b/vendor/github.com/imdario/mergo/issue33_test.go new file mode 100644 index 0000000000..ae55ae236f --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue33_test.go @@ -0,0 +1,33 @@ +package mergo + +import ( + "testing" +) + +type Foo struct { + Str string + Bslice []byte +} + +func TestIssue33Merge(t *testing.T) { + dest := Foo{Str: "a"} + toMerge := Foo{ + Str: "b", + Bslice: []byte{1, 2}, + } + if err := Merge(&dest, toMerge); err != nil { + t.Errorf("Error while merging: %s", err) + } + // Merge doesn't overwrite an attribute if in destination it doesn't have a zero value. + // In this case, Str isn't a zero value string. + if dest.Str != "a" { + t.Errorf("dest.Str should have not been override as it has a non-zero value: dest.Str(%v) != 'a'", dest.Str) + } + // If we want to override, we must use MergeWithOverwrite or Merge using WithOverride. + if err := Merge(&dest, toMerge, WithOverride); err != nil { + t.Errorf("Error while merging: %s", err) + } + if dest.Str != toMerge.Str { + t.Errorf("dest.Str should have been override: dest.Str(%v) != toMerge.Str(%v)", dest.Str, toMerge.Str) + } +} diff --git a/vendor/github.com/imdario/mergo/issue38_test.go b/vendor/github.com/imdario/mergo/issue38_test.go new file mode 100644 index 0000000000..286b68cb1c --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue38_test.go @@ -0,0 +1,59 @@ +package mergo + +import ( + "testing" + "time" +) + +type structWithoutTimePointer struct { + Created time.Time +} + +func TestIssue38Merge(t *testing.T) { + dst := structWithoutTimePointer{ + time.Now(), + } + + expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + src := structWithoutTimePointer{ + expected, + } + if err := Merge(&dst, src); err != nil { + t.Errorf("Error while merging %s", err) + } + if dst.Created == src.Created { + t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created) + } +} + +func TestIssue38MergeEmptyStruct(t *testing.T) { + dst := structWithoutTimePointer{} + + expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + src := structWithoutTimePointer{ + expected, + } + if err := Merge(&dst, src); err != nil { + t.Errorf("Error while merging %s", err) + } + if dst.Created == src.Created { + t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created) + } +} + +func TestIssue38MergeWithOverwrite(t *testing.T) { + dst := structWithoutTimePointer{ + time.Now(), + } + + expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + src := structWithoutTimePointer{ + expected, + } + if err := MergeWithOverwrite(&dst, src); err != nil { + t.Errorf("Error while merging %s", err) + } + if dst.Created != src.Created { + t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created) + } +} diff --git a/vendor/github.com/imdario/mergo/issue50_test.go b/vendor/github.com/imdario/mergo/issue50_test.go new file mode 100644 index 0000000000..89aa36345c --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue50_test.go @@ -0,0 +1,18 @@ +package mergo + +import ( + "testing" + "time" +) + +type testStruct struct { + time.Duration +} + +func TestIssue50Merge(t *testing.T) { + to := testStruct{} + from := testStruct{} + if err := Merge(&to, from); err != nil { + t.Fail() + } +} diff --git a/vendor/github.com/imdario/mergo/issue52_test.go b/vendor/github.com/imdario/mergo/issue52_test.go new file mode 100644 index 0000000000..62cd9fa7c0 --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue52_test.go @@ -0,0 +1,99 @@ +package mergo + +import ( + "reflect" + "testing" + "time" +) + +type structWithTime struct { + Birth time.Time +} + +type timeTransfomer struct { + overwrite bool +} + +func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { + if typ == reflect.TypeOf(time.Time{}) { + return func(dst, src reflect.Value) error { + if dst.CanSet() { + if t.overwrite { + isZero := src.MethodByName("IsZero") + result := isZero.Call([]reflect.Value{}) + if !result[0].Bool() { + dst.Set(src) + } + } else { + isZero := dst.MethodByName("IsZero") + result := isZero.Call([]reflect.Value{}) + if result[0].Bool() { + dst.Set(src) + } + } + } + return nil + } + } + return nil +} + +func TestOverwriteZeroSrcTime(t *testing.T) { + now := time.Now() + dst := structWithTime{now} + src := structWithTime{} + if err := MergeWithOverwrite(&dst, src); err != nil { + t.FailNow() + } + if !dst.Birth.IsZero() { + t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) + } +} + +func TestOverwriteZeroSrcTimeWithTransformer(t *testing.T) { + now := time.Now() + dst := structWithTime{now} + src := structWithTime{} + if err := MergeWithOverwrite(&dst, src, WithTransformers(timeTransfomer{true})); err != nil { + t.FailNow() + } + if dst.Birth.IsZero() { + t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) + } +} + +func TestOverwriteZeroDstTime(t *testing.T) { + now := time.Now() + dst := structWithTime{} + src := structWithTime{now} + if err := MergeWithOverwrite(&dst, src); err != nil { + t.FailNow() + } + if dst.Birth.IsZero() { + t.Fatalf("dst should have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{}) + } +} + +func TestZeroDstTime(t *testing.T) { + now := time.Now() + dst := structWithTime{} + src := structWithTime{now} + if err := Merge(&dst, src); err != nil { + t.FailNow() + } + if !dst.Birth.IsZero() { + t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{}) + } +} + +func TestZeroDstTimeWithTransformer(t *testing.T) { + now := time.Now() + dst := structWithTime{} + src := structWithTime{now} + if err := Merge(&dst, src, WithTransformers(timeTransfomer{})); err != nil { + t.FailNow() + } + if dst.Birth.IsZero() { + t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) + } +} diff --git a/vendor/github.com/imdario/mergo/issue61_test.go b/vendor/github.com/imdario/mergo/issue61_test.go new file mode 100644 index 0000000000..8efa5e4570 --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue61_test.go @@ -0,0 +1,20 @@ +package mergo + +import ( + "reflect" + "testing" +) + +func TestIssue61MergeNilMap(t *testing.T) { + type T struct { + I map[string][]string + } + t1 := T{} + t2 := T{I: map[string][]string{"hi": {"there"}}} + if err := Merge(&t1, t2); err != nil { + t.Fail() + } + if !reflect.DeepEqual(t2, T{I: map[string][]string{"hi": {"there"}}}) { + t.FailNow() + } +} diff --git a/vendor/github.com/imdario/mergo/issue64_test.go b/vendor/github.com/imdario/mergo/issue64_test.go new file mode 100644 index 0000000000..32382bef16 --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue64_test.go @@ -0,0 +1,38 @@ +package mergo + +import ( + "testing" +) + +type Student struct { + Name string + Books []string +} + +var testData = []struct { + S1 Student + S2 Student + ExpectedSlice []string +}{ + {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"a", "B"}}, + {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}}, + {Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}}, + {Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}}, +} + +func TestIssue64MergeSliceWithOverride(t *testing.T) { + for _, data := range testData { + err := Merge(&data.S2, data.S1, WithOverride) + if err != nil { + t.Errorf("Error while merging %s", err) + } + if len(data.S2.Books) != len(data.ExpectedSlice) { + t.Fatalf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice)) + } + for i, val := range data.S2.Books { + if val != data.ExpectedSlice[i] { + t.Fatalf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val) + } + } + } +} diff --git a/vendor/github.com/imdario/mergo/issue66_test.go b/vendor/github.com/imdario/mergo/issue66_test.go new file mode 100644 index 0000000000..9e4bccedcb --- /dev/null +++ b/vendor/github.com/imdario/mergo/issue66_test.go @@ -0,0 +1,48 @@ +package mergo + +import ( + "testing" +) + +type PrivateSliceTest66 struct { + PublicStrings []string + privateStrings []string +} + +func TestPrivateSlice(t *testing.T) { + p1 := PrivateSliceTest66{ + PublicStrings: []string{"one", "two", "three"}, + privateStrings: []string{"four", "five"}, + } + p2 := PrivateSliceTest66{ + PublicStrings: []string{"six", "seven"}, + } + if err := Merge(&p1, p2); err != nil { + t.Fatalf("Error during the merge: %v", err) + } + if len(p1.PublicStrings) != 3 { + t.Error("5 elements should be in 'PublicStrings' field") + } + if len(p1.privateStrings) != 2 { + t.Error("2 elements should be in 'privateStrings' field") + } +} + +func TestPrivateSliceWithAppendSlice(t *testing.T) { + p1 := PrivateSliceTest66{ + PublicStrings: []string{"one", "two", "three"}, + privateStrings: []string{"four", "five"}, + } + p2 := PrivateSliceTest66{ + PublicStrings: []string{"six", "seven"}, + } + if err := Merge(&p1, p2, WithAppendSlice); err != nil { + t.Fatalf("Error during the merge: %v", err) + } + if len(p1.PublicStrings) != 5 { + t.Error("5 elements should be in 'PublicStrings' field") + } + if len(p1.privateStrings) != 2 { + t.Error("2 elements should be in 'privateStrings' field") + } +} diff --git a/vendor/github.com/imdario/mergo/map.go b/vendor/github.com/imdario/mergo/map.go new file mode 100644 index 0000000000..6ea38e636b --- /dev/null +++ b/vendor/github.com/imdario/mergo/map.go @@ -0,0 +1,174 @@ +// Copyright 2014 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "fmt" + "reflect" + "unicode" + "unicode/utf8" +) + +func changeInitialCase(s string, mapper func(rune) rune) string { + if s == "" { + return s + } + r, n := utf8.DecodeRuneInString(s) + return string(mapper(r)) + s[n:] +} + +func isExported(field reflect.StructField) bool { + r, _ := utf8.DecodeRuneInString(field.Name) + return r >= 'A' && r <= 'Z' +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { + overwrite := config.Overwrite + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + zeroValue := reflect.Value{} + switch dst.Kind() { + case reflect.Map: + dstMap := dst.Interface().(map[string]interface{}) + for i, n := 0, src.NumField(); i < n; i++ { + srcType := src.Type() + field := srcType.Field(i) + if !isExported(field) { + continue + } + fieldName := field.Name + fieldName = changeInitialCase(fieldName, unicode.ToLower) + if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) { + dstMap[fieldName] = src.Field(i).Interface() + } + } + case reflect.Ptr: + if dst.IsNil() { + v := reflect.New(dst.Type().Elem()) + dst.Set(v) + } + dst = dst.Elem() + fallthrough + case reflect.Struct: + srcMap := src.Interface().(map[string]interface{}) + for key := range srcMap { + srcValue := srcMap[key] + fieldName := changeInitialCase(key, unicode.ToUpper) + dstElement := dst.FieldByName(fieldName) + if dstElement == zeroValue { + // We discard it because the field doesn't exist. + continue + } + srcElement := reflect.ValueOf(srcValue) + dstKind := dstElement.Kind() + srcKind := srcElement.Kind() + if srcKind == reflect.Ptr && dstKind != reflect.Ptr { + srcElement = srcElement.Elem() + srcKind = reflect.TypeOf(srcElement.Interface()).Kind() + } else if dstKind == reflect.Ptr { + // Can this work? I guess it can't. + if srcKind != reflect.Ptr && srcElement.CanAddr() { + srcPtr := srcElement.Addr() + srcElement = reflect.ValueOf(srcPtr) + srcKind = reflect.Ptr + } + } + + if !srcElement.IsValid() { + continue + } + if srcKind == dstKind { + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface { + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } else if srcKind == reflect.Map { + if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } else { + return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) + } + } + } + return +} + +// Map sets fields' values in dst from src. +// src can be a map with string keys or a struct. dst must be the opposite: +// if src is a map, dst must be a valid pointer to struct. If src is a struct, +// dst must be map[string]interface{}. +// It won't merge unexported (private) fields and will do recursively +// any exported field. +// If dst is a map, keys will be src fields' names in lower camel case. +// Missing key in src that doesn't match a field in dst will be skipped. This +// doesn't apply if dst is a map. +// This is separated method from Merge because it is cleaner and it keeps sane +// semantics: merging equal types, mapping different (restricted) types. +func Map(dst, src interface{}, opts ...func(*Config)) error { + return _map(dst, src, opts...) +} + +// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by +// non-empty src attribute values. +// Deprecated: Use Map(…) with WithOverride +func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { + return _map(dst, src, append(opts, WithOverride)...) +} + +func _map(dst, src interface{}, opts ...func(*Config)) error { + var ( + vDst, vSrc reflect.Value + err error + ) + config := &Config{} + + for _, opt := range opts { + opt(config) + } + + if vDst, vSrc, err = resolveValues(dst, src); err != nil { + return err + } + // To be friction-less, we redirect equal-type arguments + // to deepMerge. Only because arguments can be anything. + if vSrc.Kind() == vDst.Kind() { + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) + } + switch vSrc.Kind() { + case reflect.Struct: + if vDst.Kind() != reflect.Map { + return ErrExpectedMapAsDestination + } + case reflect.Map: + if vDst.Kind() != reflect.Struct { + return ErrExpectedStructAsDestination + } + default: + return ErrNotSupported + } + return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config) +} diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/github.com/imdario/mergo/merge.go new file mode 100644 index 0000000000..706b22069c --- /dev/null +++ b/vendor/github.com/imdario/mergo/merge.go @@ -0,0 +1,245 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "reflect" +) + +func hasExportedField(dst reflect.Value) (exported bool) { + for i, n := 0, dst.NumField(); i < n; i++ { + field := dst.Type().Field(i) + if field.Anonymous && dst.Field(i).Kind() == reflect.Struct { + exported = exported || hasExportedField(dst.Field(i)) + } else { + exported = exported || len(field.PkgPath) == 0 + } + } + return +} + +type Config struct { + Overwrite bool + AppendSlice bool + Transformers Transformers +} + +type Transformers interface { + Transformer(reflect.Type) func(dst, src reflect.Value) error +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { + overwrite := config.Overwrite + + if !src.IsValid() { + return + } + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + + if config.Transformers != nil && !isEmptyValue(dst) { + if fn := config.Transformers.Transformer(dst.Type()); fn != nil { + err = fn(dst, src) + return + } + } + + switch dst.Kind() { + case reflect.Struct: + if hasExportedField(dst) { + for i, n := 0, dst.NumField(); i < n; i++ { + if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { + return + } + } + } else { + if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } + case reflect.Map: + if dst.IsNil() && !src.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) + } + for _, key := range src.MapKeys() { + srcElement := src.MapIndex(key) + if !srcElement.IsValid() { + continue + } + dstElement := dst.MapIndex(key) + switch srcElement.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice: + if srcElement.IsNil() { + continue + } + fallthrough + default: + if !srcElement.CanInterface() { + continue + } + switch reflect.TypeOf(srcElement.Interface()).Kind() { + case reflect.Struct: + fallthrough + case reflect.Ptr: + fallthrough + case reflect.Map: + srcMapElm := srcElement + dstMapElm := dstElement + if srcMapElm.CanInterface() { + srcMapElm = reflect.ValueOf(srcMapElm.Interface()) + if dstMapElm.IsValid() { + dstMapElm = reflect.ValueOf(dstMapElm.Interface()) + } + } + if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil { + return + } + case reflect.Slice: + srcSlice := reflect.ValueOf(srcElement.Interface()) + + var dstSlice reflect.Value + if !dstElement.IsValid() || dstElement.IsNil() { + dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len()) + } else { + dstSlice = reflect.ValueOf(dstElement.Interface()) + } + + if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + dstSlice = srcSlice + } else if config.AppendSlice { + dstSlice = reflect.AppendSlice(dstSlice, srcSlice) + } + dst.SetMapIndex(key, dstSlice) + } + } + if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map { + continue + } + + if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) { + if dst.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) + } + dst.SetMapIndex(key, srcElement) + } + } + case reflect.Slice: + if !dst.CanSet() { + break + } + if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + dst.Set(src) + } else if config.AppendSlice { + dst.Set(reflect.AppendSlice(dst, src)) + } + case reflect.Ptr: + fallthrough + case reflect.Interface: + if src.IsNil() { + break + } + if src.Kind() != reflect.Interface { + if dst.IsNil() || overwrite { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } else if src.Kind() == reflect.Ptr { + if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { + return + } + } else if dst.Elem().Type() == src.Type() { + if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil { + return + } + } else { + return ErrDifferentArgumentsTypes + } + break + } + if dst.IsNil() || overwrite { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { + return + } + default: + if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } + return +} + +// Merge will fill any empty for value type attributes on the dst struct using corresponding +// src attributes if they themselves are not empty. dst and src must be valid same-type structs +// and dst must be a pointer to struct. +// It won't merge unexported (private) fields and will do recursively any exported field. +func Merge(dst, src interface{}, opts ...func(*Config)) error { + return merge(dst, src, opts...) +} + +// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by +// non-empty src attribute values. +// Deprecated: use Merge(…) with WithOverride +func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { + return merge(dst, src, append(opts, WithOverride)...) +} + +// WithTransformers adds transformers to merge, allowing to customize the merging of some types. +func WithTransformers(transformers Transformers) func(*Config) { + return func(config *Config) { + config.Transformers = transformers + } +} + +// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values. +func WithOverride(config *Config) { + config.Overwrite = true +} + +// WithAppendSlice will make merge append slices instead of overwriting it +func WithAppendSlice(config *Config) { + config.AppendSlice = true +} + +func merge(dst, src interface{}, opts ...func(*Config)) error { + var ( + vDst, vSrc reflect.Value + err error + ) + + config := &Config{} + + for _, opt := range opts { + opt(config) + } + + if vDst, vSrc, err = resolveValues(dst, src); err != nil { + return err + } + if vDst.Type() != vSrc.Type() { + return ErrDifferentArgumentsTypes + } + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) +} diff --git a/vendor/github.com/imdario/mergo/merge_appendslice_test.go b/vendor/github.com/imdario/mergo/merge_appendslice_test.go new file mode 100644 index 0000000000..a780f34a3c --- /dev/null +++ b/vendor/github.com/imdario/mergo/merge_appendslice_test.go @@ -0,0 +1,33 @@ +package mergo + +import ( + "testing" +) + +var testDataS = []struct { + S1 Student + S2 Student + ExpectedSlice []string +}{ + {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"1", "a", "B"}}, + {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}}, + {Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}}, + {Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}}, +} + +func TestMergeSliceWithOverrideWithAppendSlice(t *testing.T) { + for _, data := range testDataS { + err := Merge(&data.S2, data.S1, WithOverride, WithAppendSlice) + if err != nil { + t.Errorf("Error while merging %s", err) + } + if len(data.S2.Books) != len(data.ExpectedSlice) { + t.Fatalf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice)) + } + for i, val := range data.S2.Books { + if val != data.ExpectedSlice[i] { + t.Fatalf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val) + } + } + } +} diff --git a/vendor/github.com/imdario/mergo/merge_test.go b/vendor/github.com/imdario/mergo/merge_test.go new file mode 100644 index 0000000000..5bf808a786 --- /dev/null +++ b/vendor/github.com/imdario/mergo/merge_test.go @@ -0,0 +1,50 @@ +package mergo + +import ( + "reflect" + "testing" +) + +type transformer struct { + m map[reflect.Type]func(dst, src reflect.Value) error +} + +func (s *transformer) Transformer(t reflect.Type) func(dst, src reflect.Value) error { + if fn, ok := s.m[t]; ok { + return fn + } + return nil +} + +type foo struct { + s string + Bar *bar +} + +type bar struct { + i int + s map[string]string +} + +func TestMergeWithTransformerNilStruct(t *testing.T) { + a := foo{s: "foo"} + b := foo{Bar: &bar{i: 2, s: map[string]string{"foo": "bar"}}} + if err := Merge(&a, &b, WithOverride, WithTransformers(&transformer{ + m: map[reflect.Type]func(dst, src reflect.Value) error{ + reflect.TypeOf(&bar{}): func(dst, src reflect.Value) error { + // Do sthg with Elem + t.Log(dst.Elem().FieldByName("i")) + t.Log(src.Elem()) + return nil + }, + }, + })); err != nil { + t.Fatal(err) + } + if a.s != "foo" { + t.Fatalf("b not merged in properly: a.s.Value(%s) != expected(%s)", a.s, "foo") + } + if a.Bar == nil { + t.Fatalf("b not merged in properly: a.Bar shouldn't be nil") + } +} diff --git a/vendor/github.com/imdario/mergo/mergo.go b/vendor/github.com/imdario/mergo/mergo.go new file mode 100644 index 0000000000..a82fea2fdc --- /dev/null +++ b/vendor/github.com/imdario/mergo/mergo.go @@ -0,0 +1,97 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "errors" + "reflect" +) + +// Errors reported by Mergo when it finds invalid arguments. +var ( + ErrNilArguments = errors.New("src and dst must not be nil") + ErrDifferentArgumentsTypes = errors.New("src and dst must be of same type") + ErrNotSupported = errors.New("only structs and maps are supported") + ErrExpectedMapAsDestination = errors.New("dst was expected to be a map") + ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct") +) + +// During deepMerge, must keep track of checks that are +// in progress. The comparison algorithm assumes that all +// checks in progress are true when it reencounters them. +// Visited are stored in a map indexed by 17 * a1 + a2; +type visit struct { + ptr uintptr + typ reflect.Type + next *visit +} + +// From src/pkg/encoding/json/encode.go. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + if v.IsNil() { + return true + } + return isEmptyValue(v.Elem()) + case reflect.Func: + return v.IsNil() + case reflect.Invalid: + return true + } + return false +} + +func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) { + if dst == nil || src == nil { + err = ErrNilArguments + return + } + vDst = reflect.ValueOf(dst).Elem() + if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map { + err = ErrNotSupported + return + } + vSrc = reflect.ValueOf(src) + // We check if vSrc is a pointer to dereference it. + if vSrc.Kind() == reflect.Ptr { + vSrc = vSrc.Elem() + } + return +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) { + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + return // TODO refactor +} diff --git a/vendor/github.com/imdario/mergo/mergo_test.go b/vendor/github.com/imdario/mergo/mergo_test.go new file mode 100644 index 0000000000..d777538431 --- /dev/null +++ b/vendor/github.com/imdario/mergo/mergo_test.go @@ -0,0 +1,733 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mergo + +import ( + "io/ioutil" + "reflect" + "testing" + "time" + + "gopkg.in/yaml.v2" +) + +type simpleTest struct { + Value int +} + +type complexTest struct { + St simpleTest + sz int + ID string +} + +type mapTest struct { + M map[int]int +} + +type ifcTest struct { + I interface{} +} + +type moreComplextText struct { + Ct complexTest + St simpleTest + Nt simpleTest +} + +type pointerTest struct { + C *simpleTest +} + +type sliceTest struct { + S []int +} + +func TestKb(t *testing.T) { + type testStruct struct { + Name string + KeyValue map[string]interface{} + } + + akv := make(map[string]interface{}) + akv["Key1"] = "not value 1" + akv["Key2"] = "value2" + a := testStruct{} + a.Name = "A" + a.KeyValue = akv + + bkv := make(map[string]interface{}) + bkv["Key1"] = "value1" + bkv["Key3"] = "value3" + b := testStruct{} + b.Name = "B" + b.KeyValue = bkv + + ekv := make(map[string]interface{}) + ekv["Key1"] = "value1" + ekv["Key2"] = "value2" + ekv["Key3"] = "value3" + expected := testStruct{} + expected.Name = "B" + expected.KeyValue = ekv + + Merge(&b, a) + + if !reflect.DeepEqual(b, expected) { + t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected) + } +} + +func TestNil(t *testing.T) { + if err := Merge(nil, nil); err != ErrNilArguments { + t.Fail() + } +} + +func TestDifferentTypes(t *testing.T) { + a := simpleTest{42} + b := 42 + if err := Merge(&a, b); err != ErrDifferentArgumentsTypes { + t.Fail() + } +} + +func TestSimpleStruct(t *testing.T) { + a := simpleTest{} + b := simpleTest{42} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if a.Value != 42 { + t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value) + } + if !reflect.DeepEqual(a, b) { + t.FailNow() + } +} + +func TestComplexStruct(t *testing.T) { + a := complexTest{} + a.ID = "athing" + b := complexTest{simpleTest{42}, 1, "bthing"} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if a.St.Value != 42 { + t.Fatalf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value) + } + if a.sz == 1 { + t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz) + } + if a.ID == b.ID { + t.Fatalf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID) + } +} + +func TestComplexStructWithOverwrite(t *testing.T) { + a := complexTest{simpleTest{1}, 1, "do-not-overwrite-with-empty-value"} + b := complexTest{simpleTest{42}, 2, ""} + + expect := complexTest{simpleTest{42}, 1, "do-not-overwrite-with-empty-value"} + if err := MergeWithOverwrite(&a, b); err != nil { + t.FailNow() + } + + if !reflect.DeepEqual(a, expect) { + t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", a, expect) + } +} + +func TestPointerStruct(t *testing.T) { + s1 := simpleTest{} + s2 := simpleTest{19} + a := pointerTest{&s1} + b := pointerTest{&s2} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if a.C.Value != b.C.Value { + t.Fatalf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) + } +} + +type embeddingStruct struct { + embeddedStruct +} + +type embeddedStruct struct { + A string +} + +func TestEmbeddedStruct(t *testing.T) { + tests := []struct { + src embeddingStruct + dst embeddingStruct + expected embeddingStruct + }{ + { + src: embeddingStruct{ + embeddedStruct{"foo"}, + }, + dst: embeddingStruct{ + embeddedStruct{""}, + }, + expected: embeddingStruct{ + embeddedStruct{"foo"}, + }, + }, + { + src: embeddingStruct{ + embeddedStruct{""}, + }, + dst: embeddingStruct{ + embeddedStruct{"bar"}, + }, + expected: embeddingStruct{ + embeddedStruct{"bar"}, + }, + }, + { + src: embeddingStruct{ + embeddedStruct{"foo"}, + }, + dst: embeddingStruct{ + embeddedStruct{"bar"}, + }, + expected: embeddingStruct{ + embeddedStruct{"bar"}, + }, + }, + } + + for _, test := range tests { + err := Merge(&test.dst, test.src) + if err != nil { + t.Errorf("unexpected error: %v", err) + continue + } + if !reflect.DeepEqual(test.dst, test.expected) { + t.Errorf("unexpected output\nexpected:\n%+v\nsaw:\n%+v\n", test.expected, test.dst) + } + } +} + +func TestPointerStructNil(t *testing.T) { + a := pointerTest{nil} + b := pointerTest{&simpleTest{19}} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if a.C.Value != b.C.Value { + t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) + } +} + +func testSlice(t *testing.T, a []int, b []int, e []int, opts ...func(*Config)) { + t.Helper() + bc := b + + sa := sliceTest{a} + sb := sliceTest{b} + if err := Merge(&sa, sb, opts...); err != nil { + t.FailNow() + } + if !reflect.DeepEqual(sb.S, bc) { + t.Fatalf("Source slice was modified %d != %d", sb.S, bc) + } + if !reflect.DeepEqual(sa.S, e) { + t.Fatalf("b not merged in a proper way %d != %d", sa.S, e) + } + + ma := map[string][]int{"S": a} + mb := map[string][]int{"S": b} + if err := Merge(&ma, mb, opts...); err != nil { + t.FailNow() + } + if !reflect.DeepEqual(mb["S"], bc) { + t.Fatalf("map value: Source slice was modified %d != %d", mb["S"], bc) + } + if !reflect.DeepEqual(ma["S"], e) { + t.Fatalf("map value: b not merged in a proper way %d != %d", ma["S"], e) + } + + if a == nil { + // test case with missing dst key + ma := map[string][]int{} + mb := map[string][]int{"S": b} + if err := Merge(&ma, mb); err != nil { + t.FailNow() + } + if !reflect.DeepEqual(mb["S"], bc) { + t.Fatalf("missing dst key: Source slice was modified %d != %d", mb["S"], bc) + } + if !reflect.DeepEqual(ma["S"], e) { + t.Fatalf("missing dst key: b not merged in a proper way %d != %d", ma["S"], e) + } + } + + if b == nil { + // test case with missing src key + ma := map[string][]int{"S": a} + mb := map[string][]int{} + if err := Merge(&ma, mb); err != nil { + t.FailNow() + } + if !reflect.DeepEqual(mb["S"], bc) { + t.Fatalf("missing src key: Source slice was modified %d != %d", mb["S"], bc) + } + if !reflect.DeepEqual(ma["S"], e) { + t.Fatalf("missing src key: b not merged in a proper way %d != %d", ma["S"], e) + } + } +} + +func TestSlice(t *testing.T) { + testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}) + testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}) + testSlice(t, []int{1}, []int{2, 3}, []int{1}) + testSlice(t, []int{1}, []int{}, []int{1}) + testSlice(t, []int{1}, nil, []int{1}) + testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}, WithAppendSlice) + testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}, WithAppendSlice) + testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, WithAppendSlice) + testSlice(t, []int{1}, []int{}, []int{1}, WithAppendSlice) + testSlice(t, []int{1}, nil, []int{1}, WithAppendSlice) +} + +func TestEmptyMaps(t *testing.T) { + a := mapTest{} + b := mapTest{ + map[int]int{}, + } + if err := Merge(&a, b); err != nil { + t.Fail() + } + if !reflect.DeepEqual(a, b) { + t.FailNow() + } +} + +func TestEmptyToEmptyMaps(t *testing.T) { + a := mapTest{} + b := mapTest{} + if err := Merge(&a, b); err != nil { + t.Fail() + } + if !reflect.DeepEqual(a, b) { + t.FailNow() + } +} + +func TestEmptyToNotEmptyMaps(t *testing.T) { + a := mapTest{map[int]int{ + 1: 2, + 3: 4, + }} + aa := mapTest{map[int]int{ + 1: 2, + 3: 4, + }} + b := mapTest{ + map[int]int{}, + } + if err := Merge(&a, b); err != nil { + t.Fail() + } + if !reflect.DeepEqual(a, aa) { + t.FailNow() + } +} + +func TestMapsWithOverwrite(t *testing.T) { + m := map[string]simpleTest{ + "a": {}, // overwritten by 16 + "b": {42}, // not overwritten by empty value + "c": {13}, // overwritten by 12 + "d": {61}, + } + n := map[string]simpleTest{ + "a": {16}, + "b": {}, + "c": {12}, + "e": {14}, + } + expect := map[string]simpleTest{ + "a": {16}, + "b": {}, + "c": {12}, + "d": {61}, + "e": {14}, + } + + if err := MergeWithOverwrite(&m, n); err != nil { + t.Fatalf(err.Error()) + } + + if !reflect.DeepEqual(m, expect) { + t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) + } +} + +func TestMaps(t *testing.T) { + m := map[string]simpleTest{ + "a": {}, + "b": {42}, + "c": {13}, + "d": {61}, + } + n := map[string]simpleTest{ + "a": {16}, + "b": {}, + "c": {12}, + "e": {14}, + } + expect := map[string]simpleTest{ + "a": {0}, + "b": {42}, + "c": {13}, + "d": {61}, + "e": {14}, + } + + if err := Merge(&m, n); err != nil { + t.Fatalf(err.Error()) + } + + if !reflect.DeepEqual(m, expect) { + t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) + } + if m["a"].Value != 0 { + t.Fatalf(`n merged in m because I solved non-addressable map values TODO: m["a"].Value(%d) != n["a"].Value(%d)`, m["a"].Value, n["a"].Value) + } + if m["b"].Value != 42 { + t.Fatalf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value) + } + if m["c"].Value != 13 { + t.Fatalf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value) + } +} + +func TestMapsWithNilPointer(t *testing.T) { + m := map[string]*simpleTest{ + "a": nil, + "b": nil, + } + n := map[string]*simpleTest{ + "b": nil, + "c": nil, + } + expect := map[string]*simpleTest{ + "a": nil, + "b": nil, + "c": nil, + } + + if err := Merge(&m, n, WithOverride); err != nil { + t.Fatalf(err.Error()) + } + + if !reflect.DeepEqual(m, expect) { + t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) + } +} + +func TestYAMLMaps(t *testing.T) { + thing := loadYAML("testdata/thing.yml") + license := loadYAML("testdata/license.yml") + ft := thing["fields"].(map[interface{}]interface{}) + fl := license["fields"].(map[interface{}]interface{}) + // license has one extra field (site) and another already existing in thing (author) that Mergo won't override. + expectedLength := len(ft) + len(fl) - 1 + if err := Merge(&license, thing); err != nil { + t.Fatal(err.Error()) + } + currentLength := len(license["fields"].(map[interface{}]interface{})) + if currentLength != expectedLength { + t.Fatalf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength) + } + fields := license["fields"].(map[interface{}]interface{}) + if _, ok := fields["id"]; !ok { + t.Fatalf(`thing not merged in license properly, license must have a new id field from thing`) + } +} + +func TestTwoPointerValues(t *testing.T) { + a := &simpleTest{} + b := &simpleTest{42} + if err := Merge(a, b); err != nil { + t.Fatalf(`Boom. You crossed the streams: %s`, err) + } +} + +func TestMap(t *testing.T) { + a := complexTest{} + a.ID = "athing" + c := moreComplextText{a, simpleTest{}, simpleTest{}} + b := map[string]interface{}{ + "ct": map[string]interface{}{ + "st": map[string]interface{}{ + "value": 42, + }, + "sz": 1, + "id": "bthing", + }, + "st": &simpleTest{144}, // Mapping a reference + "zt": simpleTest{299}, // Mapping a missing field (zt doesn't exist) + "nt": simpleTest{3}, + } + if err := Map(&c, b); err != nil { + t.FailNow() + } + m := b["ct"].(map[string]interface{}) + n := m["st"].(map[string]interface{}) + o := b["st"].(*simpleTest) + p := b["nt"].(simpleTest) + if c.Ct.St.Value != 42 { + t.Fatalf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"]) + } + if c.St.Value != 144 { + t.Fatalf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value) + } + if c.Nt.Value != 3 { + t.Fatalf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value) + } + if c.Ct.sz == 1 { + t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"]) + } + if c.Ct.ID == m["id"] { + t.Fatalf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"]) + } +} + +func TestSimpleMap(t *testing.T) { + a := simpleTest{} + b := map[string]interface{}{ + "value": 42, + } + if err := Map(&a, b); err != nil { + t.FailNow() + } + if a.Value != 42 { + t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"]) + } +} + +func TestIfcMap(t *testing.T) { + a := ifcTest{} + b := ifcTest{42} + if err := Map(&a, b); err != nil { + t.FailNow() + } + if a.I != 42 { + t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) + } + if !reflect.DeepEqual(a, b) { + t.FailNow() + } +} + +func TestIfcMapNoOverwrite(t *testing.T) { + a := ifcTest{13} + b := ifcTest{42} + if err := Map(&a, b); err != nil { + t.FailNow() + } + if a.I != 13 { + t.Fatalf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I) + } +} + +func TestIfcMapWithOverwrite(t *testing.T) { + a := ifcTest{13} + b := ifcTest{42} + if err := MapWithOverwrite(&a, b); err != nil { + t.FailNow() + } + if a.I != 42 { + t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) + } + if !reflect.DeepEqual(a, b) { + t.FailNow() + } +} + +type pointerMapTest struct { + A int + hidden int + B *simpleTest +} + +func TestBackAndForth(t *testing.T) { + pt := pointerMapTest{42, 1, &simpleTest{66}} + m := make(map[string]interface{}) + if err := Map(&m, pt); err != nil { + t.FailNow() + } + var ( + v interface{} + ok bool + ) + if v, ok = m["a"]; v.(int) != pt.A || !ok { + t.Fatalf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A) + } + if v, ok = m["b"]; !ok { + t.Fatalf("pt not merged in properly: B is missing in m") + } + var st *simpleTest + if st = v.(*simpleTest); st.Value != 66 { + t.Fatalf("something went wrong while mapping pt on m, B wasn't copied") + } + bpt := pointerMapTest{} + if err := Map(&bpt, m); err != nil { + t.Fatal(err) + } + if bpt.A != pt.A { + t.Fatalf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A) + } + if bpt.hidden == pt.hidden { + t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden) + } + if bpt.B.Value != pt.B.Value { + t.Fatalf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value) + } +} + +func TestEmbeddedPointerUnpacking(t *testing.T) { + tests := []struct{ input pointerMapTest }{ + {pointerMapTest{42, 1, nil}}, + {pointerMapTest{42, 1, &simpleTest{66}}}, + } + newValue := 77 + m := map[string]interface{}{ + "b": map[string]interface{}{ + "value": newValue, + }, + } + for _, test := range tests { + pt := test.input + if err := MapWithOverwrite(&pt, m); err != nil { + t.FailNow() + } + if pt.B.Value != newValue { + t.Fatalf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue) + } + + } +} + +type structWithTimePointer struct { + Birth *time.Time +} + +func TestTime(t *testing.T) { + now := time.Now() + dataStruct := structWithTimePointer{ + Birth: &now, + } + dataMap := map[string]interface{}{ + "Birth": &now, + } + b := structWithTimePointer{} + if err := Merge(&b, dataStruct); err != nil { + t.FailNow() + } + if b.Birth.IsZero() { + t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) + } + if b.Birth != dataStruct.Birth { + t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) + } + b = structWithTimePointer{} + if err := Map(&b, dataMap); err != nil { + t.FailNow() + } + if b.Birth.IsZero() { + t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"]) + } +} + +type simpleNested struct { + A int +} + +type structWithNestedPtrValueMap struct { + NestedPtrValue map[string]*simpleNested +} + +func TestNestedPtrValueInMap(t *testing.T) { + src := &structWithNestedPtrValueMap{ + NestedPtrValue: map[string]*simpleNested{ + "x": { + A: 1, + }, + }, + } + dst := &structWithNestedPtrValueMap{ + NestedPtrValue: map[string]*simpleNested{ + "x": {}, + }, + } + if err := Map(dst, src); err != nil { + t.FailNow() + } + if dst.NestedPtrValue["x"].A == 0 { + t.Fatalf("Nested Ptr value not merged in properly: dst.NestedPtrValue[\"x\"].A(%v) != src.NestedPtrValue[\"x\"].A(%v)", dst.NestedPtrValue["x"].A, src.NestedPtrValue["x"].A) + } +} + +func loadYAML(path string) (m map[string]interface{}) { + m = make(map[string]interface{}) + raw, _ := ioutil.ReadFile(path) + _ = yaml.Unmarshal(raw, &m) + return +} + +type structWithMap struct { + m map[string]structWithUnexportedProperty +} + +type structWithUnexportedProperty struct { + s string +} + +func TestUnexportedProperty(t *testing.T) { + a := structWithMap{map[string]structWithUnexportedProperty{ + "key": {"hello"}, + }} + b := structWithMap{map[string]structWithUnexportedProperty{ + "key": {"hi"}, + }} + defer func() { + if r := recover(); r != nil { + t.Errorf("Should not have panicked") + } + }() + Merge(&a, b) +} + +type structWithBoolPointer struct { + C *bool +} + +func TestBooleanPointer(t *testing.T) { + bt, bf := true, false + src := structWithBoolPointer{ + &bt, + } + dst := structWithBoolPointer{ + &bf, + } + if err := Merge(&dst, src); err != nil { + t.FailNow() + } + if dst.C == src.C { + t.Fatalf("dst.C should be a different pointer than src.C") + } + if *dst.C != *src.C { + t.Fatalf("dst.C should be true") + } +} diff --git a/vendor/github.com/imdario/mergo/pr80_test.go b/vendor/github.com/imdario/mergo/pr80_test.go new file mode 100644 index 0000000000..0b3220f3bc --- /dev/null +++ b/vendor/github.com/imdario/mergo/pr80_test.go @@ -0,0 +1,18 @@ +package mergo + +import ( + "testing" +) + +type mapInterface map[string]interface{} + +func TestMergeMapsEmptyString(t *testing.T) { + a := mapInterface{"s": ""} + b := mapInterface{"s": "foo"} + if err := Merge(&a, b); err != nil { + t.Fatal(err) + } + if a["s"] != "foo" { + t.Fatalf("b not merged in properly: a.s.Value(%s) != expected(%s)", a["s"], "foo") + } +} diff --git a/vendor/github.com/imdario/mergo/pr81_test.go b/vendor/github.com/imdario/mergo/pr81_test.go new file mode 100644 index 0000000000..e90e923feb --- /dev/null +++ b/vendor/github.com/imdario/mergo/pr81_test.go @@ -0,0 +1,42 @@ +package mergo + +import ( + "testing" +) + +func TestMapInterfaceWithMultipleLayer(t *testing.T) { + m1 := map[string]interface{}{ + "k1": map[string]interface{}{ + "k1.1": "v1", + }, + } + + m2 := map[string]interface{}{ + "k1": map[string]interface{}{ + "k1.1": "v2", + "k1.2": "v3", + }, + } + + if err := Map(&m1, m2, WithOverride); err != nil { + t.Fatalf("Error merging: %v", err) + } + + // Check overwrite of sub map works + expected := "v2" + actual := m1["k1"].(map[string]interface{})["k1.1"].(string) + if actual != expected { + t.Fatalf("Expected %v but got %v", + expected, + actual) + } + + // Check new key is merged + expected = "v3" + actual = m1["k1"].(map[string]interface{})["k1.2"].(string) + if actual != expected { + t.Fatalf("Expected %v but got %v", + expected, + actual) + } +} diff --git a/vendor/github.com/imdario/mergo/testdata/license.yml b/vendor/github.com/imdario/mergo/testdata/license.yml new file mode 100644 index 0000000000..2f1ad0082b --- /dev/null +++ b/vendor/github.com/imdario/mergo/testdata/license.yml @@ -0,0 +1,4 @@ +import: ../../../../fossene/db/schema/thing.yml +fields: + site: string + author: root diff --git a/vendor/github.com/imdario/mergo/testdata/thing.yml b/vendor/github.com/imdario/mergo/testdata/thing.yml new file mode 100644 index 0000000000..1a71041250 --- /dev/null +++ b/vendor/github.com/imdario/mergo/testdata/thing.yml @@ -0,0 +1,6 @@ +fields: + id: int + name: string + parent: ref "datu:thing" + status: enum(draft, public, private) + author: updater diff --git a/vendor/golang.org/x/oauth2/AUTHORS b/vendor/golang.org/x/oauth2/AUTHORS new file mode 100644 index 0000000000..15167cd746 --- /dev/null +++ b/vendor/golang.org/x/oauth2/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTING.md b/vendor/golang.org/x/oauth2/CONTRIBUTING.md new file mode 100644 index 0000000000..dfbed62cf5 --- /dev/null +++ b/vendor/golang.org/x/oauth2/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing to Go + +Go is an open source project. + +It is the work of hundreds of contributors. We appreciate your help! + +## Filing issues + +When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. +The gophers there will answer or ask you to file an issue if you've tripped over a bug. + +## Contributing code + +Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) +before sending patches. + +Unless otherwise noted, the Go source files are distributed under +the BSD-style license found in the LICENSE file. diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTORS b/vendor/golang.org/x/oauth2/CONTRIBUTORS new file mode 100644 index 0000000000..1c4577e968 --- /dev/null +++ b/vendor/golang.org/x/oauth2/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/oauth2/LICENSE b/vendor/golang.org/x/oauth2/LICENSE new file mode 100644 index 0000000000..6a66aea5ea --- /dev/null +++ b/vendor/golang.org/x/oauth2/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/oauth2/README.md b/vendor/golang.org/x/oauth2/README.md new file mode 100644 index 0000000000..eb8dcee179 --- /dev/null +++ b/vendor/golang.org/x/oauth2/README.md @@ -0,0 +1,77 @@ +# OAuth2 for Go + +[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2) +[![GoDoc](https://godoc.org/golang.org/x/oauth2?status.svg)](https://godoc.org/golang.org/x/oauth2) + +oauth2 package contains a client implementation for OAuth 2.0 spec. + +## Installation + +~~~~ +go get golang.org/x/oauth2 +~~~~ + +Or you can manually git clone the repository to +`$(go env GOPATH)/src/golang.org/x/oauth2`. + +See godoc for further documentation and examples. + +* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2) +* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google) + + +## App Engine + +In change 96e89be (March 2015), we removed the `oauth2.Context2` type in favor +of the [`context.Context`](https://golang.org/x/net/context#Context) type from +the `golang.org/x/net/context` package + +This means it's no longer possible to use the "Classic App Engine" +`appengine.Context` type with the `oauth2` package. (You're using +Classic App Engine if you import the package `"appengine"`.) + +To work around this, you may use the new `"google.golang.org/appengine"` +package. This package has almost the same API as the `"appengine"` package, +but it can be fetched with `go get` and used on "Managed VMs" and well as +Classic App Engine. + +See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app) +for information on updating your app. + +If you don't want to update your entire app to use the new App Engine packages, +you may use both sets of packages in parallel, using only the new packages +with the `oauth2` package. + +```go +import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + newappengine "google.golang.org/appengine" + newurlfetch "google.golang.org/appengine/urlfetch" + + "appengine" +) + +func handler(w http.ResponseWriter, r *http.Request) { + var c appengine.Context = appengine.NewContext(r) + c.Infof("Logging a message with the old package") + + var ctx context.Context = newappengine.NewContext(r) + client := &http.Client{ + Transport: &oauth2.Transport{ + Source: google.AppEngineTokenSource(ctx, "scope"), + Base: &newurlfetch.Transport{Context: ctx}, + }, + } + client.Get("...") +} +``` + +## Report Issues / Send Patches + +This repository uses Gerrit for code changes. To learn how to submit changes to +this repository, see https://golang.org/doc/contribute.html. + +The main issue tracker for the oauth2 repository is located at +https://github.com/golang/oauth2/issues. diff --git a/vendor/golang.org/x/oauth2/amazon/amazon.go b/vendor/golang.org/x/oauth2/amazon/amazon.go new file mode 100644 index 0000000000..d21da11af9 --- /dev/null +++ b/vendor/golang.org/x/oauth2/amazon/amazon.go @@ -0,0 +1,16 @@ +// Copyright 2017 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package amazon provides constants for using OAuth2 to access Amazon. +package amazon + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Amazon's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.amazon.com/ap/oa", + TokenURL: "https://api.amazon.com/auth/o2/token", +} diff --git a/vendor/golang.org/x/oauth2/bitbucket/bitbucket.go b/vendor/golang.org/x/oauth2/bitbucket/bitbucket.go new file mode 100644 index 0000000000..44af1f1a9c --- /dev/null +++ b/vendor/golang.org/x/oauth2/bitbucket/bitbucket.go @@ -0,0 +1,16 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bitbucket provides constants for using OAuth2 to access Bitbucket. +package bitbucket + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Bitbucket's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://bitbucket.org/site/oauth2/authorize", + TokenURL: "https://bitbucket.org/site/oauth2/access_token", +} diff --git a/vendor/golang.org/x/oauth2/cern/cern.go b/vendor/golang.org/x/oauth2/cern/cern.go new file mode 100644 index 0000000000..8be718078a --- /dev/null +++ b/vendor/golang.org/x/oauth2/cern/cern.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cern provides constants for using OAuth2 to access CERN services. +package cern // import "golang.org/x/oauth2/cern" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is CERN's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://oauth.web.cern.ch/OAuth/Authorize", + TokenURL: "https://oauth.web.cern.ch/OAuth/Token", +} diff --git a/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go b/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go new file mode 100644 index 0000000000..c4e840d223 --- /dev/null +++ b/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go @@ -0,0 +1,109 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package clientcredentials implements the OAuth2.0 "client credentials" token flow, +// also known as the "two-legged OAuth 2.0". +// +// This should be used when the client is acting on its own behalf or when the client +// is the resource owner. It may also be used when requesting access to protected +// resources based on an authorization previously arranged with the authorization +// server. +// +// See https://tools.ietf.org/html/rfc6749#section-4.4 +package clientcredentials // import "golang.org/x/oauth2/clientcredentials" + +import ( + "fmt" + "net/http" + "net/url" + "strings" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" +) + +// Config describes a 2-legged OAuth2 flow, with both the +// client application information and the server's endpoint URLs. +type Config struct { + // ClientID is the application's ID. + ClientID string + + // ClientSecret is the application's secret. + ClientSecret string + + // TokenURL is the resource server's token endpoint + // URL. This is a constant specific to each server. + TokenURL string + + // Scope specifies optional requested permissions. + Scopes []string + + // EndpointParams specifies additional parameters for requests to the token endpoint. + EndpointParams url.Values +} + +// Token uses client credentials to retrieve a token. +// The HTTP client to use is derived from the context. +// If nil, http.DefaultClient is used. +func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) { + return c.TokenSource(ctx).Token() +} + +// Client returns an HTTP client using the provided token. +// The token will auto-refresh as necessary. The underlying +// HTTP transport will be obtained using the provided context. +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) +} + +// TokenSource returns a TokenSource that returns t until t expires, +// automatically refreshing it as necessary using the provided context and the +// client ID and client secret. +// +// Most users will use Config.Client instead. +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + source := &tokenSource{ + ctx: ctx, + conf: c, + } + return oauth2.ReuseTokenSource(nil, source) +} + +type tokenSource struct { + ctx context.Context + conf *Config +} + +// Token refreshes the token by using a new client credentials request. +// tokens received this way do not include a refresh token +func (c *tokenSource) Token() (*oauth2.Token, error) { + v := url.Values{ + "grant_type": {"client_credentials"}, + } + if len(c.conf.Scopes) > 0 { + v.Set("scope", strings.Join(c.conf.Scopes, " ")) + } + for k, p := range c.conf.EndpointParams { + if _, ok := v[k]; ok { + return nil, fmt.Errorf("oauth2: cannot overwrite parameter %q", k) + } + v[k] = p + } + tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v) + if err != nil { + if rErr, ok := err.(*internal.RetrieveError); ok { + return nil, (*oauth2.RetrieveError)(rErr) + } + return nil, err + } + t := &oauth2.Token{ + AccessToken: tk.AccessToken, + TokenType: tk.TokenType, + RefreshToken: tk.RefreshToken, + Expiry: tk.Expiry, + } + return t.WithExtra(tk.Raw), nil +} diff --git a/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials_test.go b/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials_test.go new file mode 100644 index 0000000000..108520c16e --- /dev/null +++ b/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials_test.go @@ -0,0 +1,97 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package clientcredentials + +import ( + "context" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func newConf(serverURL string) *Config { + return &Config{ + ClientID: "CLIENT_ID", + ClientSecret: "CLIENT_SECRET", + Scopes: []string{"scope1", "scope2"}, + TokenURL: serverURL + "/token", + EndpointParams: url.Values{"audience": {"audience1"}}, + } +} + +type mockTransport struct { + rt func(req *http.Request) (resp *http.Response, err error) +} + +func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + return t.rt(req) +} + +func TestTokenRequest(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() != "/token" { + t.Errorf("authenticate client request URL = %q; want %q", r.URL, "/token") + } + headerAuth := r.Header.Get("Authorization") + if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" { + t.Errorf("Unexpected authorization header, %v is found.", headerAuth) + } + if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want { + t.Errorf("Content-Type header = %q; want %q", got, want) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + r.Body.Close() + } + if err != nil { + t.Errorf("failed reading request body: %s.", err) + } + if string(body) != "audience=audience1&grant_type=client_credentials&scope=scope1+scope2" { + t.Errorf("payload = %q; want %q", string(body), "grant_type=client_credentials&scope=scope1+scope2") + } + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&token_type=bearer")) + })) + defer ts.Close() + conf := newConf(ts.URL) + tok, err := conf.Token(context.Background()) + if err != nil { + t.Error(err) + } + if !tok.Valid() { + t.Fatalf("token invalid. got: %#v", tok) + } + if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { + t.Errorf("Access token = %q; want %q", tok.AccessToken, "90d64460d14870c08c81352a05dedd3465940a7c") + } + if tok.TokenType != "bearer" { + t.Errorf("token type = %q; want %q", tok.TokenType, "bearer") + } +} + +func TestTokenRefreshRequest(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() == "/somethingelse" { + return + } + if r.URL.String() != "/token" { + t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL) + } + headerContentType := r.Header.Get("Content-Type") + if headerContentType != "application/x-www-form-urlencoded" { + t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + } + body, _ := ioutil.ReadAll(r.Body) + if string(body) != "audience=audience1&grant_type=client_credentials&scope=scope1+scope2" { + t.Errorf("Unexpected refresh token payload, %v is found.", string(body)) + } + })) + defer ts.Close() + conf := newConf(ts.URL) + c := conf.Client(context.Background()) + c.Get(ts.URL + "/somethingelse") +} diff --git a/vendor/golang.org/x/oauth2/example_test.go b/vendor/golang.org/x/oauth2/example_test.go new file mode 100644 index 0000000000..fc2f793b26 --- /dev/null +++ b/vendor/golang.org/x/oauth2/example_test.go @@ -0,0 +1,89 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2_test + +import ( + "context" + "fmt" + "log" + "net/http" + "time" + + "golang.org/x/oauth2" +) + +func ExampleConfig() { + ctx := context.Background() + conf := &oauth2.Config{ + ClientID: "YOUR_CLIENT_ID", + ClientSecret: "YOUR_CLIENT_SECRET", + Scopes: []string{"SCOPE1", "SCOPE2"}, + Endpoint: oauth2.Endpoint{ + AuthURL: "https://provider.com/o/oauth2/auth", + TokenURL: "https://provider.com/o/oauth2/token", + }, + } + + // Redirect user to consent page to ask for permission + // for the scopes specified above. + url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline) + fmt.Printf("Visit the URL for the auth dialog: %v", url) + + // Use the authorization code that is pushed to the redirect + // URL. Exchange will do the handshake to retrieve the + // initial access token. The HTTP Client returned by + // conf.Client will refresh the token as necessary. + var code string + if _, err := fmt.Scan(&code); err != nil { + log.Fatal(err) + } + tok, err := conf.Exchange(ctx, code) + if err != nil { + log.Fatal(err) + } + + client := conf.Client(ctx, tok) + client.Get("...") +} + +func ExampleConfig_customHTTP() { + ctx := context.Background() + + conf := &oauth2.Config{ + ClientID: "YOUR_CLIENT_ID", + ClientSecret: "YOUR_CLIENT_SECRET", + Scopes: []string{"SCOPE1", "SCOPE2"}, + Endpoint: oauth2.Endpoint{ + TokenURL: "https://provider.com/o/oauth2/token", + AuthURL: "https://provider.com/o/oauth2/auth", + }, + } + + // Redirect user to consent page to ask for permission + // for the scopes specified above. + url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline) + fmt.Printf("Visit the URL for the auth dialog: %v", url) + + // Use the authorization code that is pushed to the redirect + // URL. Exchange will do the handshake to retrieve the + // initial access token. The HTTP Client returned by + // conf.Client will refresh the token as necessary. + var code string + if _, err := fmt.Scan(&code); err != nil { + log.Fatal(err) + } + + // Use the custom HTTP client when requesting a token. + httpClient := &http.Client{Timeout: 2 * time.Second} + ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient) + + tok, err := conf.Exchange(ctx, code) + if err != nil { + log.Fatal(err) + } + + client := conf.Client(ctx, tok) + _ = client +} diff --git a/vendor/golang.org/x/oauth2/facebook/facebook.go b/vendor/golang.org/x/oauth2/facebook/facebook.go new file mode 100644 index 0000000000..14c801a2a1 --- /dev/null +++ b/vendor/golang.org/x/oauth2/facebook/facebook.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package facebook provides constants for using OAuth2 to access Facebook. +package facebook // import "golang.org/x/oauth2/facebook" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Facebook's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.facebook.com/dialog/oauth", + TokenURL: "https://graph.facebook.com/oauth/access_token", +} diff --git a/vendor/golang.org/x/oauth2/fitbit/fitbit.go b/vendor/golang.org/x/oauth2/fitbit/fitbit.go new file mode 100644 index 0000000000..b31b82aca8 --- /dev/null +++ b/vendor/golang.org/x/oauth2/fitbit/fitbit.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fitbit provides constants for using OAuth2 to access the Fitbit API. +package fitbit // import "golang.org/x/oauth2/fitbit" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is the Fitbit API's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.fitbit.com/oauth2/authorize", + TokenURL: "https://api.fitbit.com/oauth2/token", +} diff --git a/vendor/golang.org/x/oauth2/foursquare/foursquare.go b/vendor/golang.org/x/oauth2/foursquare/foursquare.go new file mode 100644 index 0000000000..d2fa099023 --- /dev/null +++ b/vendor/golang.org/x/oauth2/foursquare/foursquare.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package foursquare provides constants for using OAuth2 to access Foursquare. +package foursquare // import "golang.org/x/oauth2/foursquare" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Foursquare's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://foursquare.com/oauth2/authorize", + TokenURL: "https://foursquare.com/oauth2/access_token", +} diff --git a/vendor/golang.org/x/oauth2/github/github.go b/vendor/golang.org/x/oauth2/github/github.go new file mode 100644 index 0000000000..f2978015b0 --- /dev/null +++ b/vendor/golang.org/x/oauth2/github/github.go @@ -0,0 +1,16 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package github provides constants for using OAuth2 to access Github. +package github // import "golang.org/x/oauth2/github" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Github's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://github.com/login/oauth/authorize", + TokenURL: "https://github.com/login/oauth/access_token", +} diff --git a/vendor/golang.org/x/oauth2/gitlab/gitlab.go b/vendor/golang.org/x/oauth2/gitlab/gitlab.go new file mode 100644 index 0000000000..1231d75ac8 --- /dev/null +++ b/vendor/golang.org/x/oauth2/gitlab/gitlab.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gitlab provides constants for using OAuth2 to access GitLab. +package gitlab // import "golang.org/x/oauth2/gitlab" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is GitLab's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://gitlab.com/oauth/authorize", + TokenURL: "https://gitlab.com/oauth/token", +} diff --git a/vendor/golang.org/x/oauth2/google/appengine.go b/vendor/golang.org/x/oauth2/google/appengine.go new file mode 100644 index 0000000000..50d918b878 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine.go @@ -0,0 +1,89 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "sort" + "strings" + "sync" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// appengineFlex is set at init time by appengineflex_hook.go. If true, we are on App Engine Flex. +var appengineFlex bool + +// Set at init time by appengine_hook.go. If nil, we're not on App Engine. +var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error) + +// Set at init time by appengine_hook.go. If nil, we're not on App Engine. +var appengineAppIDFunc func(c context.Context) string + +// AppEngineTokenSource returns a token source that fetches tokens +// issued to the current App Engine application's service account. +// If you are implementing a 3-legged OAuth 2.0 flow on App Engine +// that involves user accounts, see oauth2.Config instead. +// +// The provided context must have come from appengine.NewContext. +func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + scopes := append([]string{}, scope...) + sort.Strings(scopes) + return &appEngineTokenSource{ + ctx: ctx, + scopes: scopes, + key: strings.Join(scopes, " "), + } +} + +// aeTokens helps the fetched tokens to be reused until their expiration. +var ( + aeTokensMu sync.Mutex + aeTokens = make(map[string]*tokenLock) // key is space-separated scopes +) + +type tokenLock struct { + mu sync.Mutex // guards t; held while fetching or updating t + t *oauth2.Token +} + +type appEngineTokenSource struct { + ctx context.Context + scopes []string + key string // to aeTokens map; space-separated scopes +} + +func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + + aeTokensMu.Lock() + tok, ok := aeTokens[ts.key] + if !ok { + tok = &tokenLock{} + aeTokens[ts.key] = tok + } + aeTokensMu.Unlock() + + tok.mu.Lock() + defer tok.mu.Unlock() + if tok.t.Valid() { + return tok.t, nil + } + access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...) + if err != nil { + return nil, err + } + tok.t = &oauth2.Token{ + AccessToken: access, + Expiry: exp, + } + return tok.t, nil +} diff --git a/vendor/golang.org/x/oauth2/google/appengine_hook.go b/vendor/golang.org/x/oauth2/google/appengine_hook.go new file mode 100644 index 0000000000..56669eaa98 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine_hook.go @@ -0,0 +1,14 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine appenginevm + +package google + +import "google.golang.org/appengine" + +func init() { + appengineTokenFunc = appengine.AccessToken + appengineAppIDFunc = appengine.AppID +} diff --git a/vendor/golang.org/x/oauth2/google/appengineflex_hook.go b/vendor/golang.org/x/oauth2/google/appengineflex_hook.go new file mode 100644 index 0000000000..5d0231af2d --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengineflex_hook.go @@ -0,0 +1,11 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appenginevm + +package google + +func init() { + appengineFlex = true // Flex doesn't support appengine.AccessToken; depend on metadata server. +} diff --git a/vendor/golang.org/x/oauth2/google/default.go b/vendor/golang.org/x/oauth2/google/default.go new file mode 100644 index 0000000000..a31607437d --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/default.go @@ -0,0 +1,115 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + + "cloud.google.com/go/compute/metadata" + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// DefaultClient returns an HTTP Client that uses the +// DefaultTokenSource to obtain authentication credentials. +func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { + ts, err := DefaultTokenSource(ctx, scope...) + if err != nil { + return nil, err + } + return oauth2.NewClient(ctx, ts), nil +} + +// DefaultTokenSource returns the token source for +// "Application Default Credentials". +// It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource. +func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) { + creds, err := FindDefaultCredentials(ctx, scope...) + if err != nil { + return nil, err + } + return creds.TokenSource, nil +} + +// Common implementation for FindDefaultCredentials. +func findDefaultCredentials(ctx context.Context, scopes []string) (*DefaultCredentials, error) { + // First, try the environment variable. + const envVar = "GOOGLE_APPLICATION_CREDENTIALS" + if filename := os.Getenv(envVar); filename != "" { + creds, err := readCredentialsFile(ctx, filename, scopes) + if err != nil { + return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err) + } + return creds, nil + } + + // Second, try a well-known file. + filename := wellKnownFile() + if creds, err := readCredentialsFile(ctx, filename, scopes); err == nil { + return creds, nil + } else if !os.IsNotExist(err) { + return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err) + } + + // Third, if we're on Google App Engine use those credentials. + if appengineTokenFunc != nil && !appengineFlex { + return &DefaultCredentials{ + ProjectID: appengineAppIDFunc(ctx), + TokenSource: AppEngineTokenSource(ctx, scopes...), + }, nil + } + + // Fourth, if we're on Google Compute Engine use the metadata server. + if metadata.OnGCE() { + id, _ := metadata.ProjectID() + return &DefaultCredentials{ + ProjectID: id, + TokenSource: ComputeTokenSource(""), + }, nil + } + + // None are found; return helpful error. + const url = "https://developers.google.com/accounts/docs/application-default-credentials" + return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url) +} + +// Common implementation for CredentialsFromJSON. +func credentialsFromJSON(ctx context.Context, jsonData []byte, scopes []string) (*DefaultCredentials, error) { + var f credentialsFile + if err := json.Unmarshal(jsonData, &f); err != nil { + return nil, err + } + ts, err := f.tokenSource(ctx, append([]string(nil), scopes...)) + if err != nil { + return nil, err + } + return &DefaultCredentials{ + ProjectID: f.ProjectID, + TokenSource: ts, + JSON: jsonData, + }, nil +} + +func wellKnownFile() string { + const f = "application_default_credentials.json" + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) + } + return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f) +} + +func readCredentialsFile(ctx context.Context, filename string, scopes []string) (*DefaultCredentials, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return CredentialsFromJSON(ctx, b, scopes...) +} diff --git a/vendor/golang.org/x/oauth2/google/doc_go19.go b/vendor/golang.org/x/oauth2/google/doc_go19.go new file mode 100644 index 0000000000..2a86325fe3 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/doc_go19.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +// Package google provides support for making OAuth2 authorized and authenticated +// HTTP requests to Google APIs. It supports the Web server flow, client-side +// credentials, service accounts, Google Compute Engine service accounts, and Google +// App Engine service accounts. +// +// A brief overview of the package follows. For more information, please read +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/docs/application-default-credentials. +// +// OAuth2 Configs +// +// Two functions in this package return golang.org/x/oauth2.Config values from Google credential +// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON, +// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or +// create an http.Client. +// +// +// Credentials +// +// The Credentials type represents Google credentials, including Application Default +// Credentials. +// +// Use FindDefaultCredentials to obtain Application Default Credentials. +// FindDefaultCredentials looks in some well-known places for a credentials file, and +// will call AppEngineTokenSource or ComputeTokenSource as needed. +// +// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials, +// then use the credentials to construct an http.Client or an oauth2.TokenSource. +// +// Use CredentialsFromJSON to obtain credentials from either of the two JSON formats +// described in OAuth2 Configs, above. The TokenSource in the returned value is the +// same as the one obtained from the oauth2.Config returned from ConfigFromJSON or +// JWTConfigFromJSON, but the Credentials may contain additional information +// that is useful is some circumstances. +package google // import "golang.org/x/oauth2/google" diff --git a/vendor/golang.org/x/oauth2/google/doc_not_go19.go b/vendor/golang.org/x/oauth2/google/doc_not_go19.go new file mode 100644 index 0000000000..5c3c6e1481 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/doc_not_go19.go @@ -0,0 +1,43 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +// Package google provides support for making OAuth2 authorized and authenticated +// HTTP requests to Google APIs. It supports the Web server flow, client-side +// credentials, service accounts, Google Compute Engine service accounts, and Google +// App Engine service accounts. +// +// A brief overview of the package follows. For more information, please read +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/docs/application-default-credentials. +// +// OAuth2 Configs +// +// Two functions in this package return golang.org/x/oauth2.Config values from Google credential +// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON, +// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or +// create an http.Client. +// +// +// Credentials +// +// The DefaultCredentials type represents Google Application Default Credentials, as +// well as other forms of credential. +// +// Use FindDefaultCredentials to obtain Application Default Credentials. +// FindDefaultCredentials looks in some well-known places for a credentials file, and +// will call AppEngineTokenSource or ComputeTokenSource as needed. +// +// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials, +// then use the credentials to construct an http.Client or an oauth2.TokenSource. +// +// Use CredentialsFromJSON to obtain credentials from either of the two JSON +// formats described in OAuth2 Configs, above. (The DefaultCredentials returned may +// not be "Application Default Credentials".) The TokenSource in the returned value +// is the same as the one obtained from the oauth2.Config returned from +// ConfigFromJSON or JWTConfigFromJSON, but the DefaultCredentials may contain +// additional information that is useful is some circumstances. +package google // import "golang.org/x/oauth2/google" diff --git a/vendor/golang.org/x/oauth2/google/example_test.go b/vendor/golang.org/x/oauth2/google/example_test.go new file mode 100644 index 0000000000..643f507161 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/example_test.go @@ -0,0 +1,162 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google_test + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + "golang.org/x/oauth2/jwt" + "google.golang.org/appengine" + "google.golang.org/appengine/urlfetch" +) + +func ExampleDefaultClient() { + client, err := google.DefaultClient(oauth2.NoContext, + "https://www.googleapis.com/auth/devstorage.full_control") + if err != nil { + log.Fatal(err) + } + client.Get("...") +} + +func Example_webServer() { + // Your credentials should be obtained from the Google + // Developer Console (https://console.developers.google.com). + conf := &oauth2.Config{ + ClientID: "YOUR_CLIENT_ID", + ClientSecret: "YOUR_CLIENT_SECRET", + RedirectURL: "YOUR_REDIRECT_URL", + Scopes: []string{ + "https://www.googleapis.com/auth/bigquery", + "https://www.googleapis.com/auth/blogger", + }, + Endpoint: google.Endpoint, + } + // Redirect user to Google's consent page to ask for permission + // for the scopes specified above. + url := conf.AuthCodeURL("state") + fmt.Printf("Visit the URL for the auth dialog: %v", url) + + // Handle the exchange code to initiate a transport. + tok, err := conf.Exchange(oauth2.NoContext, "authorization-code") + if err != nil { + log.Fatal(err) + } + client := conf.Client(oauth2.NoContext, tok) + client.Get("...") +} + +func ExampleJWTConfigFromJSON() { + // Your credentials should be obtained from the Google + // Developer Console (https://console.developers.google.com). + // Navigate to your project, then see the "Credentials" page + // under "APIs & Auth". + // To create a service account client, click "Create new Client ID", + // select "Service Account", and click "Create Client ID". A JSON + // key file will then be downloaded to your computer. + data, err := ioutil.ReadFile("/path/to/your-project-key.json") + if err != nil { + log.Fatal(err) + } + conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/bigquery") + if err != nil { + log.Fatal(err) + } + // Initiate an http.Client. The following GET request will be + // authorized and authenticated on the behalf of + // your service account. + client := conf.Client(oauth2.NoContext) + client.Get("...") +} + +func ExampleSDKConfig() { + // The credentials will be obtained from the first account that + // has been authorized with `gcloud auth login`. + conf, err := google.NewSDKConfig("") + if err != nil { + log.Fatal(err) + } + // Initiate an http.Client. The following GET request will be + // authorized and authenticated on the behalf of the SDK user. + client := conf.Client(oauth2.NoContext) + client.Get("...") +} + +func Example_serviceAccount() { + // Your credentials should be obtained from the Google + // Developer Console (https://console.developers.google.com). + conf := &jwt.Config{ + Email: "xxx@developer.gserviceaccount.com", + // The contents of your RSA private key or your PEM file + // that contains a private key. + // If you have a p12 file instead, you + // can use `openssl` to export the private key into a pem file. + // + // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes + // + // The field only supports PEM containers with no passphrase. + // The openssl command will convert p12 keys to passphrase-less PEM containers. + PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."), + Scopes: []string{ + "https://www.googleapis.com/auth/bigquery", + "https://www.googleapis.com/auth/blogger", + }, + TokenURL: google.JWTTokenURL, + // If you would like to impersonate a user, you can + // create a transport with a subject. The following GET + // request will be made on the behalf of user@example.com. + // Optional. + Subject: "user@example.com", + } + // Initiate an http.Client, the following GET request will be + // authorized and authenticated on the behalf of user@example.com. + client := conf.Client(oauth2.NoContext) + client.Get("...") +} + +func ExampleAppEngineTokenSource() { + var req *http.Request // from the ServeHTTP handler + ctx := appengine.NewContext(req) + client := &http.Client{ + Transport: &oauth2.Transport{ + Source: google.AppEngineTokenSource(ctx, "https://www.googleapis.com/auth/bigquery"), + Base: &urlfetch.Transport{ + Context: ctx, + }, + }, + } + client.Get("...") +} + +func ExampleComputeTokenSource() { + client := &http.Client{ + Transport: &oauth2.Transport{ + // Fetch from Google Compute Engine's metadata server to retrieve + // an access token for the provided account. + // If no account is specified, "default" is used. + Source: google.ComputeTokenSource(""), + }, + } + client.Get("...") +} + +func ExampleCredentialsFromJSON() { + ctx := context.Background() + data, err := ioutil.ReadFile("/path/to/key-file.json") + if err != nil { + log.Fatal(err) + } + creds, err := google.CredentialsFromJSON(ctx, data, "https://www.googleapis.com/auth/bigquery") + if err != nil { + log.Fatal(err) + } + _ = creds // TODO: Use creds. +} diff --git a/vendor/golang.org/x/oauth2/google/go19.go b/vendor/golang.org/x/oauth2/google/go19.go new file mode 100644 index 0000000000..4d0318b1e1 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/go19.go @@ -0,0 +1,57 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package google + +import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// Credentials holds Google credentials, including "Application Default Credentials". +// For more details, see: +// https://developers.google.com/accounts/docs/application-default-credentials +type Credentials struct { + ProjectID string // may be empty + TokenSource oauth2.TokenSource + + // JSON contains the raw bytes from a JSON credentials file. + // This field may be nil if authentication is provided by the + // environment and not with a credentials file, e.g. when code is + // running on Google Cloud Platform. + JSON []byte +} + +// DefaultCredentials is the old name of Credentials. +// +// Deprecated: use Credentials instead. +type DefaultCredentials = Credentials + +// FindDefaultCredentials searches for "Application Default Credentials". +// +// It looks for credentials in the following places, +// preferring the first location found: +// +// 1. A JSON file whose path is specified by the +// GOOGLE_APPLICATION_CREDENTIALS environment variable. +// 2. A JSON file in a location known to the gcloud command-line tool. +// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. +// On other systems, $HOME/.config/gcloud/application_default_credentials.json. +// 3. On Google App Engine it uses the appengine.AccessToken function. +// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches +// credentials from the metadata server. +// (In this final case any provided scopes are ignored.) +func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) { + return findDefaultCredentials(ctx, scopes) +} + +// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can +// represent either a Google Developers Console client_credentials.json file (as in +// ConfigFromJSON) or a Google Developers service account key file (as in +// JWTConfigFromJSON). +func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) { + return credentialsFromJSON(ctx, jsonData, scopes) +} diff --git a/vendor/golang.org/x/oauth2/google/google.go b/vendor/golang.org/x/oauth2/google/google.go new file mode 100644 index 0000000000..f7481fbcc6 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/google.go @@ -0,0 +1,192 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "cloud.google.com/go/compute/metadata" + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/jwt" +) + +// Endpoint is Google's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://accounts.google.com/o/oauth2/auth", + TokenURL: "https://accounts.google.com/o/oauth2/token", +} + +// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow. +const JWTTokenURL = "https://accounts.google.com/o/oauth2/token" + +// ConfigFromJSON uses a Google Developers Console client_credentials.json +// file to construct a config. +// client_credentials.json can be downloaded from +// https://console.developers.google.com, under "Credentials". Download the Web +// application credentials in the JSON format and provide the contents of the +// file as jsonKey. +func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) { + type cred struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + RedirectURIs []string `json:"redirect_uris"` + AuthURI string `json:"auth_uri"` + TokenURI string `json:"token_uri"` + } + var j struct { + Web *cred `json:"web"` + Installed *cred `json:"installed"` + } + if err := json.Unmarshal(jsonKey, &j); err != nil { + return nil, err + } + var c *cred + switch { + case j.Web != nil: + c = j.Web + case j.Installed != nil: + c = j.Installed + default: + return nil, fmt.Errorf("oauth2/google: no credentials found") + } + if len(c.RedirectURIs) < 1 { + return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json") + } + return &oauth2.Config{ + ClientID: c.ClientID, + ClientSecret: c.ClientSecret, + RedirectURL: c.RedirectURIs[0], + Scopes: scope, + Endpoint: oauth2.Endpoint{ + AuthURL: c.AuthURI, + TokenURL: c.TokenURI, + }, + }, nil +} + +// JWTConfigFromJSON uses a Google Developers service account JSON key file to read +// the credentials that authorize and authenticate the requests. +// Create a service account on "Credentials" for your project at +// https://console.developers.google.com to download a JSON key file. +func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) { + var f credentialsFile + if err := json.Unmarshal(jsonKey, &f); err != nil { + return nil, err + } + if f.Type != serviceAccountKey { + return nil, fmt.Errorf("google: read JWT from JSON credentials: 'type' field is %q (expected %q)", f.Type, serviceAccountKey) + } + scope = append([]string(nil), scope...) // copy + return f.jwtConfig(scope), nil +} + +// JSON key file types. +const ( + serviceAccountKey = "service_account" + userCredentialsKey = "authorized_user" +) + +// credentialsFile is the unmarshalled representation of a credentials file. +type credentialsFile struct { + Type string `json:"type"` // serviceAccountKey or userCredentialsKey + + // Service Account fields + ClientEmail string `json:"client_email"` + PrivateKeyID string `json:"private_key_id"` + PrivateKey string `json:"private_key"` + TokenURL string `json:"token_uri"` + ProjectID string `json:"project_id"` + + // User Credential fields + // (These typically come from gcloud auth.) + ClientSecret string `json:"client_secret"` + ClientID string `json:"client_id"` + RefreshToken string `json:"refresh_token"` +} + +func (f *credentialsFile) jwtConfig(scopes []string) *jwt.Config { + cfg := &jwt.Config{ + Email: f.ClientEmail, + PrivateKey: []byte(f.PrivateKey), + PrivateKeyID: f.PrivateKeyID, + Scopes: scopes, + TokenURL: f.TokenURL, + } + if cfg.TokenURL == "" { + cfg.TokenURL = JWTTokenURL + } + return cfg +} + +func (f *credentialsFile) tokenSource(ctx context.Context, scopes []string) (oauth2.TokenSource, error) { + switch f.Type { + case serviceAccountKey: + cfg := f.jwtConfig(scopes) + return cfg.TokenSource(ctx), nil + case userCredentialsKey: + cfg := &oauth2.Config{ + ClientID: f.ClientID, + ClientSecret: f.ClientSecret, + Scopes: scopes, + Endpoint: Endpoint, + } + tok := &oauth2.Token{RefreshToken: f.RefreshToken} + return cfg.TokenSource(ctx, tok), nil + case "": + return nil, errors.New("missing 'type' field in credentials") + default: + return nil, fmt.Errorf("unknown credential type: %q", f.Type) + } +} + +// ComputeTokenSource returns a token source that fetches access tokens +// from Google Compute Engine (GCE)'s metadata server. It's only valid to use +// this token source if your program is running on a GCE instance. +// If no account is specified, "default" is used. +// Further information about retrieving access tokens from the GCE metadata +// server can be found at https://cloud.google.com/compute/docs/authentication. +func ComputeTokenSource(account string) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, computeSource{account: account}) +} + +type computeSource struct { + account string +} + +func (cs computeSource) Token() (*oauth2.Token, error) { + if !metadata.OnGCE() { + return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE") + } + acct := cs.account + if acct == "" { + acct = "default" + } + tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token") + if err != nil { + return nil, err + } + var res struct { + AccessToken string `json:"access_token"` + ExpiresInSec int `json:"expires_in"` + TokenType string `json:"token_type"` + } + err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res) + if err != nil { + return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err) + } + if res.ExpiresInSec == 0 || res.AccessToken == "" { + return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata") + } + return &oauth2.Token{ + AccessToken: res.AccessToken, + TokenType: res.TokenType, + Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second), + }, nil +} diff --git a/vendor/golang.org/x/oauth2/google/google_test.go b/vendor/golang.org/x/oauth2/google/google_test.go new file mode 100644 index 0000000000..287c699e7b --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/google_test.go @@ -0,0 +1,116 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "strings" + "testing" +) + +var webJSONKey = []byte(` +{ + "web": { + "auth_uri": "https://google.com/o/oauth2/auth", + "client_secret": "3Oknc4jS_wA2r9i", + "token_uri": "https://google.com/o/oauth2/token", + "client_email": "222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com", + "redirect_uris": ["https://www.example.com/oauth2callback"], + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com", + "client_id": "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "javascript_origins": ["https://www.example.com"] + } +}`) + +var installedJSONKey = []byte(`{ + "installed": { + "client_id": "222-installed.apps.googleusercontent.com", + "redirect_uris": ["https://www.example.com/oauth2callback"] + } +}`) + +var jwtJSONKey = []byte(`{ + "private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b", + "private_key": "super secret key", + "client_email": "gopher@developer.gserviceaccount.com", + "client_id": "gopher.apps.googleusercontent.com", + "token_uri": "https://accounts.google.com/o/gophers/token", + "type": "service_account" +}`) + +var jwtJSONKeyNoTokenURL = []byte(`{ + "private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b", + "private_key": "super secret key", + "client_email": "gopher@developer.gserviceaccount.com", + "client_id": "gopher.apps.googleusercontent.com", + "type": "service_account" +}`) + +func TestConfigFromJSON(t *testing.T) { + conf, err := ConfigFromJSON(webJSONKey, "scope1", "scope2") + if err != nil { + t.Error(err) + } + if got, want := conf.ClientID, "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com"; got != want { + t.Errorf("ClientID = %q; want %q", got, want) + } + if got, want := conf.ClientSecret, "3Oknc4jS_wA2r9i"; got != want { + t.Errorf("ClientSecret = %q; want %q", got, want) + } + if got, want := conf.RedirectURL, "https://www.example.com/oauth2callback"; got != want { + t.Errorf("RedictURL = %q; want %q", got, want) + } + if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want { + t.Errorf("Scopes = %q; want %q", got, want) + } + if got, want := conf.Endpoint.AuthURL, "https://google.com/o/oauth2/auth"; got != want { + t.Errorf("AuthURL = %q; want %q", got, want) + } + if got, want := conf.Endpoint.TokenURL, "https://google.com/o/oauth2/token"; got != want { + t.Errorf("TokenURL = %q; want %q", got, want) + } +} + +func TestConfigFromJSON_Installed(t *testing.T) { + conf, err := ConfigFromJSON(installedJSONKey) + if err != nil { + t.Error(err) + } + if got, want := conf.ClientID, "222-installed.apps.googleusercontent.com"; got != want { + t.Errorf("ClientID = %q; want %q", got, want) + } +} + +func TestJWTConfigFromJSON(t *testing.T) { + conf, err := JWTConfigFromJSON(jwtJSONKey, "scope1", "scope2") + if err != nil { + t.Fatal(err) + } + if got, want := conf.Email, "gopher@developer.gserviceaccount.com"; got != want { + t.Errorf("Email = %q, want %q", got, want) + } + if got, want := string(conf.PrivateKey), "super secret key"; got != want { + t.Errorf("PrivateKey = %q, want %q", got, want) + } + if got, want := conf.PrivateKeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want { + t.Errorf("PrivateKeyID = %q, want %q", got, want) + } + if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want { + t.Errorf("Scopes = %q; want %q", got, want) + } + if got, want := conf.TokenURL, "https://accounts.google.com/o/gophers/token"; got != want { + t.Errorf("TokenURL = %q; want %q", got, want) + } +} + +func TestJWTConfigFromJSONNoTokenURL(t *testing.T) { + conf, err := JWTConfigFromJSON(jwtJSONKeyNoTokenURL, "scope1", "scope2") + if err != nil { + t.Fatal(err) + } + if got, want := conf.TokenURL, "https://accounts.google.com/o/oauth2/token"; got != want { + t.Errorf("TokenURL = %q; want %q", got, want) + } +} diff --git a/vendor/golang.org/x/oauth2/google/jwt.go b/vendor/golang.org/x/oauth2/google/jwt.go new file mode 100644 index 0000000000..b0fdb3a888 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/jwt.go @@ -0,0 +1,74 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "crypto/rsa" + "fmt" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" + "golang.org/x/oauth2/jws" +) + +// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON +// key file to read the credentials that authorize and authenticate the +// requests, and returns a TokenSource that does not use any OAuth2 flow but +// instead creates a JWT and sends that as the access token. +// The audience is typically a URL that specifies the scope of the credentials. +// +// Note that this is not a standard OAuth flow, but rather an +// optimization supported by a few Google services. +// Unless you know otherwise, you should use JWTConfigFromJSON instead. +func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) { + cfg, err := JWTConfigFromJSON(jsonKey) + if err != nil { + return nil, fmt.Errorf("google: could not parse JSON key: %v", err) + } + pk, err := internal.ParseKey(cfg.PrivateKey) + if err != nil { + return nil, fmt.Errorf("google: could not parse key: %v", err) + } + ts := &jwtAccessTokenSource{ + email: cfg.Email, + audience: audience, + pk: pk, + pkID: cfg.PrivateKeyID, + } + tok, err := ts.Token() + if err != nil { + return nil, err + } + return oauth2.ReuseTokenSource(tok, ts), nil +} + +type jwtAccessTokenSource struct { + email, audience string + pk *rsa.PrivateKey + pkID string +} + +func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) { + iat := time.Now() + exp := iat.Add(time.Hour) + cs := &jws.ClaimSet{ + Iss: ts.email, + Sub: ts.email, + Aud: ts.audience, + Iat: iat.Unix(), + Exp: exp.Unix(), + } + hdr := &jws.Header{ + Algorithm: "RS256", + Typ: "JWT", + KeyID: string(ts.pkID), + } + msg, err := jws.Encode(hdr, cs, ts.pk) + if err != nil { + return nil, fmt.Errorf("google: could not encode JWT: %v", err) + } + return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil +} diff --git a/vendor/golang.org/x/oauth2/google/jwt_test.go b/vendor/golang.org/x/oauth2/google/jwt_test.go new file mode 100644 index 0000000000..f844436fc8 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/jwt_test.go @@ -0,0 +1,91 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "strings" + "testing" + "time" + + "golang.org/x/oauth2/jws" +) + +func TestJWTAccessTokenSourceFromJSON(t *testing.T) { + // Generate a key we can use in the test data. + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + // Encode the key and substitute into our example JSON. + enc := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + enc, err = json.Marshal(string(enc)) + if err != nil { + t.Fatalf("json.Marshal: %v", err) + } + jsonKey := bytes.Replace(jwtJSONKey, []byte(`"super secret key"`), enc, 1) + + ts, err := JWTAccessTokenSourceFromJSON(jsonKey, "audience") + if err != nil { + t.Fatalf("JWTAccessTokenSourceFromJSON: %v\nJSON: %s", err, string(jsonKey)) + } + + tok, err := ts.Token() + if err != nil { + t.Fatalf("Token: %v", err) + } + + if got, want := tok.TokenType, "Bearer"; got != want { + t.Errorf("TokenType = %q, want %q", got, want) + } + if got := tok.Expiry; tok.Expiry.Before(time.Now()) { + t.Errorf("Expiry = %v, should not be expired", got) + } + + err = jws.Verify(tok.AccessToken, &privateKey.PublicKey) + if err != nil { + t.Errorf("jws.Verify on AccessToken: %v", err) + } + + claim, err := jws.Decode(tok.AccessToken) + if err != nil { + t.Fatalf("jws.Decode on AccessToken: %v", err) + } + + if got, want := claim.Iss, "gopher@developer.gserviceaccount.com"; got != want { + t.Errorf("Iss = %q, want %q", got, want) + } + if got, want := claim.Sub, "gopher@developer.gserviceaccount.com"; got != want { + t.Errorf("Sub = %q, want %q", got, want) + } + if got, want := claim.Aud, "audience"; got != want { + t.Errorf("Aud = %q, want %q", got, want) + } + + // Finally, check the header private key. + parts := strings.Split(tok.AccessToken, ".") + hdrJSON, err := base64.RawURLEncoding.DecodeString(parts[0]) + if err != nil { + t.Fatalf("base64 DecodeString: %v\nString: %q", err, parts[0]) + } + var hdr jws.Header + if err := json.Unmarshal([]byte(hdrJSON), &hdr); err != nil { + t.Fatalf("json.Unmarshal: %v (%q)", err, hdrJSON) + } + + if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want { + t.Errorf("Header KeyID = %q, want %q", got, want) + } +} diff --git a/vendor/golang.org/x/oauth2/google/not_go19.go b/vendor/golang.org/x/oauth2/google/not_go19.go new file mode 100644 index 0000000000..544e40624e --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/not_go19.go @@ -0,0 +1,54 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package google + +import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// DefaultCredentials holds Google credentials, including "Application Default Credentials". +// For more details, see: +// https://developers.google.com/accounts/docs/application-default-credentials +type DefaultCredentials struct { + ProjectID string // may be empty + TokenSource oauth2.TokenSource + + // JSON contains the raw bytes from a JSON credentials file. + // This field may be nil if authentication is provided by the + // environment and not with a credentials file, e.g. when code is + // running on Google Cloud Platform. + JSON []byte +} + +// FindDefaultCredentials searches for "Application Default Credentials". +// +// It looks for credentials in the following places, +// preferring the first location found: +// +// 1. A JSON file whose path is specified by the +// GOOGLE_APPLICATION_CREDENTIALS environment variable. +// 2. A JSON file in a location known to the gcloud command-line tool. +// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. +// On other systems, $HOME/.config/gcloud/application_default_credentials.json. +// 3. On Google App Engine it uses the appengine.AccessToken function. +// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches +// credentials from the metadata server. +// (In this final case any provided scopes are ignored.) +func FindDefaultCredentials(ctx context.Context, scopes ...string) (*DefaultCredentials, error) { + return findDefaultCredentials(ctx, scopes) +} + +// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can +// represent either a Google Developers Console client_credentials.json file (as in +// ConfigFromJSON) or a Google Developers service account key file (as in +// JWTConfigFromJSON). +// +// Note: despite the name, the returned credentials may not be Application Default Credentials. +func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*DefaultCredentials, error) { + return credentialsFromJSON(ctx, jsonData, scopes) +} diff --git a/vendor/golang.org/x/oauth2/google/sdk.go b/vendor/golang.org/x/oauth2/google/sdk.go new file mode 100644 index 0000000000..b9660caddf --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/sdk.go @@ -0,0 +1,201 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +type sdkCredentials struct { + Data []struct { + Credential struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenExpiry *time.Time `json:"token_expiry"` + } `json:"credential"` + Key struct { + Account string `json:"account"` + Scope string `json:"scope"` + } `json:"key"` + } +} + +// An SDKConfig provides access to tokens from an account already +// authorized via the Google Cloud SDK. +type SDKConfig struct { + conf oauth2.Config + initialToken *oauth2.Token +} + +// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK +// account. If account is empty, the account currently active in +// Google Cloud SDK properties is used. +// Google Cloud SDK credentials must be created by running `gcloud auth` +// before using this function. +// The Google Cloud SDK is available at https://cloud.google.com/sdk/. +func NewSDKConfig(account string) (*SDKConfig, error) { + configPath, err := sdkConfigPath() + if err != nil { + return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err) + } + credentialsPath := filepath.Join(configPath, "credentials") + f, err := os.Open(credentialsPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err) + } + defer f.Close() + + var c sdkCredentials + if err := json.NewDecoder(f).Decode(&c); err != nil { + return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err) + } + if len(c.Data) == 0 { + return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath) + } + if account == "" { + propertiesPath := filepath.Join(configPath, "properties") + f, err := os.Open(propertiesPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err) + } + defer f.Close() + ini, err := parseINI(f) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err) + } + core, ok := ini["core"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini) + } + active, ok := core["account"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core) + } + account = active + } + + for _, d := range c.Data { + if account == "" || d.Key.Account == account { + if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" { + return nil, fmt.Errorf("oauth2/google: no token available for account %q", account) + } + var expiry time.Time + if d.Credential.TokenExpiry != nil { + expiry = *d.Credential.TokenExpiry + } + return &SDKConfig{ + conf: oauth2.Config{ + ClientID: d.Credential.ClientID, + ClientSecret: d.Credential.ClientSecret, + Scopes: strings.Split(d.Key.Scope, " "), + Endpoint: Endpoint, + RedirectURL: "oob", + }, + initialToken: &oauth2.Token{ + AccessToken: d.Credential.AccessToken, + RefreshToken: d.Credential.RefreshToken, + Expiry: expiry, + }, + }, nil + } + } + return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account) +} + +// Client returns an HTTP client using Google Cloud SDK credentials to +// authorize requests. The token will auto-refresh as necessary. The +// underlying http.RoundTripper will be obtained using the provided +// context. The returned client and its Transport should not be +// modified. +func (c *SDKConfig) Client(ctx context.Context) *http.Client { + return &http.Client{ + Transport: &oauth2.Transport{ + Source: c.TokenSource(ctx), + }, + } +} + +// TokenSource returns an oauth2.TokenSource that retrieve tokens from +// Google Cloud SDK credentials using the provided context. +// It will returns the current access token stored in the credentials, +// and refresh it when it expires, but it won't update the credentials +// with the new access token. +func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource { + return c.conf.TokenSource(ctx, c.initialToken) +} + +// Scopes are the OAuth 2.0 scopes the current account is authorized for. +func (c *SDKConfig) Scopes() []string { + return c.conf.Scopes +} + +func parseINI(ini io.Reader) (map[string]map[string]string, error) { + result := map[string]map[string]string{ + "": {}, // root section + } + scanner := bufio.NewScanner(ini) + currentSection := "" + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, ";") { + // comment. + continue + } + if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { + currentSection = strings.TrimSpace(line[1 : len(line)-1]) + result[currentSection] = map[string]string{} + continue + } + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 && parts[0] != "" { + result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error scanning ini: %v", err) + } + return result, nil +} + +// sdkConfigPath tries to guess where the gcloud config is located. +// It can be overridden during tests. +var sdkConfigPath = func() (string, error) { + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil + } + homeDir := guessUnixHomeDir() + if homeDir == "" { + return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty") + } + return filepath.Join(homeDir, ".config", "gcloud"), nil +} + +func guessUnixHomeDir() string { + // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 + if v := os.Getenv("HOME"); v != "" { + return v + } + // Else, fall back to user.Current: + if u, err := user.Current(); err == nil { + return u.HomeDir + } + return "" +} diff --git a/vendor/golang.org/x/oauth2/google/sdk_test.go b/vendor/golang.org/x/oauth2/google/sdk_test.go new file mode 100644 index 0000000000..52b8ecadac --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/sdk_test.go @@ -0,0 +1,107 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "reflect" + "strings" + "testing" +) + +func TestSDKConfig(t *testing.T) { + sdkConfigPath = func() (string, error) { + return "testdata/gcloud", nil + } + + tests := []struct { + account string + accessToken string + err bool + }{ + {"", "bar_access_token", false}, + {"foo@example.com", "foo_access_token", false}, + {"bar@example.com", "bar_access_token", false}, + {"baz@serviceaccount.example.com", "", true}, + } + for _, tt := range tests { + c, err := NewSDKConfig(tt.account) + if got, want := err != nil, tt.err; got != want { + if !tt.err { + t.Errorf("got %v, want nil", err) + } else { + t.Errorf("got nil, want error") + } + continue + } + if err != nil { + continue + } + tok := c.initialToken + if tok == nil { + t.Errorf("got nil, want %q", tt.accessToken) + continue + } + if tok.AccessToken != tt.accessToken { + t.Errorf("got %q, want %q", tok.AccessToken, tt.accessToken) + } + } +} + +func TestParseINI(t *testing.T) { + tests := []struct { + ini string + want map[string]map[string]string + }{ + { + `root = toor +[foo] +bar = hop +ini = nin +`, + map[string]map[string]string{ + "": {"root": "toor"}, + "foo": {"bar": "hop", "ini": "nin"}, + }, + }, + { + "\t extra \t = whitespace \t\r\n \t [everywhere] \t \r\n here \t = \t there \t \r\n", + map[string]map[string]string{ + "": {"extra": "whitespace"}, + "everywhere": {"here": "there"}, + }, + }, + { + `[empty] +[section] +empty= +`, + map[string]map[string]string{ + "": {}, + "empty": {}, + "section": {"empty": ""}, + }, + }, + { + `ignore +[invalid +=stuff +;comment=true +`, + map[string]map[string]string{ + "": {}, + }, + }, + } + for _, tt := range tests { + result, err := parseINI(strings.NewReader(tt.ini)) + if err != nil { + t.Errorf("parseINI(%q) error %v, want: no error", tt.ini, err) + continue + } + if !reflect.DeepEqual(result, tt.want) { + t.Errorf("parseINI(%q) = %#v, want: %#v", tt.ini, result, tt.want) + } + } +} diff --git a/vendor/golang.org/x/oauth2/google/testdata/gcloud/credentials b/vendor/golang.org/x/oauth2/google/testdata/gcloud/credentials new file mode 100644 index 0000000000..ff5eefbd0a --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/testdata/gcloud/credentials @@ -0,0 +1,122 @@ +{ + "data": [ + { + "credential": { + "_class": "OAuth2Credentials", + "_module": "oauth2client.client", + "access_token": "foo_access_token", + "client_id": "foo_client_id", + "client_secret": "foo_client_secret", + "id_token": { + "at_hash": "foo_at_hash", + "aud": "foo_aud", + "azp": "foo_azp", + "cid": "foo_cid", + "email": "foo@example.com", + "email_verified": true, + "exp": 1420573614, + "iat": 1420569714, + "id": "1337", + "iss": "accounts.google.com", + "sub": "1337", + "token_hash": "foo_token_hash", + "verified_email": true + }, + "invalid": false, + "refresh_token": "foo_refresh_token", + "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "token_expiry": "2015-01-09T00:51:51Z", + "token_response": { + "access_token": "foo_access_token", + "expires_in": 3600, + "id_token": "foo_id_token", + "token_type": "Bearer" + }, + "token_uri": "https://accounts.google.com/o/oauth2/token", + "user_agent": "Cloud SDK Command Line Tool" + }, + "key": { + "account": "foo@example.com", + "clientId": "foo_client_id", + "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "type": "google-cloud-sdk" + } + }, + { + "credential": { + "_class": "OAuth2Credentials", + "_module": "oauth2client.client", + "access_token": "bar_access_token", + "client_id": "bar_client_id", + "client_secret": "bar_client_secret", + "id_token": { + "at_hash": "bar_at_hash", + "aud": "bar_aud", + "azp": "bar_azp", + "cid": "bar_cid", + "email": "bar@example.com", + "email_verified": true, + "exp": 1420573614, + "iat": 1420569714, + "id": "1337", + "iss": "accounts.google.com", + "sub": "1337", + "token_hash": "bar_token_hash", + "verified_email": true + }, + "invalid": false, + "refresh_token": "bar_refresh_token", + "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "token_expiry": "2015-01-09T00:51:51Z", + "token_response": { + "access_token": "bar_access_token", + "expires_in": 3600, + "id_token": "bar_id_token", + "token_type": "Bearer" + }, + "token_uri": "https://accounts.google.com/o/oauth2/token", + "user_agent": "Cloud SDK Command Line Tool" + }, + "key": { + "account": "bar@example.com", + "clientId": "bar_client_id", + "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "type": "google-cloud-sdk" + } + }, + { + "credential": { + "_class": "ServiceAccountCredentials", + "_kwargs": {}, + "_module": "oauth2client.client", + "_private_key_id": "00000000000000000000000000000000", + "_private_key_pkcs8_text": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCt3fpiynPSaUhWSIKMGV331zudwJ6GkGmvQtwsoK2S2LbvnSwU\nNxgj4fp08kIDR5p26wF4+t/HrKydMwzftXBfZ9UmLVJgRdSswmS5SmChCrfDS5OE\nvFFcN5+6w1w8/Nu657PF/dse8T0bV95YrqyoR0Osy8WHrUOMSIIbC3hRuwIDAQAB\nAoGAJrGE/KFjn0sQ7yrZ6sXmdLawrM3mObo/2uI9T60+k7SpGbBX0/Pi6nFrJMWZ\nTVONG7P3Mu5aCPzzuVRYJB0j8aldSfzABTY3HKoWCczqw1OztJiEseXGiYz4QOyr\nYU3qDyEpdhS6q6wcoLKGH+hqRmz6pcSEsc8XzOOu7s4xW8kCQQDkc75HjhbarCnd\nJJGMe3U76+6UGmdK67ltZj6k6xoB5WbTNChY9TAyI2JC+ppYV89zv3ssj4L+02u3\nHIHFGxsHAkEAwtU1qYb1tScpchPobnYUFiVKJ7KA8EZaHVaJJODW/cghTCV7BxcJ\nbgVvlmk4lFKn3lPKAgWw7PdQsBTVBUcCrQJATPwoIirizrv3u5soJUQxZIkENAqV\nxmybZx9uetrzP7JTrVbFRf0SScMcyN90hdLJiQL8+i4+gaszgFht7sNMnwJAAbfj\nq0UXcauQwALQ7/h2oONfTg5S+MuGC/AxcXPSMZbMRGGoPh3D5YaCv27aIuS/ukQ+\n6dmm/9AGlCb64fsIWQJAPaokbjIifo+LwC5gyK73Mc4t8nAOSZDenzd/2f6TCq76\nS1dcnKiPxaED7W/y6LJiuBT2rbZiQ2L93NJpFZD/UA==\n-----END RSA PRIVATE KEY-----\n", + "_revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "_scopes": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "_service_account_email": "baz@serviceaccount.example.com", + "_service_account_id": "baz.serviceaccount.example.com", + "_token_uri": "https://accounts.google.com/o/oauth2/token", + "_user_agent": "Cloud SDK Command Line Tool", + "access_token": null, + "assertion_type": null, + "client_id": null, + "client_secret": null, + "id_token": null, + "invalid": false, + "refresh_token": null, + "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "service_account_name": "baz@serviceaccount.example.com", + "token_expiry": null, + "token_response": null, + "user_agent": "Cloud SDK Command Line Tool" + }, + "key": { + "account": "baz@serviceaccount.example.com", + "clientId": "baz_client_id", + "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "type": "google-cloud-sdk" + } + } + ], + "file_version": 1 +} diff --git a/vendor/golang.org/x/oauth2/google/testdata/gcloud/properties b/vendor/golang.org/x/oauth2/google/testdata/gcloud/properties new file mode 100644 index 0000000000..025de886cf --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/testdata/gcloud/properties @@ -0,0 +1,2 @@ +[core] +account = bar@example.com \ No newline at end of file diff --git a/vendor/golang.org/x/oauth2/heroku/heroku.go b/vendor/golang.org/x/oauth2/heroku/heroku.go new file mode 100644 index 0000000000..5b4fdb8903 --- /dev/null +++ b/vendor/golang.org/x/oauth2/heroku/heroku.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package heroku provides constants for using OAuth2 to access Heroku. +package heroku // import "golang.org/x/oauth2/heroku" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Heroku's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://id.heroku.com/oauth/authorize", + TokenURL: "https://id.heroku.com/oauth/token", +} diff --git a/vendor/golang.org/x/oauth2/hipchat/hipchat.go b/vendor/golang.org/x/oauth2/hipchat/hipchat.go new file mode 100644 index 0000000000..594fe072c2 --- /dev/null +++ b/vendor/golang.org/x/oauth2/hipchat/hipchat.go @@ -0,0 +1,60 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package hipchat provides constants for using OAuth2 to access HipChat. +package hipchat // import "golang.org/x/oauth2/hipchat" + +import ( + "encoding/json" + "errors" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" +) + +// Endpoint is HipChat's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.hipchat.com/users/authorize", + TokenURL: "https://api.hipchat.com/v2/oauth/token", +} + +// ServerEndpoint returns a new oauth2.Endpoint for a HipChat Server instance +// running on the given domain or host. +func ServerEndpoint(host string) oauth2.Endpoint { + return oauth2.Endpoint{ + AuthURL: "https://" + host + "/users/authorize", + TokenURL: "https://" + host + "/v2/oauth/token", + } +} + +// ClientCredentialsConfigFromCaps generates a Config from a HipChat API +// capabilities descriptor. It does not verify the scopes against the +// capabilities document at this time. +// +// For more information see: https://www.hipchat.com/docs/apiv2/method/get_capabilities +func ClientCredentialsConfigFromCaps(capsJSON []byte, clientID, clientSecret string, scopes ...string) (*clientcredentials.Config, error) { + var caps struct { + Caps struct { + Endpoint struct { + TokenURL string `json:"tokenUrl"` + } `json:"oauth2Provider"` + } `json:"capabilities"` + } + + if err := json.Unmarshal(capsJSON, &caps); err != nil { + return nil, err + } + + // Verify required fields. + if caps.Caps.Endpoint.TokenURL == "" { + return nil, errors.New("oauth2/hipchat: missing OAuth2 token URL in the capabilities descriptor JSON") + } + + return &clientcredentials.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + TokenURL: caps.Caps.Endpoint.TokenURL, + }, nil +} diff --git a/vendor/golang.org/x/oauth2/internal/client_appengine.go b/vendor/golang.org/x/oauth2/internal/client_appengine.go new file mode 100644 index 0000000000..7434871880 --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/client_appengine.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +package internal + +import "google.golang.org/appengine/urlfetch" + +func init() { + appengineClientHook = urlfetch.Client +} diff --git a/vendor/golang.org/x/oauth2/internal/doc.go b/vendor/golang.org/x/oauth2/internal/doc.go new file mode 100644 index 0000000000..03265e888a --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/doc.go @@ -0,0 +1,6 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go new file mode 100644 index 0000000000..fc63fcab3f --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/oauth2.go @@ -0,0 +1,37 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internal + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" +) + +// ParseKey converts the binary contents of a private key file +// to an *rsa.PrivateKey. It detects whether the private key is in a +// PEM container or not. If so, it extracts the the private key +// from PEM container before conversion. It only supports PEM +// containers with no passphrase. +func ParseKey(key []byte) (*rsa.PrivateKey, error) { + block, _ := pem.Decode(key) + if block != nil { + key = block.Bytes + } + parsedKey, err := x509.ParsePKCS8PrivateKey(key) + if err != nil { + parsedKey, err = x509.ParsePKCS1PrivateKey(key) + if err != nil { + return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err) + } + } + parsed, ok := parsedKey.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("private key is invalid") + } + return parsed, nil +} diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go new file mode 100644 index 0000000000..999e668e6f --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/token.go @@ -0,0 +1,270 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internal + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "mime" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" +) + +// Token represents the credentials used to authorize +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// This type is a mirror of oauth2.Token and exists to break +// an otherwise-circular dependency. Other internal packages +// should convert this Token into an oauth2.Token before use. +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + AccessToken string + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + TokenType string + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + RefreshToken string + + // Expiry is the optional expiration time of the access token. + // + // If zero, TokenSource implementations will reuse the same + // token forever and RefreshToken or equivalent + // mechanisms for that TokenSource will not be used. + Expiry time.Time + + // Raw optionally contains extra metadata from the server + // when updating a token. + Raw interface{} +} + +// tokenJSON is the struct representing the HTTP response from OAuth2 +// providers returning a token in JSON form. +type tokenJSON struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + RefreshToken string `json:"refresh_token"` + ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number + Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in +} + +func (e *tokenJSON) expiry() (t time.Time) { + if v := e.ExpiresIn; v != 0 { + return time.Now().Add(time.Duration(v) * time.Second) + } + if v := e.Expires; v != 0 { + return time.Now().Add(time.Duration(v) * time.Second) + } + return +} + +type expirationTime int32 + +func (e *expirationTime) UnmarshalJSON(b []byte) error { + var n json.Number + err := json.Unmarshal(b, &n) + if err != nil { + return err + } + i, err := n.Int64() + if err != nil { + return err + } + *e = expirationTime(i) + return nil +} + +var brokenAuthHeaderProviders = []string{ + "https://accounts.google.com/", + "https://api.codeswholesale.com/oauth/token", + "https://api.dropbox.com/", + "https://api.dropboxapi.com/", + "https://api.instagram.com/", + "https://api.netatmo.net/", + "https://api.odnoklassniki.ru/", + "https://api.pushbullet.com/", + "https://api.soundcloud.com/", + "https://api.twitch.tv/", + "https://id.twitch.tv/", + "https://app.box.com/", + "https://connect.stripe.com/", + "https://login.mailchimp.com/", + "https://login.microsoftonline.com/", + "https://login.salesforce.com/", + "https://login.windows.net", + "https://login.live.com/", + "https://oauth.sandbox.trainingpeaks.com/", + "https://oauth.trainingpeaks.com/", + "https://oauth.vk.com/", + "https://openapi.baidu.com/", + "https://slack.com/", + "https://test-sandbox.auth.corp.google.com", + "https://test.salesforce.com/", + "https://user.gini.net/", + "https://www.douban.com/", + "https://www.googleapis.com/", + "https://www.linkedin.com/", + "https://www.strava.com/oauth/", + "https://www.wunderlist.com/oauth/", + "https://api.patreon.com/", + "https://sandbox.codeswholesale.com/oauth/token", + "https://api.sipgate.com/v1/authorization/oauth", + "https://api.medium.com/v1/tokens", + "https://log.finalsurge.com/oauth/token", + "https://multisport.todaysplan.com.au/rest/oauth/access_token", + "https://whats.todaysplan.com.au/rest/oauth/access_token", + "https://stackoverflow.com/oauth/access_token", +} + +// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints. +var brokenAuthHeaderDomains = []string{ + ".auth0.com", + ".force.com", + ".myshopify.com", + ".okta.com", + ".oktapreview.com", +} + +func RegisterBrokenAuthHeaderProvider(tokenURL string) { + brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL) +} + +// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL +// implements the OAuth2 spec correctly +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +// In summary: +// - Reddit only accepts client secret in the Authorization header +// - Dropbox accepts either it in URL param or Auth header, but not both. +// - Google only accepts URL param (not spec compliant?), not Auth header +// - Stripe only accepts client secret in Auth header with Bearer method, not Basic +func providerAuthHeaderWorks(tokenURL string) bool { + for _, s := range brokenAuthHeaderProviders { + if strings.HasPrefix(tokenURL, s) { + // Some sites fail to implement the OAuth2 spec fully. + return false + } + } + + if u, err := url.Parse(tokenURL); err == nil { + for _, s := range brokenAuthHeaderDomains { + if strings.HasSuffix(u.Host, s) { + return false + } + } + } + + // Assume the provider implements the spec properly + // otherwise. We can add more exceptions as they're + // discovered. We will _not_ be adding configurable hooks + // to this package to let users select server bugs. + return true +} + +func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) { + bustedAuth := !providerAuthHeaderWorks(tokenURL) + if bustedAuth { + if clientID != "" { + v.Set("client_id", clientID) + } + if clientSecret != "" { + v.Set("client_secret", clientSecret) + } + } + req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if !bustedAuth { + req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret)) + } + r, err := ctxhttp.Do(ctx, ContextClient(ctx), req) + if err != nil { + return nil, err + } + defer r.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + if code := r.StatusCode; code < 200 || code > 299 { + return nil, &RetrieveError{ + Response: r, + Body: body, + } + } + + var token *Token + content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) + switch content { + case "application/x-www-form-urlencoded", "text/plain": + vals, err := url.ParseQuery(string(body)) + if err != nil { + return nil, err + } + token = &Token{ + AccessToken: vals.Get("access_token"), + TokenType: vals.Get("token_type"), + RefreshToken: vals.Get("refresh_token"), + Raw: vals, + } + e := vals.Get("expires_in") + if e == "" { + // TODO(jbd): Facebook's OAuth2 implementation is broken and + // returns expires_in field in expires. Remove the fallback to expires, + // when Facebook fixes their implementation. + e = vals.Get("expires") + } + expires, _ := strconv.Atoi(e) + if expires != 0 { + token.Expiry = time.Now().Add(time.Duration(expires) * time.Second) + } + default: + var tj tokenJSON + if err = json.Unmarshal(body, &tj); err != nil { + return nil, err + } + token = &Token{ + AccessToken: tj.AccessToken, + TokenType: tj.TokenType, + RefreshToken: tj.RefreshToken, + Expiry: tj.expiry(), + Raw: make(map[string]interface{}), + } + json.Unmarshal(body, &token.Raw) // no error checks for optional fields + } + // Don't overwrite `RefreshToken` with an empty value + // if this was a token refreshing request. + if token.RefreshToken == "" { + token.RefreshToken = v.Get("refresh_token") + } + if token.AccessToken == "" { + return token, errors.New("oauth2: server response missing access_token") + } + return token, nil +} + +type RetrieveError struct { + Response *http.Response + Body []byte +} + +func (r *RetrieveError) Error() string { + return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body) +} diff --git a/vendor/golang.org/x/oauth2/internal/token_test.go b/vendor/golang.org/x/oauth2/internal/token_test.go new file mode 100644 index 0000000000..7b52e511fc --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/token_test.go @@ -0,0 +1,112 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internal + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "golang.org/x/net/context" +) + +func TestRegisterBrokenAuthHeaderProvider(t *testing.T) { + RegisterBrokenAuthHeaderProvider("https://aaa.com/") + tokenURL := "https://aaa.com/token" + if providerAuthHeaderWorks(tokenURL) { + t.Errorf("got %q as unbroken; want broken", tokenURL) + } +} + +func TestRetrieveTokenBustedNoSecret(t *testing.T) { + const clientID = "client-id" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got, want := r.FormValue("client_id"), clientID; got != want { + t.Errorf("client_id = %q; want %q", got, want) + } + if got, want := r.FormValue("client_secret"), ""; got != want { + t.Errorf("client_secret = %q; want empty", got) + } + w.Header().Set("Content-Type", "application/json") + io.WriteString(w, `{"access_token": "ACCESS_TOKEN", "token_type": "bearer"}`) + })) + defer ts.Close() + + RegisterBrokenAuthHeaderProvider(ts.URL) + _, err := RetrieveToken(context.Background(), clientID, "", ts.URL, url.Values{}) + if err != nil { + t.Errorf("RetrieveToken = %v; want no error", err) + } +} + +func Test_providerAuthHeaderWorks(t *testing.T) { + for _, p := range brokenAuthHeaderProviders { + if providerAuthHeaderWorks(p) { + t.Errorf("got %q as unbroken; want broken", p) + } + p := fmt.Sprintf("%ssomesuffix", p) + if providerAuthHeaderWorks(p) { + t.Errorf("got %q as unbroken; want broken", p) + } + } + p := "https://api.not-in-the-list-example.com/" + if !providerAuthHeaderWorks(p) { + t.Errorf("got %q as unbroken; want broken", p) + } +} + +func TestProviderAuthHeaderWorksDomain(t *testing.T) { + tests := []struct { + tokenURL string + wantWorks bool + }{ + {"https://dev-12345.okta.com/token-url", false}, + {"https://dev-12345.oktapreview.com/token-url", false}, + {"https://dev-12345.okta.org/token-url", true}, + {"https://foo.bar.force.com/token-url", false}, + {"https://foo.force.com/token-url", false}, + {"https://force.com/token-url", true}, + } + + for _, test := range tests { + got := providerAuthHeaderWorks(test.tokenURL) + if got != test.wantWorks { + t.Errorf("providerAuthHeaderWorks(%q) = %v; want %v", test.tokenURL, got, test.wantWorks) + } + } +} + +func TestRetrieveTokenWithContexts(t *testing.T) { + const clientID = "client-id" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + io.WriteString(w, `{"access_token": "ACCESS_TOKEN", "token_type": "bearer"}`) + })) + defer ts.Close() + + _, err := RetrieveToken(context.Background(), clientID, "", ts.URL, url.Values{}) + if err != nil { + t.Errorf("RetrieveToken (with background context) = %v; want no error", err) + } + + retrieved := make(chan struct{}) + cancellingts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + <-retrieved + })) + defer cancellingts.Close() + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + _, err = RetrieveToken(ctx, clientID, "", cancellingts.URL, url.Values{}) + close(retrieved) + if err == nil { + t.Errorf("RetrieveToken (with cancelled context) = nil; want error") + } +} diff --git a/vendor/golang.org/x/oauth2/internal/transport.go b/vendor/golang.org/x/oauth2/internal/transport.go new file mode 100644 index 0000000000..d16f9ae1fe --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/transport.go @@ -0,0 +1,34 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internal + +import ( + "net/http" + + "golang.org/x/net/context" +) + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient ContextKey + +// ContextKey is just an empty struct. It exists so HTTPClient can be +// an immutable public variable with a unique type. It's immutable +// because nobody else can create a ContextKey, being unexported. +type ContextKey struct{} + +var appengineClientHook func(context.Context) *http.Client + +func ContextClient(ctx context.Context) *http.Client { + if ctx != nil { + if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok { + return hc + } + } + if appengineClientHook != nil { + return appengineClientHook(ctx) + } + return http.DefaultClient +} diff --git a/vendor/golang.org/x/oauth2/jira/jira.go b/vendor/golang.org/x/oauth2/jira/jira.go new file mode 100644 index 0000000000..34415607c3 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jira/jira.go @@ -0,0 +1,167 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jira provides claims and JWT signing for OAuth2 to access JIRA/Confluence. +package jira + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" + + "golang.org/x/oauth2" +) + +// ClaimSet contains information about the JWT signature according +// to Atlassian's documentation +// https://developer.atlassian.com/cloud/jira/software/oauth-2-jwt-bearer-token-authorization-grant-type/ +type ClaimSet struct { + Issuer string `json:"iss"` + Subject string `json:"sub"` + InstalledURL string `json:"tnt"` // URL of installed app + AuthURL string `json:"aud"` // URL of auth server + ExpiresIn int64 `json:"exp"` // Must be no later that 60 seconds in the future + IssuedAt int64 `json:"iat"` +} + +var ( + defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" + defaultHeader = map[string]string{ + "typ": "JWT", + "alg": "HS256", + } +) + +// Config is the configuration for using JWT to fetch tokens, +// commonly known as "two-legged OAuth 2.0". +type Config struct { + // BaseURL for your app + BaseURL string + + // Subject is the userkey as defined by Atlassian + // Different than username (ex: /rest/api/2/user?username=alex) + Subject string + + oauth2.Config +} + +// TokenSource returns a JWT TokenSource using the configuration +// in c and the HTTP client from the provided context. +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c}) +} + +// Client returns an HTTP client wrapping the context's +// HTTP transport and adding Authorization headers with tokens +// obtained from c. +// +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) +} + +// jwtSource is a source that always does a signed JWT request for a token. +// It should typically be wrapped with a reuseTokenSource. +type jwtSource struct { + ctx context.Context + conf *Config +} + +func (js jwtSource) Token() (*oauth2.Token, error) { + exp := time.Duration(59) * time.Second + claimSet := &ClaimSet{ + Issuer: fmt.Sprintf("urn:atlassian:connect:clientid:%s", js.conf.ClientID), + Subject: fmt.Sprintf("urn:atlassian:connect:userkey:%s", js.conf.Subject), + InstalledURL: js.conf.BaseURL, + AuthURL: js.conf.Endpoint.AuthURL, + IssuedAt: time.Now().Unix(), + ExpiresIn: time.Now().Add(exp).Unix(), + } + + v := url.Values{} + v.Set("grant_type", defaultGrantType) + + // Add scopes if they exist; If not, it defaults to app scopes + if scopes := js.conf.Scopes; scopes != nil { + upperScopes := make([]string, len(scopes)) + for i, k := range scopes { + upperScopes[i] = strings.ToUpper(k) + } + v.Set("scope", strings.Join(upperScopes, "+")) + } + + // Sign claims for assertion + assertion, err := sign(js.conf.ClientSecret, claimSet) + if err != nil { + return nil, err + } + v.Set("assertion", string(assertion)) + + // Fetch access token from auth server + hc := oauth2.NewClient(js.ctx, nil) + resp, err := hc.PostForm(js.conf.Endpoint.TokenURL, v) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + if c := resp.StatusCode; c < 200 || c > 299 { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body) + } + + // tokenRes is the JSON response body. + var tokenRes struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int64 `json:"expires_in"` // relative seconds from now + } + if err := json.Unmarshal(body, &tokenRes); err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + token := &oauth2.Token{ + AccessToken: tokenRes.AccessToken, + TokenType: tokenRes.TokenType, + } + + if secs := tokenRes.ExpiresIn; secs > 0 { + token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) + } + return token, nil +} + +// Sign the claim set with the shared secret +// Result to be sent as assertion +func sign(key string, claims *ClaimSet) (string, error) { + b, err := json.Marshal(defaultHeader) + if err != nil { + return "", err + } + header := base64.RawURLEncoding.EncodeToString(b) + + jsonClaims, err := json.Marshal(claims) + if err != nil { + return "", err + } + encodedClaims := strings.TrimRight(base64.URLEncoding.EncodeToString(jsonClaims), "=") + + ss := fmt.Sprintf("%s.%s", header, encodedClaims) + + mac := hmac.New(sha256.New, []byte(key)) + mac.Write([]byte(ss)) + signature := mac.Sum(nil) + + return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(signature)), nil +} diff --git a/vendor/golang.org/x/oauth2/jira/jira_test.go b/vendor/golang.org/x/oauth2/jira/jira_test.go new file mode 100644 index 0000000000..c49940dfc3 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jira/jira_test.go @@ -0,0 +1,185 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jira + +import ( + "context" + "encoding/base64" + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/jws" +) + +func TestJWTFetch_JSONResponse(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{ + "access_token": "90d64460d14870c08c81352a05dedd3465940a7c", + "token_type": "Bearer", + "expires_in": 3600 + }`)) + })) + defer ts.Close() + + conf := &Config{ + BaseURL: "https://my.app.com", + Subject: "userkey", + Config: oauth2.Config{ + ClientID: "super_secret_client_id", + ClientSecret: "super_shared_secret", + Scopes: []string{"read", "write"}, + Endpoint: oauth2.Endpoint{ + AuthURL: "https://example.com", + TokenURL: ts.URL, + }, + }, + } + + tok, err := conf.TokenSource(context.Background()).Token() + if err != nil { + t.Fatal(err) + } + if !tok.Valid() { + t.Errorf("got invalid token: %v", tok) + } + if got, want := tok.AccessToken, "90d64460d14870c08c81352a05dedd3465940a7c"; got != want { + t.Errorf("access token = %q; want %q", got, want) + } + if got, want := tok.TokenType, "Bearer"; got != want { + t.Errorf("token type = %q; want %q", got, want) + } + if got := tok.Expiry.IsZero(); got { + t.Errorf("token expiry = %v, want none", got) + } +} + +func TestJWTFetch_BadResponse(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"token_type": "Bearer"}`)) + })) + defer ts.Close() + + conf := &Config{ + BaseURL: "https://my.app.com", + Subject: "userkey", + Config: oauth2.Config{ + ClientID: "super_secret_client_id", + ClientSecret: "super_shared_secret", + Scopes: []string{"read", "write"}, + Endpoint: oauth2.Endpoint{ + AuthURL: "https://example.com", + TokenURL: ts.URL, + }, + }, + } + + tok, err := conf.TokenSource(context.Background()).Token() + if err != nil { + t.Fatal(err) + } + if tok == nil { + t.Fatalf("got nil token; want token") + } + if tok.Valid() { + t.Errorf("got invalid token: %v", tok) + } + if got, want := tok.AccessToken, ""; got != want { + t.Errorf("access token = %q; want %q", got, want) + } + if got, want := tok.TokenType, "Bearer"; got != want { + t.Errorf("token type = %q; want %q", got, want) + } +} + +func TestJWTFetch_BadResponseType(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"access_token":123, "token_type": "Bearer"}`)) + })) + defer ts.Close() + + conf := &Config{ + BaseURL: "https://my.app.com", + Subject: "userkey", + Config: oauth2.Config{ + ClientID: "super_secret_client_id", + ClientSecret: "super_shared_secret", + Endpoint: oauth2.Endpoint{ + AuthURL: "https://example.com", + TokenURL: ts.URL, + }, + }, + } + + tok, err := conf.TokenSource(context.Background()).Token() + if err == nil { + t.Error("got a token; expected error") + if got, want := tok.AccessToken, ""; got != want { + t.Errorf("access token = %q; want %q", got, want) + } + } +} + +func TestJWTFetch_Assertion(t *testing.T) { + var assertion string + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + assertion = r.Form.Get("assertion") + + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{ + "access_token": "90d64460d14870c08c81352a05dedd3465940a7c", + "token_type": "Bearer", + "expires_in": 3600 + }`)) + })) + defer ts.Close() + + conf := &Config{ + BaseURL: "https://my.app.com", + Subject: "userkey", + Config: oauth2.Config{ + ClientID: "super_secret_client_id", + ClientSecret: "super_shared_secret", + Endpoint: oauth2.Endpoint{ + AuthURL: "https://example.com", + TokenURL: ts.URL, + }, + }, + } + + _, err := conf.TokenSource(context.Background()).Token() + if err != nil { + t.Fatalf("Failed to fetch token: %v", err) + } + + parts := strings.Split(assertion, ".") + if len(parts) != 3 { + t.Fatalf("assertion = %q; want 3 parts", assertion) + } + gotjson, err := base64.RawURLEncoding.DecodeString(parts[0]) + if err != nil { + t.Fatalf("invalid token header; err = %v", err) + } + + got := jws.Header{} + if err := json.Unmarshal(gotjson, &got); err != nil { + t.Errorf("failed to unmarshal json token header = %q; err = %v", gotjson, err) + } + + want := jws.Header{ + Algorithm: "HS256", + Typ: "JWT", + } + if got != want { + t.Errorf("access token header = %q; want %q", got, want) + } +} diff --git a/vendor/golang.org/x/oauth2/jws/jws.go b/vendor/golang.org/x/oauth2/jws/jws.go new file mode 100644 index 0000000000..683d2d271a --- /dev/null +++ b/vendor/golang.org/x/oauth2/jws/jws.go @@ -0,0 +1,182 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jws provides a partial implementation +// of JSON Web Signature encoding and decoding. +// It exists to support the golang.org/x/oauth2 package. +// +// See RFC 7515. +// +// Deprecated: this package is not intended for public use and might be +// removed in the future. It exists for internal use only. +// Please switch to another JWS package or copy this package into your own +// source tree. +package jws // import "golang.org/x/oauth2/jws" + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "strings" + "time" +) + +// ClaimSet contains information about the JWT signature including the +// permissions being requested (scopes), the target of the token, the issuer, +// the time the token was issued, and the lifetime of the token. +type ClaimSet struct { + Iss string `json:"iss"` // email address of the client_id of the application making the access token request + Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests + Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional). + Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch) + Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch) + Typ string `json:"typ,omitempty"` // token type (Optional). + + // Email for which the application is requesting delegated access (Optional). + Sub string `json:"sub,omitempty"` + + // The old name of Sub. Client keeps setting Prn to be + // complaint with legacy OAuth 2.0 providers. (Optional) + Prn string `json:"prn,omitempty"` + + // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 + // This array is marshalled using custom code (see (c *ClaimSet) encode()). + PrivateClaims map[string]interface{} `json:"-"` +} + +func (c *ClaimSet) encode() (string, error) { + // Reverting time back for machines whose time is not perfectly in sync. + // If client machine's time is in the future according + // to Google servers, an access token will not be issued. + now := time.Now().Add(-10 * time.Second) + if c.Iat == 0 { + c.Iat = now.Unix() + } + if c.Exp == 0 { + c.Exp = now.Add(time.Hour).Unix() + } + if c.Exp < c.Iat { + return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat) + } + + b, err := json.Marshal(c) + if err != nil { + return "", err + } + + if len(c.PrivateClaims) == 0 { + return base64.RawURLEncoding.EncodeToString(b), nil + } + + // Marshal private claim set and then append it to b. + prv, err := json.Marshal(c.PrivateClaims) + if err != nil { + return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims) + } + + // Concatenate public and private claim JSON objects. + if !bytes.HasSuffix(b, []byte{'}'}) { + return "", fmt.Errorf("jws: invalid JSON %s", b) + } + if !bytes.HasPrefix(prv, []byte{'{'}) { + return "", fmt.Errorf("jws: invalid JSON %s", prv) + } + b[len(b)-1] = ',' // Replace closing curly brace with a comma. + b = append(b, prv[1:]...) // Append private claims. + return base64.RawURLEncoding.EncodeToString(b), nil +} + +// Header represents the header for the signed JWS payloads. +type Header struct { + // The algorithm used for signature. + Algorithm string `json:"alg"` + + // Represents the token type. + Typ string `json:"typ"` + + // The optional hint of which key is being used. + KeyID string `json:"kid,omitempty"` +} + +func (h *Header) encode() (string, error) { + b, err := json.Marshal(h) + if err != nil { + return "", err + } + return base64.RawURLEncoding.EncodeToString(b), nil +} + +// Decode decodes a claim set from a JWS payload. +func Decode(payload string) (*ClaimSet, error) { + // decode returned id token to get expiry + s := strings.Split(payload, ".") + if len(s) < 2 { + // TODO(jbd): Provide more context about the error. + return nil, errors.New("jws: invalid token received") + } + decoded, err := base64.RawURLEncoding.DecodeString(s[1]) + if err != nil { + return nil, err + } + c := &ClaimSet{} + err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c) + return c, err +} + +// Signer returns a signature for the given data. +type Signer func(data []byte) (sig []byte, err error) + +// EncodeWithSigner encodes a header and claim set with the provided signer. +func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) { + head, err := header.encode() + if err != nil { + return "", err + } + cs, err := c.encode() + if err != nil { + return "", err + } + ss := fmt.Sprintf("%s.%s", head, cs) + sig, err := sg([]byte(ss)) + if err != nil { + return "", err + } + return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(sig)), nil +} + +// Encode encodes a signed JWS with provided header and claim set. +// This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key. +func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) { + sg := func(data []byte) (sig []byte, err error) { + h := sha256.New() + h.Write(data) + return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil)) + } + return EncodeWithSigner(header, c, sg) +} + +// Verify tests whether the provided JWT token's signature was produced by the private key +// associated with the supplied public key. +func Verify(token string, key *rsa.PublicKey) error { + parts := strings.Split(token, ".") + if len(parts) != 3 { + return errors.New("jws: invalid token received, token must have 3 parts") + } + + signedContent := parts[0] + "." + parts[1] + signatureString, err := base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + return err + } + + h := sha256.New() + h.Write([]byte(signedContent)) + return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString)) +} diff --git a/vendor/golang.org/x/oauth2/jws/jws_test.go b/vendor/golang.org/x/oauth2/jws/jws_test.go new file mode 100644 index 0000000000..39a136a29f --- /dev/null +++ b/vendor/golang.org/x/oauth2/jws/jws_test.go @@ -0,0 +1,46 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jws + +import ( + "crypto/rand" + "crypto/rsa" + "testing" +) + +func TestSignAndVerify(t *testing.T) { + header := &Header{ + Algorithm: "RS256", + Typ: "JWT", + } + payload := &ClaimSet{ + Iss: "http://google.com/", + Aud: "", + Exp: 3610, + Iat: 10, + } + + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + token, err := Encode(header, payload, privateKey) + if err != nil { + t.Fatal(err) + } + + err = Verify(token, &privateKey.PublicKey) + if err != nil { + t.Fatal(err) + } +} + +func TestVerifyFailsOnMalformedClaim(t *testing.T) { + err := Verify("abc.def", nil) + if err == nil { + t.Error("got no errors; want improperly formed JWT not to be verified") + } +} diff --git a/vendor/golang.org/x/oauth2/jwt/example_test.go b/vendor/golang.org/x/oauth2/jwt/example_test.go new file mode 100644 index 0000000000..58503d80d3 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jwt/example_test.go @@ -0,0 +1,33 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jwt_test + +import ( + "context" + + "golang.org/x/oauth2/jwt" +) + +func ExampleJWTConfig() { + ctx := context.Background() + conf := &jwt.Config{ + Email: "xxx@developer.com", + // The contents of your RSA private key or your PEM file + // that contains a private key. + // If you have a p12 file instead, you + // can use `openssl` to export the private key into a pem file. + // + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + // It only supports PEM containers with no passphrase. + PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."), + Subject: "user@example.com", + TokenURL: "https://provider.com/o/oauth2/token", + } + // Initiate an http.Client, the following GET request will be + // authorized and authenticated on the behalf of user@example.com. + client := conf.Client(ctx) + client.Get("...") +} diff --git a/vendor/golang.org/x/oauth2/jwt/jwt.go b/vendor/golang.org/x/oauth2/jwt/jwt.go new file mode 100644 index 0000000000..e08f315959 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jwt/jwt.go @@ -0,0 +1,162 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jwt implements the OAuth 2.0 JSON Web Token flow, commonly +// known as "two-legged OAuth 2.0". +// +// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12 +package jwt + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" + "golang.org/x/oauth2/jws" +) + +var ( + defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" + defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"} +) + +// Config is the configuration for using JWT to fetch tokens, +// commonly known as "two-legged OAuth 2.0". +type Config struct { + // Email is the OAuth client identifier used when communicating with + // the configured OAuth provider. + Email string + + // PrivateKey contains the contents of an RSA private key or the + // contents of a PEM file that contains a private key. The provided + // private key is used to sign JWT payloads. + // PEM containers with a passphrase are not supported. + // Use the following command to convert a PKCS 12 file into a PEM. + // + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + PrivateKey []byte + + // PrivateKeyID contains an optional hint indicating which key is being + // used. + PrivateKeyID string + + // Subject is the optional user to impersonate. + Subject string + + // Scopes optionally specifies a list of requested permission scopes. + Scopes []string + + // TokenURL is the endpoint required to complete the 2-legged JWT flow. + TokenURL string + + // Expires optionally specifies how long the token is valid for. + Expires time.Duration +} + +// TokenSource returns a JWT TokenSource using the configuration +// in c and the HTTP client from the provided context. +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c}) +} + +// Client returns an HTTP client wrapping the context's +// HTTP transport and adding Authorization headers with tokens +// obtained from c. +// +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) +} + +// jwtSource is a source that always does a signed JWT request for a token. +// It should typically be wrapped with a reuseTokenSource. +type jwtSource struct { + ctx context.Context + conf *Config +} + +func (js jwtSource) Token() (*oauth2.Token, error) { + pk, err := internal.ParseKey(js.conf.PrivateKey) + if err != nil { + return nil, err + } + hc := oauth2.NewClient(js.ctx, nil) + claimSet := &jws.ClaimSet{ + Iss: js.conf.Email, + Scope: strings.Join(js.conf.Scopes, " "), + Aud: js.conf.TokenURL, + } + if subject := js.conf.Subject; subject != "" { + claimSet.Sub = subject + // prn is the old name of sub. Keep setting it + // to be compatible with legacy OAuth 2.0 providers. + claimSet.Prn = subject + } + if t := js.conf.Expires; t > 0 { + claimSet.Exp = time.Now().Add(t).Unix() + } + h := *defaultHeader + h.KeyID = js.conf.PrivateKeyID + payload, err := jws.Encode(&h, claimSet, pk) + if err != nil { + return nil, err + } + v := url.Values{} + v.Set("grant_type", defaultGrantType) + v.Set("assertion", payload) + resp, err := hc.PostForm(js.conf.TokenURL, v) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + if c := resp.StatusCode; c < 200 || c > 299 { + return nil, &oauth2.RetrieveError{ + Response: resp, + Body: body, + } + } + // tokenRes is the JSON response body. + var tokenRes struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + IDToken string `json:"id_token"` + ExpiresIn int64 `json:"expires_in"` // relative seconds from now + } + if err := json.Unmarshal(body, &tokenRes); err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + token := &oauth2.Token{ + AccessToken: tokenRes.AccessToken, + TokenType: tokenRes.TokenType, + } + raw := make(map[string]interface{}) + json.Unmarshal(body, &raw) // no error checks for optional fields + token = token.WithExtra(raw) + + if secs := tokenRes.ExpiresIn; secs > 0 { + token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) + } + if v := tokenRes.IDToken; v != "" { + // decode returned id token to get expiry + claimSet, err := jws.Decode(v) + if err != nil { + return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err) + } + token.Expiry = time.Unix(claimSet.Exp, 0) + } + return token, nil +} diff --git a/vendor/golang.org/x/oauth2/jwt/jwt_test.go b/vendor/golang.org/x/oauth2/jwt/jwt_test.go new file mode 100644 index 0000000000..1fbb9aa7d0 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jwt/jwt_test.go @@ -0,0 +1,221 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jwt + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/jws" +) + +var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE +DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY +fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK +1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr +k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9 +/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt +3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn +2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3 +nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK +6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf +5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e +DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1 +M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g +z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y +1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK +J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U +f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx +QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA +cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr +Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw +5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg +KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84 +OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd +mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ +5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg== +-----END RSA PRIVATE KEY-----`) + +func TestJWTFetch_JSONResponse(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{ + "access_token": "90d64460d14870c08c81352a05dedd3465940a7c", + "scope": "user", + "token_type": "bearer", + "expires_in": 3600 + }`)) + })) + defer ts.Close() + + conf := &Config{ + Email: "aaa@xxx.com", + PrivateKey: dummyPrivateKey, + TokenURL: ts.URL, + } + tok, err := conf.TokenSource(context.Background()).Token() + if err != nil { + t.Fatal(err) + } + if !tok.Valid() { + t.Errorf("got invalid token: %v", tok) + } + if got, want := tok.AccessToken, "90d64460d14870c08c81352a05dedd3465940a7c"; got != want { + t.Errorf("access token = %q; want %q", got, want) + } + if got, want := tok.TokenType, "bearer"; got != want { + t.Errorf("token type = %q; want %q", got, want) + } + if got := tok.Expiry.IsZero(); got { + t.Errorf("token expiry = %v, want none", got) + } + scope := tok.Extra("scope") + if got, want := scope, "user"; got != want { + t.Errorf("scope = %q; want %q", got, want) + } +} + +func TestJWTFetch_BadResponse(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`)) + })) + defer ts.Close() + + conf := &Config{ + Email: "aaa@xxx.com", + PrivateKey: dummyPrivateKey, + TokenURL: ts.URL, + } + tok, err := conf.TokenSource(context.Background()).Token() + if err != nil { + t.Fatal(err) + } + if tok == nil { + t.Fatalf("got nil token; want token") + } + if tok.Valid() { + t.Errorf("got invalid token: %v", tok) + } + if got, want := tok.AccessToken, ""; got != want { + t.Errorf("access token = %q; want %q", got, want) + } + if got, want := tok.TokenType, "bearer"; got != want { + t.Errorf("token type = %q; want %q", got, want) + } + scope := tok.Extra("scope") + if got, want := scope, "user"; got != want { + t.Errorf("token scope = %q; want %q", got, want) + } +} + +func TestJWTFetch_BadResponseType(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`)) + })) + defer ts.Close() + conf := &Config{ + Email: "aaa@xxx.com", + PrivateKey: dummyPrivateKey, + TokenURL: ts.URL, + } + tok, err := conf.TokenSource(context.Background()).Token() + if err == nil { + t.Error("got a token; expected error") + if got, want := tok.AccessToken, ""; got != want { + t.Errorf("access token = %q; want %q", got, want) + } + } +} + +func TestJWTFetch_Assertion(t *testing.T) { + var assertion string + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + assertion = r.Form.Get("assertion") + + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{ + "access_token": "90d64460d14870c08c81352a05dedd3465940a7c", + "scope": "user", + "token_type": "bearer", + "expires_in": 3600 + }`)) + })) + defer ts.Close() + + conf := &Config{ + Email: "aaa@xxx.com", + PrivateKey: dummyPrivateKey, + PrivateKeyID: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + TokenURL: ts.URL, + } + + _, err := conf.TokenSource(context.Background()).Token() + if err != nil { + t.Fatalf("Failed to fetch token: %v", err) + } + + parts := strings.Split(assertion, ".") + if len(parts) != 3 { + t.Fatalf("assertion = %q; want 3 parts", assertion) + } + gotjson, err := base64.RawURLEncoding.DecodeString(parts[0]) + if err != nil { + t.Fatalf("invalid token header; err = %v", err) + } + + got := jws.Header{} + if err := json.Unmarshal(gotjson, &got); err != nil { + t.Errorf("failed to unmarshal json token header = %q; err = %v", gotjson, err) + } + + want := jws.Header{ + Algorithm: "RS256", + Typ: "JWT", + KeyID: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + } + if got != want { + t.Errorf("access token header = %q; want %q", got, want) + } +} + +func TestTokenRetrieveError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-type", "application/json") + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(`{"error": "invalid_grant"}`)) + })) + defer ts.Close() + + conf := &Config{ + Email: "aaa@xxx.com", + PrivateKey: dummyPrivateKey, + TokenURL: ts.URL, + } + + _, err := conf.TokenSource(context.Background()).Token() + if err == nil { + t.Fatalf("got no error, expected one") + } + _, ok := err.(*oauth2.RetrieveError) + if !ok { + t.Fatalf("got %T error, expected *RetrieveError", err) + } + // Test error string for backwards compatibility + expected := fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", "400 Bad Request", `{"error": "invalid_grant"}`) + if errStr := err.Error(); errStr != expected { + t.Fatalf("got %#v, expected %#v", errStr, expected) + } +} diff --git a/vendor/golang.org/x/oauth2/kakao/kakao.go b/vendor/golang.org/x/oauth2/kakao/kakao.go new file mode 100644 index 0000000000..6d211260cd --- /dev/null +++ b/vendor/golang.org/x/oauth2/kakao/kakao.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package kakao provides constants for using OAuth2 to access Kakao. +package kakao // import "golang.org/x/oauth2/kakao" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Kakao's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://kauth.kakao.com/oauth/authorize", + TokenURL: "https://kauth.kakao.com/oauth/token", +} diff --git a/vendor/golang.org/x/oauth2/linkedin/linkedin.go b/vendor/golang.org/x/oauth2/linkedin/linkedin.go new file mode 100644 index 0000000000..62d1de8173 --- /dev/null +++ b/vendor/golang.org/x/oauth2/linkedin/linkedin.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package linkedin provides constants for using OAuth2 to access LinkedIn. +package linkedin // import "golang.org/x/oauth2/linkedin" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is LinkedIn's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.linkedin.com/oauth/v2/authorization", + TokenURL: "https://www.linkedin.com/oauth/v2/accessToken", +} diff --git a/vendor/golang.org/x/oauth2/mailchimp/mailchimp.go b/vendor/golang.org/x/oauth2/mailchimp/mailchimp.go new file mode 100644 index 0000000000..647787ec62 --- /dev/null +++ b/vendor/golang.org/x/oauth2/mailchimp/mailchimp.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package mailchimp provides constants for using OAuth2 to access MailChimp. +package mailchimp // import "golang.org/x/oauth2/mailchimp" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is MailChimp's OAuth 2.0 endpoint. +// See http://developer.mailchimp.com/documentation/mailchimp/guides/how-to-use-oauth2/ +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://login.mailchimp.com/oauth2/authorize", + TokenURL: "https://login.mailchimp.com/oauth2/token", +} diff --git a/vendor/golang.org/x/oauth2/mailru/mailru.go b/vendor/golang.org/x/oauth2/mailru/mailru.go new file mode 100644 index 0000000000..dddd9dd0fb --- /dev/null +++ b/vendor/golang.org/x/oauth2/mailru/mailru.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package mailru provides constants for using OAuth2 to access Mail.Ru. +package mailru // import "golang.org/x/oauth2/mailru" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Mail.Ru's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://o2.mail.ru/login", + TokenURL: "https://o2.mail.ru/token", +} diff --git a/vendor/golang.org/x/oauth2/mediamath/mediamath.go b/vendor/golang.org/x/oauth2/mediamath/mediamath.go new file mode 100644 index 0000000000..3ebce5da12 --- /dev/null +++ b/vendor/golang.org/x/oauth2/mediamath/mediamath.go @@ -0,0 +1,22 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package mediamath provides constants for using OAuth2 to access MediaMath. +package mediamath // import "golang.org/x/oauth2/mediamath" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is MediaMath's OAuth 2.0 endpoint for production. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://api.mediamath.com/oauth2/v1.0/authorize", + TokenURL: "https://api.mediamath.com/oauth2/v1.0/token", +} + +// SandboxEndpoint is MediaMath's OAuth 2.0 endpoint for sandbox. +var SandboxEndpoint = oauth2.Endpoint{ + AuthURL: "https://t1sandbox.mediamath.com/oauth2/v1.0/authorize", + TokenURL: "https://t1sandbox.mediamath.com/oauth2/v1.0/token", +} diff --git a/vendor/golang.org/x/oauth2/microsoft/microsoft.go b/vendor/golang.org/x/oauth2/microsoft/microsoft.go new file mode 100644 index 0000000000..3ffbc57a69 --- /dev/null +++ b/vendor/golang.org/x/oauth2/microsoft/microsoft.go @@ -0,0 +1,31 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package microsoft provides constants for using OAuth2 to access Windows Live ID. +package microsoft // import "golang.org/x/oauth2/microsoft" + +import ( + "golang.org/x/oauth2" +) + +// LiveConnectEndpoint is Windows's Live ID OAuth 2.0 endpoint. +var LiveConnectEndpoint = oauth2.Endpoint{ + AuthURL: "https://login.live.com/oauth20_authorize.srf", + TokenURL: "https://login.live.com/oauth20_token.srf", +} + +// AzureADEndpoint returns a new oauth2.Endpoint for the given tenant at Azure Active Directory. +// If tenant is empty, it uses the tenant called `common`. +// +// For more information see: +// https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints +func AzureADEndpoint(tenant string) oauth2.Endpoint { + if tenant == "" { + tenant = "common" + } + return oauth2.Endpoint{ + AuthURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/authorize", + TokenURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token", + } +} diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go new file mode 100644 index 0000000000..16775d081b --- /dev/null +++ b/vendor/golang.org/x/oauth2/oauth2.go @@ -0,0 +1,362 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package oauth2 provides support for making +// OAuth2 authorized and authenticated HTTP requests, +// as specified in RFC 6749. +// It can additionally grant authorization with Bearer JWT. +package oauth2 // import "golang.org/x/oauth2" + +import ( + "bytes" + "errors" + "net/http" + "net/url" + "strings" + "sync" + + "golang.org/x/net/context" + "golang.org/x/oauth2/internal" +) + +// NoContext is the default context you should supply if not using +// your own context.Context (see https://golang.org/x/net/context). +// +// Deprecated: Use context.Background() or context.TODO() instead. +var NoContext = context.TODO() + +// RegisterBrokenAuthHeaderProvider registers an OAuth2 server +// identified by the tokenURL prefix as an OAuth2 implementation +// which doesn't support the HTTP Basic authentication +// scheme to authenticate with the authorization server. +// Once a server is registered, credentials (client_id and client_secret) +// will be passed as query parameters rather than being present +// in the Authorization header. +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +func RegisterBrokenAuthHeaderProvider(tokenURL string) { + internal.RegisterBrokenAuthHeaderProvider(tokenURL) +} + +// Config describes a typical 3-legged OAuth2 flow, with both the +// client application information and the server's endpoint URLs. +// For the client credentials 2-legged OAuth2 flow, see the clientcredentials +// package (https://golang.org/x/oauth2/clientcredentials). +type Config struct { + // ClientID is the application's ID. + ClientID string + + // ClientSecret is the application's secret. + ClientSecret string + + // Endpoint contains the resource server's token endpoint + // URLs. These are constants specific to each server and are + // often available via site-specific packages, such as + // google.Endpoint or github.Endpoint. + Endpoint Endpoint + + // RedirectURL is the URL to redirect users going through + // the OAuth flow, after the resource owner's URLs. + RedirectURL string + + // Scope specifies optional requested permissions. + Scopes []string +} + +// A TokenSource is anything that can return a token. +type TokenSource interface { + // Token returns a token or an error. + // Token must be safe for concurrent use by multiple goroutines. + // The returned Token must not be modified. + Token() (*Token, error) +} + +// Endpoint contains the OAuth 2.0 provider's authorization and token +// endpoint URLs. +type Endpoint struct { + AuthURL string + TokenURL string +} + +var ( + // AccessTypeOnline and AccessTypeOffline are options passed + // to the Options.AuthCodeURL method. They modify the + // "access_type" field that gets sent in the URL returned by + // AuthCodeURL. + // + // Online is the default if neither is specified. If your + // application needs to refresh access tokens when the user + // is not present at the browser, then use offline. This will + // result in your application obtaining a refresh token the + // first time your application exchanges an authorization + // code for a user. + AccessTypeOnline AuthCodeOption = SetAuthURLParam("access_type", "online") + AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline") + + // ApprovalForce forces the users to view the consent dialog + // and confirm the permissions request at the URL returned + // from AuthCodeURL, even if they've already done so. + ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force") +) + +// An AuthCodeOption is passed to Config.AuthCodeURL. +type AuthCodeOption interface { + setValue(url.Values) +} + +type setParam struct{ k, v string } + +func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) } + +// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters +// to a provider's authorization endpoint. +func SetAuthURLParam(key, value string) AuthCodeOption { + return setParam{key, value} +} + +// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page +// that asks for permissions for the required scopes explicitly. +// +// State is a token to protect the user from CSRF attacks. You must +// always provide a non-empty string and validate that it matches the +// the state query parameter on your redirect callback. +// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info. +// +// Opts may include AccessTypeOnline or AccessTypeOffline, as well +// as ApprovalForce. +// It can also be used to pass the PKCE challange. +// See https://www.oauth.com/oauth2-servers/pkce/ for more info. +func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { + var buf bytes.Buffer + buf.WriteString(c.Endpoint.AuthURL) + v := url.Values{ + "response_type": {"code"}, + "client_id": {c.ClientID}, + } + if c.RedirectURL != "" { + v.Set("redirect_uri", c.RedirectURL) + } + if len(c.Scopes) > 0 { + v.Set("scope", strings.Join(c.Scopes, " ")) + } + if state != "" { + // TODO(light): Docs say never to omit state; don't allow empty. + v.Set("state", state) + } + for _, opt := range opts { + opt.setValue(v) + } + if strings.Contains(c.Endpoint.AuthURL, "?") { + buf.WriteByte('&') + } else { + buf.WriteByte('?') + } + buf.WriteString(v.Encode()) + return buf.String() +} + +// PasswordCredentialsToken converts a resource owner username and password +// pair into a token. +// +// Per the RFC, this grant type should only be used "when there is a high +// degree of trust between the resource owner and the client (e.g., the client +// is part of the device operating system or a highly privileged application), +// and when other authorization grant types are not available." +// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info. +// +// The HTTP client to use is derived from the context. +// If nil, http.DefaultClient is used. +func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) { + v := url.Values{ + "grant_type": {"password"}, + "username": {username}, + "password": {password}, + } + if len(c.Scopes) > 0 { + v.Set("scope", strings.Join(c.Scopes, " ")) + } + return retrieveToken(ctx, c, v) +} + +// Exchange converts an authorization code into a token. +// +// It is used after a resource provider redirects the user back +// to the Redirect URI (the URL obtained from AuthCodeURL). +// +// The HTTP client to use is derived from the context. +// If a client is not provided via the context, http.DefaultClient is used. +// +// The code will be in the *http.Request.FormValue("code"). Before +// calling Exchange, be sure to validate FormValue("state"). +// +// Opts may include the PKCE verifier code if previously used in AuthCodeURL. +// See https://www.oauth.com/oauth2-servers/pkce/ for more info. +func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) { + v := url.Values{ + "grant_type": {"authorization_code"}, + "code": {code}, + } + if c.RedirectURL != "" { + v.Set("redirect_uri", c.RedirectURL) + } + for _, opt := range opts { + opt.setValue(v) + } + return retrieveToken(ctx, c, v) +} + +// Client returns an HTTP client using the provided token. +// The token will auto-refresh as necessary. The underlying +// HTTP transport will be obtained using the provided context. +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context, t *Token) *http.Client { + return NewClient(ctx, c.TokenSource(ctx, t)) +} + +// TokenSource returns a TokenSource that returns t until t expires, +// automatically refreshing it as necessary using the provided context. +// +// Most users will use Config.Client instead. +func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource { + tkr := &tokenRefresher{ + ctx: ctx, + conf: c, + } + if t != nil { + tkr.refreshToken = t.RefreshToken + } + return &reuseTokenSource{ + t: t, + new: tkr, + } +} + +// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token" +// HTTP requests to renew a token using a RefreshToken. +type tokenRefresher struct { + ctx context.Context // used to get HTTP requests + conf *Config + refreshToken string +} + +// WARNING: Token is not safe for concurrent access, as it +// updates the tokenRefresher's refreshToken field. +// Within this package, it is used by reuseTokenSource which +// synchronizes calls to this method with its own mutex. +func (tf *tokenRefresher) Token() (*Token, error) { + if tf.refreshToken == "" { + return nil, errors.New("oauth2: token expired and refresh token is not set") + } + + tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{ + "grant_type": {"refresh_token"}, + "refresh_token": {tf.refreshToken}, + }) + + if err != nil { + return nil, err + } + if tf.refreshToken != tk.RefreshToken { + tf.refreshToken = tk.RefreshToken + } + return tk, err +} + +// reuseTokenSource is a TokenSource that holds a single token in memory +// and validates its expiry before each call to retrieve it with +// Token. If it's expired, it will be auto-refreshed using the +// new TokenSource. +type reuseTokenSource struct { + new TokenSource // called when t is expired. + + mu sync.Mutex // guards t + t *Token +} + +// Token returns the current token if it's still valid, else will +// refresh the current token (using r.Context for HTTP client +// information) and return the new one. +func (s *reuseTokenSource) Token() (*Token, error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.t.Valid() { + return s.t, nil + } + t, err := s.new.Token() + if err != nil { + return nil, err + } + s.t = t + return t, nil +} + +// StaticTokenSource returns a TokenSource that always returns the same token. +// Because the provided token t is never refreshed, StaticTokenSource is only +// useful for tokens that never expire. +func StaticTokenSource(t *Token) TokenSource { + return staticTokenSource{t} +} + +// staticTokenSource is a TokenSource that always returns the same Token. +type staticTokenSource struct { + t *Token +} + +func (s staticTokenSource) Token() (*Token, error) { + return s.t, nil +} + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient internal.ContextKey + +// NewClient creates an *http.Client from a Context and TokenSource. +// The returned client is not valid beyond the lifetime of the context. +// +// Note that if a custom *http.Client is provided via the Context it +// is used only for token acquisition and is not used to configure the +// *http.Client returned from NewClient. +// +// As a special case, if src is nil, a non-OAuth2 client is returned +// using the provided context. This exists to support related OAuth2 +// packages. +func NewClient(ctx context.Context, src TokenSource) *http.Client { + if src == nil { + return internal.ContextClient(ctx) + } + return &http.Client{ + Transport: &Transport{ + Base: internal.ContextClient(ctx).Transport, + Source: ReuseTokenSource(nil, src), + }, + } +} + +// ReuseTokenSource returns a TokenSource which repeatedly returns the +// same token as long as it's valid, starting with t. +// When its cached token is invalid, a new token is obtained from src. +// +// ReuseTokenSource is typically used to reuse tokens from a cache +// (such as a file on disk) between runs of a program, rather than +// obtaining new tokens unnecessarily. +// +// The initial token t may be nil, in which case the TokenSource is +// wrapped in a caching version if it isn't one already. This also +// means it's always safe to wrap ReuseTokenSource around any other +// TokenSource without adverse effects. +func ReuseTokenSource(t *Token, src TokenSource) TokenSource { + // Don't wrap a reuseTokenSource in itself. That would work, + // but cause an unnecessary number of mutex operations. + // Just build the equivalent one. + if rt, ok := src.(*reuseTokenSource); ok { + if t == nil { + // Just use it directly. + return rt + } + src = rt.new + } + return &reuseTokenSource{ + t: t, + new: src, + } +} diff --git a/vendor/golang.org/x/oauth2/oauth2_test.go b/vendor/golang.org/x/oauth2/oauth2_test.go new file mode 100644 index 0000000000..ef12ebc578 --- /dev/null +++ b/vendor/golang.org/x/oauth2/oauth2_test.go @@ -0,0 +1,551 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "golang.org/x/net/context" +) + +type mockTransport struct { + rt func(req *http.Request) (resp *http.Response, err error) +} + +func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + return t.rt(req) +} + +func newConf(url string) *Config { + return &Config{ + ClientID: "CLIENT_ID", + ClientSecret: "CLIENT_SECRET", + RedirectURL: "REDIRECT_URL", + Scopes: []string{"scope1", "scope2"}, + Endpoint: Endpoint{ + AuthURL: url + "/auth", + TokenURL: url + "/token", + }, + } +} + +func TestAuthCodeURL(t *testing.T) { + conf := newConf("server") + url := conf.AuthCodeURL("foo", AccessTypeOffline, ApprovalForce) + const want = "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo" + if got := url; got != want { + t.Errorf("got auth code URL = %q; want %q", got, want) + } +} + +func TestAuthCodeURL_CustomParam(t *testing.T) { + conf := newConf("server") + param := SetAuthURLParam("foo", "bar") + url := conf.AuthCodeURL("baz", param) + const want = "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz" + if got := url; got != want { + t.Errorf("got auth code = %q; want %q", got, want) + } +} + +func TestAuthCodeURL_Optional(t *testing.T) { + conf := &Config{ + ClientID: "CLIENT_ID", + Endpoint: Endpoint{ + AuthURL: "/auth-url", + TokenURL: "/token-url", + }, + } + url := conf.AuthCodeURL("") + const want = "/auth-url?client_id=CLIENT_ID&response_type=code" + if got := url; got != want { + t.Fatalf("got auth code = %q; want %q", got, want) + } +} + +func TestURLUnsafeClientConfig(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Authorization"), "Basic Q0xJRU5UX0lEJTNGJTNGOkNMSUVOVF9TRUNSRVQlM0YlM0Y="; got != want { + t.Errorf("Authorization header = %q; want %q", got, want) + } + + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer")) + })) + defer ts.Close() + conf := newConf(ts.URL) + conf.ClientID = "CLIENT_ID??" + conf.ClientSecret = "CLIENT_SECRET??" + _, err := conf.Exchange(context.Background(), "exchange-code") + if err != nil { + t.Error(err) + } +} + +func TestExchangeRequest(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() != "/token" { + t.Errorf("Unexpected exchange request URL, %v is found.", r.URL) + } + headerAuth := r.Header.Get("Authorization") + if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" { + t.Errorf("Unexpected authorization header, %v is found.", headerAuth) + } + headerContentType := r.Header.Get("Content-Type") + if headerContentType != "application/x-www-form-urlencoded" { + t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Failed reading request body: %s.", err) + } + if string(body) != "code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL" { + t.Errorf("Unexpected exchange payload, %v is found.", string(body)) + } + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer")) + })) + defer ts.Close() + conf := newConf(ts.URL) + tok, err := conf.Exchange(context.Background(), "exchange-code") + if err != nil { + t.Error(err) + } + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) + } + if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { + t.Errorf("Unexpected access token, %#v.", tok.AccessToken) + } + if tok.TokenType != "bearer" { + t.Errorf("Unexpected token type, %#v.", tok.TokenType) + } + scope := tok.Extra("scope") + if scope != "user" { + t.Errorf("Unexpected value for scope: %v", scope) + } +} + +func TestExchangeRequest_CustomParam(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() != "/token" { + t.Errorf("Unexpected exchange request URL, %v is found.", r.URL) + } + headerAuth := r.Header.Get("Authorization") + if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" { + t.Errorf("Unexpected authorization header, %v is found.", headerAuth) + } + headerContentType := r.Header.Get("Content-Type") + if headerContentType != "application/x-www-form-urlencoded" { + t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Failed reading request body: %s.", err) + } + if string(body) != "code=exchange-code&foo=bar&grant_type=authorization_code&redirect_uri=REDIRECT_URL" { + t.Errorf("Unexpected exchange payload, %v is found.", string(body)) + } + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer")) + })) + defer ts.Close() + conf := newConf(ts.URL) + + param := SetAuthURLParam("foo", "bar") + tok, err := conf.Exchange(context.Background(), "exchange-code", param) + if err != nil { + t.Error(err) + } + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) + } + if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { + t.Errorf("Unexpected access token, %#v.", tok.AccessToken) + } + if tok.TokenType != "bearer" { + t.Errorf("Unexpected token type, %#v.", tok.TokenType) + } + scope := tok.Extra("scope") + if scope != "user" { + t.Errorf("Unexpected value for scope: %v", scope) + } +} + +func TestExchangeRequest_JSONResponse(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() != "/token" { + t.Errorf("Unexpected exchange request URL, %v is found.", r.URL) + } + headerAuth := r.Header.Get("Authorization") + if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" { + t.Errorf("Unexpected authorization header, %v is found.", headerAuth) + } + headerContentType := r.Header.Get("Content-Type") + if headerContentType != "application/x-www-form-urlencoded" { + t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Failed reading request body: %s.", err) + } + if string(body) != "code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL" { + t.Errorf("Unexpected exchange payload, %v is found.", string(body)) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"access_token": "90d64460d14870c08c81352a05dedd3465940a7c", "scope": "user", "token_type": "bearer", "expires_in": 86400}`)) + })) + defer ts.Close() + conf := newConf(ts.URL) + tok, err := conf.Exchange(context.Background(), "exchange-code") + if err != nil { + t.Error(err) + } + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) + } + if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { + t.Errorf("Unexpected access token, %#v.", tok.AccessToken) + } + if tok.TokenType != "bearer" { + t.Errorf("Unexpected token type, %#v.", tok.TokenType) + } + scope := tok.Extra("scope") + if scope != "user" { + t.Errorf("Unexpected value for scope: %v", scope) + } + expiresIn := tok.Extra("expires_in") + if expiresIn != float64(86400) { + t.Errorf("Unexpected non-numeric value for expires_in: %v", expiresIn) + } +} + +func TestExtraValueRetrieval(t *testing.T) { + values := url.Values{} + kvmap := map[string]string{ + "scope": "user", "token_type": "bearer", "expires_in": "86400.92", + "server_time": "1443571905.5606415", "referer_ip": "10.0.0.1", + "etag": "\"afZYj912P4alikMz_P11982\"", "request_id": "86400", + "untrimmed": " untrimmed ", + } + for key, value := range kvmap { + values.Set(key, value) + } + + tok := Token{raw: values} + scope := tok.Extra("scope") + if got, want := scope, "user"; got != want { + t.Errorf("got scope = %q; want %q", got, want) + } + serverTime := tok.Extra("server_time") + if got, want := serverTime, 1443571905.5606415; got != want { + t.Errorf("got server_time value = %v; want %v", got, want) + } + refererIP := tok.Extra("referer_ip") + if got, want := refererIP, "10.0.0.1"; got != want { + t.Errorf("got referer_ip value = %v, want %v", got, want) + } + expiresIn := tok.Extra("expires_in") + if got, want := expiresIn, 86400.92; got != want { + t.Errorf("got expires_in value = %v, want %v", got, want) + } + requestID := tok.Extra("request_id") + if got, want := requestID, int64(86400); got != want { + t.Errorf("got request_id value = %v, want %v", got, want) + } + untrimmed := tok.Extra("untrimmed") + if got, want := untrimmed, " untrimmed "; got != want { + t.Errorf("got untrimmed = %q; want %q", got, want) + } +} + +const day = 24 * time.Hour + +func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) { + seconds := int32(day.Seconds()) + for _, c := range []struct { + expires string + want bool + }{ + {fmt.Sprintf(`"expires_in": %d`, seconds), true}, + {fmt.Sprintf(`"expires_in": "%d"`, seconds), true}, // PayPal case + {fmt.Sprintf(`"expires": %d`, seconds), true}, // Facebook case + {`"expires": false`, false}, // wrong type + {`"expires": {}`, false}, // wrong type + {`"expires": "zzz"`, false}, // wrong value + } { + testExchangeRequest_JSONResponse_expiry(t, c.expires, c.want) + } +} + +func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, want bool) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp))) + })) + defer ts.Close() + conf := newConf(ts.URL) + t1 := time.Now().Add(day) + tok, err := conf.Exchange(context.Background(), "exchange-code") + t2 := time.Now().Add(day) + + if got := (err == nil); got != want { + if want { + t.Errorf("unexpected error: got %v", err) + } else { + t.Errorf("unexpected success") + } + } + if !want { + return + } + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) + } + expiry := tok.Expiry + if expiry.Before(t1) || expiry.After(t2) { + t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2) + } +} + +func TestExchangeRequest_BadResponse(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`)) + })) + defer ts.Close() + conf := newConf(ts.URL) + _, err := conf.Exchange(context.Background(), "code") + if err == nil { + t.Error("expected error from missing access_token") + } +} + +func TestExchangeRequest_BadResponseType(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`)) + })) + defer ts.Close() + conf := newConf(ts.URL) + _, err := conf.Exchange(context.Background(), "exchange-code") + if err == nil { + t.Error("expected error from non-string access_token") + } +} + +func TestExchangeRequest_NonBasicAuth(t *testing.T) { + tr := &mockTransport{ + rt: func(r *http.Request) (w *http.Response, err error) { + headerAuth := r.Header.Get("Authorization") + if headerAuth != "" { + t.Errorf("Unexpected authorization header, %v is found.", headerAuth) + } + return nil, errors.New("no response") + }, + } + c := &http.Client{Transport: tr} + conf := &Config{ + ClientID: "CLIENT_ID", + Endpoint: Endpoint{ + AuthURL: "https://accounts.google.com/auth", + TokenURL: "https://accounts.google.com/token", + }, + } + + ctx := context.WithValue(context.Background(), HTTPClient, c) + conf.Exchange(ctx, "code") +} + +func TestPasswordCredentialsTokenRequest(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + expected := "/token" + if r.URL.String() != expected { + t.Errorf("URL = %q; want %q", r.URL, expected) + } + headerAuth := r.Header.Get("Authorization") + expected = "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" + if headerAuth != expected { + t.Errorf("Authorization header = %q; want %q", headerAuth, expected) + } + headerContentType := r.Header.Get("Content-Type") + expected = "application/x-www-form-urlencoded" + if headerContentType != expected { + t.Errorf("Content-Type header = %q; want %q", headerContentType, expected) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Failed reading request body: %s.", err) + } + expected = "grant_type=password&password=password1&scope=scope1+scope2&username=user1" + if string(body) != expected { + t.Errorf("res.Body = %q; want %q", string(body), expected) + } + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer")) + })) + defer ts.Close() + conf := newConf(ts.URL) + tok, err := conf.PasswordCredentialsToken(context.Background(), "user1", "password1") + if err != nil { + t.Error(err) + } + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) + } + expected := "90d64460d14870c08c81352a05dedd3465940a7c" + if tok.AccessToken != expected { + t.Errorf("AccessToken = %q; want %q", tok.AccessToken, expected) + } + expected = "bearer" + if tok.TokenType != expected { + t.Errorf("TokenType = %q; want %q", tok.TokenType, expected) + } +} + +func TestTokenRefreshRequest(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() == "/somethingelse" { + return + } + if r.URL.String() != "/token" { + t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL) + } + headerContentType := r.Header.Get("Content-Type") + if headerContentType != "application/x-www-form-urlencoded" { + t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + } + body, _ := ioutil.ReadAll(r.Body) + if string(body) != "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" { + t.Errorf("Unexpected refresh token payload, %v is found.", string(body)) + } + })) + defer ts.Close() + conf := newConf(ts.URL) + c := conf.Client(context.Background(), &Token{RefreshToken: "REFRESH_TOKEN"}) + c.Get(ts.URL + "/somethingelse") +} + +func TestFetchWithNoRefreshToken(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() == "/somethingelse" { + return + } + if r.URL.String() != "/token" { + t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL) + } + headerContentType := r.Header.Get("Content-Type") + if headerContentType != "application/x-www-form-urlencoded" { + t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + } + body, _ := ioutil.ReadAll(r.Body) + if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" { + t.Errorf("Unexpected refresh token payload, %v is found.", string(body)) + } + })) + defer ts.Close() + conf := newConf(ts.URL) + c := conf.Client(context.Background(), nil) + _, err := c.Get(ts.URL + "/somethingelse") + if err == nil { + t.Errorf("Fetch should return an error if no refresh token is set") + } +} + +func TestTokenRetrieveError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() != "/token" { + t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL) + } + w.Header().Set("Content-type", "application/json") + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(`{"error": "invalid_grant"}`)) + })) + defer ts.Close() + conf := newConf(ts.URL) + _, err := conf.Exchange(context.Background(), "exchange-code") + if err == nil { + t.Fatalf("got no error, expected one") + } + _, ok := err.(*RetrieveError) + if !ok { + t.Fatalf("got %T error, expected *RetrieveError", err) + } + // Test error string for backwards compatibility + expected := fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", "400 Bad Request", `{"error": "invalid_grant"}`) + if errStr := err.Error(); errStr != expected { + t.Fatalf("got %#v, expected %#v", errStr, expected) + } +} + +func TestRefreshToken_RefreshTokenReplacement(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"access_token":"ACCESS_TOKEN", "scope": "user", "token_type": "bearer", "refresh_token": "NEW_REFRESH_TOKEN"}`)) + return + })) + defer ts.Close() + conf := newConf(ts.URL) + tkr := conf.TokenSource(context.Background(), &Token{RefreshToken: "OLD_REFRESH_TOKEN"}) + tk, err := tkr.Token() + if err != nil { + t.Errorf("got err = %v; want none", err) + return + } + if want := "NEW_REFRESH_TOKEN"; tk.RefreshToken != want { + t.Errorf("RefreshToken = %q; want %q", tk.RefreshToken, want) + } +} + +func TestRefreshToken_RefreshTokenPreservation(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"access_token":"ACCESS_TOKEN", "scope": "user", "token_type": "bearer"}`)) + return + })) + defer ts.Close() + conf := newConf(ts.URL) + const oldRefreshToken = "OLD_REFRESH_TOKEN" + tkr := conf.TokenSource(context.Background(), &Token{RefreshToken: oldRefreshToken}) + tk, err := tkr.Token() + if err != nil { + t.Fatalf("got err = %v; want none", err) + } + if tk.RefreshToken != oldRefreshToken { + t.Errorf("RefreshToken = %q; want %q", tk.RefreshToken, oldRefreshToken) + } +} + +func TestConfigClientWithToken(t *testing.T) { + tok := &Token{ + AccessToken: "abc123", + } + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Authorization"), fmt.Sprintf("Bearer %s", tok.AccessToken); got != want { + t.Errorf("Authorization header = %q; want %q", got, want) + } + return + })) + defer ts.Close() + conf := newConf(ts.URL) + + c := conf.Client(context.Background(), tok) + req, err := http.NewRequest("GET", ts.URL, nil) + if err != nil { + t.Error(err) + } + _, err = c.Do(req) + if err != nil { + t.Error(err) + } +} diff --git a/vendor/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go b/vendor/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go new file mode 100644 index 0000000000..c0d093ccc5 --- /dev/null +++ b/vendor/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package odnoklassniki provides constants for using OAuth2 to access Odnoklassniki. +package odnoklassniki // import "golang.org/x/oauth2/odnoklassniki" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Odnoklassniki's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.odnoklassniki.ru/oauth/authorize", + TokenURL: "https://api.odnoklassniki.ru/oauth/token.do", +} diff --git a/vendor/golang.org/x/oauth2/paypal/paypal.go b/vendor/golang.org/x/oauth2/paypal/paypal.go new file mode 100644 index 0000000000..2e713c53c8 --- /dev/null +++ b/vendor/golang.org/x/oauth2/paypal/paypal.go @@ -0,0 +1,22 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package paypal provides constants for using OAuth2 to access PayPal. +package paypal // import "golang.org/x/oauth2/paypal" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is PayPal's OAuth 2.0 endpoint in live (production) environment. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", + TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice", +} + +// SandboxEndpoint is PayPal's OAuth 2.0 endpoint in sandbox (testing) environment. +var SandboxEndpoint = oauth2.Endpoint{ + AuthURL: "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", + TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice", +} diff --git a/vendor/golang.org/x/oauth2/slack/slack.go b/vendor/golang.org/x/oauth2/slack/slack.go new file mode 100644 index 0000000000..593d2f607e --- /dev/null +++ b/vendor/golang.org/x/oauth2/slack/slack.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package slack provides constants for using OAuth2 to access Slack. +package slack // import "golang.org/x/oauth2/slack" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Slack's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://slack.com/oauth/authorize", + TokenURL: "https://slack.com/api/oauth.access", +} diff --git a/vendor/golang.org/x/oauth2/spotify/spotify.go b/vendor/golang.org/x/oauth2/spotify/spotify.go new file mode 100644 index 0000000000..c75416c008 --- /dev/null +++ b/vendor/golang.org/x/oauth2/spotify/spotify.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package spotify provides constants for using OAuth2 to access Spotify. +package spotify // import "golang.org/x/oauth2/spotify" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Spotify's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://accounts.spotify.com/authorize", + TokenURL: "https://accounts.spotify.com/api/token", +} diff --git a/vendor/golang.org/x/oauth2/stackoverflow/stackoverflow.go b/vendor/golang.org/x/oauth2/stackoverflow/stackoverflow.go new file mode 100644 index 0000000000..82711f7774 --- /dev/null +++ b/vendor/golang.org/x/oauth2/stackoverflow/stackoverflow.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package stackoverflow provides constants for using OAuth2 to access Stack Overflow. +package stackoverflow // import "golang.org/x/oauth2/stackoverflow" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Stack Overflow's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://stackoverflow.com/oauth", + TokenURL: "https://stackoverflow.com/oauth/access_token", +} diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go new file mode 100644 index 0000000000..34db8cdc8a --- /dev/null +++ b/vendor/golang.org/x/oauth2/token.go @@ -0,0 +1,175 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2/internal" +) + +// expiryDelta determines how earlier a token should be considered +// expired than its actual expiration time. It is used to avoid late +// expirations due to client-server time mismatches. +const expiryDelta = 10 * time.Second + +// Token represents the credentials used to authorize +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// Most users of this package should not access fields of Token +// directly. They're exported mostly for use by related packages +// implementing derivative OAuth2 flows. +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + AccessToken string `json:"access_token"` + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + TokenType string `json:"token_type,omitempty"` + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + RefreshToken string `json:"refresh_token,omitempty"` + + // Expiry is the optional expiration time of the access token. + // + // If zero, TokenSource implementations will reuse the same + // token forever and RefreshToken or equivalent + // mechanisms for that TokenSource will not be used. + Expiry time.Time `json:"expiry,omitempty"` + + // raw optionally contains extra metadata from the server + // when updating a token. + raw interface{} +} + +// Type returns t.TokenType if non-empty, else "Bearer". +func (t *Token) Type() string { + if strings.EqualFold(t.TokenType, "bearer") { + return "Bearer" + } + if strings.EqualFold(t.TokenType, "mac") { + return "MAC" + } + if strings.EqualFold(t.TokenType, "basic") { + return "Basic" + } + if t.TokenType != "" { + return t.TokenType + } + return "Bearer" +} + +// SetAuthHeader sets the Authorization header to r using the access +// token in t. +// +// This method is unnecessary when using Transport or an HTTP Client +// returned by this package. +func (t *Token) SetAuthHeader(r *http.Request) { + r.Header.Set("Authorization", t.Type()+" "+t.AccessToken) +} + +// WithExtra returns a new Token that's a clone of t, but using the +// provided raw extra map. This is only intended for use by packages +// implementing derivative OAuth2 flows. +func (t *Token) WithExtra(extra interface{}) *Token { + t2 := new(Token) + *t2 = *t + t2.raw = extra + return t2 +} + +// Extra returns an extra field. +// Extra fields are key-value pairs returned by the server as a +// part of the token retrieval response. +func (t *Token) Extra(key string) interface{} { + if raw, ok := t.raw.(map[string]interface{}); ok { + return raw[key] + } + + vals, ok := t.raw.(url.Values) + if !ok { + return nil + } + + v := vals.Get(key) + switch s := strings.TrimSpace(v); strings.Count(s, ".") { + case 0: // Contains no "."; try to parse as int + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + return i + } + case 1: // Contains a single "."; try to parse as float + if f, err := strconv.ParseFloat(s, 64); err == nil { + return f + } + } + + return v +} + +// expired reports whether the token is expired. +// t must be non-nil. +func (t *Token) expired() bool { + if t.Expiry.IsZero() { + return false + } + return t.Expiry.Round(0).Add(-expiryDelta).Before(time.Now()) +} + +// Valid reports whether t is non-nil, has an AccessToken, and is not expired. +func (t *Token) Valid() bool { + return t != nil && t.AccessToken != "" && !t.expired() +} + +// tokenFromInternal maps an *internal.Token struct into +// a *Token struct. +func tokenFromInternal(t *internal.Token) *Token { + if t == nil { + return nil + } + return &Token{ + AccessToken: t.AccessToken, + TokenType: t.TokenType, + RefreshToken: t.RefreshToken, + Expiry: t.Expiry, + raw: t.Raw, + } +} + +// retrieveToken takes a *Config and uses that to retrieve an *internal.Token. +// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along +// with an error.. +func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { + tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v) + if err != nil { + if rErr, ok := err.(*internal.RetrieveError); ok { + return nil, (*RetrieveError)(rErr) + } + return nil, err + } + return tokenFromInternal(tk), nil +} + +// RetrieveError is the error returned when the token endpoint returns a +// non-2XX HTTP status code. +type RetrieveError struct { + Response *http.Response + // Body is the body that was consumed by reading Response.Body. + // It may be truncated. + Body []byte +} + +func (r *RetrieveError) Error() string { + return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body) +} diff --git a/vendor/golang.org/x/oauth2/token_test.go b/vendor/golang.org/x/oauth2/token_test.go new file mode 100644 index 0000000000..80db83c29c --- /dev/null +++ b/vendor/golang.org/x/oauth2/token_test.go @@ -0,0 +1,72 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "testing" + "time" +) + +func TestTokenExtra(t *testing.T) { + type testCase struct { + key string + val interface{} + want interface{} + } + const key = "extra-key" + cases := []testCase{ + {key: key, val: "abc", want: "abc"}, + {key: key, val: 123, want: 123}, + {key: key, val: "", want: ""}, + {key: "other-key", val: "def", want: nil}, + } + for _, tc := range cases { + extra := make(map[string]interface{}) + extra[tc.key] = tc.val + tok := &Token{raw: extra} + if got, want := tok.Extra(key), tc.want; got != want { + t.Errorf("Extra(%q) = %q; want %q", key, got, want) + } + } +} + +func TestTokenExpiry(t *testing.T) { + now := time.Now() + cases := []struct { + name string + tok *Token + want bool + }{ + {name: "12 seconds", tok: &Token{Expiry: now.Add(12 * time.Second)}, want: false}, + {name: "10 seconds", tok: &Token{Expiry: now.Add(expiryDelta)}, want: true}, + {name: "-1 hour", tok: &Token{Expiry: now.Add(-1 * time.Hour)}, want: true}, + } + for _, tc := range cases { + if got, want := tc.tok.expired(), tc.want; got != want { + t.Errorf("expired (%q) = %v; want %v", tc.name, got, want) + } + } +} + +func TestTokenTypeMethod(t *testing.T) { + cases := []struct { + name string + tok *Token + want string + }{ + {name: "bearer-mixed_case", tok: &Token{TokenType: "beAREr"}, want: "Bearer"}, + {name: "default-bearer", tok: &Token{}, want: "Bearer"}, + {name: "basic", tok: &Token{TokenType: "basic"}, want: "Basic"}, + {name: "basic-capitalized", tok: &Token{TokenType: "Basic"}, want: "Basic"}, + {name: "mac", tok: &Token{TokenType: "mac"}, want: "MAC"}, + {name: "mac-caps", tok: &Token{TokenType: "MAC"}, want: "MAC"}, + {name: "mac-mixed_case", tok: &Token{TokenType: "mAc"}, want: "MAC"}, + } + for _, tc := range cases { + if got, want := tc.tok.Type(), tc.want; got != want { + t.Errorf("TokenType(%q) = %v; want %v", tc.name, got, want) + } + } +} diff --git a/vendor/golang.org/x/oauth2/transport.go b/vendor/golang.org/x/oauth2/transport.go new file mode 100644 index 0000000000..aa0d34f1e0 --- /dev/null +++ b/vendor/golang.org/x/oauth2/transport.go @@ -0,0 +1,144 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "errors" + "io" + "net/http" + "sync" +) + +// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests, +// wrapping a base RoundTripper and adding an Authorization header +// with a token from the supplied Sources. +// +// Transport is a low-level mechanism. Most code will use the +// higher-level Config.Client method instead. +type Transport struct { + // Source supplies the token to add to outgoing requests' + // Authorization headers. + Source TokenSource + + // Base is the base RoundTripper used to make HTTP requests. + // If nil, http.DefaultTransport is used. + Base http.RoundTripper + + mu sync.Mutex // guards modReq + modReq map[*http.Request]*http.Request // original -> modified +} + +// RoundTrip authorizes and authenticates the request with an +// access token from Transport's Source. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + reqBodyClosed := false + if req.Body != nil { + defer func() { + if !reqBodyClosed { + req.Body.Close() + } + }() + } + + if t.Source == nil { + return nil, errors.New("oauth2: Transport's Source is nil") + } + token, err := t.Source.Token() + if err != nil { + return nil, err + } + + req2 := cloneRequest(req) // per RoundTripper contract + token.SetAuthHeader(req2) + t.setModReq(req, req2) + res, err := t.base().RoundTrip(req2) + + // req.Body is assumed to have been closed by the base RoundTripper. + reqBodyClosed = true + + if err != nil { + t.setModReq(req, nil) + return nil, err + } + res.Body = &onEOFReader{ + rc: res.Body, + fn: func() { t.setModReq(req, nil) }, + } + return res, nil +} + +// CancelRequest cancels an in-flight request by closing its connection. +func (t *Transport) CancelRequest(req *http.Request) { + type canceler interface { + CancelRequest(*http.Request) + } + if cr, ok := t.base().(canceler); ok { + t.mu.Lock() + modReq := t.modReq[req] + delete(t.modReq, req) + t.mu.Unlock() + cr.CancelRequest(modReq) + } +} + +func (t *Transport) base() http.RoundTripper { + if t.Base != nil { + return t.Base + } + return http.DefaultTransport +} + +func (t *Transport) setModReq(orig, mod *http.Request) { + t.mu.Lock() + defer t.mu.Unlock() + if t.modReq == nil { + t.modReq = make(map[*http.Request]*http.Request) + } + if mod == nil { + delete(t.modReq, orig) + } else { + t.modReq[orig] = mod + } +} + +// cloneRequest returns a clone of the provided *http.Request. +// The clone is a shallow copy of the struct and its Header map. +func cloneRequest(r *http.Request) *http.Request { + // shallow copy of the struct + r2 := new(http.Request) + *r2 = *r + // deep copy of the Header + r2.Header = make(http.Header, len(r.Header)) + for k, s := range r.Header { + r2.Header[k] = append([]string(nil), s...) + } + return r2 +} + +type onEOFReader struct { + rc io.ReadCloser + fn func() +} + +func (r *onEOFReader) Read(p []byte) (n int, err error) { + n, err = r.rc.Read(p) + if err == io.EOF { + r.runFunc() + } + return +} + +func (r *onEOFReader) Close() error { + err := r.rc.Close() + r.runFunc() + return err +} + +func (r *onEOFReader) runFunc() { + if fn := r.fn; fn != nil { + fn() + r.fn = nil + } +} diff --git a/vendor/golang.org/x/oauth2/transport_test.go b/vendor/golang.org/x/oauth2/transport_test.go new file mode 100644 index 0000000000..faa87d5140 --- /dev/null +++ b/vendor/golang.org/x/oauth2/transport_test.go @@ -0,0 +1,168 @@ +package oauth2 + +import ( + "errors" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +type tokenSource struct{ token *Token } + +func (t *tokenSource) Token() (*Token, error) { + return t.token, nil +} + +func TestTransportNilTokenSource(t *testing.T) { + tr := &Transport{} + server := newMockServer(func(w http.ResponseWriter, r *http.Request) {}) + defer server.Close() + client := &http.Client{Transport: tr} + resp, err := client.Get(server.URL) + if err == nil { + t.Errorf("got no errors, want an error with nil token source") + } + if resp != nil { + t.Errorf("Response = %v; want nil", resp) + } +} + +type readCloseCounter struct { + CloseCount int + ReadErr error +} + +func (r *readCloseCounter) Read(b []byte) (int, error) { + return 0, r.ReadErr +} + +func (r *readCloseCounter) Close() error { + r.CloseCount++ + return nil +} + +func TestTransportCloseRequestBody(t *testing.T) { + tr := &Transport{} + server := newMockServer(func(w http.ResponseWriter, r *http.Request) {}) + defer server.Close() + client := &http.Client{Transport: tr} + body := &readCloseCounter{ + ReadErr: errors.New("readCloseCounter.Read not implemented"), + } + resp, err := client.Post(server.URL, "application/json", body) + if err == nil { + t.Errorf("got no errors, want an error with nil token source") + } + if resp != nil { + t.Errorf("Response = %v; want nil", resp) + } + if expected := 1; body.CloseCount != expected { + t.Errorf("Body was closed %d times, expected %d", body.CloseCount, expected) + } +} + +func TestTransportCloseRequestBodySuccess(t *testing.T) { + tr := &Transport{ + Source: StaticTokenSource(&Token{ + AccessToken: "abc", + }), + } + server := newMockServer(func(w http.ResponseWriter, r *http.Request) {}) + defer server.Close() + client := &http.Client{Transport: tr} + body := &readCloseCounter{ + ReadErr: io.EOF, + } + resp, err := client.Post(server.URL, "application/json", body) + if err != nil { + t.Errorf("got error %v; expected none", err) + } + if resp == nil { + t.Errorf("Response is nil; expected non-nil") + } + if expected := 1; body.CloseCount != expected { + t.Errorf("Body was closed %d times, expected %d", body.CloseCount, expected) + } +} + +func TestTransportTokenSource(t *testing.T) { + ts := &tokenSource{ + token: &Token{ + AccessToken: "abc", + }, + } + tr := &Transport{ + Source: ts, + } + server := newMockServer(func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Authorization"), "Bearer abc"; got != want { + t.Errorf("Authorization header = %q; want %q", got, want) + } + }) + defer server.Close() + client := &http.Client{Transport: tr} + res, err := client.Get(server.URL) + if err != nil { + t.Fatal(err) + } + res.Body.Close() +} + +// Test for case-sensitive token types, per https://github.com/golang/oauth2/issues/113 +func TestTransportTokenSourceTypes(t *testing.T) { + const val = "abc" + tests := []struct { + key string + val string + want string + }{ + {key: "bearer", val: val, want: "Bearer abc"}, + {key: "mac", val: val, want: "MAC abc"}, + {key: "basic", val: val, want: "Basic abc"}, + } + for _, tc := range tests { + ts := &tokenSource{ + token: &Token{ + AccessToken: tc.val, + TokenType: tc.key, + }, + } + tr := &Transport{ + Source: ts, + } + server := newMockServer(func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Authorization"), tc.want; got != want { + t.Errorf("Authorization header (%q) = %q; want %q", val, got, want) + } + }) + defer server.Close() + client := &http.Client{Transport: tr} + res, err := client.Get(server.URL) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + } +} + +func TestTokenValidNoAccessToken(t *testing.T) { + token := &Token{} + if token.Valid() { + t.Errorf("got valid with no access token; want invalid") + } +} + +func TestExpiredWithExpiry(t *testing.T) { + token := &Token{ + Expiry: time.Now().Add(-5 * time.Hour), + } + if token.Valid() { + t.Errorf("got valid with expired token; want invalid") + } +} + +func newMockServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(handler)) +} diff --git a/vendor/golang.org/x/oauth2/twitch/twitch.go b/vendor/golang.org/x/oauth2/twitch/twitch.go new file mode 100644 index 0000000000..0838e7c15f --- /dev/null +++ b/vendor/golang.org/x/oauth2/twitch/twitch.go @@ -0,0 +1,19 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package twitch provides constants for using OAuth2 to access Twitch. +package twitch // import "golang.org/x/oauth2/twitch" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Twitch's OAuth 2.0 endpoint. +// +// For more information see: +// https://dev.twitch.tv/docs/authentication +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://id.twitch.tv/oauth2/authorize", + TokenURL: "https://id.twitch.tv/oauth2/token", +} diff --git a/vendor/golang.org/x/oauth2/uber/uber.go b/vendor/golang.org/x/oauth2/uber/uber.go new file mode 100644 index 0000000000..5520a64552 --- /dev/null +++ b/vendor/golang.org/x/oauth2/uber/uber.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package uber provides constants for using OAuth2 to access Uber. +package uber // import "golang.org/x/oauth2/uber" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Uber's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://login.uber.com/oauth/v2/authorize", + TokenURL: "https://login.uber.com/oauth/v2/token", +} diff --git a/vendor/golang.org/x/oauth2/vk/vk.go b/vendor/golang.org/x/oauth2/vk/vk.go new file mode 100644 index 0000000000..bd8e159488 --- /dev/null +++ b/vendor/golang.org/x/oauth2/vk/vk.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package vk provides constants for using OAuth2 to access VK.com. +package vk // import "golang.org/x/oauth2/vk" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is VK's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://oauth.vk.com/authorize", + TokenURL: "https://oauth.vk.com/access_token", +} diff --git a/vendor/golang.org/x/oauth2/yahoo/yahoo.go b/vendor/golang.org/x/oauth2/yahoo/yahoo.go new file mode 100644 index 0000000000..9fa78a23c1 --- /dev/null +++ b/vendor/golang.org/x/oauth2/yahoo/yahoo.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package yahoo provides constants for using OAuth2 to access Yahoo. +package yahoo // import "golang.org/x/oauth2/yahoo" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Yahoo's OAuth 2.0 endpoint. +// See https://developer.yahoo.com/oauth2/guide/ +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://api.login.yahoo.com/oauth2/request_auth", + TokenURL: "https://api.login.yahoo.com/oauth2/get_token", +} diff --git a/vendor/golang.org/x/oauth2/yandex/yandex.go b/vendor/golang.org/x/oauth2/yandex/yandex.go new file mode 100644 index 0000000000..5ebf666d20 --- /dev/null +++ b/vendor/golang.org/x/oauth2/yandex/yandex.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package yandex provides constants for using OAuth2 to access Yandex APIs. +package yandex // import "golang.org/x/oauth2/yandex" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is the Yandex OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://oauth.yandex.com/authorize", + TokenURL: "https://oauth.yandex.com/token", +} diff --git a/vendor/google.golang.org/appengine/CONTRIBUTING.md b/vendor/google.golang.org/appengine/CONTRIBUTING.md new file mode 100644 index 0000000000..ffc2985208 --- /dev/null +++ b/vendor/google.golang.org/appengine/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# Contributing + +1. Sign one of the contributor license agreements below. +1. Get the package: + + `go get -d google.golang.org/appengine` +1. Change into the checked out source: + + `cd $GOPATH/src/google.golang.org/appengine` +1. Fork the repo. +1. Set your fork as a remote: + + `git remote add fork git@github.com:GITHUB_USERNAME/appengine.git` +1. Make changes, commit to your fork. +1. Send a pull request with your changes. + The first line of your commit message is conventionally a one-line summary of the change, prefixed by the primary affected package, and is used as the title of your pull request. + +# Testing + +## Running system tests + +Download and install the [Go App Engine SDK](https://cloud.google.com/appengine/docs/go/download). Make sure the `go_appengine` dir is in your `PATH`. + +Set the `APPENGINE_DEV_APPSERVER` environment variable to `/path/to/go_appengine/dev_appserver.py`. + +Run tests with `goapp test`: + +``` +goapp test -v google.golang.org/appengine/... +``` + +## Contributor License Agreements + +Before we can accept your pull requests you'll need to sign a Contributor +License Agreement (CLA): + +- **If you are an individual writing original source code** and **you own the +intellectual property**, then you'll need to sign an [individual CLA][indvcla]. +- **If you work for a company that wants to allow you to contribute your work**, +then you'll need to sign a [corporate CLA][corpcla]. + +You can sign these electronically (just scroll to the bottom). After that, +we'll be able to accept your pull requests. + +## Contributor Code of Conduct + +As contributors and maintainers of this project, +and in the interest of fostering an open and welcoming community, +we pledge to respect all people who contribute through reporting issues, +posting feature requests, updating documentation, +submitting pull requests or patches, and other activities. + +We are committed to making participation in this project +a harassment-free experience for everyone, +regardless of level of experience, gender, gender identity and expression, +sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, +such as physical or electronic +addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. +By adopting this Code of Conduct, +project maintainers commit themselves to fairly and consistently +applying these principles to every aspect of managing this project. +Project maintainers who do not follow or enforce the Code of Conduct +may be permanently removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior +may be reported by opening an issue +or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) + +[indvcla]: https://developers.google.com/open-source/cla/individual +[corpcla]: https://developers.google.com/open-source/cla/corporate diff --git a/vendor/google.golang.org/appengine/LICENSE b/vendor/google.golang.org/appengine/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/google.golang.org/appengine/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/google.golang.org/appengine/README.md b/vendor/google.golang.org/appengine/README.md new file mode 100644 index 0000000000..d86768a2c6 --- /dev/null +++ b/vendor/google.golang.org/appengine/README.md @@ -0,0 +1,73 @@ +# Go App Engine packages + +[![Build Status](https://travis-ci.org/golang/appengine.svg)](https://travis-ci.org/golang/appengine) + +This repository supports the Go runtime on *App Engine standard*. +It provides APIs for interacting with App Engine services. +Its canonical import path is `google.golang.org/appengine`. + +See https://cloud.google.com/appengine/docs/go/ +for more information. + +File issue reports and feature requests on the [GitHub's issue +tracker](https://github.com/golang/appengine/issues). + +## Upgrading an App Engine app to the flexible environment + +This package does not work on *App Engine flexible*. + +There are many differences between the App Engine standard environment and +the flexible environment. + +See the [documentation on upgrading to the flexible environment](https://cloud.google.com/appengine/docs/flexible/go/upgrading). + +## Directory structure + +The top level directory of this repository is the `appengine` package. It +contains the +basic APIs (e.g. `appengine.NewContext`) that apply across APIs. Specific API +packages are in subdirectories (e.g. `datastore`). + +There is an `internal` subdirectory that contains service protocol buffers, +plus packages required for connectivity to make API calls. App Engine apps +should not directly import any package under `internal`. + +## Updating from legacy (`import "appengine"`) packages + +If you're currently using the bare `appengine` packages +(that is, not these ones, imported via `google.golang.org/appengine`), +then you can use the `aefix` tool to help automate an upgrade to these packages. + +Run `go get google.golang.org/appengine/cmd/aefix` to install it. + +### 1. Update import paths + +The import paths for App Engine packages are now fully qualified, based at `google.golang.org/appengine`. +You will need to update your code to use import paths starting with that; for instance, +code importing `appengine/datastore` will now need to import `google.golang.org/appengine/datastore`. + +### 2. Update code using deprecated, removed or modified APIs + +Most App Engine services are available with exactly the same API. +A few APIs were cleaned up, and there are some differences: + +* `appengine.Context` has been replaced with the `Context` type from `golang.org/x/net/context`. +* Logging methods that were on `appengine.Context` are now functions in `google.golang.org/appengine/log`. +* `appengine.Timeout` has been removed. Use `context.WithTimeout` instead. +* `appengine.Datacenter` now takes a `context.Context` argument. +* `datastore.PropertyLoadSaver` has been simplified to use slices in place of channels. +* `delay.Call` now returns an error. +* `search.FieldLoadSaver` now handles document metadata. +* `urlfetch.Transport` no longer has a Deadline field; set a deadline on the + `context.Context` instead. +* `aetest` no longer declares its own Context type, and uses the standard one instead. +* `taskqueue.QueueStats` no longer takes a maxTasks argument. That argument has been + deprecated and unused for a long time. +* `appengine.BackendHostname` and `appengine.BackendInstance` were for the deprecated backends feature. + Use `appengine.ModuleHostname`and `appengine.ModuleName` instead. +* Most of `appengine/file` and parts of `appengine/blobstore` are deprecated. + Use [Google Cloud Storage](https://godoc.org/cloud.google.com/go/storage) if the + feature you require is not present in the new + [blobstore package](https://google.golang.org/appengine/blobstore). +* `appengine/socket` is not required on App Engine flexible environment / Managed VMs. + Use the standard `net` package instead. diff --git a/vendor/google.golang.org/appengine/aetest/doc.go b/vendor/google.golang.org/appengine/aetest/doc.go new file mode 100644 index 0000000000..86ce8c2c06 --- /dev/null +++ b/vendor/google.golang.org/appengine/aetest/doc.go @@ -0,0 +1,42 @@ +/* +Package aetest provides an API for running dev_appserver for use in tests. + +An example test file: + + package foo_test + + import ( + "testing" + + "google.golang.org/appengine/memcache" + "google.golang.org/appengine/aetest" + ) + + func TestFoo(t *testing.T) { + ctx, done, err := aetest.NewContext() + if err != nil { + t.Fatal(err) + } + defer done() + + it := &memcache.Item{ + Key: "some-key", + Value: []byte("some-value"), + } + err = memcache.Set(ctx, it) + if err != nil { + t.Fatalf("Set err: %v", err) + } + it, err = memcache.Get(ctx, "some-key") + if err != nil { + t.Fatalf("Get err: %v; want no error", err) + } + if g, w := string(it.Value), "some-value" ; g != w { + t.Errorf("retrieved Item.Value = %q, want %q", g, w) + } + } + +The environment variable APPENGINE_DEV_APPSERVER specifies the location of the +dev_appserver.py executable to use. If unset, the system PATH is consulted. +*/ +package aetest diff --git a/vendor/google.golang.org/appengine/aetest/instance.go b/vendor/google.golang.org/appengine/aetest/instance.go new file mode 100644 index 0000000000..38c1d4ef24 --- /dev/null +++ b/vendor/google.golang.org/appengine/aetest/instance.go @@ -0,0 +1,58 @@ +package aetest + +import ( + "io" + "net/http" + "time" + + "golang.org/x/net/context" + "google.golang.org/appengine" +) + +// Instance represents a running instance of the development API Server. +type Instance interface { + // Close kills the child api_server.py process, releasing its resources. + io.Closer + // NewRequest returns an *http.Request associated with this instance. + NewRequest(method, urlStr string, body io.Reader) (*http.Request, error) +} + +// Options is used to specify options when creating an Instance. +type Options struct { + // AppID specifies the App ID to use during tests. + // By default, "testapp". + AppID string + // StronglyConsistentDatastore is whether the local datastore should be + // strongly consistent. This will diverge from production behaviour. + StronglyConsistentDatastore bool + // SuppressDevAppServerLog is whether the dev_appserver running in tests + // should output logs. + SuppressDevAppServerLog bool + // StartupTimeout is a duration to wait for instance startup. + // By default, 15 seconds. + StartupTimeout time.Duration +} + +// NewContext starts an instance of the development API server, and returns +// a context that will route all API calls to that server, as well as a +// closure that must be called when the Context is no longer required. +func NewContext() (context.Context, func(), error) { + inst, err := NewInstance(nil) + if err != nil { + return nil, nil, err + } + req, err := inst.NewRequest("GET", "/", nil) + if err != nil { + inst.Close() + return nil, nil, err + } + ctx := appengine.NewContext(req) + return ctx, func() { + inst.Close() + }, nil +} + +// PrepareDevAppserver is a hook which, if set, will be called before the +// dev_appserver.py is started, each time it is started. If aetest.NewContext +// is invoked from the goapp test tool, this hook is unnecessary. +var PrepareDevAppserver func() error diff --git a/vendor/google.golang.org/appengine/aetest/instance_classic.go b/vendor/google.golang.org/appengine/aetest/instance_classic.go new file mode 100644 index 0000000000..fbceaa5056 --- /dev/null +++ b/vendor/google.golang.org/appengine/aetest/instance_classic.go @@ -0,0 +1,21 @@ +// +build appengine + +package aetest + +import "appengine/aetest" + +// NewInstance launches a running instance of api_server.py which can be used +// for multiple test Contexts that delegate all App Engine API calls to that +// instance. +// If opts is nil the default values are used. +func NewInstance(opts *Options) (Instance, error) { + aetest.PrepareDevAppserver = PrepareDevAppserver + var aeOpts *aetest.Options + if opts != nil { + aeOpts = &aetest.Options{ + AppID: opts.AppID, + StronglyConsistentDatastore: opts.StronglyConsistentDatastore, + } + } + return aetest.NewInstance(aeOpts) +} diff --git a/vendor/google.golang.org/appengine/aetest/instance_test.go b/vendor/google.golang.org/appengine/aetest/instance_test.go new file mode 100644 index 0000000000..e7003afd96 --- /dev/null +++ b/vendor/google.golang.org/appengine/aetest/instance_test.go @@ -0,0 +1,119 @@ +package aetest + +import ( + "os" + "testing" + + "google.golang.org/appengine" + "google.golang.org/appengine/datastore" + "google.golang.org/appengine/internal" + "google.golang.org/appengine/memcache" + "google.golang.org/appengine/user" +) + +func TestBasicAPICalls(t *testing.T) { + // Only run the test if APPENGINE_DEV_APPSERVER is explicitly set. + if os.Getenv("APPENGINE_DEV_APPSERVER") == "" { + t.Skip("APPENGINE_DEV_APPSERVER not set") + } + resetEnv := internal.SetTestEnv() + defer resetEnv() + + inst, err := NewInstance(nil) + if err != nil { + t.Fatalf("NewInstance: %v", err) + } + defer inst.Close() + + req, err := inst.NewRequest("GET", "http://example.com/page", nil) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + ctx := appengine.NewContext(req) + + it := &memcache.Item{ + Key: "some-key", + Value: []byte("some-value"), + } + err = memcache.Set(ctx, it) + if err != nil { + t.Fatalf("Set err: %v", err) + } + it, err = memcache.Get(ctx, "some-key") + if err != nil { + t.Fatalf("Get err: %v; want no error", err) + } + if g, w := string(it.Value), "some-value"; g != w { + t.Errorf("retrieved Item.Value = %q, want %q", g, w) + } + + type Entity struct{ Value string } + e := &Entity{Value: "foo"} + k := datastore.NewIncompleteKey(ctx, "Entity", nil) + k, err = datastore.Put(ctx, k, e) + if err != nil { + t.Fatalf("datastore.Put: %v", err) + } + e = new(Entity) + if err := datastore.Get(ctx, k, e); err != nil { + t.Fatalf("datastore.Get: %v", err) + } + if g, w := e.Value, "foo"; g != w { + t.Errorf("retrieved Entity.Value = %q, want %q", g, w) + } +} + +func TestContext(t *testing.T) { + // Only run the test if APPENGINE_DEV_APPSERVER is explicitly set. + if os.Getenv("APPENGINE_DEV_APPSERVER") == "" { + t.Skip("APPENGINE_DEV_APPSERVER not set") + } + + // Check that the context methods work. + _, done, err := NewContext() + if err != nil { + t.Fatalf("NewContext: %v", err) + } + done() +} + +func TestUsers(t *testing.T) { + // Only run the test if APPENGINE_DEV_APPSERVER is explicitly set. + if os.Getenv("APPENGINE_DEV_APPSERVER") == "" { + t.Skip("APPENGINE_DEV_APPSERVER not set") + } + + inst, err := NewInstance(nil) + if err != nil { + t.Fatalf("NewInstance: %v", err) + } + defer inst.Close() + + req, err := inst.NewRequest("GET", "http://example.com/page", nil) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + ctx := appengine.NewContext(req) + + if user := user.Current(ctx); user != nil { + t.Errorf("user.Current initially %v, want nil", user) + } + + u := &user.User{ + Email: "gopher@example.com", + Admin: true, + } + Login(u, req) + + if got := user.Current(ctx); got.Email != u.Email { + t.Errorf("user.Current: %v, want %v", got, u) + } + if admin := user.IsAdmin(ctx); !admin { + t.Errorf("user.IsAdmin: %t, want true", admin) + } + + Logout(req) + if user := user.Current(ctx); user != nil { + t.Errorf("user.Current after logout %v, want nil", user) + } +} diff --git a/vendor/google.golang.org/appengine/aetest/instance_vm.go b/vendor/google.golang.org/appengine/aetest/instance_vm.go new file mode 100644 index 0000000000..dcb87d5b83 --- /dev/null +++ b/vendor/google.golang.org/appengine/aetest/instance_vm.go @@ -0,0 +1,284 @@ +// +build !appengine + +package aetest + +import ( + "bufio" + "crypto/rand" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "os/exec" + "path/filepath" + "regexp" + "time" + + "golang.org/x/net/context" + "google.golang.org/appengine/internal" +) + +// NewInstance launches a running instance of api_server.py which can be used +// for multiple test Contexts that delegate all App Engine API calls to that +// instance. +// If opts is nil the default values are used. +func NewInstance(opts *Options) (Instance, error) { + i := &instance{ + opts: opts, + appID: "testapp", + startupTimeout: 15 * time.Second, + } + if opts != nil { + if opts.AppID != "" { + i.appID = opts.AppID + } + if opts.StartupTimeout > 0 { + i.startupTimeout = opts.StartupTimeout + } + } + if err := i.startChild(); err != nil { + return nil, err + } + return i, nil +} + +func newSessionID() string { + var buf [16]byte + io.ReadFull(rand.Reader, buf[:]) + return fmt.Sprintf("%x", buf[:]) +} + +// instance implements the Instance interface. +type instance struct { + opts *Options + child *exec.Cmd + apiURL *url.URL // base URL of API HTTP server + adminURL string // base URL of admin HTTP server + appDir string + appID string + startupTimeout time.Duration + relFuncs []func() // funcs to release any associated contexts +} + +// NewRequest returns an *http.Request associated with this instance. +func (i *instance) NewRequest(method, urlStr string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, urlStr, body) + if err != nil { + return nil, err + } + + // Associate this request. + req, release := internal.RegisterTestRequest(req, i.apiURL, func(ctx context.Context) context.Context { + ctx = internal.WithAppIDOverride(ctx, "dev~"+i.appID) + return ctx + }) + i.relFuncs = append(i.relFuncs, release) + + return req, nil +} + +// Close kills the child api_server.py process, releasing its resources. +func (i *instance) Close() (err error) { + for _, rel := range i.relFuncs { + rel() + } + i.relFuncs = nil + child := i.child + if child == nil { + return nil + } + defer func() { + i.child = nil + err1 := os.RemoveAll(i.appDir) + if err == nil { + err = err1 + } + }() + + if p := child.Process; p != nil { + errc := make(chan error, 1) + go func() { + errc <- child.Wait() + }() + + // Call the quit handler on the admin server. + res, err := http.Get(i.adminURL + "/quit") + if err != nil { + p.Kill() + return fmt.Errorf("unable to call /quit handler: %v", err) + } + res.Body.Close() + select { + case <-time.After(15 * time.Second): + p.Kill() + return errors.New("timeout killing child process") + case err = <-errc: + // Do nothing. + } + } + return +} + +func fileExists(path string) bool { + _, err := os.Stat(path) + return err == nil +} + +func findPython() (path string, err error) { + for _, name := range []string{"python2.7", "python"} { + path, err = exec.LookPath(name) + if err == nil { + return + } + } + return +} + +func findDevAppserver() (string, error) { + if p := os.Getenv("APPENGINE_DEV_APPSERVER"); p != "" { + if fileExists(p) { + return p, nil + } + return "", fmt.Errorf("invalid APPENGINE_DEV_APPSERVER environment variable; path %q doesn't exist", p) + } + return exec.LookPath("dev_appserver.py") +} + +var apiServerAddrRE = regexp.MustCompile(`Starting API server at: (\S+)`) +var adminServerAddrRE = regexp.MustCompile(`Starting admin server at: (\S+)`) + +func (i *instance) startChild() (err error) { + if PrepareDevAppserver != nil { + if err := PrepareDevAppserver(); err != nil { + return err + } + } + python, err := findPython() + if err != nil { + return fmt.Errorf("Could not find python interpreter: %v", err) + } + devAppserver, err := findDevAppserver() + if err != nil { + return fmt.Errorf("Could not find dev_appserver.py: %v", err) + } + + i.appDir, err = ioutil.TempDir("", "appengine-aetest") + if err != nil { + return err + } + defer func() { + if err != nil { + os.RemoveAll(i.appDir) + } + }() + err = os.Mkdir(filepath.Join(i.appDir, "app"), 0755) + if err != nil { + return err + } + err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "app.yaml"), []byte(i.appYAML()), 0644) + if err != nil { + return err + } + err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "stubapp.go"), []byte(appSource), 0644) + if err != nil { + return err + } + + appserverArgs := []string{ + devAppserver, + "--port=0", + "--api_port=0", + "--admin_port=0", + "--automatic_restart=false", + "--skip_sdk_update_check=true", + "--clear_datastore=true", + "--clear_search_indexes=true", + "--datastore_path", filepath.Join(i.appDir, "datastore"), + } + if i.opts != nil && i.opts.StronglyConsistentDatastore { + appserverArgs = append(appserverArgs, "--datastore_consistency_policy=consistent") + } + appserverArgs = append(appserverArgs, filepath.Join(i.appDir, "app")) + + i.child = exec.Command(python, + appserverArgs..., + ) + i.child.Stdout = os.Stdout + var stderr io.Reader + stderr, err = i.child.StderrPipe() + if err != nil { + return err + } + if !(i.opts != nil && i.opts.SuppressDevAppServerLog) { + stderr = io.TeeReader(stderr, os.Stderr) + } + if err = i.child.Start(); err != nil { + return err + } + + // Read stderr until we have read the URLs of the API server and admin interface. + errc := make(chan error, 1) + go func() { + s := bufio.NewScanner(stderr) + for s.Scan() { + if match := apiServerAddrRE.FindStringSubmatch(s.Text()); match != nil { + u, err := url.Parse(match[1]) + if err != nil { + errc <- fmt.Errorf("failed to parse API URL %q: %v", match[1], err) + return + } + i.apiURL = u + } + if match := adminServerAddrRE.FindStringSubmatch(s.Text()); match != nil { + i.adminURL = match[1] + } + if i.adminURL != "" && i.apiURL != nil { + break + } + } + errc <- s.Err() + }() + + select { + case <-time.After(i.startupTimeout): + if p := i.child.Process; p != nil { + p.Kill() + } + return errors.New("timeout starting child process") + case err := <-errc: + if err != nil { + return fmt.Errorf("error reading child process stderr: %v", err) + } + } + if i.adminURL == "" { + return errors.New("unable to find admin server URL") + } + if i.apiURL == nil { + return errors.New("unable to find API server URL") + } + return nil +} + +func (i *instance) appYAML() string { + return fmt.Sprintf(appYAMLTemplate, i.appID) +} + +const appYAMLTemplate = ` +application: %s +version: 1 +runtime: go +api_version: go1 + +handlers: +- url: /.* + script: _go_app +` + +const appSource = ` +package main +import "google.golang.org/appengine" +func main() { appengine.Main() } +` diff --git a/vendor/google.golang.org/appengine/aetest/user.go b/vendor/google.golang.org/appengine/aetest/user.go new file mode 100644 index 0000000000..bf9266f534 --- /dev/null +++ b/vendor/google.golang.org/appengine/aetest/user.go @@ -0,0 +1,36 @@ +package aetest + +import ( + "hash/crc32" + "net/http" + "strconv" + + "google.golang.org/appengine/user" +) + +// Login causes the provided Request to act as though issued by the given user. +func Login(u *user.User, req *http.Request) { + req.Header.Set("X-AppEngine-User-Email", u.Email) + id := u.ID + if id == "" { + id = strconv.Itoa(int(crc32.Checksum([]byte(u.Email), crc32.IEEETable))) + } + req.Header.Set("X-AppEngine-User-Id", id) + req.Header.Set("X-AppEngine-User-Federated-Identity", u.Email) + req.Header.Set("X-AppEngine-User-Federated-Provider", u.FederatedProvider) + if u.Admin { + req.Header.Set("X-AppEngine-User-Is-Admin", "1") + } else { + req.Header.Set("X-AppEngine-User-Is-Admin", "0") + } +} + +// Logout causes the provided Request to act as though issued by a logged-out +// user. +func Logout(req *http.Request) { + req.Header.Del("X-AppEngine-User-Email") + req.Header.Del("X-AppEngine-User-Id") + req.Header.Del("X-AppEngine-User-Is-Admin") + req.Header.Del("X-AppEngine-User-Federated-Identity") + req.Header.Del("X-AppEngine-User-Federated-Provider") +} diff --git a/vendor/google.golang.org/appengine/appengine.go b/vendor/google.golang.org/appengine/appengine.go new file mode 100644 index 0000000000..76dedc81d1 --- /dev/null +++ b/vendor/google.golang.org/appengine/appengine.go @@ -0,0 +1,113 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package appengine provides basic functionality for Google App Engine. +// +// For more information on how to write Go apps for Google App Engine, see: +// https://cloud.google.com/appengine/docs/go/ +package appengine // import "google.golang.org/appengine" + +import ( + "net/http" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +// The gophers party all night; the rabbits provide the beats. + +// Main is the principal entry point for an app running in App Engine. +// +// On App Engine Flexible it installs a trivial health checker if one isn't +// already registered, and starts listening on port 8080 (overridden by the +// $PORT environment variable). +// +// See https://cloud.google.com/appengine/docs/flexible/custom-runtimes#health_check_requests +// for details on how to do your own health checking. +// +// On App Engine Standard it ensures the server has started and is prepared to +// receive requests. +// +// Main never returns. +// +// Main is designed so that the app's main package looks like this: +// +// package main +// +// import ( +// "google.golang.org/appengine" +// +// _ "myapp/package0" +// _ "myapp/package1" +// ) +// +// func main() { +// appengine.Main() +// } +// +// The "myapp/packageX" packages are expected to register HTTP handlers +// in their init functions. +func Main() { + internal.Main() +} + +// IsDevAppServer reports whether the App Engine app is running in the +// development App Server. +func IsDevAppServer() bool { + return internal.IsDevAppServer() +} + +// NewContext returns a context for an in-flight HTTP request. +// This function is cheap. +func NewContext(req *http.Request) context.Context { + return internal.ReqContext(req) +} + +// WithContext returns a copy of the parent context +// and associates it with an in-flight HTTP request. +// This function is cheap. +func WithContext(parent context.Context, req *http.Request) context.Context { + return internal.WithContext(parent, req) +} + +// TODO(dsymonds): Add a Call function here? Otherwise other packages can't access internal.Call. + +// BlobKey is a key for a blobstore blob. +// +// Conceptually, this type belongs in the blobstore package, but it lives in +// the appengine package to avoid a circular dependency: blobstore depends on +// datastore, and datastore needs to refer to the BlobKey type. +type BlobKey string + +// GeoPoint represents a location as latitude/longitude in degrees. +type GeoPoint struct { + Lat, Lng float64 +} + +// Valid returns whether a GeoPoint is within [-90, 90] latitude and [-180, 180] longitude. +func (g GeoPoint) Valid() bool { + return -90 <= g.Lat && g.Lat <= 90 && -180 <= g.Lng && g.Lng <= 180 +} + +// APICallFunc defines a function type for handling an API call. +// See WithCallOverride. +type APICallFunc func(ctx context.Context, service, method string, in, out proto.Message) error + +// WithAPICallFunc returns a copy of the parent context +// that will cause API calls to invoke f instead of their normal operation. +// +// This is intended for advanced users only. +func WithAPICallFunc(ctx context.Context, f APICallFunc) context.Context { + return internal.WithCallOverride(ctx, internal.CallOverrideFunc(f)) +} + +// APICall performs an API call. +// +// This is not intended for general use; it is exported for use in conjunction +// with WithAPICallFunc. +func APICall(ctx context.Context, service, method string, in, out proto.Message) error { + return internal.Call(ctx, service, method, in, out) +} diff --git a/vendor/google.golang.org/appengine/appengine_test.go b/vendor/google.golang.org/appengine/appengine_test.go new file mode 100644 index 0000000000..f1cf0a1b96 --- /dev/null +++ b/vendor/google.golang.org/appengine/appengine_test.go @@ -0,0 +1,49 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package appengine + +import ( + "testing" +) + +func TestValidGeoPoint(t *testing.T) { + testCases := []struct { + desc string + pt GeoPoint + want bool + }{ + { + "valid", + GeoPoint{67.21, 13.37}, + true, + }, + { + "high lat", + GeoPoint{-90.01, 13.37}, + false, + }, + { + "low lat", + GeoPoint{90.01, 13.37}, + false, + }, + { + "high lng", + GeoPoint{67.21, 182}, + false, + }, + { + "low lng", + GeoPoint{67.21, -181}, + false, + }, + } + + for _, tc := range testCases { + if got := tc.pt.Valid(); got != tc.want { + t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want) + } + } +} diff --git a/vendor/google.golang.org/appengine/appengine_vm.go b/vendor/google.golang.org/appengine/appengine_vm.go new file mode 100644 index 0000000000..f4b645aad3 --- /dev/null +++ b/vendor/google.golang.org/appengine/appengine_vm.go @@ -0,0 +1,20 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package appengine + +import ( + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +// BackgroundContext returns a context not associated with a request. +// This should only be used when not servicing a request. +// This only works in App Engine "flexible environment". +func BackgroundContext() context.Context { + return internal.BackgroundContext() +} diff --git a/vendor/google.golang.org/appengine/blobstore/blobstore.go b/vendor/google.golang.org/appengine/blobstore/blobstore.go new file mode 100644 index 0000000000..dea25acca3 --- /dev/null +++ b/vendor/google.golang.org/appengine/blobstore/blobstore.go @@ -0,0 +1,306 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package blobstore provides a client for App Engine's persistent blob +// storage service. +package blobstore // import "google.golang.org/appengine/blobstore" + +import ( + "bufio" + "bytes" + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "mime" + "mime/multipart" + "net/http" + "net/textproto" + "net/url" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + "golang.org/x/text/encoding/htmlindex" + + "google.golang.org/appengine" + "google.golang.org/appengine/datastore" + "google.golang.org/appengine/internal" + + basepb "google.golang.org/appengine/internal/base" + blobpb "google.golang.org/appengine/internal/blobstore" +) + +const ( + blobInfoKind = "__BlobInfo__" + blobFileIndexKind = "__BlobFileIndex__" + zeroKey = appengine.BlobKey("") +) + +// BlobInfo is the blob metadata that is stored in the datastore. +// Filename may be empty. +type BlobInfo struct { + BlobKey appengine.BlobKey + ContentType string `datastore:"content_type"` + CreationTime time.Time `datastore:"creation"` + Filename string `datastore:"filename"` + Size int64 `datastore:"size"` + MD5 string `datastore:"md5_hash"` + + // ObjectName is the Google Cloud Storage name for this blob. + ObjectName string `datastore:"gs_object_name"` +} + +// isErrFieldMismatch returns whether err is a datastore.ErrFieldMismatch. +// +// The blobstore stores blob metadata in the datastore. When loading that +// metadata, it may contain fields that we don't care about. datastore.Get will +// return datastore.ErrFieldMismatch in that case, so we ignore that specific +// error. +func isErrFieldMismatch(err error) bool { + _, ok := err.(*datastore.ErrFieldMismatch) + return ok +} + +// Stat returns the BlobInfo for a provided blobKey. If no blob was found for +// that key, Stat returns datastore.ErrNoSuchEntity. +func Stat(c context.Context, blobKey appengine.BlobKey) (*BlobInfo, error) { + c, _ = appengine.Namespace(c, "") // Blobstore is always in the empty string namespace + dskey := datastore.NewKey(c, blobInfoKind, string(blobKey), 0, nil) + bi := &BlobInfo{ + BlobKey: blobKey, + } + if err := datastore.Get(c, dskey, bi); err != nil && !isErrFieldMismatch(err) { + return nil, err + } + return bi, nil +} + +// Send sets the headers on response to instruct App Engine to send a blob as +// the response body. This is more efficient than reading and writing it out +// manually and isn't subject to normal response size limits. +func Send(response http.ResponseWriter, blobKey appengine.BlobKey) { + hdr := response.Header() + hdr.Set("X-AppEngine-BlobKey", string(blobKey)) + + if hdr.Get("Content-Type") == "" { + // This value is known to dev_appserver to mean automatic. + // In production this is remapped to the empty value which + // means automatic. + hdr.Set("Content-Type", "application/vnd.google.appengine.auto") + } +} + +// UploadURL creates an upload URL for the form that the user will +// fill out, passing the application path to load when the POST of the +// form is completed. These URLs expire and should not be reused. The +// opts parameter may be nil. +func UploadURL(c context.Context, successPath string, opts *UploadURLOptions) (*url.URL, error) { + req := &blobpb.CreateUploadURLRequest{ + SuccessPath: proto.String(successPath), + } + if opts != nil { + if n := opts.MaxUploadBytes; n != 0 { + req.MaxUploadSizeBytes = &n + } + if n := opts.MaxUploadBytesPerBlob; n != 0 { + req.MaxUploadSizePerBlobBytes = &n + } + if s := opts.StorageBucket; s != "" { + req.GsBucketName = &s + } + } + res := &blobpb.CreateUploadURLResponse{} + if err := internal.Call(c, "blobstore", "CreateUploadURL", req, res); err != nil { + return nil, err + } + return url.Parse(*res.Url) +} + +// UploadURLOptions are the options to create an upload URL. +type UploadURLOptions struct { + MaxUploadBytes int64 // optional + MaxUploadBytesPerBlob int64 // optional + + // StorageBucket specifies the Google Cloud Storage bucket in which + // to store the blob. + // This is required if you use Cloud Storage instead of Blobstore. + // Your application must have permission to write to the bucket. + // You may optionally specify a bucket name and path in the format + // "bucket_name/path", in which case the included path will be the + // prefix of the uploaded object's name. + StorageBucket string +} + +// Delete deletes a blob. +func Delete(c context.Context, blobKey appengine.BlobKey) error { + return DeleteMulti(c, []appengine.BlobKey{blobKey}) +} + +// DeleteMulti deletes multiple blobs. +func DeleteMulti(c context.Context, blobKey []appengine.BlobKey) error { + s := make([]string, len(blobKey)) + for i, b := range blobKey { + s[i] = string(b) + } + req := &blobpb.DeleteBlobRequest{ + BlobKey: s, + } + res := &basepb.VoidProto{} + if err := internal.Call(c, "blobstore", "DeleteBlob", req, res); err != nil { + return err + } + return nil +} + +func errorf(format string, args ...interface{}) error { + return fmt.Errorf("blobstore: "+format, args...) +} + +// ParseUpload parses the synthetic POST request that your app gets from +// App Engine after a user's successful upload of blobs. Given the request, +// ParseUpload returns a map of the blobs received (keyed by HTML form +// element name) and other non-blob POST parameters. +func ParseUpload(req *http.Request) (blobs map[string][]*BlobInfo, other url.Values, err error) { + _, params, err := mime.ParseMediaType(req.Header.Get("Content-Type")) + if err != nil { + return nil, nil, err + } + boundary := params["boundary"] + if boundary == "" { + return nil, nil, errorf("did not find MIME multipart boundary") + } + + blobs = make(map[string][]*BlobInfo) + other = make(url.Values) + + mreader := multipart.NewReader(io.MultiReader(req.Body, strings.NewReader("\r\n\r\n")), boundary) + for { + part, perr := mreader.NextPart() + if perr == io.EOF { + break + } + if perr != nil { + return nil, nil, errorf("error reading next mime part with boundary %q (len=%d): %v", + boundary, len(boundary), perr) + } + + bi := &BlobInfo{} + ctype, params, err := mime.ParseMediaType(part.Header.Get("Content-Disposition")) + if err != nil { + return nil, nil, err + } + bi.Filename = params["filename"] + formKey := params["name"] + + ctype, params, err = mime.ParseMediaType(part.Header.Get("Content-Type")) + if err != nil { + return nil, nil, err + } + bi.BlobKey = appengine.BlobKey(params["blob-key"]) + charset := params["charset"] + + if ctype != "message/external-body" || bi.BlobKey == "" { + if formKey != "" { + slurp, serr := ioutil.ReadAll(part) + if serr != nil { + return nil, nil, errorf("error reading %q MIME part", formKey) + } + + // Handle base64 content transfer encoding. multipart.Part transparently + // handles quoted-printable, and no special handling is required for + // 7bit, 8bit, or binary. + ctype, params, err = mime.ParseMediaType(part.Header.Get("Content-Transfer-Encoding")) + if err == nil && ctype == "base64" { + slurp, serr = ioutil.ReadAll(base64.NewDecoder( + base64.StdEncoding, bytes.NewReader(slurp))) + if serr != nil { + return nil, nil, errorf("error %s decoding %q MIME part", ctype, formKey) + } + } + + // Handle charset + if charset != "" { + encoding, err := htmlindex.Get(charset) + if err != nil { + return nil, nil, errorf("error getting decoder for charset %q", charset) + } + + slurp, err = encoding.NewDecoder().Bytes(slurp) + if err != nil { + return nil, nil, errorf("error decoding from charset %q", charset) + } + } + + other[formKey] = append(other[formKey], string(slurp)) + } + continue + } + + // App Engine sends a MIME header as the body of each MIME part. + tp := textproto.NewReader(bufio.NewReader(part)) + header, mimeerr := tp.ReadMIMEHeader() + if mimeerr != nil { + return nil, nil, mimeerr + } + bi.Size, err = strconv.ParseInt(header.Get("Content-Length"), 10, 64) + if err != nil { + return nil, nil, err + } + bi.ContentType = header.Get("Content-Type") + + // Parse the time from the MIME header like: + // X-AppEngine-Upload-Creation: 2011-03-15 21:38:34.712136 + createDate := header.Get("X-AppEngine-Upload-Creation") + if createDate == "" { + return nil, nil, errorf("expected to find an X-AppEngine-Upload-Creation header") + } + bi.CreationTime, err = time.Parse("2006-01-02 15:04:05.000000", createDate) + if err != nil { + return nil, nil, errorf("error parsing X-AppEngine-Upload-Creation: %s", err) + } + + if hdr := header.Get("Content-MD5"); hdr != "" { + md5, err := base64.URLEncoding.DecodeString(hdr) + if err != nil { + return nil, nil, errorf("bad Content-MD5 %q: %v", hdr, err) + } + bi.MD5 = string(md5) + } + + // If the GCS object name was provided, record it. + bi.ObjectName = header.Get("X-AppEngine-Cloud-Storage-Object") + + blobs[formKey] = append(blobs[formKey], bi) + } + return +} + +// Reader is a blob reader. +type Reader interface { + io.Reader + io.ReaderAt + io.Seeker +} + +// NewReader returns a reader for a blob. It always succeeds; if the blob does +// not exist then an error will be reported upon first read. +func NewReader(c context.Context, blobKey appengine.BlobKey) Reader { + return openBlob(c, blobKey) +} + +// BlobKeyForFile returns a BlobKey for a Google Storage file. +// The filename should be of the form "/gs/bucket_name/object_name". +func BlobKeyForFile(c context.Context, filename string) (appengine.BlobKey, error) { + req := &blobpb.CreateEncodedGoogleStorageKeyRequest{ + Filename: &filename, + } + res := &blobpb.CreateEncodedGoogleStorageKeyResponse{} + if err := internal.Call(c, "blobstore", "CreateEncodedGoogleStorageKey", req, res); err != nil { + return "", err + } + return appengine.BlobKey(*res.BlobKey), nil +} diff --git a/vendor/google.golang.org/appengine/blobstore/blobstore_test.go b/vendor/google.golang.org/appengine/blobstore/blobstore_test.go new file mode 100644 index 0000000000..4616211ed8 --- /dev/null +++ b/vendor/google.golang.org/appengine/blobstore/blobstore_test.go @@ -0,0 +1,289 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package blobstore + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "mime/multipart" + "mime/quotedprintable" + "net/http" + "net/textproto" + "os" + "strconv" + "strings" + "testing" + + "golang.org/x/text/encoding/htmlindex" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal/aetesting" + + pb "google.golang.org/appengine/internal/blobstore" +) + +const rbs = readBufferSize + +const charsetUTF8 = "utf-8" +const charsetISO2022JP = "iso-2022-jp" +const nonASCIIStr = "Hello, 世界" + +func min(x, y int) int { + if x < y { + return x + } + return y +} + +func fakeFetchData(req *pb.FetchDataRequest, res *pb.FetchDataResponse) error { + i0 := int(*req.StartIndex) + i1 := int(*req.EndIndex + 1) // Blobstore's end-indices are inclusive; Go's are exclusive. + bk := *req.BlobKey + if i := strings.Index(bk, "."); i != -1 { + // Strip everything past the ".". + bk = bk[:i] + } + switch bk { + case "a14p": + const s = "abcdefghijklmnop" + i0 := min(len(s), i0) + i1 := min(len(s), i1) + res.Data = []byte(s[i0:i1]) + case "longBlob": + res.Data = make([]byte, i1-i0) + for i := range res.Data { + res.Data[i] = 'A' + uint8(i0/rbs) + i0++ + } + } + return nil +} + +// step is one step of a readerTest. +// It consists of a Reader method to call, the method arguments +// (lenp, offset, whence) and the expected results. +type step struct { + method string + lenp int + offset int64 + whence int + want string + wantErr error +} + +var readerTest = []struct { + blobKey string + step []step +}{ + {"noSuchBlobKey", []step{ + {"Read", 8, 0, 0, "", io.EOF}, + }}, + {"a14p.0", []step{ + // Test basic reads. + {"Read", 1, 0, 0, "a", nil}, + {"Read", 3, 0, 0, "bcd", nil}, + {"Read", 1, 0, 0, "e", nil}, + {"Read", 2, 0, 0, "fg", nil}, + // Test Seek. + {"Seek", 0, 2, os.SEEK_SET, "2", nil}, + {"Read", 5, 0, 0, "cdefg", nil}, + {"Seek", 0, 2, os.SEEK_CUR, "9", nil}, + {"Read", 1, 0, 0, "j", nil}, + // Test reads up to and past EOF. + {"Read", 5, 0, 0, "klmno", nil}, + {"Read", 5, 0, 0, "p", nil}, + {"Read", 5, 0, 0, "", io.EOF}, + // Test ReadAt. + {"ReadAt", 4, 0, 0, "abcd", nil}, + {"ReadAt", 4, 3, 0, "defg", nil}, + {"ReadAt", 4, 12, 0, "mnop", nil}, + {"ReadAt", 4, 13, 0, "nop", io.EOF}, + {"ReadAt", 4, 99, 0, "", io.EOF}, + }}, + {"a14p.1", []step{ + // Test Seek before any reads. + {"Seek", 0, 2, os.SEEK_SET, "2", nil}, + {"Read", 1, 0, 0, "c", nil}, + // Test that ReadAt doesn't affect the Read offset. + {"ReadAt", 3, 9, 0, "jkl", nil}, + {"Read", 3, 0, 0, "def", nil}, + }}, + {"a14p.2", []step{ + // Test ReadAt before any reads or seeks. + {"ReadAt", 2, 14, 0, "op", nil}, + }}, + {"longBlob.0", []step{ + // Test basic read. + {"Read", 1, 0, 0, "A", nil}, + // Test that Read returns early when the buffer is exhausted. + {"Seek", 0, rbs - 2, os.SEEK_SET, strconv.Itoa(rbs - 2), nil}, + {"Read", 5, 0, 0, "AA", nil}, + {"Read", 3, 0, 0, "BBB", nil}, + // Test that what we just read is still in the buffer. + {"Seek", 0, rbs - 2, os.SEEK_SET, strconv.Itoa(rbs - 2), nil}, + {"Read", 5, 0, 0, "AABBB", nil}, + // Test ReadAt. + {"ReadAt", 3, rbs - 4, 0, "AAA", nil}, + {"ReadAt", 6, rbs - 4, 0, "AAAABB", nil}, + {"ReadAt", 8, rbs - 4, 0, "AAAABBBB", nil}, + {"ReadAt", 5, rbs - 4, 0, "AAAAB", nil}, + {"ReadAt", 2, rbs - 4, 0, "AA", nil}, + // Test seeking backwards from the Read offset. + {"Seek", 0, 2*rbs - 8, os.SEEK_SET, strconv.Itoa(2*rbs - 8), nil}, + {"Read", 1, 0, 0, "B", nil}, + {"Read", 1, 0, 0, "B", nil}, + {"Read", 1, 0, 0, "B", nil}, + {"Read", 1, 0, 0, "B", nil}, + {"Read", 8, 0, 0, "BBBBCCCC", nil}, + }}, + {"longBlob.1", []step{ + // Test ReadAt with a slice larger than the buffer size. + {"LargeReadAt", 2*rbs - 2, 0, 0, strconv.Itoa(2*rbs - 2), nil}, + {"LargeReadAt", 2*rbs - 1, 0, 0, strconv.Itoa(2*rbs - 1), nil}, + {"LargeReadAt", 2*rbs + 0, 0, 0, strconv.Itoa(2*rbs + 0), nil}, + {"LargeReadAt", 2*rbs + 1, 0, 0, strconv.Itoa(2*rbs + 1), nil}, + {"LargeReadAt", 2*rbs + 2, 0, 0, strconv.Itoa(2*rbs + 2), nil}, + {"LargeReadAt", 2*rbs - 2, 1, 0, strconv.Itoa(2*rbs - 2), nil}, + {"LargeReadAt", 2*rbs - 1, 1, 0, strconv.Itoa(2*rbs - 1), nil}, + {"LargeReadAt", 2*rbs + 0, 1, 0, strconv.Itoa(2*rbs + 0), nil}, + {"LargeReadAt", 2*rbs + 1, 1, 0, strconv.Itoa(2*rbs + 1), nil}, + {"LargeReadAt", 2*rbs + 2, 1, 0, strconv.Itoa(2*rbs + 2), nil}, + }}, +} + +func TestReader(t *testing.T) { + for _, rt := range readerTest { + c := aetesting.FakeSingleContext(t, "blobstore", "FetchData", fakeFetchData) + r := NewReader(c, appengine.BlobKey(rt.blobKey)) + for i, step := range rt.step { + var ( + got string + gotErr error + n int + offset int64 + ) + switch step.method { + case "LargeReadAt": + p := make([]byte, step.lenp) + n, gotErr = r.ReadAt(p, step.offset) + got = strconv.Itoa(n) + case "Read": + p := make([]byte, step.lenp) + n, gotErr = r.Read(p) + got = string(p[:n]) + case "ReadAt": + p := make([]byte, step.lenp) + n, gotErr = r.ReadAt(p, step.offset) + got = string(p[:n]) + case "Seek": + offset, gotErr = r.Seek(step.offset, step.whence) + got = strconv.FormatInt(offset, 10) + default: + t.Fatalf("unknown method: %s", step.method) + } + if gotErr != step.wantErr { + t.Fatalf("%s step %d: got error %v want %v", rt.blobKey, i, gotErr, step.wantErr) + } + if got != step.want { + t.Fatalf("%s step %d: got %q want %q", rt.blobKey, i, got, step.want) + } + } + } +} + +// doPlainTextParseUploadTest tests ParseUpload's decoding of non-file form fields. +// It ensures that MIME multipart parts with Content-Type not equal to +// "message/external-body" (i.e. form fields that are not file uploads) are decoded +// correctly according to the value of their Content-Transfer-Encoding header field. +// If charset is not the empty string it will be set in the request's Content-Type +// header field, and if encoding is not the empty string then the Content-Transfer-Encoding +// header field will be set. +func doPlainTextParseUploadTest(t *testing.T, charset string, encoding string, + rawContent string, encodedContent string) { + bodyBuf := &bytes.Buffer{} + w := multipart.NewWriter(bodyBuf) + + fieldName := "foo" + hdr := textproto.MIMEHeader{} + hdr.Set("Content-Disposition", fmt.Sprintf("form-data; name=%q", fieldName)) + + if charset != "" { + hdr.Set("Content-Type", fmt.Sprintf("text/plain; charset=%q", charset)) + } else { + hdr.Set("Content-Type", "text/plain") + } + + if encoding != "" { + hdr.Set("Content-Transfer-Encoding", encoding) + } + + pw, err := w.CreatePart(hdr) + if err != nil { + t.Fatalf("error creating part: %v", err) + } + pw.Write([]byte(encodedContent)) + + if err := w.Close(); err != nil { + t.Fatalf("error closing multipart writer: %v\n", err) + } + + req, err := http.NewRequest("POST", "/upload", bodyBuf) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + + req.Header.Set("Content-Type", w.FormDataContentType()) + _, other, err := ParseUpload(req) + if err != nil { + t.Fatalf("error parsing upload: %v", err) + } + + if other[fieldName][0] != rawContent { + t.Errorf("got %q expected %q", other[fieldName][0], rawContent) + } +} + +func TestParseUploadUTF8Base64Encoding(t *testing.T) { + encoded := base64.StdEncoding.EncodeToString([]byte(nonASCIIStr)) + doPlainTextParseUploadTest(t, charsetUTF8, "base64", nonASCIIStr, encoded) +} + +func TestParseUploadUTF8Base64EncodingMultiline(t *testing.T) { + testStr := "words words words words words words words words words words words words" + encoded := "d29yZHMgd29yZHMgd29yZHMgd29yZHMgd29yZHMgd29yZHMgd29yZHMgd29yZHMgd29yZHMgd29y\r\nZHMgd29yZHMgd29yZHM=" + doPlainTextParseUploadTest(t, charsetUTF8, "base64", testStr, encoded) +} + +func TestParseUploadUTF8QuotedPrintableEncoding(t *testing.T) { + var encoded bytes.Buffer + writer := quotedprintable.NewWriter(&encoded) + writer.Write([]byte(nonASCIIStr)) + writer.Close() + + doPlainTextParseUploadTest(t, charsetUTF8, "quoted-printable", nonASCIIStr, + encoded.String()) +} + +func TestParseUploadISO2022JPBase64Encoding(t *testing.T) { + testStr := "こんにちは" + encoding, err := htmlindex.Get(charsetISO2022JP) + if err != nil { + t.Fatalf("error getting encoding: %v", err) + } + + charsetEncoded, err := encoding.NewEncoder().String(testStr) + if err != nil { + t.Fatalf("error encoding string: %v", err) + } + + base64Encoded := base64.StdEncoding.EncodeToString([]byte(charsetEncoded)) + doPlainTextParseUploadTest(t, charsetISO2022JP, "base64", testStr, base64Encoded) +} + +func TestParseUploadNoEncoding(t *testing.T) { + doPlainTextParseUploadTest(t, "", "", "Hello", "Hello") +} diff --git a/vendor/google.golang.org/appengine/blobstore/read.go b/vendor/google.golang.org/appengine/blobstore/read.go new file mode 100644 index 0000000000..578b1f550a --- /dev/null +++ b/vendor/google.golang.org/appengine/blobstore/read.go @@ -0,0 +1,160 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package blobstore + +import ( + "errors" + "fmt" + "io" + "os" + "sync" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + + blobpb "google.golang.org/appengine/internal/blobstore" +) + +// openBlob returns a reader for a blob. It always succeeds; if the blob does +// not exist then an error will be reported upon first read. +func openBlob(c context.Context, blobKey appengine.BlobKey) Reader { + return &reader{ + c: c, + blobKey: blobKey, + } +} + +const readBufferSize = 256 * 1024 + +// reader is a blob reader. It implements the Reader interface. +type reader struct { + c context.Context + + // Either blobKey or filename is set: + blobKey appengine.BlobKey + filename string + + closeFunc func() // is nil if unavailable or already closed. + + // buf is the read buffer. r is how much of buf has been read. + // off is the offset of buf[0] relative to the start of the blob. + // An invariant is 0 <= r && r <= len(buf). + // Reads that don't require an RPC call will increment r but not off. + // Seeks may modify r without discarding the buffer, but only if the + // invariant can be maintained. + mu sync.Mutex + buf []byte + r int + off int64 +} + +func (r *reader) Close() error { + if f := r.closeFunc; f != nil { + f() + } + r.closeFunc = nil + return nil +} + +func (r *reader) Read(p []byte) (int, error) { + if len(p) == 0 { + return 0, nil + } + r.mu.Lock() + defer r.mu.Unlock() + if r.r == len(r.buf) { + if err := r.fetch(r.off + int64(r.r)); err != nil { + return 0, err + } + } + n := copy(p, r.buf[r.r:]) + r.r += n + return n, nil +} + +func (r *reader) ReadAt(p []byte, off int64) (int, error) { + if len(p) == 0 { + return 0, nil + } + r.mu.Lock() + defer r.mu.Unlock() + // Convert relative offsets to absolute offsets. + ab0 := r.off + int64(r.r) + ab1 := r.off + int64(len(r.buf)) + ap0 := off + ap1 := off + int64(len(p)) + // Check if we can satisfy the read entirely out of the existing buffer. + if r.off <= ap0 && ap1 <= ab1 { + // Convert off from an absolute offset to a relative offset. + rp0 := int(ap0 - r.off) + return copy(p, r.buf[rp0:]), nil + } + // Restore the original Read/Seek offset after ReadAt completes. + defer r.seek(ab0) + // Repeatedly fetch and copy until we have filled p. + n := 0 + for len(p) > 0 { + if err := r.fetch(off + int64(n)); err != nil { + return n, err + } + r.r = copy(p, r.buf) + n += r.r + p = p[r.r:] + } + return n, nil +} + +func (r *reader) Seek(offset int64, whence int) (ret int64, err error) { + r.mu.Lock() + defer r.mu.Unlock() + switch whence { + case os.SEEK_SET: + ret = offset + case os.SEEK_CUR: + ret = r.off + int64(r.r) + offset + case os.SEEK_END: + return 0, errors.New("seeking relative to the end of a blob isn't supported") + default: + return 0, fmt.Errorf("invalid Seek whence value: %d", whence) + } + if ret < 0 { + return 0, errors.New("negative Seek offset") + } + return r.seek(ret) +} + +// fetch fetches readBufferSize bytes starting at the given offset. On success, +// the data is saved as r.buf. +func (r *reader) fetch(off int64) error { + req := &blobpb.FetchDataRequest{ + BlobKey: proto.String(string(r.blobKey)), + StartIndex: proto.Int64(off), + EndIndex: proto.Int64(off + readBufferSize - 1), // EndIndex is inclusive. + } + res := &blobpb.FetchDataResponse{} + if err := internal.Call(r.c, "blobstore", "FetchData", req, res); err != nil { + return err + } + if len(res.Data) == 0 { + return io.EOF + } + r.buf, r.r, r.off = res.Data, 0, off + return nil +} + +// seek seeks to the given offset with an effective whence equal to SEEK_SET. +// It discards the read buffer if the invariant cannot be maintained. +func (r *reader) seek(off int64) (int64, error) { + delta := off - r.off + if delta >= 0 && delta < int64(len(r.buf)) { + r.r = int(delta) + return off, nil + } + r.buf, r.r, r.off = nil, 0, off + return off, nil +} diff --git a/vendor/google.golang.org/appengine/capability/capability.go b/vendor/google.golang.org/appengine/capability/capability.go new file mode 100644 index 0000000000..3a60bd55fe --- /dev/null +++ b/vendor/google.golang.org/appengine/capability/capability.go @@ -0,0 +1,52 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package capability exposes information about outages and scheduled downtime +for specific API capabilities. + +This package does not work in App Engine "flexible environment". + +Example: + if !capability.Enabled(c, "datastore_v3", "write") { + // show user a different page + } +*/ +package capability // import "google.golang.org/appengine/capability" + +import ( + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + "google.golang.org/appengine/log" + + pb "google.golang.org/appengine/internal/capability" +) + +// Enabled returns whether an API's capabilities are enabled. +// The wildcard "*" capability matches every capability of an API. +// If the underlying RPC fails (if the package is unknown, for example), +// false is returned and information is written to the application log. +func Enabled(ctx context.Context, api, capability string) bool { + req := &pb.IsEnabledRequest{ + Package: &api, + Capability: []string{capability}, + } + res := &pb.IsEnabledResponse{} + if err := internal.Call(ctx, "capability_service", "IsEnabled", req, res); err != nil { + log.Warningf(ctx, "capability.Enabled: RPC failed: %v", err) + return false + } + switch *res.SummaryStatus { + case pb.IsEnabledResponse_ENABLED, + pb.IsEnabledResponse_SCHEDULED_FUTURE, + pb.IsEnabledResponse_SCHEDULED_NOW: + return true + case pb.IsEnabledResponse_UNKNOWN: + log.Errorf(ctx, "capability.Enabled: unknown API capability %s/%s", api, capability) + return false + default: + return false + } +} diff --git a/vendor/google.golang.org/appengine/channel/channel.go b/vendor/google.golang.org/appengine/channel/channel.go new file mode 100644 index 0000000000..96945f6d68 --- /dev/null +++ b/vendor/google.golang.org/appengine/channel/channel.go @@ -0,0 +1,87 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package channel implements the server side of App Engine's Channel API. + +Create creates a new channel associated with the given clientID, +which must be unique to the client that will use the returned token. + + token, err := channel.Create(c, "player1") + if err != nil { + // handle error + } + // return token to the client in an HTTP response + +Send sends a message to the client over the channel identified by clientID. + + channel.Send(c, "player1", "Game over!") + +Deprecated: The Channel API feature has been deprecated and is going to be removed. See the Channel API Turndown document for details and timetable. + +https://cloud.google.com/appengine/docs/deprecations/channel +*/ +package channel // import "google.golang.org/appengine/channel" + +import ( + "encoding/json" + + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + basepb "google.golang.org/appengine/internal/base" + pb "google.golang.org/appengine/internal/channel" +) + +// Create creates a channel and returns a token for use by the client. +// The clientID is an application-provided string used to identify the client. +func Create(c context.Context, clientID string) (token string, err error) { + req := &pb.CreateChannelRequest{ + ApplicationKey: &clientID, + } + resp := &pb.CreateChannelResponse{} + err = internal.Call(c, service, "CreateChannel", req, resp) + token = resp.GetToken() + return token, remapError(err) +} + +// Send sends a message on the channel associated with clientID. +func Send(c context.Context, clientID, message string) error { + req := &pb.SendMessageRequest{ + ApplicationKey: &clientID, + Message: &message, + } + resp := &basepb.VoidProto{} + return remapError(internal.Call(c, service, "SendChannelMessage", req, resp)) +} + +// SendJSON is a helper function that sends a JSON-encoded value +// on the channel associated with clientID. +func SendJSON(c context.Context, clientID string, value interface{}) error { + m, err := json.Marshal(value) + if err != nil { + return err + } + return Send(c, clientID, string(m)) +} + +// remapError fixes any APIError referencing "xmpp" into one referencing "channel". +func remapError(err error) error { + if e, ok := err.(*internal.APIError); ok { + if e.Service == "xmpp" { + e.Service = "channel" + } + } + return err +} + +var service = "xmpp" // prod + +func init() { + if appengine.IsDevAppServer() { + service = "channel" // dev + } + internal.RegisterErrorCodeMap("channel", pb.ChannelServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/channel/channel_test.go b/vendor/google.golang.org/appengine/channel/channel_test.go new file mode 100644 index 0000000000..c7498eb83b --- /dev/null +++ b/vendor/google.golang.org/appengine/channel/channel_test.go @@ -0,0 +1,21 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package channel + +import ( + "testing" + + "google.golang.org/appengine/internal" +) + +func TestRemapError(t *testing.T) { + err := &internal.APIError{ + Service: "xmpp", + } + err = remapError(err).(*internal.APIError) + if err.Service != "channel" { + t.Errorf("err.Service = %q, want %q", err.Service, "channel") + } +} diff --git a/vendor/google.golang.org/appengine/cloudsql/cloudsql.go b/vendor/google.golang.org/appengine/cloudsql/cloudsql.go new file mode 100644 index 0000000000..7b27e6b12d --- /dev/null +++ b/vendor/google.golang.org/appengine/cloudsql/cloudsql.go @@ -0,0 +1,62 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package cloudsql exposes access to Google Cloud SQL databases. + +This package does not work in App Engine "flexible environment". + +This package is intended for MySQL drivers to make App Engine-specific +connections. Applications should use this package through database/sql: +Select a pure Go MySQL driver that supports this package, and use sql.Open +with protocol "cloudsql" and an address of the Cloud SQL instance. + +A Go MySQL driver that has been tested to work well with Cloud SQL +is the go-sql-driver: + import "database/sql" + import _ "github.com/go-sql-driver/mysql" + + db, err := sql.Open("mysql", "user@cloudsql(project-id:instance-name)/dbname") + + +Another driver that works well with Cloud SQL is the mymysql driver: + import "database/sql" + import _ "github.com/ziutek/mymysql/godrv" + + db, err := sql.Open("mymysql", "cloudsql:instance-name*dbname/user/password") + + +Using either of these drivers, you can perform a standard SQL query. +This example assumes there is a table named 'users' with +columns 'first_name' and 'last_name': + + rows, err := db.Query("SELECT first_name, last_name FROM users") + if err != nil { + log.Errorf(ctx, "db.Query: %v", err) + } + defer rows.Close() + + for rows.Next() { + var firstName string + var lastName string + if err := rows.Scan(&firstName, &lastName); err != nil { + log.Errorf(ctx, "rows.Scan: %v", err) + continue + } + log.Infof(ctx, "First: %v - Last: %v", firstName, lastName) + } + if err := rows.Err(); err != nil { + log.Errorf(ctx, "Row error: %v", err) + } +*/ +package cloudsql + +import ( + "net" +) + +// Dial connects to the named Cloud SQL instance. +func Dial(instance string) (net.Conn, error) { + return connect(instance) +} diff --git a/vendor/google.golang.org/appengine/cloudsql/cloudsql_classic.go b/vendor/google.golang.org/appengine/cloudsql/cloudsql_classic.go new file mode 100644 index 0000000000..af62dba146 --- /dev/null +++ b/vendor/google.golang.org/appengine/cloudsql/cloudsql_classic.go @@ -0,0 +1,17 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build appengine + +package cloudsql + +import ( + "net" + + "appengine/cloudsql" +) + +func connect(instance string) (net.Conn, error) { + return cloudsql.Dial(instance) +} diff --git a/vendor/google.golang.org/appengine/cloudsql/cloudsql_vm.go b/vendor/google.golang.org/appengine/cloudsql/cloudsql_vm.go new file mode 100644 index 0000000000..90fa7b31e0 --- /dev/null +++ b/vendor/google.golang.org/appengine/cloudsql/cloudsql_vm.go @@ -0,0 +1,16 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package cloudsql + +import ( + "errors" + "net" +) + +func connect(instance string) (net.Conn, error) { + return nil, errors.New(`cloudsql: not supported in App Engine "flexible environment"`) +} diff --git a/vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go b/vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go new file mode 100644 index 0000000000..c66849e833 --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go @@ -0,0 +1,342 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Program aebundler turns a Go app into a fully self-contained tar file. +// The app and its subdirectories (if any) are placed under "." +// and the dependencies from $GOPATH are placed under ./_gopath/src. +// A main func is synthesized if one does not exist. +// +// A sample Dockerfile to be used with this bundler could look like this: +// FROM gcr.io/google-appengine/go-compat +// ADD . /app +// RUN GOPATH=/app/_gopath go build -tags appenginevm -o /app/_ah/exe +package main + +import ( + "archive/tar" + "flag" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +var ( + output = flag.String("o", "", "name of output tar file or '-' for stdout") + rootDir = flag.String("root", ".", "directory name of application root") + vm = flag.Bool("vm", true, `bundle an app for App Engine "flexible environment"`) + + skipFiles = map[string]bool{ + ".git": true, + ".gitconfig": true, + ".hg": true, + ".travis.yml": true, + } +) + +const ( + newMain = `package main +import "google.golang.org/appengine" +func main() { + appengine.Main() +} +` +) + +func usage() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "\t%s -o \tBundle app to named tar file or stdout\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "\noptional arguments:\n") + flag.PrintDefaults() +} + +func main() { + flag.Usage = usage + flag.Parse() + + var tags []string + if *vm { + tags = append(tags, "appenginevm") + } else { + tags = append(tags, "appengine") + } + + tarFile := *output + if tarFile == "" { + usage() + errorf("Required -o flag not specified.") + } + + app, err := analyze(tags) + if err != nil { + errorf("Error analyzing app: %v", err) + } + if err := app.bundle(tarFile); err != nil { + errorf("Unable to bundle app: %v", err) + } +} + +// errorf prints the error message and exits. +func errorf(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, "aebundler: "+format+"\n", a...) + os.Exit(1) +} + +type app struct { + hasMain bool + appFiles []string + imports map[string]string +} + +// analyze checks the app for building with the given build tags and returns hasMain, +// app files, and a map of full directory import names to original import names. +func analyze(tags []string) (*app, error) { + ctxt := buildContext(tags) + hasMain, appFiles, err := checkMain(ctxt) + if err != nil { + return nil, err + } + gopath := filepath.SplitList(ctxt.GOPATH) + im, err := imports(ctxt, *rootDir, gopath) + return &app{ + hasMain: hasMain, + appFiles: appFiles, + imports: im, + }, err +} + +// buildContext returns the context for building the source. +func buildContext(tags []string) *build.Context { + return &build.Context{ + GOARCH: build.Default.GOARCH, + GOOS: build.Default.GOOS, + GOROOT: build.Default.GOROOT, + GOPATH: build.Default.GOPATH, + Compiler: build.Default.Compiler, + BuildTags: append(build.Default.BuildTags, tags...), + } +} + +// bundle bundles the app into the named tarFile ("-"==stdout). +func (s *app) bundle(tarFile string) (err error) { + var out io.Writer + if tarFile == "-" { + out = os.Stdout + } else { + f, err := os.Create(tarFile) + if err != nil { + return err + } + defer func() { + if cerr := f.Close(); err == nil { + err = cerr + } + }() + out = f + } + tw := tar.NewWriter(out) + + for srcDir, importName := range s.imports { + dstDir := "_gopath/src/" + importName + if err = copyTree(tw, dstDir, srcDir); err != nil { + return fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err) + } + } + if err := copyTree(tw, ".", *rootDir); err != nil { + return fmt.Errorf("unable to copy root directory to /app: %v", err) + } + if !s.hasMain { + if err := synthesizeMain(tw, s.appFiles); err != nil { + return fmt.Errorf("unable to synthesize new main func: %v", err) + } + } + + if err := tw.Close(); err != nil { + return fmt.Errorf("unable to close tar file %v: %v", tarFile, err) + } + return nil +} + +// synthesizeMain generates a new main func and writes it to the tarball. +func synthesizeMain(tw *tar.Writer, appFiles []string) error { + appMap := make(map[string]bool) + for _, f := range appFiles { + appMap[f] = true + } + var f string + for i := 0; i < 100; i++ { + f = fmt.Sprintf("app_main%d.go", i) + if !appMap[filepath.Join(*rootDir, f)] { + break + } + } + if appMap[filepath.Join(*rootDir, f)] { + return fmt.Errorf("unable to find unique name for %v", f) + } + hdr := &tar.Header{ + Name: f, + Mode: 0644, + Size: int64(len(newMain)), + } + if err := tw.WriteHeader(hdr); err != nil { + return fmt.Errorf("unable to write header for %v: %v", f, err) + } + if _, err := tw.Write([]byte(newMain)); err != nil { + return fmt.Errorf("unable to write %v to tar file: %v", f, err) + } + return nil +} + +// imports returns a map of all import directories (recursively) used by the app. +// The return value maps full directory names to original import names. +func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) { + pkg, err := ctxt.ImportDir(srcDir, 0) + if err != nil { + return nil, fmt.Errorf("unable to analyze source: %v", err) + } + + // Resolve all non-standard-library imports + result := make(map[string]string) + for _, v := range pkg.Imports { + if !strings.Contains(v, ".") { + continue + } + src, err := findInGopath(v, gopath) + if err != nil { + return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err) + } + result[src] = v + im, err := imports(ctxt, src, gopath) + if err != nil { + return nil, fmt.Errorf("unable to parse package %v: %v", src, err) + } + for k, v := range im { + result[k] = v + } + } + return result, nil +} + +// findInGopath searches the gopath for the named import directory. +func findInGopath(dir string, gopath []string) (string, error) { + for _, v := range gopath { + dst := filepath.Join(v, "src", dir) + if _, err := os.Stat(dst); err == nil { + return dst, nil + } + } + return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath) +} + +// copyTree copies srcDir to tar file dstDir, ignoring skipFiles. +func copyTree(tw *tar.Writer, dstDir, srcDir string) error { + entries, err := ioutil.ReadDir(srcDir) + if err != nil { + return fmt.Errorf("unable to read dir %v: %v", srcDir, err) + } + for _, entry := range entries { + n := entry.Name() + if skipFiles[n] { + continue + } + s := filepath.Join(srcDir, n) + d := filepath.Join(dstDir, n) + if entry.IsDir() { + if err := copyTree(tw, d, s); err != nil { + return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err) + } + continue + } + if err := copyFile(tw, d, s); err != nil { + return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err) + } + } + return nil +} + +// copyFile copies src to tar file dst. +func copyFile(tw *tar.Writer, dst, src string) error { + s, err := os.Open(src) + if err != nil { + return fmt.Errorf("unable to open %v: %v", src, err) + } + defer s.Close() + fi, err := s.Stat() + if err != nil { + return fmt.Errorf("unable to stat %v: %v", src, err) + } + + hdr, err := tar.FileInfoHeader(fi, dst) + if err != nil { + return fmt.Errorf("unable to create tar header for %v: %v", dst, err) + } + hdr.Name = dst + if err := tw.WriteHeader(hdr); err != nil { + return fmt.Errorf("unable to write header for %v: %v", dst, err) + } + _, err = io.Copy(tw, s) + if err != nil { + return fmt.Errorf("unable to copy %v to %v: %v", src, dst, err) + } + return nil +} + +// checkMain verifies that there is a single "main" function. +// It also returns a list of all Go source files in the app. +func checkMain(ctxt *build.Context) (bool, []string, error) { + pkg, err := ctxt.ImportDir(*rootDir, 0) + if err != nil { + return false, nil, fmt.Errorf("unable to analyze source: %v", err) + } + if !pkg.IsCommand() { + errorf("Your app's package needs to be changed from %q to \"main\".\n", pkg.Name) + } + // Search for a "func main" + var hasMain bool + var appFiles []string + for _, f := range pkg.GoFiles { + n := filepath.Join(*rootDir, f) + appFiles = append(appFiles, n) + if hasMain, err = readFile(n); err != nil { + return false, nil, fmt.Errorf("error parsing %q: %v", n, err) + } + } + return hasMain, appFiles, nil +} + +// isMain returns whether the given function declaration is a main function. +// Such a function must be called "main", not have a receiver, and have no arguments or return types. +func isMain(f *ast.FuncDecl) bool { + ft := f.Type + return f.Name.Name == "main" && f.Recv == nil && ft.Params.NumFields() == 0 && ft.Results.NumFields() == 0 +} + +// readFile reads and parses the Go source code file and returns whether it has a main function. +func readFile(filename string) (hasMain bool, err error) { + var src []byte + src, err = ioutil.ReadFile(filename) + if err != nil { + return + } + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, filename, src, 0) + for _, decl := range file.Decls { + funcDecl, ok := decl.(*ast.FuncDecl) + if !ok { + continue + } + if !isMain(funcDecl) { + continue + } + hasMain = true + break + } + return +} diff --git a/vendor/google.golang.org/appengine/cmd/aedeploy/aedeploy.go b/vendor/google.golang.org/appengine/cmd/aedeploy/aedeploy.go new file mode 100644 index 0000000000..8093c93ff4 --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aedeploy/aedeploy.go @@ -0,0 +1,72 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Program aedeploy assists with deploying App Engine "flexible environment" Go apps to production. +// A temporary directory is created; the app, its subdirectories, and all its +// dependencies from $GOPATH are copied into the directory; then the app +// is deployed to production with the provided command. +// +// The app must be in "package main". +// +// This command must be issued from within the root directory of the app +// (where the app.yaml file is located). +package main + +import ( + "flag" + "fmt" + "log" + "os" + "os/exec" + "strings" +) + +func usage() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "\t%s gcloud --verbosity debug app deploy --version myversion ./app.yaml\tDeploy app to production\n", os.Args[0]) +} + +var verbose bool + +// vlogf logs to stderr if the "-v" flag is provided. +func vlogf(f string, v ...interface{}) { + if !verbose { + return + } + log.Printf("[aedeploy] "+f, v...) +} + +func main() { + flag.BoolVar(&verbose, "v", false, "Verbose logging.") + flag.Usage = usage + flag.Parse() + if flag.NArg() < 1 { + usage() + os.Exit(1) + } + + notice := func() { + fmt.Fprintln(os.Stderr, `NOTICE: aedeploy is deprecated. Just use "gcloud app deploy".`) + } + + notice() + if err := deploy(); err != nil { + fmt.Fprintf(os.Stderr, os.Args[0]+": Error: %v\n", err) + notice() + fmt.Fprintln(os.Stderr, `You might need to update gcloud. Run "gcloud components update".`) + os.Exit(1) + } + notice() // Make sure they see it at the end. +} + +// deploy calls the provided command to deploy the app from the temporary directory. +func deploy() error { + vlogf("Running command %v", flag.Args()) + cmd := exec.Command(flag.Arg(0), flag.Args()[1:]...) + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("unable to run %q: %v", strings.Join(flag.Args(), " "), err) + } + return nil +} diff --git a/vendor/google.golang.org/appengine/cmd/aefix/ae.go b/vendor/google.golang.org/appengine/cmd/aefix/ae.go new file mode 100644 index 0000000000..0fe2d4ae9f --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aefix/ae.go @@ -0,0 +1,185 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "path" + "strconv" + "strings" +) + +const ( + ctxPackage = "golang.org/x/net/context" + + newPackageBase = "google.golang.org/" + stutterPackage = false +) + +func init() { + register(fix{ + "ae", + "2016-04-15", + aeFn, + `Update old App Engine APIs to new App Engine APIs`, + }) +} + +// logMethod is the set of methods on appengine.Context used for logging. +var logMethod = map[string]bool{ + "Debugf": true, + "Infof": true, + "Warningf": true, + "Errorf": true, + "Criticalf": true, +} + +// mapPackage turns "appengine" into "google.golang.org/appengine", etc. +func mapPackage(s string) string { + if stutterPackage { + s += "/" + path.Base(s) + } + return newPackageBase + s +} + +func aeFn(f *ast.File) bool { + // During the walk, we track the last thing seen that looks like + // an appengine.Context, and reset it once the walk leaves a func. + var lastContext *ast.Ident + + fixed := false + + // Update imports. + mainImp := "appengine" + for _, imp := range f.Imports { + pth, _ := strconv.Unquote(imp.Path.Value) + if pth == "appengine" || strings.HasPrefix(pth, "appengine/") { + newPth := mapPackage(pth) + imp.Path.Value = strconv.Quote(newPth) + fixed = true + + if pth == "appengine" { + mainImp = newPth + } + } + } + + // Update any API changes. + walk(f, func(n interface{}) { + if ft, ok := n.(*ast.FuncType); ok && ft.Params != nil { + // See if this func has an `appengine.Context arg`. + // If so, remember its identifier. + for _, param := range ft.Params.List { + if !isPkgDot(param.Type, "appengine", "Context") { + continue + } + if len(param.Names) == 1 { + lastContext = param.Names[0] + break + } + } + return + } + + if as, ok := n.(*ast.AssignStmt); ok { + if len(as.Lhs) == 1 && len(as.Rhs) == 1 { + // If this node is an assignment from an appengine.NewContext invocation, + // remember the identifier on the LHS. + if isCall(as.Rhs[0], "appengine", "NewContext") { + if ident, ok := as.Lhs[0].(*ast.Ident); ok { + lastContext = ident + return + } + } + // x (=|:=) appengine.Timeout(y, z) + // should become + // x, _ (=|:=) context.WithTimeout(y, z) + if isCall(as.Rhs[0], "appengine", "Timeout") { + addImport(f, ctxPackage) + as.Lhs = append(as.Lhs, ast.NewIdent("_")) + // isCall already did the type checking. + sel := as.Rhs[0].(*ast.CallExpr).Fun.(*ast.SelectorExpr) + sel.X = ast.NewIdent("context") + sel.Sel = ast.NewIdent("WithTimeout") + fixed = true + return + } + } + return + } + + // If this node is a FuncDecl, we've finished the function, so reset lastContext. + if _, ok := n.(*ast.FuncDecl); ok { + lastContext = nil + return + } + + if call, ok := n.(*ast.CallExpr); ok { + if isPkgDot(call.Fun, "appengine", "Datacenter") && len(call.Args) == 0 { + insertContext(f, call, lastContext) + fixed = true + return + } + if isPkgDot(call.Fun, "taskqueue", "QueueStats") && len(call.Args) == 3 { + call.Args = call.Args[:2] // drop last arg + fixed = true + return + } + + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return + } + if lastContext != nil && refersTo(sel.X, lastContext) && logMethod[sel.Sel.Name] { + // c.Errorf(...) + // should become + // log.Errorf(c, ...) + addImport(f, mapPackage("appengine/log")) + sel.X = &ast.Ident{ // ast.NewIdent doesn't preserve the position. + NamePos: sel.X.Pos(), + Name: "log", + } + insertContext(f, call, lastContext) + fixed = true + return + } + } + }) + + // Change any `appengine.Context` to `context.Context`. + // Do this in a separate walk because the previous walk + // wants to identify "appengine.Context". + walk(f, func(n interface{}) { + expr, ok := n.(ast.Expr) + if ok && isPkgDot(expr, "appengine", "Context") { + addImport(f, ctxPackage) + // isPkgDot did the type checking. + n.(*ast.SelectorExpr).X.(*ast.Ident).Name = "context" + fixed = true + return + } + }) + + // The changes above might remove the need to import "appengine". + // Check if it's used, and drop it if it isn't. + if fixed && !usesImport(f, mainImp) { + deleteImport(f, mainImp) + } + + return fixed +} + +// ctx may be nil. +func insertContext(f *ast.File, call *ast.CallExpr, ctx *ast.Ident) { + if ctx == nil { + // context is unknown, so use a plain "ctx". + ctx = ast.NewIdent("ctx") + } else { + // Create a fresh *ast.Ident so we drop the position information. + ctx = ast.NewIdent(ctx.Name) + } + + call.Args = append([]ast.Expr{ctx}, call.Args...) +} diff --git a/vendor/google.golang.org/appengine/cmd/aefix/ae_test.go b/vendor/google.golang.org/appengine/cmd/aefix/ae_test.go new file mode 100644 index 0000000000..21f5695b9a --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aefix/ae_test.go @@ -0,0 +1,144 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(aeTests, nil) +} + +var aeTests = []testCase{ + // Collection of fixes: + // - imports + // - appengine.Timeout -> context.WithTimeout + // - add ctx arg to appengine.Datacenter + // - logging API + { + Name: "ae.0", + In: `package foo + +import ( + "net/http" + "time" + + "appengine" + "appengine/datastore" +) + +func f(w http.ResponseWriter, r *http.Request) { + c := appengine.NewContext(r) + + c = appengine.Timeout(c, 5*time.Second) + err := datastore.ErrNoSuchEntity + c.Errorf("Something interesting happened: %v", err) + _ = appengine.Datacenter() +} +`, + Out: `package foo + +import ( + "net/http" + "time" + + "golang.org/x/net/context" + "google.golang.org/appengine" + "google.golang.org/appengine/datastore" + "google.golang.org/appengine/log" +) + +func f(w http.ResponseWriter, r *http.Request) { + c := appengine.NewContext(r) + + c, _ = context.WithTimeout(c, 5*time.Second) + err := datastore.ErrNoSuchEntity + log.Errorf(c, "Something interesting happened: %v", err) + _ = appengine.Datacenter(c) +} +`, + }, + + // Updating a function that takes an appengine.Context arg. + { + Name: "ae.1", + In: `package foo + +import ( + "appengine" +) + +func LogSomething(c2 appengine.Context) { + c2.Warningf("Stand back! I'm going to try science!") +} +`, + Out: `package foo + +import ( + "golang.org/x/net/context" + "google.golang.org/appengine/log" +) + +func LogSomething(c2 context.Context) { + log.Warningf(c2, "Stand back! I'm going to try science!") +} +`, + }, + + // Less widely used API changes: + // - drop maxTasks arg to taskqueue.QueueStats + { + Name: "ae.2", + In: `package foo + +import ( + "appengine" + "appengine/taskqueue" +) + +func f(ctx appengine.Context) { + stats, err := taskqueue.QueueStats(ctx, []string{"one", "two"}, 0) +} +`, + Out: `package foo + +import ( + "golang.org/x/net/context" + "google.golang.org/appengine/taskqueue" +) + +func f(ctx context.Context) { + stats, err := taskqueue.QueueStats(ctx, []string{"one", "two"}) +} +`, + }, + + // Check that the main "appengine" import will not be dropped + // if an appengine.Context -> context.Context change happens + // but the appengine package is still referenced. + { + Name: "ae.3", + In: `package foo + +import ( + "appengine" + "io" +) + +func f(ctx appengine.Context, w io.Writer) { + _ = appengine.IsDevAppServer() +} +`, + Out: `package foo + +import ( + "golang.org/x/net/context" + "google.golang.org/appengine" + "io" +) + +func f(ctx context.Context, w io.Writer) { + _ = appengine.IsDevAppServer() +} +`, + }, +} diff --git a/vendor/google.golang.org/appengine/cmd/aefix/fix.go b/vendor/google.golang.org/appengine/cmd/aefix/fix.go new file mode 100644 index 0000000000..a100be794e --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aefix/fix.go @@ -0,0 +1,848 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path" + "reflect" + "strconv" + "strings" +) + +type fix struct { + name string + date string // date that fix was introduced, in YYYY-MM-DD format + f func(*ast.File) bool + desc string +} + +// main runs sort.Sort(byName(fixes)) before printing list of fixes. +type byName []fix + +func (f byName) Len() int { return len(f) } +func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f byName) Less(i, j int) bool { return f[i].name < f[j].name } + +// main runs sort.Sort(byDate(fixes)) before applying fixes. +type byDate []fix + +func (f byDate) Len() int { return len(f) } +func (f byDate) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f byDate) Less(i, j int) bool { return f[i].date < f[j].date } + +var fixes []fix + +func register(f fix) { + fixes = append(fixes, f) +} + +// walk traverses the AST x, calling visit(y) for each node y in the tree but +// also with a pointer to each ast.Expr, ast.Stmt, and *ast.BlockStmt, +// in a bottom-up traversal. +func walk(x interface{}, visit func(interface{})) { + walkBeforeAfter(x, nop, visit) +} + +func nop(interface{}) {} + +// walkBeforeAfter is like walk but calls before(x) before traversing +// x's children and after(x) afterward. +func walkBeforeAfter(x interface{}, before, after func(interface{})) { + before(x) + + switch n := x.(type) { + default: + panic(fmt.Errorf("unexpected type %T in walkBeforeAfter", x)) + + case nil: + + // pointers to interfaces + case *ast.Decl: + walkBeforeAfter(*n, before, after) + case *ast.Expr: + walkBeforeAfter(*n, before, after) + case *ast.Spec: + walkBeforeAfter(*n, before, after) + case *ast.Stmt: + walkBeforeAfter(*n, before, after) + + // pointers to struct pointers + case **ast.BlockStmt: + walkBeforeAfter(*n, before, after) + case **ast.CallExpr: + walkBeforeAfter(*n, before, after) + case **ast.FieldList: + walkBeforeAfter(*n, before, after) + case **ast.FuncType: + walkBeforeAfter(*n, before, after) + case **ast.Ident: + walkBeforeAfter(*n, before, after) + case **ast.BasicLit: + walkBeforeAfter(*n, before, after) + + // pointers to slices + case *[]ast.Decl: + walkBeforeAfter(*n, before, after) + case *[]ast.Expr: + walkBeforeAfter(*n, before, after) + case *[]*ast.File: + walkBeforeAfter(*n, before, after) + case *[]*ast.Ident: + walkBeforeAfter(*n, before, after) + case *[]ast.Spec: + walkBeforeAfter(*n, before, after) + case *[]ast.Stmt: + walkBeforeAfter(*n, before, after) + + // These are ordered and grouped to match ../../pkg/go/ast/ast.go + case *ast.Field: + walkBeforeAfter(&n.Names, before, after) + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Tag, before, after) + case *ast.FieldList: + for _, field := range n.List { + walkBeforeAfter(field, before, after) + } + case *ast.BadExpr: + case *ast.Ident: + case *ast.Ellipsis: + walkBeforeAfter(&n.Elt, before, after) + case *ast.BasicLit: + case *ast.FuncLit: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.CompositeLit: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Elts, before, after) + case *ast.ParenExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.SelectorExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.IndexExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Index, before, after) + case *ast.SliceExpr: + walkBeforeAfter(&n.X, before, after) + if n.Low != nil { + walkBeforeAfter(&n.Low, before, after) + } + if n.High != nil { + walkBeforeAfter(&n.High, before, after) + } + case *ast.TypeAssertExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Type, before, after) + case *ast.CallExpr: + walkBeforeAfter(&n.Fun, before, after) + walkBeforeAfter(&n.Args, before, after) + case *ast.StarExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.UnaryExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.BinaryExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Y, before, after) + case *ast.KeyValueExpr: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + + case *ast.ArrayType: + walkBeforeAfter(&n.Len, before, after) + walkBeforeAfter(&n.Elt, before, after) + case *ast.StructType: + walkBeforeAfter(&n.Fields, before, after) + case *ast.FuncType: + walkBeforeAfter(&n.Params, before, after) + if n.Results != nil { + walkBeforeAfter(&n.Results, before, after) + } + case *ast.InterfaceType: + walkBeforeAfter(&n.Methods, before, after) + case *ast.MapType: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + case *ast.ChanType: + walkBeforeAfter(&n.Value, before, after) + + case *ast.BadStmt: + case *ast.DeclStmt: + walkBeforeAfter(&n.Decl, before, after) + case *ast.EmptyStmt: + case *ast.LabeledStmt: + walkBeforeAfter(&n.Stmt, before, after) + case *ast.ExprStmt: + walkBeforeAfter(&n.X, before, after) + case *ast.SendStmt: + walkBeforeAfter(&n.Chan, before, after) + walkBeforeAfter(&n.Value, before, after) + case *ast.IncDecStmt: + walkBeforeAfter(&n.X, before, after) + case *ast.AssignStmt: + walkBeforeAfter(&n.Lhs, before, after) + walkBeforeAfter(&n.Rhs, before, after) + case *ast.GoStmt: + walkBeforeAfter(&n.Call, before, after) + case *ast.DeferStmt: + walkBeforeAfter(&n.Call, before, after) + case *ast.ReturnStmt: + walkBeforeAfter(&n.Results, before, after) + case *ast.BranchStmt: + case *ast.BlockStmt: + walkBeforeAfter(&n.List, before, after) + case *ast.IfStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Cond, before, after) + walkBeforeAfter(&n.Body, before, after) + walkBeforeAfter(&n.Else, before, after) + case *ast.CaseClause: + walkBeforeAfter(&n.List, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.SwitchStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Tag, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.TypeSwitchStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Assign, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.CommClause: + walkBeforeAfter(&n.Comm, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.SelectStmt: + walkBeforeAfter(&n.Body, before, after) + case *ast.ForStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Cond, before, after) + walkBeforeAfter(&n.Post, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.RangeStmt: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Body, before, after) + + case *ast.ImportSpec: + case *ast.ValueSpec: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Values, before, after) + walkBeforeAfter(&n.Names, before, after) + case *ast.TypeSpec: + walkBeforeAfter(&n.Type, before, after) + + case *ast.BadDecl: + case *ast.GenDecl: + walkBeforeAfter(&n.Specs, before, after) + case *ast.FuncDecl: + if n.Recv != nil { + walkBeforeAfter(&n.Recv, before, after) + } + walkBeforeAfter(&n.Type, before, after) + if n.Body != nil { + walkBeforeAfter(&n.Body, before, after) + } + + case *ast.File: + walkBeforeAfter(&n.Decls, before, after) + + case *ast.Package: + walkBeforeAfter(&n.Files, before, after) + + case []*ast.File: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Decl: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Expr: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []*ast.Ident: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Stmt: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Spec: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + } + after(x) +} + +// imports returns true if f imports path. +func imports(f *ast.File, path string) bool { + return importSpec(f, path) != nil +} + +// importSpec returns the import spec if f imports path, +// or nil otherwise. +func importSpec(f *ast.File, path string) *ast.ImportSpec { + for _, s := range f.Imports { + if importPath(s) == path { + return s + } + } + return nil +} + +// importPath returns the unquoted import path of s, +// or "" if the path is not properly quoted. +func importPath(s *ast.ImportSpec) string { + t, err := strconv.Unquote(s.Path.Value) + if err == nil { + return t + } + return "" +} + +// declImports reports whether gen contains an import of path. +func declImports(gen *ast.GenDecl, path string) bool { + if gen.Tok != token.IMPORT { + return false + } + for _, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if importPath(impspec) == path { + return true + } + } + return false +} + +// isPkgDot returns true if t is the expression "pkg.name" +// where pkg is an imported identifier. +func isPkgDot(t ast.Expr, pkg, name string) bool { + sel, ok := t.(*ast.SelectorExpr) + return ok && isTopName(sel.X, pkg) && sel.Sel.String() == name +} + +// isPtrPkgDot returns true if f is the expression "*pkg.name" +// where pkg is an imported identifier. +func isPtrPkgDot(t ast.Expr, pkg, name string) bool { + ptr, ok := t.(*ast.StarExpr) + return ok && isPkgDot(ptr.X, pkg, name) +} + +// isTopName returns true if n is a top-level unresolved identifier with the given name. +func isTopName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.Name == name && id.Obj == nil +} + +// isName returns true if n is an identifier with the given name. +func isName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.String() == name +} + +// isCall returns true if t is a call to pkg.name. +func isCall(t ast.Expr, pkg, name string) bool { + call, ok := t.(*ast.CallExpr) + return ok && isPkgDot(call.Fun, pkg, name) +} + +// If n is an *ast.Ident, isIdent returns it; otherwise isIdent returns nil. +func isIdent(n interface{}) *ast.Ident { + id, _ := n.(*ast.Ident) + return id +} + +// refersTo returns true if n is a reference to the same object as x. +func refersTo(n ast.Node, x *ast.Ident) bool { + id, ok := n.(*ast.Ident) + // The test of id.Name == x.Name handles top-level unresolved + // identifiers, which all have Obj == nil. + return ok && id.Obj == x.Obj && id.Name == x.Name +} + +// isBlank returns true if n is the blank identifier. +func isBlank(n ast.Expr) bool { + return isName(n, "_") +} + +// isEmptyString returns true if n is an empty string literal. +func isEmptyString(n ast.Expr) bool { + lit, ok := n.(*ast.BasicLit) + return ok && lit.Kind == token.STRING && len(lit.Value) == 2 +} + +func warn(pos token.Pos, msg string, args ...interface{}) { + if pos.IsValid() { + msg = "%s: " + msg + arg1 := []interface{}{fset.Position(pos).String()} + args = append(arg1, args...) + } + fmt.Fprintf(os.Stderr, msg+"\n", args...) +} + +// countUses returns the number of uses of the identifier x in scope. +func countUses(x *ast.Ident, scope []ast.Stmt) int { + count := 0 + ff := func(n interface{}) { + if n, ok := n.(ast.Node); ok && refersTo(n, x) { + count++ + } + } + for _, n := range scope { + walk(n, ff) + } + return count +} + +// rewriteUses replaces all uses of the identifier x and !x in scope +// with f(x.Pos()) and fnot(x.Pos()). +func rewriteUses(x *ast.Ident, f, fnot func(token.Pos) ast.Expr, scope []ast.Stmt) { + var lastF ast.Expr + ff := func(n interface{}) { + ptr, ok := n.(*ast.Expr) + if !ok { + return + } + nn := *ptr + + // The child node was just walked and possibly replaced. + // If it was replaced and this is a negation, replace with fnot(p). + not, ok := nn.(*ast.UnaryExpr) + if ok && not.Op == token.NOT && not.X == lastF { + *ptr = fnot(nn.Pos()) + return + } + if refersTo(nn, x) { + lastF = f(nn.Pos()) + *ptr = lastF + } + } + for _, n := range scope { + walk(n, ff) + } +} + +// assignsTo returns true if any of the code in scope assigns to or takes the address of x. +func assignsTo(x *ast.Ident, scope []ast.Stmt) bool { + assigned := false + ff := func(n interface{}) { + if assigned { + return + } + switch n := n.(type) { + case *ast.UnaryExpr: + // use of &x + if n.Op == token.AND && refersTo(n.X, x) { + assigned = true + return + } + case *ast.AssignStmt: + for _, l := range n.Lhs { + if refersTo(l, x) { + assigned = true + return + } + } + } + } + for _, n := range scope { + if assigned { + break + } + walk(n, ff) + } + return assigned +} + +// newPkgDot returns an ast.Expr referring to "pkg.name" at position pos. +func newPkgDot(pos token.Pos, pkg, name string) ast.Expr { + return &ast.SelectorExpr{ + X: &ast.Ident{ + NamePos: pos, + Name: pkg, + }, + Sel: &ast.Ident{ + NamePos: pos, + Name: name, + }, + } +} + +// renameTop renames all references to the top-level name old. +// It returns true if it makes any changes. +func renameTop(f *ast.File, old, new string) bool { + var fixed bool + + // Rename any conflicting imports + // (assuming package name is last element of path). + for _, s := range f.Imports { + if s.Name != nil { + if s.Name.Name == old { + s.Name.Name = new + fixed = true + } + } else { + _, thisName := path.Split(importPath(s)) + if thisName == old { + s.Name = ast.NewIdent(new) + fixed = true + } + } + } + + // Rename any top-level declarations. + for _, d := range f.Decls { + switch d := d.(type) { + case *ast.FuncDecl: + if d.Recv == nil && d.Name.Name == old { + d.Name.Name = new + d.Name.Obj.Name = new + fixed = true + } + case *ast.GenDecl: + for _, s := range d.Specs { + switch s := s.(type) { + case *ast.TypeSpec: + if s.Name.Name == old { + s.Name.Name = new + s.Name.Obj.Name = new + fixed = true + } + case *ast.ValueSpec: + for _, n := range s.Names { + if n.Name == old { + n.Name = new + n.Obj.Name = new + fixed = true + } + } + } + } + } + } + + // Rename top-level old to new, both unresolved names + // (probably defined in another file) and names that resolve + // to a declaration we renamed. + walk(f, func(n interface{}) { + id, ok := n.(*ast.Ident) + if ok && isTopName(id, old) { + id.Name = new + fixed = true + } + if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new { + id.Name = id.Obj.Name + fixed = true + } + }) + + return fixed +} + +// matchLen returns the length of the longest prefix shared by x and y. +func matchLen(x, y string) int { + i := 0 + for i < len(x) && i < len(y) && x[i] == y[i] { + i++ + } + return i +} + +// addImport adds the import path to the file f, if absent. +func addImport(f *ast.File, ipath string) (added bool) { + if imports(f, ipath) { + return false + } + + // Determine name of import. + // Assume added imports follow convention of using last element. + _, name := path.Split(ipath) + + // Rename any conflicting top-level references from name to name_. + renameTop(f, name, name+"_") + + newImport := &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote(ipath), + }, + } + + // Find an import decl to add to. + var ( + bestMatch = -1 + lastImport = -1 + impDecl *ast.GenDecl + impIndex = -1 + ) + for i, decl := range f.Decls { + gen, ok := decl.(*ast.GenDecl) + if ok && gen.Tok == token.IMPORT { + lastImport = i + // Do not add to import "C", to avoid disrupting the + // association with its doc comment, breaking cgo. + if declImports(gen, "C") { + continue + } + + // Compute longest shared prefix with imports in this block. + for j, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + n := matchLen(importPath(impspec), ipath) + if n > bestMatch { + bestMatch = n + impDecl = gen + impIndex = j + } + } + } + } + + // If no import decl found, add one after the last import. + if impDecl == nil { + impDecl = &ast.GenDecl{ + Tok: token.IMPORT, + } + f.Decls = append(f.Decls, nil) + copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) + f.Decls[lastImport+1] = impDecl + } + + // Ensure the import decl has parentheses, if needed. + if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() { + impDecl.Lparen = impDecl.Pos() + } + + insertAt := impIndex + 1 + if insertAt == 0 { + insertAt = len(impDecl.Specs) + } + impDecl.Specs = append(impDecl.Specs, nil) + copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) + impDecl.Specs[insertAt] = newImport + if insertAt > 0 { + // Assign same position as the previous import, + // so that the sorter sees it as being in the same block. + prev := impDecl.Specs[insertAt-1] + newImport.Path.ValuePos = prev.Pos() + newImport.EndPos = prev.Pos() + } + + f.Imports = append(f.Imports, newImport) + return true +} + +// deleteImport deletes the import path from the file f, if present. +func deleteImport(f *ast.File, path string) (deleted bool) { + oldImport := importSpec(f, path) + + // Find the import node that imports path, if any. + for i, decl := range f.Decls { + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT { + continue + } + for j, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if oldImport != impspec { + continue + } + + // We found an import spec that imports path. + // Delete it. + deleted = true + copy(gen.Specs[j:], gen.Specs[j+1:]) + gen.Specs = gen.Specs[:len(gen.Specs)-1] + + // If this was the last import spec in this decl, + // delete the decl, too. + if len(gen.Specs) == 0 { + copy(f.Decls[i:], f.Decls[i+1:]) + f.Decls = f.Decls[:len(f.Decls)-1] + } else if len(gen.Specs) == 1 { + gen.Lparen = token.NoPos // drop parens + } + if j > 0 { + // We deleted an entry but now there will be + // a blank line-sized hole where the import was. + // Close the hole by making the previous + // import appear to "end" where this one did. + gen.Specs[j-1].(*ast.ImportSpec).EndPos = impspec.End() + } + break + } + } + + // Delete it from f.Imports. + for i, imp := range f.Imports { + if imp == oldImport { + copy(f.Imports[i:], f.Imports[i+1:]) + f.Imports = f.Imports[:len(f.Imports)-1] + break + } + } + + return +} + +// rewriteImport rewrites any import of path oldPath to path newPath. +func rewriteImport(f *ast.File, oldPath, newPath string) (rewrote bool) { + for _, imp := range f.Imports { + if importPath(imp) == oldPath { + rewrote = true + // record old End, because the default is to compute + // it using the length of imp.Path.Value. + imp.EndPos = imp.End() + imp.Path.Value = strconv.Quote(newPath) + } + } + return +} + +func usesImport(f *ast.File, path string) (used bool) { + spec := importSpec(f, path) + if spec == nil { + return + } + + name := spec.Name.String() + switch name { + case "": + // If the package name is not explicitly specified, + // make an educated guess. This is not guaranteed to be correct. + lastSlash := strings.LastIndex(path, "/") + if lastSlash == -1 { + name = path + } else { + name = path[lastSlash+1:] + } + case "_", ".": + // Not sure if this import is used - err on the side of caution. + return true + } + + walk(f, func(n interface{}) { + sel, ok := n.(*ast.SelectorExpr) + if ok && isTopName(sel.X, name) { + used = true + } + }) + + return +} + +func expr(s string) ast.Expr { + x, err := parser.ParseExpr(s) + if err != nil { + panic("parsing " + s + ": " + err.Error()) + } + // Remove position information to avoid spurious newlines. + killPos(reflect.ValueOf(x)) + return x +} + +var posType = reflect.TypeOf(token.Pos(0)) + +func killPos(v reflect.Value) { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + if !v.IsNil() { + killPos(v.Elem()) + } + case reflect.Slice: + n := v.Len() + for i := 0; i < n; i++ { + killPos(v.Index(i)) + } + case reflect.Struct: + n := v.NumField() + for i := 0; i < n; i++ { + f := v.Field(i) + if f.Type() == posType { + f.SetInt(0) + continue + } + killPos(f) + } + } +} + +// A Rename describes a single renaming. +type rename struct { + OldImport string // only apply rename if this import is present + NewImport string // add this import during rewrite + Old string // old name: p.T or *p.T + New string // new name: p.T or *p.T +} + +func renameFix(tab []rename) func(*ast.File) bool { + return func(f *ast.File) bool { + return renameFixTab(f, tab) + } +} + +func parseName(s string) (ptr bool, pkg, nam string) { + i := strings.Index(s, ".") + if i < 0 { + panic("parseName: invalid name " + s) + } + if strings.HasPrefix(s, "*") { + ptr = true + s = s[1:] + i-- + } + pkg = s[:i] + nam = s[i+1:] + return +} + +func renameFixTab(f *ast.File, tab []rename) bool { + fixed := false + added := map[string]bool{} + check := map[string]bool{} + for _, t := range tab { + if !imports(f, t.OldImport) { + continue + } + optr, opkg, onam := parseName(t.Old) + walk(f, func(n interface{}) { + np, ok := n.(*ast.Expr) + if !ok { + return + } + x := *np + if optr { + p, ok := x.(*ast.StarExpr) + if !ok { + return + } + x = p.X + } + if !isPkgDot(x, opkg, onam) { + return + } + if t.NewImport != "" && !added[t.NewImport] { + addImport(f, t.NewImport) + added[t.NewImport] = true + } + *np = expr(t.New) + check[t.OldImport] = true + fixed = true + }) + } + + for ipath := range check { + if !usesImport(f, ipath) { + deleteImport(f, ipath) + } + } + return fixed +} diff --git a/vendor/google.golang.org/appengine/cmd/aefix/main.go b/vendor/google.golang.org/appengine/cmd/aefix/main.go new file mode 100644 index 0000000000..8e193a6ad9 --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aefix/main.go @@ -0,0 +1,258 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" +) + +var ( + fset = token.NewFileSet() + exitCode = 0 +) + +var allowedRewrites = flag.String("r", "", + "restrict the rewrites to this comma-separated list") + +var forceRewrites = flag.String("force", "", + "force these fixes to run even if the code looks updated") + +var allowed, force map[string]bool + +var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files") + +// enable for debugging fix failures +const debug = false // display incorrectly reformatted source and exit + +func usage() { + fmt.Fprintf(os.Stderr, "usage: aefix [-diff] [-r fixname,...] [-force fixname,...] [path ...]\n") + flag.PrintDefaults() + fmt.Fprintf(os.Stderr, "\nAvailable rewrites are:\n") + sort.Sort(byName(fixes)) + for _, f := range fixes { + fmt.Fprintf(os.Stderr, "\n%s\n", f.name) + desc := strings.TrimSpace(f.desc) + desc = strings.Replace(desc, "\n", "\n\t", -1) + fmt.Fprintf(os.Stderr, "\t%s\n", desc) + } + os.Exit(2) +} + +func main() { + flag.Usage = usage + flag.Parse() + + sort.Sort(byDate(fixes)) + + if *allowedRewrites != "" { + allowed = make(map[string]bool) + for _, f := range strings.Split(*allowedRewrites, ",") { + allowed[f] = true + } + } + + if *forceRewrites != "" { + force = make(map[string]bool) + for _, f := range strings.Split(*forceRewrites, ",") { + force[f] = true + } + } + + if flag.NArg() == 0 { + if err := processFile("standard input", true); err != nil { + report(err) + } + os.Exit(exitCode) + } + + for i := 0; i < flag.NArg(); i++ { + path := flag.Arg(i) + switch dir, err := os.Stat(path); { + case err != nil: + report(err) + case dir.IsDir(): + walkDir(path) + default: + if err := processFile(path, false); err != nil { + report(err) + } + } + } + + os.Exit(exitCode) +} + +const parserMode = parser.ParseComments + +func gofmtFile(f *ast.File) ([]byte, error) { + var buf bytes.Buffer + if err := format.Node(&buf, fset, f); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func processFile(filename string, useStdin bool) error { + var f *os.File + var err error + var fixlog bytes.Buffer + + if useStdin { + f = os.Stdin + } else { + f, err = os.Open(filename) + if err != nil { + return err + } + defer f.Close() + } + + src, err := ioutil.ReadAll(f) + if err != nil { + return err + } + + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err != nil { + return err + } + + // Apply all fixes to file. + newFile := file + fixed := false + for _, fix := range fixes { + if allowed != nil && !allowed[fix.name] { + continue + } + if fix.f(newFile) { + fixed = true + fmt.Fprintf(&fixlog, " %s", fix.name) + + // AST changed. + // Print and parse, to update any missing scoping + // or position information for subsequent fixers. + newSrc, err := gofmtFile(newFile) + if err != nil { + return err + } + newFile, err = parser.ParseFile(fset, filename, newSrc, parserMode) + if err != nil { + if debug { + fmt.Printf("%s", newSrc) + report(err) + os.Exit(exitCode) + } + return err + } + } + } + if !fixed { + return nil + } + fmt.Fprintf(os.Stderr, "%s: fixed %s\n", filename, fixlog.String()[1:]) + + // Print AST. We did that after each fix, so this appears + // redundant, but it is necessary to generate gofmt-compatible + // source code in a few cases. The official gofmt style is the + // output of the printer run on a standard AST generated by the parser, + // but the source we generated inside the loop above is the + // output of the printer run on a mangled AST generated by a fixer. + newSrc, err := gofmtFile(newFile) + if err != nil { + return err + } + + if *doDiff { + data, err := diff(src, newSrc) + if err != nil { + return fmt.Errorf("computing diff: %s", err) + } + fmt.Printf("diff %s fixed/%s\n", filename, filename) + os.Stdout.Write(data) + return nil + } + + if useStdin { + os.Stdout.Write(newSrc) + return nil + } + + return ioutil.WriteFile(f.Name(), newSrc, 0) +} + +var gofmtBuf bytes.Buffer + +func gofmt(n interface{}) string { + gofmtBuf.Reset() + if err := format.Node(&gofmtBuf, fset, n); err != nil { + return "<" + err.Error() + ">" + } + return gofmtBuf.String() +} + +func report(err error) { + scanner.PrintError(os.Stderr, err) + exitCode = 2 +} + +func walkDir(path string) { + filepath.Walk(path, visitFile) +} + +func visitFile(path string, f os.FileInfo, err error) error { + if err == nil && isGoFile(f) { + err = processFile(path, false) + } + if err != nil { + report(err) + } + return nil +} + +func isGoFile(f os.FileInfo) bool { + // ignore non-Go files + name := f.Name() + return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") +} + +func diff(b1, b2 []byte) (data []byte, err error) { + f1, err := ioutil.TempFile("", "go-fix") + if err != nil { + return nil, err + } + defer os.Remove(f1.Name()) + defer f1.Close() + + f2, err := ioutil.TempFile("", "go-fix") + if err != nil { + return nil, err + } + defer os.Remove(f2.Name()) + defer f2.Close() + + f1.Write(b1) + f2.Write(b2) + + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return +} diff --git a/vendor/google.golang.org/appengine/cmd/aefix/main_test.go b/vendor/google.golang.org/appengine/cmd/aefix/main_test.go new file mode 100644 index 0000000000..2151bf29e1 --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aefix/main_test.go @@ -0,0 +1,129 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "go/parser" + "strings" + "testing" +) + +type testCase struct { + Name string + Fn func(*ast.File) bool + In string + Out string +} + +var testCases []testCase + +func addTestCases(t []testCase, fn func(*ast.File) bool) { + // Fill in fn to avoid repetition in definitions. + if fn != nil { + for i := range t { + if t[i].Fn == nil { + t[i].Fn = fn + } + } + } + testCases = append(testCases, t...) +} + +func fnop(*ast.File) bool { return false } + +func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string, mustBeGofmt bool) (out string, fixed, ok bool) { + file, err := parser.ParseFile(fset, desc, in, parserMode) + if err != nil { + t.Errorf("%s: parsing: %v", desc, err) + return + } + + outb, err := gofmtFile(file) + if err != nil { + t.Errorf("%s: printing: %v", desc, err) + return + } + if s := string(outb); in != s && mustBeGofmt { + t.Errorf("%s: not gofmt-formatted.\n--- %s\n%s\n--- %s | gofmt\n%s", + desc, desc, in, desc, s) + tdiff(t, in, s) + return + } + + if fn == nil { + for _, fix := range fixes { + if fix.f(file) { + fixed = true + } + } + } else { + fixed = fn(file) + } + + outb, err = gofmtFile(file) + if err != nil { + t.Errorf("%s: printing: %v", desc, err) + return + } + + return string(outb), fixed, true +} + +func TestRewrite(t *testing.T) { + for _, tt := range testCases { + // Apply fix: should get tt.Out. + out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true) + if !ok { + continue + } + + // reformat to get printing right + out, _, ok = parseFixPrint(t, fnop, tt.Name, out, false) + if !ok { + continue + } + + if out != tt.Out { + t.Errorf("%s: incorrect output.\n", tt.Name) + if !strings.HasPrefix(tt.Name, "testdata/") { + t.Errorf("--- have\n%s\n--- want\n%s", out, tt.Out) + } + tdiff(t, out, tt.Out) + continue + } + + if changed := out != tt.In; changed != fixed { + t.Errorf("%s: changed=%v != fixed=%v", tt.Name, changed, fixed) + continue + } + + // Should not change if run again. + out2, fixed2, ok := parseFixPrint(t, tt.Fn, tt.Name+" output", out, true) + if !ok { + continue + } + + if fixed2 { + t.Errorf("%s: applied fixes during second round", tt.Name) + continue + } + + if out2 != out { + t.Errorf("%s: changed output after second round of fixes.\n--- output after first round\n%s\n--- output after second round\n%s", + tt.Name, out, out2) + tdiff(t, out, out2) + } + } +} + +func tdiff(t *testing.T, a, b string) { + data, err := diff([]byte(a), []byte(b)) + if err != nil { + t.Error(err) + return + } + t.Error(string(data)) +} diff --git a/vendor/google.golang.org/appengine/cmd/aefix/typecheck.go b/vendor/google.golang.org/appengine/cmd/aefix/typecheck.go new file mode 100644 index 0000000000..d54d375478 --- /dev/null +++ b/vendor/google.golang.org/appengine/cmd/aefix/typecheck.go @@ -0,0 +1,673 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "go/ast" + "go/token" + "os" + "reflect" + "strings" +) + +// Partial type checker. +// +// The fact that it is partial is very important: the input is +// an AST and a description of some type information to +// assume about one or more packages, but not all the +// packages that the program imports. The checker is +// expected to do as much as it can with what it has been +// given. There is not enough information supplied to do +// a full type check, but the type checker is expected to +// apply information that can be derived from variable +// declarations, function and method returns, and type switches +// as far as it can, so that the caller can still tell the types +// of expression relevant to a particular fix. +// +// TODO(rsc,gri): Replace with go/typechecker. +// Doing that could be an interesting test case for go/typechecker: +// the constraints about working with partial information will +// likely exercise it in interesting ways. The ideal interface would +// be to pass typecheck a map from importpath to package API text +// (Go source code), but for now we use data structures (TypeConfig, Type). +// +// The strings mostly use gofmt form. +// +// A Field or FieldList has as its type a comma-separated list +// of the types of the fields. For example, the field list +// x, y, z int +// has type "int, int, int". + +// The prefix "type " is the type of a type. +// For example, given +// var x int +// type T int +// x's type is "int" but T's type is "type int". +// mkType inserts the "type " prefix. +// getType removes it. +// isType tests for it. + +func mkType(t string) string { + return "type " + t +} + +func getType(t string) string { + if !isType(t) { + return "" + } + return t[len("type "):] +} + +func isType(t string) bool { + return strings.HasPrefix(t, "type ") +} + +// TypeConfig describes the universe of relevant types. +// For ease of creation, the types are all referred to by string +// name (e.g., "reflect.Value"). TypeByName is the only place +// where the strings are resolved. + +type TypeConfig struct { + Type map[string]*Type + Var map[string]string + Func map[string]string +} + +// typeof returns the type of the given name, which may be of +// the form "x" or "p.X". +func (cfg *TypeConfig) typeof(name string) string { + if cfg.Var != nil { + if t := cfg.Var[name]; t != "" { + return t + } + } + if cfg.Func != nil { + if t := cfg.Func[name]; t != "" { + return "func()" + t + } + } + return "" +} + +// Type describes the Fields and Methods of a type. +// If the field or method cannot be found there, it is next +// looked for in the Embed list. +type Type struct { + Field map[string]string // map field name to type + Method map[string]string // map method name to comma-separated return types (should start with "func ") + Embed []string // list of types this type embeds (for extra methods) + Def string // definition of named type +} + +// dot returns the type of "typ.name", making its decision +// using the type information in cfg. +func (typ *Type) dot(cfg *TypeConfig, name string) string { + if typ.Field != nil { + if t := typ.Field[name]; t != "" { + return t + } + } + if typ.Method != nil { + if t := typ.Method[name]; t != "" { + return t + } + } + + for _, e := range typ.Embed { + etyp := cfg.Type[e] + if etyp != nil { + if t := etyp.dot(cfg, name); t != "" { + return t + } + } + } + + return "" +} + +// typecheck type checks the AST f assuming the information in cfg. +// It returns two maps with type information: +// typeof maps AST nodes to type information in gofmt string form. +// assign maps type strings to lists of expressions that were assigned +// to values of another type that were assigned to that type. +func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, assign map[string][]interface{}) { + typeof = make(map[interface{}]string) + assign = make(map[string][]interface{}) + cfg1 := &TypeConfig{} + *cfg1 = *cfg // make copy so we can add locally + copied := false + + // gather function declarations + for _, decl := range f.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok { + continue + } + typecheck1(cfg, fn.Type, typeof, assign) + t := typeof[fn.Type] + if fn.Recv != nil { + // The receiver must be a type. + rcvr := typeof[fn.Recv] + if !isType(rcvr) { + if len(fn.Recv.List) != 1 { + continue + } + rcvr = mkType(gofmt(fn.Recv.List[0].Type)) + typeof[fn.Recv.List[0].Type] = rcvr + } + rcvr = getType(rcvr) + if rcvr != "" && rcvr[0] == '*' { + rcvr = rcvr[1:] + } + typeof[rcvr+"."+fn.Name.Name] = t + } else { + if isType(t) { + t = getType(t) + } else { + t = gofmt(fn.Type) + } + typeof[fn.Name] = t + + // Record typeof[fn.Name.Obj] for future references to fn.Name. + typeof[fn.Name.Obj] = t + } + } + + // gather struct declarations + for _, decl := range f.Decls { + d, ok := decl.(*ast.GenDecl) + if ok { + for _, s := range d.Specs { + switch s := s.(type) { + case *ast.TypeSpec: + if cfg1.Type[s.Name.Name] != nil { + break + } + if !copied { + copied = true + // Copy map lazily: it's time. + cfg1.Type = make(map[string]*Type) + for k, v := range cfg.Type { + cfg1.Type[k] = v + } + } + t := &Type{Field: map[string]string{}} + cfg1.Type[s.Name.Name] = t + switch st := s.Type.(type) { + case *ast.StructType: + for _, f := range st.Fields.List { + for _, n := range f.Names { + t.Field[n.Name] = gofmt(f.Type) + } + } + case *ast.ArrayType, *ast.StarExpr, *ast.MapType: + t.Def = gofmt(st) + } + } + } + } + } + + typecheck1(cfg1, f, typeof, assign) + return typeof, assign +} + +func makeExprList(a []*ast.Ident) []ast.Expr { + var b []ast.Expr + for _, x := range a { + b = append(b, x) + } + return b +} + +// Typecheck1 is the recursive form of typecheck. +// It is like typecheck but adds to the information in typeof +// instead of allocating a new map. +func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, assign map[string][]interface{}) { + // set sets the type of n to typ. + // If isDecl is true, n is being declared. + set := func(n ast.Expr, typ string, isDecl bool) { + if typeof[n] != "" || typ == "" { + if typeof[n] != typ { + assign[typ] = append(assign[typ], n) + } + return + } + typeof[n] = typ + + // If we obtained typ from the declaration of x + // propagate the type to all the uses. + // The !isDecl case is a cheat here, but it makes + // up in some cases for not paying attention to + // struct fields. The real type checker will be + // more accurate so we won't need the cheat. + if id, ok := n.(*ast.Ident); ok && id.Obj != nil && (isDecl || typeof[id.Obj] == "") { + typeof[id.Obj] = typ + } + } + + // Type-check an assignment lhs = rhs. + // If isDecl is true, this is := so we can update + // the types of the objects that lhs refers to. + typecheckAssign := func(lhs, rhs []ast.Expr, isDecl bool) { + if len(lhs) > 1 && len(rhs) == 1 { + if _, ok := rhs[0].(*ast.CallExpr); ok { + t := split(typeof[rhs[0]]) + // Lists should have same length but may not; pair what can be paired. + for i := 0; i < len(lhs) && i < len(t); i++ { + set(lhs[i], t[i], isDecl) + } + return + } + } + if len(lhs) == 1 && len(rhs) == 2 { + // x = y, ok + rhs = rhs[:1] + } else if len(lhs) == 2 && len(rhs) == 1 { + // x, ok = y + lhs = lhs[:1] + } + + // Match as much as we can. + for i := 0; i < len(lhs) && i < len(rhs); i++ { + x, y := lhs[i], rhs[i] + if typeof[y] != "" { + set(x, typeof[y], isDecl) + } else { + set(y, typeof[x], false) + } + } + } + + expand := func(s string) string { + typ := cfg.Type[s] + if typ != nil && typ.Def != "" { + return typ.Def + } + return s + } + + // The main type check is a recursive algorithm implemented + // by walkBeforeAfter(n, before, after). + // Most of it is bottom-up, but in a few places we need + // to know the type of the function we are checking. + // The before function records that information on + // the curfn stack. + var curfn []*ast.FuncType + + before := func(n interface{}) { + // push function type on stack + switch n := n.(type) { + case *ast.FuncDecl: + curfn = append(curfn, n.Type) + case *ast.FuncLit: + curfn = append(curfn, n.Type) + } + } + + // After is the real type checker. + after := func(n interface{}) { + if n == nil { + return + } + if false && reflect.TypeOf(n).Kind() == reflect.Ptr { // debugging trace + defer func() { + if t := typeof[n]; t != "" { + pos := fset.Position(n.(ast.Node).Pos()) + fmt.Fprintf(os.Stderr, "%s: typeof[%s] = %s\n", pos, gofmt(n), t) + } + }() + } + + switch n := n.(type) { + case *ast.FuncDecl, *ast.FuncLit: + // pop function type off stack + curfn = curfn[:len(curfn)-1] + + case *ast.FuncType: + typeof[n] = mkType(joinFunc(split(typeof[n.Params]), split(typeof[n.Results]))) + + case *ast.FieldList: + // Field list is concatenation of sub-lists. + t := "" + for _, field := range n.List { + if t != "" { + t += ", " + } + t += typeof[field] + } + typeof[n] = t + + case *ast.Field: + // Field is one instance of the type per name. + all := "" + t := typeof[n.Type] + if !isType(t) { + // Create a type, because it is typically *T or *p.T + // and we might care about that type. + t = mkType(gofmt(n.Type)) + typeof[n.Type] = t + } + t = getType(t) + if len(n.Names) == 0 { + all = t + } else { + for _, id := range n.Names { + if all != "" { + all += ", " + } + all += t + typeof[id.Obj] = t + typeof[id] = t + } + } + typeof[n] = all + + case *ast.ValueSpec: + // var declaration. Use type if present. + if n.Type != nil { + t := typeof[n.Type] + if !isType(t) { + t = mkType(gofmt(n.Type)) + typeof[n.Type] = t + } + t = getType(t) + for _, id := range n.Names { + set(id, t, true) + } + } + // Now treat same as assignment. + typecheckAssign(makeExprList(n.Names), n.Values, true) + + case *ast.AssignStmt: + typecheckAssign(n.Lhs, n.Rhs, n.Tok == token.DEFINE) + + case *ast.Ident: + // Identifier can take its type from underlying object. + if t := typeof[n.Obj]; t != "" { + typeof[n] = t + } + + case *ast.SelectorExpr: + // Field or method. + name := n.Sel.Name + if t := typeof[n.X]; t != "" { + if strings.HasPrefix(t, "*") { + t = t[1:] // implicit * + } + if typ := cfg.Type[t]; typ != nil { + if t := typ.dot(cfg, name); t != "" { + typeof[n] = t + return + } + } + tt := typeof[t+"."+name] + if isType(tt) { + typeof[n] = getType(tt) + return + } + } + // Package selector. + if x, ok := n.X.(*ast.Ident); ok && x.Obj == nil { + str := x.Name + "." + name + if cfg.Type[str] != nil { + typeof[n] = mkType(str) + return + } + if t := cfg.typeof(x.Name + "." + name); t != "" { + typeof[n] = t + return + } + } + + case *ast.CallExpr: + // make(T) has type T. + if isTopName(n.Fun, "make") && len(n.Args) >= 1 { + typeof[n] = gofmt(n.Args[0]) + return + } + // new(T) has type *T + if isTopName(n.Fun, "new") && len(n.Args) == 1 { + typeof[n] = "*" + gofmt(n.Args[0]) + return + } + // Otherwise, use type of function to determine arguments. + t := typeof[n.Fun] + in, out := splitFunc(t) + if in == nil && out == nil { + return + } + typeof[n] = join(out) + for i, arg := range n.Args { + if i >= len(in) { + break + } + if typeof[arg] == "" { + typeof[arg] = in[i] + } + } + + case *ast.TypeAssertExpr: + // x.(type) has type of x. + if n.Type == nil { + typeof[n] = typeof[n.X] + return + } + // x.(T) has type T. + if t := typeof[n.Type]; isType(t) { + typeof[n] = getType(t) + } else { + typeof[n] = gofmt(n.Type) + } + + case *ast.SliceExpr: + // x[i:j] has type of x. + typeof[n] = typeof[n.X] + + case *ast.IndexExpr: + // x[i] has key type of x's type. + t := expand(typeof[n.X]) + if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") { + // Lazy: assume there are no nested [] in the array + // length or map key type. + if i := strings.Index(t, "]"); i >= 0 { + typeof[n] = t[i+1:] + } + } + + case *ast.StarExpr: + // *x for x of type *T has type T when x is an expr. + // We don't use the result when *x is a type, but + // compute it anyway. + t := expand(typeof[n.X]) + if isType(t) { + typeof[n] = "type *" + getType(t) + } else if strings.HasPrefix(t, "*") { + typeof[n] = t[len("*"):] + } + + case *ast.UnaryExpr: + // &x for x of type T has type *T. + t := typeof[n.X] + if t != "" && n.Op == token.AND { + typeof[n] = "*" + t + } + + case *ast.CompositeLit: + // T{...} has type T. + typeof[n] = gofmt(n.Type) + + case *ast.ParenExpr: + // (x) has type of x. + typeof[n] = typeof[n.X] + + case *ast.RangeStmt: + t := expand(typeof[n.X]) + if t == "" { + return + } + var key, value string + if t == "string" { + key, value = "int", "rune" + } else if strings.HasPrefix(t, "[") { + key = "int" + if i := strings.Index(t, "]"); i >= 0 { + value = t[i+1:] + } + } else if strings.HasPrefix(t, "map[") { + if i := strings.Index(t, "]"); i >= 0 { + key, value = t[4:i], t[i+1:] + } + } + changed := false + if n.Key != nil && key != "" { + changed = true + set(n.Key, key, n.Tok == token.DEFINE) + } + if n.Value != nil && value != "" { + changed = true + set(n.Value, value, n.Tok == token.DEFINE) + } + // Ugly failure of vision: already type-checked body. + // Do it again now that we have that type info. + if changed { + typecheck1(cfg, n.Body, typeof, assign) + } + + case *ast.TypeSwitchStmt: + // Type of variable changes for each case in type switch, + // but go/parser generates just one variable. + // Repeat type check for each case with more precise + // type information. + as, ok := n.Assign.(*ast.AssignStmt) + if !ok { + return + } + varx, ok := as.Lhs[0].(*ast.Ident) + if !ok { + return + } + t := typeof[varx] + for _, cas := range n.Body.List { + cas := cas.(*ast.CaseClause) + if len(cas.List) == 1 { + // Variable has specific type only when there is + // exactly one type in the case list. + if tt := typeof[cas.List[0]]; isType(tt) { + tt = getType(tt) + typeof[varx] = tt + typeof[varx.Obj] = tt + typecheck1(cfg, cas.Body, typeof, assign) + } + } + } + // Restore t. + typeof[varx] = t + typeof[varx.Obj] = t + + case *ast.ReturnStmt: + if len(curfn) == 0 { + // Probably can't happen. + return + } + f := curfn[len(curfn)-1] + res := n.Results + if f.Results != nil { + t := split(typeof[f.Results]) + for i := 0; i < len(res) && i < len(t); i++ { + set(res[i], t[i], false) + } + } + } + } + walkBeforeAfter(f, before, after) +} + +// Convert between function type strings and lists of types. +// Using strings makes this a little harder, but it makes +// a lot of the rest of the code easier. This will all go away +// when we can use go/typechecker directly. + +// splitFunc splits "func(x,y,z) (a,b,c)" into ["x", "y", "z"] and ["a", "b", "c"]. +func splitFunc(s string) (in, out []string) { + if !strings.HasPrefix(s, "func(") { + return nil, nil + } + + i := len("func(") // index of beginning of 'in' arguments + nparen := 0 + for j := i; j < len(s); j++ { + switch s[j] { + case '(': + nparen++ + case ')': + nparen-- + if nparen < 0 { + // found end of parameter list + out := strings.TrimSpace(s[j+1:]) + if len(out) >= 2 && out[0] == '(' && out[len(out)-1] == ')' { + out = out[1 : len(out)-1] + } + return split(s[i:j]), split(out) + } + } + } + return nil, nil +} + +// joinFunc is the inverse of splitFunc. +func joinFunc(in, out []string) string { + outs := "" + if len(out) == 1 { + outs = " " + out[0] + } else if len(out) > 1 { + outs = " (" + join(out) + ")" + } + return "func(" + join(in) + ")" + outs +} + +// split splits "int, float" into ["int", "float"] and splits "" into []. +func split(s string) []string { + out := []string{} + i := 0 // current type being scanned is s[i:j]. + nparen := 0 + for j := 0; j < len(s); j++ { + switch s[j] { + case ' ': + if i == j { + i++ + } + case '(': + nparen++ + case ')': + nparen-- + if nparen < 0 { + // probably can't happen + return nil + } + case ',': + if nparen == 0 { + if i < j { + out = append(out, s[i:j]) + } + i = j + 1 + } + } + } + if nparen != 0 { + // probably can't happen + return nil + } + if i < len(s) { + out = append(out, s[i:]) + } + return out +} + +// join is the inverse of split. +func join(x []string) string { + return strings.Join(x, ", ") +} diff --git a/vendor/google.golang.org/appengine/datastore/datastore.go b/vendor/google.golang.org/appengine/datastore/datastore.go new file mode 100644 index 0000000000..576bc50132 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/datastore.go @@ -0,0 +1,407 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "errors" + "fmt" + "reflect" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/datastore" +) + +var ( + // ErrInvalidEntityType is returned when functions like Get or Next are + // passed a dst or src argument of invalid type. + ErrInvalidEntityType = errors.New("datastore: invalid entity type") + // ErrInvalidKey is returned when an invalid key is presented. + ErrInvalidKey = errors.New("datastore: invalid key") + // ErrNoSuchEntity is returned when no entity was found for a given key. + ErrNoSuchEntity = errors.New("datastore: no such entity") +) + +// ErrFieldMismatch is returned when a field is to be loaded into a different +// type than the one it was stored from, or when a field is missing or +// unexported in the destination struct. +// StructType is the type of the struct pointed to by the destination argument +// passed to Get or to Iterator.Next. +type ErrFieldMismatch struct { + StructType reflect.Type + FieldName string + Reason string +} + +func (e *ErrFieldMismatch) Error() string { + return fmt.Sprintf("datastore: cannot load field %q into a %q: %s", + e.FieldName, e.StructType, e.Reason) +} + +// protoToKey converts a Reference proto to a *Key. If the key is invalid, +// protoToKey will return the invalid key along with ErrInvalidKey. +func protoToKey(r *pb.Reference) (k *Key, err error) { + appID := r.GetApp() + namespace := r.GetNameSpace() + for _, e := range r.Path.Element { + k = &Key{ + kind: e.GetType(), + stringID: e.GetName(), + intID: e.GetId(), + parent: k, + appID: appID, + namespace: namespace, + } + if !k.valid() { + return k, ErrInvalidKey + } + } + return +} + +// keyToProto converts a *Key to a Reference proto. +func keyToProto(defaultAppID string, k *Key) *pb.Reference { + appID := k.appID + if appID == "" { + appID = defaultAppID + } + n := 0 + for i := k; i != nil; i = i.parent { + n++ + } + e := make([]*pb.Path_Element, n) + for i := k; i != nil; i = i.parent { + n-- + e[n] = &pb.Path_Element{ + Type: &i.kind, + } + // At most one of {Name,Id} should be set. + // Neither will be set for incomplete keys. + if i.stringID != "" { + e[n].Name = &i.stringID + } else if i.intID != 0 { + e[n].Id = &i.intID + } + } + var namespace *string + if k.namespace != "" { + namespace = proto.String(k.namespace) + } + return &pb.Reference{ + App: proto.String(appID), + NameSpace: namespace, + Path: &pb.Path{ + Element: e, + }, + } +} + +// multiKeyToProto is a batch version of keyToProto. +func multiKeyToProto(appID string, key []*Key) []*pb.Reference { + ret := make([]*pb.Reference, len(key)) + for i, k := range key { + ret[i] = keyToProto(appID, k) + } + return ret +} + +// multiValid is a batch version of Key.valid. It returns an error, not a +// []bool. +func multiValid(key []*Key) error { + invalid := false + for _, k := range key { + if !k.valid() { + invalid = true + break + } + } + if !invalid { + return nil + } + err := make(appengine.MultiError, len(key)) + for i, k := range key { + if !k.valid() { + err[i] = ErrInvalidKey + } + } + return err +} + +// It's unfortunate that the two semantically equivalent concepts pb.Reference +// and pb.PropertyValue_ReferenceValue aren't the same type. For example, the +// two have different protobuf field numbers. + +// referenceValueToKey is the same as protoToKey except the input is a +// PropertyValue_ReferenceValue instead of a Reference. +func referenceValueToKey(r *pb.PropertyValue_ReferenceValue) (k *Key, err error) { + appID := r.GetApp() + namespace := r.GetNameSpace() + for _, e := range r.Pathelement { + k = &Key{ + kind: e.GetType(), + stringID: e.GetName(), + intID: e.GetId(), + parent: k, + appID: appID, + namespace: namespace, + } + if !k.valid() { + return nil, ErrInvalidKey + } + } + return +} + +// keyToReferenceValue is the same as keyToProto except the output is a +// PropertyValue_ReferenceValue instead of a Reference. +func keyToReferenceValue(defaultAppID string, k *Key) *pb.PropertyValue_ReferenceValue { + ref := keyToProto(defaultAppID, k) + pe := make([]*pb.PropertyValue_ReferenceValue_PathElement, len(ref.Path.Element)) + for i, e := range ref.Path.Element { + pe[i] = &pb.PropertyValue_ReferenceValue_PathElement{ + Type: e.Type, + Id: e.Id, + Name: e.Name, + } + } + return &pb.PropertyValue_ReferenceValue{ + App: ref.App, + NameSpace: ref.NameSpace, + Pathelement: pe, + } +} + +type multiArgType int + +const ( + multiArgTypeInvalid multiArgType = iota + multiArgTypePropertyLoadSaver + multiArgTypeStruct + multiArgTypeStructPtr + multiArgTypeInterface +) + +// checkMultiArg checks that v has type []S, []*S, []I, or []P, for some struct +// type S, for some interface type I, or some non-interface non-pointer type P +// such that P or *P implements PropertyLoadSaver. +// +// It returns what category the slice's elements are, and the reflect.Type +// that represents S, I or P. +// +// As a special case, PropertyList is an invalid type for v. +func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) { + if v.Kind() != reflect.Slice { + return multiArgTypeInvalid, nil + } + if v.Type() == typeOfPropertyList { + return multiArgTypeInvalid, nil + } + elemType = v.Type().Elem() + if reflect.PtrTo(elemType).Implements(typeOfPropertyLoadSaver) { + return multiArgTypePropertyLoadSaver, elemType + } + switch elemType.Kind() { + case reflect.Struct: + return multiArgTypeStruct, elemType + case reflect.Interface: + return multiArgTypeInterface, elemType + case reflect.Ptr: + elemType = elemType.Elem() + if elemType.Kind() == reflect.Struct { + return multiArgTypeStructPtr, elemType + } + } + return multiArgTypeInvalid, nil +} + +// Get loads the entity stored for k into dst, which must be a struct pointer +// or implement PropertyLoadSaver. If there is no such entity for the key, Get +// returns ErrNoSuchEntity. +// +// The values of dst's unmatched struct fields are not modified, and matching +// slice-typed fields are not reset before appending to them. In particular, it +// is recommended to pass a pointer to a zero valued struct on each Get call. +// +// ErrFieldMismatch is returned when a field is to be loaded into a different +// type than the one it was stored from, or when a field is missing or +// unexported in the destination struct. ErrFieldMismatch is only returned if +// dst is a struct pointer. +func Get(c context.Context, key *Key, dst interface{}) error { + if dst == nil { // GetMulti catches nil interface; we need to catch nil ptr here + return ErrInvalidEntityType + } + err := GetMulti(c, []*Key{key}, []interface{}{dst}) + if me, ok := err.(appengine.MultiError); ok { + return me[0] + } + return err +} + +// GetMulti is a batch version of Get. +// +// dst must be a []S, []*S, []I or []P, for some struct type S, some interface +// type I, or some non-interface non-pointer type P such that P or *P +// implements PropertyLoadSaver. If an []I, each element must be a valid dst +// for Get: it must be a struct pointer or implement PropertyLoadSaver. +// +// As a special case, PropertyList is an invalid type for dst, even though a +// PropertyList is a slice of structs. It is treated as invalid to avoid being +// mistakenly passed when []PropertyList was intended. +func GetMulti(c context.Context, key []*Key, dst interface{}) error { + v := reflect.ValueOf(dst) + multiArgType, _ := checkMultiArg(v) + if multiArgType == multiArgTypeInvalid { + return errors.New("datastore: dst has invalid type") + } + if len(key) != v.Len() { + return errors.New("datastore: key and dst slices have different length") + } + if len(key) == 0 { + return nil + } + if err := multiValid(key); err != nil { + return err + } + req := &pb.GetRequest{ + Key: multiKeyToProto(internal.FullyQualifiedAppID(c), key), + } + res := &pb.GetResponse{} + if err := internal.Call(c, "datastore_v3", "Get", req, res); err != nil { + return err + } + if len(key) != len(res.Entity) { + return errors.New("datastore: internal error: server returned the wrong number of entities") + } + multiErr, any := make(appengine.MultiError, len(key)), false + for i, e := range res.Entity { + if e.Entity == nil { + multiErr[i] = ErrNoSuchEntity + } else { + elem := v.Index(i) + if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct { + elem = elem.Addr() + } + if multiArgType == multiArgTypeStructPtr && elem.IsNil() { + elem.Set(reflect.New(elem.Type().Elem())) + } + multiErr[i] = loadEntity(elem.Interface(), e.Entity) + } + if multiErr[i] != nil { + any = true + } + } + if any { + return multiErr + } + return nil +} + +// Put saves the entity src into the datastore with key k. src must be a struct +// pointer or implement PropertyLoadSaver; if a struct pointer then any +// unexported fields of that struct will be skipped. If k is an incomplete key, +// the returned key will be a unique key generated by the datastore. +func Put(c context.Context, key *Key, src interface{}) (*Key, error) { + k, err := PutMulti(c, []*Key{key}, []interface{}{src}) + if err != nil { + if me, ok := err.(appengine.MultiError); ok { + return nil, me[0] + } + return nil, err + } + return k[0], nil +} + +// PutMulti is a batch version of Put. +// +// src must satisfy the same conditions as the dst argument to GetMulti. +func PutMulti(c context.Context, key []*Key, src interface{}) ([]*Key, error) { + v := reflect.ValueOf(src) + multiArgType, _ := checkMultiArg(v) + if multiArgType == multiArgTypeInvalid { + return nil, errors.New("datastore: src has invalid type") + } + if len(key) != v.Len() { + return nil, errors.New("datastore: key and src slices have different length") + } + if len(key) == 0 { + return nil, nil + } + appID := internal.FullyQualifiedAppID(c) + if err := multiValid(key); err != nil { + return nil, err + } + req := &pb.PutRequest{} + for i := range key { + elem := v.Index(i) + if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct { + elem = elem.Addr() + } + sProto, err := saveEntity(appID, key[i], elem.Interface()) + if err != nil { + return nil, err + } + req.Entity = append(req.Entity, sProto) + } + res := &pb.PutResponse{} + if err := internal.Call(c, "datastore_v3", "Put", req, res); err != nil { + return nil, err + } + if len(key) != len(res.Key) { + return nil, errors.New("datastore: internal error: server returned the wrong number of keys") + } + ret := make([]*Key, len(key)) + for i := range ret { + var err error + ret[i], err = protoToKey(res.Key[i]) + if err != nil || ret[i].Incomplete() { + return nil, errors.New("datastore: internal error: server returned an invalid key") + } + } + return ret, nil +} + +// Delete deletes the entity for the given key. +func Delete(c context.Context, key *Key) error { + err := DeleteMulti(c, []*Key{key}) + if me, ok := err.(appengine.MultiError); ok { + return me[0] + } + return err +} + +// DeleteMulti is a batch version of Delete. +func DeleteMulti(c context.Context, key []*Key) error { + if len(key) == 0 { + return nil + } + if err := multiValid(key); err != nil { + return err + } + req := &pb.DeleteRequest{ + Key: multiKeyToProto(internal.FullyQualifiedAppID(c), key), + } + res := &pb.DeleteResponse{} + return internal.Call(c, "datastore_v3", "Delete", req, res) +} + +func namespaceMod(m proto.Message, namespace string) { + // pb.Query is the only type that has a name_space field. + // All other namespace support in datastore is in the keys. + switch m := m.(type) { + case *pb.Query: + if m.NameSpace == nil { + m.NameSpace = &namespace + } + } +} + +func init() { + internal.NamespaceMods["datastore_v3"] = namespaceMod + internal.RegisterErrorCodeMap("datastore_v3", pb.Error_ErrorCode_name) + internal.RegisterTimeoutErrorCode("datastore_v3", int32(pb.Error_TIMEOUT)) +} diff --git a/vendor/google.golang.org/appengine/datastore/datastore_test.go b/vendor/google.golang.org/appengine/datastore/datastore_test.go new file mode 100644 index 0000000000..683cd15f33 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/datastore_test.go @@ -0,0 +1,1750 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "reflect" + "sort" + "strings" + "testing" + "time" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/datastore" +) + +const testAppID = "testApp" + +type ( + myBlob []byte + myByte byte + myString string +) + +func makeMyByteSlice(n int) []myByte { + b := make([]myByte, n) + for i := range b { + b[i] = myByte(i) + } + return b +} + +func makeInt8Slice(n int) []int8 { + b := make([]int8, n) + for i := range b { + b[i] = int8(i) + } + return b +} + +func makeUint8Slice(n int) []uint8 { + b := make([]uint8, n) + for i := range b { + b[i] = uint8(i) + } + return b +} + +func newKey(stringID string, parent *Key) *Key { + return &Key{ + kind: "kind", + stringID: stringID, + intID: 0, + parent: parent, + appID: testAppID, + } +} + +var ( + testKey0 = newKey("name0", nil) + testKey1a = newKey("name1", nil) + testKey1b = newKey("name1", nil) + testKey2a = newKey("name2", testKey0) + testKey2b = newKey("name2", testKey0) + testGeoPt0 = appengine.GeoPoint{Lat: 1.2, Lng: 3.4} + testGeoPt1 = appengine.GeoPoint{Lat: 5, Lng: 10} + testBadGeoPt = appengine.GeoPoint{Lat: 1000, Lng: 34} + + now = time.Unix(1e9, 0).UTC() +) + +type B0 struct { + B []byte +} + +type B1 struct { + B []int8 +} + +type B2 struct { + B myBlob +} + +type B3 struct { + B []myByte +} + +type B4 struct { + B [][]byte +} + +type B5 struct { + B ByteString +} + +type C0 struct { + I int + C chan int +} + +type C1 struct { + I int + C *chan int +} + +type C2 struct { + I int + C []chan int +} + +type C3 struct { + C string +} + +type E struct{} + +type G0 struct { + G appengine.GeoPoint +} + +type G1 struct { + G []appengine.GeoPoint +} + +type K0 struct { + K *Key +} + +type K1 struct { + K []*Key +} + +type S struct { + St string +} + +type NoOmit struct { + A string + B int `datastore:"Bb"` + C bool `datastore:",noindex"` +} + +type OmitAll struct { + A string `datastore:",omitempty"` + B int `datastore:"Bb,omitempty"` + C bool `datastore:",omitempty,noindex"` + D time.Time `datastore:",omitempty"` + F []int `datastore:",omitempty"` +} + +type Omit struct { + A string `datastore:",omitempty"` + B int `datastore:"Bb,omitempty"` + C bool `datastore:",omitempty,noindex"` + D time.Time `datastore:",omitempty"` + F []int `datastore:",omitempty"` + S `datastore:",omitempty"` +} + +type NoOmits struct { + No []NoOmit `datastore:",omitempty"` + S `datastore:",omitempty"` + Ss S `datastore:",omitempty"` +} + +type N0 struct { + X0 + Nonymous X0 + Ignore string `datastore:"-"` + Other string +} + +type N1 struct { + X0 + Nonymous []X0 + Ignore string `datastore:"-"` + Other string +} + +type N2 struct { + N1 `datastore:"red"` + Green N1 `datastore:"green"` + Blue N1 + White N1 `datastore:"-"` +} + +type O0 struct { + I int64 +} + +type O1 struct { + I int32 +} + +type U0 struct { + U uint +} + +type U1 struct { + U string +} + +type T struct { + T time.Time +} + +type X0 struct { + S string + I int + i int +} + +type X1 struct { + S myString + I int32 + J int64 +} + +type X2 struct { + Z string + i int +} + +type X3 struct { + S bool + I int +} + +type Y0 struct { + B bool + F []float64 + G []float64 +} + +type Y1 struct { + B bool + F float64 +} + +type Y2 struct { + B bool + F []int64 +} + +type Tagged struct { + A int `datastore:"a,noindex"` + B []int `datastore:"b"` + C int `datastore:",noindex"` + D int `datastore:""` + E int + // The "flatten" option is parsed but ignored for now. + F int `datastore:",noindex,flatten"` + G int `datastore:",flatten"` + I int `datastore:"-"` + J int `datastore:",noindex" json:"j"` + + Y0 `datastore:"-"` + Z chan int `datastore:"-,"` +} + +type InvalidTagged1 struct { + I int `datastore:"\t"` +} + +type InvalidTagged2 struct { + I int + J int `datastore:"I"` +} + +type Inner1 struct { + W int32 + X string +} + +type Inner2 struct { + Y float64 +} + +type Inner3 struct { + Z bool +} + +type Outer struct { + A int16 + I []Inner1 + J Inner2 + Inner3 +} + +type OuterEquivalent struct { + A int16 + IDotW []int32 `datastore:"I.W"` + IDotX []string `datastore:"I.X"` + JDotY float64 `datastore:"J.Y"` + Z bool +} + +type Dotted struct { + A DottedA `datastore:"A0.A1.A2"` +} + +type DottedA struct { + B DottedB `datastore:"B3"` +} + +type DottedB struct { + C int `datastore:"C4.C5"` +} + +type SliceOfSlices struct { + I int + S []struct { + J int + F []float64 + } +} + +type Recursive struct { + I int + R []Recursive +} + +type MutuallyRecursive0 struct { + I int + R []MutuallyRecursive1 +} + +type MutuallyRecursive1 struct { + I int + R []MutuallyRecursive0 +} + +type Doubler struct { + S string + I int64 + B bool +} + +type Repeat struct { + Key string + Value []byte +} + +type Repeated struct { + Repeats []Repeat +} + +func (d *Doubler) Load(props []Property) error { + return LoadStruct(d, props) +} + +type EmbeddedTime struct { + time.Time +} + +type SpecialTime struct { + MyTime EmbeddedTime +} + +func (d *Doubler) Save() ([]Property, error) { + // Save the default Property slice to an in-memory buffer (a PropertyList). + props, err := SaveStruct(d) + if err != nil { + return nil, err + } + var list PropertyList + if err := list.Load(props); err != nil { + return nil, err + } + + // Edit that PropertyList, and send it on. + for i := range list { + switch v := list[i].Value.(type) { + case string: + // + means string concatenation. + list[i].Value = v + v + case int64: + // + means integer addition. + list[i].Value = v + v + } + } + return list.Save() +} + +var _ PropertyLoadSaver = (*Doubler)(nil) + +type Deriver struct { + S, Derived, Ignored string +} + +func (e *Deriver) Load(props []Property) error { + for _, p := range props { + if p.Name != "S" { + continue + } + e.S = p.Value.(string) + e.Derived = "derived+" + e.S + } + return nil +} + +func (e *Deriver) Save() ([]Property, error) { + return []Property{ + { + Name: "S", + Value: e.S, + }, + }, nil +} + +var _ PropertyLoadSaver = (*Deriver)(nil) + +type BadMultiPropEntity struct{} + +func (e *BadMultiPropEntity) Load(props []Property) error { + return errors.New("unimplemented") +} + +func (e *BadMultiPropEntity) Save() ([]Property, error) { + // Write multiple properties with the same name "I", but Multiple is false. + var props []Property + for i := 0; i < 3; i++ { + props = append(props, Property{ + Name: "I", + Value: int64(i), + }) + } + return props, nil +} + +var _ PropertyLoadSaver = (*BadMultiPropEntity)(nil) + +type BK struct { + Key appengine.BlobKey +} + +type testCase struct { + desc string + src interface{} + want interface{} + putErr string + getErr string +} + +var testCases = []testCase{ + { + "chan save fails", + &C0{I: -1}, + &E{}, + "unsupported struct field", + "", + }, + { + "*chan save fails", + &C1{I: -1}, + &E{}, + "unsupported struct field", + "", + }, + { + "[]chan save fails", + &C2{I: -1, C: make([]chan int, 8)}, + &E{}, + "unsupported struct field", + "", + }, + { + "chan load fails", + &C3{C: "not a chan"}, + &C0{}, + "", + "type mismatch", + }, + { + "*chan load fails", + &C3{C: "not a *chan"}, + &C1{}, + "", + "type mismatch", + }, + { + "[]chan load fails", + &C3{C: "not a []chan"}, + &C2{}, + "", + "type mismatch", + }, + { + "empty struct", + &E{}, + &E{}, + "", + "", + }, + { + "geopoint", + &G0{G: testGeoPt0}, + &G0{G: testGeoPt0}, + "", + "", + }, + { + "geopoint invalid", + &G0{G: testBadGeoPt}, + &G0{}, + "invalid GeoPoint value", + "", + }, + { + "geopoint as props", + &G0{G: testGeoPt0}, + &PropertyList{ + Property{Name: "G", Value: testGeoPt0, NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "geopoint slice", + &G1{G: []appengine.GeoPoint{testGeoPt0, testGeoPt1}}, + &G1{G: []appengine.GeoPoint{testGeoPt0, testGeoPt1}}, + "", + "", + }, + { + "omit empty, all", + &OmitAll{}, + new(PropertyList), + "", + "", + }, + { + "omit empty", + &Omit{}, + &PropertyList{ + Property{Name: "St", Value: "", NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "omit empty, fields populated", + &Omit{ + A: "a", + B: 10, + C: true, + D: now, + F: []int{11}, + }, + &PropertyList{ + Property{Name: "A", Value: "a", NoIndex: false, Multiple: false}, + Property{Name: "Bb", Value: int64(10), NoIndex: false, Multiple: false}, + Property{Name: "C", Value: true, NoIndex: true, Multiple: false}, + Property{Name: "D", Value: now, NoIndex: false, Multiple: false}, + Property{Name: "F", Value: int64(11), NoIndex: false, Multiple: true}, + Property{Name: "St", Value: "", NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "omit empty, fields populated", + &Omit{ + A: "a", + B: 10, + C: true, + D: now, + F: []int{11}, + S: S{St: "string"}, + }, + &PropertyList{ + Property{Name: "A", Value: "a", NoIndex: false, Multiple: false}, + Property{Name: "Bb", Value: int64(10), NoIndex: false, Multiple: false}, + Property{Name: "C", Value: true, NoIndex: true, Multiple: false}, + Property{Name: "D", Value: now, NoIndex: false, Multiple: false}, + Property{Name: "F", Value: int64(11), NoIndex: false, Multiple: true}, + Property{Name: "St", Value: "string", NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "omit empty does not propagate", + &NoOmits{ + No: []NoOmit{ + NoOmit{}, + }, + S: S{}, + Ss: S{}, + }, + &PropertyList{ + Property{Name: "No.A", Value: "", NoIndex: false, Multiple: true}, + Property{Name: "No.Bb", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "No.C", Value: false, NoIndex: true, Multiple: true}, + Property{Name: "Ss.St", Value: "", NoIndex: false, Multiple: false}, + Property{Name: "St", Value: "", NoIndex: false, Multiple: false}}, + "", + "", + }, + { + "key", + &K0{K: testKey1a}, + &K0{K: testKey1b}, + "", + "", + }, + { + "key with parent", + &K0{K: testKey2a}, + &K0{K: testKey2b}, + "", + "", + }, + { + "nil key", + &K0{}, + &K0{}, + "", + "", + }, + { + "all nil keys in slice", + &K1{[]*Key{nil, nil}}, + &K1{[]*Key{nil, nil}}, + "", + "", + }, + { + "some nil keys in slice", + &K1{[]*Key{testKey1a, nil, testKey2a}}, + &K1{[]*Key{testKey1b, nil, testKey2b}}, + "", + "", + }, + { + "overflow", + &O0{I: 1 << 48}, + &O1{}, + "", + "overflow", + }, + { + "time", + &T{T: time.Unix(1e9, 0)}, + &T{T: time.Unix(1e9, 0)}, + "", + "", + }, + { + "time as props", + &T{T: time.Unix(1e9, 0)}, + &PropertyList{ + Property{Name: "T", Value: time.Unix(1e9, 0).UTC(), NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "uint save", + &U0{U: 1}, + &U0{}, + "unsupported struct field", + "", + }, + { + "uint load", + &U1{U: "not a uint"}, + &U0{}, + "", + "type mismatch", + }, + { + "zero", + &X0{}, + &X0{}, + "", + "", + }, + { + "basic", + &X0{S: "one", I: 2, i: 3}, + &X0{S: "one", I: 2}, + "", + "", + }, + { + "save string/int load myString/int32", + &X0{S: "one", I: 2, i: 3}, + &X1{S: "one", I: 2}, + "", + "", + }, + { + "missing fields", + &X0{S: "one", I: 2, i: 3}, + &X2{}, + "", + "no such struct field", + }, + { + "save string load bool", + &X0{S: "one", I: 2, i: 3}, + &X3{I: 2}, + "", + "type mismatch", + }, + { + "basic slice", + &Y0{B: true, F: []float64{7, 8, 9}}, + &Y0{B: true, F: []float64{7, 8, 9}}, + "", + "", + }, + { + "save []float64 load float64", + &Y0{B: true, F: []float64{7, 8, 9}}, + &Y1{B: true}, + "", + "requires a slice", + }, + { + "save []float64 load []int64", + &Y0{B: true, F: []float64{7, 8, 9}}, + &Y2{B: true}, + "", + "type mismatch", + }, + { + "single slice is too long", + &Y0{F: make([]float64, maxIndexedProperties+1)}, + &Y0{}, + "too many indexed properties", + "", + }, + { + "two slices are too long", + &Y0{F: make([]float64, maxIndexedProperties), G: make([]float64, maxIndexedProperties)}, + &Y0{}, + "too many indexed properties", + "", + }, + { + "one slice and one scalar are too long", + &Y0{F: make([]float64, maxIndexedProperties), B: true}, + &Y0{}, + "too many indexed properties", + "", + }, + { + "slice of slices of bytes", + &Repeated{ + Repeats: []Repeat{ + { + Key: "key 1", + Value: []byte("value 1"), + }, + { + Key: "key 2", + Value: []byte("value 2"), + }, + }, + }, + &Repeated{ + Repeats: []Repeat{ + { + Key: "key 1", + Value: []byte("value 1"), + }, + { + Key: "key 2", + Value: []byte("value 2"), + }, + }, + }, + "", + "", + }, + { + "long blob", + &B0{B: makeUint8Slice(maxIndexedProperties + 1)}, + &B0{B: makeUint8Slice(maxIndexedProperties + 1)}, + "", + "", + }, + { + "long []int8 is too long", + &B1{B: makeInt8Slice(maxIndexedProperties + 1)}, + &B1{}, + "too many indexed properties", + "", + }, + { + "short []int8", + &B1{B: makeInt8Slice(3)}, + &B1{B: makeInt8Slice(3)}, + "", + "", + }, + { + "long myBlob", + &B2{B: makeUint8Slice(maxIndexedProperties + 1)}, + &B2{B: makeUint8Slice(maxIndexedProperties + 1)}, + "", + "", + }, + { + "short myBlob", + &B2{B: makeUint8Slice(3)}, + &B2{B: makeUint8Slice(3)}, + "", + "", + }, + { + "long []myByte", + &B3{B: makeMyByteSlice(maxIndexedProperties + 1)}, + &B3{B: makeMyByteSlice(maxIndexedProperties + 1)}, + "", + "", + }, + { + "short []myByte", + &B3{B: makeMyByteSlice(3)}, + &B3{B: makeMyByteSlice(3)}, + "", + "", + }, + { + "slice of blobs", + &B4{B: [][]byte{ + makeUint8Slice(3), + makeUint8Slice(4), + makeUint8Slice(5), + }}, + &B4{B: [][]byte{ + makeUint8Slice(3), + makeUint8Slice(4), + makeUint8Slice(5), + }}, + "", + "", + }, + { + "short ByteString", + &B5{B: ByteString(makeUint8Slice(3))}, + &B5{B: ByteString(makeUint8Slice(3))}, + "", + "", + }, + { + "short ByteString as props", + &B5{B: ByteString(makeUint8Slice(3))}, + &PropertyList{ + Property{Name: "B", Value: ByteString(makeUint8Slice(3)), NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "short ByteString into string", + &B5{B: ByteString("legacy")}, + &struct{ B string }{"legacy"}, + "", + "", + }, + { + "[]byte must be noindex", + &PropertyList{ + Property{Name: "B", Value: makeUint8Slice(3), NoIndex: false}, + }, + nil, + "cannot index a []byte valued Property", + "", + }, + { + "save tagged load props", + &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, F: 6, G: 7, I: 8, J: 9}, + &PropertyList{ + // A and B are renamed to a and b; A and C are noindex, I is ignored. + // Indexed properties are loaded before raw properties. Thus, the + // result is: b, b, b, D, E, a, c. + Property{Name: "C", Value: int64(3), NoIndex: true, Multiple: false}, + Property{Name: "D", Value: int64(4), NoIndex: false, Multiple: false}, + Property{Name: "E", Value: int64(5), NoIndex: false, Multiple: false}, + Property{Name: "F", Value: int64(6), NoIndex: true, Multiple: false}, + Property{Name: "G", Value: int64(7), NoIndex: false, Multiple: false}, + Property{Name: "J", Value: int64(9), NoIndex: true, Multiple: false}, + Property{Name: "a", Value: int64(1), NoIndex: true, Multiple: false}, + Property{Name: "b", Value: int64(21), NoIndex: false, Multiple: true}, + Property{Name: "b", Value: int64(22), NoIndex: false, Multiple: true}, + Property{Name: "b", Value: int64(23), NoIndex: false, Multiple: true}, + }, + "", + "", + }, + { + "save tagged load tagged", + &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6, J: 7}, + &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, J: 7}, + "", + "", + }, + { + "save props load tagged", + &PropertyList{ + Property{Name: "A", Value: int64(11), NoIndex: true, Multiple: false}, + Property{Name: "a", Value: int64(12), NoIndex: true, Multiple: false}, + }, + &Tagged{A: 12}, + "", + `cannot load field "A"`, + }, + { + "invalid tagged1", + &InvalidTagged1{I: 1}, + &InvalidTagged1{}, + "struct tag has invalid property name", + "", + }, + { + "invalid tagged2", + &InvalidTagged2{I: 1, J: 2}, + &InvalidTagged2{}, + "struct tag has repeated property name", + "", + }, + { + "doubler", + &Doubler{S: "s", I: 1, B: true}, + &Doubler{S: "ss", I: 2, B: true}, + "", + "", + }, + { + "save struct load props", + &X0{S: "s", I: 1}, + &PropertyList{ + Property{Name: "I", Value: int64(1), NoIndex: false, Multiple: false}, + Property{Name: "S", Value: "s", NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "save props load struct", + &PropertyList{ + Property{Name: "S", Value: "s", NoIndex: false, Multiple: false}, + Property{Name: "I", Value: int64(1), NoIndex: false, Multiple: false}, + }, + &X0{S: "s", I: 1}, + "", + "", + }, + { + "nil-value props", + &PropertyList{ + Property{Name: "I", Value: nil, NoIndex: false, Multiple: false}, + Property{Name: "B", Value: nil, NoIndex: false, Multiple: false}, + Property{Name: "S", Value: nil, NoIndex: false, Multiple: false}, + Property{Name: "F", Value: nil, NoIndex: false, Multiple: false}, + Property{Name: "K", Value: nil, NoIndex: false, Multiple: false}, + Property{Name: "T", Value: nil, NoIndex: false, Multiple: false}, + Property{Name: "J", Value: nil, NoIndex: false, Multiple: true}, + Property{Name: "J", Value: int64(7), NoIndex: false, Multiple: true}, + Property{Name: "J", Value: nil, NoIndex: false, Multiple: true}, + }, + &struct { + I int64 + B bool + S string + F float64 + K *Key + T time.Time + J []int64 + }{ + J: []int64{0, 7, 0}, + }, + "", + "", + }, + { + "save outer load props", + &Outer{ + A: 1, + I: []Inner1{ + {10, "ten"}, + {20, "twenty"}, + {30, "thirty"}, + }, + J: Inner2{ + Y: 3.14, + }, + Inner3: Inner3{ + Z: true, + }, + }, + &PropertyList{ + Property{Name: "A", Value: int64(1), NoIndex: false, Multiple: false}, + Property{Name: "I.W", Value: int64(10), NoIndex: false, Multiple: true}, + Property{Name: "I.W", Value: int64(20), NoIndex: false, Multiple: true}, + Property{Name: "I.W", Value: int64(30), NoIndex: false, Multiple: true}, + Property{Name: "I.X", Value: "ten", NoIndex: false, Multiple: true}, + Property{Name: "I.X", Value: "twenty", NoIndex: false, Multiple: true}, + Property{Name: "I.X", Value: "thirty", NoIndex: false, Multiple: true}, + Property{Name: "J.Y", Value: float64(3.14), NoIndex: false, Multiple: false}, + Property{Name: "Z", Value: true, NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "save props load outer-equivalent", + &PropertyList{ + Property{Name: "A", Value: int64(1), NoIndex: false, Multiple: false}, + Property{Name: "I.W", Value: int64(10), NoIndex: false, Multiple: true}, + Property{Name: "I.X", Value: "ten", NoIndex: false, Multiple: true}, + Property{Name: "I.W", Value: int64(20), NoIndex: false, Multiple: true}, + Property{Name: "I.X", Value: "twenty", NoIndex: false, Multiple: true}, + Property{Name: "I.W", Value: int64(30), NoIndex: false, Multiple: true}, + Property{Name: "I.X", Value: "thirty", NoIndex: false, Multiple: true}, + Property{Name: "J.Y", Value: float64(3.14), NoIndex: false, Multiple: false}, + Property{Name: "Z", Value: true, NoIndex: false, Multiple: false}, + }, + &OuterEquivalent{ + A: 1, + IDotW: []int32{10, 20, 30}, + IDotX: []string{"ten", "twenty", "thirty"}, + JDotY: 3.14, + Z: true, + }, + "", + "", + }, + { + "save outer-equivalent load outer", + &OuterEquivalent{ + A: 1, + IDotW: []int32{10, 20, 30}, + IDotX: []string{"ten", "twenty", "thirty"}, + JDotY: 3.14, + Z: true, + }, + &Outer{ + A: 1, + I: []Inner1{ + {10, "ten"}, + {20, "twenty"}, + {30, "thirty"}, + }, + J: Inner2{ + Y: 3.14, + }, + Inner3: Inner3{ + Z: true, + }, + }, + "", + "", + }, + { + "dotted names save", + &Dotted{A: DottedA{B: DottedB{C: 88}}}, + &PropertyList{ + Property{Name: "A0.A1.A2.B3.C4.C5", Value: int64(88), NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "dotted names load", + &PropertyList{ + Property{Name: "A0.A1.A2.B3.C4.C5", Value: int64(99), NoIndex: false, Multiple: false}, + }, + &Dotted{A: DottedA{B: DottedB{C: 99}}}, + "", + "", + }, + { + "save struct load deriver", + &X0{S: "s", I: 1}, + &Deriver{S: "s", Derived: "derived+s"}, + "", + "", + }, + { + "save deriver load struct", + &Deriver{S: "s", Derived: "derived+s", Ignored: "ignored"}, + &X0{S: "s"}, + "", + "", + }, + { + "bad multi-prop entity", + &BadMultiPropEntity{}, + &BadMultiPropEntity{}, + "Multiple is false", + "", + }, + // Regression: CL 25062824 broke handling of appengine.BlobKey fields. + { + "appengine.BlobKey", + &BK{Key: "blah"}, + &BK{Key: "blah"}, + "", + "", + }, + { + "zero time.Time", + &T{T: time.Time{}}, + &T{T: time.Time{}}, + "", + "", + }, + { + "time.Time near Unix zero time", + &T{T: time.Unix(0, 4e3)}, + &T{T: time.Unix(0, 4e3)}, + "", + "", + }, + { + "time.Time, far in the future", + &T{T: time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC)}, + &T{T: time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC)}, + "", + "", + }, + { + "time.Time, very far in the past", + &T{T: time.Date(-300000, 1, 1, 0, 0, 0, 0, time.UTC)}, + &T{}, + "time value out of range", + "", + }, + { + "time.Time, very far in the future", + &T{T: time.Date(294248, 1, 1, 0, 0, 0, 0, time.UTC)}, + &T{}, + "time value out of range", + "", + }, + { + "structs", + &N0{ + X0: X0{S: "one", I: 2, i: 3}, + Nonymous: X0{S: "four", I: 5, i: 6}, + Ignore: "ignore", + Other: "other", + }, + &N0{ + X0: X0{S: "one", I: 2}, + Nonymous: X0{S: "four", I: 5}, + Other: "other", + }, + "", + "", + }, + { + "slice of structs", + &N1{ + X0: X0{S: "one", I: 2, i: 3}, + Nonymous: []X0{ + {S: "four", I: 5, i: 6}, + {S: "seven", I: 8, i: 9}, + {S: "ten", I: 11, i: 12}, + {S: "thirteen", I: 14, i: 15}, + }, + Ignore: "ignore", + Other: "other", + }, + &N1{ + X0: X0{S: "one", I: 2}, + Nonymous: []X0{ + {S: "four", I: 5}, + {S: "seven", I: 8}, + {S: "ten", I: 11}, + {S: "thirteen", I: 14}, + }, + Other: "other", + }, + "", + "", + }, + { + "structs with slices of structs", + &N2{ + N1: N1{ + X0: X0{S: "rouge"}, + Nonymous: []X0{ + {S: "rosso0"}, + {S: "rosso1"}, + }, + }, + Green: N1{ + X0: X0{S: "vert"}, + Nonymous: []X0{ + {S: "verde0"}, + {S: "verde1"}, + {S: "verde2"}, + }, + }, + Blue: N1{ + X0: X0{S: "bleu"}, + Nonymous: []X0{ + {S: "blu0"}, + {S: "blu1"}, + {S: "blu2"}, + {S: "blu3"}, + }, + }, + }, + &N2{ + N1: N1{ + X0: X0{S: "rouge"}, + Nonymous: []X0{ + {S: "rosso0"}, + {S: "rosso1"}, + }, + }, + Green: N1{ + X0: X0{S: "vert"}, + Nonymous: []X0{ + {S: "verde0"}, + {S: "verde1"}, + {S: "verde2"}, + }, + }, + Blue: N1{ + X0: X0{S: "bleu"}, + Nonymous: []X0{ + {S: "blu0"}, + {S: "blu1"}, + {S: "blu2"}, + {S: "blu3"}, + }, + }, + }, + "", + "", + }, + { + "save structs load props", + &N2{ + N1: N1{ + X0: X0{S: "rouge"}, + Nonymous: []X0{ + {S: "rosso0"}, + {S: "rosso1"}, + }, + }, + Green: N1{ + X0: X0{S: "vert"}, + Nonymous: []X0{ + {S: "verde0"}, + {S: "verde1"}, + {S: "verde2"}, + }, + }, + Blue: N1{ + X0: X0{S: "bleu"}, + Nonymous: []X0{ + {S: "blu0"}, + {S: "blu1"}, + {S: "blu2"}, + {S: "blu3"}, + }, + }, + }, + &PropertyList{ + Property{Name: "Blue.I", Value: int64(0), NoIndex: false, Multiple: false}, + Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.S", Value: "blu0", NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.S", Value: "blu1", NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.S", Value: "blu2", NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.S", Value: "blu3", NoIndex: false, Multiple: true}, + Property{Name: "Blue.Other", Value: "", NoIndex: false, Multiple: false}, + Property{Name: "Blue.S", Value: "bleu", NoIndex: false, Multiple: false}, + Property{Name: "green.I", Value: int64(0), NoIndex: false, Multiple: false}, + Property{Name: "green.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.S", Value: "verde0", NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.S", Value: "verde1", NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.S", Value: "verde2", NoIndex: false, Multiple: true}, + Property{Name: "green.Other", Value: "", NoIndex: false, Multiple: false}, + Property{Name: "green.S", Value: "vert", NoIndex: false, Multiple: false}, + Property{Name: "red.I", Value: int64(0), NoIndex: false, Multiple: false}, + Property{Name: "red.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "red.Nonymous.I", Value: int64(0), NoIndex: false, Multiple: true}, + Property{Name: "red.Nonymous.S", Value: "rosso0", NoIndex: false, Multiple: true}, + Property{Name: "red.Nonymous.S", Value: "rosso1", NoIndex: false, Multiple: true}, + Property{Name: "red.Other", Value: "", NoIndex: false, Multiple: false}, + Property{Name: "red.S", Value: "rouge", NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "save props load structs with ragged fields", + &PropertyList{ + Property{Name: "red.S", Value: "rot", NoIndex: false, Multiple: false}, + Property{Name: "green.Nonymous.I", Value: int64(10), NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.I", Value: int64(11), NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.I", Value: int64(12), NoIndex: false, Multiple: true}, + Property{Name: "green.Nonymous.I", Value: int64(13), NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.S", Value: "blau0", NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.I", Value: int64(20), NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.S", Value: "blau1", NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.I", Value: int64(21), NoIndex: false, Multiple: true}, + Property{Name: "Blue.Nonymous.S", Value: "blau2", NoIndex: false, Multiple: true}, + }, + &N2{ + N1: N1{ + X0: X0{S: "rot"}, + }, + Green: N1{ + Nonymous: []X0{ + {I: 10}, + {I: 11}, + {I: 12}, + {I: 13}, + }, + }, + Blue: N1{ + Nonymous: []X0{ + {S: "blau0", I: 20}, + {S: "blau1", I: 21}, + {S: "blau2"}, + }, + }, + }, + "", + "", + }, + { + "save structs with noindex tags", + &struct { + A struct { + X string `datastore:",noindex"` + Y string + } `datastore:",noindex"` + B struct { + X string `datastore:",noindex"` + Y string + } + }{}, + &PropertyList{ + Property{Name: "A.X", Value: "", NoIndex: true, Multiple: false}, + Property{Name: "A.Y", Value: "", NoIndex: true, Multiple: false}, + Property{Name: "B.X", Value: "", NoIndex: true, Multiple: false}, + Property{Name: "B.Y", Value: "", NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "embedded struct with name override", + &struct { + Inner1 `datastore:"foo"` + }{}, + &PropertyList{ + Property{Name: "foo.W", Value: int64(0), NoIndex: false, Multiple: false}, + Property{Name: "foo.X", Value: "", NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "slice of slices", + &SliceOfSlices{}, + nil, + "flattening nested structs leads to a slice of slices", + "", + }, + { + "recursive struct", + &Recursive{}, + nil, + "recursive struct", + "", + }, + { + "mutually recursive struct", + &MutuallyRecursive0{}, + nil, + "recursive struct", + "", + }, + { + "non-exported struct fields", + &struct { + i, J int64 + }{i: 1, J: 2}, + &PropertyList{ + Property{Name: "J", Value: int64(2), NoIndex: false, Multiple: false}, + }, + "", + "", + }, + { + "json.RawMessage", + &struct { + J json.RawMessage + }{ + J: json.RawMessage("rawr"), + }, + &PropertyList{ + Property{Name: "J", Value: []byte("rawr"), NoIndex: true, Multiple: false}, + }, + "", + "", + }, + { + "json.RawMessage to myBlob", + &struct { + B json.RawMessage + }{ + B: json.RawMessage("rawr"), + }, + &B2{B: myBlob("rawr")}, + "", + "", + }, + { + "embedded time field", + &SpecialTime{MyTime: EmbeddedTime{now}}, + &SpecialTime{MyTime: EmbeddedTime{now}}, + "", + "", + }, + { + "embedded time load", + &PropertyList{ + Property{Name: "MyTime.", Value: now, NoIndex: false, Multiple: false}, + }, + &SpecialTime{MyTime: EmbeddedTime{now}}, + "", + "", + }, +} + +// checkErr returns the empty string if either both want and err are zero, +// or if want is a non-empty substring of err's string representation. +func checkErr(want string, err error) string { + if err != nil { + got := err.Error() + if want == "" || strings.Index(got, want) == -1 { + return got + } + } else if want != "" { + return fmt.Sprintf("want error %q", want) + } + return "" +} + +func TestRoundTrip(t *testing.T) { + for _, tc := range testCases { + p, err := saveEntity(testAppID, testKey0, tc.src) + if s := checkErr(tc.putErr, err); s != "" { + t.Errorf("%s: save: %s", tc.desc, s) + continue + } + if p == nil { + continue + } + var got interface{} + if _, ok := tc.want.(*PropertyList); ok { + got = new(PropertyList) + } else { + got = reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() + } + err = loadEntity(got, p) + if s := checkErr(tc.getErr, err); s != "" { + t.Errorf("%s: load: %s", tc.desc, s) + continue + } + if pl, ok := got.(*PropertyList); ok { + // Sort by name to make sure we have a deterministic order. + sort.Stable(byName(*pl)) + } + equal := false + if gotT, ok := got.(*T); ok { + // Round tripping a time.Time can result in a different time.Location: Local instead of UTC. + // We therefore test equality explicitly, instead of relying on reflect.DeepEqual. + equal = gotT.T.Equal(tc.want.(*T).T) + } else { + equal = reflect.DeepEqual(got, tc.want) + } + if !equal { + t.Errorf("%s: compare: got %v want %v", tc.desc, got, tc.want) + continue + } + } +} + +type byName PropertyList + +func (s byName) Len() int { return len(s) } +func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name } +func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func TestQueryConstruction(t *testing.T) { + tests := []struct { + q, exp *Query + err string + }{ + { + q: NewQuery("Foo"), + exp: &Query{ + kind: "Foo", + limit: -1, + }, + }, + { + // Regular filtered query with standard spacing. + q: NewQuery("Foo").Filter("foo >", 7), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: greaterThan, + Value: 7, + }, + }, + limit: -1, + }, + }, + { + // Filtered query with no spacing. + q: NewQuery("Foo").Filter("foo=", 6), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: equal, + Value: 6, + }, + }, + limit: -1, + }, + }, + { + // Filtered query with funky spacing. + q: NewQuery("Foo").Filter(" foo< ", 8), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: lessThan, + Value: 8, + }, + }, + limit: -1, + }, + }, + { + // Filtered query with multicharacter op. + q: NewQuery("Foo").Filter("foo >=", 9), + exp: &Query{ + kind: "Foo", + filter: []filter{ + { + FieldName: "foo", + Op: greaterEq, + Value: 9, + }, + }, + limit: -1, + }, + }, + { + // Query with ordering. + q: NewQuery("Foo").Order("bar"), + exp: &Query{ + kind: "Foo", + order: []order{ + { + FieldName: "bar", + Direction: ascending, + }, + }, + limit: -1, + }, + }, + { + // Query with reverse ordering, and funky spacing. + q: NewQuery("Foo").Order(" - bar"), + exp: &Query{ + kind: "Foo", + order: []order{ + { + FieldName: "bar", + Direction: descending, + }, + }, + limit: -1, + }, + }, + { + // Query with an empty ordering. + q: NewQuery("Foo").Order(""), + err: "empty order", + }, + { + // Query with a + ordering. + q: NewQuery("Foo").Order("+bar"), + err: "invalid order", + }, + } + for i, test := range tests { + if test.q.err != nil { + got := test.q.err.Error() + if !strings.Contains(got, test.err) { + t.Errorf("%d: error mismatch: got %q want something containing %q", i, got, test.err) + } + continue + } + if !reflect.DeepEqual(test.q, test.exp) { + t.Errorf("%d: mismatch: got %v want %v", i, test.q, test.exp) + } + } +} + +func TestStringMeaning(t *testing.T) { + var xx [4]interface{} + xx[0] = &struct { + X string + }{"xx0"} + xx[1] = &struct { + X string `datastore:",noindex"` + }{"xx1"} + xx[2] = &struct { + X []byte + }{[]byte("xx2")} + xx[3] = &struct { + X []byte `datastore:",noindex"` + }{[]byte("xx3")} + + indexed := [4]bool{ + true, + false, + false, // A []byte is always no-index. + false, + } + want := [4]pb.Property_Meaning{ + pb.Property_NO_MEANING, + pb.Property_TEXT, + pb.Property_BLOB, + pb.Property_BLOB, + } + + for i, x := range xx { + props, err := SaveStruct(x) + if err != nil { + t.Errorf("i=%d: SaveStruct: %v", i, err) + continue + } + e, err := propertiesToProto("appID", testKey0, props) + if err != nil { + t.Errorf("i=%d: propertiesToProto: %v", i, err) + continue + } + var p *pb.Property + switch { + case indexed[i] && len(e.Property) == 1: + p = e.Property[0] + case !indexed[i] && len(e.RawProperty) == 1: + p = e.RawProperty[0] + default: + t.Errorf("i=%d: EntityProto did not have expected property slice", i) + continue + } + if got := p.GetMeaning(); got != want[i] { + t.Errorf("i=%d: meaning: got %v, want %v", i, got, want[i]) + continue + } + } +} + +func TestNamespaceResetting(t *testing.T) { + // These environment variables are necessary because *Query.Run will + // call internal.FullyQualifiedAppID which checks these variables or falls + // back to the Metadata service that is not available in tests. + environ := []struct { + key, value string + }{ + {"GAE_LONG_APP_ID", "my-app-id"}, + {"GAE_PARTITION", "1"}, + } + for _, v := range environ { + old := os.Getenv(v.key) + os.Setenv(v.key, v.value) + v.value = old + } + defer func() { // Restore old environment after the test completes. + for _, v := range environ { + if v.value == "" { + os.Unsetenv(v.key) + continue + } + os.Setenv(v.key, v.value) + } + }() + + namec := make(chan *string, 1) + c0 := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(req *pb.Query, res *pb.QueryResult) error { + namec <- req.NameSpace + return fmt.Errorf("RPC error") + }) + + // Check that wrapping c0 in a namespace twice works correctly. + c1, err := appengine.Namespace(c0, "A") + if err != nil { + t.Fatalf("appengine.Namespace: %v", err) + } + c2, err := appengine.Namespace(c1, "") // should act as the original context + if err != nil { + t.Fatalf("appengine.Namespace: %v", err) + } + + q := NewQuery("SomeKind") + + q.Run(c0) + if ns := <-namec; ns != nil { + t.Errorf(`RunQuery with c0: ns = %q, want nil`, *ns) + } + + q.Run(c1) + if ns := <-namec; ns == nil { + t.Error(`RunQuery with c1: ns = nil, want "A"`) + } else if *ns != "A" { + t.Errorf(`RunQuery with c1: ns = %q, want "A"`, *ns) + } + + q.Run(c2) + if ns := <-namec; ns != nil { + t.Errorf(`RunQuery with c2: ns = %q, want nil`, *ns) + } +} diff --git a/vendor/google.golang.org/appengine/datastore/doc.go b/vendor/google.golang.org/appengine/datastore/doc.go new file mode 100644 index 0000000000..85616cf274 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/doc.go @@ -0,0 +1,361 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package datastore provides a client for App Engine's datastore service. + + +Basic Operations + +Entities are the unit of storage and are associated with a key. A key +consists of an optional parent key, a string application ID, a string kind +(also known as an entity type), and either a StringID or an IntID. A +StringID is also known as an entity name or key name. + +It is valid to create a key with a zero StringID and a zero IntID; this is +called an incomplete key, and does not refer to any saved entity. Putting an +entity into the datastore under an incomplete key will cause a unique key +to be generated for that entity, with a non-zero IntID. + +An entity's contents are a mapping from case-sensitive field names to values. +Valid value types are: + - signed integers (int, int8, int16, int32 and int64), + - bool, + - string, + - float32 and float64, + - []byte (up to 1 megabyte in length), + - any type whose underlying type is one of the above predeclared types, + - ByteString, + - *Key, + - time.Time (stored with microsecond precision), + - appengine.BlobKey, + - appengine.GeoPoint, + - structs whose fields are all valid value types, + - slices of any of the above. + +Slices of structs are valid, as are structs that contain slices. However, if +one struct contains another, then at most one of those can be repeated. This +disqualifies recursively defined struct types: any struct T that (directly or +indirectly) contains a []T. + +The Get and Put functions load and save an entity's contents. An entity's +contents are typically represented by a struct pointer. + +Example code: + + type Entity struct { + Value string + } + + func handle(w http.ResponseWriter, r *http.Request) { + ctx := appengine.NewContext(r) + + k := datastore.NewKey(ctx, "Entity", "stringID", 0, nil) + e := new(Entity) + if err := datastore.Get(ctx, k, e); err != nil { + http.Error(w, err.Error(), 500) + return + } + + old := e.Value + e.Value = r.URL.Path + + if _, err := datastore.Put(ctx, k, e); err != nil { + http.Error(w, err.Error(), 500) + return + } + + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + fmt.Fprintf(w, "old=%q\nnew=%q\n", old, e.Value) + } + +GetMulti, PutMulti and DeleteMulti are batch versions of the Get, Put and +Delete functions. They take a []*Key instead of a *Key, and may return an +appengine.MultiError when encountering partial failure. + + +Properties + +An entity's contents can be represented by a variety of types. These are +typically struct pointers, but can also be any type that implements the +PropertyLoadSaver interface. If using a struct pointer, you do not have to +explicitly implement the PropertyLoadSaver interface; the datastore will +automatically convert via reflection. If a struct pointer does implement that +interface then those methods will be used in preference to the default +behavior for struct pointers. Struct pointers are more strongly typed and are +easier to use; PropertyLoadSavers are more flexible. + +The actual types passed do not have to match between Get and Put calls or even +across different calls to datastore. It is valid to put a *PropertyList and +get that same entity as a *myStruct, or put a *myStruct0 and get a *myStruct1. +Conceptually, any entity is saved as a sequence of properties, and is loaded +into the destination value on a property-by-property basis. When loading into +a struct pointer, an entity that cannot be completely represented (such as a +missing field) will result in an ErrFieldMismatch error but it is up to the +caller whether this error is fatal, recoverable or ignorable. + +By default, for struct pointers, all properties are potentially indexed, and +the property name is the same as the field name (and hence must start with an +upper case letter). + +Fields may have a `datastore:"name,options"` tag. The tag name is the +property name, which must be one or more valid Go identifiers joined by ".", +but may start with a lower case letter. An empty tag name means to just use the +field name. A "-" tag name means that the datastore will ignore that field. + +The only valid options are "omitempty" and "noindex". + +If the options include "omitempty" and the value of the field is empty, then the field will be omitted on Save. +The empty values are false, 0, any nil interface value, and any array, slice, map, or string of length zero. +Struct field values will never be empty. + +If options include "noindex" then the field will not be indexed. All fields are indexed +by default. Strings or byte slices longer than 1500 bytes cannot be indexed; +fields used to store long strings and byte slices must be tagged with "noindex" +or they will cause Put operations to fail. + +To use multiple options together, separate them by a comma. +The order does not matter. + +If the options is "" then the comma may be omitted. + +Example code: + + // A and B are renamed to a and b. + // A, C and J are not indexed. + // D's tag is equivalent to having no tag at all (E). + // I is ignored entirely by the datastore. + // J has tag information for both the datastore and json packages. + type TaggedStruct struct { + A int `datastore:"a,noindex"` + B int `datastore:"b"` + C int `datastore:",noindex"` + D int `datastore:""` + E int + I int `datastore:"-"` + J int `datastore:",noindex" json:"j"` + } + + +Structured Properties + +If the struct pointed to contains other structs, then the nested or embedded +structs are flattened. For example, given these definitions: + + type Inner1 struct { + W int32 + X string + } + + type Inner2 struct { + Y float64 + } + + type Inner3 struct { + Z bool + } + + type Outer struct { + A int16 + I []Inner1 + J Inner2 + Inner3 + } + +then an Outer's properties would be equivalent to those of: + + type OuterEquivalent struct { + A int16 + IDotW []int32 `datastore:"I.W"` + IDotX []string `datastore:"I.X"` + JDotY float64 `datastore:"J.Y"` + Z bool + } + +If Outer's embedded Inner3 field was tagged as `datastore:"Foo"` then the +equivalent field would instead be: FooDotZ bool `datastore:"Foo.Z"`. + +If an outer struct is tagged "noindex" then all of its implicit flattened +fields are effectively "noindex". + + +The PropertyLoadSaver Interface + +An entity's contents can also be represented by any type that implements the +PropertyLoadSaver interface. This type may be a struct pointer, but it does +not have to be. The datastore package will call Load when getting the entity's +contents, and Save when putting the entity's contents. +Possible uses include deriving non-stored fields, verifying fields, or indexing +a field only if its value is positive. + +Example code: + + type CustomPropsExample struct { + I, J int + // Sum is not stored, but should always be equal to I + J. + Sum int `datastore:"-"` + } + + func (x *CustomPropsExample) Load(ps []datastore.Property) error { + // Load I and J as usual. + if err := datastore.LoadStruct(x, ps); err != nil { + return err + } + // Derive the Sum field. + x.Sum = x.I + x.J + return nil + } + + func (x *CustomPropsExample) Save() ([]datastore.Property, error) { + // Validate the Sum field. + if x.Sum != x.I + x.J { + return nil, errors.New("CustomPropsExample has inconsistent sum") + } + // Save I and J as usual. The code below is equivalent to calling + // "return datastore.SaveStruct(x)", but is done manually for + // demonstration purposes. + return []datastore.Property{ + { + Name: "I", + Value: int64(x.I), + }, + { + Name: "J", + Value: int64(x.J), + }, + }, nil + } + +The *PropertyList type implements PropertyLoadSaver, and can therefore hold an +arbitrary entity's contents. + + +Queries + +Queries retrieve entities based on their properties or key's ancestry. Running +a query yields an iterator of results: either keys or (key, entity) pairs. +Queries are re-usable and it is safe to call Query.Run from concurrent +goroutines. Iterators are not safe for concurrent use. + +Queries are immutable, and are either created by calling NewQuery, or derived +from an existing query by calling a method like Filter or Order that returns a +new query value. A query is typically constructed by calling NewQuery followed +by a chain of zero or more such methods. These methods are: + - Ancestor and Filter constrain the entities returned by running a query. + - Order affects the order in which they are returned. + - Project constrains the fields returned. + - Distinct de-duplicates projected entities. + - KeysOnly makes the iterator return only keys, not (key, entity) pairs. + - Start, End, Offset and Limit define which sub-sequence of matching entities + to return. Start and End take cursors, Offset and Limit take integers. Start + and Offset affect the first result, End and Limit affect the last result. + If both Start and Offset are set, then the offset is relative to Start. + If both End and Limit are set, then the earliest constraint wins. Limit is + relative to Start+Offset, not relative to End. As a special case, a + negative limit means unlimited. + +Example code: + + type Widget struct { + Description string + Price int + } + + func handle(w http.ResponseWriter, r *http.Request) { + ctx := appengine.NewContext(r) + q := datastore.NewQuery("Widget"). + Filter("Price <", 1000). + Order("-Price") + b := new(bytes.Buffer) + for t := q.Run(ctx); ; { + var x Widget + key, err := t.Next(&x) + if err == datastore.Done { + break + } + if err != nil { + serveError(ctx, w, err) + return + } + fmt.Fprintf(b, "Key=%v\nWidget=%#v\n\n", key, x) + } + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + io.Copy(w, b) + } + + +Transactions + +RunInTransaction runs a function in a transaction. + +Example code: + + type Counter struct { + Count int + } + + func inc(ctx context.Context, key *datastore.Key) (int, error) { + var x Counter + if err := datastore.Get(ctx, key, &x); err != nil && err != datastore.ErrNoSuchEntity { + return 0, err + } + x.Count++ + if _, err := datastore.Put(ctx, key, &x); err != nil { + return 0, err + } + return x.Count, nil + } + + func handle(w http.ResponseWriter, r *http.Request) { + ctx := appengine.NewContext(r) + var count int + err := datastore.RunInTransaction(ctx, func(ctx context.Context) error { + var err1 error + count, err1 = inc(ctx, datastore.NewKey(ctx, "Counter", "singleton", 0, nil)) + return err1 + }, nil) + if err != nil { + serveError(ctx, w, err) + return + } + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + fmt.Fprintf(w, "Count=%d", count) + } + + +Metadata + +The datastore package provides access to some of App Engine's datastore +metadata. This metadata includes information about the entity groups, +namespaces, entity kinds, and properties in the datastore, as well as the +property representations for each property. + +Example code: + + func handle(w http.ResponseWriter, r *http.Request) { + // Print all the kinds in the datastore, with all the indexed + // properties (and their representations) for each. + ctx := appengine.NewContext(r) + + kinds, err := datastore.Kinds(ctx) + if err != nil { + serveError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + for _, kind := range kinds { + fmt.Fprintf(w, "%s:\n", kind) + props, err := datastore.KindProperties(ctx, kind) + if err != nil { + fmt.Fprintln(w, "\t(unable to retrieve properties)") + continue + } + for p, rep := range props { + fmt.Fprintf(w, "\t-%s (%s)\n", p, strings.Join(rep, ", ")) + } + } + } +*/ +package datastore // import "google.golang.org/appengine/datastore" diff --git a/vendor/google.golang.org/appengine/datastore/key.go b/vendor/google.golang.org/appengine/datastore/key.go new file mode 100644 index 0000000000..6ab83eaf65 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/key.go @@ -0,0 +1,396 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "bytes" + "encoding/base64" + "encoding/gob" + "errors" + "fmt" + "strconv" + "strings" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/datastore" +) + +type KeyRangeCollisionError struct { + start int64 + end int64 +} + +func (e *KeyRangeCollisionError) Error() string { + return fmt.Sprintf("datastore: Collision when attempting to allocate range [%d, %d]", + e.start, e.end) +} + +type KeyRangeContentionError struct { + start int64 + end int64 +} + +func (e *KeyRangeContentionError) Error() string { + return fmt.Sprintf("datastore: Contention when attempting to allocate range [%d, %d]", + e.start, e.end) +} + +// Key represents the datastore key for a stored entity, and is immutable. +type Key struct { + kind string + stringID string + intID int64 + parent *Key + appID string + namespace string +} + +// Kind returns the key's kind (also known as entity type). +func (k *Key) Kind() string { + return k.kind +} + +// StringID returns the key's string ID (also known as an entity name or key +// name), which may be "". +func (k *Key) StringID() string { + return k.stringID +} + +// IntID returns the key's integer ID, which may be 0. +func (k *Key) IntID() int64 { + return k.intID +} + +// Parent returns the key's parent key, which may be nil. +func (k *Key) Parent() *Key { + return k.parent +} + +// AppID returns the key's application ID. +func (k *Key) AppID() string { + return k.appID +} + +// Namespace returns the key's namespace. +func (k *Key) Namespace() string { + return k.namespace +} + +// Incomplete returns whether the key does not refer to a stored entity. +// In particular, whether the key has a zero StringID and a zero IntID. +func (k *Key) Incomplete() bool { + return k.stringID == "" && k.intID == 0 +} + +// valid returns whether the key is valid. +func (k *Key) valid() bool { + if k == nil { + return false + } + for ; k != nil; k = k.parent { + if k.kind == "" || k.appID == "" { + return false + } + if k.stringID != "" && k.intID != 0 { + return false + } + if k.parent != nil { + if k.parent.Incomplete() { + return false + } + if k.parent.appID != k.appID || k.parent.namespace != k.namespace { + return false + } + } + } + return true +} + +// Equal returns whether two keys are equal. +func (k *Key) Equal(o *Key) bool { + for k != nil && o != nil { + if k.kind != o.kind || k.stringID != o.stringID || k.intID != o.intID || k.appID != o.appID || k.namespace != o.namespace { + return false + } + k, o = k.parent, o.parent + } + return k == o +} + +// root returns the furthest ancestor of a key, which may be itself. +func (k *Key) root() *Key { + for k.parent != nil { + k = k.parent + } + return k +} + +// marshal marshals the key's string representation to the buffer. +func (k *Key) marshal(b *bytes.Buffer) { + if k.parent != nil { + k.parent.marshal(b) + } + b.WriteByte('/') + b.WriteString(k.kind) + b.WriteByte(',') + if k.stringID != "" { + b.WriteString(k.stringID) + } else { + b.WriteString(strconv.FormatInt(k.intID, 10)) + } +} + +// String returns a string representation of the key. +func (k *Key) String() string { + if k == nil { + return "" + } + b := bytes.NewBuffer(make([]byte, 0, 512)) + k.marshal(b) + return b.String() +} + +type gobKey struct { + Kind string + StringID string + IntID int64 + Parent *gobKey + AppID string + Namespace string +} + +func keyToGobKey(k *Key) *gobKey { + if k == nil { + return nil + } + return &gobKey{ + Kind: k.kind, + StringID: k.stringID, + IntID: k.intID, + Parent: keyToGobKey(k.parent), + AppID: k.appID, + Namespace: k.namespace, + } +} + +func gobKeyToKey(gk *gobKey) *Key { + if gk == nil { + return nil + } + return &Key{ + kind: gk.Kind, + stringID: gk.StringID, + intID: gk.IntID, + parent: gobKeyToKey(gk.Parent), + appID: gk.AppID, + namespace: gk.Namespace, + } +} + +func (k *Key) GobEncode() ([]byte, error) { + buf := new(bytes.Buffer) + if err := gob.NewEncoder(buf).Encode(keyToGobKey(k)); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (k *Key) GobDecode(buf []byte) error { + gk := new(gobKey) + if err := gob.NewDecoder(bytes.NewBuffer(buf)).Decode(gk); err != nil { + return err + } + *k = *gobKeyToKey(gk) + return nil +} + +func (k *Key) MarshalJSON() ([]byte, error) { + return []byte(`"` + k.Encode() + `"`), nil +} + +func (k *Key) UnmarshalJSON(buf []byte) error { + if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' { + return errors.New("datastore: bad JSON key") + } + k2, err := DecodeKey(string(buf[1 : len(buf)-1])) + if err != nil { + return err + } + *k = *k2 + return nil +} + +// Encode returns an opaque representation of the key +// suitable for use in HTML and URLs. +// This is compatible with the Python and Java runtimes. +func (k *Key) Encode() string { + ref := keyToProto("", k) + + b, err := proto.Marshal(ref) + if err != nil { + panic(err) + } + + // Trailing padding is stripped. + return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") +} + +// DecodeKey decodes a key from the opaque representation returned by Encode. +func DecodeKey(encoded string) (*Key, error) { + // Re-add padding. + if m := len(encoded) % 4; m != 0 { + encoded += strings.Repeat("=", 4-m) + } + + b, err := base64.URLEncoding.DecodeString(encoded) + if err != nil { + return nil, err + } + + ref := new(pb.Reference) + if err := proto.Unmarshal(b, ref); err != nil { + return nil, err + } + + return protoToKey(ref) +} + +// NewIncompleteKey creates a new incomplete key. +// kind cannot be empty. +func NewIncompleteKey(c context.Context, kind string, parent *Key) *Key { + return NewKey(c, kind, "", 0, parent) +} + +// NewKey creates a new key. +// kind cannot be empty. +// Either one or both of stringID and intID must be zero. If both are zero, +// the key returned is incomplete. +// parent must either be a complete key or nil. +func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key { + // If there's a parent key, use its namespace. + // Otherwise, use any namespace attached to the context. + var namespace string + if parent != nil { + namespace = parent.namespace + } else { + namespace = internal.NamespaceFromContext(c) + } + + return &Key{ + kind: kind, + stringID: stringID, + intID: intID, + parent: parent, + appID: internal.FullyQualifiedAppID(c), + namespace: namespace, + } +} + +// AllocateIDs returns a range of n integer IDs with the given kind and parent +// combination. kind cannot be empty; parent may be nil. The IDs in the range +// returned will not be used by the datastore's automatic ID sequence generator +// and may be used with NewKey without conflict. +// +// The range is inclusive at the low end and exclusive at the high end. In +// other words, valid intIDs x satisfy low <= x && x < high. +// +// If no error is returned, low + n == high. +func AllocateIDs(c context.Context, kind string, parent *Key, n int) (low, high int64, err error) { + if kind == "" { + return 0, 0, errors.New("datastore: AllocateIDs given an empty kind") + } + if n < 0 { + return 0, 0, fmt.Errorf("datastore: AllocateIDs given a negative count: %d", n) + } + if n == 0 { + return 0, 0, nil + } + req := &pb.AllocateIdsRequest{ + ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)), + Size: proto.Int64(int64(n)), + } + res := &pb.AllocateIdsResponse{} + if err := internal.Call(c, "datastore_v3", "AllocateIds", req, res); err != nil { + return 0, 0, err + } + // The protobuf is inclusive at both ends. Idiomatic Go (e.g. slices, for loops) + // is inclusive at the low end and exclusive at the high end, so we add 1. + low = res.GetStart() + high = res.GetEnd() + 1 + if low+int64(n) != high { + return 0, 0, fmt.Errorf("datastore: internal error: could not allocate %d IDs", n) + } + return low, high, nil +} + +// AllocateIDRange allocates a range of IDs with specific endpoints. +// The range is inclusive at both the low and high end. Once these IDs have been +// allocated, you can manually assign them to newly created entities. +// +// The Datastore's automatic ID allocator never assigns a key that has already +// been allocated (either through automatic ID allocation or through an explicit +// AllocateIDs call). As a result, entities written to the given key range will +// never be overwritten. However, writing entities with manually assigned keys in +// this range may overwrite existing entities (or new entities written by a separate +// request), depending on the error returned. +// +// Use this only if you have an existing numeric ID range that you want to reserve +// (for example, bulk loading entities that already have IDs). If you don't care +// about which IDs you receive, use AllocateIDs instead. +// +// AllocateIDRange returns nil if the range is successfully allocated. If one or more +// entities with an ID in the given range already exist, it returns a KeyRangeCollisionError. +// If the Datastore has already cached IDs in this range (e.g. from a previous call to +// AllocateIDRange), it returns a KeyRangeContentionError. Errors of other types indicate +// problems with arguments or an error returned directly from the Datastore. +func AllocateIDRange(c context.Context, kind string, parent *Key, start, end int64) (err error) { + if kind == "" { + return errors.New("datastore: AllocateIDRange given an empty kind") + } + + if start < 1 || end < 1 { + return errors.New("datastore: AllocateIDRange start and end must both be greater than 0") + } + + if start > end { + return errors.New("datastore: AllocateIDRange start must be before end") + } + + req := &pb.AllocateIdsRequest{ + ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)), + Max: proto.Int64(end), + } + res := &pb.AllocateIdsResponse{} + if err := internal.Call(c, "datastore_v3", "AllocateIds", req, res); err != nil { + return err + } + + // Check for collisions, i.e. existing entities with IDs in this range. + // We could do this before the allocation, but we'd still have to do it + // afterward as well to catch the race condition where an entity is inserted + // after that initial check but before the allocation. Skip the up-front check + // and just do it once. + q := NewQuery(kind).Filter("__key__ >=", NewKey(c, kind, "", start, parent)). + Filter("__key__ <=", NewKey(c, kind, "", end, parent)).KeysOnly().Limit(1) + + keys, err := q.GetAll(c, nil) + if err != nil { + return err + } + if len(keys) != 0 { + return &KeyRangeCollisionError{start: start, end: end} + } + + // Check for a race condition, i.e. cases where the datastore may have + // cached ID batches that contain IDs in this range. + if start < res.GetStart() { + return &KeyRangeContentionError{start: start, end: end} + } + + return nil +} diff --git a/vendor/google.golang.org/appengine/datastore/key_test.go b/vendor/google.golang.org/appengine/datastore/key_test.go new file mode 100644 index 0000000000..1fb3e97523 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/key_test.go @@ -0,0 +1,204 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "testing" + + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +func TestKeyEncoding(t *testing.T) { + testCases := []struct { + desc string + key *Key + exp string + }{ + { + desc: "A simple key with an int ID", + key: &Key{ + kind: "Person", + intID: 1, + appID: "glibrary", + }, + exp: "aghnbGlicmFyeXIMCxIGUGVyc29uGAEM", + }, + { + desc: "A simple key with a string ID", + key: &Key{ + kind: "Graph", + stringID: "graph:7-day-active", + appID: "glibrary", + }, + exp: "aghnbGlicmFyeXIdCxIFR3JhcGgiEmdyYXBoOjctZGF5LWFjdGl2ZQw", + }, + { + desc: "A key with a parent", + key: &Key{ + kind: "WordIndex", + intID: 1033, + parent: &Key{ + kind: "WordIndex", + intID: 1020032, + appID: "glibrary", + }, + appID: "glibrary", + }, + exp: "aghnbGlicmFyeXIhCxIJV29yZEluZGV4GIChPgwLEglXb3JkSW5kZXgYiQgM", + }, + } + for _, tc := range testCases { + enc := tc.key.Encode() + if enc != tc.exp { + t.Errorf("%s: got %q, want %q", tc.desc, enc, tc.exp) + } + + key, err := DecodeKey(tc.exp) + if err != nil { + t.Errorf("%s: failed decoding key: %v", tc.desc, err) + continue + } + if !key.Equal(tc.key) { + t.Errorf("%s: decoded key %v, want %v", tc.desc, key, tc.key) + } + } +} + +func TestKeyGob(t *testing.T) { + k := &Key{ + kind: "Gopher", + intID: 3, + parent: &Key{ + kind: "Mom", + stringID: "narwhal", + appID: "gopher-con", + }, + appID: "gopher-con", + } + + buf := new(bytes.Buffer) + if err := gob.NewEncoder(buf).Encode(k); err != nil { + t.Fatalf("gob encode failed: %v", err) + } + + k2 := new(Key) + if err := gob.NewDecoder(buf).Decode(k2); err != nil { + t.Fatalf("gob decode failed: %v", err) + } + if !k2.Equal(k) { + t.Errorf("gob round trip of %v produced %v", k, k2) + } +} + +func TestNilKeyGob(t *testing.T) { + type S struct { + Key *Key + } + s1 := new(S) + + buf := new(bytes.Buffer) + if err := gob.NewEncoder(buf).Encode(s1); err != nil { + t.Fatalf("gob encode failed: %v", err) + } + + s2 := new(S) + if err := gob.NewDecoder(buf).Decode(s2); err != nil { + t.Fatalf("gob decode failed: %v", err) + } + if s2.Key != nil { + t.Errorf("gob round trip of nil key produced %v", s2.Key) + } +} + +func TestKeyJSON(t *testing.T) { + k := &Key{ + kind: "Gopher", + intID: 2, + parent: &Key{ + kind: "Mom", + stringID: "narwhal", + appID: "gopher-con", + }, + appID: "gopher-con", + } + exp := `"` + k.Encode() + `"` + + buf, err := json.Marshal(k) + if err != nil { + t.Fatalf("json.Marshal failed: %v", err) + } + if s := string(buf); s != exp { + t.Errorf("JSON encoding of key %v: got %q, want %q", k, s, exp) + } + + k2 := new(Key) + if err := json.Unmarshal(buf, k2); err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } + if !k2.Equal(k) { + t.Errorf("JSON round trip of %v produced %v", k, k2) + } +} + +func TestNilKeyJSON(t *testing.T) { + type S struct { + Key *Key + } + s1 := new(S) + + buf, err := json.Marshal(s1) + if err != nil { + t.Fatalf("json.Marshal failed: %v", err) + } + + s2 := new(S) + if err := json.Unmarshal(buf, s2); err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } + if s2.Key != nil { + t.Errorf("JSON round trip of nil key produced %v", s2.Key) + } +} + +func TestIncompleteKeyWithParent(t *testing.T) { + c := internal.WithAppIDOverride(context.Background(), "s~some-app") + + // fadduh is a complete key. + fadduh := NewKey(c, "Person", "", 1, nil) + if fadduh.Incomplete() { + t.Fatalf("fadduh is incomplete") + } + + // robert is an incomplete key with fadduh as a parent. + robert := NewIncompleteKey(c, "Person", fadduh) + if !robert.Incomplete() { + t.Fatalf("robert is complete") + } + + // Both should be valid keys. + if !fadduh.valid() { + t.Errorf("fadduh is invalid: %v", fadduh) + } + if !robert.valid() { + t.Errorf("robert is invalid: %v", robert) + } +} + +func TestNamespace(t *testing.T) { + key := &Key{ + kind: "Person", + intID: 1, + appID: "s~some-app", + namespace: "mynamespace", + } + if g, w := key.Namespace(), "mynamespace"; g != w { + t.Errorf("key.Namespace() = %q, want %q", g, w) + } +} diff --git a/vendor/google.golang.org/appengine/datastore/load.go b/vendor/google.golang.org/appengine/datastore/load.go new file mode 100644 index 0000000000..38a6365397 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/load.go @@ -0,0 +1,429 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "fmt" + "reflect" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "google.golang.org/appengine" + pb "google.golang.org/appengine/internal/datastore" +) + +var ( + typeOfBlobKey = reflect.TypeOf(appengine.BlobKey("")) + typeOfByteSlice = reflect.TypeOf([]byte(nil)) + typeOfByteString = reflect.TypeOf(ByteString(nil)) + typeOfGeoPoint = reflect.TypeOf(appengine.GeoPoint{}) + typeOfTime = reflect.TypeOf(time.Time{}) + typeOfKeyPtr = reflect.TypeOf(&Key{}) + typeOfEntityPtr = reflect.TypeOf(&Entity{}) +) + +// typeMismatchReason returns a string explaining why the property p could not +// be stored in an entity field of type v.Type(). +func typeMismatchReason(pValue interface{}, v reflect.Value) string { + entityType := "empty" + switch pValue.(type) { + case int64: + entityType = "int" + case bool: + entityType = "bool" + case string: + entityType = "string" + case float64: + entityType = "float" + case *Key: + entityType = "*datastore.Key" + case time.Time: + entityType = "time.Time" + case appengine.BlobKey: + entityType = "appengine.BlobKey" + case appengine.GeoPoint: + entityType = "appengine.GeoPoint" + case ByteString: + entityType = "datastore.ByteString" + case []byte: + entityType = "[]byte" + } + return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type()) +} + +type propertyLoader struct { + // m holds the number of times a substruct field like "Foo.Bar.Baz" has + // been seen so far. The map is constructed lazily. + m map[string]int +} + +func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p Property, requireSlice bool) string { + var v reflect.Value + var sliceIndex int + + name := p.Name + + // If name ends with a '.', the last field is anonymous. + // In this case, strings.Split will give us "" as the + // last element of our fields slice, which will match the "" + // field name in the substruct codec. + fields := strings.Split(name, ".") + + for len(fields) > 0 { + var decoder fieldCodec + var ok bool + + // Cut off the last field (delimited by ".") and find its parent + // in the codec. + // eg. for name "A.B.C.D", split off "A.B.C" and try to + // find a field in the codec with this name. + // Loop again with "A.B", etc. + for i := len(fields); i > 0; i-- { + parent := strings.Join(fields[:i], ".") + decoder, ok = codec.fields[parent] + if ok { + fields = fields[i:] + break + } + } + + // If we never found a matching field in the codec, return + // error message. + if !ok { + return "no such struct field" + } + + v = initField(structValue, decoder.path) + if !v.IsValid() { + return "no such struct field" + } + if !v.CanSet() { + return "cannot set struct field" + } + + if decoder.structCodec != nil { + codec = decoder.structCodec + structValue = v + } + + if v.Kind() == reflect.Slice && v.Type() != typeOfByteSlice { + if l.m == nil { + l.m = make(map[string]int) + } + sliceIndex = l.m[p.Name] + l.m[p.Name] = sliceIndex + 1 + for v.Len() <= sliceIndex { + v.Set(reflect.Append(v, reflect.New(v.Type().Elem()).Elem())) + } + structValue = v.Index(sliceIndex) + requireSlice = false + } + } + + var slice reflect.Value + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { + slice = v + v = reflect.New(v.Type().Elem()).Elem() + } else if requireSlice { + return "multiple-valued property requires a slice field type" + } + + // Convert indexValues to a Go value with a meaning derived from the + // destination type. + pValue := p.Value + if iv, ok := pValue.(indexValue); ok { + meaning := pb.Property_NO_MEANING + switch v.Type() { + case typeOfBlobKey: + meaning = pb.Property_BLOBKEY + case typeOfByteSlice: + meaning = pb.Property_BLOB + case typeOfByteString: + meaning = pb.Property_BYTESTRING + case typeOfGeoPoint: + meaning = pb.Property_GEORSS_POINT + case typeOfTime: + meaning = pb.Property_GD_WHEN + case typeOfEntityPtr: + meaning = pb.Property_ENTITY_PROTO + } + var err error + pValue, err = propValue(iv.value, meaning) + if err != nil { + return err.Error() + } + } + + if errReason := setVal(v, pValue); errReason != "" { + // Set the slice back to its zero value. + if slice.IsValid() { + slice.Set(reflect.Zero(slice.Type())) + } + return errReason + } + + if slice.IsValid() { + slice.Index(sliceIndex).Set(v) + } + + return "" +} + +// setVal sets v to the value pValue. +func setVal(v reflect.Value, pValue interface{}) string { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x, ok := pValue.(int64) + if !ok && pValue != nil { + return typeMismatchReason(pValue, v) + } + if v.OverflowInt(x) { + return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type()) + } + v.SetInt(x) + case reflect.Bool: + x, ok := pValue.(bool) + if !ok && pValue != nil { + return typeMismatchReason(pValue, v) + } + v.SetBool(x) + case reflect.String: + switch x := pValue.(type) { + case appengine.BlobKey: + v.SetString(string(x)) + case ByteString: + v.SetString(string(x)) + case string: + v.SetString(x) + default: + if pValue != nil { + return typeMismatchReason(pValue, v) + } + } + case reflect.Float32, reflect.Float64: + x, ok := pValue.(float64) + if !ok && pValue != nil { + return typeMismatchReason(pValue, v) + } + if v.OverflowFloat(x) { + return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type()) + } + v.SetFloat(x) + case reflect.Ptr: + x, ok := pValue.(*Key) + if !ok && pValue != nil { + return typeMismatchReason(pValue, v) + } + if _, ok := v.Interface().(*Key); !ok { + return typeMismatchReason(pValue, v) + } + v.Set(reflect.ValueOf(x)) + case reflect.Struct: + switch v.Type() { + case typeOfTime: + x, ok := pValue.(time.Time) + if !ok && pValue != nil { + return typeMismatchReason(pValue, v) + } + v.Set(reflect.ValueOf(x)) + case typeOfGeoPoint: + x, ok := pValue.(appengine.GeoPoint) + if !ok && pValue != nil { + return typeMismatchReason(pValue, v) + } + v.Set(reflect.ValueOf(x)) + default: + ent, ok := pValue.(*Entity) + if !ok { + return typeMismatchReason(pValue, v) + } + + // Recursively load nested struct + pls, err := newStructPLS(v.Addr().Interface()) + if err != nil { + return err.Error() + } + + // if ent has a Key value and our struct has a Key field, + // load the Entity's Key value into the Key field on the struct. + if ent.Key != nil && pls.codec.keyField != -1 { + + pls.v.Field(pls.codec.keyField).Set(reflect.ValueOf(ent.Key)) + } + + err = pls.Load(ent.Properties) + if err != nil { + return err.Error() + } + } + case reflect.Slice: + x, ok := pValue.([]byte) + if !ok { + if y, yok := pValue.(ByteString); yok { + x, ok = []byte(y), true + } + } + if !ok && pValue != nil { + return typeMismatchReason(pValue, v) + } + if v.Type().Elem().Kind() != reflect.Uint8 { + return typeMismatchReason(pValue, v) + } + v.SetBytes(x) + default: + return typeMismatchReason(pValue, v) + } + return "" +} + +// initField is similar to reflect's Value.FieldByIndex, in that it +// returns the nested struct field corresponding to index, but it +// initialises any nil pointers encountered when traversing the structure. +func initField(val reflect.Value, index []int) reflect.Value { + for _, i := range index[:len(index)-1] { + val = val.Field(i) + if val.Kind() == reflect.Ptr { + if val.IsNil() { + val.Set(reflect.New(val.Type().Elem())) + } + val = val.Elem() + } + } + return val.Field(index[len(index)-1]) +} + +// loadEntity loads an EntityProto into PropertyLoadSaver or struct pointer. +func loadEntity(dst interface{}, src *pb.EntityProto) (err error) { + ent, err := protoToEntity(src) + if err != nil { + return err + } + if e, ok := dst.(PropertyLoadSaver); ok { + return e.Load(ent.Properties) + } + return LoadStruct(dst, ent.Properties) +} + +func (s structPLS) Load(props []Property) error { + var fieldName, reason string + var l propertyLoader + for _, p := range props { + if errStr := l.load(s.codec, s.v, p, p.Multiple); errStr != "" { + // We don't return early, as we try to load as many properties as possible. + // It is valid to load an entity into a struct that cannot fully represent it. + // That case returns an error, but the caller is free to ignore it. + fieldName, reason = p.Name, errStr + } + } + if reason != "" { + return &ErrFieldMismatch{ + StructType: s.v.Type(), + FieldName: fieldName, + Reason: reason, + } + } + return nil +} + +func protoToEntity(src *pb.EntityProto) (*Entity, error) { + props, rawProps := src.Property, src.RawProperty + outProps := make([]Property, 0, len(props)+len(rawProps)) + for { + var ( + x *pb.Property + noIndex bool + ) + if len(props) > 0 { + x, props = props[0], props[1:] + } else if len(rawProps) > 0 { + x, rawProps = rawProps[0], rawProps[1:] + noIndex = true + } else { + break + } + + var value interface{} + if x.Meaning != nil && *x.Meaning == pb.Property_INDEX_VALUE { + value = indexValue{x.Value} + } else { + var err error + value, err = propValue(x.Value, x.GetMeaning()) + if err != nil { + return nil, err + } + } + outProps = append(outProps, Property{ + Name: x.GetName(), + Value: value, + NoIndex: noIndex, + Multiple: x.GetMultiple(), + }) + } + + var key *Key + if src.Key != nil { + // Ignore any error, since nested entity values + // are allowed to have an invalid key. + key, _ = protoToKey(src.Key) + } + return &Entity{key, outProps}, nil +} + +// propValue returns a Go value that combines the raw PropertyValue with a +// meaning. For example, an Int64Value with GD_WHEN becomes a time.Time. +func propValue(v *pb.PropertyValue, m pb.Property_Meaning) (interface{}, error) { + switch { + case v.Int64Value != nil: + if m == pb.Property_GD_WHEN { + return fromUnixMicro(*v.Int64Value), nil + } else { + return *v.Int64Value, nil + } + case v.BooleanValue != nil: + return *v.BooleanValue, nil + case v.StringValue != nil: + if m == pb.Property_BLOB { + return []byte(*v.StringValue), nil + } else if m == pb.Property_BLOBKEY { + return appengine.BlobKey(*v.StringValue), nil + } else if m == pb.Property_BYTESTRING { + return ByteString(*v.StringValue), nil + } else if m == pb.Property_ENTITY_PROTO { + var ent pb.EntityProto + err := proto.Unmarshal([]byte(*v.StringValue), &ent) + if err != nil { + return nil, err + } + return protoToEntity(&ent) + } else { + return *v.StringValue, nil + } + case v.DoubleValue != nil: + return *v.DoubleValue, nil + case v.Referencevalue != nil: + key, err := referenceValueToKey(v.Referencevalue) + if err != nil { + return nil, err + } + return key, nil + case v.Pointvalue != nil: + // NOTE: Strangely, latitude maps to X, longitude to Y. + return appengine.GeoPoint{Lat: v.Pointvalue.GetX(), Lng: v.Pointvalue.GetY()}, nil + } + return nil, nil +} + +// indexValue is a Property value that is created when entities are loaded from +// an index, such as from a projection query. +// +// Such Property values do not contain all of the metadata required to be +// faithfully represented as a Go value, and are instead represented as an +// opaque indexValue. Load the properties into a concrete struct type (e.g. by +// passing a struct pointer to Iterator.Next) to reconstruct actual Go values +// of type int, string, time.Time, etc. +type indexValue struct { + value *pb.PropertyValue +} diff --git a/vendor/google.golang.org/appengine/datastore/load_test.go b/vendor/google.golang.org/appengine/datastore/load_test.go new file mode 100644 index 0000000000..46029bba51 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/load_test.go @@ -0,0 +1,656 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "reflect" + "testing" + + proto "github.com/golang/protobuf/proto" + pb "google.golang.org/appengine/internal/datastore" +) + +type Simple struct { + I int64 +} + +type SimpleWithTag struct { + I int64 `datastore:"II"` +} + +type NestedSimpleWithTag struct { + A SimpleWithTag `datastore:"AA"` +} + +type NestedSliceOfSimple struct { + A []Simple +} + +type SimpleTwoFields struct { + S string + SS string +} + +type NestedSimpleAnonymous struct { + Simple + X string +} + +type NestedSimple struct { + A Simple + I int64 +} + +type NestedSimple1 struct { + A Simple + X string +} + +type NestedSimple2X struct { + AA NestedSimple + A SimpleTwoFields + S string +} + +type BDotB struct { + B string `datastore:"B.B"` +} + +type ABDotB struct { + A BDotB +} + +type MultiAnonymous struct { + Simple + SimpleTwoFields + X string +} + +var ( + // these values need to be addressable + testString2 = "two" + testString3 = "three" + testInt64 = int64(2) + + fieldNameI = "I" + fieldNameX = "X" + fieldNameS = "S" + fieldNameSS = "SS" + fieldNameADotI = "A.I" + fieldNameAADotII = "AA.II" + fieldNameADotBDotB = "A.B.B" +) + +func TestLoadEntityNestedLegacy(t *testing.T) { + testCases := []struct { + desc string + src *pb.EntityProto + want interface{} + }{ + { + "nested", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameX, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + &pb.Property{ + Name: &fieldNameADotI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + }, + }, + }, + &NestedSimple1{ + A: Simple{I: testInt64}, + X: testString2, + }, + }, + { + "nested with tag", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameAADotII, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + }, + }, + }, + &NestedSimpleWithTag{ + A: SimpleWithTag{I: testInt64}, + }, + }, + { + "nested with anonymous struct field", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameX, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + &pb.Property{ + Name: &fieldNameI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + }, + }, + }, + &NestedSimpleAnonymous{ + Simple: Simple{I: testInt64}, + X: testString2, + }, + }, + { + "nested with dotted field tag", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameADotBDotB, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + }, + }, + &ABDotB{ + A: BDotB{ + B: testString2, + }, + }, + }, + { + "nested with dotted field tag", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + }, + &pb.Property{ + Name: &fieldNameS, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + &pb.Property{ + Name: &fieldNameSS, + Value: &pb.PropertyValue{ + StringValue: &testString3, + }, + }, + &pb.Property{ + Name: &fieldNameX, + Value: &pb.PropertyValue{ + StringValue: &testString3, + }, + }, + }, + }, + &MultiAnonymous{ + Simple: Simple{I: testInt64}, + SimpleTwoFields: SimpleTwoFields{S: "two", SS: "three"}, + X: "three", + }, + }, + } + + for _, tc := range testCases { + dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() + err := loadEntity(dst, tc.src) + if err != nil { + t.Errorf("loadEntity: %s: %v", tc.desc, err) + continue + } + + if !reflect.DeepEqual(tc.want, dst) { + t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, dst, tc.want) + } + } +} + +type WithKey struct { + X string + I int64 + K *Key `datastore:"__key__"` +} + +type NestedWithKey struct { + N WithKey + Y string +} + +var ( + incompleteKey = newKey("", nil) + invalidKey = newKey("s", incompleteKey) + + // these values need to be addressable + fieldNameA = "A" + fieldNameK = "K" + fieldNameN = "N" + fieldNameY = "Y" + fieldNameAA = "AA" + fieldNameII = "II" + fieldNameBDotB = "B.B" + + entityProtoMeaning = pb.Property_ENTITY_PROTO + + TRUE = true + FALSE = false +) + +var ( + simpleEntityProto, nestedSimpleEntityProto, + simpleTwoFieldsEntityProto, simpleWithTagEntityProto, + bDotBEntityProto, withKeyEntityProto string +) + +func init() { + // simpleEntityProto corresponds to: + // Simple{I: testInt64} + simpleEntityProtob, err := proto.Marshal(&pb.EntityProto{ + Key: keyToProto("", incompleteKey), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + Multiple: &FALSE, + }, + }, + EntityGroup: &pb.Path{}, + }) + if err != nil { + panic(err) + } + simpleEntityProto = string(simpleEntityProtob) + + // nestedSimpleEntityProto corresponds to: + // NestedSimple{ + // A: Simple{I: testInt64}, + // I: testInt64, + // } + nestedSimpleEntityProtob, err := proto.Marshal(&pb.EntityProto{ + Key: keyToProto("", incompleteKey), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameA, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + StringValue: &simpleEntityProto, + }, + Multiple: &FALSE, + }, + &pb.Property{ + Name: &fieldNameI, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + Multiple: &FALSE, + }, + }, + EntityGroup: &pb.Path{}, + }) + if err != nil { + panic(err) + } + nestedSimpleEntityProto = string(nestedSimpleEntityProtob) + + // simpleTwoFieldsEntityProto corresponds to: + // SimpleTwoFields{S: testString2, SS: testString3} + simpleTwoFieldsEntityProtob, err := proto.Marshal(&pb.EntityProto{ + Key: keyToProto("", incompleteKey), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameS, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + Multiple: &FALSE, + }, + &pb.Property{ + Name: &fieldNameSS, + Value: &pb.PropertyValue{ + StringValue: &testString3, + }, + Multiple: &FALSE, + }, + }, + EntityGroup: &pb.Path{}, + }) + if err != nil { + panic(err) + } + simpleTwoFieldsEntityProto = string(simpleTwoFieldsEntityProtob) + + // simpleWithTagEntityProto corresponds to: + // SimpleWithTag{I: testInt64} + simpleWithTagEntityProtob, err := proto.Marshal(&pb.EntityProto{ + Key: keyToProto("", incompleteKey), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameII, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + Multiple: &FALSE, + }, + }, + EntityGroup: &pb.Path{}, + }) + if err != nil { + panic(err) + } + simpleWithTagEntityProto = string(simpleWithTagEntityProtob) + + // bDotBEntityProto corresponds to: + // BDotB{ + // B: testString2, + // } + bDotBEntityProtob, err := proto.Marshal(&pb.EntityProto{ + Key: keyToProto("", incompleteKey), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameBDotB, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + Multiple: &FALSE, + }, + }, + EntityGroup: &pb.Path{}, + }) + if err != nil { + panic(err) + } + bDotBEntityProto = string(bDotBEntityProtob) + + // withKeyEntityProto corresponds to: + // WithKey{ + // X: testString3, + // I: testInt64, + // K: testKey1a, + // } + withKeyEntityProtob, err := proto.Marshal(&pb.EntityProto{ + Key: keyToProto("", testKey1a), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameX, + Value: &pb.PropertyValue{ + StringValue: &testString3, + }, + Multiple: &FALSE, + }, + &pb.Property{ + Name: &fieldNameI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + Multiple: &FALSE, + }, + }, + EntityGroup: &pb.Path{}, + }) + if err != nil { + panic(err) + } + withKeyEntityProto = string(withKeyEntityProtob) + +} + +func TestLoadEntityNested(t *testing.T) { + testCases := []struct { + desc string + src *pb.EntityProto + want interface{} + }{ + { + "nested basic", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameA, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + StringValue: &simpleEntityProto, + }, + }, + &pb.Property{ + Name: &fieldNameI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + }, + }, + }, + &NestedSimple{ + A: Simple{I: 2}, + I: 2, + }, + }, + { + "nested with struct tags", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameAA, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + StringValue: &simpleWithTagEntityProto, + }, + }, + }, + }, + &NestedSimpleWithTag{ + A: SimpleWithTag{I: testInt64}, + }, + }, + { + "nested 2x", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameAA, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + StringValue: &nestedSimpleEntityProto, + }, + }, + &pb.Property{ + Name: &fieldNameA, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + StringValue: &simpleTwoFieldsEntityProto, + }, + }, + &pb.Property{ + Name: &fieldNameS, + Value: &pb.PropertyValue{ + StringValue: &testString3, + }, + }, + }, + }, + &NestedSimple2X{ + AA: NestedSimple{ + A: Simple{I: testInt64}, + I: testInt64, + }, + A: SimpleTwoFields{S: testString2, SS: testString3}, + S: testString3, + }, + }, + { + "nested anonymous", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + }, + &pb.Property{ + Name: &fieldNameX, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + }, + }, + &NestedSimpleAnonymous{ + Simple: Simple{I: testInt64}, + X: testString2, + }, + }, + { + "nested simple with slice", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameA, + Meaning: &entityProtoMeaning, + Multiple: &TRUE, + Value: &pb.PropertyValue{ + StringValue: &simpleEntityProto, + }, + }, + &pb.Property{ + Name: &fieldNameA, + Meaning: &entityProtoMeaning, + Multiple: &TRUE, + Value: &pb.PropertyValue{ + StringValue: &simpleEntityProto, + }, + }, + }, + }, + &NestedSliceOfSimple{ + A: []Simple{Simple{I: testInt64}, Simple{I: testInt64}}, + }, + }, + { + "nested with multiple anonymous fields", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameI, + Value: &pb.PropertyValue{ + Int64Value: &testInt64, + }, + }, + &pb.Property{ + Name: &fieldNameS, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + &pb.Property{ + Name: &fieldNameSS, + Value: &pb.PropertyValue{ + StringValue: &testString3, + }, + }, + &pb.Property{ + Name: &fieldNameX, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + }, + }, + &MultiAnonymous{ + Simple: Simple{I: testInt64}, + SimpleTwoFields: SimpleTwoFields{S: testString2, SS: testString3}, + X: testString2, + }, + }, + { + "nested with dotted field tag", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameA, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + StringValue: &bDotBEntityProto, + }, + }, + }, + }, + &ABDotB{ + A: BDotB{ + B: testString2, + }, + }, + }, + { + "nested entity with key", + &pb.EntityProto{ + Key: keyToProto("some-app-id", testKey0), + Property: []*pb.Property{ + &pb.Property{ + Name: &fieldNameY, + Value: &pb.PropertyValue{ + StringValue: &testString2, + }, + }, + &pb.Property{ + Name: &fieldNameN, + Meaning: &entityProtoMeaning, + Value: &pb.PropertyValue{ + StringValue: &withKeyEntityProto, + }, + }, + }, + }, + &NestedWithKey{ + Y: testString2, + N: WithKey{ + X: testString3, + I: testInt64, + K: testKey1a, + }, + }, + }, + } + + for _, tc := range testCases { + dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() + err := loadEntity(dst, tc.src) + if err != nil { + t.Errorf("loadEntity: %s: %v", tc.desc, err) + continue + } + + if !reflect.DeepEqual(tc.want, dst) { + t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, dst, tc.want) + } + } +} diff --git a/vendor/google.golang.org/appengine/datastore/metadata.go b/vendor/google.golang.org/appengine/datastore/metadata.go new file mode 100644 index 0000000000..6acacc3db9 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/metadata.go @@ -0,0 +1,78 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import "golang.org/x/net/context" + +// Datastore kinds for the metadata entities. +const ( + namespaceKind = "__namespace__" + kindKind = "__kind__" + propertyKind = "__property__" +) + +// Namespaces returns all the datastore namespaces. +func Namespaces(ctx context.Context) ([]string, error) { + // TODO(djd): Support range queries. + q := NewQuery(namespaceKind).KeysOnly() + keys, err := q.GetAll(ctx, nil) + if err != nil { + return nil, err + } + // The empty namespace key uses a numeric ID (==1), but luckily + // the string ID defaults to "" for numeric IDs anyway. + return keyNames(keys), nil +} + +// Kinds returns the names of all the kinds in the current namespace. +func Kinds(ctx context.Context) ([]string, error) { + // TODO(djd): Support range queries. + q := NewQuery(kindKind).KeysOnly() + keys, err := q.GetAll(ctx, nil) + if err != nil { + return nil, err + } + return keyNames(keys), nil +} + +// keyNames returns a slice of the provided keys' names (string IDs). +func keyNames(keys []*Key) []string { + n := make([]string, 0, len(keys)) + for _, k := range keys { + n = append(n, k.StringID()) + } + return n +} + +// KindProperties returns all the indexed properties for the given kind. +// The properties are returned as a map of property names to a slice of the +// representation types. The representation types for the supported Go property +// types are: +// "INT64": signed integers and time.Time +// "DOUBLE": float32 and float64 +// "BOOLEAN": bool +// "STRING": string, []byte and ByteString +// "POINT": appengine.GeoPoint +// "REFERENCE": *Key +// "USER": (not used in the Go runtime) +func KindProperties(ctx context.Context, kind string) (map[string][]string, error) { + // TODO(djd): Support range queries. + kindKey := NewKey(ctx, kindKind, kind, 0, nil) + q := NewQuery(propertyKind).Ancestor(kindKey) + + propMap := map[string][]string{} + props := []struct { + Repr []string `datastore:"property_representation"` + }{} + + keys, err := q.GetAll(ctx, &props) + if err != nil { + return nil, err + } + for i, p := range props { + propMap[keys[i].StringID()] = p.Repr + } + return propMap, nil +} diff --git a/vendor/google.golang.org/appengine/datastore/prop.go b/vendor/google.golang.org/appengine/datastore/prop.go new file mode 100644 index 0000000000..5cb2079d88 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/prop.go @@ -0,0 +1,330 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "fmt" + "reflect" + "strings" + "sync" + "unicode" +) + +// Entities with more than this many indexed properties will not be saved. +const maxIndexedProperties = 20000 + +// []byte fields more than 1 megabyte long will not be loaded or saved. +const maxBlobLen = 1 << 20 + +// Property is a name/value pair plus some metadata. A datastore entity's +// contents are loaded and saved as a sequence of Properties. An entity can +// have multiple Properties with the same name, provided that p.Multiple is +// true on all of that entity's Properties with that name. +type Property struct { + // Name is the property name. + Name string + // Value is the property value. The valid types are: + // - int64 + // - bool + // - string + // - float64 + // - ByteString + // - *Key + // - time.Time + // - appengine.BlobKey + // - appengine.GeoPoint + // - []byte (up to 1 megabyte in length) + // - *Entity (representing a nested struct) + // This set is smaller than the set of valid struct field types that the + // datastore can load and save. A Property Value cannot be a slice (apart + // from []byte); use multiple Properties instead. Also, a Value's type + // must be explicitly on the list above; it is not sufficient for the + // underlying type to be on that list. For example, a Value of "type + // myInt64 int64" is invalid. Smaller-width integers and floats are also + // invalid. Again, this is more restrictive than the set of valid struct + // field types. + // + // A Value will have an opaque type when loading entities from an index, + // such as via a projection query. Load entities into a struct instead + // of a PropertyLoadSaver when using a projection query. + // + // A Value may also be the nil interface value; this is equivalent to + // Python's None but not directly representable by a Go struct. Loading + // a nil-valued property into a struct will set that field to the zero + // value. + Value interface{} + // NoIndex is whether the datastore cannot index this property. + NoIndex bool + // Multiple is whether the entity can have multiple properties with + // the same name. Even if a particular instance only has one property with + // a certain name, Multiple should be true if a struct would best represent + // it as a field of type []T instead of type T. + Multiple bool +} + +// An Entity is the value type for a nested struct. +// This type is only used for a Property's Value. +type Entity struct { + Key *Key + Properties []Property +} + +// ByteString is a short byte slice (up to 1500 bytes) that can be indexed. +type ByteString []byte + +// PropertyLoadSaver can be converted from and to a slice of Properties. +type PropertyLoadSaver interface { + Load([]Property) error + Save() ([]Property, error) +} + +// PropertyList converts a []Property to implement PropertyLoadSaver. +type PropertyList []Property + +var ( + typeOfPropertyLoadSaver = reflect.TypeOf((*PropertyLoadSaver)(nil)).Elem() + typeOfPropertyList = reflect.TypeOf(PropertyList(nil)) +) + +// Load loads all of the provided properties into l. +// It does not first reset *l to an empty slice. +func (l *PropertyList) Load(p []Property) error { + *l = append(*l, p...) + return nil +} + +// Save saves all of l's properties as a slice or Properties. +func (l *PropertyList) Save() ([]Property, error) { + return *l, nil +} + +// validPropertyName returns whether name consists of one or more valid Go +// identifiers joined by ".". +func validPropertyName(name string) bool { + if name == "" { + return false + } + for _, s := range strings.Split(name, ".") { + if s == "" { + return false + } + first := true + for _, c := range s { + if first { + first = false + if c != '_' && !unicode.IsLetter(c) { + return false + } + } else { + if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return false + } + } + } + } + return true +} + +// structCodec describes how to convert a struct to and from a sequence of +// properties. +type structCodec struct { + // fields gives the field codec for the structTag with the given name. + fields map[string]fieldCodec + // hasSlice is whether a struct or any of its nested or embedded structs + // has a slice-typed field (other than []byte). + hasSlice bool + // keyField is the index of a *Key field with structTag __key__. + // This field is not relevant for the top level struct, only for + // nested structs. + keyField int + // complete is whether the structCodec is complete. An incomplete + // structCodec may be encountered when walking a recursive struct. + complete bool +} + +// fieldCodec is a struct field's index and, if that struct field's type is +// itself a struct, that substruct's structCodec. +type fieldCodec struct { + // path is the index path to the field + path []int + noIndex bool + // omitEmpty indicates that the field should be omitted on save + // if empty. + omitEmpty bool + // structCodec is the codec fot the struct field at index 'path', + // or nil if the field is not a struct. + structCodec *structCodec +} + +// structCodecs collects the structCodecs that have already been calculated. +var ( + structCodecsMutex sync.Mutex + structCodecs = make(map[reflect.Type]*structCodec) +) + +// getStructCodec returns the structCodec for the given struct type. +func getStructCodec(t reflect.Type) (*structCodec, error) { + structCodecsMutex.Lock() + defer structCodecsMutex.Unlock() + return getStructCodecLocked(t) +} + +// getStructCodecLocked implements getStructCodec. The structCodecsMutex must +// be held when calling this function. +func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) { + c, ok := structCodecs[t] + if ok { + return c, nil + } + c = &structCodec{ + fields: make(map[string]fieldCodec), + // We initialize keyField to -1 so that the zero-value is not + // misinterpreted as index 0. + keyField: -1, + } + + // Add c to the structCodecs map before we are sure it is good. If t is + // a recursive type, it needs to find the incomplete entry for itself in + // the map. + structCodecs[t] = c + defer func() { + if retErr != nil { + delete(structCodecs, t) + } + }() + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + // Skip unexported fields. + // Note that if f is an anonymous, unexported struct field, + // we will promote its fields. + if f.PkgPath != "" && !f.Anonymous { + continue + } + + tags := strings.Split(f.Tag.Get("datastore"), ",") + name := tags[0] + opts := make(map[string]bool) + for _, t := range tags[1:] { + opts[t] = true + } + switch { + case name == "": + if !f.Anonymous { + name = f.Name + } + case name == "-": + continue + case name == "__key__": + if f.Type != typeOfKeyPtr { + return nil, fmt.Errorf("datastore: __key__ field on struct %v is not a *datastore.Key", t) + } + c.keyField = i + case !validPropertyName(name): + return nil, fmt.Errorf("datastore: struct tag has invalid property name: %q", name) + } + + substructType, fIsSlice := reflect.Type(nil), false + switch f.Type.Kind() { + case reflect.Struct: + substructType = f.Type + case reflect.Slice: + if f.Type.Elem().Kind() == reflect.Struct { + substructType = f.Type.Elem() + } + fIsSlice = f.Type != typeOfByteSlice + c.hasSlice = c.hasSlice || fIsSlice + } + + var sub *structCodec + if substructType != nil && substructType != typeOfTime && substructType != typeOfGeoPoint { + var err error + sub, err = getStructCodecLocked(substructType) + if err != nil { + return nil, err + } + if !sub.complete { + return nil, fmt.Errorf("datastore: recursive struct: field %q", f.Name) + } + if fIsSlice && sub.hasSlice { + return nil, fmt.Errorf( + "datastore: flattening nested structs leads to a slice of slices: field %q", f.Name) + } + c.hasSlice = c.hasSlice || sub.hasSlice + // If f is an anonymous struct field, we promote the substruct's fields up to this level + // in the linked list of struct codecs. + if f.Anonymous { + for subname, subfield := range sub.fields { + if name != "" { + subname = name + "." + subname + } + if _, ok := c.fields[subname]; ok { + return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", subname) + } + c.fields[subname] = fieldCodec{ + path: append([]int{i}, subfield.path...), + noIndex: subfield.noIndex || opts["noindex"], + omitEmpty: subfield.omitEmpty, + structCodec: subfield.structCodec, + } + } + continue + } + } + + if _, ok := c.fields[name]; ok { + return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", name) + } + c.fields[name] = fieldCodec{ + path: []int{i}, + noIndex: opts["noindex"], + omitEmpty: opts["omitempty"], + structCodec: sub, + } + } + c.complete = true + return c, nil +} + +// structPLS adapts a struct to be a PropertyLoadSaver. +type structPLS struct { + v reflect.Value + codec *structCodec +} + +// newStructPLS returns a structPLS, which implements the +// PropertyLoadSaver interface, for the struct pointer p. +func newStructPLS(p interface{}) (*structPLS, error) { + v := reflect.ValueOf(p) + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return nil, ErrInvalidEntityType + } + v = v.Elem() + codec, err := getStructCodec(v.Type()) + if err != nil { + return nil, err + } + return &structPLS{v, codec}, nil +} + +// LoadStruct loads the properties from p to dst. +// dst must be a struct pointer. +func LoadStruct(dst interface{}, p []Property) error { + x, err := newStructPLS(dst) + if err != nil { + return err + } + return x.Load(p) +} + +// SaveStruct returns the properties from src as a slice of Properties. +// src must be a struct pointer. +func SaveStruct(src interface{}) ([]Property, error) { + x, err := newStructPLS(src) + if err != nil { + return nil, err + } + return x.Save() +} diff --git a/vendor/google.golang.org/appengine/datastore/prop_test.go b/vendor/google.golang.org/appengine/datastore/prop_test.go new file mode 100644 index 0000000000..646a18f00d --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/prop_test.go @@ -0,0 +1,672 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "reflect" + "sort" + "testing" + "time" + + "google.golang.org/appengine" +) + +func TestValidPropertyName(t *testing.T) { + testCases := []struct { + name string + want bool + }{ + // Invalid names. + {"", false}, + {"'", false}, + {".", false}, + {"..", false}, + {".foo", false}, + {"0", false}, + {"00", false}, + {"X.X.4.X.X", false}, + {"\n", false}, + {"\x00", false}, + {"abc\xffz", false}, + {"foo.", false}, + {"foo..", false}, + {"foo..bar", false}, + {"☃", false}, + {`"`, false}, + // Valid names. + {"AB", true}, + {"Abc", true}, + {"X.X.X.X.X", true}, + {"_", true}, + {"_0", true}, + {"a", true}, + {"a_B", true}, + {"f00", true}, + {"f0o", true}, + {"fo0", true}, + {"foo", true}, + {"foo.bar", true}, + {"foo.bar.baz", true}, + {"世界", true}, + } + for _, tc := range testCases { + got := validPropertyName(tc.name) + if got != tc.want { + t.Errorf("%q: got %v, want %v", tc.name, got, tc.want) + } + } +} + +func TestStructCodec(t *testing.T) { + type oStruct struct { + O int + } + type pStruct struct { + P int + Q int + } + type rStruct struct { + R int + S pStruct + T oStruct + oStruct + } + type uStruct struct { + U int + v int + } + type vStruct struct { + V string `datastore:",noindex"` + } + oStructCodec := &structCodec{ + fields: map[string]fieldCodec{ + "O": {path: []int{0}}, + }, + complete: true, + } + pStructCodec := &structCodec{ + fields: map[string]fieldCodec{ + "P": {path: []int{0}}, + "Q": {path: []int{1}}, + }, + complete: true, + } + rStructCodec := &structCodec{ + fields: map[string]fieldCodec{ + "R": {path: []int{0}}, + "S": {path: []int{1}, structCodec: pStructCodec}, + "T": {path: []int{2}, structCodec: oStructCodec}, + "O": {path: []int{3, 0}}, + }, + complete: true, + } + uStructCodec := &structCodec{ + fields: map[string]fieldCodec{ + "U": {path: []int{0}}, + }, + complete: true, + } + vStructCodec := &structCodec{ + fields: map[string]fieldCodec{ + "V": {path: []int{0}, noIndex: true}, + }, + complete: true, + } + + testCases := []struct { + desc string + structValue interface{} + want *structCodec + }{ + { + "oStruct", + oStruct{}, + oStructCodec, + }, + { + "pStruct", + pStruct{}, + pStructCodec, + }, + { + "rStruct", + rStruct{}, + rStructCodec, + }, + { + "uStruct", + uStruct{}, + uStructCodec, + }, + { + "non-basic fields", + struct { + B appengine.BlobKey + K *Key + T time.Time + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "B": {path: []int{0}}, + "K": {path: []int{1}}, + "T": {path: []int{2}}, + }, + complete: true, + }, + }, + { + "struct tags with ignored embed", + struct { + A int `datastore:"a,noindex"` + B int `datastore:"b"` + C int `datastore:",noindex"` + D int `datastore:""` + E int + I int `datastore:"-"` + J int `datastore:",noindex" json:"j"` + oStruct `datastore:"-"` + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "a": {path: []int{0}, noIndex: true}, + "b": {path: []int{1}}, + "C": {path: []int{2}, noIndex: true}, + "D": {path: []int{3}}, + "E": {path: []int{4}}, + "J": {path: []int{6}, noIndex: true}, + }, + complete: true, + }, + }, + { + "unexported fields", + struct { + A int + b int + C int `datastore:"x"` + d int `datastore:"Y"` + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "A": {path: []int{0}}, + "x": {path: []int{2}}, + }, + complete: true, + }, + }, + { + "nested and embedded structs", + struct { + A int + B int + CC oStruct + DDD rStruct + oStruct + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "A": {path: []int{0}}, + "B": {path: []int{1}}, + "CC": {path: []int{2}, structCodec: oStructCodec}, + "DDD": {path: []int{3}, structCodec: rStructCodec}, + "O": {path: []int{4, 0}}, + }, + complete: true, + }, + }, + { + "struct tags with nested and embedded structs", + struct { + A int `datastore:"-"` + B int `datastore:"w"` + C oStruct `datastore:"xx"` + D rStruct `datastore:"y"` + oStruct `datastore:"z"` + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "w": {path: []int{1}}, + "xx": {path: []int{2}, structCodec: oStructCodec}, + "y": {path: []int{3}, structCodec: rStructCodec}, + "z.O": {path: []int{4, 0}}, + }, + complete: true, + }, + }, + { + "unexported nested and embedded structs", + struct { + a int + B int + c uStruct + D uStruct + uStruct + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "B": {path: []int{1}}, + "D": {path: []int{3}, structCodec: uStructCodec}, + "U": {path: []int{4, 0}}, + }, + complete: true, + }, + }, + { + "noindex nested struct", + struct { + A oStruct `datastore:",noindex"` + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "A": {path: []int{0}, structCodec: oStructCodec, noIndex: true}, + }, + complete: true, + }, + }, + { + "noindex slice", + struct { + A []string `datastore:",noindex"` + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "A": {path: []int{0}, noIndex: true}, + }, + hasSlice: true, + complete: true, + }, + }, + { + "noindex embedded struct slice", + struct { + // vStruct has a single field, V, also with noindex. + A []vStruct `datastore:",noindex"` + }{}, + &structCodec{ + fields: map[string]fieldCodec{ + "A": {path: []int{0}, structCodec: vStructCodec, noIndex: true}, + }, + hasSlice: true, + complete: true, + }, + }, + } + + for _, tc := range testCases { + got, err := getStructCodec(reflect.TypeOf(tc.structValue)) + if err != nil { + t.Errorf("%s: getStructCodec: %v", tc.desc, err) + continue + } + // can't reflect.DeepEqual b/c element order in fields map may differ + if !isEqualStructCodec(got, tc.want) { + t.Errorf("%s\ngot %+v\nwant %+v\n", tc.desc, got, tc.want) + } + } +} + +func isEqualStructCodec(got, want *structCodec) bool { + if got.complete != want.complete { + return false + } + if got.hasSlice != want.hasSlice { + return false + } + if len(got.fields) != len(want.fields) { + return false + } + for name, wantF := range want.fields { + gotF := got.fields[name] + if !reflect.DeepEqual(wantF.path, gotF.path) { + return false + } + if wantF.noIndex != gotF.noIndex { + return false + } + if wantF.structCodec != nil { + if gotF.structCodec == nil { + return false + } + if !isEqualStructCodec(gotF.structCodec, wantF.structCodec) { + return false + } + } + } + + return true +} + +func TestRepeatedPropertyName(t *testing.T) { + good := []interface{}{ + struct { + A int `datastore:"-"` + }{}, + struct { + A int `datastore:"b"` + B int + }{}, + struct { + A int + B int `datastore:"B"` + }{}, + struct { + A int `datastore:"B"` + B int `datastore:"-"` + }{}, + struct { + A int `datastore:"-"` + B int `datastore:"A"` + }{}, + struct { + A int `datastore:"B"` + B int `datastore:"A"` + }{}, + struct { + A int `datastore:"B"` + B int `datastore:"C"` + C int `datastore:"A"` + }{}, + struct { + A int `datastore:"B"` + B int `datastore:"C"` + C int `datastore:"D"` + }{}, + } + bad := []interface{}{ + struct { + A int `datastore:"B"` + B int + }{}, + struct { + A int + B int `datastore:"A"` + }{}, + struct { + A int `datastore:"C"` + B int `datastore:"C"` + }{}, + struct { + A int `datastore:"B"` + B int `datastore:"C"` + C int `datastore:"B"` + }{}, + } + testGetStructCodec(t, good, bad) +} + +func TestFlatteningNestedStructs(t *testing.T) { + type DeepGood struct { + A struct { + B []struct { + C struct { + D int + } + } + } + } + type DeepBad struct { + A struct { + B []struct { + C struct { + D []int + } + } + } + } + type ISay struct { + Tomato int + } + type YouSay struct { + Tomato int + } + type Tweedledee struct { + Dee int `datastore:"D"` + } + type Tweedledum struct { + Dum int `datastore:"D"` + } + + good := []interface{}{ + struct { + X []struct { + Y string + } + }{}, + struct { + X []struct { + Y []byte + } + }{}, + struct { + P []int + X struct { + Y []int + } + }{}, + struct { + X struct { + Y []int + } + Q []int + }{}, + struct { + P []int + X struct { + Y []int + } + Q []int + }{}, + struct { + DeepGood + }{}, + struct { + DG DeepGood + }{}, + struct { + Foo struct { + Z int + } `datastore:"A"` + Bar struct { + Z int + } `datastore:"B"` + }{}, + } + bad := []interface{}{ + struct { + X []struct { + Y []string + } + }{}, + struct { + X []struct { + Y []int + } + }{}, + struct { + DeepBad + }{}, + struct { + DB DeepBad + }{}, + struct { + ISay + YouSay + }{}, + struct { + Tweedledee + Tweedledum + }{}, + struct { + Foo struct { + Z int + } `datastore:"A"` + Bar struct { + Z int + } `datastore:"A"` + }{}, + } + testGetStructCodec(t, good, bad) +} + +func testGetStructCodec(t *testing.T, good []interface{}, bad []interface{}) { + for _, x := range good { + if _, err := getStructCodec(reflect.TypeOf(x)); err != nil { + t.Errorf("type %T: got non-nil error (%s), want nil", x, err) + } + } + for _, x := range bad { + if _, err := getStructCodec(reflect.TypeOf(x)); err == nil { + t.Errorf("type %T: got nil error, want non-nil", x) + } + } +} + +func TestNilKeyIsStored(t *testing.T) { + x := struct { + K *Key + I int + }{} + p := PropertyList{} + // Save x as properties. + p1, _ := SaveStruct(&x) + p.Load(p1) + // Set x's fields to non-zero. + x.K = &Key{} + x.I = 2 + // Load x from properties. + p2, _ := p.Save() + LoadStruct(&x, p2) + // Check that x's fields were set to zero. + if x.K != nil { + t.Errorf("K field was not zero") + } + if x.I != 0 { + t.Errorf("I field was not zero") + } +} + +func TestSaveStructOmitEmpty(t *testing.T) { + // Expected props names are sorted alphabetically + expectedPropNamesForSingles := []string{"EmptyValue", "NonEmptyValue", "OmitEmptyWithValue"} + expectedPropNamesForSlices := []string{"NonEmptyValue", "NonEmptyValue", "OmitEmptyWithValue", "OmitEmptyWithValue"} + + testOmitted := func(expectedPropNames []string, src interface{}) { + // t.Helper() - this is available from Go version 1.9, but we also support Go versions 1.6, 1.7, 1.8 + if props, err := SaveStruct(src); err != nil { + t.Fatal(err) + } else { + // Collect names for reporting if diffs from expected and for easier sorting + actualPropNames := make([]string, len(props)) + for i := range props { + actualPropNames[i] = props[i].Name + } + // Sort actuals for comparing with already sorted expected names + sort.Sort(sort.StringSlice(actualPropNames)) + if !reflect.DeepEqual(actualPropNames, expectedPropNames) { + t.Errorf("Expected this properties: %v, got: %v", expectedPropNames, actualPropNames) + } + } + } + + testOmitted(expectedPropNamesForSingles, &struct { + EmptyValue int + NonEmptyValue int + OmitEmptyNoValue int `datastore:",omitempty"` + OmitEmptyWithValue int `datastore:",omitempty"` + }{ + NonEmptyValue: 1, + OmitEmptyWithValue: 2, + }) + + testOmitted(expectedPropNamesForSlices, &struct { + EmptyValue []int + NonEmptyValue []int + OmitEmptyNoValue []int `datastore:",omitempty"` + OmitEmptyWithValue []int `datastore:",omitempty"` + }{ + NonEmptyValue: []int{1, 2}, + OmitEmptyWithValue: []int{3, 4}, + }) + + testOmitted(expectedPropNamesForSingles, &struct { + EmptyValue bool + NonEmptyValue bool + OmitEmptyNoValue bool `datastore:",omitempty"` + OmitEmptyWithValue bool `datastore:",omitempty"` + }{ + NonEmptyValue: true, + OmitEmptyWithValue: true, + }) + + testOmitted(expectedPropNamesForSlices, &struct { + EmptyValue []bool + NonEmptyValue []bool + OmitEmptyNoValue []bool `datastore:",omitempty"` + OmitEmptyWithValue []bool `datastore:",omitempty"` + }{ + NonEmptyValue: []bool{true, true}, + OmitEmptyWithValue: []bool{true, true}, + }) + + testOmitted(expectedPropNamesForSingles, &struct { + EmptyValue string + NonEmptyValue string + OmitEmptyNoValue string `datastore:",omitempty"` + OmitEmptyWithValue string `datastore:",omitempty"` + }{ + NonEmptyValue: "s", + OmitEmptyWithValue: "s", + }) + + testOmitted(expectedPropNamesForSlices, &struct { + EmptyValue []string + NonEmptyValue []string + OmitEmptyNoValue []string `datastore:",omitempty"` + OmitEmptyWithValue []string `datastore:",omitempty"` + }{ + NonEmptyValue: []string{"s1", "s2"}, + OmitEmptyWithValue: []string{"s3", "s4"}, + }) + + testOmitted(expectedPropNamesForSingles, &struct { + EmptyValue float32 + NonEmptyValue float32 + OmitEmptyNoValue float32 `datastore:",omitempty"` + OmitEmptyWithValue float32 `datastore:",omitempty"` + }{ + NonEmptyValue: 1.1, + OmitEmptyWithValue: 1.2, + }) + + testOmitted(expectedPropNamesForSlices, &struct { + EmptyValue []float32 + NonEmptyValue []float32 + OmitEmptyNoValue []float32 `datastore:",omitempty"` + OmitEmptyWithValue []float32 `datastore:",omitempty"` + }{ + NonEmptyValue: []float32{1.1, 2.2}, + OmitEmptyWithValue: []float32{3.3, 4.4}, + }) + + testOmitted(expectedPropNamesForSingles, &struct { + EmptyValue time.Time + NonEmptyValue time.Time + OmitEmptyNoValue time.Time `datastore:",omitempty"` + OmitEmptyWithValue time.Time `datastore:",omitempty"` + }{ + NonEmptyValue: now, + OmitEmptyWithValue: now, + }) + + testOmitted(expectedPropNamesForSlices, &struct { + EmptyValue []time.Time + NonEmptyValue []time.Time + OmitEmptyNoValue []time.Time `datastore:",omitempty"` + OmitEmptyWithValue []time.Time `datastore:",omitempty"` + }{ + NonEmptyValue: []time.Time{now, now}, + OmitEmptyWithValue: []time.Time{now, now}, + }) +} diff --git a/vendor/google.golang.org/appengine/datastore/query.go b/vendor/google.golang.org/appengine/datastore/query.go new file mode 100644 index 0000000000..c1ea4adf69 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/query.go @@ -0,0 +1,757 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "encoding/base64" + "errors" + "fmt" + "math" + "reflect" + "strings" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/datastore" +) + +type operator int + +const ( + lessThan operator = iota + lessEq + equal + greaterEq + greaterThan +) + +var operatorToProto = map[operator]*pb.Query_Filter_Operator{ + lessThan: pb.Query_Filter_LESS_THAN.Enum(), + lessEq: pb.Query_Filter_LESS_THAN_OR_EQUAL.Enum(), + equal: pb.Query_Filter_EQUAL.Enum(), + greaterEq: pb.Query_Filter_GREATER_THAN_OR_EQUAL.Enum(), + greaterThan: pb.Query_Filter_GREATER_THAN.Enum(), +} + +// filter is a conditional filter on query results. +type filter struct { + FieldName string + Op operator + Value interface{} +} + +type sortDirection int + +const ( + ascending sortDirection = iota + descending +) + +var sortDirectionToProto = map[sortDirection]*pb.Query_Order_Direction{ + ascending: pb.Query_Order_ASCENDING.Enum(), + descending: pb.Query_Order_DESCENDING.Enum(), +} + +// order is a sort order on query results. +type order struct { + FieldName string + Direction sortDirection +} + +// NewQuery creates a new Query for a specific entity kind. +// +// An empty kind means to return all entities, including entities created and +// managed by other App Engine features, and is called a kindless query. +// Kindless queries cannot include filters or sort orders on property values. +func NewQuery(kind string) *Query { + return &Query{ + kind: kind, + limit: -1, + } +} + +// Query represents a datastore query. +type Query struct { + kind string + ancestor *Key + filter []filter + order []order + projection []string + + distinct bool + keysOnly bool + eventual bool + limit int32 + offset int32 + count int32 + start *pb.CompiledCursor + end *pb.CompiledCursor + + err error +} + +func (q *Query) clone() *Query { + x := *q + // Copy the contents of the slice-typed fields to a new backing store. + if len(q.filter) > 0 { + x.filter = make([]filter, len(q.filter)) + copy(x.filter, q.filter) + } + if len(q.order) > 0 { + x.order = make([]order, len(q.order)) + copy(x.order, q.order) + } + return &x +} + +// Ancestor returns a derivative query with an ancestor filter. +// The ancestor should not be nil. +func (q *Query) Ancestor(ancestor *Key) *Query { + q = q.clone() + if ancestor == nil { + q.err = errors.New("datastore: nil query ancestor") + return q + } + q.ancestor = ancestor + return q +} + +// EventualConsistency returns a derivative query that returns eventually +// consistent results. +// It only has an effect on ancestor queries. +func (q *Query) EventualConsistency() *Query { + q = q.clone() + q.eventual = true + return q +} + +// Filter returns a derivative query with a field-based filter. +// The filterStr argument must be a field name followed by optional space, +// followed by an operator, one of ">", "<", ">=", "<=", or "=". +// Fields are compared against the provided value using the operator. +// Multiple filters are AND'ed together. +func (q *Query) Filter(filterStr string, value interface{}) *Query { + q = q.clone() + filterStr = strings.TrimSpace(filterStr) + if len(filterStr) < 1 { + q.err = errors.New("datastore: invalid filter: " + filterStr) + return q + } + f := filter{ + FieldName: strings.TrimRight(filterStr, " ><=!"), + Value: value, + } + switch op := strings.TrimSpace(filterStr[len(f.FieldName):]); op { + case "<=": + f.Op = lessEq + case ">=": + f.Op = greaterEq + case "<": + f.Op = lessThan + case ">": + f.Op = greaterThan + case "=": + f.Op = equal + default: + q.err = fmt.Errorf("datastore: invalid operator %q in filter %q", op, filterStr) + return q + } + q.filter = append(q.filter, f) + return q +} + +// Order returns a derivative query with a field-based sort order. Orders are +// applied in the order they are added. The default order is ascending; to sort +// in descending order prefix the fieldName with a minus sign (-). +func (q *Query) Order(fieldName string) *Query { + q = q.clone() + fieldName = strings.TrimSpace(fieldName) + o := order{ + Direction: ascending, + FieldName: fieldName, + } + if strings.HasPrefix(fieldName, "-") { + o.Direction = descending + o.FieldName = strings.TrimSpace(fieldName[1:]) + } else if strings.HasPrefix(fieldName, "+") { + q.err = fmt.Errorf("datastore: invalid order: %q", fieldName) + return q + } + if len(o.FieldName) == 0 { + q.err = errors.New("datastore: empty order") + return q + } + q.order = append(q.order, o) + return q +} + +// Project returns a derivative query that yields only the given fields. It +// cannot be used with KeysOnly. +func (q *Query) Project(fieldNames ...string) *Query { + q = q.clone() + q.projection = append([]string(nil), fieldNames...) + return q +} + +// Distinct returns a derivative query that yields de-duplicated entities with +// respect to the set of projected fields. It is only used for projection +// queries. +func (q *Query) Distinct() *Query { + q = q.clone() + q.distinct = true + return q +} + +// KeysOnly returns a derivative query that yields only keys, not keys and +// entities. It cannot be used with projection queries. +func (q *Query) KeysOnly() *Query { + q = q.clone() + q.keysOnly = true + return q +} + +// Limit returns a derivative query that has a limit on the number of results +// returned. A negative value means unlimited. +func (q *Query) Limit(limit int) *Query { + q = q.clone() + if limit < math.MinInt32 || limit > math.MaxInt32 { + q.err = errors.New("datastore: query limit overflow") + return q + } + q.limit = int32(limit) + return q +} + +// Offset returns a derivative query that has an offset of how many keys to +// skip over before returning results. A negative value is invalid. +func (q *Query) Offset(offset int) *Query { + q = q.clone() + if offset < 0 { + q.err = errors.New("datastore: negative query offset") + return q + } + if offset > math.MaxInt32 { + q.err = errors.New("datastore: query offset overflow") + return q + } + q.offset = int32(offset) + return q +} + +// BatchSize returns a derivative query to fetch the supplied number of results +// at once. This value should be greater than zero, and equal to or less than +// the Limit. +func (q *Query) BatchSize(size int) *Query { + q = q.clone() + if size <= 0 || size > math.MaxInt32 { + q.err = errors.New("datastore: query batch size overflow") + return q + } + q.count = int32(size) + return q +} + +// Start returns a derivative query with the given start point. +func (q *Query) Start(c Cursor) *Query { + q = q.clone() + if c.cc == nil { + q.err = errors.New("datastore: invalid cursor") + return q + } + q.start = c.cc + return q +} + +// End returns a derivative query with the given end point. +func (q *Query) End(c Cursor) *Query { + q = q.clone() + if c.cc == nil { + q.err = errors.New("datastore: invalid cursor") + return q + } + q.end = c.cc + return q +} + +// toProto converts the query to a protocol buffer. +func (q *Query) toProto(dst *pb.Query, appID string) error { + if len(q.projection) != 0 && q.keysOnly { + return errors.New("datastore: query cannot both project and be keys-only") + } + dst.Reset() + dst.App = proto.String(appID) + if q.kind != "" { + dst.Kind = proto.String(q.kind) + } + if q.ancestor != nil { + dst.Ancestor = keyToProto(appID, q.ancestor) + if q.eventual { + dst.Strong = proto.Bool(false) + } + } + if q.projection != nil { + dst.PropertyName = q.projection + if q.distinct { + dst.GroupByPropertyName = q.projection + } + } + if q.keysOnly { + dst.KeysOnly = proto.Bool(true) + dst.RequirePerfectPlan = proto.Bool(true) + } + for _, qf := range q.filter { + if qf.FieldName == "" { + return errors.New("datastore: empty query filter field name") + } + p, errStr := valueToProto(appID, qf.FieldName, reflect.ValueOf(qf.Value), false) + if errStr != "" { + return errors.New("datastore: bad query filter value type: " + errStr) + } + xf := &pb.Query_Filter{ + Op: operatorToProto[qf.Op], + Property: []*pb.Property{p}, + } + if xf.Op == nil { + return errors.New("datastore: unknown query filter operator") + } + dst.Filter = append(dst.Filter, xf) + } + for _, qo := range q.order { + if qo.FieldName == "" { + return errors.New("datastore: empty query order field name") + } + xo := &pb.Query_Order{ + Property: proto.String(qo.FieldName), + Direction: sortDirectionToProto[qo.Direction], + } + if xo.Direction == nil { + return errors.New("datastore: unknown query order direction") + } + dst.Order = append(dst.Order, xo) + } + if q.limit >= 0 { + dst.Limit = proto.Int32(q.limit) + } + if q.offset != 0 { + dst.Offset = proto.Int32(q.offset) + } + if q.count != 0 { + dst.Count = proto.Int32(q.count) + } + dst.CompiledCursor = q.start + dst.EndCompiledCursor = q.end + dst.Compile = proto.Bool(true) + return nil +} + +// Count returns the number of results for the query. +// +// The running time and number of API calls made by Count scale linearly with +// the sum of the query's offset and limit. Unless the result count is +// expected to be small, it is best to specify a limit; otherwise Count will +// continue until it finishes counting or the provided context expires. +func (q *Query) Count(c context.Context) (int, error) { + // Check that the query is well-formed. + if q.err != nil { + return 0, q.err + } + + // Run a copy of the query, with keysOnly true (if we're not a projection, + // since the two are incompatible), and an adjusted offset. We also set the + // limit to zero, as we don't want any actual entity data, just the number + // of skipped results. + newQ := q.clone() + newQ.keysOnly = len(newQ.projection) == 0 + newQ.limit = 0 + if q.limit < 0 { + // If the original query was unlimited, set the new query's offset to maximum. + newQ.offset = math.MaxInt32 + } else { + newQ.offset = q.offset + q.limit + if newQ.offset < 0 { + // Do the best we can, in the presence of overflow. + newQ.offset = math.MaxInt32 + } + } + req := &pb.Query{} + if err := newQ.toProto(req, internal.FullyQualifiedAppID(c)); err != nil { + return 0, err + } + res := &pb.QueryResult{} + if err := internal.Call(c, "datastore_v3", "RunQuery", req, res); err != nil { + return 0, err + } + + // n is the count we will return. For example, suppose that our original + // query had an offset of 4 and a limit of 2008: the count will be 2008, + // provided that there are at least 2012 matching entities. However, the + // RPCs will only skip 1000 results at a time. The RPC sequence is: + // call RunQuery with (offset, limit) = (2012, 0) // 2012 == newQ.offset + // response has (skippedResults, moreResults) = (1000, true) + // n += 1000 // n == 1000 + // call Next with (offset, limit) = (1012, 0) // 1012 == newQ.offset - n + // response has (skippedResults, moreResults) = (1000, true) + // n += 1000 // n == 2000 + // call Next with (offset, limit) = (12, 0) // 12 == newQ.offset - n + // response has (skippedResults, moreResults) = (12, false) + // n += 12 // n == 2012 + // // exit the loop + // n -= 4 // n == 2008 + var n int32 + for { + // The QueryResult should have no actual entity data, just skipped results. + if len(res.Result) != 0 { + return 0, errors.New("datastore: internal error: Count request returned too much data") + } + n += res.GetSkippedResults() + if !res.GetMoreResults() { + break + } + if err := callNext(c, res, newQ.offset-n, q.count); err != nil { + return 0, err + } + } + n -= q.offset + if n < 0 { + // If the offset was greater than the number of matching entities, + // return 0 instead of negative. + n = 0 + } + return int(n), nil +} + +// callNext issues a datastore_v3/Next RPC to advance a cursor, such as that +// returned by a query with more results. +func callNext(c context.Context, res *pb.QueryResult, offset, count int32) error { + if res.Cursor == nil { + return errors.New("datastore: internal error: server did not return a cursor") + } + req := &pb.NextRequest{ + Cursor: res.Cursor, + } + if count >= 0 { + req.Count = proto.Int32(count) + } + if offset != 0 { + req.Offset = proto.Int32(offset) + } + if res.CompiledCursor != nil { + req.Compile = proto.Bool(true) + } + res.Reset() + return internal.Call(c, "datastore_v3", "Next", req, res) +} + +// GetAll runs the query in the given context and returns all keys that match +// that query, as well as appending the values to dst. +// +// dst must have type *[]S or *[]*S or *[]P, for some struct type S or some non- +// interface, non-pointer type P such that P or *P implements PropertyLoadSaver. +// +// As a special case, *PropertyList is an invalid type for dst, even though a +// PropertyList is a slice of structs. It is treated as invalid to avoid being +// mistakenly passed when *[]PropertyList was intended. +// +// The keys returned by GetAll will be in a 1-1 correspondence with the entities +// added to dst. +// +// If q is a ``keys-only'' query, GetAll ignores dst and only returns the keys. +// +// The running time and number of API calls made by GetAll scale linearly with +// the sum of the query's offset and limit. Unless the result count is +// expected to be small, it is best to specify a limit; otherwise GetAll will +// continue until it finishes collecting results or the provided context +// expires. +func (q *Query) GetAll(c context.Context, dst interface{}) ([]*Key, error) { + var ( + dv reflect.Value + mat multiArgType + elemType reflect.Type + errFieldMismatch error + ) + if !q.keysOnly { + dv = reflect.ValueOf(dst) + if dv.Kind() != reflect.Ptr || dv.IsNil() { + return nil, ErrInvalidEntityType + } + dv = dv.Elem() + mat, elemType = checkMultiArg(dv) + if mat == multiArgTypeInvalid || mat == multiArgTypeInterface { + return nil, ErrInvalidEntityType + } + } + + var keys []*Key + for t := q.Run(c); ; { + k, e, err := t.next() + if err == Done { + break + } + if err != nil { + return keys, err + } + if !q.keysOnly { + ev := reflect.New(elemType) + if elemType.Kind() == reflect.Map { + // This is a special case. The zero values of a map type are + // not immediately useful; they have to be make'd. + // + // Funcs and channels are similar, in that a zero value is not useful, + // but even a freshly make'd channel isn't useful: there's no fixed + // channel buffer size that is always going to be large enough, and + // there's no goroutine to drain the other end. Theoretically, these + // types could be supported, for example by sniffing for a constructor + // method or requiring prior registration, but for now it's not a + // frequent enough concern to be worth it. Programmers can work around + // it by explicitly using Iterator.Next instead of the Query.GetAll + // convenience method. + x := reflect.MakeMap(elemType) + ev.Elem().Set(x) + } + if err = loadEntity(ev.Interface(), e); err != nil { + if _, ok := err.(*ErrFieldMismatch); ok { + // We continue loading entities even in the face of field mismatch errors. + // If we encounter any other error, that other error is returned. Otherwise, + // an ErrFieldMismatch is returned. + errFieldMismatch = err + } else { + return keys, err + } + } + if mat != multiArgTypeStructPtr { + ev = ev.Elem() + } + dv.Set(reflect.Append(dv, ev)) + } + keys = append(keys, k) + } + return keys, errFieldMismatch +} + +// Run runs the query in the given context. +func (q *Query) Run(c context.Context) *Iterator { + if q.err != nil { + return &Iterator{err: q.err} + } + t := &Iterator{ + c: c, + limit: q.limit, + count: q.count, + q: q, + prevCC: q.start, + } + var req pb.Query + if err := q.toProto(&req, internal.FullyQualifiedAppID(c)); err != nil { + t.err = err + return t + } + if err := internal.Call(c, "datastore_v3", "RunQuery", &req, &t.res); err != nil { + t.err = err + return t + } + offset := q.offset - t.res.GetSkippedResults() + var count int32 + if t.count > 0 && (t.limit < 0 || t.count < t.limit) { + count = t.count + } else { + count = t.limit + } + for offset > 0 && t.res.GetMoreResults() { + t.prevCC = t.res.CompiledCursor + if err := callNext(t.c, &t.res, offset, count); err != nil { + t.err = err + break + } + skip := t.res.GetSkippedResults() + if skip < 0 { + t.err = errors.New("datastore: internal error: negative number of skipped_results") + break + } + offset -= skip + } + if offset < 0 { + t.err = errors.New("datastore: internal error: query offset was overshot") + } + return t +} + +// Iterator is the result of running a query. +type Iterator struct { + c context.Context + err error + // res is the result of the most recent RunQuery or Next API call. + res pb.QueryResult + // i is how many elements of res.Result we have iterated over. + i int + // limit is the limit on the number of results this iterator should return. + // A negative value means unlimited. + limit int32 + // count is the number of results this iterator should fetch at once. This + // should be equal to or greater than zero. + count int32 + // q is the original query which yielded this iterator. + q *Query + // prevCC is the compiled cursor that marks the end of the previous batch + // of results. + prevCC *pb.CompiledCursor +} + +// Done is returned when a query iteration has completed. +var Done = errors.New("datastore: query has no more results") + +// Next returns the key of the next result. When there are no more results, +// Done is returned as the error. +// +// If the query is not keys only and dst is non-nil, it also loads the entity +// stored for that key into the struct pointer or PropertyLoadSaver dst, with +// the same semantics and possible errors as for the Get function. +func (t *Iterator) Next(dst interface{}) (*Key, error) { + k, e, err := t.next() + if err != nil { + return nil, err + } + if dst != nil && !t.q.keysOnly { + err = loadEntity(dst, e) + } + return k, err +} + +func (t *Iterator) next() (*Key, *pb.EntityProto, error) { + if t.err != nil { + return nil, nil, t.err + } + + // Issue datastore_v3/Next RPCs as necessary. + for t.i == len(t.res.Result) { + if !t.res.GetMoreResults() { + t.err = Done + return nil, nil, t.err + } + t.prevCC = t.res.CompiledCursor + var count int32 + if t.count > 0 && (t.limit < 0 || t.count < t.limit) { + count = t.count + } else { + count = t.limit + } + if err := callNext(t.c, &t.res, 0, count); err != nil { + t.err = err + return nil, nil, t.err + } + if t.res.GetSkippedResults() != 0 { + t.err = errors.New("datastore: internal error: iterator has skipped results") + return nil, nil, t.err + } + t.i = 0 + if t.limit >= 0 { + t.limit -= int32(len(t.res.Result)) + if t.limit < 0 { + t.err = errors.New("datastore: internal error: query returned more results than the limit") + return nil, nil, t.err + } + } + } + + // Extract the key from the t.i'th element of t.res.Result. + e := t.res.Result[t.i] + t.i++ + if e.Key == nil { + return nil, nil, errors.New("datastore: internal error: server did not return a key") + } + k, err := protoToKey(e.Key) + if err != nil || k.Incomplete() { + return nil, nil, errors.New("datastore: internal error: server returned an invalid key") + } + return k, e, nil +} + +// Cursor returns a cursor for the iterator's current location. +func (t *Iterator) Cursor() (Cursor, error) { + if t.err != nil && t.err != Done { + return Cursor{}, t.err + } + // If we are at either end of the current batch of results, + // return the compiled cursor at that end. + skipped := t.res.GetSkippedResults() + if t.i == 0 && skipped == 0 { + if t.prevCC == nil { + // A nil pointer (of type *pb.CompiledCursor) means no constraint: + // passing it as the end cursor of a new query means unlimited results + // (glossing over the integer limit parameter for now). + // A non-nil pointer to an empty pb.CompiledCursor means the start: + // passing it as the end cursor of a new query means 0 results. + // If prevCC was nil, then the original query had no start cursor, but + // Iterator.Cursor should return "the start" instead of unlimited. + return Cursor{&zeroCC}, nil + } + return Cursor{t.prevCC}, nil + } + if t.i == len(t.res.Result) { + return Cursor{t.res.CompiledCursor}, nil + } + // Otherwise, re-run the query offset to this iterator's position, starting from + // the most recent compiled cursor. This is done on a best-effort basis, as it + // is racy; if a concurrent process has added or removed entities, then the + // cursor returned may be inconsistent. + q := t.q.clone() + q.start = t.prevCC + q.offset = skipped + int32(t.i) + q.limit = 0 + q.keysOnly = len(q.projection) == 0 + t1 := q.Run(t.c) + _, _, err := t1.next() + if err != Done { + if err == nil { + err = fmt.Errorf("datastore: internal error: zero-limit query did not have zero results") + } + return Cursor{}, err + } + return Cursor{t1.res.CompiledCursor}, nil +} + +var zeroCC pb.CompiledCursor + +// Cursor is an iterator's position. It can be converted to and from an opaque +// string. A cursor can be used from different HTTP requests, but only with a +// query with the same kind, ancestor, filter and order constraints. +type Cursor struct { + cc *pb.CompiledCursor +} + +// String returns a base-64 string representation of a cursor. +func (c Cursor) String() string { + if c.cc == nil { + return "" + } + b, err := proto.Marshal(c.cc) + if err != nil { + // The only way to construct a Cursor with a non-nil cc field is to + // unmarshal from the byte representation. We panic if the unmarshal + // succeeds but the marshaling of the unchanged protobuf value fails. + panic(fmt.Sprintf("datastore: internal error: malformed cursor: %v", err)) + } + return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") +} + +// Decode decodes a cursor from its base-64 string representation. +func DecodeCursor(s string) (Cursor, error) { + if s == "" { + return Cursor{&zeroCC}, nil + } + if n := len(s) % 4; n != 0 { + s += strings.Repeat("=", 4-n) + } + b, err := base64.URLEncoding.DecodeString(s) + if err != nil { + return Cursor{}, err + } + cc := &pb.CompiledCursor{} + if err := proto.Unmarshal(b, cc); err != nil { + return Cursor{}, err + } + return Cursor{cc}, nil +} diff --git a/vendor/google.golang.org/appengine/datastore/query_test.go b/vendor/google.golang.org/appengine/datastore/query_test.go new file mode 100644 index 0000000000..45e5313ba2 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/query_test.go @@ -0,0 +1,584 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "errors" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine/internal" + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/datastore" +) + +var ( + path1 = &pb.Path{ + Element: []*pb.Path_Element{ + { + Type: proto.String("Gopher"), + Id: proto.Int64(6), + }, + }, + } + path2 = &pb.Path{ + Element: []*pb.Path_Element{ + { + Type: proto.String("Gopher"), + Id: proto.Int64(6), + }, + { + Type: proto.String("Gopher"), + Id: proto.Int64(8), + }, + }, + } +) + +func fakeRunQuery(in *pb.Query, out *pb.QueryResult) error { + expectedIn := &pb.Query{ + App: proto.String("dev~fake-app"), + Kind: proto.String("Gopher"), + Compile: proto.Bool(true), + } + if !proto.Equal(in, expectedIn) { + return fmt.Errorf("unsupported argument: got %v want %v", in, expectedIn) + } + *out = pb.QueryResult{ + Result: []*pb.EntityProto{ + { + Key: &pb.Reference{ + App: proto.String("s~test-app"), + Path: path1, + }, + EntityGroup: path1, + Property: []*pb.Property{ + { + Meaning: pb.Property_TEXT.Enum(), + Name: proto.String("Name"), + Value: &pb.PropertyValue{ + StringValue: proto.String("George"), + }, + }, + { + Name: proto.String("Height"), + Value: &pb.PropertyValue{ + Int64Value: proto.Int64(32), + }, + }, + }, + }, + { + Key: &pb.Reference{ + App: proto.String("s~test-app"), + Path: path2, + }, + EntityGroup: path1, // ancestor is George + Property: []*pb.Property{ + { + Meaning: pb.Property_TEXT.Enum(), + Name: proto.String("Name"), + Value: &pb.PropertyValue{ + StringValue: proto.String("Rufus"), + }, + }, + // No height for Rufus. + }, + }, + }, + MoreResults: proto.Bool(false), + } + return nil +} + +type StructThatImplementsPLS struct{} + +func (StructThatImplementsPLS) Load(p []Property) error { return nil } +func (StructThatImplementsPLS) Save() ([]Property, error) { return nil, nil } + +var _ PropertyLoadSaver = StructThatImplementsPLS{} + +type StructPtrThatImplementsPLS struct{} + +func (*StructPtrThatImplementsPLS) Load(p []Property) error { return nil } +func (*StructPtrThatImplementsPLS) Save() ([]Property, error) { return nil, nil } + +var _ PropertyLoadSaver = &StructPtrThatImplementsPLS{} + +type PropertyMap map[string]Property + +func (m PropertyMap) Load(props []Property) error { + for _, p := range props { + if p.Multiple { + return errors.New("PropertyMap does not support multiple properties") + } + m[p.Name] = p + } + return nil +} + +func (m PropertyMap) Save() ([]Property, error) { + props := make([]Property, 0, len(m)) + for _, p := range m { + if p.Multiple { + return nil, errors.New("PropertyMap does not support multiple properties") + } + props = append(props, p) + } + return props, nil +} + +var _ PropertyLoadSaver = PropertyMap{} + +type Gopher struct { + Name string + Height int +} + +// typeOfEmptyInterface is the type of interface{}, but we can't use +// reflect.TypeOf((interface{})(nil)) directly because TypeOf takes an +// interface{}. +var typeOfEmptyInterface = reflect.TypeOf((*interface{})(nil)).Elem() + +func TestCheckMultiArg(t *testing.T) { + testCases := []struct { + v interface{} + mat multiArgType + elemType reflect.Type + }{ + // Invalid cases. + {nil, multiArgTypeInvalid, nil}, + {Gopher{}, multiArgTypeInvalid, nil}, + {&Gopher{}, multiArgTypeInvalid, nil}, + {PropertyList{}, multiArgTypeInvalid, nil}, // This is a special case. + {PropertyMap{}, multiArgTypeInvalid, nil}, + {[]*PropertyList(nil), multiArgTypeInvalid, nil}, + {[]*PropertyMap(nil), multiArgTypeInvalid, nil}, + {[]**Gopher(nil), multiArgTypeInvalid, nil}, + {[]*interface{}(nil), multiArgTypeInvalid, nil}, + // Valid cases. + { + []PropertyList(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(PropertyList{}), + }, + { + []PropertyMap(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(PropertyMap{}), + }, + { + []StructThatImplementsPLS(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(StructThatImplementsPLS{}), + }, + { + []StructPtrThatImplementsPLS(nil), + multiArgTypePropertyLoadSaver, + reflect.TypeOf(StructPtrThatImplementsPLS{}), + }, + { + []Gopher(nil), + multiArgTypeStruct, + reflect.TypeOf(Gopher{}), + }, + { + []*Gopher(nil), + multiArgTypeStructPtr, + reflect.TypeOf(Gopher{}), + }, + { + []interface{}(nil), + multiArgTypeInterface, + typeOfEmptyInterface, + }, + } + for _, tc := range testCases { + mat, elemType := checkMultiArg(reflect.ValueOf(tc.v)) + if mat != tc.mat || elemType != tc.elemType { + t.Errorf("checkMultiArg(%T): got %v, %v want %v, %v", + tc.v, mat, elemType, tc.mat, tc.elemType) + } + } +} + +func TestSimpleQuery(t *testing.T) { + struct1 := Gopher{Name: "George", Height: 32} + struct2 := Gopher{Name: "Rufus"} + pList1 := PropertyList{ + { + Name: "Name", + Value: "George", + }, + { + Name: "Height", + Value: int64(32), + }, + } + pList2 := PropertyList{ + { + Name: "Name", + Value: "Rufus", + }, + } + pMap1 := PropertyMap{ + "Name": Property{ + Name: "Name", + Value: "George", + }, + "Height": Property{ + Name: "Height", + Value: int64(32), + }, + } + pMap2 := PropertyMap{ + "Name": Property{ + Name: "Name", + Value: "Rufus", + }, + } + + testCases := []struct { + dst interface{} + want interface{} + }{ + // The destination must have type *[]P, *[]S or *[]*S, for some non-interface + // type P such that *P implements PropertyLoadSaver, or for some struct type S. + {new([]Gopher), &[]Gopher{struct1, struct2}}, + {new([]*Gopher), &[]*Gopher{&struct1, &struct2}}, + {new([]PropertyList), &[]PropertyList{pList1, pList2}}, + {new([]PropertyMap), &[]PropertyMap{pMap1, pMap2}}, + + // Any other destination type is invalid. + {0, nil}, + {Gopher{}, nil}, + {PropertyList{}, nil}, + {PropertyMap{}, nil}, + {[]int{}, nil}, + {[]Gopher{}, nil}, + {[]PropertyList{}, nil}, + {new(int), nil}, + {new(Gopher), nil}, + {new(PropertyList), nil}, // This is a special case. + {new(PropertyMap), nil}, + {new([]int), nil}, + {new([]map[int]int), nil}, + {new([]map[string]Property), nil}, + {new([]map[string]interface{}), nil}, + {new([]*int), nil}, + {new([]*map[int]int), nil}, + {new([]*map[string]Property), nil}, + {new([]*map[string]interface{}), nil}, + {new([]**Gopher), nil}, + {new([]*PropertyList), nil}, + {new([]*PropertyMap), nil}, + } + for _, tc := range testCases { + nCall := 0 + c := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(in *pb.Query, out *pb.QueryResult) error { + nCall++ + return fakeRunQuery(in, out) + }) + c = internal.WithAppIDOverride(c, "dev~fake-app") + + var ( + expectedErr error + expectedNCall int + ) + if tc.want == nil { + expectedErr = ErrInvalidEntityType + } else { + expectedNCall = 1 + } + keys, err := NewQuery("Gopher").GetAll(c, tc.dst) + if err != expectedErr { + t.Errorf("dst type %T: got error [%v], want [%v]", tc.dst, err, expectedErr) + continue + } + if nCall != expectedNCall { + t.Errorf("dst type %T: Context.Call was called an incorrect number of times: got %d want %d", tc.dst, nCall, expectedNCall) + continue + } + if err != nil { + continue + } + + key1 := NewKey(c, "Gopher", "", 6, nil) + expectedKeys := []*Key{ + key1, + NewKey(c, "Gopher", "", 8, key1), + } + if l1, l2 := len(keys), len(expectedKeys); l1 != l2 { + t.Errorf("dst type %T: got %d keys, want %d keys", tc.dst, l1, l2) + continue + } + for i, key := range keys { + if key.AppID() != "s~test-app" { + t.Errorf(`dst type %T: Key #%d's AppID = %q, want "s~test-app"`, tc.dst, i, key.AppID()) + continue + } + if !keysEqual(key, expectedKeys[i]) { + t.Errorf("dst type %T: got key #%d %v, want %v", tc.dst, i, key, expectedKeys[i]) + continue + } + } + + if !reflect.DeepEqual(tc.dst, tc.want) { + t.Errorf("dst type %T: Entities got %+v, want %+v", tc.dst, tc.dst, tc.want) + continue + } + } +} + +// keysEqual is like (*Key).Equal, but ignores the App ID. +func keysEqual(a, b *Key) bool { + for a != nil && b != nil { + if a.Kind() != b.Kind() || a.StringID() != b.StringID() || a.IntID() != b.IntID() { + return false + } + a, b = a.Parent(), b.Parent() + } + return a == b +} + +func TestQueriesAreImmutable(t *testing.T) { + // Test that deriving q2 from q1 does not modify q1. + q0 := NewQuery("foo") + q1 := NewQuery("foo") + q2 := q1.Offset(2) + if !reflect.DeepEqual(q0, q1) { + t.Errorf("q0 and q1 were not equal") + } + if reflect.DeepEqual(q1, q2) { + t.Errorf("q1 and q2 were equal") + } + + // Test that deriving from q4 twice does not conflict, even though + // q4 has a long list of order clauses. This tests that the arrays + // backed by a query's slice of orders are not shared. + f := func() *Query { + q := NewQuery("bar") + // 47 is an ugly number that is unlikely to be near a re-allocation + // point in repeated append calls. For example, it's not near a power + // of 2 or a multiple of 10. + for i := 0; i < 47; i++ { + q = q.Order(fmt.Sprintf("x%d", i)) + } + return q + } + q3 := f().Order("y") + q4 := f() + q5 := q4.Order("y") + q6 := q4.Order("z") + if !reflect.DeepEqual(q3, q5) { + t.Errorf("q3 and q5 were not equal") + } + if reflect.DeepEqual(q5, q6) { + t.Errorf("q5 and q6 were equal") + } +} + +func TestFilterParser(t *testing.T) { + testCases := []struct { + filterStr string + wantOK bool + wantFieldName string + wantOp operator + }{ + // Supported ops. + {"x<", true, "x", lessThan}, + {"x <", true, "x", lessThan}, + {"x <", true, "x", lessThan}, + {" x < ", true, "x", lessThan}, + {"x <=", true, "x", lessEq}, + {"x =", true, "x", equal}, + {"x >=", true, "x", greaterEq}, + {"x >", true, "x", greaterThan}, + {"in >", true, "in", greaterThan}, + {"in>", true, "in", greaterThan}, + // Valid but (currently) unsupported ops. + {"x!=", false, "", 0}, + {"x !=", false, "", 0}, + {" x != ", false, "", 0}, + {"x IN", false, "", 0}, + {"x in", false, "", 0}, + // Invalid ops. + {"x EQ", false, "", 0}, + {"x lt", false, "", 0}, + {"x <>", false, "", 0}, + {"x >>", false, "", 0}, + {"x ==", false, "", 0}, + {"x =<", false, "", 0}, + {"x =>", false, "", 0}, + {"x !", false, "", 0}, + {"x ", false, "", 0}, + {"x", false, "", 0}, + } + for _, tc := range testCases { + q := NewQuery("foo").Filter(tc.filterStr, 42) + if ok := q.err == nil; ok != tc.wantOK { + t.Errorf("%q: ok=%t, want %t", tc.filterStr, ok, tc.wantOK) + continue + } + if !tc.wantOK { + continue + } + if len(q.filter) != 1 { + t.Errorf("%q: len=%d, want %d", tc.filterStr, len(q.filter), 1) + continue + } + got, want := q.filter[0], filter{tc.wantFieldName, tc.wantOp, 42} + if got != want { + t.Errorf("%q: got %v, want %v", tc.filterStr, got, want) + continue + } + } +} + +func TestQueryToProto(t *testing.T) { + // The context is required to make Keys for the test cases. + var got *pb.Query + NoErr := errors.New("No error") + c := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(in *pb.Query, out *pb.QueryResult) error { + got = in + return NoErr // return a non-nil error so Run doesn't keep going. + }) + c = internal.WithAppIDOverride(c, "dev~fake-app") + + testCases := []struct { + desc string + query *Query + want *pb.Query + err string + }{ + { + desc: "empty", + query: NewQuery(""), + want: &pb.Query{}, + }, + { + desc: "standard query", + query: NewQuery("kind").Order("-I").Filter("I >", 17).Filter("U =", "Dave").Limit(7).Offset(42).BatchSize(5), + want: &pb.Query{ + Kind: proto.String("kind"), + Filter: []*pb.Query_Filter{ + { + Op: pb.Query_Filter_GREATER_THAN.Enum(), + Property: []*pb.Property{ + { + Name: proto.String("I"), + Value: &pb.PropertyValue{Int64Value: proto.Int64(17)}, + Multiple: proto.Bool(false), + }, + }, + }, + { + Op: pb.Query_Filter_EQUAL.Enum(), + Property: []*pb.Property{ + { + Name: proto.String("U"), + Value: &pb.PropertyValue{StringValue: proto.String("Dave")}, + Multiple: proto.Bool(false), + }, + }, + }, + }, + Order: []*pb.Query_Order{ + { + Property: proto.String("I"), + Direction: pb.Query_Order_DESCENDING.Enum(), + }, + }, + Limit: proto.Int32(7), + Offset: proto.Int32(42), + Count: proto.Int32(5), + }, + }, + { + desc: "ancestor", + query: NewQuery("").Ancestor(NewKey(c, "kind", "Mummy", 0, nil)), + want: &pb.Query{ + Ancestor: &pb.Reference{ + App: proto.String("dev~fake-app"), + Path: &pb.Path{ + Element: []*pb.Path_Element{{Type: proto.String("kind"), Name: proto.String("Mummy")}}, + }, + }, + }, + }, + { + desc: "projection", + query: NewQuery("").Project("A", "B"), + want: &pb.Query{ + PropertyName: []string{"A", "B"}, + }, + }, + { + desc: "projection with distinct", + query: NewQuery("").Project("A", "B").Distinct(), + want: &pb.Query{ + PropertyName: []string{"A", "B"}, + GroupByPropertyName: []string{"A", "B"}, + }, + }, + { + desc: "keys only", + query: NewQuery("").KeysOnly(), + want: &pb.Query{ + KeysOnly: proto.Bool(true), + RequirePerfectPlan: proto.Bool(true), + }, + }, + { + desc: "empty filter", + query: NewQuery("kind").Filter("=", 17), + err: "empty query filter field nam", + }, + { + desc: "bad filter type", + query: NewQuery("kind").Filter("M =", map[string]bool{}), + err: "bad query filter value type", + }, + { + desc: "bad filter operator", + query: NewQuery("kind").Filter("I <<=", 17), + err: `invalid operator "<<=" in filter "I <<="`, + }, + { + desc: "empty order", + query: NewQuery("kind").Order(""), + err: "empty order", + }, + { + desc: "bad order direction", + query: NewQuery("kind").Order("+I"), + err: `invalid order: "+I`, + }, + } + + for _, tt := range testCases { + got = nil + if _, err := tt.query.Run(c).Next(nil); err != NoErr { + if tt.err == "" || !strings.Contains(err.Error(), tt.err) { + t.Errorf("%s: error %v, want %q", tt.desc, err, tt.err) + } + continue + } + if tt.err != "" { + t.Errorf("%s: no error, want %q", tt.desc, tt.err) + continue + } + // Fields that are common to all protos. + tt.want.App = proto.String("dev~fake-app") + tt.want.Compile = proto.Bool(true) + if !proto.Equal(got, tt.want) { + t.Errorf("%s:\ngot %v\nwant %v", tt.desc, got, tt.want) + } + } +} diff --git a/vendor/google.golang.org/appengine/datastore/save.go b/vendor/google.golang.org/appengine/datastore/save.go new file mode 100644 index 0000000000..7b045a5955 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/save.go @@ -0,0 +1,333 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "errors" + "fmt" + "math" + "reflect" + "time" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine" + pb "google.golang.org/appengine/internal/datastore" +) + +func toUnixMicro(t time.Time) int64 { + // We cannot use t.UnixNano() / 1e3 because we want to handle times more than + // 2^63 nanoseconds (which is about 292 years) away from 1970, and those cannot + // be represented in the numerator of a single int64 divide. + return t.Unix()*1e6 + int64(t.Nanosecond()/1e3) +} + +func fromUnixMicro(t int64) time.Time { + return time.Unix(t/1e6, (t%1e6)*1e3).UTC() +} + +var ( + minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6)*1e3) + maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)*1e3) +) + +// valueToProto converts a named value to a newly allocated Property. +// The returned error string is empty on success. +func valueToProto(defaultAppID, name string, v reflect.Value, multiple bool) (p *pb.Property, errStr string) { + var ( + pv pb.PropertyValue + unsupported bool + ) + switch v.Kind() { + case reflect.Invalid: + // No-op. + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + pv.Int64Value = proto.Int64(v.Int()) + case reflect.Bool: + pv.BooleanValue = proto.Bool(v.Bool()) + case reflect.String: + pv.StringValue = proto.String(v.String()) + case reflect.Float32, reflect.Float64: + pv.DoubleValue = proto.Float64(v.Float()) + case reflect.Ptr: + if k, ok := v.Interface().(*Key); ok { + if k != nil { + pv.Referencevalue = keyToReferenceValue(defaultAppID, k) + } + } else { + unsupported = true + } + case reflect.Struct: + switch t := v.Interface().(type) { + case time.Time: + if t.Before(minTime) || t.After(maxTime) { + return nil, "time value out of range" + } + pv.Int64Value = proto.Int64(toUnixMicro(t)) + case appengine.GeoPoint: + if !t.Valid() { + return nil, "invalid GeoPoint value" + } + // NOTE: Strangely, latitude maps to X, longitude to Y. + pv.Pointvalue = &pb.PropertyValue_PointValue{X: &t.Lat, Y: &t.Lng} + default: + unsupported = true + } + case reflect.Slice: + if b, ok := v.Interface().([]byte); ok { + pv.StringValue = proto.String(string(b)) + } else { + // nvToProto should already catch slice values. + // If we get here, we have a slice of slice values. + unsupported = true + } + default: + unsupported = true + } + if unsupported { + return nil, "unsupported datastore value type: " + v.Type().String() + } + p = &pb.Property{ + Name: proto.String(name), + Value: &pv, + Multiple: proto.Bool(multiple), + } + if v.IsValid() { + switch v.Interface().(type) { + case []byte: + p.Meaning = pb.Property_BLOB.Enum() + case ByteString: + p.Meaning = pb.Property_BYTESTRING.Enum() + case appengine.BlobKey: + p.Meaning = pb.Property_BLOBKEY.Enum() + case time.Time: + p.Meaning = pb.Property_GD_WHEN.Enum() + case appengine.GeoPoint: + p.Meaning = pb.Property_GEORSS_POINT.Enum() + } + } + return p, "" +} + +type saveOpts struct { + noIndex bool + multiple bool + omitEmpty bool +} + +// saveEntity saves an EntityProto into a PropertyLoadSaver or struct pointer. +func saveEntity(defaultAppID string, key *Key, src interface{}) (*pb.EntityProto, error) { + var err error + var props []Property + if e, ok := src.(PropertyLoadSaver); ok { + props, err = e.Save() + } else { + props, err = SaveStruct(src) + } + if err != nil { + return nil, err + } + return propertiesToProto(defaultAppID, key, props) +} + +func saveStructProperty(props *[]Property, name string, opts saveOpts, v reflect.Value) error { + if opts.omitEmpty && isEmptyValue(v) { + return nil + } + p := Property{ + Name: name, + NoIndex: opts.noIndex, + Multiple: opts.multiple, + } + switch x := v.Interface().(type) { + case *Key: + p.Value = x + case time.Time: + p.Value = x + case appengine.BlobKey: + p.Value = x + case appengine.GeoPoint: + p.Value = x + case ByteString: + p.Value = x + default: + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p.Value = v.Int() + case reflect.Bool: + p.Value = v.Bool() + case reflect.String: + p.Value = v.String() + case reflect.Float32, reflect.Float64: + p.Value = v.Float() + case reflect.Slice: + if v.Type().Elem().Kind() == reflect.Uint8 { + p.NoIndex = true + p.Value = v.Bytes() + } + case reflect.Struct: + if !v.CanAddr() { + return fmt.Errorf("datastore: unsupported struct field: value is unaddressable") + } + sub, err := newStructPLS(v.Addr().Interface()) + if err != nil { + return fmt.Errorf("datastore: unsupported struct field: %v", err) + } + return sub.save(props, name+".", opts) + } + } + if p.Value == nil { + return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type()) + } + *props = append(*props, p) + return nil +} + +func (s structPLS) Save() ([]Property, error) { + var props []Property + if err := s.save(&props, "", saveOpts{}); err != nil { + return nil, err + } + return props, nil +} + +func (s structPLS) save(props *[]Property, prefix string, opts saveOpts) error { + for name, f := range s.codec.fields { + name = prefix + name + v := s.v.FieldByIndex(f.path) + if !v.IsValid() || !v.CanSet() { + continue + } + var opts1 saveOpts + opts1.noIndex = opts.noIndex || f.noIndex + opts1.multiple = opts.multiple + opts1.omitEmpty = f.omitEmpty // don't propagate + // For slice fields that aren't []byte, save each element. + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { + opts1.multiple = true + for j := 0; j < v.Len(); j++ { + if err := saveStructProperty(props, name, opts1, v.Index(j)); err != nil { + return err + } + } + continue + } + // Otherwise, save the field itself. + if err := saveStructProperty(props, name, opts1, v); err != nil { + return err + } + } + return nil +} + +func propertiesToProto(defaultAppID string, key *Key, props []Property) (*pb.EntityProto, error) { + e := &pb.EntityProto{ + Key: keyToProto(defaultAppID, key), + } + if key.parent == nil { + e.EntityGroup = &pb.Path{} + } else { + e.EntityGroup = keyToProto(defaultAppID, key.root()).Path + } + prevMultiple := make(map[string]bool) + + for _, p := range props { + if pm, ok := prevMultiple[p.Name]; ok { + if !pm || !p.Multiple { + return nil, fmt.Errorf("datastore: multiple Properties with Name %q, but Multiple is false", p.Name) + } + } else { + prevMultiple[p.Name] = p.Multiple + } + + x := &pb.Property{ + Name: proto.String(p.Name), + Value: new(pb.PropertyValue), + Multiple: proto.Bool(p.Multiple), + } + switch v := p.Value.(type) { + case int64: + x.Value.Int64Value = proto.Int64(v) + case bool: + x.Value.BooleanValue = proto.Bool(v) + case string: + x.Value.StringValue = proto.String(v) + if p.NoIndex { + x.Meaning = pb.Property_TEXT.Enum() + } + case float64: + x.Value.DoubleValue = proto.Float64(v) + case *Key: + if v != nil { + x.Value.Referencevalue = keyToReferenceValue(defaultAppID, v) + } + case time.Time: + if v.Before(minTime) || v.After(maxTime) { + return nil, fmt.Errorf("datastore: time value out of range") + } + x.Value.Int64Value = proto.Int64(toUnixMicro(v)) + x.Meaning = pb.Property_GD_WHEN.Enum() + case appengine.BlobKey: + x.Value.StringValue = proto.String(string(v)) + x.Meaning = pb.Property_BLOBKEY.Enum() + case appengine.GeoPoint: + if !v.Valid() { + return nil, fmt.Errorf("datastore: invalid GeoPoint value") + } + // NOTE: Strangely, latitude maps to X, longitude to Y. + x.Value.Pointvalue = &pb.PropertyValue_PointValue{X: &v.Lat, Y: &v.Lng} + x.Meaning = pb.Property_GEORSS_POINT.Enum() + case []byte: + x.Value.StringValue = proto.String(string(v)) + x.Meaning = pb.Property_BLOB.Enum() + if !p.NoIndex { + return nil, fmt.Errorf("datastore: cannot index a []byte valued Property with Name %q", p.Name) + } + case ByteString: + x.Value.StringValue = proto.String(string(v)) + x.Meaning = pb.Property_BYTESTRING.Enum() + default: + if p.Value != nil { + return nil, fmt.Errorf("datastore: invalid Value type for a Property with Name %q", p.Name) + } + } + + if p.NoIndex { + e.RawProperty = append(e.RawProperty, x) + } else { + e.Property = append(e.Property, x) + if len(e.Property) > maxIndexedProperties { + return nil, errors.New("datastore: too many indexed properties") + } + } + } + return e, nil +} + +// isEmptyValue is taken from the encoding/json package in the standard library. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + // TODO(perfomance): Only reflect.String needed, other property types are not supported (copy/paste from json package) + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + // TODO(perfomance): Uint* are unsupported property types - should be removed (copy/paste from json package) + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Struct: + switch x := v.Interface().(type) { + case time.Time: + return x.IsZero() + } + } + return false +} diff --git a/vendor/google.golang.org/appengine/datastore/time_test.go b/vendor/google.golang.org/appengine/datastore/time_test.go new file mode 100644 index 0000000000..ba74b449eb --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/time_test.go @@ -0,0 +1,65 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "testing" + "time" +) + +func TestUnixMicro(t *testing.T) { + // Test that all these time.Time values survive a round trip to unix micros. + testCases := []time.Time{ + {}, + time.Date(2, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(23, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(234, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1000, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1600, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1700, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1800, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), + time.Unix(-1e6, -1000), + time.Unix(-1e6, 0), + time.Unix(-1e6, +1000), + time.Unix(-60, -1000), + time.Unix(-60, 0), + time.Unix(-60, +1000), + time.Unix(-1, -1000), + time.Unix(-1, 0), + time.Unix(-1, +1000), + time.Unix(0, -3000), + time.Unix(0, -2000), + time.Unix(0, -1000), + time.Unix(0, 0), + time.Unix(0, +1000), + time.Unix(0, +2000), + time.Unix(+60, -1000), + time.Unix(+60, 0), + time.Unix(+60, +1000), + time.Unix(+1e6, -1000), + time.Unix(+1e6, 0), + time.Unix(+1e6, +1000), + time.Date(1999, 12, 31, 23, 59, 59, 999000, time.UTC), + time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(2006, 1, 2, 15, 4, 5, 678000, time.UTC), + time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + time.Date(3456, 1, 1, 0, 0, 0, 0, time.UTC), + } + for _, tc := range testCases { + got := fromUnixMicro(toUnixMicro(tc)) + if !got.Equal(tc) { + t.Errorf("got %q, want %q", got, tc) + } + } + + // Test that a time.Time that isn't an integral number of microseconds + // is not perfectly reconstructed after a round trip. + t0 := time.Unix(0, 123) + t1 := fromUnixMicro(toUnixMicro(t0)) + if t1.Nanosecond()%1000 != 0 || t0.Nanosecond()%1000 == 0 { + t.Errorf("quantization to µs: got %q with %d ns, started with %d ns", t1, t1.Nanosecond(), t0.Nanosecond()) + } +} diff --git a/vendor/google.golang.org/appengine/datastore/transaction.go b/vendor/google.golang.org/appengine/datastore/transaction.go new file mode 100644 index 0000000000..2ae8428f85 --- /dev/null +++ b/vendor/google.golang.org/appengine/datastore/transaction.go @@ -0,0 +1,96 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package datastore + +import ( + "errors" + + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/datastore" +) + +func init() { + internal.RegisterTransactionSetter(func(x *pb.Query, t *pb.Transaction) { + x.Transaction = t + }) + internal.RegisterTransactionSetter(func(x *pb.GetRequest, t *pb.Transaction) { + x.Transaction = t + }) + internal.RegisterTransactionSetter(func(x *pb.PutRequest, t *pb.Transaction) { + x.Transaction = t + }) + internal.RegisterTransactionSetter(func(x *pb.DeleteRequest, t *pb.Transaction) { + x.Transaction = t + }) +} + +// ErrConcurrentTransaction is returned when a transaction is rolled back due +// to a conflict with a concurrent transaction. +var ErrConcurrentTransaction = errors.New("datastore: concurrent transaction") + +// RunInTransaction runs f in a transaction. It calls f with a transaction +// context tc that f should use for all App Engine operations. +// +// If f returns nil, RunInTransaction attempts to commit the transaction, +// returning nil if it succeeds. If the commit fails due to a conflicting +// transaction, RunInTransaction retries f, each time with a new transaction +// context. It gives up and returns ErrConcurrentTransaction after three +// failed attempts. The number of attempts can be configured by specifying +// TransactionOptions.Attempts. +// +// If f returns non-nil, then any datastore changes will not be applied and +// RunInTransaction returns that same error. The function f is not retried. +// +// Note that when f returns, the transaction is not yet committed. Calling code +// must be careful not to assume that any of f's changes have been committed +// until RunInTransaction returns nil. +// +// Since f may be called multiple times, f should usually be idempotent. +// datastore.Get is not idempotent when unmarshaling slice fields. +// +// Nested transactions are not supported; c may not be a transaction context. +func RunInTransaction(c context.Context, f func(tc context.Context) error, opts *TransactionOptions) error { + xg := false + if opts != nil { + xg = opts.XG + } + readOnly := false + if opts != nil { + readOnly = opts.ReadOnly + } + attempts := 3 + if opts != nil && opts.Attempts > 0 { + attempts = opts.Attempts + } + var t *pb.Transaction + var err error + for i := 0; i < attempts; i++ { + if t, err = internal.RunTransactionOnce(c, f, xg, readOnly, t); err != internal.ErrConcurrentTransaction { + return err + } + } + return ErrConcurrentTransaction +} + +// TransactionOptions are the options for running a transaction. +type TransactionOptions struct { + // XG is whether the transaction can cross multiple entity groups. In + // comparison, a single group transaction is one where all datastore keys + // used have the same root key. Note that cross group transactions do not + // have the same behavior as single group transactions. In particular, it + // is much more likely to see partially applied transactions in different + // entity groups, in global queries. + // It is valid to set XG to true even if the transaction is within a + // single entity group. + XG bool + // Attempts controls the number of retries to perform when commits fail + // due to a conflicting transaction. If omitted, it defaults to 3. + Attempts int + // ReadOnly controls whether the transaction is a read only transaction. + // Read only transactions are potentially more efficient. + ReadOnly bool +} diff --git a/vendor/google.golang.org/appengine/delay/delay.go b/vendor/google.golang.org/appengine/delay/delay.go new file mode 100644 index 0000000000..52915a4224 --- /dev/null +++ b/vendor/google.golang.org/appengine/delay/delay.go @@ -0,0 +1,295 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package delay provides a way to execute code outside the scope of a +user request by using the taskqueue API. + +To declare a function that may be executed later, call Func +in a top-level assignment context, passing it an arbitrary string key +and a function whose first argument is of type context.Context. +The key is used to look up the function so it can be called later. + var laterFunc = delay.Func("key", myFunc) +It is also possible to use a function literal. + var laterFunc = delay.Func("key", func(c context.Context, x string) { + // ... + }) + +To call a function, invoke its Call method. + laterFunc.Call(c, "something") +A function may be called any number of times. If the function has any +return arguments, and the last one is of type error, the function may +return a non-nil error to signal that the function should be retried. + +The arguments to functions may be of any type that is encodable by the gob +package. If an argument is of interface type, it is the client's responsibility +to register with the gob package whatever concrete type may be passed for that +argument; see http://golang.org/pkg/gob/#Register for details. + +Any errors during initialization or execution of a function will be +logged to the application logs. Error logs that occur during initialization will +be associated with the request that invoked the Call method. + +The state of a function invocation that has not yet successfully +executed is preserved by combining the file name in which it is declared +with the string key that was passed to the Func function. Updating an app +with pending function invocations is safe as long as the relevant +functions have the (filename, key) combination preserved. + +The delay package uses the Task Queue API to create tasks that call the +reserved application path "/_ah/queue/go/delay". +This path must not be marked as "login: required" in app.yaml; +it must be marked as "login: admin" or have no access restriction. +*/ +package delay // import "google.golang.org/appengine/delay" + +import ( + "bytes" + "encoding/gob" + "errors" + "fmt" + "net/http" + "reflect" + "runtime" + + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/log" + "google.golang.org/appengine/taskqueue" +) + +// Function represents a function that may have a delayed invocation. +type Function struct { + fv reflect.Value // Kind() == reflect.Func + key string + err error // any error during initialization +} + +const ( + // The HTTP path for invocations. + path = "/_ah/queue/go/delay" + // Use the default queue. + queue = "" +) + +type contextKey int + +var ( + // registry of all delayed functions + funcs = make(map[string]*Function) + + // precomputed types + errorType = reflect.TypeOf((*error)(nil)).Elem() + + // errors + errFirstArg = errors.New("first argument must be context.Context") + errOutsideDelayFunc = errors.New("request headers are only available inside a delay.Func") + + // context keys + headersContextKey contextKey = 0 +) + +// Func declares a new Function. The second argument must be a function with a +// first argument of type context.Context. +// This function must be called at program initialization time. That means it +// must be called in a global variable declaration or from an init function. +// This restriction is necessary because the instance that delays a function +// call may not be the one that executes it. Only the code executed at program +// initialization time is guaranteed to have been run by an instance before it +// receives a request. +func Func(key string, i interface{}) *Function { + f := &Function{fv: reflect.ValueOf(i)} + + // Derive unique, somewhat stable key for this func. + _, file, _, _ := runtime.Caller(1) + f.key = file + ":" + key + + t := f.fv.Type() + if t.Kind() != reflect.Func { + f.err = errors.New("not a function") + return f + } + if t.NumIn() == 0 || !isContext(t.In(0)) { + f.err = errFirstArg + return f + } + + // Register the function's arguments with the gob package. + // This is required because they are marshaled inside a []interface{}. + // gob.Register only expects to be called during initialization; + // that's fine because this function expects the same. + for i := 0; i < t.NumIn(); i++ { + // Only concrete types may be registered. If the argument has + // interface type, the client is resposible for registering the + // concrete types it will hold. + if t.In(i).Kind() == reflect.Interface { + continue + } + gob.Register(reflect.Zero(t.In(i)).Interface()) + } + + if old := funcs[f.key]; old != nil { + old.err = fmt.Errorf("multiple functions registered for %s in %s", key, file) + } + funcs[f.key] = f + return f +} + +type invocation struct { + Key string + Args []interface{} +} + +// Call invokes a delayed function. +// err := f.Call(c, ...) +// is equivalent to +// t, _ := f.Task(...) +// _, err := taskqueue.Add(c, t, "") +func (f *Function) Call(c context.Context, args ...interface{}) error { + t, err := f.Task(args...) + if err != nil { + return err + } + _, err = taskqueueAdder(c, t, queue) + return err +} + +// Task creates a Task that will invoke the function. +// Its parameters may be tweaked before adding it to a queue. +// Users should not modify the Path or Payload fields of the returned Task. +func (f *Function) Task(args ...interface{}) (*taskqueue.Task, error) { + if f.err != nil { + return nil, fmt.Errorf("delay: func is invalid: %v", f.err) + } + + nArgs := len(args) + 1 // +1 for the context.Context + ft := f.fv.Type() + minArgs := ft.NumIn() + if ft.IsVariadic() { + minArgs-- + } + if nArgs < minArgs { + return nil, fmt.Errorf("delay: too few arguments to func: %d < %d", nArgs, minArgs) + } + if !ft.IsVariadic() && nArgs > minArgs { + return nil, fmt.Errorf("delay: too many arguments to func: %d > %d", nArgs, minArgs) + } + + // Check arg types. + for i := 1; i < nArgs; i++ { + at := reflect.TypeOf(args[i-1]) + var dt reflect.Type + if i < minArgs { + // not a variadic arg + dt = ft.In(i) + } else { + // a variadic arg + dt = ft.In(minArgs).Elem() + } + // nil arguments won't have a type, so they need special handling. + if at == nil { + // nil interface + switch dt.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + continue // may be nil + } + return nil, fmt.Errorf("delay: argument %d has wrong type: %v is not nilable", i, dt) + } + switch at.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + av := reflect.ValueOf(args[i-1]) + if av.IsNil() { + // nil value in interface; not supported by gob, so we replace it + // with a nil interface value + args[i-1] = nil + } + } + if !at.AssignableTo(dt) { + return nil, fmt.Errorf("delay: argument %d has wrong type: %v is not assignable to %v", i, at, dt) + } + } + + inv := invocation{ + Key: f.key, + Args: args, + } + + buf := new(bytes.Buffer) + if err := gob.NewEncoder(buf).Encode(inv); err != nil { + return nil, fmt.Errorf("delay: gob encoding failed: %v", err) + } + + return &taskqueue.Task{ + Path: path, + Payload: buf.Bytes(), + }, nil +} + +// Request returns the special task-queue HTTP request headers for the current +// task queue handler. Returns an error if called from outside a delay.Func. +func RequestHeaders(c context.Context) (*taskqueue.RequestHeaders, error) { + if ret, ok := c.Value(headersContextKey).(*taskqueue.RequestHeaders); ok { + return ret, nil + } + return nil, errOutsideDelayFunc +} + +var taskqueueAdder = taskqueue.Add // for testing + +func init() { + http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) { + runFunc(appengine.NewContext(req), w, req) + }) +} + +func runFunc(c context.Context, w http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + + c = context.WithValue(c, headersContextKey, taskqueue.ParseRequestHeaders(req.Header)) + + var inv invocation + if err := gob.NewDecoder(req.Body).Decode(&inv); err != nil { + log.Errorf(c, "delay: failed decoding task payload: %v", err) + log.Warningf(c, "delay: dropping task") + return + } + + f := funcs[inv.Key] + if f == nil { + log.Errorf(c, "delay: no func with key %q found", inv.Key) + log.Warningf(c, "delay: dropping task") + return + } + + ft := f.fv.Type() + in := []reflect.Value{reflect.ValueOf(c)} + for _, arg := range inv.Args { + var v reflect.Value + if arg != nil { + v = reflect.ValueOf(arg) + } else { + // Task was passed a nil argument, so we must construct + // the zero value for the argument here. + n := len(in) // we're constructing the nth argument + var at reflect.Type + if !ft.IsVariadic() || n < ft.NumIn()-1 { + at = ft.In(n) + } else { + at = ft.In(ft.NumIn() - 1).Elem() + } + v = reflect.Zero(at) + } + in = append(in, v) + } + out := f.fv.Call(in) + + if n := ft.NumOut(); n > 0 && ft.Out(n-1) == errorType { + if errv := out[n-1]; !errv.IsNil() { + log.Errorf(c, "delay: func failed (will retry): %v", errv.Interface()) + w.WriteHeader(http.StatusInternalServerError) + return + } + } +} diff --git a/vendor/google.golang.org/appengine/delay/delay_go17.go b/vendor/google.golang.org/appengine/delay/delay_go17.go new file mode 100644 index 0000000000..9a59e8b0de --- /dev/null +++ b/vendor/google.golang.org/appengine/delay/delay_go17.go @@ -0,0 +1,23 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +//+build go1.7 + +package delay + +import ( + stdctx "context" + "reflect" + + netctx "golang.org/x/net/context" +) + +var ( + stdContextType = reflect.TypeOf((*stdctx.Context)(nil)).Elem() + netContextType = reflect.TypeOf((*netctx.Context)(nil)).Elem() +) + +func isContext(t reflect.Type) bool { + return t == stdContextType || t == netContextType +} diff --git a/vendor/google.golang.org/appengine/delay/delay_go17_test.go b/vendor/google.golang.org/appengine/delay/delay_go17_test.go new file mode 100644 index 0000000000..0e708d005e --- /dev/null +++ b/vendor/google.golang.org/appengine/delay/delay_go17_test.go @@ -0,0 +1,55 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +//+build go1.7 + +package delay + +import ( + "bytes" + stdctx "context" + "net/http" + "net/http/httptest" + "testing" + + netctx "golang.org/x/net/context" + "google.golang.org/appengine/taskqueue" +) + +var ( + stdCtxRuns = 0 + stdCtxFunc = Func("stdctx", func(c stdctx.Context) { + stdCtxRuns++ + }) +) + +func TestStandardContext(t *testing.T) { + // Fake out the adding of a task. + var task *taskqueue.Task + taskqueueAdder = func(_ netctx.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) { + if queue != "" { + t.Errorf(`Got queue %q, expected ""`, queue) + } + task = tk + return tk, nil + } + + c := newFakeContext() + stdCtxRuns = 0 // reset state + if err := stdCtxFunc.Call(c.ctx); err != nil { + t.Fatal("Function.Call:", err) + } + + // Simulate the Task Queue service. + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + + if stdCtxRuns != 1 { + t.Errorf("stdCtxRuns: got %d, want 1", stdCtxRuns) + } +} diff --git a/vendor/google.golang.org/appengine/delay/delay_pre17.go b/vendor/google.golang.org/appengine/delay/delay_pre17.go new file mode 100644 index 0000000000..d30c75dfb4 --- /dev/null +++ b/vendor/google.golang.org/appengine/delay/delay_pre17.go @@ -0,0 +1,19 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +//+build !go1.7 + +package delay + +import ( + "reflect" + + "golang.org/x/net/context" +) + +var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() + +func isContext(t reflect.Type) bool { + return t == contextType +} diff --git a/vendor/google.golang.org/appengine/delay/delay_test.go b/vendor/google.golang.org/appengine/delay/delay_test.go new file mode 100644 index 0000000000..3df2bf7e33 --- /dev/null +++ b/vendor/google.golang.org/appengine/delay/delay_test.go @@ -0,0 +1,428 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package delay + +import ( + "bytes" + "encoding/gob" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "reflect" + "testing" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + "google.golang.org/appengine/taskqueue" +) + +type CustomType struct { + N int +} + +type CustomInterface interface { + N() int +} + +type CustomImpl int + +func (c CustomImpl) N() int { return int(c) } + +// CustomImpl needs to be registered with gob. +func init() { + gob.Register(CustomImpl(0)) +} + +var ( + invalidFunc = Func("invalid", func() {}) + + regFuncRuns = 0 + regFuncMsg = "" + regFunc = Func("reg", func(c context.Context, arg string) { + regFuncRuns++ + regFuncMsg = arg + }) + + custFuncTally = 0 + custFunc = Func("cust", func(c context.Context, ct *CustomType, ci CustomInterface) { + a, b := 2, 3 + if ct != nil { + a = ct.N + } + if ci != nil { + b = ci.N() + } + custFuncTally += a + b + }) + + anotherCustFunc = Func("cust2", func(c context.Context, n int, ct *CustomType, ci CustomInterface) { + }) + + varFuncMsg = "" + varFunc = Func("variadic", func(c context.Context, format string, args ...int) { + // convert []int to []interface{} for fmt.Sprintf. + as := make([]interface{}, len(args)) + for i, a := range args { + as[i] = a + } + varFuncMsg = fmt.Sprintf(format, as...) + }) + + errFuncRuns = 0 + errFuncErr = errors.New("error!") + errFunc = Func("err", func(c context.Context) error { + errFuncRuns++ + if errFuncRuns == 1 { + return nil + } + return errFuncErr + }) + + dupeWhich = 0 + dupe1Func = Func("dupe", func(c context.Context) { + if dupeWhich == 0 { + dupeWhich = 1 + } + }) + dupe2Func = Func("dupe", func(c context.Context) { + if dupeWhich == 0 { + dupeWhich = 2 + } + }) + + reqFuncRuns = 0 + reqFuncHeaders *taskqueue.RequestHeaders + reqFuncErr error + reqFunc = Func("req", func(c context.Context) { + reqFuncRuns++ + reqFuncHeaders, reqFuncErr = RequestHeaders(c) + }) +) + +type fakeContext struct { + ctx context.Context + logging [][]interface{} +} + +func newFakeContext() *fakeContext { + f := new(fakeContext) + f.ctx = internal.WithCallOverride(context.Background(), f.call) + f.ctx = internal.WithLogOverride(f.ctx, f.logf) + return f +} + +func (f *fakeContext) call(ctx context.Context, service, method string, in, out proto.Message) error { + panic("should never be called") +} + +var logLevels = map[int64]string{1: "INFO", 3: "ERROR"} + +func (f *fakeContext) logf(level int64, format string, args ...interface{}) { + f.logging = append(f.logging, append([]interface{}{logLevels[level], format}, args...)) +} + +func TestInvalidFunction(t *testing.T) { + c := newFakeContext() + + if got, want := invalidFunc.Call(c.ctx), fmt.Errorf("delay: func is invalid: %s", errFirstArg); got.Error() != want.Error() { + t.Errorf("Incorrect error: got %q, want %q", got, want) + } +} + +func TestVariadicFunctionArguments(t *testing.T) { + // Check the argument type validation for variadic functions. + + c := newFakeContext() + + calls := 0 + taskqueueAdder = func(c context.Context, t *taskqueue.Task, _ string) (*taskqueue.Task, error) { + calls++ + return t, nil + } + + varFunc.Call(c.ctx, "hi") + varFunc.Call(c.ctx, "%d", 12) + varFunc.Call(c.ctx, "%d %d %d", 3, 1, 4) + if calls != 3 { + t.Errorf("Got %d calls to taskqueueAdder, want 3", calls) + } + + if got, want := varFunc.Call(c.ctx, "%d %s", 12, "a string is bad"), errors.New("delay: argument 3 has wrong type: string is not assignable to int"); got.Error() != want.Error() { + t.Errorf("Incorrect error: got %q, want %q", got, want) + } +} + +func TestBadArguments(t *testing.T) { + // Try running regFunc with different sets of inappropriate arguments. + + c := newFakeContext() + + tests := []struct { + args []interface{} // all except context + wantErr string + }{ + { + args: nil, + wantErr: "delay: too few arguments to func: 1 < 2", + }, + { + args: []interface{}{"lala", 53}, + wantErr: "delay: too many arguments to func: 3 > 2", + }, + { + args: []interface{}{53}, + wantErr: "delay: argument 1 has wrong type: int is not assignable to string", + }, + } + for i, tc := range tests { + got := regFunc.Call(c.ctx, tc.args...) + if got.Error() != tc.wantErr { + t.Errorf("Call %v: got %q, want %q", i, got, tc.wantErr) + } + } +} + +func TestRunningFunction(t *testing.T) { + c := newFakeContext() + + // Fake out the adding of a task. + var task *taskqueue.Task + taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) { + if queue != "" { + t.Errorf(`Got queue %q, expected ""`, queue) + } + task = tk + return tk, nil + } + + regFuncRuns, regFuncMsg = 0, "" // reset state + const msg = "Why, hello!" + regFunc.Call(c.ctx, msg) + + // Simulate the Task Queue service. + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + + if regFuncRuns != 1 { + t.Errorf("regFuncRuns: got %d, want 1", regFuncRuns) + } + if regFuncMsg != msg { + t.Errorf("regFuncMsg: got %q, want %q", regFuncMsg, msg) + } +} + +func TestCustomType(t *testing.T) { + c := newFakeContext() + + // Fake out the adding of a task. + var task *taskqueue.Task + taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) { + if queue != "" { + t.Errorf(`Got queue %q, expected ""`, queue) + } + task = tk + return tk, nil + } + + custFuncTally = 0 // reset state + custFunc.Call(c.ctx, &CustomType{N: 11}, CustomImpl(13)) + + // Simulate the Task Queue service. + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + + if custFuncTally != 24 { + t.Errorf("custFuncTally = %d, want 24", custFuncTally) + } + + // Try the same, but with nil values; one is a nil pointer (and thus a non-nil interface value), + // and the other is a nil interface value. + custFuncTally = 0 // reset state + custFunc.Call(c.ctx, (*CustomType)(nil), nil) + + // Simulate the Task Queue service. + req, err = http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw = httptest.NewRecorder() + runFunc(c.ctx, rw, req) + + if custFuncTally != 5 { + t.Errorf("custFuncTally = %d, want 5", custFuncTally) + } +} + +func TestRunningVariadic(t *testing.T) { + c := newFakeContext() + + // Fake out the adding of a task. + var task *taskqueue.Task + taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) { + if queue != "" { + t.Errorf(`Got queue %q, expected ""`, queue) + } + task = tk + return tk, nil + } + + varFuncMsg = "" // reset state + varFunc.Call(c.ctx, "Amiga %d has %d KB RAM", 500, 512) + + // Simulate the Task Queue service. + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + + const expected = "Amiga 500 has 512 KB RAM" + if varFuncMsg != expected { + t.Errorf("varFuncMsg = %q, want %q", varFuncMsg, expected) + } +} + +func TestErrorFunction(t *testing.T) { + c := newFakeContext() + + // Fake out the adding of a task. + var task *taskqueue.Task + taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) { + if queue != "" { + t.Errorf(`Got queue %q, expected ""`, queue) + } + task = tk + return tk, nil + } + + errFunc.Call(c.ctx) + + // Simulate the Task Queue service. + // The first call should succeed; the second call should fail. + { + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + } + { + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + if rw.Code != http.StatusInternalServerError { + t.Errorf("Got status code %d, want %d", rw.Code, http.StatusInternalServerError) + } + + wantLogging := [][]interface{}{ + {"ERROR", "delay: func failed (will retry): %v", errFuncErr}, + } + if !reflect.DeepEqual(c.logging, wantLogging) { + t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging) + } + } +} + +func TestDuplicateFunction(t *testing.T) { + c := newFakeContext() + + // Fake out the adding of a task. + var task *taskqueue.Task + taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) { + if queue != "" { + t.Errorf(`Got queue %q, expected ""`, queue) + } + task = tk + return tk, nil + } + + if err := dupe1Func.Call(c.ctx); err == nil { + t.Error("dupe1Func.Call did not return error") + } + if task != nil { + t.Error("dupe1Func.Call posted a task") + } + if err := dupe2Func.Call(c.ctx); err != nil { + t.Errorf("dupe2Func.Call error: %v", err) + } + if task == nil { + t.Fatalf("dupe2Func.Call did not post a task") + } + + // Simulate the Task Queue service. + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + + if dupeWhich == 1 { + t.Error("dupe2Func.Call used old registered function") + } else if dupeWhich != 2 { + t.Errorf("dupeWhich = %d; want 2", dupeWhich) + } +} + +func TestGetRequestHeadersFromContext(t *testing.T) { + c := newFakeContext() + + // Outside a delay.Func should return an error. + headers, err := RequestHeaders(c.ctx) + if headers != nil { + t.Errorf("RequestHeaders outside Func, got %v, want nil", headers) + } + if err != errOutsideDelayFunc { + t.Errorf("RequestHeaders outside Func err, got %v, want %v", err, errOutsideDelayFunc) + } + + // Fake out the adding of a task. + var task *taskqueue.Task + taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) { + if queue != "" { + t.Errorf(`Got queue %q, expected ""`, queue) + } + task = tk + return tk, nil + } + + reqFunc.Call(c.ctx) + + reqFuncRuns, reqFuncHeaders = 0, nil // reset state + // Simulate the Task Queue service. + req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload)) + req.Header.Set("x-appengine-taskname", "foobar") + if err != nil { + t.Fatalf("Failed making http.Request: %v", err) + } + rw := httptest.NewRecorder() + runFunc(c.ctx, rw, req) + + if reqFuncRuns != 1 { + t.Errorf("reqFuncRuns: got %d, want 1", reqFuncRuns) + } + if reqFuncHeaders.TaskName != "foobar" { + t.Errorf("reqFuncHeaders.TaskName: got %v, want 'foobar'", reqFuncHeaders.TaskName) + } + if reqFuncErr != nil { + t.Errorf("reqFuncErr: got %v, want nil", reqFuncErr) + } +} diff --git a/vendor/google.golang.org/appengine/demos/guestbook/app.yaml b/vendor/google.golang.org/appengine/demos/guestbook/app.yaml new file mode 100644 index 0000000000..3342503328 --- /dev/null +++ b/vendor/google.golang.org/appengine/demos/guestbook/app.yaml @@ -0,0 +1,14 @@ +# Demo application for App Engine "flexible environment". +runtime: go +vm: true +api_version: go1 + +handlers: +# Favicon. Without this, the browser hits this once per page view. +- url: /favicon.ico + static_files: favicon.ico + upload: favicon.ico + +# Main app. All the real work is here. +- url: /.* + script: _go_app diff --git a/vendor/google.golang.org/appengine/demos/guestbook/favicon.ico b/vendor/google.golang.org/appengine/demos/guestbook/favicon.ico new file mode 100644 index 0000000000..1a71ea772e Binary files /dev/null and b/vendor/google.golang.org/appengine/demos/guestbook/favicon.ico differ diff --git a/vendor/google.golang.org/appengine/demos/guestbook/guestbook.go b/vendor/google.golang.org/appengine/demos/guestbook/guestbook.go new file mode 100644 index 0000000000..04a0432bb4 --- /dev/null +++ b/vendor/google.golang.org/appengine/demos/guestbook/guestbook.go @@ -0,0 +1,109 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// This example only works on App Engine "flexible environment". +// +build !appengine + +package main + +import ( + "html/template" + "net/http" + "time" + + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/datastore" + "google.golang.org/appengine/log" + "google.golang.org/appengine/user" +) + +var initTime time.Time + +type Greeting struct { + Author string + Content string + Date time.Time +} + +func main() { + http.HandleFunc("/", handleMainPage) + http.HandleFunc("/sign", handleSign) + appengine.Main() +} + +// guestbookKey returns the key used for all guestbook entries. +func guestbookKey(ctx context.Context) *datastore.Key { + // The string "default_guestbook" here could be varied to have multiple guestbooks. + return datastore.NewKey(ctx, "Guestbook", "default_guestbook", 0, nil) +} + +var tpl = template.Must(template.ParseGlob("templates/*.html")) + +func handleMainPage(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + http.Error(w, "GET requests only", http.StatusMethodNotAllowed) + return + } + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + ctx := appengine.NewContext(r) + tic := time.Now() + q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(ctx)).Order("-Date").Limit(10) + var gg []*Greeting + if _, err := q.GetAll(ctx, &gg); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + log.Errorf(ctx, "GetAll: %v", err) + return + } + log.Infof(ctx, "Datastore lookup took %s", time.Since(tic).String()) + log.Infof(ctx, "Rendering %d greetings", len(gg)) + + var email, logout, login string + if u := user.Current(ctx); u != nil { + logout, _ = user.LogoutURL(ctx, "/") + email = u.Email + } else { + login, _ = user.LoginURL(ctx, "/") + } + data := struct { + Greetings []*Greeting + Login, Logout, Email string + }{ + Greetings: gg, + Login: login, + Logout: logout, + Email: email, + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := tpl.ExecuteTemplate(w, "guestbook.html", data); err != nil { + log.Errorf(ctx, "%v", err) + } +} + +func handleSign(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "POST requests only", http.StatusMethodNotAllowed) + return + } + ctx := appengine.NewContext(r) + g := &Greeting{ + Content: r.FormValue("content"), + Date: time.Now(), + } + if u := user.Current(ctx); u != nil { + g.Author = u.String() + } + key := datastore.NewIncompleteKey(ctx, "Greeting", guestbookKey(ctx)) + if _, err := datastore.Put(ctx, key, g); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Redirect with 303 which causes the subsequent request to use GET. + http.Redirect(w, r, "/", http.StatusSeeOther) +} diff --git a/vendor/google.golang.org/appengine/demos/guestbook/index.yaml b/vendor/google.golang.org/appengine/demos/guestbook/index.yaml new file mode 100644 index 0000000000..315ffeb0e4 --- /dev/null +++ b/vendor/google.golang.org/appengine/demos/guestbook/index.yaml @@ -0,0 +1,7 @@ +indexes: + +- kind: Greeting + ancestor: yes + properties: + - name: Date + direction: desc diff --git a/vendor/google.golang.org/appengine/demos/guestbook/templates/guestbook.html b/vendor/google.golang.org/appengine/demos/guestbook/templates/guestbook.html new file mode 100644 index 0000000000..322b7cf63c --- /dev/null +++ b/vendor/google.golang.org/appengine/demos/guestbook/templates/guestbook.html @@ -0,0 +1,26 @@ + + + + Guestbook Demo + + +

+ {{with .Email}}You are currently logged in as {{.}}.{{end}} + {{with .Login}}Sign in{{end}} + {{with .Logout}}Sign out{{end}} +

+ + {{range .Greetings }} +

+ {{with .Author}}{{.}}{{else}}An anonymous person{{end}} + on {{.Date.Format "3:04pm, Mon 2 Jan"}} + wrote

{{.Content}}
+

+ {{end}} + +
+
+
+
+ + diff --git a/vendor/google.golang.org/appengine/demos/helloworld/app.yaml b/vendor/google.golang.org/appengine/demos/helloworld/app.yaml new file mode 100644 index 0000000000..15091192f7 --- /dev/null +++ b/vendor/google.golang.org/appengine/demos/helloworld/app.yaml @@ -0,0 +1,10 @@ +runtime: go +api_version: go1 +vm: true + +handlers: +- url: /favicon.ico + static_files: favicon.ico + upload: favicon.ico +- url: /.* + script: _go_app diff --git a/vendor/google.golang.org/appengine/demos/helloworld/favicon.ico b/vendor/google.golang.org/appengine/demos/helloworld/favicon.ico new file mode 100644 index 0000000000..f19c04d270 Binary files /dev/null and b/vendor/google.golang.org/appengine/demos/helloworld/favicon.ico differ diff --git a/vendor/google.golang.org/appengine/demos/helloworld/helloworld.go b/vendor/google.golang.org/appengine/demos/helloworld/helloworld.go new file mode 100644 index 0000000000..fbe9f56ed8 --- /dev/null +++ b/vendor/google.golang.org/appengine/demos/helloworld/helloworld.go @@ -0,0 +1,50 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// This example only works on App Engine "flexible environment". +// +build !appengine + +package main + +import ( + "html/template" + "net/http" + "time" + + "google.golang.org/appengine" + "google.golang.org/appengine/log" +) + +var initTime = time.Now() + +func main() { + http.HandleFunc("/", handle) + appengine.Main() +} + +func handle(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + ctx := appengine.NewContext(r) + log.Infof(ctx, "Serving the front page.") + + tmpl.Execute(w, time.Since(initTime)) +} + +var tmpl = template.Must(template.New("front").Parse(` + + +

+Hello, World! 세상아 안녕! +

+ +

+This instance has been running for {{.}}. +

+ + +`)) diff --git a/vendor/google.golang.org/appengine/errors.go b/vendor/google.golang.org/appengine/errors.go new file mode 100644 index 0000000000..16d0772e2a --- /dev/null +++ b/vendor/google.golang.org/appengine/errors.go @@ -0,0 +1,46 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// This file provides error functions for common API failure modes. + +package appengine + +import ( + "fmt" + + "google.golang.org/appengine/internal" +) + +// IsOverQuota reports whether err represents an API call failure +// due to insufficient available quota. +func IsOverQuota(err error) bool { + callErr, ok := err.(*internal.CallError) + return ok && callErr.Code == 4 +} + +// MultiError is returned by batch operations when there are errors with +// particular elements. Errors will be in a one-to-one correspondence with +// the input elements; successful elements will have a nil entry. +type MultiError []error + +func (m MultiError) Error() string { + s, n := "", 0 + for _, e := range m { + if e != nil { + if n == 0 { + s = e.Error() + } + n++ + } + } + switch n { + case 0: + return "(0 errors)" + case 1: + return s + case 2: + return s + " (and 1 other error)" + } + return fmt.Sprintf("%s (and %d other errors)", s, n-1) +} diff --git a/vendor/google.golang.org/appengine/file/file.go b/vendor/google.golang.org/appengine/file/file.go new file mode 100644 index 0000000000..c3cd58baf0 --- /dev/null +++ b/vendor/google.golang.org/appengine/file/file.go @@ -0,0 +1,28 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package file provides helper functions for using Google Cloud Storage. +package file + +import ( + "fmt" + + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + aipb "google.golang.org/appengine/internal/app_identity" +) + +// DefaultBucketName returns the name of this application's +// default Google Cloud Storage bucket. +func DefaultBucketName(c context.Context) (string, error) { + req := &aipb.GetDefaultGcsBucketNameRequest{} + res := &aipb.GetDefaultGcsBucketNameResponse{} + + err := internal.Call(c, "app_identity_service", "GetDefaultGcsBucketName", req, res) + if err != nil { + return "", fmt.Errorf("file: no default bucket name returned in RPC response: %v", res) + } + return res.GetDefaultGcsBucketName(), nil +} diff --git a/vendor/google.golang.org/appengine/identity.go b/vendor/google.golang.org/appengine/identity.go new file mode 100644 index 0000000000..b8dcf8f361 --- /dev/null +++ b/vendor/google.golang.org/appengine/identity.go @@ -0,0 +1,142 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package appengine + +import ( + "time" + + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/app_identity" + modpb "google.golang.org/appengine/internal/modules" +) + +// AppID returns the application ID for the current application. +// The string will be a plain application ID (e.g. "appid"), with a +// domain prefix for custom domain deployments (e.g. "example.com:appid"). +func AppID(c context.Context) string { return internal.AppID(c) } + +// DefaultVersionHostname returns the standard hostname of the default version +// of the current application (e.g. "my-app.appspot.com"). This is suitable for +// use in constructing URLs. +func DefaultVersionHostname(c context.Context) string { + return internal.DefaultVersionHostname(c) +} + +// ModuleName returns the module name of the current instance. +func ModuleName(c context.Context) string { + return internal.ModuleName(c) +} + +// ModuleHostname returns a hostname of a module instance. +// If module is the empty string, it refers to the module of the current instance. +// If version is empty, it refers to the version of the current instance if valid, +// or the default version of the module of the current instance. +// If instance is empty, ModuleHostname returns the load-balancing hostname. +func ModuleHostname(c context.Context, module, version, instance string) (string, error) { + req := &modpb.GetHostnameRequest{} + if module != "" { + req.Module = &module + } + if version != "" { + req.Version = &version + } + if instance != "" { + req.Instance = &instance + } + res := &modpb.GetHostnameResponse{} + if err := internal.Call(c, "modules", "GetHostname", req, res); err != nil { + return "", err + } + return *res.Hostname, nil +} + +// VersionID returns the version ID for the current application. +// It will be of the form "X.Y", where X is specified in app.yaml, +// and Y is a number generated when each version of the app is uploaded. +// It does not include a module name. +func VersionID(c context.Context) string { return internal.VersionID(c) } + +// InstanceID returns a mostly-unique identifier for this instance. +func InstanceID() string { return internal.InstanceID() } + +// Datacenter returns an identifier for the datacenter that the instance is running in. +func Datacenter(c context.Context) string { return internal.Datacenter(c) } + +// ServerSoftware returns the App Engine release version. +// In production, it looks like "Google App Engine/X.Y.Z". +// In the development appserver, it looks like "Development/X.Y". +func ServerSoftware() string { return internal.ServerSoftware() } + +// RequestID returns a string that uniquely identifies the request. +func RequestID(c context.Context) string { return internal.RequestID(c) } + +// AccessToken generates an OAuth2 access token for the specified scopes on +// behalf of service account of this application. This token will expire after +// the returned time. +func AccessToken(c context.Context, scopes ...string) (token string, expiry time.Time, err error) { + req := &pb.GetAccessTokenRequest{Scope: scopes} + res := &pb.GetAccessTokenResponse{} + + err = internal.Call(c, "app_identity_service", "GetAccessToken", req, res) + if err != nil { + return "", time.Time{}, err + } + return res.GetAccessToken(), time.Unix(res.GetExpirationTime(), 0), nil +} + +// Certificate represents a public certificate for the app. +type Certificate struct { + KeyName string + Data []byte // PEM-encoded X.509 certificate +} + +// PublicCertificates retrieves the public certificates for the app. +// They can be used to verify a signature returned by SignBytes. +func PublicCertificates(c context.Context) ([]Certificate, error) { + req := &pb.GetPublicCertificateForAppRequest{} + res := &pb.GetPublicCertificateForAppResponse{} + if err := internal.Call(c, "app_identity_service", "GetPublicCertificatesForApp", req, res); err != nil { + return nil, err + } + var cs []Certificate + for _, pc := range res.PublicCertificateList { + cs = append(cs, Certificate{ + KeyName: pc.GetKeyName(), + Data: []byte(pc.GetX509CertificatePem()), + }) + } + return cs, nil +} + +// ServiceAccount returns a string representing the service account name, in +// the form of an email address (typically app_id@appspot.gserviceaccount.com). +func ServiceAccount(c context.Context) (string, error) { + req := &pb.GetServiceAccountNameRequest{} + res := &pb.GetServiceAccountNameResponse{} + + err := internal.Call(c, "app_identity_service", "GetServiceAccountName", req, res) + if err != nil { + return "", err + } + return res.GetServiceAccountName(), err +} + +// SignBytes signs bytes using a private key unique to your application. +func SignBytes(c context.Context, bytes []byte) (keyName string, signature []byte, err error) { + req := &pb.SignForAppRequest{BytesToSign: bytes} + res := &pb.SignForAppResponse{} + + if err := internal.Call(c, "app_identity_service", "SignForApp", req, res); err != nil { + return "", nil, err + } + return res.GetKeyName(), res.GetSignatureBytes(), nil +} + +func init() { + internal.RegisterErrorCodeMap("app_identity_service", pb.AppIdentityServiceError_ErrorCode_name) + internal.RegisterErrorCodeMap("modules", modpb.ModulesServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/image/image.go b/vendor/google.golang.org/appengine/image/image.go new file mode 100644 index 0000000000..027a41b702 --- /dev/null +++ b/vendor/google.golang.org/appengine/image/image.go @@ -0,0 +1,67 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package image provides image services. +package image // import "google.golang.org/appengine/image" + +import ( + "fmt" + "net/url" + + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/image" +) + +type ServingURLOptions struct { + Secure bool // whether the URL should use HTTPS + + // Size must be between zero and 1600. + // If Size is non-zero, a resized version of the image is served, + // and Size is the served image's longest dimension. The aspect ratio is preserved. + // If Crop is true the image is cropped from the center instead of being resized. + Size int + Crop bool +} + +// ServingURL returns a URL that will serve an image from Blobstore. +func ServingURL(c context.Context, key appengine.BlobKey, opts *ServingURLOptions) (*url.URL, error) { + req := &pb.ImagesGetUrlBaseRequest{ + BlobKey: (*string)(&key), + } + if opts != nil && opts.Secure { + req.CreateSecureUrl = &opts.Secure + } + res := &pb.ImagesGetUrlBaseResponse{} + if err := internal.Call(c, "images", "GetUrlBase", req, res); err != nil { + return nil, err + } + + // The URL may have suffixes added to dynamically resize or crop: + // - adding "=s32" will serve the image resized to 32 pixels, preserving the aspect ratio. + // - adding "=s32-c" is the same as "=s32" except it will be cropped. + u := *res.Url + if opts != nil && opts.Size > 0 { + u += fmt.Sprintf("=s%d", opts.Size) + if opts.Crop { + u += "-c" + } + } + return url.Parse(u) +} + +// DeleteServingURL deletes the serving URL for an image. +func DeleteServingURL(c context.Context, key appengine.BlobKey) error { + req := &pb.ImagesDeleteUrlBaseRequest{ + BlobKey: (*string)(&key), + } + res := &pb.ImagesDeleteUrlBaseResponse{} + return internal.Call(c, "images", "DeleteUrlBase", req, res) +} + +func init() { + internal.RegisterErrorCodeMap("images", pb.ImagesServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/internal/aetesting/fake.go b/vendor/google.golang.org/appengine/internal/aetesting/fake.go new file mode 100644 index 0000000000..eb5b2c65b6 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/aetesting/fake.go @@ -0,0 +1,81 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package aetesting provides utilities for testing App Engine packages. +// This is not for testing user applications. +package aetesting + +import ( + "fmt" + "net/http" + "reflect" + "testing" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +// FakeSingleContext returns a context whose Call invocations will be serviced +// by f, which should be a function that has two arguments of the input and output +// protocol buffer type, and one error return. +func FakeSingleContext(t *testing.T, service, method string, f interface{}) context.Context { + fv := reflect.ValueOf(f) + if fv.Kind() != reflect.Func { + t.Fatal("not a function") + } + ft := fv.Type() + if ft.NumIn() != 2 || ft.NumOut() != 1 { + t.Fatalf("f has %d in and %d out, want 2 in and 1 out", ft.NumIn(), ft.NumOut()) + } + for i := 0; i < 2; i++ { + at := ft.In(i) + if !at.Implements(protoMessageType) { + t.Fatalf("arg %d does not implement proto.Message", i) + } + } + if ft.Out(0) != errorType { + t.Fatalf("f's return is %v, want error", ft.Out(0)) + } + s := &single{ + t: t, + service: service, + method: method, + f: fv, + } + return internal.WithCallOverride(internal.ContextForTesting(&http.Request{}), s.call) +} + +var ( + protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem() + errorType = reflect.TypeOf((*error)(nil)).Elem() +) + +type single struct { + t *testing.T + service, method string + f reflect.Value +} + +func (s *single) call(ctx context.Context, service, method string, in, out proto.Message) error { + if service == "__go__" { + if method == "GetNamespace" { + return nil // always yield an empty namespace + } + return fmt.Errorf("Unknown API call /%s.%s", service, method) + } + if service != s.service || method != s.method { + s.t.Fatalf("Unexpected call to /%s.%s", service, method) + } + ins := []reflect.Value{ + reflect.ValueOf(in), + reflect.ValueOf(out), + } + outs := s.f.Call(ins) + if outs[0].IsNil() { + return nil + } + return outs[0].Interface().(error) +} diff --git a/vendor/google.golang.org/appengine/internal/api.go b/vendor/google.golang.org/appengine/internal/api.go new file mode 100644 index 0000000000..16f87c5d37 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/api.go @@ -0,0 +1,660 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine +// +build go1.7 + +package internal + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "net/url" + "os" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/golang/protobuf/proto" + netcontext "golang.org/x/net/context" + + basepb "google.golang.org/appengine/internal/base" + logpb "google.golang.org/appengine/internal/log" + remotepb "google.golang.org/appengine/internal/remote_api" +) + +const ( + apiPath = "/rpc_http" + defaultTicketSuffix = "/default.20150612t184001.0" +) + +var ( + // Incoming headers. + ticketHeader = http.CanonicalHeaderKey("X-AppEngine-API-Ticket") + dapperHeader = http.CanonicalHeaderKey("X-Google-DapperTraceInfo") + traceHeader = http.CanonicalHeaderKey("X-Cloud-Trace-Context") + curNamespaceHeader = http.CanonicalHeaderKey("X-AppEngine-Current-Namespace") + userIPHeader = http.CanonicalHeaderKey("X-AppEngine-User-IP") + remoteAddrHeader = http.CanonicalHeaderKey("X-AppEngine-Remote-Addr") + + // Outgoing headers. + apiEndpointHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Endpoint") + apiEndpointHeaderValue = []string{"app-engine-apis"} + apiMethodHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Method") + apiMethodHeaderValue = []string{"/VMRemoteAPI.CallRemoteAPI"} + apiDeadlineHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline") + apiContentType = http.CanonicalHeaderKey("Content-Type") + apiContentTypeValue = []string{"application/octet-stream"} + logFlushHeader = http.CanonicalHeaderKey("X-AppEngine-Log-Flush-Count") + + apiHTTPClient = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: limitDial, + }, + } + + defaultTicketOnce sync.Once + defaultTicket string + backgroundContextOnce sync.Once + backgroundContext netcontext.Context +) + +func apiURL() *url.URL { + host, port := "appengine.googleapis.internal", "10001" + if h := os.Getenv("API_HOST"); h != "" { + host = h + } + if p := os.Getenv("API_PORT"); p != "" { + port = p + } + return &url.URL{ + Scheme: "http", + Host: host + ":" + port, + Path: apiPath, + } +} + +func handleHTTP(w http.ResponseWriter, r *http.Request) { + c := &context{ + req: r, + outHeader: w.Header(), + apiURL: apiURL(), + } + r = r.WithContext(withContext(r.Context(), c)) + c.req = r + + stopFlushing := make(chan int) + + // Patch up RemoteAddr so it looks reasonable. + if addr := r.Header.Get(userIPHeader); addr != "" { + r.RemoteAddr = addr + } else if addr = r.Header.Get(remoteAddrHeader); addr != "" { + r.RemoteAddr = addr + } else { + // Should not normally reach here, but pick a sensible default anyway. + r.RemoteAddr = "127.0.0.1" + } + // The address in the headers will most likely be of these forms: + // 123.123.123.123 + // 2001:db8::1 + // net/http.Request.RemoteAddr is specified to be in "IP:port" form. + if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil { + // Assume the remote address is only a host; add a default port. + r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") + } + + // Start goroutine responsible for flushing app logs. + // This is done after adding c to ctx.m (and stopped before removing it) + // because flushing logs requires making an API call. + go c.logFlusher(stopFlushing) + + executeRequestSafely(c, r) + c.outHeader = nil // make sure header changes aren't respected any more + + stopFlushing <- 1 // any logging beyond this point will be dropped + + // Flush any pending logs asynchronously. + c.pendingLogs.Lock() + flushes := c.pendingLogs.flushes + if len(c.pendingLogs.lines) > 0 { + flushes++ + } + c.pendingLogs.Unlock() + go c.flushLog(false) + w.Header().Set(logFlushHeader, strconv.Itoa(flushes)) + + // Avoid nil Write call if c.Write is never called. + if c.outCode != 0 { + w.WriteHeader(c.outCode) + } + if c.outBody != nil { + w.Write(c.outBody) + } +} + +func executeRequestSafely(c *context, r *http.Request) { + defer func() { + if x := recover(); x != nil { + logf(c, 4, "%s", renderPanic(x)) // 4 == critical + c.outCode = 500 + } + }() + + http.DefaultServeMux.ServeHTTP(c, r) +} + +func renderPanic(x interface{}) string { + buf := make([]byte, 16<<10) // 16 KB should be plenty + buf = buf[:runtime.Stack(buf, false)] + + // Remove the first few stack frames: + // this func + // the recover closure in the caller + // That will root the stack trace at the site of the panic. + const ( + skipStart = "internal.renderPanic" + skipFrames = 2 + ) + start := bytes.Index(buf, []byte(skipStart)) + p := start + for i := 0; i < skipFrames*2 && p+1 < len(buf); i++ { + p = bytes.IndexByte(buf[p+1:], '\n') + p + 1 + if p < 0 { + break + } + } + if p >= 0 { + // buf[start:p+1] is the block to remove. + // Copy buf[p+1:] over buf[start:] and shrink buf. + copy(buf[start:], buf[p+1:]) + buf = buf[:len(buf)-(p+1-start)] + } + + // Add panic heading. + head := fmt.Sprintf("panic: %v\n\n", x) + if len(head) > len(buf) { + // Extremely unlikely to happen. + return head + } + copy(buf[len(head):], buf) + copy(buf, head) + + return string(buf) +} + +// context represents the context of an in-flight HTTP request. +// It implements the appengine.Context and http.ResponseWriter interfaces. +type context struct { + req *http.Request + + outCode int + outHeader http.Header + outBody []byte + + pendingLogs struct { + sync.Mutex + lines []*logpb.UserAppLogLine + flushes int + } + + apiURL *url.URL +} + +var contextKey = "holds a *context" + +// jointContext joins two contexts in a superficial way. +// It takes values and timeouts from a base context, and only values from another context. +type jointContext struct { + base netcontext.Context + valuesOnly netcontext.Context +} + +func (c jointContext) Deadline() (time.Time, bool) { + return c.base.Deadline() +} + +func (c jointContext) Done() <-chan struct{} { + return c.base.Done() +} + +func (c jointContext) Err() error { + return c.base.Err() +} + +func (c jointContext) Value(key interface{}) interface{} { + if val := c.base.Value(key); val != nil { + return val + } + return c.valuesOnly.Value(key) +} + +// fromContext returns the App Engine context or nil if ctx is not +// derived from an App Engine context. +func fromContext(ctx netcontext.Context) *context { + c, _ := ctx.Value(&contextKey).(*context) + return c +} + +func withContext(parent netcontext.Context, c *context) netcontext.Context { + ctx := netcontext.WithValue(parent, &contextKey, c) + if ns := c.req.Header.Get(curNamespaceHeader); ns != "" { + ctx = withNamespace(ctx, ns) + } + return ctx +} + +func toContext(c *context) netcontext.Context { + return withContext(netcontext.Background(), c) +} + +func IncomingHeaders(ctx netcontext.Context) http.Header { + if c := fromContext(ctx); c != nil { + return c.req.Header + } + return nil +} + +func ReqContext(req *http.Request) netcontext.Context { + return req.Context() +} + +func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { + return jointContext{ + base: parent, + valuesOnly: req.Context(), + } +} + +// DefaultTicket returns a ticket used for background context or dev_appserver. +func DefaultTicket() string { + defaultTicketOnce.Do(func() { + if IsDevAppServer() { + defaultTicket = "testapp" + defaultTicketSuffix + return + } + appID := partitionlessAppID() + escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1) + majVersion := VersionID(nil) + if i := strings.Index(majVersion, "."); i > 0 { + majVersion = majVersion[:i] + } + defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID()) + }) + return defaultTicket +} + +func BackgroundContext() netcontext.Context { + backgroundContextOnce.Do(func() { + // Compute background security ticket. + ticket := DefaultTicket() + + c := &context{ + req: &http.Request{ + Header: http.Header{ + ticketHeader: []string{ticket}, + }, + }, + apiURL: apiURL(), + } + backgroundContext = toContext(c) + + // TODO(dsymonds): Wire up the shutdown handler to do a final flush. + go c.logFlusher(make(chan int)) + }) + + return backgroundContext +} + +// RegisterTestRequest registers the HTTP request req for testing, such that +// any API calls are sent to the provided URL. It returns a closure to delete +// the registration. +// It should only be used by aetest package. +func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netcontext.Context) netcontext.Context) (*http.Request, func()) { + c := &context{ + req: req, + apiURL: apiURL, + } + ctx := withContext(decorate(req.Context()), c) + req = req.WithContext(ctx) + c.req = req + return req, func() {} +} + +var errTimeout = &CallError{ + Detail: "Deadline exceeded", + Code: int32(remotepb.RpcError_CANCELLED), + Timeout: true, +} + +func (c *context) Header() http.Header { return c.outHeader } + +// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status +// codes do not permit a response body (nor response entity headers such as +// Content-Length, Content-Type, etc). +func bodyAllowedForStatus(status int) bool { + switch { + case status >= 100 && status <= 199: + return false + case status == 204: + return false + case status == 304: + return false + } + return true +} + +func (c *context) Write(b []byte) (int, error) { + if c.outCode == 0 { + c.WriteHeader(http.StatusOK) + } + if len(b) > 0 && !bodyAllowedForStatus(c.outCode) { + return 0, http.ErrBodyNotAllowed + } + c.outBody = append(c.outBody, b...) + return len(b), nil +} + +func (c *context) WriteHeader(code int) { + if c.outCode != 0 { + logf(c, 3, "WriteHeader called multiple times on request.") // error level + return + } + c.outCode = code +} + +func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) { + hreq := &http.Request{ + Method: "POST", + URL: c.apiURL, + Header: http.Header{ + apiEndpointHeader: apiEndpointHeaderValue, + apiMethodHeader: apiMethodHeaderValue, + apiContentType: apiContentTypeValue, + apiDeadlineHeader: []string{strconv.FormatFloat(timeout.Seconds(), 'f', -1, 64)}, + }, + Body: ioutil.NopCloser(bytes.NewReader(body)), + ContentLength: int64(len(body)), + Host: c.apiURL.Host, + } + if info := c.req.Header.Get(dapperHeader); info != "" { + hreq.Header.Set(dapperHeader, info) + } + if info := c.req.Header.Get(traceHeader); info != "" { + hreq.Header.Set(traceHeader, info) + } + + tr := apiHTTPClient.Transport.(*http.Transport) + + var timedOut int32 // atomic; set to 1 if timed out + t := time.AfterFunc(timeout, func() { + atomic.StoreInt32(&timedOut, 1) + tr.CancelRequest(hreq) + }) + defer t.Stop() + defer func() { + // Check if timeout was exceeded. + if atomic.LoadInt32(&timedOut) != 0 { + err = errTimeout + } + }() + + hresp, err := apiHTTPClient.Do(hreq) + if err != nil { + return nil, &CallError{ + Detail: fmt.Sprintf("service bridge HTTP failed: %v", err), + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + defer hresp.Body.Close() + hrespBody, err := ioutil.ReadAll(hresp.Body) + if hresp.StatusCode != 200 { + return nil, &CallError{ + Detail: fmt.Sprintf("service bridge returned HTTP %d (%q)", hresp.StatusCode, hrespBody), + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + if err != nil { + return nil, &CallError{ + Detail: fmt.Sprintf("service bridge response bad: %v", err), + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + return hrespBody, nil +} + +func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error { + if ns := NamespaceFromContext(ctx); ns != "" { + if fn, ok := NamespaceMods[service]; ok { + fn(in, ns) + } + } + + if f, ctx, ok := callOverrideFromContext(ctx); ok { + return f(ctx, service, method, in, out) + } + + // Handle already-done contexts quickly. + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + c := fromContext(ctx) + if c == nil { + // Give a good error message rather than a panic lower down. + return errNotAppEngineContext + } + + // Apply transaction modifications if we're in a transaction. + if t := transactionFromContext(ctx); t != nil { + if t.finished { + return errors.New("transaction context has expired") + } + applyTransaction(in, &t.transaction) + } + + // Default RPC timeout is 60s. + timeout := 60 * time.Second + if deadline, ok := ctx.Deadline(); ok { + timeout = deadline.Sub(time.Now()) + } + + data, err := proto.Marshal(in) + if err != nil { + return err + } + + ticket := c.req.Header.Get(ticketHeader) + // Use a test ticket under test environment. + if ticket == "" { + if appid := ctx.Value(&appIDOverrideKey); appid != nil { + ticket = appid.(string) + defaultTicketSuffix + } + } + // Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver. + if ticket == "" { + ticket = DefaultTicket() + } + req := &remotepb.Request{ + ServiceName: &service, + Method: &method, + Request: data, + RequestId: &ticket, + } + hreqBody, err := proto.Marshal(req) + if err != nil { + return err + } + + hrespBody, err := c.post(hreqBody, timeout) + if err != nil { + return err + } + + res := &remotepb.Response{} + if err := proto.Unmarshal(hrespBody, res); err != nil { + return err + } + if res.RpcError != nil { + ce := &CallError{ + Detail: res.RpcError.GetDetail(), + Code: *res.RpcError.Code, + } + switch remotepb.RpcError_ErrorCode(ce.Code) { + case remotepb.RpcError_CANCELLED, remotepb.RpcError_DEADLINE_EXCEEDED: + ce.Timeout = true + } + return ce + } + if res.ApplicationError != nil { + return &APIError{ + Service: *req.ServiceName, + Detail: res.ApplicationError.GetDetail(), + Code: *res.ApplicationError.Code, + } + } + if res.Exception != nil || res.JavaException != nil { + // This shouldn't happen, but let's be defensive. + return &CallError{ + Detail: "service bridge returned exception", + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + return proto.Unmarshal(res.Response, out) +} + +func (c *context) Request() *http.Request { + return c.req +} + +func (c *context) addLogLine(ll *logpb.UserAppLogLine) { + // Truncate long log lines. + // TODO(dsymonds): Check if this is still necessary. + const lim = 8 << 10 + if len(*ll.Message) > lim { + suffix := fmt.Sprintf("...(length %d)", len(*ll.Message)) + ll.Message = proto.String((*ll.Message)[:lim-len(suffix)] + suffix) + } + + c.pendingLogs.Lock() + c.pendingLogs.lines = append(c.pendingLogs.lines, ll) + c.pendingLogs.Unlock() +} + +var logLevelName = map[int64]string{ + 0: "DEBUG", + 1: "INFO", + 2: "WARNING", + 3: "ERROR", + 4: "CRITICAL", +} + +func logf(c *context, level int64, format string, args ...interface{}) { + if c == nil { + panic("not an App Engine context") + } + s := fmt.Sprintf(format, args...) + s = strings.TrimRight(s, "\n") // Remove any trailing newline characters. + c.addLogLine(&logpb.UserAppLogLine{ + TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3), + Level: &level, + Message: &s, + }) + log.Print(logLevelName[level] + ": " + s) +} + +// flushLog attempts to flush any pending logs to the appserver. +// It should not be called concurrently. +func (c *context) flushLog(force bool) (flushed bool) { + c.pendingLogs.Lock() + // Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious. + n, rem := 0, 30<<20 + for ; n < len(c.pendingLogs.lines); n++ { + ll := c.pendingLogs.lines[n] + // Each log line will require about 3 bytes of overhead. + nb := proto.Size(ll) + 3 + if nb > rem { + break + } + rem -= nb + } + lines := c.pendingLogs.lines[:n] + c.pendingLogs.lines = c.pendingLogs.lines[n:] + c.pendingLogs.Unlock() + + if len(lines) == 0 && !force { + // Nothing to flush. + return false + } + + rescueLogs := false + defer func() { + if rescueLogs { + c.pendingLogs.Lock() + c.pendingLogs.lines = append(lines, c.pendingLogs.lines...) + c.pendingLogs.Unlock() + } + }() + + buf, err := proto.Marshal(&logpb.UserAppLogGroup{ + LogLine: lines, + }) + if err != nil { + log.Printf("internal.flushLog: marshaling UserAppLogGroup: %v", err) + rescueLogs = true + return false + } + + req := &logpb.FlushRequest{ + Logs: buf, + } + res := &basepb.VoidProto{} + c.pendingLogs.Lock() + c.pendingLogs.flushes++ + c.pendingLogs.Unlock() + if err := Call(toContext(c), "logservice", "Flush", req, res); err != nil { + log.Printf("internal.flushLog: Flush RPC: %v", err) + rescueLogs = true + return false + } + return true +} + +const ( + // Log flushing parameters. + flushInterval = 1 * time.Second + forceFlushInterval = 60 * time.Second +) + +func (c *context) logFlusher(stop <-chan int) { + lastFlush := time.Now() + tick := time.NewTicker(flushInterval) + for { + select { + case <-stop: + // Request finished. + tick.Stop() + return + case <-tick.C: + force := time.Now().Sub(lastFlush) > forceFlushInterval + if c.flushLog(force) { + lastFlush = time.Now() + } + } + } +} + +func ContextForTesting(req *http.Request) netcontext.Context { + return toContext(&context{req: req}) +} diff --git a/vendor/google.golang.org/appengine/internal/api_classic.go b/vendor/google.golang.org/appengine/internal/api_classic.go new file mode 100644 index 0000000000..f0f40b2e35 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/api_classic.go @@ -0,0 +1,169 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build appengine + +package internal + +import ( + "errors" + "fmt" + "net/http" + "time" + + "appengine" + "appengine_internal" + basepb "appengine_internal/base" + + "github.com/golang/protobuf/proto" + netcontext "golang.org/x/net/context" +) + +var contextKey = "holds an appengine.Context" + +// fromContext returns the App Engine context or nil if ctx is not +// derived from an App Engine context. +func fromContext(ctx netcontext.Context) appengine.Context { + c, _ := ctx.Value(&contextKey).(appengine.Context) + return c +} + +// This is only for classic App Engine adapters. +func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error) { + c := fromContext(ctx) + if c == nil { + return nil, errNotAppEngineContext + } + return c, nil +} + +func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context { + ctx := netcontext.WithValue(parent, &contextKey, c) + + s := &basepb.StringProto{} + c.Call("__go__", "GetNamespace", &basepb.VoidProto{}, s, nil) + if ns := s.GetValue(); ns != "" { + ctx = NamespacedContext(ctx, ns) + } + + return ctx +} + +func IncomingHeaders(ctx netcontext.Context) http.Header { + if c := fromContext(ctx); c != nil { + if req, ok := c.Request().(*http.Request); ok { + return req.Header + } + } + return nil +} + +func ReqContext(req *http.Request) netcontext.Context { + return WithContext(netcontext.Background(), req) +} + +func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { + c := appengine.NewContext(req) + return withContext(parent, c) +} + +type testingContext struct { + appengine.Context + + req *http.Request +} + +func (t *testingContext) FullyQualifiedAppID() string { return "dev~testcontext" } +func (t *testingContext) Call(service, method string, _, _ appengine_internal.ProtoMessage, _ *appengine_internal.CallOptions) error { + if service == "__go__" && method == "GetNamespace" { + return nil + } + return fmt.Errorf("testingContext: unsupported Call") +} +func (t *testingContext) Request() interface{} { return t.req } + +func ContextForTesting(req *http.Request) netcontext.Context { + return withContext(netcontext.Background(), &testingContext{req: req}) +} + +func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error { + if ns := NamespaceFromContext(ctx); ns != "" { + if fn, ok := NamespaceMods[service]; ok { + fn(in, ns) + } + } + + if f, ctx, ok := callOverrideFromContext(ctx); ok { + return f(ctx, service, method, in, out) + } + + // Handle already-done contexts quickly. + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + c := fromContext(ctx) + if c == nil { + // Give a good error message rather than a panic lower down. + return errNotAppEngineContext + } + + // Apply transaction modifications if we're in a transaction. + if t := transactionFromContext(ctx); t != nil { + if t.finished { + return errors.New("transaction context has expired") + } + applyTransaction(in, &t.transaction) + } + + var opts *appengine_internal.CallOptions + if d, ok := ctx.Deadline(); ok { + opts = &appengine_internal.CallOptions{ + Timeout: d.Sub(time.Now()), + } + } + + err := c.Call(service, method, in, out, opts) + switch v := err.(type) { + case *appengine_internal.APIError: + return &APIError{ + Service: v.Service, + Detail: v.Detail, + Code: v.Code, + } + case *appengine_internal.CallError: + return &CallError{ + Detail: v.Detail, + Code: v.Code, + Timeout: v.Timeout, + } + } + return err +} + +func handleHTTP(w http.ResponseWriter, r *http.Request) { + panic("handleHTTP called; this should be impossible") +} + +func logf(c appengine.Context, level int64, format string, args ...interface{}) { + var fn func(format string, args ...interface{}) + switch level { + case 0: + fn = c.Debugf + case 1: + fn = c.Infof + case 2: + fn = c.Warningf + case 3: + fn = c.Errorf + case 4: + fn = c.Criticalf + default: + // This shouldn't happen. + fn = c.Criticalf + } + fn(format, args...) +} diff --git a/vendor/google.golang.org/appengine/internal/api_common.go b/vendor/google.golang.org/appengine/internal/api_common.go new file mode 100644 index 0000000000..e0c0b214b7 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/api_common.go @@ -0,0 +1,123 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package internal + +import ( + "errors" + "os" + + "github.com/golang/protobuf/proto" + netcontext "golang.org/x/net/context" +) + +var errNotAppEngineContext = errors.New("not an App Engine context") + +type CallOverrideFunc func(ctx netcontext.Context, service, method string, in, out proto.Message) error + +var callOverrideKey = "holds []CallOverrideFunc" + +func WithCallOverride(ctx netcontext.Context, f CallOverrideFunc) netcontext.Context { + // We avoid appending to any existing call override + // so we don't risk overwriting a popped stack below. + var cofs []CallOverrideFunc + if uf, ok := ctx.Value(&callOverrideKey).([]CallOverrideFunc); ok { + cofs = append(cofs, uf...) + } + cofs = append(cofs, f) + return netcontext.WithValue(ctx, &callOverrideKey, cofs) +} + +func callOverrideFromContext(ctx netcontext.Context) (CallOverrideFunc, netcontext.Context, bool) { + cofs, _ := ctx.Value(&callOverrideKey).([]CallOverrideFunc) + if len(cofs) == 0 { + return nil, nil, false + } + // We found a list of overrides; grab the last, and reconstitute a + // context that will hide it. + f := cofs[len(cofs)-1] + ctx = netcontext.WithValue(ctx, &callOverrideKey, cofs[:len(cofs)-1]) + return f, ctx, true +} + +type logOverrideFunc func(level int64, format string, args ...interface{}) + +var logOverrideKey = "holds a logOverrideFunc" + +func WithLogOverride(ctx netcontext.Context, f logOverrideFunc) netcontext.Context { + return netcontext.WithValue(ctx, &logOverrideKey, f) +} + +var appIDOverrideKey = "holds a string, being the full app ID" + +func WithAppIDOverride(ctx netcontext.Context, appID string) netcontext.Context { + return netcontext.WithValue(ctx, &appIDOverrideKey, appID) +} + +var namespaceKey = "holds the namespace string" + +func withNamespace(ctx netcontext.Context, ns string) netcontext.Context { + return netcontext.WithValue(ctx, &namespaceKey, ns) +} + +func NamespaceFromContext(ctx netcontext.Context) string { + // If there's no namespace, return the empty string. + ns, _ := ctx.Value(&namespaceKey).(string) + return ns +} + +// FullyQualifiedAppID returns the fully-qualified application ID. +// This may contain a partition prefix (e.g. "s~" for High Replication apps), +// or a domain prefix (e.g. "example.com:"). +func FullyQualifiedAppID(ctx netcontext.Context) string { + if id, ok := ctx.Value(&appIDOverrideKey).(string); ok { + return id + } + return fullyQualifiedAppID(ctx) +} + +func Logf(ctx netcontext.Context, level int64, format string, args ...interface{}) { + if f, ok := ctx.Value(&logOverrideKey).(logOverrideFunc); ok { + f(level, format, args...) + return + } + c := fromContext(ctx) + if c == nil { + panic(errNotAppEngineContext) + } + logf(c, level, format, args...) +} + +// NamespacedContext wraps a Context to support namespaces. +func NamespacedContext(ctx netcontext.Context, namespace string) netcontext.Context { + return withNamespace(ctx, namespace) +} + +// SetTestEnv sets the env variables for testing background ticket in Flex. +func SetTestEnv() func() { + var environ = []struct { + key, value string + }{ + {"GAE_LONG_APP_ID", "my-app-id"}, + {"GAE_MINOR_VERSION", "067924799508853122"}, + {"GAE_MODULE_INSTANCE", "0"}, + {"GAE_MODULE_NAME", "default"}, + {"GAE_MODULE_VERSION", "20150612t184001"}, + } + + for _, v := range environ { + old := os.Getenv(v.key) + os.Setenv(v.key, v.value) + v.value = old + } + return func() { // Restore old environment after the test completes. + for _, v := range environ { + if v.value == "" { + os.Unsetenv(v.key) + continue + } + os.Setenv(v.key, v.value) + } + } +} diff --git a/vendor/google.golang.org/appengine/internal/api_pre17.go b/vendor/google.golang.org/appengine/internal/api_pre17.go new file mode 100644 index 0000000000..028b4f056e --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/api_pre17.go @@ -0,0 +1,682 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine +// +build !go1.7 + +package internal + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "net/url" + "os" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/golang/protobuf/proto" + netcontext "golang.org/x/net/context" + + basepb "google.golang.org/appengine/internal/base" + logpb "google.golang.org/appengine/internal/log" + remotepb "google.golang.org/appengine/internal/remote_api" +) + +const ( + apiPath = "/rpc_http" + defaultTicketSuffix = "/default.20150612t184001.0" +) + +var ( + // Incoming headers. + ticketHeader = http.CanonicalHeaderKey("X-AppEngine-API-Ticket") + dapperHeader = http.CanonicalHeaderKey("X-Google-DapperTraceInfo") + traceHeader = http.CanonicalHeaderKey("X-Cloud-Trace-Context") + curNamespaceHeader = http.CanonicalHeaderKey("X-AppEngine-Current-Namespace") + userIPHeader = http.CanonicalHeaderKey("X-AppEngine-User-IP") + remoteAddrHeader = http.CanonicalHeaderKey("X-AppEngine-Remote-Addr") + + // Outgoing headers. + apiEndpointHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Endpoint") + apiEndpointHeaderValue = []string{"app-engine-apis"} + apiMethodHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Method") + apiMethodHeaderValue = []string{"/VMRemoteAPI.CallRemoteAPI"} + apiDeadlineHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline") + apiContentType = http.CanonicalHeaderKey("Content-Type") + apiContentTypeValue = []string{"application/octet-stream"} + logFlushHeader = http.CanonicalHeaderKey("X-AppEngine-Log-Flush-Count") + + apiHTTPClient = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: limitDial, + }, + } + + defaultTicketOnce sync.Once + defaultTicket string +) + +func apiURL() *url.URL { + host, port := "appengine.googleapis.internal", "10001" + if h := os.Getenv("API_HOST"); h != "" { + host = h + } + if p := os.Getenv("API_PORT"); p != "" { + port = p + } + return &url.URL{ + Scheme: "http", + Host: host + ":" + port, + Path: apiPath, + } +} + +func handleHTTP(w http.ResponseWriter, r *http.Request) { + c := &context{ + req: r, + outHeader: w.Header(), + apiURL: apiURL(), + } + stopFlushing := make(chan int) + + ctxs.Lock() + ctxs.m[r] = c + ctxs.Unlock() + defer func() { + ctxs.Lock() + delete(ctxs.m, r) + ctxs.Unlock() + }() + + // Patch up RemoteAddr so it looks reasonable. + if addr := r.Header.Get(userIPHeader); addr != "" { + r.RemoteAddr = addr + } else if addr = r.Header.Get(remoteAddrHeader); addr != "" { + r.RemoteAddr = addr + } else { + // Should not normally reach here, but pick a sensible default anyway. + r.RemoteAddr = "127.0.0.1" + } + // The address in the headers will most likely be of these forms: + // 123.123.123.123 + // 2001:db8::1 + // net/http.Request.RemoteAddr is specified to be in "IP:port" form. + if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil { + // Assume the remote address is only a host; add a default port. + r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") + } + + // Start goroutine responsible for flushing app logs. + // This is done after adding c to ctx.m (and stopped before removing it) + // because flushing logs requires making an API call. + go c.logFlusher(stopFlushing) + + executeRequestSafely(c, r) + c.outHeader = nil // make sure header changes aren't respected any more + + stopFlushing <- 1 // any logging beyond this point will be dropped + + // Flush any pending logs asynchronously. + c.pendingLogs.Lock() + flushes := c.pendingLogs.flushes + if len(c.pendingLogs.lines) > 0 { + flushes++ + } + c.pendingLogs.Unlock() + go c.flushLog(false) + w.Header().Set(logFlushHeader, strconv.Itoa(flushes)) + + // Avoid nil Write call if c.Write is never called. + if c.outCode != 0 { + w.WriteHeader(c.outCode) + } + if c.outBody != nil { + w.Write(c.outBody) + } +} + +func executeRequestSafely(c *context, r *http.Request) { + defer func() { + if x := recover(); x != nil { + logf(c, 4, "%s", renderPanic(x)) // 4 == critical + c.outCode = 500 + } + }() + + http.DefaultServeMux.ServeHTTP(c, r) +} + +func renderPanic(x interface{}) string { + buf := make([]byte, 16<<10) // 16 KB should be plenty + buf = buf[:runtime.Stack(buf, false)] + + // Remove the first few stack frames: + // this func + // the recover closure in the caller + // That will root the stack trace at the site of the panic. + const ( + skipStart = "internal.renderPanic" + skipFrames = 2 + ) + start := bytes.Index(buf, []byte(skipStart)) + p := start + for i := 0; i < skipFrames*2 && p+1 < len(buf); i++ { + p = bytes.IndexByte(buf[p+1:], '\n') + p + 1 + if p < 0 { + break + } + } + if p >= 0 { + // buf[start:p+1] is the block to remove. + // Copy buf[p+1:] over buf[start:] and shrink buf. + copy(buf[start:], buf[p+1:]) + buf = buf[:len(buf)-(p+1-start)] + } + + // Add panic heading. + head := fmt.Sprintf("panic: %v\n\n", x) + if len(head) > len(buf) { + // Extremely unlikely to happen. + return head + } + copy(buf[len(head):], buf) + copy(buf, head) + + return string(buf) +} + +var ctxs = struct { + sync.Mutex + m map[*http.Request]*context + bg *context // background context, lazily initialized + // dec is used by tests to decorate the netcontext.Context returned + // for a given request. This allows tests to add overrides (such as + // WithAppIDOverride) to the context. The map is nil outside tests. + dec map[*http.Request]func(netcontext.Context) netcontext.Context +}{ + m: make(map[*http.Request]*context), +} + +// context represents the context of an in-flight HTTP request. +// It implements the appengine.Context and http.ResponseWriter interfaces. +type context struct { + req *http.Request + + outCode int + outHeader http.Header + outBody []byte + + pendingLogs struct { + sync.Mutex + lines []*logpb.UserAppLogLine + flushes int + } + + apiURL *url.URL +} + +var contextKey = "holds a *context" + +// fromContext returns the App Engine context or nil if ctx is not +// derived from an App Engine context. +func fromContext(ctx netcontext.Context) *context { + c, _ := ctx.Value(&contextKey).(*context) + return c +} + +func withContext(parent netcontext.Context, c *context) netcontext.Context { + ctx := netcontext.WithValue(parent, &contextKey, c) + if ns := c.req.Header.Get(curNamespaceHeader); ns != "" { + ctx = withNamespace(ctx, ns) + } + return ctx +} + +func toContext(c *context) netcontext.Context { + return withContext(netcontext.Background(), c) +} + +func IncomingHeaders(ctx netcontext.Context) http.Header { + if c := fromContext(ctx); c != nil { + return c.req.Header + } + return nil +} + +func ReqContext(req *http.Request) netcontext.Context { + return WithContext(netcontext.Background(), req) +} + +func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { + ctxs.Lock() + c := ctxs.m[req] + d := ctxs.dec[req] + ctxs.Unlock() + + if d != nil { + parent = d(parent) + } + + if c == nil { + // Someone passed in an http.Request that is not in-flight. + // We panic here rather than panicking at a later point + // so that stack traces will be more sensible. + log.Panic("appengine: NewContext passed an unknown http.Request") + } + return withContext(parent, c) +} + +// DefaultTicket returns a ticket used for background context or dev_appserver. +func DefaultTicket() string { + defaultTicketOnce.Do(func() { + if IsDevAppServer() { + defaultTicket = "testapp" + defaultTicketSuffix + return + } + appID := partitionlessAppID() + escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1) + majVersion := VersionID(nil) + if i := strings.Index(majVersion, "."); i > 0 { + majVersion = majVersion[:i] + } + defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID()) + }) + return defaultTicket +} + +func BackgroundContext() netcontext.Context { + ctxs.Lock() + defer ctxs.Unlock() + + if ctxs.bg != nil { + return toContext(ctxs.bg) + } + + // Compute background security ticket. + ticket := DefaultTicket() + + ctxs.bg = &context{ + req: &http.Request{ + Header: http.Header{ + ticketHeader: []string{ticket}, + }, + }, + apiURL: apiURL(), + } + + // TODO(dsymonds): Wire up the shutdown handler to do a final flush. + go ctxs.bg.logFlusher(make(chan int)) + + return toContext(ctxs.bg) +} + +// RegisterTestRequest registers the HTTP request req for testing, such that +// any API calls are sent to the provided URL. It returns a closure to delete +// the registration. +// It should only be used by aetest package. +func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netcontext.Context) netcontext.Context) (*http.Request, func()) { + c := &context{ + req: req, + apiURL: apiURL, + } + ctxs.Lock() + defer ctxs.Unlock() + if _, ok := ctxs.m[req]; ok { + log.Panic("req already associated with context") + } + if _, ok := ctxs.dec[req]; ok { + log.Panic("req already associated with context") + } + if ctxs.dec == nil { + ctxs.dec = make(map[*http.Request]func(netcontext.Context) netcontext.Context) + } + ctxs.m[req] = c + ctxs.dec[req] = decorate + + return req, func() { + ctxs.Lock() + delete(ctxs.m, req) + delete(ctxs.dec, req) + ctxs.Unlock() + } +} + +var errTimeout = &CallError{ + Detail: "Deadline exceeded", + Code: int32(remotepb.RpcError_CANCELLED), + Timeout: true, +} + +func (c *context) Header() http.Header { return c.outHeader } + +// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status +// codes do not permit a response body (nor response entity headers such as +// Content-Length, Content-Type, etc). +func bodyAllowedForStatus(status int) bool { + switch { + case status >= 100 && status <= 199: + return false + case status == 204: + return false + case status == 304: + return false + } + return true +} + +func (c *context) Write(b []byte) (int, error) { + if c.outCode == 0 { + c.WriteHeader(http.StatusOK) + } + if len(b) > 0 && !bodyAllowedForStatus(c.outCode) { + return 0, http.ErrBodyNotAllowed + } + c.outBody = append(c.outBody, b...) + return len(b), nil +} + +func (c *context) WriteHeader(code int) { + if c.outCode != 0 { + logf(c, 3, "WriteHeader called multiple times on request.") // error level + return + } + c.outCode = code +} + +func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) { + hreq := &http.Request{ + Method: "POST", + URL: c.apiURL, + Header: http.Header{ + apiEndpointHeader: apiEndpointHeaderValue, + apiMethodHeader: apiMethodHeaderValue, + apiContentType: apiContentTypeValue, + apiDeadlineHeader: []string{strconv.FormatFloat(timeout.Seconds(), 'f', -1, 64)}, + }, + Body: ioutil.NopCloser(bytes.NewReader(body)), + ContentLength: int64(len(body)), + Host: c.apiURL.Host, + } + if info := c.req.Header.Get(dapperHeader); info != "" { + hreq.Header.Set(dapperHeader, info) + } + if info := c.req.Header.Get(traceHeader); info != "" { + hreq.Header.Set(traceHeader, info) + } + + tr := apiHTTPClient.Transport.(*http.Transport) + + var timedOut int32 // atomic; set to 1 if timed out + t := time.AfterFunc(timeout, func() { + atomic.StoreInt32(&timedOut, 1) + tr.CancelRequest(hreq) + }) + defer t.Stop() + defer func() { + // Check if timeout was exceeded. + if atomic.LoadInt32(&timedOut) != 0 { + err = errTimeout + } + }() + + hresp, err := apiHTTPClient.Do(hreq) + if err != nil { + return nil, &CallError{ + Detail: fmt.Sprintf("service bridge HTTP failed: %v", err), + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + defer hresp.Body.Close() + hrespBody, err := ioutil.ReadAll(hresp.Body) + if hresp.StatusCode != 200 { + return nil, &CallError{ + Detail: fmt.Sprintf("service bridge returned HTTP %d (%q)", hresp.StatusCode, hrespBody), + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + if err != nil { + return nil, &CallError{ + Detail: fmt.Sprintf("service bridge response bad: %v", err), + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + return hrespBody, nil +} + +func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error { + if ns := NamespaceFromContext(ctx); ns != "" { + if fn, ok := NamespaceMods[service]; ok { + fn(in, ns) + } + } + + if f, ctx, ok := callOverrideFromContext(ctx); ok { + return f(ctx, service, method, in, out) + } + + // Handle already-done contexts quickly. + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + c := fromContext(ctx) + if c == nil { + // Give a good error message rather than a panic lower down. + return errNotAppEngineContext + } + + // Apply transaction modifications if we're in a transaction. + if t := transactionFromContext(ctx); t != nil { + if t.finished { + return errors.New("transaction context has expired") + } + applyTransaction(in, &t.transaction) + } + + // Default RPC timeout is 60s. + timeout := 60 * time.Second + if deadline, ok := ctx.Deadline(); ok { + timeout = deadline.Sub(time.Now()) + } + + data, err := proto.Marshal(in) + if err != nil { + return err + } + + ticket := c.req.Header.Get(ticketHeader) + // Use a test ticket under test environment. + if ticket == "" { + if appid := ctx.Value(&appIDOverrideKey); appid != nil { + ticket = appid.(string) + defaultTicketSuffix + } + } + // Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver. + if ticket == "" { + ticket = DefaultTicket() + } + req := &remotepb.Request{ + ServiceName: &service, + Method: &method, + Request: data, + RequestId: &ticket, + } + hreqBody, err := proto.Marshal(req) + if err != nil { + return err + } + + hrespBody, err := c.post(hreqBody, timeout) + if err != nil { + return err + } + + res := &remotepb.Response{} + if err := proto.Unmarshal(hrespBody, res); err != nil { + return err + } + if res.RpcError != nil { + ce := &CallError{ + Detail: res.RpcError.GetDetail(), + Code: *res.RpcError.Code, + } + switch remotepb.RpcError_ErrorCode(ce.Code) { + case remotepb.RpcError_CANCELLED, remotepb.RpcError_DEADLINE_EXCEEDED: + ce.Timeout = true + } + return ce + } + if res.ApplicationError != nil { + return &APIError{ + Service: *req.ServiceName, + Detail: res.ApplicationError.GetDetail(), + Code: *res.ApplicationError.Code, + } + } + if res.Exception != nil || res.JavaException != nil { + // This shouldn't happen, but let's be defensive. + return &CallError{ + Detail: "service bridge returned exception", + Code: int32(remotepb.RpcError_UNKNOWN), + } + } + return proto.Unmarshal(res.Response, out) +} + +func (c *context) Request() *http.Request { + return c.req +} + +func (c *context) addLogLine(ll *logpb.UserAppLogLine) { + // Truncate long log lines. + // TODO(dsymonds): Check if this is still necessary. + const lim = 8 << 10 + if len(*ll.Message) > lim { + suffix := fmt.Sprintf("...(length %d)", len(*ll.Message)) + ll.Message = proto.String((*ll.Message)[:lim-len(suffix)] + suffix) + } + + c.pendingLogs.Lock() + c.pendingLogs.lines = append(c.pendingLogs.lines, ll) + c.pendingLogs.Unlock() +} + +var logLevelName = map[int64]string{ + 0: "DEBUG", + 1: "INFO", + 2: "WARNING", + 3: "ERROR", + 4: "CRITICAL", +} + +func logf(c *context, level int64, format string, args ...interface{}) { + if c == nil { + panic("not an App Engine context") + } + s := fmt.Sprintf(format, args...) + s = strings.TrimRight(s, "\n") // Remove any trailing newline characters. + c.addLogLine(&logpb.UserAppLogLine{ + TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3), + Level: &level, + Message: &s, + }) + log.Print(logLevelName[level] + ": " + s) +} + +// flushLog attempts to flush any pending logs to the appserver. +// It should not be called concurrently. +func (c *context) flushLog(force bool) (flushed bool) { + c.pendingLogs.Lock() + // Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious. + n, rem := 0, 30<<20 + for ; n < len(c.pendingLogs.lines); n++ { + ll := c.pendingLogs.lines[n] + // Each log line will require about 3 bytes of overhead. + nb := proto.Size(ll) + 3 + if nb > rem { + break + } + rem -= nb + } + lines := c.pendingLogs.lines[:n] + c.pendingLogs.lines = c.pendingLogs.lines[n:] + c.pendingLogs.Unlock() + + if len(lines) == 0 && !force { + // Nothing to flush. + return false + } + + rescueLogs := false + defer func() { + if rescueLogs { + c.pendingLogs.Lock() + c.pendingLogs.lines = append(lines, c.pendingLogs.lines...) + c.pendingLogs.Unlock() + } + }() + + buf, err := proto.Marshal(&logpb.UserAppLogGroup{ + LogLine: lines, + }) + if err != nil { + log.Printf("internal.flushLog: marshaling UserAppLogGroup: %v", err) + rescueLogs = true + return false + } + + req := &logpb.FlushRequest{ + Logs: buf, + } + res := &basepb.VoidProto{} + c.pendingLogs.Lock() + c.pendingLogs.flushes++ + c.pendingLogs.Unlock() + if err := Call(toContext(c), "logservice", "Flush", req, res); err != nil { + log.Printf("internal.flushLog: Flush RPC: %v", err) + rescueLogs = true + return false + } + return true +} + +const ( + // Log flushing parameters. + flushInterval = 1 * time.Second + forceFlushInterval = 60 * time.Second +) + +func (c *context) logFlusher(stop <-chan int) { + lastFlush := time.Now() + tick := time.NewTicker(flushInterval) + for { + select { + case <-stop: + // Request finished. + tick.Stop() + return + case <-tick.C: + force := time.Now().Sub(lastFlush) > forceFlushInterval + if c.flushLog(force) { + lastFlush = time.Now() + } + } + } +} + +func ContextForTesting(req *http.Request) netcontext.Context { + return toContext(&context{req: req}) +} diff --git a/vendor/google.golang.org/appengine/internal/api_race_test.go b/vendor/google.golang.org/appengine/internal/api_race_test.go new file mode 100644 index 0000000000..6cfe90649c --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/api_race_test.go @@ -0,0 +1,9 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build race + +package internal + +func init() { raceDetector = true } diff --git a/vendor/google.golang.org/appengine/internal/api_test.go b/vendor/google.golang.org/appengine/internal/api_test.go new file mode 100644 index 0000000000..76624a28ef --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/api_test.go @@ -0,0 +1,466 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package internal + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "os" + "os/exec" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/golang/protobuf/proto" + netcontext "golang.org/x/net/context" + + basepb "google.golang.org/appengine/internal/base" + remotepb "google.golang.org/appengine/internal/remote_api" +) + +const testTicketHeader = "X-Magic-Ticket-Header" + +func init() { + ticketHeader = testTicketHeader +} + +type fakeAPIHandler struct { + hang chan int // used for RunSlowly RPC + + LogFlushes int32 // atomic +} + +func (f *fakeAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + writeResponse := func(res *remotepb.Response) { + hresBody, err := proto.Marshal(res) + if err != nil { + http.Error(w, fmt.Sprintf("Failed encoding API response: %v", err), 500) + return + } + w.Write(hresBody) + } + + if r.URL.Path != "/rpc_http" { + http.NotFound(w, r) + return + } + hreqBody, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, fmt.Sprintf("Bad body: %v", err), 500) + return + } + apiReq := &remotepb.Request{} + if err := proto.Unmarshal(hreqBody, apiReq); err != nil { + http.Error(w, fmt.Sprintf("Bad encoded API request: %v", err), 500) + return + } + if *apiReq.RequestId != "s3cr3t" && *apiReq.RequestId != DefaultTicket() { + writeResponse(&remotepb.Response{ + RpcError: &remotepb.RpcError{ + Code: proto.Int32(int32(remotepb.RpcError_SECURITY_VIOLATION)), + Detail: proto.String("bad security ticket"), + }, + }) + return + } + if got, want := r.Header.Get(dapperHeader), "trace-001"; got != want { + writeResponse(&remotepb.Response{ + RpcError: &remotepb.RpcError{ + Code: proto.Int32(int32(remotepb.RpcError_BAD_REQUEST)), + Detail: proto.String(fmt.Sprintf("trace info = %q, want %q", got, want)), + }, + }) + return + } + + service, method := *apiReq.ServiceName, *apiReq.Method + var resOut proto.Message + if service == "actordb" && method == "LookupActor" { + req := &basepb.StringProto{} + res := &basepb.StringProto{} + if err := proto.Unmarshal(apiReq.Request, req); err != nil { + http.Error(w, fmt.Sprintf("Bad encoded request: %v", err), 500) + return + } + if *req.Value == "Doctor Who" { + res.Value = proto.String("David Tennant") + } + resOut = res + } + if service == "errors" { + switch method { + case "Non200": + http.Error(w, "I'm a little teapot.", 418) + return + case "ShortResponse": + w.Header().Set("Content-Length", "100") + w.Write([]byte("way too short")) + return + case "OverQuota": + writeResponse(&remotepb.Response{ + RpcError: &remotepb.RpcError{ + Code: proto.Int32(int32(remotepb.RpcError_OVER_QUOTA)), + Detail: proto.String("you are hogging the resources!"), + }, + }) + return + case "RunSlowly": + // TestAPICallRPCFailure creates f.hang, but does not strobe it + // until Call returns with remotepb.RpcError_CANCELLED. + // This is here to force a happens-before relationship between + // the httptest server handler and shutdown. + <-f.hang + resOut = &basepb.VoidProto{} + } + } + if service == "logservice" && method == "Flush" { + // Pretend log flushing is slow. + time.Sleep(50 * time.Millisecond) + atomic.AddInt32(&f.LogFlushes, 1) + resOut = &basepb.VoidProto{} + } + + encOut, err := proto.Marshal(resOut) + if err != nil { + http.Error(w, fmt.Sprintf("Failed encoding response: %v", err), 500) + return + } + writeResponse(&remotepb.Response{ + Response: encOut, + }) +} + +func setup() (f *fakeAPIHandler, c *context, cleanup func()) { + f = &fakeAPIHandler{} + srv := httptest.NewServer(f) + u, err := url.Parse(srv.URL + apiPath) + if err != nil { + panic(fmt.Sprintf("url.Parse(%q): %v", srv.URL+apiPath, err)) + } + return f, &context{ + req: &http.Request{ + Header: http.Header{ + ticketHeader: []string{"s3cr3t"}, + dapperHeader: []string{"trace-001"}, + }, + }, + apiURL: u, + }, srv.Close +} + +func TestAPICall(t *testing.T) { + _, c, cleanup := setup() + defer cleanup() + + req := &basepb.StringProto{ + Value: proto.String("Doctor Who"), + } + res := &basepb.StringProto{} + err := Call(toContext(c), "actordb", "LookupActor", req, res) + if err != nil { + t.Fatalf("API call failed: %v", err) + } + if got, want := *res.Value, "David Tennant"; got != want { + t.Errorf("Response is %q, want %q", got, want) + } +} + +func TestAPICallTicketUnavailable(t *testing.T) { + resetEnv := SetTestEnv() + defer resetEnv() + _, c, cleanup := setup() + defer cleanup() + + c.req.Header.Set(ticketHeader, "") + req := &basepb.StringProto{ + Value: proto.String("Doctor Who"), + } + res := &basepb.StringProto{} + err := Call(toContext(c), "actordb", "LookupActor", req, res) + if err != nil { + t.Fatalf("API call failed: %v", err) + } + if got, want := *res.Value, "David Tennant"; got != want { + t.Errorf("Response is %q, want %q", got, want) + } +} + +func TestAPICallRPCFailure(t *testing.T) { + f, c, cleanup := setup() + defer cleanup() + + testCases := []struct { + method string + code remotepb.RpcError_ErrorCode + }{ + {"Non200", remotepb.RpcError_UNKNOWN}, + {"ShortResponse", remotepb.RpcError_UNKNOWN}, + {"OverQuota", remotepb.RpcError_OVER_QUOTA}, + {"RunSlowly", remotepb.RpcError_CANCELLED}, + } + f.hang = make(chan int) // only for RunSlowly + for _, tc := range testCases { + ctx, _ := netcontext.WithTimeout(toContext(c), 100*time.Millisecond) + err := Call(ctx, "errors", tc.method, &basepb.VoidProto{}, &basepb.VoidProto{}) + ce, ok := err.(*CallError) + if !ok { + t.Errorf("%s: API call error is %T (%v), want *CallError", tc.method, err, err) + continue + } + if ce.Code != int32(tc.code) { + t.Errorf("%s: ce.Code = %d, want %d", tc.method, ce.Code, tc.code) + } + if tc.method == "RunSlowly" { + f.hang <- 1 // release the HTTP handler + } + } +} + +func TestAPICallDialFailure(t *testing.T) { + // See what happens if the API host is unresponsive. + // This should time out quickly, not hang forever. + _, c, cleanup := setup() + defer cleanup() + // Reset the URL to the production address so that dialing fails. + c.apiURL = apiURL() + + start := time.Now() + err := Call(toContext(c), "foo", "bar", &basepb.VoidProto{}, &basepb.VoidProto{}) + const max = 1 * time.Second + if taken := time.Since(start); taken > max { + t.Errorf("Dial hang took too long: %v > %v", taken, max) + } + if err == nil { + t.Error("Call did not fail") + } +} + +func TestDelayedLogFlushing(t *testing.T) { + f, c, cleanup := setup() + defer cleanup() + + http.HandleFunc("/quick_log", func(w http.ResponseWriter, r *http.Request) { + logC := WithContext(netcontext.Background(), r) + fromContext(logC).apiURL = c.apiURL // Otherwise it will try to use the default URL. + Logf(logC, 1, "It's a lovely day.") + w.WriteHeader(200) + w.Write(make([]byte, 100<<10)) // write 100 KB to force HTTP flush + }) + + r := &http.Request{ + Method: "GET", + URL: &url.URL{ + Scheme: "http", + Path: "/quick_log", + }, + Header: c.req.Header, + Body: ioutil.NopCloser(bytes.NewReader(nil)), + } + w := httptest.NewRecorder() + + // Check that log flushing does not hold up the HTTP response. + start := time.Now() + handleHTTP(w, r) + if d := time.Since(start); d > 10*time.Millisecond { + t.Errorf("handleHTTP took %v, want under 10ms", d) + } + const hdr = "X-AppEngine-Log-Flush-Count" + if h := w.HeaderMap.Get(hdr); h != "1" { + t.Errorf("%s header = %q, want %q", hdr, h, "1") + } + if f := atomic.LoadInt32(&f.LogFlushes); f != 0 { + t.Errorf("After HTTP response: f.LogFlushes = %d, want 0", f) + } + + // Check that the log flush eventually comes in. + time.Sleep(100 * time.Millisecond) + if f := atomic.LoadInt32(&f.LogFlushes); f != 1 { + t.Errorf("After 100ms: f.LogFlushes = %d, want 1", f) + } +} + +func TestRemoteAddr(t *testing.T) { + var addr string + http.HandleFunc("/remote_addr", func(w http.ResponseWriter, r *http.Request) { + addr = r.RemoteAddr + }) + + testCases := []struct { + headers http.Header + addr string + }{ + {http.Header{"X-Appengine-User-Ip": []string{"10.5.2.1"}}, "10.5.2.1:80"}, + {http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4"}}, "1.2.3.4:80"}, + {http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4:8080"}}, "1.2.3.4:8080"}, + { + http.Header{"X-Appengine-Remote-Addr": []string{"2401:fa00:9:1:7646:a0ff:fe90:ca66"}}, + "[2401:fa00:9:1:7646:a0ff:fe90:ca66]:80", + }, + { + http.Header{"X-Appengine-Remote-Addr": []string{"[::1]:http"}}, + "[::1]:http", + }, + {http.Header{}, "127.0.0.1:80"}, + } + + for _, tc := range testCases { + r := &http.Request{ + Method: "GET", + URL: &url.URL{Scheme: "http", Path: "/remote_addr"}, + Header: tc.headers, + Body: ioutil.NopCloser(bytes.NewReader(nil)), + } + handleHTTP(httptest.NewRecorder(), r) + if addr != tc.addr { + t.Errorf("Header %v, got %q, want %q", tc.headers, addr, tc.addr) + } + } +} + +func TestPanickingHandler(t *testing.T) { + http.HandleFunc("/panic", func(http.ResponseWriter, *http.Request) { + panic("whoops!") + }) + r := &http.Request{ + Method: "GET", + URL: &url.URL{Scheme: "http", Path: "/panic"}, + Body: ioutil.NopCloser(bytes.NewReader(nil)), + } + rec := httptest.NewRecorder() + handleHTTP(rec, r) + if rec.Code != 500 { + t.Errorf("Panicking handler returned HTTP %d, want HTTP %d", rec.Code, 500) + } +} + +var raceDetector = false + +func TestAPICallAllocations(t *testing.T) { + if raceDetector { + t.Skip("not running under race detector") + } + + // Run the test API server in a subprocess so we aren't counting its allocations. + u, cleanup := launchHelperProcess(t) + defer cleanup() + c := &context{ + req: &http.Request{ + Header: http.Header{ + ticketHeader: []string{"s3cr3t"}, + dapperHeader: []string{"trace-001"}, + }, + }, + apiURL: u, + } + + req := &basepb.StringProto{ + Value: proto.String("Doctor Who"), + } + res := &basepb.StringProto{} + var apiErr error + avg := testing.AllocsPerRun(100, func() { + ctx, _ := netcontext.WithTimeout(toContext(c), 100*time.Millisecond) + if err := Call(ctx, "actordb", "LookupActor", req, res); err != nil && apiErr == nil { + apiErr = err // get the first error only + } + }) + if apiErr != nil { + t.Errorf("API call failed: %v", apiErr) + } + + // Lots of room for improvement... + // TODO(djd): Reduce maximum to 85 once the App Engine SDK is based on 1.6. + const min, max float64 = 70, 100 + if avg < min || max < avg { + t.Errorf("Allocations per API call = %g, want in [%g,%g]", avg, min, max) + } +} + +func launchHelperProcess(t *testing.T) (apiURL *url.URL, cleanup func()) { + cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess") + cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} + stdin, err := cmd.StdinPipe() + if err != nil { + t.Fatalf("StdinPipe: %v", err) + } + stdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatalf("StdoutPipe: %v", err) + } + if err := cmd.Start(); err != nil { + t.Fatalf("Starting helper process: %v", err) + } + + scan := bufio.NewScanner(stdout) + var u *url.URL + for scan.Scan() { + line := scan.Text() + if hp := strings.TrimPrefix(line, helperProcessMagic); hp != line { + var err error + u, err = url.Parse(hp) + if err != nil { + t.Fatalf("Failed to parse %q: %v", hp, err) + } + break + } + } + if err := scan.Err(); err != nil { + t.Fatalf("Scanning helper process stdout: %v", err) + } + if u == nil { + t.Fatal("Helper process never reported") + } + + return u, func() { + stdin.Close() + if err := cmd.Wait(); err != nil { + t.Errorf("Helper process did not exit cleanly: %v", err) + } + } +} + +const helperProcessMagic = "A lovely helper process is listening at " + +// This isn't a real test. It's used as a helper process. +func TestHelperProcess(*testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + defer os.Exit(0) + + f := &fakeAPIHandler{} + srv := httptest.NewServer(f) + defer srv.Close() + fmt.Println(helperProcessMagic + srv.URL + apiPath) + + // Wait for stdin to be closed. + io.Copy(ioutil.Discard, os.Stdin) +} + +func TestBackgroundContext(t *testing.T) { + resetEnv := SetTestEnv() + defer resetEnv() + + ctx, key := fromContext(BackgroundContext()), "X-Magic-Ticket-Header" + if g, w := ctx.req.Header.Get(key), "my-app-id/default.20150612t184001.0"; g != w { + t.Errorf("%v = %q, want %q", key, g, w) + } + + // Check that using the background context doesn't panic. + req := &basepb.StringProto{ + Value: proto.String("Doctor Who"), + } + res := &basepb.StringProto{} + Call(BackgroundContext(), "actordb", "LookupActor", req, res) // expected to fail +} diff --git a/vendor/google.golang.org/appengine/internal/app_id.go b/vendor/google.golang.org/appengine/internal/app_id.go new file mode 100644 index 0000000000..11df8c07b5 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/app_id.go @@ -0,0 +1,28 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package internal + +import ( + "strings" +) + +func parseFullAppID(appid string) (partition, domain, displayID string) { + if i := strings.Index(appid, "~"); i != -1 { + partition, appid = appid[:i], appid[i+1:] + } + if i := strings.Index(appid, ":"); i != -1 { + domain, appid = appid[:i], appid[i+1:] + } + return partition, domain, appid +} + +// appID returns "appid" or "domain.com:appid". +func appID(fullAppID string) string { + _, dom, dis := parseFullAppID(fullAppID) + if dom != "" { + return dom + ":" + dis + } + return dis +} diff --git a/vendor/google.golang.org/appengine/internal/app_id_test.go b/vendor/google.golang.org/appengine/internal/app_id_test.go new file mode 100644 index 0000000000..e69195cd40 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/app_id_test.go @@ -0,0 +1,34 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package internal + +import ( + "testing" +) + +func TestAppIDParsing(t *testing.T) { + testCases := []struct { + in string + partition, domain, displayID string + }{ + {"simple-app-id", "", "", "simple-app-id"}, + {"domain.com:domain-app-id", "", "domain.com", "domain-app-id"}, + {"part~partition-app-id", "part", "", "partition-app-id"}, + {"part~domain.com:display", "part", "domain.com", "display"}, + } + + for _, tc := range testCases { + part, dom, dis := parseFullAppID(tc.in) + if part != tc.partition { + t.Errorf("partition of %q: got %q, want %q", tc.in, part, tc.partition) + } + if dom != tc.domain { + t.Errorf("domain of %q: got %q, want %q", tc.in, dom, tc.domain) + } + if dis != tc.displayID { + t.Errorf("displayID of %q: got %q, want %q", tc.in, dis, tc.displayID) + } + } +} diff --git a/vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go b/vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go new file mode 100644 index 0000000000..89d3ea9c2d --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go @@ -0,0 +1,385 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/app_identity/app_identity_service.proto + +/* +Package app_identity is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/app_identity/app_identity_service.proto + +It has these top-level messages: + AppIdentityServiceError + SignForAppRequest + SignForAppResponse + GetPublicCertificateForAppRequest + PublicCertificate + GetPublicCertificateForAppResponse + GetServiceAccountNameRequest + GetServiceAccountNameResponse + GetAccessTokenRequest + GetAccessTokenResponse + GetDefaultGcsBucketNameRequest + GetDefaultGcsBucketNameResponse +*/ +package app_identity + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type AppIdentityServiceError_ErrorCode int32 + +const ( + AppIdentityServiceError_SUCCESS AppIdentityServiceError_ErrorCode = 0 + AppIdentityServiceError_UNKNOWN_SCOPE AppIdentityServiceError_ErrorCode = 9 + AppIdentityServiceError_BLOB_TOO_LARGE AppIdentityServiceError_ErrorCode = 1000 + AppIdentityServiceError_DEADLINE_EXCEEDED AppIdentityServiceError_ErrorCode = 1001 + AppIdentityServiceError_NOT_A_VALID_APP AppIdentityServiceError_ErrorCode = 1002 + AppIdentityServiceError_UNKNOWN_ERROR AppIdentityServiceError_ErrorCode = 1003 + AppIdentityServiceError_NOT_ALLOWED AppIdentityServiceError_ErrorCode = 1005 + AppIdentityServiceError_NOT_IMPLEMENTED AppIdentityServiceError_ErrorCode = 1006 +) + +var AppIdentityServiceError_ErrorCode_name = map[int32]string{ + 0: "SUCCESS", + 9: "UNKNOWN_SCOPE", + 1000: "BLOB_TOO_LARGE", + 1001: "DEADLINE_EXCEEDED", + 1002: "NOT_A_VALID_APP", + 1003: "UNKNOWN_ERROR", + 1005: "NOT_ALLOWED", + 1006: "NOT_IMPLEMENTED", +} +var AppIdentityServiceError_ErrorCode_value = map[string]int32{ + "SUCCESS": 0, + "UNKNOWN_SCOPE": 9, + "BLOB_TOO_LARGE": 1000, + "DEADLINE_EXCEEDED": 1001, + "NOT_A_VALID_APP": 1002, + "UNKNOWN_ERROR": 1003, + "NOT_ALLOWED": 1005, + "NOT_IMPLEMENTED": 1006, +} + +func (x AppIdentityServiceError_ErrorCode) Enum() *AppIdentityServiceError_ErrorCode { + p := new(AppIdentityServiceError_ErrorCode) + *p = x + return p +} +func (x AppIdentityServiceError_ErrorCode) String() string { + return proto.EnumName(AppIdentityServiceError_ErrorCode_name, int32(x)) +} +func (x *AppIdentityServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(AppIdentityServiceError_ErrorCode_value, data, "AppIdentityServiceError_ErrorCode") + if err != nil { + return err + } + *x = AppIdentityServiceError_ErrorCode(value) + return nil +} +func (AppIdentityServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type AppIdentityServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *AppIdentityServiceError) Reset() { *m = AppIdentityServiceError{} } +func (m *AppIdentityServiceError) String() string { return proto.CompactTextString(m) } +func (*AppIdentityServiceError) ProtoMessage() {} +func (*AppIdentityServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type SignForAppRequest struct { + BytesToSign []byte `protobuf:"bytes,1,opt,name=bytes_to_sign,json=bytesToSign" json:"bytes_to_sign,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SignForAppRequest) Reset() { *m = SignForAppRequest{} } +func (m *SignForAppRequest) String() string { return proto.CompactTextString(m) } +func (*SignForAppRequest) ProtoMessage() {} +func (*SignForAppRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *SignForAppRequest) GetBytesToSign() []byte { + if m != nil { + return m.BytesToSign + } + return nil +} + +type SignForAppResponse struct { + KeyName *string `protobuf:"bytes,1,opt,name=key_name,json=keyName" json:"key_name,omitempty"` + SignatureBytes []byte `protobuf:"bytes,2,opt,name=signature_bytes,json=signatureBytes" json:"signature_bytes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SignForAppResponse) Reset() { *m = SignForAppResponse{} } +func (m *SignForAppResponse) String() string { return proto.CompactTextString(m) } +func (*SignForAppResponse) ProtoMessage() {} +func (*SignForAppResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *SignForAppResponse) GetKeyName() string { + if m != nil && m.KeyName != nil { + return *m.KeyName + } + return "" +} + +func (m *SignForAppResponse) GetSignatureBytes() []byte { + if m != nil { + return m.SignatureBytes + } + return nil +} + +type GetPublicCertificateForAppRequest struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetPublicCertificateForAppRequest) Reset() { *m = GetPublicCertificateForAppRequest{} } +func (m *GetPublicCertificateForAppRequest) String() string { return proto.CompactTextString(m) } +func (*GetPublicCertificateForAppRequest) ProtoMessage() {} +func (*GetPublicCertificateForAppRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{3} +} + +type PublicCertificate struct { + KeyName *string `protobuf:"bytes,1,opt,name=key_name,json=keyName" json:"key_name,omitempty"` + X509CertificatePem *string `protobuf:"bytes,2,opt,name=x509_certificate_pem,json=x509CertificatePem" json:"x509_certificate_pem,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PublicCertificate) Reset() { *m = PublicCertificate{} } +func (m *PublicCertificate) String() string { return proto.CompactTextString(m) } +func (*PublicCertificate) ProtoMessage() {} +func (*PublicCertificate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *PublicCertificate) GetKeyName() string { + if m != nil && m.KeyName != nil { + return *m.KeyName + } + return "" +} + +func (m *PublicCertificate) GetX509CertificatePem() string { + if m != nil && m.X509CertificatePem != nil { + return *m.X509CertificatePem + } + return "" +} + +type GetPublicCertificateForAppResponse struct { + PublicCertificateList []*PublicCertificate `protobuf:"bytes,1,rep,name=public_certificate_list,json=publicCertificateList" json:"public_certificate_list,omitempty"` + MaxClientCacheTimeInSecond *int64 `protobuf:"varint,2,opt,name=max_client_cache_time_in_second,json=maxClientCacheTimeInSecond" json:"max_client_cache_time_in_second,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetPublicCertificateForAppResponse) Reset() { *m = GetPublicCertificateForAppResponse{} } +func (m *GetPublicCertificateForAppResponse) String() string { return proto.CompactTextString(m) } +func (*GetPublicCertificateForAppResponse) ProtoMessage() {} +func (*GetPublicCertificateForAppResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{5} +} + +func (m *GetPublicCertificateForAppResponse) GetPublicCertificateList() []*PublicCertificate { + if m != nil { + return m.PublicCertificateList + } + return nil +} + +func (m *GetPublicCertificateForAppResponse) GetMaxClientCacheTimeInSecond() int64 { + if m != nil && m.MaxClientCacheTimeInSecond != nil { + return *m.MaxClientCacheTimeInSecond + } + return 0 +} + +type GetServiceAccountNameRequest struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetServiceAccountNameRequest) Reset() { *m = GetServiceAccountNameRequest{} } +func (m *GetServiceAccountNameRequest) String() string { return proto.CompactTextString(m) } +func (*GetServiceAccountNameRequest) ProtoMessage() {} +func (*GetServiceAccountNameRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +type GetServiceAccountNameResponse struct { + ServiceAccountName *string `protobuf:"bytes,1,opt,name=service_account_name,json=serviceAccountName" json:"service_account_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetServiceAccountNameResponse) Reset() { *m = GetServiceAccountNameResponse{} } +func (m *GetServiceAccountNameResponse) String() string { return proto.CompactTextString(m) } +func (*GetServiceAccountNameResponse) ProtoMessage() {} +func (*GetServiceAccountNameResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *GetServiceAccountNameResponse) GetServiceAccountName() string { + if m != nil && m.ServiceAccountName != nil { + return *m.ServiceAccountName + } + return "" +} + +type GetAccessTokenRequest struct { + Scope []string `protobuf:"bytes,1,rep,name=scope" json:"scope,omitempty"` + ServiceAccountId *int64 `protobuf:"varint,2,opt,name=service_account_id,json=serviceAccountId" json:"service_account_id,omitempty"` + ServiceAccountName *string `protobuf:"bytes,3,opt,name=service_account_name,json=serviceAccountName" json:"service_account_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetAccessTokenRequest) Reset() { *m = GetAccessTokenRequest{} } +func (m *GetAccessTokenRequest) String() string { return proto.CompactTextString(m) } +func (*GetAccessTokenRequest) ProtoMessage() {} +func (*GetAccessTokenRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *GetAccessTokenRequest) GetScope() []string { + if m != nil { + return m.Scope + } + return nil +} + +func (m *GetAccessTokenRequest) GetServiceAccountId() int64 { + if m != nil && m.ServiceAccountId != nil { + return *m.ServiceAccountId + } + return 0 +} + +func (m *GetAccessTokenRequest) GetServiceAccountName() string { + if m != nil && m.ServiceAccountName != nil { + return *m.ServiceAccountName + } + return "" +} + +type GetAccessTokenResponse struct { + AccessToken *string `protobuf:"bytes,1,opt,name=access_token,json=accessToken" json:"access_token,omitempty"` + ExpirationTime *int64 `protobuf:"varint,2,opt,name=expiration_time,json=expirationTime" json:"expiration_time,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetAccessTokenResponse) Reset() { *m = GetAccessTokenResponse{} } +func (m *GetAccessTokenResponse) String() string { return proto.CompactTextString(m) } +func (*GetAccessTokenResponse) ProtoMessage() {} +func (*GetAccessTokenResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *GetAccessTokenResponse) GetAccessToken() string { + if m != nil && m.AccessToken != nil { + return *m.AccessToken + } + return "" +} + +func (m *GetAccessTokenResponse) GetExpirationTime() int64 { + if m != nil && m.ExpirationTime != nil { + return *m.ExpirationTime + } + return 0 +} + +type GetDefaultGcsBucketNameRequest struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetDefaultGcsBucketNameRequest) Reset() { *m = GetDefaultGcsBucketNameRequest{} } +func (m *GetDefaultGcsBucketNameRequest) String() string { return proto.CompactTextString(m) } +func (*GetDefaultGcsBucketNameRequest) ProtoMessage() {} +func (*GetDefaultGcsBucketNameRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +type GetDefaultGcsBucketNameResponse struct { + DefaultGcsBucketName *string `protobuf:"bytes,1,opt,name=default_gcs_bucket_name,json=defaultGcsBucketName" json:"default_gcs_bucket_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetDefaultGcsBucketNameResponse) Reset() { *m = GetDefaultGcsBucketNameResponse{} } +func (m *GetDefaultGcsBucketNameResponse) String() string { return proto.CompactTextString(m) } +func (*GetDefaultGcsBucketNameResponse) ProtoMessage() {} +func (*GetDefaultGcsBucketNameResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{11} +} + +func (m *GetDefaultGcsBucketNameResponse) GetDefaultGcsBucketName() string { + if m != nil && m.DefaultGcsBucketName != nil { + return *m.DefaultGcsBucketName + } + return "" +} + +func init() { + proto.RegisterType((*AppIdentityServiceError)(nil), "appengine.AppIdentityServiceError") + proto.RegisterType((*SignForAppRequest)(nil), "appengine.SignForAppRequest") + proto.RegisterType((*SignForAppResponse)(nil), "appengine.SignForAppResponse") + proto.RegisterType((*GetPublicCertificateForAppRequest)(nil), "appengine.GetPublicCertificateForAppRequest") + proto.RegisterType((*PublicCertificate)(nil), "appengine.PublicCertificate") + proto.RegisterType((*GetPublicCertificateForAppResponse)(nil), "appengine.GetPublicCertificateForAppResponse") + proto.RegisterType((*GetServiceAccountNameRequest)(nil), "appengine.GetServiceAccountNameRequest") + proto.RegisterType((*GetServiceAccountNameResponse)(nil), "appengine.GetServiceAccountNameResponse") + proto.RegisterType((*GetAccessTokenRequest)(nil), "appengine.GetAccessTokenRequest") + proto.RegisterType((*GetAccessTokenResponse)(nil), "appengine.GetAccessTokenResponse") + proto.RegisterType((*GetDefaultGcsBucketNameRequest)(nil), "appengine.GetDefaultGcsBucketNameRequest") + proto.RegisterType((*GetDefaultGcsBucketNameResponse)(nil), "appengine.GetDefaultGcsBucketNameResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/app_identity/app_identity_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 676 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0xdb, 0x6e, 0xda, 0x58, + 0x14, 0x1d, 0x26, 0x1a, 0x31, 0x6c, 0x12, 0x62, 0xce, 0x90, 0xcb, 0x8c, 0x32, 0xb9, 0x78, 0x1e, + 0x26, 0x0f, 0x15, 0x89, 0x2a, 0x45, 0x55, 0x1f, 0x8d, 0xed, 0x22, 0x54, 0x07, 0x53, 0x43, 0x9a, + 0xa8, 0x2f, 0xa7, 0xce, 0x61, 0xc7, 0x3d, 0x02, 0x9f, 0xe3, 0xda, 0x87, 0x0a, 0x3e, 0xa2, 0x3f, + 0xd2, 0x9f, 0xe8, 0x5b, 0xbf, 0xa5, 0x17, 0xb5, 0xdf, 0x50, 0xd9, 0x38, 0x5c, 0x92, 0x92, 0x37, + 0xbc, 0xf6, 0x5a, 0xcb, 0x6b, 0x2f, 0x6d, 0x0c, 0x4e, 0x20, 0x65, 0x30, 0xc4, 0x7a, 0x20, 0x87, + 0xbe, 0x08, 0xea, 0x32, 0x0e, 0x4e, 0xfc, 0x28, 0x42, 0x11, 0x70, 0x81, 0x27, 0x5c, 0x28, 0x8c, + 0x85, 0x3f, 0x4c, 0x21, 0xca, 0xfb, 0x28, 0x14, 0x57, 0x93, 0xa5, 0x07, 0x9a, 0x60, 0xfc, 0x8e, + 0x33, 0xac, 0x47, 0xb1, 0x54, 0x92, 0x94, 0x66, 0x5a, 0xfd, 0x53, 0x01, 0x76, 0x8c, 0x28, 0x6a, + 0xe5, 0xc4, 0xee, 0x94, 0x67, 0xc7, 0xb1, 0x8c, 0xf5, 0x0f, 0x05, 0x28, 0x65, 0xbf, 0x4c, 0xd9, + 0x47, 0x52, 0x86, 0x62, 0xf7, 0xc2, 0x34, 0xed, 0x6e, 0x57, 0xfb, 0x8d, 0x54, 0x61, 0xe3, 0xa2, + 0xfd, 0xbc, 0xed, 0x5e, 0xb6, 0x69, 0xd7, 0x74, 0x3b, 0xb6, 0x56, 0x22, 0x7f, 0x41, 0xa5, 0xe1, + 0xb8, 0x0d, 0xda, 0x73, 0x5d, 0xea, 0x18, 0x5e, 0xd3, 0xd6, 0x3e, 0x17, 0xc9, 0x36, 0x54, 0x2d, + 0xdb, 0xb0, 0x9c, 0x56, 0xdb, 0xa6, 0xf6, 0x95, 0x69, 0xdb, 0x96, 0x6d, 0x69, 0x5f, 0x8a, 0xa4, + 0x06, 0x9b, 0x6d, 0xb7, 0x47, 0x0d, 0xfa, 0xd2, 0x70, 0x5a, 0x16, 0x35, 0x3a, 0x1d, 0xed, 0x6b, + 0x91, 0x90, 0xb9, 0xab, 0xed, 0x79, 0xae, 0xa7, 0x7d, 0x2b, 0x12, 0x0d, 0xca, 0x19, 0xd3, 0x71, + 0xdc, 0x4b, 0xdb, 0xd2, 0xbe, 0xcf, 0xb4, 0xad, 0xf3, 0x8e, 0x63, 0x9f, 0xdb, 0xed, 0x9e, 0x6d, + 0x69, 0x3f, 0x8a, 0xfa, 0x13, 0xa8, 0x76, 0x79, 0x20, 0x9e, 0xc9, 0xd8, 0x88, 0x22, 0x0f, 0xdf, + 0x8e, 0x30, 0x51, 0x44, 0x87, 0x8d, 0xeb, 0x89, 0xc2, 0x84, 0x2a, 0x49, 0x13, 0x1e, 0x88, 0xdd, + 0xc2, 0x61, 0xe1, 0x78, 0xdd, 0x2b, 0x67, 0x60, 0x4f, 0xa6, 0x02, 0xfd, 0x0a, 0xc8, 0xa2, 0x30, + 0x89, 0xa4, 0x48, 0x90, 0xfc, 0x0d, 0x7f, 0x0e, 0x70, 0x42, 0x85, 0x1f, 0x62, 0x26, 0x2a, 0x79, + 0xc5, 0x01, 0x4e, 0xda, 0x7e, 0x88, 0xe4, 0x7f, 0xd8, 0x4c, 0xbd, 0x7c, 0x35, 0x8a, 0x91, 0x66, + 0x4e, 0xbb, 0xbf, 0x67, 0xb6, 0x95, 0x19, 0xdc, 0x48, 0x51, 0xfd, 0x3f, 0x38, 0x6a, 0xa2, 0xea, + 0x8c, 0xae, 0x87, 0x9c, 0x99, 0x18, 0x2b, 0x7e, 0xc3, 0x99, 0xaf, 0x70, 0x29, 0xa2, 0xfe, 0x1a, + 0xaa, 0xf7, 0x18, 0x0f, 0xbd, 0xfd, 0x14, 0x6a, 0xe3, 0xb3, 0xd3, 0xa7, 0x94, 0xcd, 0xe9, 0x34, + 0xc2, 0x30, 0x8b, 0x50, 0xf2, 0x48, 0x3a, 0x5b, 0x70, 0xea, 0x60, 0xa8, 0x7f, 0x2c, 0x80, 0xfe, + 0x50, 0x8e, 0x7c, 0xe3, 0x1e, 0xec, 0x44, 0x19, 0x65, 0xc9, 0x7a, 0xc8, 0x13, 0xb5, 0x5b, 0x38, + 0x5c, 0x3b, 0x2e, 0x3f, 0xde, 0xab, 0xcf, 0xce, 0xa6, 0x7e, 0xcf, 0xcc, 0xdb, 0x8a, 0xee, 0x42, + 0x0e, 0x4f, 0x14, 0x31, 0xe1, 0x20, 0xf4, 0xc7, 0x94, 0x0d, 0x39, 0x0a, 0x45, 0x99, 0xcf, 0xde, + 0x20, 0x55, 0x3c, 0x44, 0xca, 0x05, 0x4d, 0x90, 0x49, 0xd1, 0xcf, 0x92, 0xaf, 0x79, 0xff, 0x84, + 0xfe, 0xd8, 0xcc, 0x58, 0x66, 0x4a, 0xea, 0xf1, 0x10, 0x5b, 0xa2, 0x9b, 0x31, 0xf4, 0x7d, 0xd8, + 0x6b, 0xa2, 0xca, 0x6f, 0xd3, 0x60, 0x4c, 0x8e, 0x84, 0x4a, 0xcb, 0xb8, 0xed, 0xf0, 0x05, 0xfc, + 0xbb, 0x62, 0x9e, 0xef, 0x76, 0x0a, 0xb5, 0xfc, 0x1f, 0x40, 0xfd, 0xe9, 0x78, 0xb1, 0x5b, 0x92, + 0xdc, 0x53, 0xea, 0xef, 0x0b, 0xb0, 0xd5, 0x44, 0x65, 0x30, 0x86, 0x49, 0xd2, 0x93, 0x03, 0x14, + 0xb7, 0x37, 0x55, 0x83, 0x3f, 0x12, 0x26, 0x23, 0xcc, 0x5a, 0x29, 0x79, 0xd3, 0x07, 0xf2, 0x08, + 0xc8, 0xdd, 0x37, 0xf0, 0xdb, 0xd5, 0xb4, 0x65, 0xff, 0x56, 0x7f, 0x65, 0x9e, 0xb5, 0x95, 0x79, + 0xfa, 0xb0, 0x7d, 0x37, 0x4e, 0xbe, 0xdb, 0x11, 0xac, 0xfb, 0x19, 0x4c, 0x55, 0x8a, 0xe7, 0x3b, + 0x95, 0xfd, 0x39, 0x35, 0xbd, 0x58, 0x1c, 0x47, 0x3c, 0xf6, 0x15, 0x97, 0x22, 0xab, 0x3f, 0x4f, + 0x56, 0x99, 0xc3, 0x69, 0xe1, 0xfa, 0x21, 0xec, 0x37, 0x51, 0x59, 0x78, 0xe3, 0x8f, 0x86, 0xaa, + 0xc9, 0x92, 0xc6, 0x88, 0x0d, 0x70, 0xa9, 0xea, 0x2b, 0x38, 0x58, 0xc9, 0xc8, 0x03, 0x9d, 0xc1, + 0x4e, 0x7f, 0x3a, 0xa7, 0x01, 0x4b, 0xe8, 0x75, 0xc6, 0x58, 0xec, 0xbb, 0xd6, 0xff, 0x85, 0xbc, + 0x51, 0x79, 0xb5, 0xbe, 0xf8, 0xc9, 0xfa, 0x19, 0x00, 0x00, 0xff, 0xff, 0x37, 0x4c, 0x56, 0x38, + 0xf3, 0x04, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.proto b/vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.proto new file mode 100644 index 0000000000..19610ca5b7 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.proto @@ -0,0 +1,64 @@ +syntax = "proto2"; +option go_package = "app_identity"; + +package appengine; + +message AppIdentityServiceError { + enum ErrorCode { + SUCCESS = 0; + UNKNOWN_SCOPE = 9; + BLOB_TOO_LARGE = 1000; + DEADLINE_EXCEEDED = 1001; + NOT_A_VALID_APP = 1002; + UNKNOWN_ERROR = 1003; + NOT_ALLOWED = 1005; + NOT_IMPLEMENTED = 1006; + } +} + +message SignForAppRequest { + optional bytes bytes_to_sign = 1; +} + +message SignForAppResponse { + optional string key_name = 1; + optional bytes signature_bytes = 2; +} + +message GetPublicCertificateForAppRequest { +} + +message PublicCertificate { + optional string key_name = 1; + optional string x509_certificate_pem = 2; +} + +message GetPublicCertificateForAppResponse { + repeated PublicCertificate public_certificate_list = 1; + optional int64 max_client_cache_time_in_second = 2; +} + +message GetServiceAccountNameRequest { +} + +message GetServiceAccountNameResponse { + optional string service_account_name = 1; +} + +message GetAccessTokenRequest { + repeated string scope = 1; + optional int64 service_account_id = 2; + optional string service_account_name = 3; +} + +message GetAccessTokenResponse { + optional string access_token = 1; + optional int64 expiration_time = 2; +} + +message GetDefaultGcsBucketNameRequest { +} + +message GetDefaultGcsBucketNameResponse { + optional string default_gcs_bucket_name = 1; +} diff --git a/vendor/google.golang.org/appengine/internal/base/api_base.pb.go b/vendor/google.golang.org/appengine/internal/base/api_base.pb.go new file mode 100644 index 0000000000..6205a7aee5 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/base/api_base.pb.go @@ -0,0 +1,176 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/base/api_base.proto + +/* +Package base is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/base/api_base.proto + +It has these top-level messages: + StringProto + Integer32Proto + Integer64Proto + BoolProto + DoubleProto + BytesProto + VoidProto +*/ +package base + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type StringProto struct { + Value *string `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *StringProto) Reset() { *m = StringProto{} } +func (m *StringProto) String() string { return proto.CompactTextString(m) } +func (*StringProto) ProtoMessage() {} +func (*StringProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *StringProto) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +type Integer32Proto struct { + Value *int32 `protobuf:"varint,1,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Integer32Proto) Reset() { *m = Integer32Proto{} } +func (m *Integer32Proto) String() string { return proto.CompactTextString(m) } +func (*Integer32Proto) ProtoMessage() {} +func (*Integer32Proto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *Integer32Proto) GetValue() int32 { + if m != nil && m.Value != nil { + return *m.Value + } + return 0 +} + +type Integer64Proto struct { + Value *int64 `protobuf:"varint,1,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Integer64Proto) Reset() { *m = Integer64Proto{} } +func (m *Integer64Proto) String() string { return proto.CompactTextString(m) } +func (*Integer64Proto) ProtoMessage() {} +func (*Integer64Proto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *Integer64Proto) GetValue() int64 { + if m != nil && m.Value != nil { + return *m.Value + } + return 0 +} + +type BoolProto struct { + Value *bool `protobuf:"varint,1,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BoolProto) Reset() { *m = BoolProto{} } +func (m *BoolProto) String() string { return proto.CompactTextString(m) } +func (*BoolProto) ProtoMessage() {} +func (*BoolProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *BoolProto) GetValue() bool { + if m != nil && m.Value != nil { + return *m.Value + } + return false +} + +type DoubleProto struct { + Value *float64 `protobuf:"fixed64,1,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DoubleProto) Reset() { *m = DoubleProto{} } +func (m *DoubleProto) String() string { return proto.CompactTextString(m) } +func (*DoubleProto) ProtoMessage() {} +func (*DoubleProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *DoubleProto) GetValue() float64 { + if m != nil && m.Value != nil { + return *m.Value + } + return 0 +} + +type BytesProto struct { + Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BytesProto) Reset() { *m = BytesProto{} } +func (m *BytesProto) String() string { return proto.CompactTextString(m) } +func (*BytesProto) ProtoMessage() {} +func (*BytesProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *BytesProto) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type VoidProto struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *VoidProto) Reset() { *m = VoidProto{} } +func (m *VoidProto) String() string { return proto.CompactTextString(m) } +func (*VoidProto) ProtoMessage() {} +func (*VoidProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func init() { + proto.RegisterType((*StringProto)(nil), "appengine.base.StringProto") + proto.RegisterType((*Integer32Proto)(nil), "appengine.base.Integer32Proto") + proto.RegisterType((*Integer64Proto)(nil), "appengine.base.Integer64Proto") + proto.RegisterType((*BoolProto)(nil), "appengine.base.BoolProto") + proto.RegisterType((*DoubleProto)(nil), "appengine.base.DoubleProto") + proto.RegisterType((*BytesProto)(nil), "appengine.base.BytesProto") + proto.RegisterType((*VoidProto)(nil), "appengine.base.VoidProto") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/base/api_base.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 199 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0xcf, 0x3f, 0x4b, 0xc6, 0x30, + 0x10, 0x06, 0x70, 0x5a, 0xad, 0xb4, 0x57, 0xe9, 0x20, 0x0e, 0x1d, 0xb5, 0x05, 0x71, 0x4a, 0x40, + 0x45, 0x9c, 0x83, 0x8b, 0x9b, 0x28, 0x38, 0xb8, 0x48, 0x8a, 0xc7, 0x11, 0x08, 0xb9, 0x90, 0xa6, + 0x82, 0xdf, 0x5e, 0xda, 0xd2, 0xfa, 0xc2, 0x9b, 0xed, 0xfe, 0xfc, 0xe0, 0xe1, 0x81, 0x27, 0x62, + 0x26, 0x8b, 0x82, 0xd8, 0x6a, 0x47, 0x82, 0x03, 0x49, 0xed, 0x3d, 0x3a, 0x32, 0x0e, 0xa5, 0x71, + 0x11, 0x83, 0xd3, 0x56, 0x0e, 0x7a, 0x44, 0xa9, 0xbd, 0xf9, 0x9a, 0x07, 0xe1, 0x03, 0x47, 0xbe, + 0x68, 0x76, 0x27, 0xe6, 0x6b, 0xd7, 0x43, 0xfd, 0x1e, 0x83, 0x71, 0xf4, 0xba, 0xbc, 0x2f, 0xa1, + 0xf8, 0xd1, 0x76, 0xc2, 0x36, 0xbb, 0xca, 0x6f, 0xab, 0xb7, 0x75, 0xe9, 0x6e, 0xa0, 0x79, 0x71, + 0x11, 0x09, 0xc3, 0xfd, 0x5d, 0xc2, 0x15, 0xc7, 0xee, 0xf1, 0x21, 0xe1, 0x4e, 0x36, 0x77, 0x0d, + 0x95, 0x62, 0xb6, 0x09, 0x52, 0x6e, 0xa4, 0x87, 0xfa, 0x99, 0xa7, 0xc1, 0x62, 0x02, 0x65, 0xff, + 0x79, 0xa0, 0x7e, 0x23, 0x8e, 0xab, 0x69, 0x0f, 0xcd, 0xb9, 0xca, 0xcb, 0xdd, 0xd5, 0x50, 0x7d, + 0xb0, 0xf9, 0x5e, 0x98, 0x3a, 0xfb, 0x3c, 0x9d, 0x9b, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xba, + 0x37, 0x25, 0xea, 0x44, 0x01, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/base/api_base.proto b/vendor/google.golang.org/appengine/internal/base/api_base.proto new file mode 100644 index 0000000000..56cd7a3cad --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/base/api_base.proto @@ -0,0 +1,33 @@ +// Built-in base types for API calls. Primarily useful as return types. + +syntax = "proto2"; +option go_package = "base"; + +package appengine.base; + +message StringProto { + required string value = 1; +} + +message Integer32Proto { + required int32 value = 1; +} + +message Integer64Proto { + required int64 value = 1; +} + +message BoolProto { + required bool value = 1; +} + +message DoubleProto { + required double value = 1; +} + +message BytesProto { + required bytes value = 1 [ctype=CORD]; +} + +message VoidProto { +} diff --git a/vendor/google.golang.org/appengine/internal/blobstore/blobstore_service.pb.go b/vendor/google.golang.org/appengine/internal/blobstore/blobstore_service.pb.go new file mode 100644 index 0000000000..ac5ff6e505 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/blobstore/blobstore_service.pb.go @@ -0,0 +1,438 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/blobstore/blobstore_service.proto + +/* +Package blobstore is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/blobstore/blobstore_service.proto + +It has these top-level messages: + BlobstoreServiceError + CreateUploadURLRequest + CreateUploadURLResponse + DeleteBlobRequest + FetchDataRequest + FetchDataResponse + CloneBlobRequest + CloneBlobResponse + DecodeBlobKeyRequest + DecodeBlobKeyResponse + CreateEncodedGoogleStorageKeyRequest + CreateEncodedGoogleStorageKeyResponse +*/ +package blobstore + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type BlobstoreServiceError_ErrorCode int32 + +const ( + BlobstoreServiceError_OK BlobstoreServiceError_ErrorCode = 0 + BlobstoreServiceError_INTERNAL_ERROR BlobstoreServiceError_ErrorCode = 1 + BlobstoreServiceError_URL_TOO_LONG BlobstoreServiceError_ErrorCode = 2 + BlobstoreServiceError_PERMISSION_DENIED BlobstoreServiceError_ErrorCode = 3 + BlobstoreServiceError_BLOB_NOT_FOUND BlobstoreServiceError_ErrorCode = 4 + BlobstoreServiceError_DATA_INDEX_OUT_OF_RANGE BlobstoreServiceError_ErrorCode = 5 + BlobstoreServiceError_BLOB_FETCH_SIZE_TOO_LARGE BlobstoreServiceError_ErrorCode = 6 + BlobstoreServiceError_ARGUMENT_OUT_OF_RANGE BlobstoreServiceError_ErrorCode = 8 + BlobstoreServiceError_INVALID_BLOB_KEY BlobstoreServiceError_ErrorCode = 9 +) + +var BlobstoreServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INTERNAL_ERROR", + 2: "URL_TOO_LONG", + 3: "PERMISSION_DENIED", + 4: "BLOB_NOT_FOUND", + 5: "DATA_INDEX_OUT_OF_RANGE", + 6: "BLOB_FETCH_SIZE_TOO_LARGE", + 8: "ARGUMENT_OUT_OF_RANGE", + 9: "INVALID_BLOB_KEY", +} +var BlobstoreServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INTERNAL_ERROR": 1, + "URL_TOO_LONG": 2, + "PERMISSION_DENIED": 3, + "BLOB_NOT_FOUND": 4, + "DATA_INDEX_OUT_OF_RANGE": 5, + "BLOB_FETCH_SIZE_TOO_LARGE": 6, + "ARGUMENT_OUT_OF_RANGE": 8, + "INVALID_BLOB_KEY": 9, +} + +func (x BlobstoreServiceError_ErrorCode) Enum() *BlobstoreServiceError_ErrorCode { + p := new(BlobstoreServiceError_ErrorCode) + *p = x + return p +} +func (x BlobstoreServiceError_ErrorCode) String() string { + return proto.EnumName(BlobstoreServiceError_ErrorCode_name, int32(x)) +} +func (x *BlobstoreServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(BlobstoreServiceError_ErrorCode_value, data, "BlobstoreServiceError_ErrorCode") + if err != nil { + return err + } + *x = BlobstoreServiceError_ErrorCode(value) + return nil +} +func (BlobstoreServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type BlobstoreServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *BlobstoreServiceError) Reset() { *m = BlobstoreServiceError{} } +func (m *BlobstoreServiceError) String() string { return proto.CompactTextString(m) } +func (*BlobstoreServiceError) ProtoMessage() {} +func (*BlobstoreServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type CreateUploadURLRequest struct { + SuccessPath *string `protobuf:"bytes,1,req,name=success_path,json=successPath" json:"success_path,omitempty"` + MaxUploadSizeBytes *int64 `protobuf:"varint,2,opt,name=max_upload_size_bytes,json=maxUploadSizeBytes" json:"max_upload_size_bytes,omitempty"` + MaxUploadSizePerBlobBytes *int64 `protobuf:"varint,3,opt,name=max_upload_size_per_blob_bytes,json=maxUploadSizePerBlobBytes" json:"max_upload_size_per_blob_bytes,omitempty"` + GsBucketName *string `protobuf:"bytes,4,opt,name=gs_bucket_name,json=gsBucketName" json:"gs_bucket_name,omitempty"` + UrlExpiryTimeSeconds *int32 `protobuf:"varint,5,opt,name=url_expiry_time_seconds,json=urlExpiryTimeSeconds" json:"url_expiry_time_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateUploadURLRequest) Reset() { *m = CreateUploadURLRequest{} } +func (m *CreateUploadURLRequest) String() string { return proto.CompactTextString(m) } +func (*CreateUploadURLRequest) ProtoMessage() {} +func (*CreateUploadURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *CreateUploadURLRequest) GetSuccessPath() string { + if m != nil && m.SuccessPath != nil { + return *m.SuccessPath + } + return "" +} + +func (m *CreateUploadURLRequest) GetMaxUploadSizeBytes() int64 { + if m != nil && m.MaxUploadSizeBytes != nil { + return *m.MaxUploadSizeBytes + } + return 0 +} + +func (m *CreateUploadURLRequest) GetMaxUploadSizePerBlobBytes() int64 { + if m != nil && m.MaxUploadSizePerBlobBytes != nil { + return *m.MaxUploadSizePerBlobBytes + } + return 0 +} + +func (m *CreateUploadURLRequest) GetGsBucketName() string { + if m != nil && m.GsBucketName != nil { + return *m.GsBucketName + } + return "" +} + +func (m *CreateUploadURLRequest) GetUrlExpiryTimeSeconds() int32 { + if m != nil && m.UrlExpiryTimeSeconds != nil { + return *m.UrlExpiryTimeSeconds + } + return 0 +} + +type CreateUploadURLResponse struct { + Url *string `protobuf:"bytes,1,req,name=url" json:"url,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateUploadURLResponse) Reset() { *m = CreateUploadURLResponse{} } +func (m *CreateUploadURLResponse) String() string { return proto.CompactTextString(m) } +func (*CreateUploadURLResponse) ProtoMessage() {} +func (*CreateUploadURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *CreateUploadURLResponse) GetUrl() string { + if m != nil && m.Url != nil { + return *m.Url + } + return "" +} + +type DeleteBlobRequest struct { + BlobKey []string `protobuf:"bytes,1,rep,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + Token *string `protobuf:"bytes,2,opt,name=token" json:"token,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteBlobRequest) Reset() { *m = DeleteBlobRequest{} } +func (m *DeleteBlobRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteBlobRequest) ProtoMessage() {} +func (*DeleteBlobRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *DeleteBlobRequest) GetBlobKey() []string { + if m != nil { + return m.BlobKey + } + return nil +} + +func (m *DeleteBlobRequest) GetToken() string { + if m != nil && m.Token != nil { + return *m.Token + } + return "" +} + +type FetchDataRequest struct { + BlobKey *string `protobuf:"bytes,1,req,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + StartIndex *int64 `protobuf:"varint,2,req,name=start_index,json=startIndex" json:"start_index,omitempty"` + EndIndex *int64 `protobuf:"varint,3,req,name=end_index,json=endIndex" json:"end_index,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FetchDataRequest) Reset() { *m = FetchDataRequest{} } +func (m *FetchDataRequest) String() string { return proto.CompactTextString(m) } +func (*FetchDataRequest) ProtoMessage() {} +func (*FetchDataRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *FetchDataRequest) GetBlobKey() string { + if m != nil && m.BlobKey != nil { + return *m.BlobKey + } + return "" +} + +func (m *FetchDataRequest) GetStartIndex() int64 { + if m != nil && m.StartIndex != nil { + return *m.StartIndex + } + return 0 +} + +func (m *FetchDataRequest) GetEndIndex() int64 { + if m != nil && m.EndIndex != nil { + return *m.EndIndex + } + return 0 +} + +type FetchDataResponse struct { + Data []byte `protobuf:"bytes,1000,req,name=data" json:"data,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FetchDataResponse) Reset() { *m = FetchDataResponse{} } +func (m *FetchDataResponse) String() string { return proto.CompactTextString(m) } +func (*FetchDataResponse) ProtoMessage() {} +func (*FetchDataResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *FetchDataResponse) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type CloneBlobRequest struct { + BlobKey []byte `protobuf:"bytes,1,req,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + MimeType []byte `protobuf:"bytes,2,req,name=mime_type,json=mimeType" json:"mime_type,omitempty"` + TargetAppId []byte `protobuf:"bytes,3,req,name=target_app_id,json=targetAppId" json:"target_app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CloneBlobRequest) Reset() { *m = CloneBlobRequest{} } +func (m *CloneBlobRequest) String() string { return proto.CompactTextString(m) } +func (*CloneBlobRequest) ProtoMessage() {} +func (*CloneBlobRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *CloneBlobRequest) GetBlobKey() []byte { + if m != nil { + return m.BlobKey + } + return nil +} + +func (m *CloneBlobRequest) GetMimeType() []byte { + if m != nil { + return m.MimeType + } + return nil +} + +func (m *CloneBlobRequest) GetTargetAppId() []byte { + if m != nil { + return m.TargetAppId + } + return nil +} + +type CloneBlobResponse struct { + BlobKey []byte `protobuf:"bytes,1,req,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CloneBlobResponse) Reset() { *m = CloneBlobResponse{} } +func (m *CloneBlobResponse) String() string { return proto.CompactTextString(m) } +func (*CloneBlobResponse) ProtoMessage() {} +func (*CloneBlobResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *CloneBlobResponse) GetBlobKey() []byte { + if m != nil { + return m.BlobKey + } + return nil +} + +type DecodeBlobKeyRequest struct { + BlobKey []string `protobuf:"bytes,1,rep,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DecodeBlobKeyRequest) Reset() { *m = DecodeBlobKeyRequest{} } +func (m *DecodeBlobKeyRequest) String() string { return proto.CompactTextString(m) } +func (*DecodeBlobKeyRequest) ProtoMessage() {} +func (*DecodeBlobKeyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *DecodeBlobKeyRequest) GetBlobKey() []string { + if m != nil { + return m.BlobKey + } + return nil +} + +type DecodeBlobKeyResponse struct { + Decoded []string `protobuf:"bytes,1,rep,name=decoded" json:"decoded,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DecodeBlobKeyResponse) Reset() { *m = DecodeBlobKeyResponse{} } +func (m *DecodeBlobKeyResponse) String() string { return proto.CompactTextString(m) } +func (*DecodeBlobKeyResponse) ProtoMessage() {} +func (*DecodeBlobKeyResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *DecodeBlobKeyResponse) GetDecoded() []string { + if m != nil { + return m.Decoded + } + return nil +} + +type CreateEncodedGoogleStorageKeyRequest struct { + Filename *string `protobuf:"bytes,1,req,name=filename" json:"filename,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateEncodedGoogleStorageKeyRequest) Reset() { *m = CreateEncodedGoogleStorageKeyRequest{} } +func (m *CreateEncodedGoogleStorageKeyRequest) String() string { return proto.CompactTextString(m) } +func (*CreateEncodedGoogleStorageKeyRequest) ProtoMessage() {} +func (*CreateEncodedGoogleStorageKeyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{10} +} + +func (m *CreateEncodedGoogleStorageKeyRequest) GetFilename() string { + if m != nil && m.Filename != nil { + return *m.Filename + } + return "" +} + +type CreateEncodedGoogleStorageKeyResponse struct { + BlobKey *string `protobuf:"bytes,1,req,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateEncodedGoogleStorageKeyResponse) Reset() { *m = CreateEncodedGoogleStorageKeyResponse{} } +func (m *CreateEncodedGoogleStorageKeyResponse) String() string { return proto.CompactTextString(m) } +func (*CreateEncodedGoogleStorageKeyResponse) ProtoMessage() {} +func (*CreateEncodedGoogleStorageKeyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{11} +} + +func (m *CreateEncodedGoogleStorageKeyResponse) GetBlobKey() string { + if m != nil && m.BlobKey != nil { + return *m.BlobKey + } + return "" +} + +func init() { + proto.RegisterType((*BlobstoreServiceError)(nil), "appengine.BlobstoreServiceError") + proto.RegisterType((*CreateUploadURLRequest)(nil), "appengine.CreateUploadURLRequest") + proto.RegisterType((*CreateUploadURLResponse)(nil), "appengine.CreateUploadURLResponse") + proto.RegisterType((*DeleteBlobRequest)(nil), "appengine.DeleteBlobRequest") + proto.RegisterType((*FetchDataRequest)(nil), "appengine.FetchDataRequest") + proto.RegisterType((*FetchDataResponse)(nil), "appengine.FetchDataResponse") + proto.RegisterType((*CloneBlobRequest)(nil), "appengine.CloneBlobRequest") + proto.RegisterType((*CloneBlobResponse)(nil), "appengine.CloneBlobResponse") + proto.RegisterType((*DecodeBlobKeyRequest)(nil), "appengine.DecodeBlobKeyRequest") + proto.RegisterType((*DecodeBlobKeyResponse)(nil), "appengine.DecodeBlobKeyResponse") + proto.RegisterType((*CreateEncodedGoogleStorageKeyRequest)(nil), "appengine.CreateEncodedGoogleStorageKeyRequest") + proto.RegisterType((*CreateEncodedGoogleStorageKeyResponse)(nil), "appengine.CreateEncodedGoogleStorageKeyResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/blobstore/blobstore_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 737 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xe1, 0x6e, 0xe3, 0x44, + 0x10, 0xc6, 0x4e, 0x7b, 0x8d, 0xa7, 0xe1, 0xe4, 0xae, 0x1a, 0x9a, 0x52, 0x01, 0xc1, 0x3a, 0xa4, + 0x48, 0xa0, 0x56, 0xfd, 0xc1, 0x03, 0xd8, 0xb5, 0x13, 0xac, 0xe6, 0xec, 0x6a, 0xe3, 0x20, 0xb8, + 0x3f, 0xab, 0x6d, 0x3c, 0xb8, 0x56, 0x1d, 0xaf, 0x59, 0x6f, 0x50, 0x73, 0x0f, 0xc1, 0xbb, 0xf1, + 0x16, 0x48, 0xbc, 0x04, 0xf2, 0xda, 0x6d, 0x73, 0x07, 0x77, 0xf7, 0x6f, 0xe7, 0xfb, 0xf6, 0x9b, + 0xf9, 0x66, 0x66, 0xb5, 0x30, 0xcd, 0x84, 0xc8, 0x0a, 0x3c, 0xcf, 0x44, 0xc1, 0xcb, 0xec, 0x5c, + 0xc8, 0xec, 0x82, 0x57, 0x15, 0x96, 0x59, 0x5e, 0xe2, 0x45, 0x5e, 0x2a, 0x94, 0x25, 0x2f, 0x2e, + 0x6e, 0x0b, 0x71, 0x5b, 0x2b, 0x21, 0xf1, 0xf9, 0xc4, 0x6a, 0x94, 0x7f, 0xe4, 0x2b, 0x3c, 0xaf, + 0xa4, 0x50, 0x82, 0x58, 0x4f, 0x2a, 0xe7, 0x1f, 0x03, 0x86, 0xde, 0xe3, 0xb5, 0x45, 0x7b, 0x2b, + 0x90, 0x52, 0x48, 0xe7, 0x2f, 0x03, 0x2c, 0x7d, 0xba, 0x12, 0x29, 0x92, 0x17, 0x60, 0xc6, 0xd7, + 0xf6, 0x67, 0x84, 0xc0, 0xcb, 0x30, 0x4a, 0x02, 0x1a, 0xb9, 0x73, 0x16, 0x50, 0x1a, 0x53, 0xdb, + 0x20, 0x36, 0x0c, 0x96, 0x74, 0xce, 0x92, 0x38, 0x66, 0xf3, 0x38, 0x9a, 0xd9, 0x26, 0x19, 0xc2, + 0xd1, 0x4d, 0x40, 0x5f, 0x87, 0x8b, 0x45, 0x18, 0x47, 0xcc, 0x0f, 0xa2, 0x30, 0xf0, 0xed, 0x5e, + 0x23, 0xf6, 0xe6, 0xb1, 0xc7, 0xa2, 0x38, 0x61, 0xd3, 0x78, 0x19, 0xf9, 0xf6, 0x1e, 0x39, 0x83, + 0x13, 0xdf, 0x4d, 0x5c, 0x16, 0x46, 0x7e, 0xf0, 0x0b, 0x8b, 0x97, 0x09, 0x8b, 0xa7, 0x8c, 0xba, + 0xd1, 0x2c, 0xb0, 0xf7, 0xc9, 0x57, 0x70, 0xaa, 0x05, 0xd3, 0x20, 0xb9, 0xfa, 0x89, 0x2d, 0xc2, + 0x37, 0x41, 0x5b, 0xc5, 0xa5, 0xb3, 0xc0, 0x7e, 0x41, 0x4e, 0x61, 0xe8, 0xd2, 0xd9, 0xf2, 0x75, + 0x10, 0x25, 0xef, 0x2a, 0xfb, 0xe4, 0x18, 0xec, 0x30, 0xfa, 0xd9, 0x9d, 0x87, 0x3e, 0xd3, 0x19, + 0xae, 0x83, 0x5f, 0x6d, 0xcb, 0xf9, 0xd3, 0x84, 0x2f, 0xae, 0x24, 0x72, 0x85, 0xcb, 0xaa, 0x10, + 0x3c, 0x5d, 0xd2, 0x39, 0xc5, 0xdf, 0x37, 0x58, 0x2b, 0xf2, 0x2d, 0x0c, 0xea, 0xcd, 0x6a, 0x85, + 0x75, 0xcd, 0x2a, 0xae, 0xee, 0x46, 0xc6, 0xd8, 0x9c, 0x58, 0xf4, 0xb0, 0xc3, 0x6e, 0xb8, 0xba, + 0x23, 0x97, 0x30, 0x5c, 0xf3, 0x07, 0xb6, 0xd1, 0x52, 0x56, 0xe7, 0x6f, 0x91, 0xdd, 0x6e, 0x15, + 0xd6, 0x23, 0x73, 0x6c, 0x4c, 0x7a, 0x94, 0xac, 0xf9, 0x43, 0x9b, 0x76, 0x91, 0xbf, 0x45, 0xaf, + 0x61, 0x88, 0x0b, 0x5f, 0xbf, 0x2f, 0xa9, 0x50, 0xb2, 0x66, 0x31, 0x9d, 0xb6, 0xa7, 0xb5, 0xa7, + 0xef, 0x68, 0x6f, 0x50, 0x36, 0x3b, 0x69, 0x53, 0xbc, 0x82, 0x97, 0x59, 0xcd, 0x6e, 0x37, 0xab, + 0x7b, 0x54, 0xac, 0xe4, 0x6b, 0x1c, 0xed, 0x8d, 0x8d, 0x89, 0x45, 0x07, 0x59, 0xed, 0x69, 0x30, + 0xe2, 0x6b, 0x24, 0x3f, 0xc2, 0xc9, 0x46, 0x16, 0x0c, 0x1f, 0xaa, 0x5c, 0x6e, 0x99, 0xca, 0xd7, + 0xcd, 0xce, 0x57, 0xa2, 0x4c, 0xeb, 0xd1, 0xfe, 0xd8, 0x98, 0xec, 0xd3, 0xe3, 0x8d, 0x2c, 0x02, + 0xcd, 0x26, 0xf9, 0x1a, 0x17, 0x2d, 0xe7, 0x7c, 0x0f, 0x27, 0xff, 0x99, 0x47, 0x5d, 0x89, 0xb2, + 0x46, 0x62, 0x43, 0x6f, 0x23, 0x8b, 0x6e, 0x0e, 0xcd, 0xd1, 0xf1, 0xe1, 0xc8, 0xc7, 0x02, 0x15, + 0x36, 0xe6, 0x1e, 0xe7, 0x76, 0x0a, 0x7d, 0xdd, 0xcd, 0x3d, 0x6e, 0x47, 0xc6, 0xb8, 0x37, 0xb1, + 0xe8, 0x41, 0x13, 0x5f, 0xe3, 0x96, 0x1c, 0xc3, 0xbe, 0x12, 0xf7, 0x58, 0xea, 0xf9, 0x58, 0xb4, + 0x0d, 0x9c, 0x7b, 0xb0, 0xa7, 0xa8, 0x56, 0x77, 0x3e, 0x57, 0xfc, 0xff, 0x93, 0x98, 0xbb, 0x49, + 0xbe, 0x81, 0xc3, 0x5a, 0x71, 0xa9, 0x58, 0x5e, 0xa6, 0xf8, 0x30, 0x32, 0xc7, 0xe6, 0xa4, 0x47, + 0x41, 0x43, 0x61, 0x83, 0x90, 0x33, 0xb0, 0xb0, 0x4c, 0x3b, 0xba, 0xa7, 0xe9, 0x3e, 0x96, 0xa9, + 0x26, 0x9d, 0x1f, 0xe0, 0x68, 0xa7, 0x58, 0xd7, 0xd9, 0x09, 0xec, 0xa5, 0x5c, 0xf1, 0xd1, 0xdf, + 0x07, 0x63, 0x73, 0x32, 0xf0, 0xcc, 0xbe, 0x41, 0x35, 0xe0, 0x94, 0x60, 0x5f, 0x15, 0xa2, 0xfc, + 0x48, 0x7f, 0xe6, 0x64, 0xf0, 0x6c, 0xed, 0x0c, 0xac, 0x75, 0x33, 0x68, 0xb5, 0xad, 0x50, 0x1b, + 0x1b, 0xd0, 0x7e, 0x03, 0x24, 0xdb, 0x0a, 0x89, 0x03, 0x9f, 0x2b, 0x2e, 0x33, 0x54, 0x8c, 0x57, + 0x15, 0xcb, 0x53, 0x6d, 0x6d, 0x40, 0x0f, 0x5b, 0xd0, 0xad, 0xaa, 0x30, 0x75, 0xce, 0xe1, 0x68, + 0xa7, 0x5e, 0xe7, 0xee, 0xc3, 0x05, 0x9d, 0x4b, 0x38, 0xf6, 0x71, 0x25, 0x52, 0x2d, 0xb8, 0xc6, + 0xed, 0xa7, 0x77, 0xe0, 0x5c, 0xc2, 0xf0, 0x3d, 0x49, 0x57, 0x66, 0x04, 0x07, 0xa9, 0x26, 0xd2, + 0x47, 0x49, 0x17, 0x3a, 0x1e, 0xbc, 0x6a, 0xdf, 0x44, 0x50, 0x6a, 0x60, 0xa6, 0x3f, 0x9d, 0x85, + 0x12, 0x92, 0x67, 0xb8, 0x53, 0xf5, 0x4b, 0xe8, 0xff, 0x96, 0x17, 0xa8, 0x9f, 0x64, 0xbb, 0xb4, + 0xa7, 0xd8, 0xf1, 0xe0, 0xbb, 0x4f, 0xe4, 0xf8, 0x40, 0xb7, 0xcf, 0xd6, 0xbd, 0xc3, 0x37, 0xd6, + 0xd3, 0x07, 0xf6, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xfb, 0x81, 0x94, 0xfb, 0x04, 0x00, + 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/blobstore/blobstore_service.proto b/vendor/google.golang.org/appengine/internal/blobstore/blobstore_service.proto new file mode 100644 index 0000000000..33b265032c --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/blobstore/blobstore_service.proto @@ -0,0 +1,71 @@ +syntax = "proto2"; +option go_package = "blobstore"; + +package appengine; + +message BlobstoreServiceError { + enum ErrorCode { + OK = 0; + INTERNAL_ERROR = 1; + URL_TOO_LONG = 2; + PERMISSION_DENIED = 3; + BLOB_NOT_FOUND = 4; + DATA_INDEX_OUT_OF_RANGE = 5; + BLOB_FETCH_SIZE_TOO_LARGE = 6; + ARGUMENT_OUT_OF_RANGE = 8; + INVALID_BLOB_KEY = 9; + } +} + +message CreateUploadURLRequest { + required string success_path = 1; + optional int64 max_upload_size_bytes = 2; + optional int64 max_upload_size_per_blob_bytes = 3; + optional string gs_bucket_name = 4; + optional int32 url_expiry_time_seconds = 5; +} + +message CreateUploadURLResponse { + required string url = 1; +} + +message DeleteBlobRequest { + repeated string blob_key = 1; + optional string token = 2; +} + +message FetchDataRequest { + required string blob_key = 1; + required int64 start_index = 2; + required int64 end_index = 3; +} + +message FetchDataResponse { + required bytes data = 1000 [ctype = CORD]; +} + +message CloneBlobRequest { + required bytes blob_key = 1; + required bytes mime_type = 2; + required bytes target_app_id = 3; +} + +message CloneBlobResponse { + required bytes blob_key = 1; +} + +message DecodeBlobKeyRequest { + repeated string blob_key = 1; +} + +message DecodeBlobKeyResponse { + repeated string decoded = 1; +} + +message CreateEncodedGoogleStorageKeyRequest { + required string filename = 1; +} + +message CreateEncodedGoogleStorageKeyResponse { + required string blob_key = 1; +} diff --git a/vendor/google.golang.org/appengine/internal/capability/capability_service.pb.go b/vendor/google.golang.org/appengine/internal/capability/capability_service.pb.go new file mode 100644 index 0000000000..4d888941f3 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/capability/capability_service.pb.go @@ -0,0 +1,171 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/capability/capability_service.proto + +/* +Package capability is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/capability/capability_service.proto + +It has these top-level messages: + IsEnabledRequest + IsEnabledResponse +*/ +package capability + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type IsEnabledResponse_SummaryStatus int32 + +const ( + IsEnabledResponse_DEFAULT IsEnabledResponse_SummaryStatus = 0 + IsEnabledResponse_ENABLED IsEnabledResponse_SummaryStatus = 1 + IsEnabledResponse_SCHEDULED_FUTURE IsEnabledResponse_SummaryStatus = 2 + IsEnabledResponse_SCHEDULED_NOW IsEnabledResponse_SummaryStatus = 3 + IsEnabledResponse_DISABLED IsEnabledResponse_SummaryStatus = 4 + IsEnabledResponse_UNKNOWN IsEnabledResponse_SummaryStatus = 5 +) + +var IsEnabledResponse_SummaryStatus_name = map[int32]string{ + 0: "DEFAULT", + 1: "ENABLED", + 2: "SCHEDULED_FUTURE", + 3: "SCHEDULED_NOW", + 4: "DISABLED", + 5: "UNKNOWN", +} +var IsEnabledResponse_SummaryStatus_value = map[string]int32{ + "DEFAULT": 0, + "ENABLED": 1, + "SCHEDULED_FUTURE": 2, + "SCHEDULED_NOW": 3, + "DISABLED": 4, + "UNKNOWN": 5, +} + +func (x IsEnabledResponse_SummaryStatus) Enum() *IsEnabledResponse_SummaryStatus { + p := new(IsEnabledResponse_SummaryStatus) + *p = x + return p +} +func (x IsEnabledResponse_SummaryStatus) String() string { + return proto.EnumName(IsEnabledResponse_SummaryStatus_name, int32(x)) +} +func (x *IsEnabledResponse_SummaryStatus) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IsEnabledResponse_SummaryStatus_value, data, "IsEnabledResponse_SummaryStatus") + if err != nil { + return err + } + *x = IsEnabledResponse_SummaryStatus(value) + return nil +} +func (IsEnabledResponse_SummaryStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{1, 0} +} + +type IsEnabledRequest struct { + Package *string `protobuf:"bytes,1,req,name=package" json:"package,omitempty"` + Capability []string `protobuf:"bytes,2,rep,name=capability" json:"capability,omitempty"` + Call []string `protobuf:"bytes,3,rep,name=call" json:"call,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IsEnabledRequest) Reset() { *m = IsEnabledRequest{} } +func (m *IsEnabledRequest) String() string { return proto.CompactTextString(m) } +func (*IsEnabledRequest) ProtoMessage() {} +func (*IsEnabledRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *IsEnabledRequest) GetPackage() string { + if m != nil && m.Package != nil { + return *m.Package + } + return "" +} + +func (m *IsEnabledRequest) GetCapability() []string { + if m != nil { + return m.Capability + } + return nil +} + +func (m *IsEnabledRequest) GetCall() []string { + if m != nil { + return m.Call + } + return nil +} + +type IsEnabledResponse struct { + SummaryStatus *IsEnabledResponse_SummaryStatus `protobuf:"varint,1,opt,name=summary_status,json=summaryStatus,enum=appengine.IsEnabledResponse_SummaryStatus" json:"summary_status,omitempty"` + TimeUntilScheduled *int64 `protobuf:"varint,2,opt,name=time_until_scheduled,json=timeUntilScheduled" json:"time_until_scheduled,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IsEnabledResponse) Reset() { *m = IsEnabledResponse{} } +func (m *IsEnabledResponse) String() string { return proto.CompactTextString(m) } +func (*IsEnabledResponse) ProtoMessage() {} +func (*IsEnabledResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *IsEnabledResponse) GetSummaryStatus() IsEnabledResponse_SummaryStatus { + if m != nil && m.SummaryStatus != nil { + return *m.SummaryStatus + } + return IsEnabledResponse_DEFAULT +} + +func (m *IsEnabledResponse) GetTimeUntilScheduled() int64 { + if m != nil && m.TimeUntilScheduled != nil { + return *m.TimeUntilScheduled + } + return 0 +} + +func init() { + proto.RegisterType((*IsEnabledRequest)(nil), "appengine.IsEnabledRequest") + proto.RegisterType((*IsEnabledResponse)(nil), "appengine.IsEnabledResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/capability/capability_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 359 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xd1, 0x8a, 0x9b, 0x40, + 0x14, 0x86, 0xa3, 0xa6, 0xa4, 0x9e, 0x26, 0xc1, 0x0c, 0xb9, 0x90, 0xb6, 0x14, 0xf1, 0x4a, 0x7a, + 0x61, 0x4a, 0xde, 0x20, 0x89, 0x86, 0x84, 0x06, 0x43, 0x35, 0x12, 0x28, 0x14, 0x3b, 0x31, 0x83, + 0x95, 0x8e, 0xa3, 0xeb, 0x8c, 0x0b, 0x79, 0x82, 0x7d, 0xed, 0x45, 0x43, 0x8c, 0xcb, 0x2e, 0x7b, + 0x77, 0xce, 0xf9, 0xf9, 0xfe, 0x99, 0x73, 0x7e, 0xd8, 0x24, 0x79, 0x9e, 0x50, 0x62, 0x27, 0x39, + 0xc5, 0x2c, 0xb1, 0xf3, 0x32, 0x99, 0xe1, 0xa2, 0x20, 0x2c, 0x49, 0x19, 0x99, 0xa5, 0x4c, 0x90, + 0x92, 0x61, 0x3a, 0x8b, 0x71, 0x81, 0x4f, 0x29, 0x4d, 0xc5, 0xa5, 0x53, 0x46, 0x9c, 0x94, 0x8f, + 0x69, 0x4c, 0xec, 0xa2, 0xcc, 0x45, 0x8e, 0xd4, 0x96, 0x33, 0xff, 0x82, 0xb6, 0xe5, 0x2e, 0xc3, + 0x27, 0x4a, 0xce, 0x3e, 0x79, 0xa8, 0x08, 0x17, 0x48, 0x87, 0x41, 0x81, 0xe3, 0xff, 0x38, 0x21, + 0xba, 0x64, 0xc8, 0x96, 0xea, 0xdf, 0x5a, 0xf4, 0x0d, 0xe0, 0x6e, 0xaa, 0xcb, 0x86, 0x62, 0xa9, + 0x7e, 0x67, 0x82, 0x10, 0xf4, 0x63, 0x4c, 0xa9, 0xae, 0x34, 0x4a, 0x53, 0x9b, 0x4f, 0x32, 0x4c, + 0x3a, 0x4f, 0xf0, 0x22, 0x67, 0x9c, 0xa0, 0x5f, 0x30, 0xe6, 0x55, 0x96, 0xe1, 0xf2, 0x12, 0x71, + 0x81, 0x45, 0xc5, 0x75, 0xc9, 0x90, 0xac, 0xf1, 0xfc, 0xbb, 0xdd, 0xfe, 0xcd, 0x7e, 0x45, 0xd9, + 0xc1, 0x15, 0x09, 0x1a, 0xc2, 0x1f, 0xf1, 0x6e, 0x8b, 0x7e, 0xc0, 0x54, 0xa4, 0x19, 0x89, 0x2a, + 0x26, 0x52, 0x1a, 0xf1, 0xf8, 0x1f, 0x39, 0x57, 0x94, 0x9c, 0x75, 0xd9, 0x90, 0x2c, 0xc5, 0x47, + 0xb5, 0x16, 0xd6, 0x52, 0x70, 0x53, 0xcc, 0x0c, 0x46, 0x2f, 0x1c, 0xd1, 0x27, 0x18, 0x38, 0xee, + 0x7a, 0x11, 0xee, 0x0e, 0x5a, 0xaf, 0x6e, 0x5c, 0x6f, 0xb1, 0xdc, 0xb9, 0x8e, 0x26, 0xa1, 0x29, + 0x68, 0xc1, 0x6a, 0xe3, 0x3a, 0xe1, 0xce, 0x75, 0xa2, 0x75, 0x78, 0x08, 0x7d, 0x57, 0x93, 0xd1, + 0x04, 0x46, 0xf7, 0xa9, 0xb7, 0x3f, 0x6a, 0x0a, 0x1a, 0xc2, 0x47, 0x67, 0x1b, 0x5c, 0xb1, 0x7e, + 0xed, 0x11, 0x7a, 0x3f, 0xbd, 0xfd, 0xd1, 0xd3, 0x3e, 0xcc, 0xff, 0xc0, 0x64, 0xd5, 0xde, 0x2a, + 0xb8, 0x26, 0x82, 0x36, 0xa0, 0xb6, 0x7b, 0xa2, 0x2f, 0x6f, 0x6f, 0xdf, 0xc4, 0xf2, 0xf9, 0xeb, + 0x7b, 0xa7, 0x31, 0x7b, 0xcb, 0xe1, 0xef, 0x4e, 0x14, 0xcf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc0, + 0x03, 0x26, 0x25, 0x2e, 0x02, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/capability/capability_service.proto b/vendor/google.golang.org/appengine/internal/capability/capability_service.proto new file mode 100644 index 0000000000..5660ab6ee7 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/capability/capability_service.proto @@ -0,0 +1,28 @@ +syntax = "proto2"; +option go_package = "capability"; + +package appengine; + +message IsEnabledRequest { + required string package = 1; + repeated string capability = 2; + repeated string call = 3; +} + +message IsEnabledResponse { + enum SummaryStatus { + DEFAULT = 0; + ENABLED = 1; + SCHEDULED_FUTURE = 2; + SCHEDULED_NOW = 3; + DISABLED = 4; + UNKNOWN = 5; + } + optional SummaryStatus summary_status = 1; + + optional int64 time_until_scheduled = 2; +} + +service CapabilityService { + rpc IsEnabled(IsEnabledRequest) returns (IsEnabledResponse) {}; +} diff --git a/vendor/google.golang.org/appengine/internal/channel/channel_service.pb.go b/vendor/google.golang.org/appengine/internal/channel/channel_service.pb.go new file mode 100644 index 0000000000..ea81f5038a --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/channel/channel_service.pb.go @@ -0,0 +1,201 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/channel/channel_service.proto + +/* +Package channel is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/channel/channel_service.proto + +It has these top-level messages: + ChannelServiceError + CreateChannelRequest + CreateChannelResponse + SendMessageRequest +*/ +package channel + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type ChannelServiceError_ErrorCode int32 + +const ( + ChannelServiceError_OK ChannelServiceError_ErrorCode = 0 + ChannelServiceError_INTERNAL_ERROR ChannelServiceError_ErrorCode = 1 + ChannelServiceError_INVALID_CHANNEL_KEY ChannelServiceError_ErrorCode = 2 + ChannelServiceError_BAD_MESSAGE ChannelServiceError_ErrorCode = 3 + ChannelServiceError_INVALID_CHANNEL_TOKEN_DURATION ChannelServiceError_ErrorCode = 4 + ChannelServiceError_APPID_ALIAS_REQUIRED ChannelServiceError_ErrorCode = 5 +) + +var ChannelServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INTERNAL_ERROR", + 2: "INVALID_CHANNEL_KEY", + 3: "BAD_MESSAGE", + 4: "INVALID_CHANNEL_TOKEN_DURATION", + 5: "APPID_ALIAS_REQUIRED", +} +var ChannelServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INTERNAL_ERROR": 1, + "INVALID_CHANNEL_KEY": 2, + "BAD_MESSAGE": 3, + "INVALID_CHANNEL_TOKEN_DURATION": 4, + "APPID_ALIAS_REQUIRED": 5, +} + +func (x ChannelServiceError_ErrorCode) Enum() *ChannelServiceError_ErrorCode { + p := new(ChannelServiceError_ErrorCode) + *p = x + return p +} +func (x ChannelServiceError_ErrorCode) String() string { + return proto.EnumName(ChannelServiceError_ErrorCode_name, int32(x)) +} +func (x *ChannelServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ChannelServiceError_ErrorCode_value, data, "ChannelServiceError_ErrorCode") + if err != nil { + return err + } + *x = ChannelServiceError_ErrorCode(value) + return nil +} +func (ChannelServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type ChannelServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *ChannelServiceError) Reset() { *m = ChannelServiceError{} } +func (m *ChannelServiceError) String() string { return proto.CompactTextString(m) } +func (*ChannelServiceError) ProtoMessage() {} +func (*ChannelServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type CreateChannelRequest struct { + ApplicationKey *string `protobuf:"bytes,1,req,name=application_key,json=applicationKey" json:"application_key,omitempty"` + DurationMinutes *int32 `protobuf:"varint,2,opt,name=duration_minutes,json=durationMinutes" json:"duration_minutes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateChannelRequest) Reset() { *m = CreateChannelRequest{} } +func (m *CreateChannelRequest) String() string { return proto.CompactTextString(m) } +func (*CreateChannelRequest) ProtoMessage() {} +func (*CreateChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *CreateChannelRequest) GetApplicationKey() string { + if m != nil && m.ApplicationKey != nil { + return *m.ApplicationKey + } + return "" +} + +func (m *CreateChannelRequest) GetDurationMinutes() int32 { + if m != nil && m.DurationMinutes != nil { + return *m.DurationMinutes + } + return 0 +} + +type CreateChannelResponse struct { + Token *string `protobuf:"bytes,2,opt,name=token" json:"token,omitempty"` + DurationMinutes *int32 `protobuf:"varint,3,opt,name=duration_minutes,json=durationMinutes" json:"duration_minutes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateChannelResponse) Reset() { *m = CreateChannelResponse{} } +func (m *CreateChannelResponse) String() string { return proto.CompactTextString(m) } +func (*CreateChannelResponse) ProtoMessage() {} +func (*CreateChannelResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *CreateChannelResponse) GetToken() string { + if m != nil && m.Token != nil { + return *m.Token + } + return "" +} + +func (m *CreateChannelResponse) GetDurationMinutes() int32 { + if m != nil && m.DurationMinutes != nil { + return *m.DurationMinutes + } + return 0 +} + +type SendMessageRequest struct { + ApplicationKey *string `protobuf:"bytes,1,req,name=application_key,json=applicationKey" json:"application_key,omitempty"` + Message *string `protobuf:"bytes,2,req,name=message" json:"message,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SendMessageRequest) Reset() { *m = SendMessageRequest{} } +func (m *SendMessageRequest) String() string { return proto.CompactTextString(m) } +func (*SendMessageRequest) ProtoMessage() {} +func (*SendMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *SendMessageRequest) GetApplicationKey() string { + if m != nil && m.ApplicationKey != nil { + return *m.ApplicationKey + } + return "" +} + +func (m *SendMessageRequest) GetMessage() string { + if m != nil && m.Message != nil { + return *m.Message + } + return "" +} + +func init() { + proto.RegisterType((*ChannelServiceError)(nil), "appengine.ChannelServiceError") + proto.RegisterType((*CreateChannelRequest)(nil), "appengine.CreateChannelRequest") + proto.RegisterType((*CreateChannelResponse)(nil), "appengine.CreateChannelResponse") + proto.RegisterType((*SendMessageRequest)(nil), "appengine.SendMessageRequest") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/channel/channel_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 355 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0xcd, 0xee, 0xd2, 0x40, + 0x14, 0xc5, 0x6d, 0xff, 0x22, 0xe9, 0x35, 0x81, 0x66, 0xc0, 0xd8, 0x95, 0x21, 0xdd, 0x88, 0x1b, + 0x78, 0x86, 0xa1, 0x9d, 0x68, 0xd3, 0xd2, 0xe2, 0x14, 0xfc, 0xda, 0x4c, 0x26, 0x70, 0x53, 0x2b, + 0x65, 0xa6, 0x4e, 0x8b, 0x09, 0x4f, 0xe1, 0x63, 0xf8, 0x9a, 0x26, 0x14, 0x88, 0x21, 0x6c, 0x5c, + 0xcd, 0x9c, 0x93, 0xdf, 0x39, 0x33, 0x37, 0x17, 0x16, 0x85, 0xd6, 0x45, 0x85, 0xb3, 0x42, 0x57, + 0x52, 0x15, 0x33, 0x6d, 0x8a, 0xb9, 0xac, 0x6b, 0x54, 0x45, 0xa9, 0x70, 0x5e, 0xaa, 0x16, 0x8d, + 0x92, 0xd5, 0x7c, 0xfb, 0x5d, 0x2a, 0x85, 0xb7, 0x53, 0x34, 0x68, 0x7e, 0x95, 0x5b, 0x9c, 0xd5, + 0x46, 0xb7, 0x9a, 0x38, 0xb7, 0x84, 0xff, 0xc7, 0x82, 0x51, 0xd0, 0x41, 0x79, 0xc7, 0x30, 0x63, + 0xb4, 0xf1, 0x7f, 0x5b, 0xe0, 0x9c, 0x6f, 0x81, 0xde, 0x21, 0x79, 0x01, 0x76, 0x16, 0xbb, 0xcf, + 0x08, 0x81, 0x41, 0x94, 0xae, 0x19, 0x4f, 0x69, 0x22, 0x18, 0xe7, 0x19, 0x77, 0x2d, 0xf2, 0x1a, + 0x46, 0x51, 0xfa, 0x89, 0x26, 0x51, 0x28, 0x82, 0x0f, 0x34, 0x4d, 0x59, 0x22, 0x62, 0xf6, 0xd5, + 0xb5, 0xc9, 0x10, 0x5e, 0x2e, 0x68, 0x28, 0x96, 0x2c, 0xcf, 0xe9, 0x7b, 0xe6, 0x3e, 0x11, 0x1f, + 0xde, 0xdc, 0x93, 0xeb, 0x2c, 0x66, 0xa9, 0x08, 0x37, 0x9c, 0xae, 0xa3, 0x2c, 0x75, 0x9f, 0x13, + 0x0f, 0xc6, 0x74, 0xb5, 0x8a, 0x42, 0x41, 0x93, 0x88, 0xe6, 0x82, 0xb3, 0x8f, 0x9b, 0x88, 0xb3, + 0xd0, 0xed, 0xf9, 0x3f, 0x60, 0x1c, 0x18, 0x94, 0x2d, 0x5e, 0xbe, 0xcb, 0xf1, 0xe7, 0x11, 0x9b, + 0x96, 0xbc, 0x85, 0xa1, 0xac, 0xeb, 0xaa, 0xdc, 0xca, 0xb6, 0xd4, 0x4a, 0xec, 0xf1, 0xe4, 0x59, + 0x13, 0x7b, 0xea, 0xf0, 0xc1, 0x3f, 0x76, 0x8c, 0x27, 0xf2, 0x0e, 0xdc, 0xdd, 0xd1, 0x74, 0xd4, + 0xa1, 0x54, 0xc7, 0x16, 0x1b, 0xcf, 0x9e, 0x58, 0xd3, 0x1e, 0x1f, 0x5e, 0xfd, 0x65, 0x67, 0xfb, + 0x5f, 0xe0, 0xd5, 0xdd, 0x5b, 0x4d, 0xad, 0x55, 0x83, 0x64, 0x0c, 0xbd, 0x56, 0xef, 0x51, 0x9d, + 0x83, 0x0e, 0xef, 0xc4, 0xc3, 0xe6, 0xa7, 0xc7, 0xcd, 0x9f, 0x81, 0xe4, 0xa8, 0x76, 0x4b, 0x6c, + 0x1a, 0x59, 0xe0, 0x7f, 0xcf, 0xe0, 0x41, 0xff, 0xd0, 0x45, 0x3d, 0xfb, 0x0c, 0x5c, 0xe5, 0xc2, + 0xf9, 0xd6, 0xbf, 0x2c, 0xfb, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe9, 0x0a, 0x77, 0x06, 0x23, + 0x02, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/channel/channel_service.proto b/vendor/google.golang.org/appengine/internal/channel/channel_service.proto new file mode 100644 index 0000000000..2b5a918ca6 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/channel/channel_service.proto @@ -0,0 +1,30 @@ +syntax = "proto2"; +option go_package = "channel"; + +package appengine; + +message ChannelServiceError { + enum ErrorCode { + OK = 0; + INTERNAL_ERROR = 1; + INVALID_CHANNEL_KEY = 2; + BAD_MESSAGE = 3; + INVALID_CHANNEL_TOKEN_DURATION = 4; + APPID_ALIAS_REQUIRED = 5; + } +} + +message CreateChannelRequest { + required string application_key = 1; + optional int32 duration_minutes = 2; +} + +message CreateChannelResponse { + optional string token = 2; + optional int32 duration_minutes = 3; +} + +message SendMessageRequest { + required string application_key = 1; + required string message = 2; +} diff --git a/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go new file mode 100644 index 0000000000..393342c131 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go @@ -0,0 +1,3244 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/datastore/datastore_v3.proto + +/* +Package datastore is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/datastore/datastore_v3.proto + +It has these top-level messages: + Action + PropertyValue + Property + Path + Reference + User + EntityProto + CompositeProperty + Index + CompositeIndex + IndexPostfix + IndexPosition + Snapshot + InternalHeader + Transaction + Query + CompiledQuery + CompiledCursor + Cursor + Error + Cost + GetRequest + GetResponse + PutRequest + PutResponse + TouchRequest + TouchResponse + DeleteRequest + DeleteResponse + NextRequest + QueryResult + AllocateIdsRequest + AllocateIdsResponse + CompositeIndices + AddActionsRequest + AddActionsResponse + BeginTransactionRequest + CommitResponse +*/ +package datastore + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Property_Meaning int32 + +const ( + Property_NO_MEANING Property_Meaning = 0 + Property_BLOB Property_Meaning = 14 + Property_TEXT Property_Meaning = 15 + Property_BYTESTRING Property_Meaning = 16 + Property_ATOM_CATEGORY Property_Meaning = 1 + Property_ATOM_LINK Property_Meaning = 2 + Property_ATOM_TITLE Property_Meaning = 3 + Property_ATOM_CONTENT Property_Meaning = 4 + Property_ATOM_SUMMARY Property_Meaning = 5 + Property_ATOM_AUTHOR Property_Meaning = 6 + Property_GD_WHEN Property_Meaning = 7 + Property_GD_EMAIL Property_Meaning = 8 + Property_GEORSS_POINT Property_Meaning = 9 + Property_GD_IM Property_Meaning = 10 + Property_GD_PHONENUMBER Property_Meaning = 11 + Property_GD_POSTALADDRESS Property_Meaning = 12 + Property_GD_RATING Property_Meaning = 13 + Property_BLOBKEY Property_Meaning = 17 + Property_ENTITY_PROTO Property_Meaning = 19 + Property_INDEX_VALUE Property_Meaning = 18 +) + +var Property_Meaning_name = map[int32]string{ + 0: "NO_MEANING", + 14: "BLOB", + 15: "TEXT", + 16: "BYTESTRING", + 1: "ATOM_CATEGORY", + 2: "ATOM_LINK", + 3: "ATOM_TITLE", + 4: "ATOM_CONTENT", + 5: "ATOM_SUMMARY", + 6: "ATOM_AUTHOR", + 7: "GD_WHEN", + 8: "GD_EMAIL", + 9: "GEORSS_POINT", + 10: "GD_IM", + 11: "GD_PHONENUMBER", + 12: "GD_POSTALADDRESS", + 13: "GD_RATING", + 17: "BLOBKEY", + 19: "ENTITY_PROTO", + 18: "INDEX_VALUE", +} +var Property_Meaning_value = map[string]int32{ + "NO_MEANING": 0, + "BLOB": 14, + "TEXT": 15, + "BYTESTRING": 16, + "ATOM_CATEGORY": 1, + "ATOM_LINK": 2, + "ATOM_TITLE": 3, + "ATOM_CONTENT": 4, + "ATOM_SUMMARY": 5, + "ATOM_AUTHOR": 6, + "GD_WHEN": 7, + "GD_EMAIL": 8, + "GEORSS_POINT": 9, + "GD_IM": 10, + "GD_PHONENUMBER": 11, + "GD_POSTALADDRESS": 12, + "GD_RATING": 13, + "BLOBKEY": 17, + "ENTITY_PROTO": 19, + "INDEX_VALUE": 18, +} + +func (x Property_Meaning) Enum() *Property_Meaning { + p := new(Property_Meaning) + *p = x + return p +} +func (x Property_Meaning) String() string { + return proto.EnumName(Property_Meaning_name, int32(x)) +} +func (x *Property_Meaning) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Property_Meaning_value, data, "Property_Meaning") + if err != nil { + return err + } + *x = Property_Meaning(value) + return nil +} +func (Property_Meaning) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } + +type Property_FtsTokenizationOption int32 + +const ( + Property_HTML Property_FtsTokenizationOption = 1 + Property_ATOM Property_FtsTokenizationOption = 2 +) + +var Property_FtsTokenizationOption_name = map[int32]string{ + 1: "HTML", + 2: "ATOM", +} +var Property_FtsTokenizationOption_value = map[string]int32{ + "HTML": 1, + "ATOM": 2, +} + +func (x Property_FtsTokenizationOption) Enum() *Property_FtsTokenizationOption { + p := new(Property_FtsTokenizationOption) + *p = x + return p +} +func (x Property_FtsTokenizationOption) String() string { + return proto.EnumName(Property_FtsTokenizationOption_name, int32(x)) +} +func (x *Property_FtsTokenizationOption) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Property_FtsTokenizationOption_value, data, "Property_FtsTokenizationOption") + if err != nil { + return err + } + *x = Property_FtsTokenizationOption(value) + return nil +} +func (Property_FtsTokenizationOption) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{2, 1} +} + +type EntityProto_Kind int32 + +const ( + EntityProto_GD_CONTACT EntityProto_Kind = 1 + EntityProto_GD_EVENT EntityProto_Kind = 2 + EntityProto_GD_MESSAGE EntityProto_Kind = 3 +) + +var EntityProto_Kind_name = map[int32]string{ + 1: "GD_CONTACT", + 2: "GD_EVENT", + 3: "GD_MESSAGE", +} +var EntityProto_Kind_value = map[string]int32{ + "GD_CONTACT": 1, + "GD_EVENT": 2, + "GD_MESSAGE": 3, +} + +func (x EntityProto_Kind) Enum() *EntityProto_Kind { + p := new(EntityProto_Kind) + *p = x + return p +} +func (x EntityProto_Kind) String() string { + return proto.EnumName(EntityProto_Kind_name, int32(x)) +} +func (x *EntityProto_Kind) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(EntityProto_Kind_value, data, "EntityProto_Kind") + if err != nil { + return err + } + *x = EntityProto_Kind(value) + return nil +} +func (EntityProto_Kind) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{6, 0} } + +type Index_Property_Direction int32 + +const ( + Index_Property_ASCENDING Index_Property_Direction = 1 + Index_Property_DESCENDING Index_Property_Direction = 2 +) + +var Index_Property_Direction_name = map[int32]string{ + 1: "ASCENDING", + 2: "DESCENDING", +} +var Index_Property_Direction_value = map[string]int32{ + "ASCENDING": 1, + "DESCENDING": 2, +} + +func (x Index_Property_Direction) Enum() *Index_Property_Direction { + p := new(Index_Property_Direction) + *p = x + return p +} +func (x Index_Property_Direction) String() string { + return proto.EnumName(Index_Property_Direction_name, int32(x)) +} +func (x *Index_Property_Direction) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Index_Property_Direction_value, data, "Index_Property_Direction") + if err != nil { + return err + } + *x = Index_Property_Direction(value) + return nil +} +func (Index_Property_Direction) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{8, 0, 0} +} + +type CompositeIndex_State int32 + +const ( + CompositeIndex_WRITE_ONLY CompositeIndex_State = 1 + CompositeIndex_READ_WRITE CompositeIndex_State = 2 + CompositeIndex_DELETED CompositeIndex_State = 3 + CompositeIndex_ERROR CompositeIndex_State = 4 +) + +var CompositeIndex_State_name = map[int32]string{ + 1: "WRITE_ONLY", + 2: "READ_WRITE", + 3: "DELETED", + 4: "ERROR", +} +var CompositeIndex_State_value = map[string]int32{ + "WRITE_ONLY": 1, + "READ_WRITE": 2, + "DELETED": 3, + "ERROR": 4, +} + +func (x CompositeIndex_State) Enum() *CompositeIndex_State { + p := new(CompositeIndex_State) + *p = x + return p +} +func (x CompositeIndex_State) String() string { + return proto.EnumName(CompositeIndex_State_name, int32(x)) +} +func (x *CompositeIndex_State) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(CompositeIndex_State_value, data, "CompositeIndex_State") + if err != nil { + return err + } + *x = CompositeIndex_State(value) + return nil +} +func (CompositeIndex_State) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{9, 0} } + +type Snapshot_Status int32 + +const ( + Snapshot_INACTIVE Snapshot_Status = 0 + Snapshot_ACTIVE Snapshot_Status = 1 +) + +var Snapshot_Status_name = map[int32]string{ + 0: "INACTIVE", + 1: "ACTIVE", +} +var Snapshot_Status_value = map[string]int32{ + "INACTIVE": 0, + "ACTIVE": 1, +} + +func (x Snapshot_Status) Enum() *Snapshot_Status { + p := new(Snapshot_Status) + *p = x + return p +} +func (x Snapshot_Status) String() string { + return proto.EnumName(Snapshot_Status_name, int32(x)) +} +func (x *Snapshot_Status) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Snapshot_Status_value, data, "Snapshot_Status") + if err != nil { + return err + } + *x = Snapshot_Status(value) + return nil +} +func (Snapshot_Status) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{12, 0} } + +type Query_Hint int32 + +const ( + Query_ORDER_FIRST Query_Hint = 1 + Query_ANCESTOR_FIRST Query_Hint = 2 + Query_FILTER_FIRST Query_Hint = 3 +) + +var Query_Hint_name = map[int32]string{ + 1: "ORDER_FIRST", + 2: "ANCESTOR_FIRST", + 3: "FILTER_FIRST", +} +var Query_Hint_value = map[string]int32{ + "ORDER_FIRST": 1, + "ANCESTOR_FIRST": 2, + "FILTER_FIRST": 3, +} + +func (x Query_Hint) Enum() *Query_Hint { + p := new(Query_Hint) + *p = x + return p +} +func (x Query_Hint) String() string { + return proto.EnumName(Query_Hint_name, int32(x)) +} +func (x *Query_Hint) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Query_Hint_value, data, "Query_Hint") + if err != nil { + return err + } + *x = Query_Hint(value) + return nil +} +func (Query_Hint) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 0} } + +type Query_Filter_Operator int32 + +const ( + Query_Filter_LESS_THAN Query_Filter_Operator = 1 + Query_Filter_LESS_THAN_OR_EQUAL Query_Filter_Operator = 2 + Query_Filter_GREATER_THAN Query_Filter_Operator = 3 + Query_Filter_GREATER_THAN_OR_EQUAL Query_Filter_Operator = 4 + Query_Filter_EQUAL Query_Filter_Operator = 5 + Query_Filter_IN Query_Filter_Operator = 6 + Query_Filter_EXISTS Query_Filter_Operator = 7 +) + +var Query_Filter_Operator_name = map[int32]string{ + 1: "LESS_THAN", + 2: "LESS_THAN_OR_EQUAL", + 3: "GREATER_THAN", + 4: "GREATER_THAN_OR_EQUAL", + 5: "EQUAL", + 6: "IN", + 7: "EXISTS", +} +var Query_Filter_Operator_value = map[string]int32{ + "LESS_THAN": 1, + "LESS_THAN_OR_EQUAL": 2, + "GREATER_THAN": 3, + "GREATER_THAN_OR_EQUAL": 4, + "EQUAL": 5, + "IN": 6, + "EXISTS": 7, +} + +func (x Query_Filter_Operator) Enum() *Query_Filter_Operator { + p := new(Query_Filter_Operator) + *p = x + return p +} +func (x Query_Filter_Operator) String() string { + return proto.EnumName(Query_Filter_Operator_name, int32(x)) +} +func (x *Query_Filter_Operator) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Query_Filter_Operator_value, data, "Query_Filter_Operator") + if err != nil { + return err + } + *x = Query_Filter_Operator(value) + return nil +} +func (Query_Filter_Operator) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 0, 0} } + +type Query_Order_Direction int32 + +const ( + Query_Order_ASCENDING Query_Order_Direction = 1 + Query_Order_DESCENDING Query_Order_Direction = 2 +) + +var Query_Order_Direction_name = map[int32]string{ + 1: "ASCENDING", + 2: "DESCENDING", +} +var Query_Order_Direction_value = map[string]int32{ + "ASCENDING": 1, + "DESCENDING": 2, +} + +func (x Query_Order_Direction) Enum() *Query_Order_Direction { + p := new(Query_Order_Direction) + *p = x + return p +} +func (x Query_Order_Direction) String() string { + return proto.EnumName(Query_Order_Direction_name, int32(x)) +} +func (x *Query_Order_Direction) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Query_Order_Direction_value, data, "Query_Order_Direction") + if err != nil { + return err + } + *x = Query_Order_Direction(value) + return nil +} +func (Query_Order_Direction) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 1, 0} } + +type Error_ErrorCode int32 + +const ( + Error_BAD_REQUEST Error_ErrorCode = 1 + Error_CONCURRENT_TRANSACTION Error_ErrorCode = 2 + Error_INTERNAL_ERROR Error_ErrorCode = 3 + Error_NEED_INDEX Error_ErrorCode = 4 + Error_TIMEOUT Error_ErrorCode = 5 + Error_PERMISSION_DENIED Error_ErrorCode = 6 + Error_BIGTABLE_ERROR Error_ErrorCode = 7 + Error_COMMITTED_BUT_STILL_APPLYING Error_ErrorCode = 8 + Error_CAPABILITY_DISABLED Error_ErrorCode = 9 + Error_TRY_ALTERNATE_BACKEND Error_ErrorCode = 10 + Error_SAFE_TIME_TOO_OLD Error_ErrorCode = 11 +) + +var Error_ErrorCode_name = map[int32]string{ + 1: "BAD_REQUEST", + 2: "CONCURRENT_TRANSACTION", + 3: "INTERNAL_ERROR", + 4: "NEED_INDEX", + 5: "TIMEOUT", + 6: "PERMISSION_DENIED", + 7: "BIGTABLE_ERROR", + 8: "COMMITTED_BUT_STILL_APPLYING", + 9: "CAPABILITY_DISABLED", + 10: "TRY_ALTERNATE_BACKEND", + 11: "SAFE_TIME_TOO_OLD", +} +var Error_ErrorCode_value = map[string]int32{ + "BAD_REQUEST": 1, + "CONCURRENT_TRANSACTION": 2, + "INTERNAL_ERROR": 3, + "NEED_INDEX": 4, + "TIMEOUT": 5, + "PERMISSION_DENIED": 6, + "BIGTABLE_ERROR": 7, + "COMMITTED_BUT_STILL_APPLYING": 8, + "CAPABILITY_DISABLED": 9, + "TRY_ALTERNATE_BACKEND": 10, + "SAFE_TIME_TOO_OLD": 11, +} + +func (x Error_ErrorCode) Enum() *Error_ErrorCode { + p := new(Error_ErrorCode) + *p = x + return p +} +func (x Error_ErrorCode) String() string { + return proto.EnumName(Error_ErrorCode_name, int32(x)) +} +func (x *Error_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Error_ErrorCode_value, data, "Error_ErrorCode") + if err != nil { + return err + } + *x = Error_ErrorCode(value) + return nil +} +func (Error_ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{19, 0} } + +type PutRequest_AutoIdPolicy int32 + +const ( + PutRequest_CURRENT PutRequest_AutoIdPolicy = 0 + PutRequest_SEQUENTIAL PutRequest_AutoIdPolicy = 1 +) + +var PutRequest_AutoIdPolicy_name = map[int32]string{ + 0: "CURRENT", + 1: "SEQUENTIAL", +} +var PutRequest_AutoIdPolicy_value = map[string]int32{ + "CURRENT": 0, + "SEQUENTIAL": 1, +} + +func (x PutRequest_AutoIdPolicy) Enum() *PutRequest_AutoIdPolicy { + p := new(PutRequest_AutoIdPolicy) + *p = x + return p +} +func (x PutRequest_AutoIdPolicy) String() string { + return proto.EnumName(PutRequest_AutoIdPolicy_name, int32(x)) +} +func (x *PutRequest_AutoIdPolicy) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(PutRequest_AutoIdPolicy_value, data, "PutRequest_AutoIdPolicy") + if err != nil { + return err + } + *x = PutRequest_AutoIdPolicy(value) + return nil +} +func (PutRequest_AutoIdPolicy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{23, 0} } + +type BeginTransactionRequest_TransactionMode int32 + +const ( + BeginTransactionRequest_UNKNOWN BeginTransactionRequest_TransactionMode = 0 + BeginTransactionRequest_READ_ONLY BeginTransactionRequest_TransactionMode = 1 + BeginTransactionRequest_READ_WRITE BeginTransactionRequest_TransactionMode = 2 +) + +var BeginTransactionRequest_TransactionMode_name = map[int32]string{ + 0: "UNKNOWN", + 1: "READ_ONLY", + 2: "READ_WRITE", +} +var BeginTransactionRequest_TransactionMode_value = map[string]int32{ + "UNKNOWN": 0, + "READ_ONLY": 1, + "READ_WRITE": 2, +} + +func (x BeginTransactionRequest_TransactionMode) Enum() *BeginTransactionRequest_TransactionMode { + p := new(BeginTransactionRequest_TransactionMode) + *p = x + return p +} +func (x BeginTransactionRequest_TransactionMode) String() string { + return proto.EnumName(BeginTransactionRequest_TransactionMode_name, int32(x)) +} +func (x *BeginTransactionRequest_TransactionMode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(BeginTransactionRequest_TransactionMode_value, data, "BeginTransactionRequest_TransactionMode") + if err != nil { + return err + } + *x = BeginTransactionRequest_TransactionMode(value) + return nil +} +func (BeginTransactionRequest_TransactionMode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{36, 0} +} + +type Action struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *Action) Reset() { *m = Action{} } +func (m *Action) String() string { return proto.CompactTextString(m) } +func (*Action) ProtoMessage() {} +func (*Action) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type PropertyValue struct { + Int64Value *int64 `protobuf:"varint,1,opt,name=int64Value" json:"int64Value,omitempty"` + BooleanValue *bool `protobuf:"varint,2,opt,name=booleanValue" json:"booleanValue,omitempty"` + StringValue *string `protobuf:"bytes,3,opt,name=stringValue" json:"stringValue,omitempty"` + DoubleValue *float64 `protobuf:"fixed64,4,opt,name=doubleValue" json:"doubleValue,omitempty"` + Pointvalue *PropertyValue_PointValue `protobuf:"group,5,opt,name=PointValue,json=pointvalue" json:"pointvalue,omitempty"` + Uservalue *PropertyValue_UserValue `protobuf:"group,8,opt,name=UserValue,json=uservalue" json:"uservalue,omitempty"` + Referencevalue *PropertyValue_ReferenceValue `protobuf:"group,12,opt,name=ReferenceValue,json=referencevalue" json:"referencevalue,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PropertyValue) Reset() { *m = PropertyValue{} } +func (m *PropertyValue) String() string { return proto.CompactTextString(m) } +func (*PropertyValue) ProtoMessage() {} +func (*PropertyValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *PropertyValue) GetInt64Value() int64 { + if m != nil && m.Int64Value != nil { + return *m.Int64Value + } + return 0 +} + +func (m *PropertyValue) GetBooleanValue() bool { + if m != nil && m.BooleanValue != nil { + return *m.BooleanValue + } + return false +} + +func (m *PropertyValue) GetStringValue() string { + if m != nil && m.StringValue != nil { + return *m.StringValue + } + return "" +} + +func (m *PropertyValue) GetDoubleValue() float64 { + if m != nil && m.DoubleValue != nil { + return *m.DoubleValue + } + return 0 +} + +func (m *PropertyValue) GetPointvalue() *PropertyValue_PointValue { + if m != nil { + return m.Pointvalue + } + return nil +} + +func (m *PropertyValue) GetUservalue() *PropertyValue_UserValue { + if m != nil { + return m.Uservalue + } + return nil +} + +func (m *PropertyValue) GetReferencevalue() *PropertyValue_ReferenceValue { + if m != nil { + return m.Referencevalue + } + return nil +} + +type PropertyValue_PointValue struct { + X *float64 `protobuf:"fixed64,6,req,name=x" json:"x,omitempty"` + Y *float64 `protobuf:"fixed64,7,req,name=y" json:"y,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PropertyValue_PointValue) Reset() { *m = PropertyValue_PointValue{} } +func (m *PropertyValue_PointValue) String() string { return proto.CompactTextString(m) } +func (*PropertyValue_PointValue) ProtoMessage() {} +func (*PropertyValue_PointValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} } + +func (m *PropertyValue_PointValue) GetX() float64 { + if m != nil && m.X != nil { + return *m.X + } + return 0 +} + +func (m *PropertyValue_PointValue) GetY() float64 { + if m != nil && m.Y != nil { + return *m.Y + } + return 0 +} + +type PropertyValue_UserValue struct { + Email *string `protobuf:"bytes,9,req,name=email" json:"email,omitempty"` + AuthDomain *string `protobuf:"bytes,10,req,name=auth_domain,json=authDomain" json:"auth_domain,omitempty"` + Nickname *string `protobuf:"bytes,11,opt,name=nickname" json:"nickname,omitempty"` + FederatedIdentity *string `protobuf:"bytes,21,opt,name=federated_identity,json=federatedIdentity" json:"federated_identity,omitempty"` + FederatedProvider *string `protobuf:"bytes,22,opt,name=federated_provider,json=federatedProvider" json:"federated_provider,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PropertyValue_UserValue) Reset() { *m = PropertyValue_UserValue{} } +func (m *PropertyValue_UserValue) String() string { return proto.CompactTextString(m) } +func (*PropertyValue_UserValue) ProtoMessage() {} +func (*PropertyValue_UserValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} } + +func (m *PropertyValue_UserValue) GetEmail() string { + if m != nil && m.Email != nil { + return *m.Email + } + return "" +} + +func (m *PropertyValue_UserValue) GetAuthDomain() string { + if m != nil && m.AuthDomain != nil { + return *m.AuthDomain + } + return "" +} + +func (m *PropertyValue_UserValue) GetNickname() string { + if m != nil && m.Nickname != nil { + return *m.Nickname + } + return "" +} + +func (m *PropertyValue_UserValue) GetFederatedIdentity() string { + if m != nil && m.FederatedIdentity != nil { + return *m.FederatedIdentity + } + return "" +} + +func (m *PropertyValue_UserValue) GetFederatedProvider() string { + if m != nil && m.FederatedProvider != nil { + return *m.FederatedProvider + } + return "" +} + +type PropertyValue_ReferenceValue struct { + App *string `protobuf:"bytes,13,req,name=app" json:"app,omitempty"` + NameSpace *string `protobuf:"bytes,20,opt,name=name_space,json=nameSpace" json:"name_space,omitempty"` + Pathelement []*PropertyValue_ReferenceValue_PathElement `protobuf:"group,14,rep,name=PathElement,json=pathelement" json:"pathelement,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PropertyValue_ReferenceValue) Reset() { *m = PropertyValue_ReferenceValue{} } +func (m *PropertyValue_ReferenceValue) String() string { return proto.CompactTextString(m) } +func (*PropertyValue_ReferenceValue) ProtoMessage() {} +func (*PropertyValue_ReferenceValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 2} } + +func (m *PropertyValue_ReferenceValue) GetApp() string { + if m != nil && m.App != nil { + return *m.App + } + return "" +} + +func (m *PropertyValue_ReferenceValue) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *PropertyValue_ReferenceValue) GetPathelement() []*PropertyValue_ReferenceValue_PathElement { + if m != nil { + return m.Pathelement + } + return nil +} + +type PropertyValue_ReferenceValue_PathElement struct { + Type *string `protobuf:"bytes,15,req,name=type" json:"type,omitempty"` + Id *int64 `protobuf:"varint,16,opt,name=id" json:"id,omitempty"` + Name *string `protobuf:"bytes,17,opt,name=name" json:"name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PropertyValue_ReferenceValue_PathElement) Reset() { + *m = PropertyValue_ReferenceValue_PathElement{} +} +func (m *PropertyValue_ReferenceValue_PathElement) String() string { return proto.CompactTextString(m) } +func (*PropertyValue_ReferenceValue_PathElement) ProtoMessage() {} +func (*PropertyValue_ReferenceValue_PathElement) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{1, 2, 0} +} + +func (m *PropertyValue_ReferenceValue_PathElement) GetType() string { + if m != nil && m.Type != nil { + return *m.Type + } + return "" +} + +func (m *PropertyValue_ReferenceValue_PathElement) GetId() int64 { + if m != nil && m.Id != nil { + return *m.Id + } + return 0 +} + +func (m *PropertyValue_ReferenceValue_PathElement) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +type Property struct { + Meaning *Property_Meaning `protobuf:"varint,1,opt,name=meaning,enum=appengine.Property_Meaning,def=0" json:"meaning,omitempty"` + MeaningUri *string `protobuf:"bytes,2,opt,name=meaning_uri,json=meaningUri" json:"meaning_uri,omitempty"` + Name *string `protobuf:"bytes,3,req,name=name" json:"name,omitempty"` + Value *PropertyValue `protobuf:"bytes,5,req,name=value" json:"value,omitempty"` + Multiple *bool `protobuf:"varint,4,req,name=multiple" json:"multiple,omitempty"` + Searchable *bool `protobuf:"varint,6,opt,name=searchable,def=0" json:"searchable,omitempty"` + FtsTokenizationOption *Property_FtsTokenizationOption `protobuf:"varint,8,opt,name=fts_tokenization_option,json=ftsTokenizationOption,enum=appengine.Property_FtsTokenizationOption" json:"fts_tokenization_option,omitempty"` + Locale *string `protobuf:"bytes,9,opt,name=locale,def=en" json:"locale,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Property) Reset() { *m = Property{} } +func (m *Property) String() string { return proto.CompactTextString(m) } +func (*Property) ProtoMessage() {} +func (*Property) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +const Default_Property_Meaning Property_Meaning = Property_NO_MEANING +const Default_Property_Searchable bool = false +const Default_Property_Locale string = "en" + +func (m *Property) GetMeaning() Property_Meaning { + if m != nil && m.Meaning != nil { + return *m.Meaning + } + return Default_Property_Meaning +} + +func (m *Property) GetMeaningUri() string { + if m != nil && m.MeaningUri != nil { + return *m.MeaningUri + } + return "" +} + +func (m *Property) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *Property) GetValue() *PropertyValue { + if m != nil { + return m.Value + } + return nil +} + +func (m *Property) GetMultiple() bool { + if m != nil && m.Multiple != nil { + return *m.Multiple + } + return false +} + +func (m *Property) GetSearchable() bool { + if m != nil && m.Searchable != nil { + return *m.Searchable + } + return Default_Property_Searchable +} + +func (m *Property) GetFtsTokenizationOption() Property_FtsTokenizationOption { + if m != nil && m.FtsTokenizationOption != nil { + return *m.FtsTokenizationOption + } + return Property_HTML +} + +func (m *Property) GetLocale() string { + if m != nil && m.Locale != nil { + return *m.Locale + } + return Default_Property_Locale +} + +type Path struct { + Element []*Path_Element `protobuf:"group,1,rep,name=Element,json=element" json:"element,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Path) Reset() { *m = Path{} } +func (m *Path) String() string { return proto.CompactTextString(m) } +func (*Path) ProtoMessage() {} +func (*Path) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *Path) GetElement() []*Path_Element { + if m != nil { + return m.Element + } + return nil +} + +type Path_Element struct { + Type *string `protobuf:"bytes,2,req,name=type" json:"type,omitempty"` + Id *int64 `protobuf:"varint,3,opt,name=id" json:"id,omitempty"` + Name *string `protobuf:"bytes,4,opt,name=name" json:"name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Path_Element) Reset() { *m = Path_Element{} } +func (m *Path_Element) String() string { return proto.CompactTextString(m) } +func (*Path_Element) ProtoMessage() {} +func (*Path_Element) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} } + +func (m *Path_Element) GetType() string { + if m != nil && m.Type != nil { + return *m.Type + } + return "" +} + +func (m *Path_Element) GetId() int64 { + if m != nil && m.Id != nil { + return *m.Id + } + return 0 +} + +func (m *Path_Element) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +type Reference struct { + App *string `protobuf:"bytes,13,req,name=app" json:"app,omitempty"` + NameSpace *string `protobuf:"bytes,20,opt,name=name_space,json=nameSpace" json:"name_space,omitempty"` + Path *Path `protobuf:"bytes,14,req,name=path" json:"path,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Reference) Reset() { *m = Reference{} } +func (m *Reference) String() string { return proto.CompactTextString(m) } +func (*Reference) ProtoMessage() {} +func (*Reference) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *Reference) GetApp() string { + if m != nil && m.App != nil { + return *m.App + } + return "" +} + +func (m *Reference) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *Reference) GetPath() *Path { + if m != nil { + return m.Path + } + return nil +} + +type User struct { + Email *string `protobuf:"bytes,1,req,name=email" json:"email,omitempty"` + AuthDomain *string `protobuf:"bytes,2,req,name=auth_domain,json=authDomain" json:"auth_domain,omitempty"` + Nickname *string `protobuf:"bytes,3,opt,name=nickname" json:"nickname,omitempty"` + FederatedIdentity *string `protobuf:"bytes,6,opt,name=federated_identity,json=federatedIdentity" json:"federated_identity,omitempty"` + FederatedProvider *string `protobuf:"bytes,7,opt,name=federated_provider,json=federatedProvider" json:"federated_provider,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *User) Reset() { *m = User{} } +func (m *User) String() string { return proto.CompactTextString(m) } +func (*User) ProtoMessage() {} +func (*User) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *User) GetEmail() string { + if m != nil && m.Email != nil { + return *m.Email + } + return "" +} + +func (m *User) GetAuthDomain() string { + if m != nil && m.AuthDomain != nil { + return *m.AuthDomain + } + return "" +} + +func (m *User) GetNickname() string { + if m != nil && m.Nickname != nil { + return *m.Nickname + } + return "" +} + +func (m *User) GetFederatedIdentity() string { + if m != nil && m.FederatedIdentity != nil { + return *m.FederatedIdentity + } + return "" +} + +func (m *User) GetFederatedProvider() string { + if m != nil && m.FederatedProvider != nil { + return *m.FederatedProvider + } + return "" +} + +type EntityProto struct { + Key *Reference `protobuf:"bytes,13,req,name=key" json:"key,omitempty"` + EntityGroup *Path `protobuf:"bytes,16,req,name=entity_group,json=entityGroup" json:"entity_group,omitempty"` + Owner *User `protobuf:"bytes,17,opt,name=owner" json:"owner,omitempty"` + Kind *EntityProto_Kind `protobuf:"varint,4,opt,name=kind,enum=appengine.EntityProto_Kind" json:"kind,omitempty"` + KindUri *string `protobuf:"bytes,5,opt,name=kind_uri,json=kindUri" json:"kind_uri,omitempty"` + Property []*Property `protobuf:"bytes,14,rep,name=property" json:"property,omitempty"` + RawProperty []*Property `protobuf:"bytes,15,rep,name=raw_property,json=rawProperty" json:"raw_property,omitempty"` + Rank *int32 `protobuf:"varint,18,opt,name=rank" json:"rank,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *EntityProto) Reset() { *m = EntityProto{} } +func (m *EntityProto) String() string { return proto.CompactTextString(m) } +func (*EntityProto) ProtoMessage() {} +func (*EntityProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *EntityProto) GetKey() *Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *EntityProto) GetEntityGroup() *Path { + if m != nil { + return m.EntityGroup + } + return nil +} + +func (m *EntityProto) GetOwner() *User { + if m != nil { + return m.Owner + } + return nil +} + +func (m *EntityProto) GetKind() EntityProto_Kind { + if m != nil && m.Kind != nil { + return *m.Kind + } + return EntityProto_GD_CONTACT +} + +func (m *EntityProto) GetKindUri() string { + if m != nil && m.KindUri != nil { + return *m.KindUri + } + return "" +} + +func (m *EntityProto) GetProperty() []*Property { + if m != nil { + return m.Property + } + return nil +} + +func (m *EntityProto) GetRawProperty() []*Property { + if m != nil { + return m.RawProperty + } + return nil +} + +func (m *EntityProto) GetRank() int32 { + if m != nil && m.Rank != nil { + return *m.Rank + } + return 0 +} + +type CompositeProperty struct { + IndexId *int64 `protobuf:"varint,1,req,name=index_id,json=indexId" json:"index_id,omitempty"` + Value []string `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompositeProperty) Reset() { *m = CompositeProperty{} } +func (m *CompositeProperty) String() string { return proto.CompactTextString(m) } +func (*CompositeProperty) ProtoMessage() {} +func (*CompositeProperty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *CompositeProperty) GetIndexId() int64 { + if m != nil && m.IndexId != nil { + return *m.IndexId + } + return 0 +} + +func (m *CompositeProperty) GetValue() []string { + if m != nil { + return m.Value + } + return nil +} + +type Index struct { + EntityType *string `protobuf:"bytes,1,req,name=entity_type,json=entityType" json:"entity_type,omitempty"` + Ancestor *bool `protobuf:"varint,5,req,name=ancestor" json:"ancestor,omitempty"` + Property []*Index_Property `protobuf:"group,2,rep,name=Property,json=property" json:"property,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Index) Reset() { *m = Index{} } +func (m *Index) String() string { return proto.CompactTextString(m) } +func (*Index) ProtoMessage() {} +func (*Index) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *Index) GetEntityType() string { + if m != nil && m.EntityType != nil { + return *m.EntityType + } + return "" +} + +func (m *Index) GetAncestor() bool { + if m != nil && m.Ancestor != nil { + return *m.Ancestor + } + return false +} + +func (m *Index) GetProperty() []*Index_Property { + if m != nil { + return m.Property + } + return nil +} + +type Index_Property struct { + Name *string `protobuf:"bytes,3,req,name=name" json:"name,omitempty"` + Direction *Index_Property_Direction `protobuf:"varint,4,opt,name=direction,enum=appengine.Index_Property_Direction,def=1" json:"direction,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Index_Property) Reset() { *m = Index_Property{} } +func (m *Index_Property) String() string { return proto.CompactTextString(m) } +func (*Index_Property) ProtoMessage() {} +func (*Index_Property) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8, 0} } + +const Default_Index_Property_Direction Index_Property_Direction = Index_Property_ASCENDING + +func (m *Index_Property) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *Index_Property) GetDirection() Index_Property_Direction { + if m != nil && m.Direction != nil { + return *m.Direction + } + return Default_Index_Property_Direction +} + +type CompositeIndex struct { + AppId *string `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + Id *int64 `protobuf:"varint,2,req,name=id" json:"id,omitempty"` + Definition *Index `protobuf:"bytes,3,req,name=definition" json:"definition,omitempty"` + State *CompositeIndex_State `protobuf:"varint,4,req,name=state,enum=appengine.CompositeIndex_State" json:"state,omitempty"` + OnlyUseIfRequired *bool `protobuf:"varint,6,opt,name=only_use_if_required,json=onlyUseIfRequired,def=0" json:"only_use_if_required,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompositeIndex) Reset() { *m = CompositeIndex{} } +func (m *CompositeIndex) String() string { return proto.CompactTextString(m) } +func (*CompositeIndex) ProtoMessage() {} +func (*CompositeIndex) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +const Default_CompositeIndex_OnlyUseIfRequired bool = false + +func (m *CompositeIndex) GetAppId() string { + if m != nil && m.AppId != nil { + return *m.AppId + } + return "" +} + +func (m *CompositeIndex) GetId() int64 { + if m != nil && m.Id != nil { + return *m.Id + } + return 0 +} + +func (m *CompositeIndex) GetDefinition() *Index { + if m != nil { + return m.Definition + } + return nil +} + +func (m *CompositeIndex) GetState() CompositeIndex_State { + if m != nil && m.State != nil { + return *m.State + } + return CompositeIndex_WRITE_ONLY +} + +func (m *CompositeIndex) GetOnlyUseIfRequired() bool { + if m != nil && m.OnlyUseIfRequired != nil { + return *m.OnlyUseIfRequired + } + return Default_CompositeIndex_OnlyUseIfRequired +} + +type IndexPostfix struct { + IndexValue []*IndexPostfix_IndexValue `protobuf:"bytes,1,rep,name=index_value,json=indexValue" json:"index_value,omitempty"` + Key *Reference `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Before *bool `protobuf:"varint,3,opt,name=before,def=1" json:"before,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexPostfix) Reset() { *m = IndexPostfix{} } +func (m *IndexPostfix) String() string { return proto.CompactTextString(m) } +func (*IndexPostfix) ProtoMessage() {} +func (*IndexPostfix) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +const Default_IndexPostfix_Before bool = true + +func (m *IndexPostfix) GetIndexValue() []*IndexPostfix_IndexValue { + if m != nil { + return m.IndexValue + } + return nil +} + +func (m *IndexPostfix) GetKey() *Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *IndexPostfix) GetBefore() bool { + if m != nil && m.Before != nil { + return *m.Before + } + return Default_IndexPostfix_Before +} + +type IndexPostfix_IndexValue struct { + PropertyName *string `protobuf:"bytes,1,req,name=property_name,json=propertyName" json:"property_name,omitempty"` + Value *PropertyValue `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexPostfix_IndexValue) Reset() { *m = IndexPostfix_IndexValue{} } +func (m *IndexPostfix_IndexValue) String() string { return proto.CompactTextString(m) } +func (*IndexPostfix_IndexValue) ProtoMessage() {} +func (*IndexPostfix_IndexValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10, 0} } + +func (m *IndexPostfix_IndexValue) GetPropertyName() string { + if m != nil && m.PropertyName != nil { + return *m.PropertyName + } + return "" +} + +func (m *IndexPostfix_IndexValue) GetValue() *PropertyValue { + if m != nil { + return m.Value + } + return nil +} + +type IndexPosition struct { + Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` + Before *bool `protobuf:"varint,2,opt,name=before,def=1" json:"before,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexPosition) Reset() { *m = IndexPosition{} } +func (m *IndexPosition) String() string { return proto.CompactTextString(m) } +func (*IndexPosition) ProtoMessage() {} +func (*IndexPosition) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +const Default_IndexPosition_Before bool = true + +func (m *IndexPosition) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +func (m *IndexPosition) GetBefore() bool { + if m != nil && m.Before != nil { + return *m.Before + } + return Default_IndexPosition_Before +} + +type Snapshot struct { + Ts *int64 `protobuf:"varint,1,req,name=ts" json:"ts,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (m *Snapshot) String() string { return proto.CompactTextString(m) } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *Snapshot) GetTs() int64 { + if m != nil && m.Ts != nil { + return *m.Ts + } + return 0 +} + +type InternalHeader struct { + Qos *string `protobuf:"bytes,1,opt,name=qos" json:"qos,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *InternalHeader) Reset() { *m = InternalHeader{} } +func (m *InternalHeader) String() string { return proto.CompactTextString(m) } +func (*InternalHeader) ProtoMessage() {} +func (*InternalHeader) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *InternalHeader) GetQos() string { + if m != nil && m.Qos != nil { + return *m.Qos + } + return "" +} + +type Transaction struct { + Header *InternalHeader `protobuf:"bytes,4,opt,name=header" json:"header,omitempty"` + Handle *uint64 `protobuf:"fixed64,1,req,name=handle" json:"handle,omitempty"` + App *string `protobuf:"bytes,2,req,name=app" json:"app,omitempty"` + MarkChanges *bool `protobuf:"varint,3,opt,name=mark_changes,json=markChanges,def=0" json:"mark_changes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Transaction) Reset() { *m = Transaction{} } +func (m *Transaction) String() string { return proto.CompactTextString(m) } +func (*Transaction) ProtoMessage() {} +func (*Transaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +const Default_Transaction_MarkChanges bool = false + +func (m *Transaction) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *Transaction) GetHandle() uint64 { + if m != nil && m.Handle != nil { + return *m.Handle + } + return 0 +} + +func (m *Transaction) GetApp() string { + if m != nil && m.App != nil { + return *m.App + } + return "" +} + +func (m *Transaction) GetMarkChanges() bool { + if m != nil && m.MarkChanges != nil { + return *m.MarkChanges + } + return Default_Transaction_MarkChanges +} + +type Query struct { + Header *InternalHeader `protobuf:"bytes,39,opt,name=header" json:"header,omitempty"` + App *string `protobuf:"bytes,1,req,name=app" json:"app,omitempty"` + NameSpace *string `protobuf:"bytes,29,opt,name=name_space,json=nameSpace" json:"name_space,omitempty"` + Kind *string `protobuf:"bytes,3,opt,name=kind" json:"kind,omitempty"` + Ancestor *Reference `protobuf:"bytes,17,opt,name=ancestor" json:"ancestor,omitempty"` + Filter []*Query_Filter `protobuf:"group,4,rep,name=Filter,json=filter" json:"filter,omitempty"` + SearchQuery *string `protobuf:"bytes,8,opt,name=search_query,json=searchQuery" json:"search_query,omitempty"` + Order []*Query_Order `protobuf:"group,9,rep,name=Order,json=order" json:"order,omitempty"` + Hint *Query_Hint `protobuf:"varint,18,opt,name=hint,enum=appengine.Query_Hint" json:"hint,omitempty"` + Count *int32 `protobuf:"varint,23,opt,name=count" json:"count,omitempty"` + Offset *int32 `protobuf:"varint,12,opt,name=offset,def=0" json:"offset,omitempty"` + Limit *int32 `protobuf:"varint,16,opt,name=limit" json:"limit,omitempty"` + CompiledCursor *CompiledCursor `protobuf:"bytes,30,opt,name=compiled_cursor,json=compiledCursor" json:"compiled_cursor,omitempty"` + EndCompiledCursor *CompiledCursor `protobuf:"bytes,31,opt,name=end_compiled_cursor,json=endCompiledCursor" json:"end_compiled_cursor,omitempty"` + CompositeIndex []*CompositeIndex `protobuf:"bytes,19,rep,name=composite_index,json=compositeIndex" json:"composite_index,omitempty"` + RequirePerfectPlan *bool `protobuf:"varint,20,opt,name=require_perfect_plan,json=requirePerfectPlan,def=0" json:"require_perfect_plan,omitempty"` + KeysOnly *bool `protobuf:"varint,21,opt,name=keys_only,json=keysOnly,def=0" json:"keys_only,omitempty"` + Transaction *Transaction `protobuf:"bytes,22,opt,name=transaction" json:"transaction,omitempty"` + Compile *bool `protobuf:"varint,25,opt,name=compile,def=0" json:"compile,omitempty"` + FailoverMs *int64 `protobuf:"varint,26,opt,name=failover_ms,json=failoverMs" json:"failover_ms,omitempty"` + Strong *bool `protobuf:"varint,32,opt,name=strong" json:"strong,omitempty"` + PropertyName []string `protobuf:"bytes,33,rep,name=property_name,json=propertyName" json:"property_name,omitempty"` + GroupByPropertyName []string `protobuf:"bytes,34,rep,name=group_by_property_name,json=groupByPropertyName" json:"group_by_property_name,omitempty"` + Distinct *bool `protobuf:"varint,24,opt,name=distinct" json:"distinct,omitempty"` + MinSafeTimeSeconds *int64 `protobuf:"varint,35,opt,name=min_safe_time_seconds,json=minSafeTimeSeconds" json:"min_safe_time_seconds,omitempty"` + SafeReplicaName []string `protobuf:"bytes,36,rep,name=safe_replica_name,json=safeReplicaName" json:"safe_replica_name,omitempty"` + PersistOffset *bool `protobuf:"varint,37,opt,name=persist_offset,json=persistOffset,def=0" json:"persist_offset,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Query) Reset() { *m = Query{} } +func (m *Query) String() string { return proto.CompactTextString(m) } +func (*Query) ProtoMessage() {} +func (*Query) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +const Default_Query_Offset int32 = 0 +const Default_Query_RequirePerfectPlan bool = false +const Default_Query_KeysOnly bool = false +const Default_Query_Compile bool = false +const Default_Query_PersistOffset bool = false + +func (m *Query) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *Query) GetApp() string { + if m != nil && m.App != nil { + return *m.App + } + return "" +} + +func (m *Query) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *Query) GetKind() string { + if m != nil && m.Kind != nil { + return *m.Kind + } + return "" +} + +func (m *Query) GetAncestor() *Reference { + if m != nil { + return m.Ancestor + } + return nil +} + +func (m *Query) GetFilter() []*Query_Filter { + if m != nil { + return m.Filter + } + return nil +} + +func (m *Query) GetSearchQuery() string { + if m != nil && m.SearchQuery != nil { + return *m.SearchQuery + } + return "" +} + +func (m *Query) GetOrder() []*Query_Order { + if m != nil { + return m.Order + } + return nil +} + +func (m *Query) GetHint() Query_Hint { + if m != nil && m.Hint != nil { + return *m.Hint + } + return Query_ORDER_FIRST +} + +func (m *Query) GetCount() int32 { + if m != nil && m.Count != nil { + return *m.Count + } + return 0 +} + +func (m *Query) GetOffset() int32 { + if m != nil && m.Offset != nil { + return *m.Offset + } + return Default_Query_Offset +} + +func (m *Query) GetLimit() int32 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return 0 +} + +func (m *Query) GetCompiledCursor() *CompiledCursor { + if m != nil { + return m.CompiledCursor + } + return nil +} + +func (m *Query) GetEndCompiledCursor() *CompiledCursor { + if m != nil { + return m.EndCompiledCursor + } + return nil +} + +func (m *Query) GetCompositeIndex() []*CompositeIndex { + if m != nil { + return m.CompositeIndex + } + return nil +} + +func (m *Query) GetRequirePerfectPlan() bool { + if m != nil && m.RequirePerfectPlan != nil { + return *m.RequirePerfectPlan + } + return Default_Query_RequirePerfectPlan +} + +func (m *Query) GetKeysOnly() bool { + if m != nil && m.KeysOnly != nil { + return *m.KeysOnly + } + return Default_Query_KeysOnly +} + +func (m *Query) GetTransaction() *Transaction { + if m != nil { + return m.Transaction + } + return nil +} + +func (m *Query) GetCompile() bool { + if m != nil && m.Compile != nil { + return *m.Compile + } + return Default_Query_Compile +} + +func (m *Query) GetFailoverMs() int64 { + if m != nil && m.FailoverMs != nil { + return *m.FailoverMs + } + return 0 +} + +func (m *Query) GetStrong() bool { + if m != nil && m.Strong != nil { + return *m.Strong + } + return false +} + +func (m *Query) GetPropertyName() []string { + if m != nil { + return m.PropertyName + } + return nil +} + +func (m *Query) GetGroupByPropertyName() []string { + if m != nil { + return m.GroupByPropertyName + } + return nil +} + +func (m *Query) GetDistinct() bool { + if m != nil && m.Distinct != nil { + return *m.Distinct + } + return false +} + +func (m *Query) GetMinSafeTimeSeconds() int64 { + if m != nil && m.MinSafeTimeSeconds != nil { + return *m.MinSafeTimeSeconds + } + return 0 +} + +func (m *Query) GetSafeReplicaName() []string { + if m != nil { + return m.SafeReplicaName + } + return nil +} + +func (m *Query) GetPersistOffset() bool { + if m != nil && m.PersistOffset != nil { + return *m.PersistOffset + } + return Default_Query_PersistOffset +} + +type Query_Filter struct { + Op *Query_Filter_Operator `protobuf:"varint,6,req,name=op,enum=appengine.Query_Filter_Operator" json:"op,omitempty"` + Property []*Property `protobuf:"bytes,14,rep,name=property" json:"property,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Query_Filter) Reset() { *m = Query_Filter{} } +func (m *Query_Filter) String() string { return proto.CompactTextString(m) } +func (*Query_Filter) ProtoMessage() {} +func (*Query_Filter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 0} } + +func (m *Query_Filter) GetOp() Query_Filter_Operator { + if m != nil && m.Op != nil { + return *m.Op + } + return Query_Filter_LESS_THAN +} + +func (m *Query_Filter) GetProperty() []*Property { + if m != nil { + return m.Property + } + return nil +} + +type Query_Order struct { + Property *string `protobuf:"bytes,10,req,name=property" json:"property,omitempty"` + Direction *Query_Order_Direction `protobuf:"varint,11,opt,name=direction,enum=appengine.Query_Order_Direction,def=1" json:"direction,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Query_Order) Reset() { *m = Query_Order{} } +func (m *Query_Order) String() string { return proto.CompactTextString(m) } +func (*Query_Order) ProtoMessage() {} +func (*Query_Order) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 1} } + +const Default_Query_Order_Direction Query_Order_Direction = Query_Order_ASCENDING + +func (m *Query_Order) GetProperty() string { + if m != nil && m.Property != nil { + return *m.Property + } + return "" +} + +func (m *Query_Order) GetDirection() Query_Order_Direction { + if m != nil && m.Direction != nil { + return *m.Direction + } + return Default_Query_Order_Direction +} + +type CompiledQuery struct { + Primaryscan *CompiledQuery_PrimaryScan `protobuf:"group,1,req,name=PrimaryScan,json=primaryscan" json:"primaryscan,omitempty"` + Mergejoinscan []*CompiledQuery_MergeJoinScan `protobuf:"group,7,rep,name=MergeJoinScan,json=mergejoinscan" json:"mergejoinscan,omitempty"` + IndexDef *Index `protobuf:"bytes,21,opt,name=index_def,json=indexDef" json:"index_def,omitempty"` + Offset *int32 `protobuf:"varint,10,opt,name=offset,def=0" json:"offset,omitempty"` + Limit *int32 `protobuf:"varint,11,opt,name=limit" json:"limit,omitempty"` + KeysOnly *bool `protobuf:"varint,12,req,name=keys_only,json=keysOnly" json:"keys_only,omitempty"` + PropertyName []string `protobuf:"bytes,24,rep,name=property_name,json=propertyName" json:"property_name,omitempty"` + DistinctInfixSize *int32 `protobuf:"varint,25,opt,name=distinct_infix_size,json=distinctInfixSize" json:"distinct_infix_size,omitempty"` + Entityfilter *CompiledQuery_EntityFilter `protobuf:"group,13,opt,name=EntityFilter,json=entityfilter" json:"entityfilter,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompiledQuery) Reset() { *m = CompiledQuery{} } +func (m *CompiledQuery) String() string { return proto.CompactTextString(m) } +func (*CompiledQuery) ProtoMessage() {} +func (*CompiledQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +const Default_CompiledQuery_Offset int32 = 0 + +func (m *CompiledQuery) GetPrimaryscan() *CompiledQuery_PrimaryScan { + if m != nil { + return m.Primaryscan + } + return nil +} + +func (m *CompiledQuery) GetMergejoinscan() []*CompiledQuery_MergeJoinScan { + if m != nil { + return m.Mergejoinscan + } + return nil +} + +func (m *CompiledQuery) GetIndexDef() *Index { + if m != nil { + return m.IndexDef + } + return nil +} + +func (m *CompiledQuery) GetOffset() int32 { + if m != nil && m.Offset != nil { + return *m.Offset + } + return Default_CompiledQuery_Offset +} + +func (m *CompiledQuery) GetLimit() int32 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return 0 +} + +func (m *CompiledQuery) GetKeysOnly() bool { + if m != nil && m.KeysOnly != nil { + return *m.KeysOnly + } + return false +} + +func (m *CompiledQuery) GetPropertyName() []string { + if m != nil { + return m.PropertyName + } + return nil +} + +func (m *CompiledQuery) GetDistinctInfixSize() int32 { + if m != nil && m.DistinctInfixSize != nil { + return *m.DistinctInfixSize + } + return 0 +} + +func (m *CompiledQuery) GetEntityfilter() *CompiledQuery_EntityFilter { + if m != nil { + return m.Entityfilter + } + return nil +} + +type CompiledQuery_PrimaryScan struct { + IndexName *string `protobuf:"bytes,2,opt,name=index_name,json=indexName" json:"index_name,omitempty"` + StartKey *string `protobuf:"bytes,3,opt,name=start_key,json=startKey" json:"start_key,omitempty"` + StartInclusive *bool `protobuf:"varint,4,opt,name=start_inclusive,json=startInclusive" json:"start_inclusive,omitempty"` + EndKey *string `protobuf:"bytes,5,opt,name=end_key,json=endKey" json:"end_key,omitempty"` + EndInclusive *bool `protobuf:"varint,6,opt,name=end_inclusive,json=endInclusive" json:"end_inclusive,omitempty"` + StartPostfixValue []string `protobuf:"bytes,22,rep,name=start_postfix_value,json=startPostfixValue" json:"start_postfix_value,omitempty"` + EndPostfixValue []string `protobuf:"bytes,23,rep,name=end_postfix_value,json=endPostfixValue" json:"end_postfix_value,omitempty"` + EndUnappliedLogTimestampUs *int64 `protobuf:"varint,19,opt,name=end_unapplied_log_timestamp_us,json=endUnappliedLogTimestampUs" json:"end_unapplied_log_timestamp_us,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompiledQuery_PrimaryScan) Reset() { *m = CompiledQuery_PrimaryScan{} } +func (m *CompiledQuery_PrimaryScan) String() string { return proto.CompactTextString(m) } +func (*CompiledQuery_PrimaryScan) ProtoMessage() {} +func (*CompiledQuery_PrimaryScan) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16, 0} } + +func (m *CompiledQuery_PrimaryScan) GetIndexName() string { + if m != nil && m.IndexName != nil { + return *m.IndexName + } + return "" +} + +func (m *CompiledQuery_PrimaryScan) GetStartKey() string { + if m != nil && m.StartKey != nil { + return *m.StartKey + } + return "" +} + +func (m *CompiledQuery_PrimaryScan) GetStartInclusive() bool { + if m != nil && m.StartInclusive != nil { + return *m.StartInclusive + } + return false +} + +func (m *CompiledQuery_PrimaryScan) GetEndKey() string { + if m != nil && m.EndKey != nil { + return *m.EndKey + } + return "" +} + +func (m *CompiledQuery_PrimaryScan) GetEndInclusive() bool { + if m != nil && m.EndInclusive != nil { + return *m.EndInclusive + } + return false +} + +func (m *CompiledQuery_PrimaryScan) GetStartPostfixValue() []string { + if m != nil { + return m.StartPostfixValue + } + return nil +} + +func (m *CompiledQuery_PrimaryScan) GetEndPostfixValue() []string { + if m != nil { + return m.EndPostfixValue + } + return nil +} + +func (m *CompiledQuery_PrimaryScan) GetEndUnappliedLogTimestampUs() int64 { + if m != nil && m.EndUnappliedLogTimestampUs != nil { + return *m.EndUnappliedLogTimestampUs + } + return 0 +} + +type CompiledQuery_MergeJoinScan struct { + IndexName *string `protobuf:"bytes,8,req,name=index_name,json=indexName" json:"index_name,omitempty"` + PrefixValue []string `protobuf:"bytes,9,rep,name=prefix_value,json=prefixValue" json:"prefix_value,omitempty"` + ValuePrefix *bool `protobuf:"varint,20,opt,name=value_prefix,json=valuePrefix,def=0" json:"value_prefix,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompiledQuery_MergeJoinScan) Reset() { *m = CompiledQuery_MergeJoinScan{} } +func (m *CompiledQuery_MergeJoinScan) String() string { return proto.CompactTextString(m) } +func (*CompiledQuery_MergeJoinScan) ProtoMessage() {} +func (*CompiledQuery_MergeJoinScan) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16, 1} } + +const Default_CompiledQuery_MergeJoinScan_ValuePrefix bool = false + +func (m *CompiledQuery_MergeJoinScan) GetIndexName() string { + if m != nil && m.IndexName != nil { + return *m.IndexName + } + return "" +} + +func (m *CompiledQuery_MergeJoinScan) GetPrefixValue() []string { + if m != nil { + return m.PrefixValue + } + return nil +} + +func (m *CompiledQuery_MergeJoinScan) GetValuePrefix() bool { + if m != nil && m.ValuePrefix != nil { + return *m.ValuePrefix + } + return Default_CompiledQuery_MergeJoinScan_ValuePrefix +} + +type CompiledQuery_EntityFilter struct { + Distinct *bool `protobuf:"varint,14,opt,name=distinct,def=0" json:"distinct,omitempty"` + Kind *string `protobuf:"bytes,17,opt,name=kind" json:"kind,omitempty"` + Ancestor *Reference `protobuf:"bytes,18,opt,name=ancestor" json:"ancestor,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompiledQuery_EntityFilter) Reset() { *m = CompiledQuery_EntityFilter{} } +func (m *CompiledQuery_EntityFilter) String() string { return proto.CompactTextString(m) } +func (*CompiledQuery_EntityFilter) ProtoMessage() {} +func (*CompiledQuery_EntityFilter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16, 2} } + +const Default_CompiledQuery_EntityFilter_Distinct bool = false + +func (m *CompiledQuery_EntityFilter) GetDistinct() bool { + if m != nil && m.Distinct != nil { + return *m.Distinct + } + return Default_CompiledQuery_EntityFilter_Distinct +} + +func (m *CompiledQuery_EntityFilter) GetKind() string { + if m != nil && m.Kind != nil { + return *m.Kind + } + return "" +} + +func (m *CompiledQuery_EntityFilter) GetAncestor() *Reference { + if m != nil { + return m.Ancestor + } + return nil +} + +type CompiledCursor struct { + Position *CompiledCursor_Position `protobuf:"group,2,opt,name=Position,json=position" json:"position,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompiledCursor) Reset() { *m = CompiledCursor{} } +func (m *CompiledCursor) String() string { return proto.CompactTextString(m) } +func (*CompiledCursor) ProtoMessage() {} +func (*CompiledCursor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *CompiledCursor) GetPosition() *CompiledCursor_Position { + if m != nil { + return m.Position + } + return nil +} + +type CompiledCursor_Position struct { + StartKey *string `protobuf:"bytes,27,opt,name=start_key,json=startKey" json:"start_key,omitempty"` + Indexvalue []*CompiledCursor_Position_IndexValue `protobuf:"group,29,rep,name=IndexValue,json=indexvalue" json:"indexvalue,omitempty"` + Key *Reference `protobuf:"bytes,32,opt,name=key" json:"key,omitempty"` + StartInclusive *bool `protobuf:"varint,28,opt,name=start_inclusive,json=startInclusive,def=1" json:"start_inclusive,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompiledCursor_Position) Reset() { *m = CompiledCursor_Position{} } +func (m *CompiledCursor_Position) String() string { return proto.CompactTextString(m) } +func (*CompiledCursor_Position) ProtoMessage() {} +func (*CompiledCursor_Position) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17, 0} } + +const Default_CompiledCursor_Position_StartInclusive bool = true + +func (m *CompiledCursor_Position) GetStartKey() string { + if m != nil && m.StartKey != nil { + return *m.StartKey + } + return "" +} + +func (m *CompiledCursor_Position) GetIndexvalue() []*CompiledCursor_Position_IndexValue { + if m != nil { + return m.Indexvalue + } + return nil +} + +func (m *CompiledCursor_Position) GetKey() *Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *CompiledCursor_Position) GetStartInclusive() bool { + if m != nil && m.StartInclusive != nil { + return *m.StartInclusive + } + return Default_CompiledCursor_Position_StartInclusive +} + +type CompiledCursor_Position_IndexValue struct { + Property *string `protobuf:"bytes,30,opt,name=property" json:"property,omitempty"` + Value *PropertyValue `protobuf:"bytes,31,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompiledCursor_Position_IndexValue) Reset() { *m = CompiledCursor_Position_IndexValue{} } +func (m *CompiledCursor_Position_IndexValue) String() string { return proto.CompactTextString(m) } +func (*CompiledCursor_Position_IndexValue) ProtoMessage() {} +func (*CompiledCursor_Position_IndexValue) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{17, 0, 0} +} + +func (m *CompiledCursor_Position_IndexValue) GetProperty() string { + if m != nil && m.Property != nil { + return *m.Property + } + return "" +} + +func (m *CompiledCursor_Position_IndexValue) GetValue() *PropertyValue { + if m != nil { + return m.Value + } + return nil +} + +type Cursor struct { + Cursor *uint64 `protobuf:"fixed64,1,req,name=cursor" json:"cursor,omitempty"` + App *string `protobuf:"bytes,2,opt,name=app" json:"app,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Cursor) Reset() { *m = Cursor{} } +func (m *Cursor) String() string { return proto.CompactTextString(m) } +func (*Cursor) ProtoMessage() {} +func (*Cursor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *Cursor) GetCursor() uint64 { + if m != nil && m.Cursor != nil { + return *m.Cursor + } + return 0 +} + +func (m *Cursor) GetApp() string { + if m != nil && m.App != nil { + return *m.App + } + return "" +} + +type Error struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *Error) Reset() { *m = Error{} } +func (m *Error) String() string { return proto.CompactTextString(m) } +func (*Error) ProtoMessage() {} +func (*Error) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +type Cost struct { + IndexWrites *int32 `protobuf:"varint,1,opt,name=index_writes,json=indexWrites" json:"index_writes,omitempty"` + IndexWriteBytes *int32 `protobuf:"varint,2,opt,name=index_write_bytes,json=indexWriteBytes" json:"index_write_bytes,omitempty"` + EntityWrites *int32 `protobuf:"varint,3,opt,name=entity_writes,json=entityWrites" json:"entity_writes,omitempty"` + EntityWriteBytes *int32 `protobuf:"varint,4,opt,name=entity_write_bytes,json=entityWriteBytes" json:"entity_write_bytes,omitempty"` + Commitcost *Cost_CommitCost `protobuf:"group,5,opt,name=CommitCost,json=commitcost" json:"commitcost,omitempty"` + ApproximateStorageDelta *int32 `protobuf:"varint,8,opt,name=approximate_storage_delta,json=approximateStorageDelta" json:"approximate_storage_delta,omitempty"` + IdSequenceUpdates *int32 `protobuf:"varint,9,opt,name=id_sequence_updates,json=idSequenceUpdates" json:"id_sequence_updates,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Cost) Reset() { *m = Cost{} } +func (m *Cost) String() string { return proto.CompactTextString(m) } +func (*Cost) ProtoMessage() {} +func (*Cost) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } + +func (m *Cost) GetIndexWrites() int32 { + if m != nil && m.IndexWrites != nil { + return *m.IndexWrites + } + return 0 +} + +func (m *Cost) GetIndexWriteBytes() int32 { + if m != nil && m.IndexWriteBytes != nil { + return *m.IndexWriteBytes + } + return 0 +} + +func (m *Cost) GetEntityWrites() int32 { + if m != nil && m.EntityWrites != nil { + return *m.EntityWrites + } + return 0 +} + +func (m *Cost) GetEntityWriteBytes() int32 { + if m != nil && m.EntityWriteBytes != nil { + return *m.EntityWriteBytes + } + return 0 +} + +func (m *Cost) GetCommitcost() *Cost_CommitCost { + if m != nil { + return m.Commitcost + } + return nil +} + +func (m *Cost) GetApproximateStorageDelta() int32 { + if m != nil && m.ApproximateStorageDelta != nil { + return *m.ApproximateStorageDelta + } + return 0 +} + +func (m *Cost) GetIdSequenceUpdates() int32 { + if m != nil && m.IdSequenceUpdates != nil { + return *m.IdSequenceUpdates + } + return 0 +} + +type Cost_CommitCost struct { + RequestedEntityPuts *int32 `protobuf:"varint,6,opt,name=requested_entity_puts,json=requestedEntityPuts" json:"requested_entity_puts,omitempty"` + RequestedEntityDeletes *int32 `protobuf:"varint,7,opt,name=requested_entity_deletes,json=requestedEntityDeletes" json:"requested_entity_deletes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Cost_CommitCost) Reset() { *m = Cost_CommitCost{} } +func (m *Cost_CommitCost) String() string { return proto.CompactTextString(m) } +func (*Cost_CommitCost) ProtoMessage() {} +func (*Cost_CommitCost) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20, 0} } + +func (m *Cost_CommitCost) GetRequestedEntityPuts() int32 { + if m != nil && m.RequestedEntityPuts != nil { + return *m.RequestedEntityPuts + } + return 0 +} + +func (m *Cost_CommitCost) GetRequestedEntityDeletes() int32 { + if m != nil && m.RequestedEntityDeletes != nil { + return *m.RequestedEntityDeletes + } + return 0 +} + +type GetRequest struct { + Header *InternalHeader `protobuf:"bytes,6,opt,name=header" json:"header,omitempty"` + Key []*Reference `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"` + Transaction *Transaction `protobuf:"bytes,2,opt,name=transaction" json:"transaction,omitempty"` + FailoverMs *int64 `protobuf:"varint,3,opt,name=failover_ms,json=failoverMs" json:"failover_ms,omitempty"` + Strong *bool `protobuf:"varint,4,opt,name=strong" json:"strong,omitempty"` + AllowDeferred *bool `protobuf:"varint,5,opt,name=allow_deferred,json=allowDeferred,def=0" json:"allow_deferred,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetRequest) Reset() { *m = GetRequest{} } +func (m *GetRequest) String() string { return proto.CompactTextString(m) } +func (*GetRequest) ProtoMessage() {} +func (*GetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +const Default_GetRequest_AllowDeferred bool = false + +func (m *GetRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *GetRequest) GetKey() []*Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *GetRequest) GetTransaction() *Transaction { + if m != nil { + return m.Transaction + } + return nil +} + +func (m *GetRequest) GetFailoverMs() int64 { + if m != nil && m.FailoverMs != nil { + return *m.FailoverMs + } + return 0 +} + +func (m *GetRequest) GetStrong() bool { + if m != nil && m.Strong != nil { + return *m.Strong + } + return false +} + +func (m *GetRequest) GetAllowDeferred() bool { + if m != nil && m.AllowDeferred != nil { + return *m.AllowDeferred + } + return Default_GetRequest_AllowDeferred +} + +type GetResponse struct { + Entity []*GetResponse_Entity `protobuf:"group,1,rep,name=Entity,json=entity" json:"entity,omitempty"` + Deferred []*Reference `protobuf:"bytes,5,rep,name=deferred" json:"deferred,omitempty"` + InOrder *bool `protobuf:"varint,6,opt,name=in_order,json=inOrder,def=1" json:"in_order,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetResponse) Reset() { *m = GetResponse{} } +func (m *GetResponse) String() string { return proto.CompactTextString(m) } +func (*GetResponse) ProtoMessage() {} +func (*GetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } + +const Default_GetResponse_InOrder bool = true + +func (m *GetResponse) GetEntity() []*GetResponse_Entity { + if m != nil { + return m.Entity + } + return nil +} + +func (m *GetResponse) GetDeferred() []*Reference { + if m != nil { + return m.Deferred + } + return nil +} + +func (m *GetResponse) GetInOrder() bool { + if m != nil && m.InOrder != nil { + return *m.InOrder + } + return Default_GetResponse_InOrder +} + +type GetResponse_Entity struct { + Entity *EntityProto `protobuf:"bytes,2,opt,name=entity" json:"entity,omitempty"` + Key *Reference `protobuf:"bytes,4,opt,name=key" json:"key,omitempty"` + Version *int64 `protobuf:"varint,3,opt,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetResponse_Entity) Reset() { *m = GetResponse_Entity{} } +func (m *GetResponse_Entity) String() string { return proto.CompactTextString(m) } +func (*GetResponse_Entity) ProtoMessage() {} +func (*GetResponse_Entity) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22, 0} } + +func (m *GetResponse_Entity) GetEntity() *EntityProto { + if m != nil { + return m.Entity + } + return nil +} + +func (m *GetResponse_Entity) GetKey() *Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *GetResponse_Entity) GetVersion() int64 { + if m != nil && m.Version != nil { + return *m.Version + } + return 0 +} + +type PutRequest struct { + Header *InternalHeader `protobuf:"bytes,11,opt,name=header" json:"header,omitempty"` + Entity []*EntityProto `protobuf:"bytes,1,rep,name=entity" json:"entity,omitempty"` + Transaction *Transaction `protobuf:"bytes,2,opt,name=transaction" json:"transaction,omitempty"` + CompositeIndex []*CompositeIndex `protobuf:"bytes,3,rep,name=composite_index,json=compositeIndex" json:"composite_index,omitempty"` + Trusted *bool `protobuf:"varint,4,opt,name=trusted,def=0" json:"trusted,omitempty"` + Force *bool `protobuf:"varint,7,opt,name=force,def=0" json:"force,omitempty"` + MarkChanges *bool `protobuf:"varint,8,opt,name=mark_changes,json=markChanges,def=0" json:"mark_changes,omitempty"` + Snapshot []*Snapshot `protobuf:"bytes,9,rep,name=snapshot" json:"snapshot,omitempty"` + AutoIdPolicy *PutRequest_AutoIdPolicy `protobuf:"varint,10,opt,name=auto_id_policy,json=autoIdPolicy,enum=appengine.PutRequest_AutoIdPolicy,def=0" json:"auto_id_policy,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PutRequest) Reset() { *m = PutRequest{} } +func (m *PutRequest) String() string { return proto.CompactTextString(m) } +func (*PutRequest) ProtoMessage() {} +func (*PutRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +const Default_PutRequest_Trusted bool = false +const Default_PutRequest_Force bool = false +const Default_PutRequest_MarkChanges bool = false +const Default_PutRequest_AutoIdPolicy PutRequest_AutoIdPolicy = PutRequest_CURRENT + +func (m *PutRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *PutRequest) GetEntity() []*EntityProto { + if m != nil { + return m.Entity + } + return nil +} + +func (m *PutRequest) GetTransaction() *Transaction { + if m != nil { + return m.Transaction + } + return nil +} + +func (m *PutRequest) GetCompositeIndex() []*CompositeIndex { + if m != nil { + return m.CompositeIndex + } + return nil +} + +func (m *PutRequest) GetTrusted() bool { + if m != nil && m.Trusted != nil { + return *m.Trusted + } + return Default_PutRequest_Trusted +} + +func (m *PutRequest) GetForce() bool { + if m != nil && m.Force != nil { + return *m.Force + } + return Default_PutRequest_Force +} + +func (m *PutRequest) GetMarkChanges() bool { + if m != nil && m.MarkChanges != nil { + return *m.MarkChanges + } + return Default_PutRequest_MarkChanges +} + +func (m *PutRequest) GetSnapshot() []*Snapshot { + if m != nil { + return m.Snapshot + } + return nil +} + +func (m *PutRequest) GetAutoIdPolicy() PutRequest_AutoIdPolicy { + if m != nil && m.AutoIdPolicy != nil { + return *m.AutoIdPolicy + } + return Default_PutRequest_AutoIdPolicy +} + +type PutResponse struct { + Key []*Reference `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"` + Cost *Cost `protobuf:"bytes,2,opt,name=cost" json:"cost,omitempty"` + Version []int64 `protobuf:"varint,3,rep,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PutResponse) Reset() { *m = PutResponse{} } +func (m *PutResponse) String() string { return proto.CompactTextString(m) } +func (*PutResponse) ProtoMessage() {} +func (*PutResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +func (m *PutResponse) GetKey() []*Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *PutResponse) GetCost() *Cost { + if m != nil { + return m.Cost + } + return nil +} + +func (m *PutResponse) GetVersion() []int64 { + if m != nil { + return m.Version + } + return nil +} + +type TouchRequest struct { + Header *InternalHeader `protobuf:"bytes,10,opt,name=header" json:"header,omitempty"` + Key []*Reference `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"` + CompositeIndex []*CompositeIndex `protobuf:"bytes,2,rep,name=composite_index,json=compositeIndex" json:"composite_index,omitempty"` + Force *bool `protobuf:"varint,3,opt,name=force,def=0" json:"force,omitempty"` + Snapshot []*Snapshot `protobuf:"bytes,9,rep,name=snapshot" json:"snapshot,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TouchRequest) Reset() { *m = TouchRequest{} } +func (m *TouchRequest) String() string { return proto.CompactTextString(m) } +func (*TouchRequest) ProtoMessage() {} +func (*TouchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } + +const Default_TouchRequest_Force bool = false + +func (m *TouchRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *TouchRequest) GetKey() []*Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *TouchRequest) GetCompositeIndex() []*CompositeIndex { + if m != nil { + return m.CompositeIndex + } + return nil +} + +func (m *TouchRequest) GetForce() bool { + if m != nil && m.Force != nil { + return *m.Force + } + return Default_TouchRequest_Force +} + +func (m *TouchRequest) GetSnapshot() []*Snapshot { + if m != nil { + return m.Snapshot + } + return nil +} + +type TouchResponse struct { + Cost *Cost `protobuf:"bytes,1,opt,name=cost" json:"cost,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TouchResponse) Reset() { *m = TouchResponse{} } +func (m *TouchResponse) String() string { return proto.CompactTextString(m) } +func (*TouchResponse) ProtoMessage() {} +func (*TouchResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } + +func (m *TouchResponse) GetCost() *Cost { + if m != nil { + return m.Cost + } + return nil +} + +type DeleteRequest struct { + Header *InternalHeader `protobuf:"bytes,10,opt,name=header" json:"header,omitempty"` + Key []*Reference `protobuf:"bytes,6,rep,name=key" json:"key,omitempty"` + Transaction *Transaction `protobuf:"bytes,5,opt,name=transaction" json:"transaction,omitempty"` + Trusted *bool `protobuf:"varint,4,opt,name=trusted,def=0" json:"trusted,omitempty"` + Force *bool `protobuf:"varint,7,opt,name=force,def=0" json:"force,omitempty"` + MarkChanges *bool `protobuf:"varint,8,opt,name=mark_changes,json=markChanges,def=0" json:"mark_changes,omitempty"` + Snapshot []*Snapshot `protobuf:"bytes,9,rep,name=snapshot" json:"snapshot,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } +func (m *DeleteRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteRequest) ProtoMessage() {} +func (*DeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } + +const Default_DeleteRequest_Trusted bool = false +const Default_DeleteRequest_Force bool = false +const Default_DeleteRequest_MarkChanges bool = false + +func (m *DeleteRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *DeleteRequest) GetKey() []*Reference { + if m != nil { + return m.Key + } + return nil +} + +func (m *DeleteRequest) GetTransaction() *Transaction { + if m != nil { + return m.Transaction + } + return nil +} + +func (m *DeleteRequest) GetTrusted() bool { + if m != nil && m.Trusted != nil { + return *m.Trusted + } + return Default_DeleteRequest_Trusted +} + +func (m *DeleteRequest) GetForce() bool { + if m != nil && m.Force != nil { + return *m.Force + } + return Default_DeleteRequest_Force +} + +func (m *DeleteRequest) GetMarkChanges() bool { + if m != nil && m.MarkChanges != nil { + return *m.MarkChanges + } + return Default_DeleteRequest_MarkChanges +} + +func (m *DeleteRequest) GetSnapshot() []*Snapshot { + if m != nil { + return m.Snapshot + } + return nil +} + +type DeleteResponse struct { + Cost *Cost `protobuf:"bytes,1,opt,name=cost" json:"cost,omitempty"` + Version []int64 `protobuf:"varint,3,rep,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteResponse) Reset() { *m = DeleteResponse{} } +func (m *DeleteResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteResponse) ProtoMessage() {} +func (*DeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +func (m *DeleteResponse) GetCost() *Cost { + if m != nil { + return m.Cost + } + return nil +} + +func (m *DeleteResponse) GetVersion() []int64 { + if m != nil { + return m.Version + } + return nil +} + +type NextRequest struct { + Header *InternalHeader `protobuf:"bytes,5,opt,name=header" json:"header,omitempty"` + Cursor *Cursor `protobuf:"bytes,1,req,name=cursor" json:"cursor,omitempty"` + Count *int32 `protobuf:"varint,2,opt,name=count" json:"count,omitempty"` + Offset *int32 `protobuf:"varint,4,opt,name=offset,def=0" json:"offset,omitempty"` + Compile *bool `protobuf:"varint,3,opt,name=compile,def=0" json:"compile,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *NextRequest) Reset() { *m = NextRequest{} } +func (m *NextRequest) String() string { return proto.CompactTextString(m) } +func (*NextRequest) ProtoMessage() {} +func (*NextRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } + +const Default_NextRequest_Offset int32 = 0 +const Default_NextRequest_Compile bool = false + +func (m *NextRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *NextRequest) GetCursor() *Cursor { + if m != nil { + return m.Cursor + } + return nil +} + +func (m *NextRequest) GetCount() int32 { + if m != nil && m.Count != nil { + return *m.Count + } + return 0 +} + +func (m *NextRequest) GetOffset() int32 { + if m != nil && m.Offset != nil { + return *m.Offset + } + return Default_NextRequest_Offset +} + +func (m *NextRequest) GetCompile() bool { + if m != nil && m.Compile != nil { + return *m.Compile + } + return Default_NextRequest_Compile +} + +type QueryResult struct { + Cursor *Cursor `protobuf:"bytes,1,opt,name=cursor" json:"cursor,omitempty"` + Result []*EntityProto `protobuf:"bytes,2,rep,name=result" json:"result,omitempty"` + SkippedResults *int32 `protobuf:"varint,7,opt,name=skipped_results,json=skippedResults" json:"skipped_results,omitempty"` + MoreResults *bool `protobuf:"varint,3,req,name=more_results,json=moreResults" json:"more_results,omitempty"` + KeysOnly *bool `protobuf:"varint,4,opt,name=keys_only,json=keysOnly" json:"keys_only,omitempty"` + IndexOnly *bool `protobuf:"varint,9,opt,name=index_only,json=indexOnly" json:"index_only,omitempty"` + SmallOps *bool `protobuf:"varint,10,opt,name=small_ops,json=smallOps" json:"small_ops,omitempty"` + CompiledQuery *CompiledQuery `protobuf:"bytes,5,opt,name=compiled_query,json=compiledQuery" json:"compiled_query,omitempty"` + CompiledCursor *CompiledCursor `protobuf:"bytes,6,opt,name=compiled_cursor,json=compiledCursor" json:"compiled_cursor,omitempty"` + Index []*CompositeIndex `protobuf:"bytes,8,rep,name=index" json:"index,omitempty"` + Version []int64 `protobuf:"varint,11,rep,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *QueryResult) Reset() { *m = QueryResult{} } +func (m *QueryResult) String() string { return proto.CompactTextString(m) } +func (*QueryResult) ProtoMessage() {} +func (*QueryResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } + +func (m *QueryResult) GetCursor() *Cursor { + if m != nil { + return m.Cursor + } + return nil +} + +func (m *QueryResult) GetResult() []*EntityProto { + if m != nil { + return m.Result + } + return nil +} + +func (m *QueryResult) GetSkippedResults() int32 { + if m != nil && m.SkippedResults != nil { + return *m.SkippedResults + } + return 0 +} + +func (m *QueryResult) GetMoreResults() bool { + if m != nil && m.MoreResults != nil { + return *m.MoreResults + } + return false +} + +func (m *QueryResult) GetKeysOnly() bool { + if m != nil && m.KeysOnly != nil { + return *m.KeysOnly + } + return false +} + +func (m *QueryResult) GetIndexOnly() bool { + if m != nil && m.IndexOnly != nil { + return *m.IndexOnly + } + return false +} + +func (m *QueryResult) GetSmallOps() bool { + if m != nil && m.SmallOps != nil { + return *m.SmallOps + } + return false +} + +func (m *QueryResult) GetCompiledQuery() *CompiledQuery { + if m != nil { + return m.CompiledQuery + } + return nil +} + +func (m *QueryResult) GetCompiledCursor() *CompiledCursor { + if m != nil { + return m.CompiledCursor + } + return nil +} + +func (m *QueryResult) GetIndex() []*CompositeIndex { + if m != nil { + return m.Index + } + return nil +} + +func (m *QueryResult) GetVersion() []int64 { + if m != nil { + return m.Version + } + return nil +} + +type AllocateIdsRequest struct { + Header *InternalHeader `protobuf:"bytes,4,opt,name=header" json:"header,omitempty"` + ModelKey *Reference `protobuf:"bytes,1,opt,name=model_key,json=modelKey" json:"model_key,omitempty"` + Size *int64 `protobuf:"varint,2,opt,name=size" json:"size,omitempty"` + Max *int64 `protobuf:"varint,3,opt,name=max" json:"max,omitempty"` + Reserve []*Reference `protobuf:"bytes,5,rep,name=reserve" json:"reserve,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AllocateIdsRequest) Reset() { *m = AllocateIdsRequest{} } +func (m *AllocateIdsRequest) String() string { return proto.CompactTextString(m) } +func (*AllocateIdsRequest) ProtoMessage() {} +func (*AllocateIdsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +func (m *AllocateIdsRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *AllocateIdsRequest) GetModelKey() *Reference { + if m != nil { + return m.ModelKey + } + return nil +} + +func (m *AllocateIdsRequest) GetSize() int64 { + if m != nil && m.Size != nil { + return *m.Size + } + return 0 +} + +func (m *AllocateIdsRequest) GetMax() int64 { + if m != nil && m.Max != nil { + return *m.Max + } + return 0 +} + +func (m *AllocateIdsRequest) GetReserve() []*Reference { + if m != nil { + return m.Reserve + } + return nil +} + +type AllocateIdsResponse struct { + Start *int64 `protobuf:"varint,1,req,name=start" json:"start,omitempty"` + End *int64 `protobuf:"varint,2,req,name=end" json:"end,omitempty"` + Cost *Cost `protobuf:"bytes,3,opt,name=cost" json:"cost,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AllocateIdsResponse) Reset() { *m = AllocateIdsResponse{} } +func (m *AllocateIdsResponse) String() string { return proto.CompactTextString(m) } +func (*AllocateIdsResponse) ProtoMessage() {} +func (*AllocateIdsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +func (m *AllocateIdsResponse) GetStart() int64 { + if m != nil && m.Start != nil { + return *m.Start + } + return 0 +} + +func (m *AllocateIdsResponse) GetEnd() int64 { + if m != nil && m.End != nil { + return *m.End + } + return 0 +} + +func (m *AllocateIdsResponse) GetCost() *Cost { + if m != nil { + return m.Cost + } + return nil +} + +type CompositeIndices struct { + Index []*CompositeIndex `protobuf:"bytes,1,rep,name=index" json:"index,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompositeIndices) Reset() { *m = CompositeIndices{} } +func (m *CompositeIndices) String() string { return proto.CompactTextString(m) } +func (*CompositeIndices) ProtoMessage() {} +func (*CompositeIndices) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } + +func (m *CompositeIndices) GetIndex() []*CompositeIndex { + if m != nil { + return m.Index + } + return nil +} + +type AddActionsRequest struct { + Header *InternalHeader `protobuf:"bytes,3,opt,name=header" json:"header,omitempty"` + Transaction *Transaction `protobuf:"bytes,1,req,name=transaction" json:"transaction,omitempty"` + Action []*Action `protobuf:"bytes,2,rep,name=action" json:"action,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AddActionsRequest) Reset() { *m = AddActionsRequest{} } +func (m *AddActionsRequest) String() string { return proto.CompactTextString(m) } +func (*AddActionsRequest) ProtoMessage() {} +func (*AddActionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } + +func (m *AddActionsRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *AddActionsRequest) GetTransaction() *Transaction { + if m != nil { + return m.Transaction + } + return nil +} + +func (m *AddActionsRequest) GetAction() []*Action { + if m != nil { + return m.Action + } + return nil +} + +type AddActionsResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *AddActionsResponse) Reset() { *m = AddActionsResponse{} } +func (m *AddActionsResponse) String() string { return proto.CompactTextString(m) } +func (*AddActionsResponse) ProtoMessage() {} +func (*AddActionsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} } + +type BeginTransactionRequest struct { + Header *InternalHeader `protobuf:"bytes,3,opt,name=header" json:"header,omitempty"` + App *string `protobuf:"bytes,1,req,name=app" json:"app,omitempty"` + AllowMultipleEg *bool `protobuf:"varint,2,opt,name=allow_multiple_eg,json=allowMultipleEg,def=0" json:"allow_multiple_eg,omitempty"` + DatabaseId *string `protobuf:"bytes,4,opt,name=database_id,json=databaseId" json:"database_id,omitempty"` + Mode *BeginTransactionRequest_TransactionMode `protobuf:"varint,5,opt,name=mode,enum=appengine.BeginTransactionRequest_TransactionMode,def=0" json:"mode,omitempty"` + PreviousTransaction *Transaction `protobuf:"bytes,7,opt,name=previous_transaction,json=previousTransaction" json:"previous_transaction,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BeginTransactionRequest) Reset() { *m = BeginTransactionRequest{} } +func (m *BeginTransactionRequest) String() string { return proto.CompactTextString(m) } +func (*BeginTransactionRequest) ProtoMessage() {} +func (*BeginTransactionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} } + +const Default_BeginTransactionRequest_AllowMultipleEg bool = false +const Default_BeginTransactionRequest_Mode BeginTransactionRequest_TransactionMode = BeginTransactionRequest_UNKNOWN + +func (m *BeginTransactionRequest) GetHeader() *InternalHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *BeginTransactionRequest) GetApp() string { + if m != nil && m.App != nil { + return *m.App + } + return "" +} + +func (m *BeginTransactionRequest) GetAllowMultipleEg() bool { + if m != nil && m.AllowMultipleEg != nil { + return *m.AllowMultipleEg + } + return Default_BeginTransactionRequest_AllowMultipleEg +} + +func (m *BeginTransactionRequest) GetDatabaseId() string { + if m != nil && m.DatabaseId != nil { + return *m.DatabaseId + } + return "" +} + +func (m *BeginTransactionRequest) GetMode() BeginTransactionRequest_TransactionMode { + if m != nil && m.Mode != nil { + return *m.Mode + } + return Default_BeginTransactionRequest_Mode +} + +func (m *BeginTransactionRequest) GetPreviousTransaction() *Transaction { + if m != nil { + return m.PreviousTransaction + } + return nil +} + +type CommitResponse struct { + Cost *Cost `protobuf:"bytes,1,opt,name=cost" json:"cost,omitempty"` + Version []*CommitResponse_Version `protobuf:"group,3,rep,name=Version,json=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CommitResponse) Reset() { *m = CommitResponse{} } +func (m *CommitResponse) String() string { return proto.CompactTextString(m) } +func (*CommitResponse) ProtoMessage() {} +func (*CommitResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} } + +func (m *CommitResponse) GetCost() *Cost { + if m != nil { + return m.Cost + } + return nil +} + +func (m *CommitResponse) GetVersion() []*CommitResponse_Version { + if m != nil { + return m.Version + } + return nil +} + +type CommitResponse_Version struct { + RootEntityKey *Reference `protobuf:"bytes,4,req,name=root_entity_key,json=rootEntityKey" json:"root_entity_key,omitempty"` + Version *int64 `protobuf:"varint,5,req,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CommitResponse_Version) Reset() { *m = CommitResponse_Version{} } +func (m *CommitResponse_Version) String() string { return proto.CompactTextString(m) } +func (*CommitResponse_Version) ProtoMessage() {} +func (*CommitResponse_Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37, 0} } + +func (m *CommitResponse_Version) GetRootEntityKey() *Reference { + if m != nil { + return m.RootEntityKey + } + return nil +} + +func (m *CommitResponse_Version) GetVersion() int64 { + if m != nil && m.Version != nil { + return *m.Version + } + return 0 +} + +func init() { + proto.RegisterType((*Action)(nil), "appengine.Action") + proto.RegisterType((*PropertyValue)(nil), "appengine.PropertyValue") + proto.RegisterType((*PropertyValue_PointValue)(nil), "appengine.PropertyValue.PointValue") + proto.RegisterType((*PropertyValue_UserValue)(nil), "appengine.PropertyValue.UserValue") + proto.RegisterType((*PropertyValue_ReferenceValue)(nil), "appengine.PropertyValue.ReferenceValue") + proto.RegisterType((*PropertyValue_ReferenceValue_PathElement)(nil), "appengine.PropertyValue.ReferenceValue.PathElement") + proto.RegisterType((*Property)(nil), "appengine.Property") + proto.RegisterType((*Path)(nil), "appengine.Path") + proto.RegisterType((*Path_Element)(nil), "appengine.Path.Element") + proto.RegisterType((*Reference)(nil), "appengine.Reference") + proto.RegisterType((*User)(nil), "appengine.User") + proto.RegisterType((*EntityProto)(nil), "appengine.EntityProto") + proto.RegisterType((*CompositeProperty)(nil), "appengine.CompositeProperty") + proto.RegisterType((*Index)(nil), "appengine.Index") + proto.RegisterType((*Index_Property)(nil), "appengine.Index.Property") + proto.RegisterType((*CompositeIndex)(nil), "appengine.CompositeIndex") + proto.RegisterType((*IndexPostfix)(nil), "appengine.IndexPostfix") + proto.RegisterType((*IndexPostfix_IndexValue)(nil), "appengine.IndexPostfix.IndexValue") + proto.RegisterType((*IndexPosition)(nil), "appengine.IndexPosition") + proto.RegisterType((*Snapshot)(nil), "appengine.Snapshot") + proto.RegisterType((*InternalHeader)(nil), "appengine.InternalHeader") + proto.RegisterType((*Transaction)(nil), "appengine.Transaction") + proto.RegisterType((*Query)(nil), "appengine.Query") + proto.RegisterType((*Query_Filter)(nil), "appengine.Query.Filter") + proto.RegisterType((*Query_Order)(nil), "appengine.Query.Order") + proto.RegisterType((*CompiledQuery)(nil), "appengine.CompiledQuery") + proto.RegisterType((*CompiledQuery_PrimaryScan)(nil), "appengine.CompiledQuery.PrimaryScan") + proto.RegisterType((*CompiledQuery_MergeJoinScan)(nil), "appengine.CompiledQuery.MergeJoinScan") + proto.RegisterType((*CompiledQuery_EntityFilter)(nil), "appengine.CompiledQuery.EntityFilter") + proto.RegisterType((*CompiledCursor)(nil), "appengine.CompiledCursor") + proto.RegisterType((*CompiledCursor_Position)(nil), "appengine.CompiledCursor.Position") + proto.RegisterType((*CompiledCursor_Position_IndexValue)(nil), "appengine.CompiledCursor.Position.IndexValue") + proto.RegisterType((*Cursor)(nil), "appengine.Cursor") + proto.RegisterType((*Error)(nil), "appengine.Error") + proto.RegisterType((*Cost)(nil), "appengine.Cost") + proto.RegisterType((*Cost_CommitCost)(nil), "appengine.Cost.CommitCost") + proto.RegisterType((*GetRequest)(nil), "appengine.GetRequest") + proto.RegisterType((*GetResponse)(nil), "appengine.GetResponse") + proto.RegisterType((*GetResponse_Entity)(nil), "appengine.GetResponse.Entity") + proto.RegisterType((*PutRequest)(nil), "appengine.PutRequest") + proto.RegisterType((*PutResponse)(nil), "appengine.PutResponse") + proto.RegisterType((*TouchRequest)(nil), "appengine.TouchRequest") + proto.RegisterType((*TouchResponse)(nil), "appengine.TouchResponse") + proto.RegisterType((*DeleteRequest)(nil), "appengine.DeleteRequest") + proto.RegisterType((*DeleteResponse)(nil), "appengine.DeleteResponse") + proto.RegisterType((*NextRequest)(nil), "appengine.NextRequest") + proto.RegisterType((*QueryResult)(nil), "appengine.QueryResult") + proto.RegisterType((*AllocateIdsRequest)(nil), "appengine.AllocateIdsRequest") + proto.RegisterType((*AllocateIdsResponse)(nil), "appengine.AllocateIdsResponse") + proto.RegisterType((*CompositeIndices)(nil), "appengine.CompositeIndices") + proto.RegisterType((*AddActionsRequest)(nil), "appengine.AddActionsRequest") + proto.RegisterType((*AddActionsResponse)(nil), "appengine.AddActionsResponse") + proto.RegisterType((*BeginTransactionRequest)(nil), "appengine.BeginTransactionRequest") + proto.RegisterType((*CommitResponse)(nil), "appengine.CommitResponse") + proto.RegisterType((*CommitResponse_Version)(nil), "appengine.CommitResponse.Version") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/datastore/datastore_v3.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 4156 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x73, 0xe3, 0x46, + 0x76, 0x37, 0xc1, 0xef, 0x47, 0x89, 0x82, 0x5a, 0xf3, 0xc1, 0xa1, 0x3f, 0x46, 0xc6, 0xac, 0x6d, + 0xd9, 0x6b, 0x73, 0x6c, 0xf9, 0x23, 0x5b, 0x4a, 0x76, 0x1d, 0x4a, 0xc4, 0x68, 0x90, 0xa1, 0x48, + 0xb9, 0x09, 0xd9, 0x9e, 0x5c, 0x50, 0x18, 0xa2, 0x29, 0x21, 0x43, 0x02, 0x30, 0x00, 0x6a, 0x46, + 0x93, 0xe4, 0x90, 0x4b, 0x2a, 0x55, 0x5b, 0xa9, 0x1c, 0x92, 0x4a, 0x25, 0xf9, 0x07, 0x72, 0xc8, + 0x39, 0x95, 0xaa, 0x54, 0xf6, 0x98, 0x5b, 0x0e, 0x7b, 0xc9, 0x31, 0x95, 0x73, 0xf2, 0x27, 0x24, + 0x39, 0xa4, 0xfa, 0x75, 0x03, 0x02, 0x28, 0x4a, 0x23, 0x6d, 0xf6, 0x90, 0x13, 0xd1, 0xef, 0xfd, + 0xba, 0xf1, 0xfa, 0xf5, 0xfb, 0x6c, 0x10, 0xba, 0xc7, 0xbe, 0x7f, 0x3c, 0x65, 0x9d, 0x63, 0x7f, + 0x6a, 0x7b, 0xc7, 0x1d, 0x3f, 0x3c, 0x7e, 0x68, 0x07, 0x01, 0xf3, 0x8e, 0x5d, 0x8f, 0x3d, 0x74, + 0xbd, 0x98, 0x85, 0x9e, 0x3d, 0x7d, 0xe8, 0xd8, 0xb1, 0x1d, 0xc5, 0x7e, 0xc8, 0xce, 0x9f, 0xac, + 0xd3, 0xcf, 0x3b, 0x41, 0xe8, 0xc7, 0x3e, 0xa9, 0xa7, 0x13, 0xb4, 0x1a, 0x54, 0xba, 0xe3, 0xd8, + 0xf5, 0x3d, 0xed, 0x1f, 0x2b, 0xb0, 0x7a, 0x18, 0xfa, 0x01, 0x0b, 0xe3, 0xb3, 0x6f, 0xed, 0xe9, + 0x9c, 0x91, 0x77, 0x00, 0x5c, 0x2f, 0xfe, 0xea, 0x0b, 0x1c, 0xb5, 0x0a, 0x9b, 0x85, 0xad, 0x22, + 0xcd, 0x50, 0x88, 0x06, 0x2b, 0xcf, 0x7c, 0x7f, 0xca, 0x6c, 0x4f, 0x20, 0x94, 0xcd, 0xc2, 0x56, + 0x8d, 0xe6, 0x68, 0x64, 0x13, 0x1a, 0x51, 0x1c, 0xba, 0xde, 0xb1, 0x80, 0x14, 0x37, 0x0b, 0x5b, + 0x75, 0x9a, 0x25, 0x71, 0x84, 0xe3, 0xcf, 0x9f, 0x4d, 0x99, 0x40, 0x94, 0x36, 0x0b, 0x5b, 0x05, + 0x9a, 0x25, 0x91, 0x3d, 0x80, 0xc0, 0x77, 0xbd, 0xf8, 0x14, 0x01, 0xe5, 0xcd, 0xc2, 0x16, 0x6c, + 0x3f, 0xe8, 0xa4, 0x7b, 0xe8, 0xe4, 0xa4, 0xee, 0x1c, 0x72, 0x28, 0x3e, 0xd2, 0xcc, 0x34, 0xf2, + 0xdb, 0x50, 0x9f, 0x47, 0x2c, 0x14, 0x6b, 0xd4, 0x70, 0x0d, 0xed, 0xd2, 0x35, 0x8e, 0x22, 0x16, + 0x8a, 0x25, 0xce, 0x27, 0x91, 0x21, 0x34, 0x43, 0x36, 0x61, 0x21, 0xf3, 0xc6, 0x4c, 0x2c, 0xb3, + 0x82, 0xcb, 0x7c, 0x70, 0xe9, 0x32, 0x34, 0x81, 0x8b, 0xb5, 0x16, 0xa6, 0xb7, 0xb7, 0x00, 0xce, + 0x85, 0x25, 0x2b, 0x50, 0x78, 0xd9, 0xaa, 0x6c, 0x2a, 0x5b, 0x05, 0x5a, 0x78, 0xc9, 0x47, 0x67, + 0xad, 0xaa, 0x18, 0x9d, 0xb5, 0xff, 0xa9, 0x00, 0xf5, 0x54, 0x26, 0x72, 0x0b, 0xca, 0x6c, 0x66, + 0xbb, 0xd3, 0x56, 0x7d, 0x53, 0xd9, 0xaa, 0x53, 0x31, 0x20, 0xf7, 0xa1, 0x61, 0xcf, 0xe3, 0x13, + 0xcb, 0xf1, 0x67, 0xb6, 0xeb, 0xb5, 0x00, 0x79, 0xc0, 0x49, 0x3d, 0xa4, 0x90, 0x36, 0xd4, 0x3c, + 0x77, 0xfc, 0xdc, 0xb3, 0x67, 0xac, 0xd5, 0xc0, 0x73, 0x48, 0xc7, 0xe4, 0x13, 0x20, 0x13, 0xe6, + 0xb0, 0xd0, 0x8e, 0x99, 0x63, 0xb9, 0x0e, 0xf3, 0x62, 0x37, 0x3e, 0x6b, 0xdd, 0x46, 0xd4, 0x7a, + 0xca, 0x31, 0x24, 0x23, 0x0f, 0x0f, 0x42, 0xff, 0xd4, 0x75, 0x58, 0xd8, 0xba, 0xb3, 0x00, 0x3f, + 0x94, 0x8c, 0xf6, 0xbf, 0x17, 0xa0, 0x99, 0xd7, 0x05, 0x51, 0xa1, 0x68, 0x07, 0x41, 0x6b, 0x15, + 0xa5, 0xe4, 0x8f, 0xe4, 0x6d, 0x00, 0x2e, 0x8a, 0x15, 0x05, 0xf6, 0x98, 0xb5, 0x6e, 0xe1, 0x5a, + 0x75, 0x4e, 0x19, 0x71, 0x02, 0x39, 0x82, 0x46, 0x60, 0xc7, 0x27, 0x6c, 0xca, 0x66, 0xcc, 0x8b, + 0x5b, 0xcd, 0xcd, 0xe2, 0x16, 0x6c, 0x7f, 0x7e, 0x4d, 0xd5, 0x77, 0x0e, 0xed, 0xf8, 0x44, 0x17, + 0x53, 0x69, 0x76, 0x9d, 0xb6, 0x0e, 0x8d, 0x0c, 0x8f, 0x10, 0x28, 0xc5, 0x67, 0x01, 0x6b, 0xad, + 0xa1, 0x5c, 0xf8, 0x4c, 0x9a, 0xa0, 0xb8, 0x4e, 0x4b, 0x45, 0xf3, 0x57, 0x5c, 0x87, 0x63, 0x50, + 0x87, 0xeb, 0x28, 0x22, 0x3e, 0x6b, 0xff, 0x51, 0x86, 0x5a, 0x22, 0x00, 0xe9, 0x42, 0x75, 0xc6, + 0x6c, 0xcf, 0xf5, 0x8e, 0xd1, 0x69, 0x9a, 0xdb, 0x6f, 0x2e, 0x11, 0xb3, 0x73, 0x20, 0x20, 0x3b, + 0x30, 0x18, 0x5a, 0x07, 0x7a, 0x77, 0x60, 0x0c, 0xf6, 0x69, 0x32, 0x8f, 0x1f, 0xa6, 0x7c, 0xb4, + 0xe6, 0xa1, 0x8b, 0x9e, 0x55, 0xa7, 0x20, 0x49, 0x47, 0xa1, 0x9b, 0x0a, 0x51, 0x14, 0x82, 0xe2, + 0x21, 0x76, 0xa0, 0x9c, 0xb8, 0x88, 0xb2, 0xd5, 0xd8, 0x6e, 0x5d, 0xa6, 0x1c, 0x2a, 0x60, 0xdc, + 0x20, 0x66, 0xf3, 0x69, 0xec, 0x06, 0x53, 0xee, 0x76, 0xca, 0x56, 0x8d, 0xa6, 0x63, 0xf2, 0x1e, + 0x40, 0xc4, 0xec, 0x70, 0x7c, 0x62, 0x3f, 0x9b, 0xb2, 0x56, 0x85, 0x7b, 0xf6, 0x4e, 0x79, 0x62, + 0x4f, 0x23, 0x46, 0x33, 0x0c, 0x62, 0xc3, 0xdd, 0x49, 0x1c, 0x59, 0xb1, 0xff, 0x9c, 0x79, 0xee, + 0x2b, 0x9b, 0x07, 0x12, 0xcb, 0x0f, 0xf8, 0x0f, 0xfa, 0x58, 0x73, 0xfb, 0xc3, 0x65, 0x5b, 0x7f, + 0x14, 0x47, 0x66, 0x66, 0xc6, 0x10, 0x27, 0xd0, 0xdb, 0x93, 0x65, 0x64, 0xd2, 0x86, 0xca, 0xd4, + 0x1f, 0xdb, 0x53, 0xd6, 0xaa, 0x73, 0x2d, 0xec, 0x28, 0xcc, 0xa3, 0x92, 0xa2, 0xfd, 0xb3, 0x02, + 0x55, 0xa9, 0x47, 0xd2, 0x84, 0x8c, 0x26, 0xd5, 0x37, 0x48, 0x0d, 0x4a, 0xbb, 0xfd, 0xe1, 0xae, + 0xda, 0xe4, 0x4f, 0xa6, 0xfe, 0xbd, 0xa9, 0xae, 0x71, 0xcc, 0xee, 0x53, 0x53, 0x1f, 0x99, 0x94, + 0x63, 0x54, 0xb2, 0x0e, 0xab, 0x5d, 0x73, 0x78, 0x60, 0xed, 0x75, 0x4d, 0x7d, 0x7f, 0x48, 0x9f, + 0xaa, 0x05, 0xb2, 0x0a, 0x75, 0x24, 0xf5, 0x8d, 0xc1, 0x13, 0x55, 0xe1, 0x33, 0x70, 0x68, 0x1a, + 0x66, 0x5f, 0x57, 0x8b, 0x44, 0x85, 0x15, 0x31, 0x63, 0x38, 0x30, 0xf5, 0x81, 0xa9, 0x96, 0x52, + 0xca, 0xe8, 0xe8, 0xe0, 0xa0, 0x4b, 0x9f, 0xaa, 0x65, 0xb2, 0x06, 0x0d, 0xa4, 0x74, 0x8f, 0xcc, + 0xc7, 0x43, 0xaa, 0x56, 0x48, 0x03, 0xaa, 0xfb, 0x3d, 0xeb, 0xbb, 0xc7, 0xfa, 0x40, 0xad, 0x92, + 0x15, 0xa8, 0xed, 0xf7, 0x2c, 0xfd, 0xa0, 0x6b, 0xf4, 0xd5, 0x1a, 0x9f, 0xbd, 0xaf, 0x0f, 0xe9, + 0x68, 0x64, 0x1d, 0x0e, 0x8d, 0x81, 0xa9, 0xd6, 0x49, 0x1d, 0xca, 0xfb, 0x3d, 0xcb, 0x38, 0x50, + 0x81, 0x10, 0x68, 0xee, 0xf7, 0xac, 0xc3, 0xc7, 0xc3, 0x81, 0x3e, 0x38, 0x3a, 0xd8, 0xd5, 0xa9, + 0xda, 0x20, 0xb7, 0x40, 0xe5, 0xb4, 0xe1, 0xc8, 0xec, 0xf6, 0xbb, 0xbd, 0x1e, 0xd5, 0x47, 0x23, + 0x75, 0x85, 0x4b, 0xbd, 0xdf, 0xb3, 0x68, 0xd7, 0xe4, 0xfb, 0x5a, 0xe5, 0x2f, 0xe4, 0x7b, 0x7f, + 0xa2, 0x3f, 0x55, 0xd7, 0xf9, 0x2b, 0xf4, 0x81, 0x69, 0x98, 0x4f, 0xad, 0x43, 0x3a, 0x34, 0x87, + 0xea, 0x06, 0x17, 0xd0, 0x18, 0xf4, 0xf4, 0xef, 0xad, 0x6f, 0xbb, 0xfd, 0x23, 0x5d, 0x25, 0xda, + 0x8f, 0xe1, 0xf6, 0xd2, 0x33, 0xe1, 0xaa, 0x7b, 0x6c, 0x1e, 0xf4, 0xd5, 0x02, 0x7f, 0xe2, 0x9b, + 0x52, 0x15, 0xed, 0x0f, 0xa0, 0xc4, 0x5d, 0x86, 0x7c, 0x06, 0xd5, 0xc4, 0x1b, 0x0b, 0xe8, 0x8d, + 0x77, 0xb3, 0x67, 0x6d, 0xc7, 0x27, 0x9d, 0xc4, 0xe3, 0x12, 0x5c, 0xbb, 0x0b, 0xd5, 0x45, 0x4f, + 0x53, 0x2e, 0x78, 0x5a, 0xf1, 0x82, 0xa7, 0x95, 0x32, 0x9e, 0x66, 0x43, 0x3d, 0xf5, 0xed, 0x9b, + 0x47, 0x91, 0x07, 0x50, 0xe2, 0xde, 0xdf, 0x6a, 0xa2, 0x87, 0xac, 0x2d, 0x08, 0x4c, 0x91, 0xa9, + 0xfd, 0x43, 0x01, 0x4a, 0x3c, 0xda, 0x9e, 0x07, 0xda, 0xc2, 0x15, 0x81, 0x56, 0xb9, 0x32, 0xd0, + 0x16, 0xaf, 0x15, 0x68, 0x2b, 0x37, 0x0b, 0xb4, 0xd5, 0x4b, 0x02, 0xad, 0xf6, 0x67, 0x45, 0x68, + 0xe8, 0x38, 0xf3, 0x10, 0x13, 0xfd, 0xfb, 0x50, 0x7c, 0xce, 0xce, 0x50, 0x3f, 0x8d, 0xed, 0x5b, + 0x99, 0xdd, 0xa6, 0x2a, 0xa4, 0x1c, 0x40, 0xb6, 0x61, 0x45, 0xbc, 0xd0, 0x3a, 0x0e, 0xfd, 0x79, + 0xd0, 0x52, 0x97, 0xab, 0xa7, 0x21, 0x40, 0xfb, 0x1c, 0x43, 0xde, 0x83, 0xb2, 0xff, 0xc2, 0x63, + 0x21, 0xc6, 0xc1, 0x3c, 0x98, 0x2b, 0x8f, 0x0a, 0x2e, 0x79, 0x08, 0xa5, 0xe7, 0xae, 0xe7, 0xe0, + 0x19, 0xe6, 0x23, 0x61, 0x46, 0xd0, 0xce, 0x13, 0xd7, 0x73, 0x28, 0x02, 0xc9, 0x3d, 0xa8, 0xf1, + 0x5f, 0x8c, 0x7b, 0x65, 0xdc, 0x68, 0x95, 0x8f, 0x79, 0xd0, 0x7b, 0x08, 0xb5, 0x40, 0xc6, 0x10, + 0x4c, 0x00, 0x8d, 0xed, 0x8d, 0x25, 0xe1, 0x85, 0xa6, 0x20, 0xf2, 0x15, 0xac, 0x84, 0xf6, 0x0b, + 0x2b, 0x9d, 0xb4, 0x76, 0xf9, 0xa4, 0x46, 0x68, 0xbf, 0x48, 0x23, 0x38, 0x81, 0x52, 0x68, 0x7b, + 0xcf, 0x5b, 0x64, 0xb3, 0xb0, 0x55, 0xa6, 0xf8, 0xac, 0x7d, 0x01, 0x25, 0x2e, 0x25, 0x8f, 0x08, + 0xfb, 0x3d, 0xf4, 0xff, 0xee, 0x9e, 0xa9, 0x16, 0x12, 0x7f, 0xfe, 0x96, 0x47, 0x03, 0x45, 0x72, + 0x0f, 0xf4, 0xd1, 0xa8, 0xbb, 0xaf, 0xab, 0x45, 0xad, 0x07, 0xeb, 0x7b, 0xfe, 0x2c, 0xf0, 0x23, + 0x37, 0x66, 0xe9, 0xf2, 0xf7, 0xa0, 0xe6, 0x7a, 0x0e, 0x7b, 0x69, 0xb9, 0x0e, 0x9a, 0x56, 0x91, + 0x56, 0x71, 0x6c, 0x38, 0xdc, 0xe4, 0x4e, 0x65, 0x31, 0x55, 0xe4, 0x26, 0x87, 0x03, 0xed, 0x2f, + 0x15, 0x28, 0x1b, 0x1c, 0xc1, 0x8d, 0x4f, 0x9e, 0x14, 0x7a, 0x8f, 0x30, 0x4c, 0x10, 0x24, 0x93, + 0xfb, 0x50, 0x1b, 0x6a, 0xb6, 0x37, 0x66, 0xbc, 0xe2, 0xc3, 0x3c, 0x50, 0xa3, 0xe9, 0x98, 0x7c, + 0x99, 0xd1, 0x9f, 0x82, 0x2e, 0x7b, 0x2f, 0xa3, 0x0a, 0x7c, 0xc1, 0x12, 0x2d, 0xb6, 0xff, 0xaa, + 0x90, 0x49, 0x6e, 0xcb, 0x12, 0x4f, 0x1f, 0xea, 0x8e, 0x1b, 0x32, 0xac, 0x23, 0xe5, 0x41, 0x3f, + 0xb8, 0x74, 0xe1, 0x4e, 0x2f, 0x81, 0xee, 0xd4, 0xbb, 0xa3, 0x3d, 0x7d, 0xd0, 0xe3, 0x99, 0xef, + 0x7c, 0x01, 0xed, 0x23, 0xa8, 0xa7, 0x10, 0x0c, 0xc7, 0x09, 0x48, 0x2d, 0x70, 0xf5, 0xf6, 0xf4, + 0x74, 0xac, 0x68, 0x7f, 0xad, 0x40, 0x33, 0xd5, 0xaf, 0xd0, 0xd0, 0x6d, 0xa8, 0xd8, 0x41, 0x90, + 0xa8, 0xb6, 0x4e, 0xcb, 0x76, 0x10, 0x18, 0x8e, 0x8c, 0x2d, 0x0a, 0x6a, 0x9b, 0xc7, 0x96, 0x4f, + 0x01, 0x1c, 0x36, 0x71, 0x3d, 0x17, 0x85, 0x2e, 0xa2, 0xc1, 0xab, 0x8b, 0x42, 0xd3, 0x0c, 0x86, + 0x7c, 0x09, 0xe5, 0x28, 0xb6, 0x63, 0x91, 0x2b, 0x9b, 0xdb, 0xf7, 0x33, 0xe0, 0xbc, 0x08, 0x9d, + 0x11, 0x87, 0x51, 0x81, 0x26, 0x5f, 0xc1, 0x2d, 0xdf, 0x9b, 0x9e, 0x59, 0xf3, 0x88, 0x59, 0xee, + 0xc4, 0x0a, 0xd9, 0x0f, 0x73, 0x37, 0x64, 0x4e, 0x3e, 0xa7, 0xae, 0x73, 0xc8, 0x51, 0xc4, 0x8c, + 0x09, 0x95, 0x7c, 0xed, 0x6b, 0x28, 0xe3, 0x3a, 0x7c, 0xcf, 0xdf, 0x51, 0xc3, 0xd4, 0xad, 0xe1, + 0xa0, 0xff, 0x54, 0xe8, 0x80, 0xea, 0xdd, 0x9e, 0x85, 0x44, 0x55, 0xe1, 0xc1, 0xbe, 0xa7, 0xf7, + 0x75, 0x53, 0xef, 0xa9, 0x45, 0x9e, 0x3d, 0x74, 0x4a, 0x87, 0x54, 0x2d, 0x69, 0xff, 0x53, 0x80, + 0x15, 0x94, 0xe7, 0xd0, 0x8f, 0xe2, 0x89, 0xfb, 0x92, 0xec, 0x41, 0x43, 0x98, 0xdd, 0xa9, 0x2c, + 0xe8, 0xb9, 0x33, 0x68, 0x8b, 0x7b, 0x96, 0x68, 0x31, 0x90, 0x75, 0xb4, 0x9b, 0x3e, 0x27, 0x21, + 0x45, 0x41, 0xa7, 0xbf, 0x22, 0xa4, 0xbc, 0x05, 0x95, 0x67, 0x6c, 0xe2, 0x87, 0x22, 0x04, 0xd6, + 0x76, 0x4a, 0x71, 0x38, 0x67, 0x54, 0xd2, 0xda, 0x36, 0xc0, 0xf9, 0xfa, 0xe4, 0x01, 0xac, 0x26, + 0xc6, 0x66, 0xa1, 0x71, 0x89, 0x93, 0x5b, 0x49, 0x88, 0x83, 0x5c, 0x75, 0xa3, 0x5c, 0xab, 0xba, + 0xd1, 0xbe, 0x86, 0xd5, 0x64, 0x3f, 0xe2, 0xfc, 0x54, 0x21, 0x79, 0x01, 0x63, 0xca, 0x82, 0x8c, + 0xca, 0x45, 0x19, 0xb5, 0x9f, 0x41, 0x6d, 0xe4, 0xd9, 0x41, 0x74, 0xe2, 0xc7, 0xdc, 0x7a, 0xe2, + 0x48, 0xfa, 0xaa, 0x12, 0x47, 0x9a, 0x06, 0x15, 0x7e, 0x38, 0xf3, 0x88, 0xbb, 0xbf, 0x31, 0xe8, + 0xee, 0x99, 0xc6, 0xb7, 0xba, 0xfa, 0x06, 0x01, 0xa8, 0xc8, 0xe7, 0x82, 0xa6, 0x41, 0xd3, 0x90, + 0xed, 0xd8, 0x63, 0x66, 0x3b, 0x2c, 0xe4, 0x12, 0xfc, 0xe0, 0x47, 0x89, 0x04, 0x3f, 0xf8, 0x91, + 0xf6, 0x17, 0x05, 0x68, 0x98, 0xa1, 0xed, 0x45, 0xb6, 0x30, 0xf7, 0xcf, 0xa0, 0x72, 0x82, 0x58, + 0x74, 0xa3, 0xc6, 0x82, 0x7f, 0x66, 0x17, 0xa3, 0x12, 0x48, 0xee, 0x40, 0xe5, 0xc4, 0xf6, 0x9c, + 0xa9, 0xd0, 0x5a, 0x85, 0xca, 0x51, 0x92, 0x1b, 0x95, 0xf3, 0xdc, 0xb8, 0x05, 0x2b, 0x33, 0x3b, + 0x7c, 0x6e, 0x8d, 0x4f, 0x6c, 0xef, 0x98, 0x45, 0xf2, 0x60, 0xa4, 0x05, 0x36, 0x38, 0x6b, 0x4f, + 0x70, 0xb4, 0xbf, 0x5f, 0x81, 0xf2, 0x37, 0x73, 0x16, 0x9e, 0x65, 0x04, 0xfa, 0xe0, 0xba, 0x02, + 0xc9, 0x17, 0x17, 0x2e, 0x4b, 0xca, 0x6f, 0x2f, 0x26, 0x65, 0x22, 0x53, 0x84, 0xc8, 0x95, 0x22, + 0x0b, 0x7c, 0x9a, 0x09, 0x63, 0xeb, 0x57, 0xd8, 0xda, 0x79, 0x70, 0x7b, 0x08, 0x95, 0x89, 0x3b, + 0x8d, 0x51, 0x75, 0x8b, 0xd5, 0x08, 0xee, 0xa5, 0xf3, 0x08, 0xd9, 0x54, 0xc2, 0xc8, 0xbb, 0xb0, + 0x22, 0x2a, 0x59, 0xeb, 0x07, 0xce, 0xc6, 0x82, 0x95, 0xf7, 0xa6, 0x48, 0x13, 0xbb, 0xff, 0x18, + 0xca, 0x7e, 0xc8, 0x37, 0x5f, 0xc7, 0x25, 0xef, 0x5c, 0x58, 0x72, 0xc8, 0xb9, 0x54, 0x80, 0xc8, + 0x87, 0x50, 0x3a, 0x71, 0xbd, 0x18, 0xb3, 0x46, 0x73, 0xfb, 0xf6, 0x05, 0xf0, 0x63, 0xd7, 0x8b, + 0x29, 0x42, 0x78, 0x98, 0x1f, 0xfb, 0x73, 0x2f, 0x6e, 0xdd, 0xc5, 0x0c, 0x23, 0x06, 0xe4, 0x1e, + 0x54, 0xfc, 0xc9, 0x24, 0x62, 0x31, 0x76, 0x96, 0xe5, 0x9d, 0xc2, 0xa7, 0x54, 0x12, 0xf8, 0x84, + 0xa9, 0x3b, 0x73, 0x63, 0xec, 0x43, 0xca, 0x54, 0x0c, 0xc8, 0x2e, 0xac, 0x8d, 0xfd, 0x59, 0xe0, + 0x4e, 0x99, 0x63, 0x8d, 0xe7, 0x61, 0xe4, 0x87, 0xad, 0x77, 0x2e, 0x1c, 0xd3, 0x9e, 0x44, 0xec, + 0x21, 0x80, 0x36, 0xc7, 0xb9, 0x31, 0x31, 0x60, 0x83, 0x79, 0x8e, 0xb5, 0xb8, 0xce, 0xfd, 0xd7, + 0xad, 0xb3, 0xce, 0x3c, 0x27, 0x4f, 0x4a, 0xc4, 0xc1, 0x48, 0x68, 0x61, 0xcc, 0x68, 0x6d, 0x60, + 0x90, 0xb9, 0x77, 0x69, 0xac, 0x14, 0xe2, 0x64, 0xc2, 0xf7, 0x6f, 0xc0, 0x2d, 0x19, 0x22, 0xad, + 0x80, 0x85, 0x13, 0x36, 0x8e, 0xad, 0x60, 0x6a, 0x7b, 0x58, 0xca, 0xa5, 0xc6, 0x4a, 0x24, 0xe4, + 0x50, 0x20, 0x0e, 0xa7, 0xb6, 0x47, 0x34, 0xa8, 0x3f, 0x67, 0x67, 0x91, 0xc5, 0x23, 0x29, 0x76, + 0xae, 0x29, 0xba, 0xc6, 0xe9, 0x43, 0x6f, 0x7a, 0x46, 0x7e, 0x02, 0x8d, 0xf8, 0xdc, 0xdb, 0xb0, + 0x61, 0x6d, 0xe4, 0x4e, 0x35, 0xe3, 0x8b, 0x34, 0x0b, 0x25, 0xf7, 0xa1, 0x2a, 0x35, 0xd4, 0xba, + 0x97, 0x5d, 0x3b, 0xa1, 0xf2, 0xc4, 0x3c, 0xb1, 0xdd, 0xa9, 0x7f, 0xca, 0x42, 0x6b, 0x16, 0xb5, + 0xda, 0xe2, 0xb6, 0x24, 0x21, 0x1d, 0x44, 0xdc, 0x4f, 0xa3, 0x38, 0xf4, 0xbd, 0xe3, 0xd6, 0x26, + 0xde, 0x93, 0xc8, 0xd1, 0xc5, 0xe0, 0xf7, 0x2e, 0x66, 0xfe, 0x7c, 0xf0, 0xfb, 0x1c, 0xee, 0x60, + 0x65, 0x66, 0x3d, 0x3b, 0xb3, 0xf2, 0x68, 0x0d, 0xd1, 0x1b, 0xc8, 0xdd, 0x3d, 0x3b, 0xcc, 0x4e, + 0x6a, 0x43, 0xcd, 0x71, 0xa3, 0xd8, 0xf5, 0xc6, 0x71, 0xab, 0x85, 0xef, 0x4c, 0xc7, 0xe4, 0x33, + 0xb8, 0x3d, 0x73, 0x3d, 0x2b, 0xb2, 0x27, 0xcc, 0x8a, 0x5d, 0xee, 0x9b, 0x6c, 0xec, 0x7b, 0x4e, + 0xd4, 0x7a, 0x80, 0x82, 0x93, 0x99, 0xeb, 0x8d, 0xec, 0x09, 0x33, 0xdd, 0x19, 0x1b, 0x09, 0x0e, + 0xf9, 0x08, 0xd6, 0x11, 0x1e, 0xb2, 0x60, 0xea, 0x8e, 0x6d, 0xf1, 0xfa, 0x1f, 0xe1, 0xeb, 0xd7, + 0x38, 0x83, 0x0a, 0x3a, 0xbe, 0xfa, 0x63, 0x68, 0x06, 0x2c, 0x8c, 0xdc, 0x28, 0xb6, 0xa4, 0x45, + 0xbf, 0x97, 0xd5, 0xda, 0xaa, 0x64, 0x0e, 0x91, 0xd7, 0xfe, 0xcf, 0x02, 0x54, 0x84, 0x73, 0x92, + 0x4f, 0x41, 0xf1, 0x03, 0xbc, 0x06, 0x69, 0x6e, 0x6f, 0x5e, 0xe2, 0xc1, 0x9d, 0x61, 0xc0, 0xeb, + 0x5e, 0x3f, 0xa4, 0x8a, 0x1f, 0xdc, 0xb8, 0x28, 0xd4, 0xfe, 0x10, 0x6a, 0xc9, 0x02, 0xbc, 0xbc, + 0xe8, 0xeb, 0xa3, 0x91, 0x65, 0x3e, 0xee, 0x0e, 0xd4, 0x02, 0xb9, 0x03, 0x24, 0x1d, 0x5a, 0x43, + 0x6a, 0xe9, 0xdf, 0x1c, 0x75, 0xfb, 0xaa, 0x82, 0x5d, 0x1a, 0xd5, 0xbb, 0xa6, 0x4e, 0x05, 0xb2, + 0x48, 0xee, 0xc1, 0xed, 0x2c, 0xe5, 0x1c, 0x5c, 0xc2, 0x14, 0x8c, 0x8f, 0x65, 0x52, 0x01, 0xc5, + 0x18, 0xa8, 0x15, 0x9e, 0x16, 0xf4, 0xef, 0x8d, 0x91, 0x39, 0x52, 0xab, 0xed, 0xbf, 0x29, 0x40, + 0x19, 0xc3, 0x06, 0x3f, 0x9f, 0x54, 0x72, 0x71, 0x5d, 0x73, 0x5e, 0xb9, 0x1a, 0xd9, 0x92, 0xaa, + 0x81, 0x01, 0x65, 0x73, 0x79, 0xf4, 0xf9, 0xb5, 0xd6, 0x53, 0x3f, 0x85, 0x12, 0x8f, 0x52, 0xbc, + 0x43, 0x1c, 0xd2, 0x9e, 0x4e, 0xad, 0x47, 0x06, 0x1d, 0xf1, 0x2a, 0x97, 0x40, 0xb3, 0x3b, 0xd8, + 0xd3, 0x47, 0xe6, 0x30, 0xa1, 0xa1, 0x56, 0x1e, 0x19, 0x7d, 0x33, 0x45, 0x15, 0xb5, 0x9f, 0xd7, + 0x60, 0x35, 0x89, 0x09, 0x22, 0x82, 0x3e, 0x82, 0x46, 0x10, 0xba, 0x33, 0x3b, 0x3c, 0x8b, 0xc6, + 0xb6, 0x87, 0x49, 0x01, 0xb6, 0x7f, 0xb4, 0x24, 0xaa, 0x88, 0x1d, 0x1d, 0x0a, 0xec, 0x68, 0x6c, + 0x7b, 0x34, 0x3b, 0x91, 0xf4, 0x61, 0x75, 0xc6, 0xc2, 0x63, 0xf6, 0x7b, 0xbe, 0xeb, 0xe1, 0x4a, + 0x55, 0x8c, 0xc8, 0xef, 0x5f, 0xba, 0xd2, 0x01, 0x47, 0xff, 0x8e, 0xef, 0x7a, 0xb8, 0x56, 0x7e, + 0x32, 0xf9, 0x04, 0xea, 0xa2, 0x12, 0x72, 0xd8, 0x04, 0x63, 0xc5, 0xb2, 0xda, 0x4f, 0xd4, 0xe8, + 0x3d, 0x36, 0xc9, 0xc4, 0x65, 0xb8, 0x34, 0x2e, 0x37, 0xb2, 0x71, 0xf9, 0xcd, 0x6c, 0x2c, 0x5a, + 0x11, 0x55, 0x78, 0x1a, 0x84, 0x2e, 0x38, 0x7c, 0x6b, 0x89, 0xc3, 0x77, 0x60, 0x23, 0xf1, 0x55, + 0xcb, 0xf5, 0x26, 0xee, 0x4b, 0x2b, 0x72, 0x5f, 0x89, 0xd8, 0x53, 0xa6, 0xeb, 0x09, 0xcb, 0xe0, + 0x9c, 0x91, 0xfb, 0x8a, 0x11, 0x23, 0xe9, 0xe0, 0x64, 0x0e, 0x5c, 0xc5, 0xab, 0xc9, 0xf7, 0x2e, + 0x55, 0x8f, 0x68, 0xbe, 0x64, 0x46, 0xcc, 0x4d, 0x6d, 0xff, 0x52, 0x81, 0x46, 0xe6, 0x1c, 0x78, + 0xf6, 0x16, 0xca, 0x42, 0x61, 0xc5, 0x55, 0x94, 0x50, 0x1f, 0x4a, 0xfa, 0x26, 0xd4, 0xa3, 0xd8, + 0x0e, 0x63, 0x8b, 0x17, 0x57, 0xb2, 0xdd, 0x45, 0xc2, 0x13, 0x76, 0x46, 0x3e, 0x80, 0x35, 0xc1, + 0x74, 0xbd, 0xf1, 0x74, 0x1e, 0xb9, 0xa7, 0xa2, 0x99, 0xaf, 0xd1, 0x26, 0x92, 0x8d, 0x84, 0x4a, + 0xee, 0x42, 0x95, 0x67, 0x21, 0xbe, 0x86, 0x68, 0xfa, 0x2a, 0xcc, 0x73, 0xf8, 0x0a, 0x0f, 0x60, + 0x95, 0x33, 0xce, 0xe7, 0x57, 0xc4, 0x2d, 0x33, 0xf3, 0x9c, 0xf3, 0xd9, 0x1d, 0xd8, 0x10, 0xaf, + 0x09, 0x44, 0xf1, 0x2a, 0x2b, 0xdc, 0x3b, 0xa8, 0xd8, 0x75, 0x64, 0xc9, 0xb2, 0x56, 0x14, 0x9c, + 0x1f, 0x01, 0xcf, 0x5e, 0x0b, 0xe8, 0xbb, 0x22, 0x94, 0x31, 0xcf, 0xc9, 0x61, 0x77, 0xe1, 0x1d, + 0x8e, 0x9d, 0x7b, 0x76, 0x10, 0x4c, 0x5d, 0xe6, 0x58, 0x53, 0xff, 0x18, 0x43, 0x66, 0x14, 0xdb, + 0xb3, 0xc0, 0x9a, 0x47, 0xad, 0x0d, 0x0c, 0x99, 0x6d, 0xe6, 0x39, 0x47, 0x09, 0xa8, 0xef, 0x1f, + 0x9b, 0x09, 0xe4, 0x28, 0x6a, 0xff, 0x3e, 0xac, 0xe6, 0xec, 0x71, 0x41, 0xa7, 0x35, 0x74, 0xfe, + 0x8c, 0x4e, 0xdf, 0x85, 0x95, 0x20, 0x64, 0xe7, 0xa2, 0xd5, 0x51, 0xb4, 0x86, 0xa0, 0x09, 0xb1, + 0xb6, 0x60, 0x05, 0x79, 0x96, 0x20, 0xe6, 0xf3, 0x63, 0x03, 0x59, 0x87, 0xc8, 0x69, 0xbf, 0x80, + 0x95, 0xec, 0x69, 0x93, 0x77, 0x33, 0x69, 0xa1, 0x99, 0xcb, 0x93, 0x69, 0x76, 0x48, 0x2a, 0xb2, + 0xf5, 0x4b, 0x2a, 0x32, 0x72, 0x9d, 0x8a, 0x4c, 0xfb, 0x2f, 0xd9, 0x9c, 0x65, 0x2a, 0x84, 0x9f, + 0x41, 0x2d, 0x90, 0xf5, 0x38, 0x5a, 0x52, 0xfe, 0x12, 0x3e, 0x0f, 0xee, 0x24, 0x95, 0x3b, 0x4d, + 0xe7, 0xb4, 0xff, 0x56, 0x81, 0x5a, 0x5a, 0xd0, 0xe7, 0x2c, 0xef, 0xcd, 0x05, 0xcb, 0x3b, 0x90, + 0x1a, 0x16, 0x0a, 0x7c, 0x1b, 0xa3, 0xc5, 0x27, 0xaf, 0x7f, 0xd7, 0xc5, 0xb6, 0xe7, 0x34, 0xdb, + 0xf6, 0x6c, 0xbe, 0xae, 0xed, 0xf9, 0xe4, 0xa2, 0xc1, 0xbf, 0x95, 0xe9, 0x2d, 0x16, 0xcc, 0xbe, + 0xfd, 0x7d, 0xae, 0x0f, 0xca, 0x26, 0x84, 0x77, 0xc4, 0x7e, 0xd2, 0x84, 0x90, 0xb6, 0x3f, 0xf7, + 0xaf, 0xd7, 0xfe, 0x6c, 0x43, 0x45, 0xea, 0xfc, 0x0e, 0x54, 0x64, 0x4d, 0x27, 0x1b, 0x04, 0x31, + 0x3a, 0x6f, 0x10, 0x0a, 0xb2, 0x4e, 0xd7, 0x7e, 0xae, 0x40, 0x59, 0x0f, 0x43, 0x3f, 0xd4, 0xfe, + 0x48, 0x81, 0x3a, 0x3e, 0xed, 0xf9, 0x0e, 0xe3, 0xd9, 0x60, 0xb7, 0xdb, 0xb3, 0xa8, 0xfe, 0xcd, + 0x91, 0x8e, 0xd9, 0xa0, 0x0d, 0x77, 0xf6, 0x86, 0x83, 0xbd, 0x23, 0x4a, 0xf5, 0x81, 0x69, 0x99, + 0xb4, 0x3b, 0x18, 0xf1, 0xb6, 0x67, 0x38, 0x50, 0x15, 0x9e, 0x29, 0x8c, 0x81, 0xa9, 0xd3, 0x41, + 0xb7, 0x6f, 0x89, 0x56, 0xb4, 0x88, 0x77, 0xb3, 0xba, 0xde, 0xb3, 0xf0, 0xd6, 0x51, 0x2d, 0xf1, + 0x96, 0xd5, 0x34, 0x0e, 0xf4, 0xe1, 0x91, 0xa9, 0x96, 0xc9, 0x6d, 0x58, 0x3f, 0xd4, 0xe9, 0x81, + 0x31, 0x1a, 0x19, 0xc3, 0x81, 0xd5, 0xd3, 0x07, 0x86, 0xde, 0x53, 0x2b, 0x7c, 0x9d, 0x5d, 0x63, + 0xdf, 0xec, 0xee, 0xf6, 0x75, 0xb9, 0x4e, 0x95, 0x6c, 0xc2, 0x5b, 0x7b, 0xc3, 0x83, 0x03, 0xc3, + 0x34, 0xf5, 0x9e, 0xb5, 0x7b, 0x64, 0x5a, 0x23, 0xd3, 0xe8, 0xf7, 0xad, 0xee, 0xe1, 0x61, 0xff, + 0x29, 0x4f, 0x60, 0x35, 0x72, 0x17, 0x36, 0xf6, 0xba, 0x87, 0xdd, 0x5d, 0xa3, 0x6f, 0x98, 0x4f, + 0xad, 0x9e, 0x31, 0xe2, 0xf3, 0x7b, 0x6a, 0x9d, 0x27, 0x6c, 0x93, 0x3e, 0xb5, 0xba, 0x7d, 0x14, + 0xcd, 0xd4, 0xad, 0xdd, 0xee, 0xde, 0x13, 0x7d, 0xd0, 0x53, 0x81, 0x0b, 0x30, 0xea, 0x3e, 0xd2, + 0x2d, 0x2e, 0x92, 0x65, 0x0e, 0x87, 0xd6, 0xb0, 0xdf, 0x53, 0x1b, 0xda, 0xbf, 0x14, 0xa1, 0xb4, + 0xe7, 0x47, 0x31, 0xf7, 0x46, 0xe1, 0xac, 0x2f, 0x42, 0x37, 0x66, 0xa2, 0x7f, 0x2b, 0x53, 0xd1, + 0x4b, 0x7f, 0x87, 0x24, 0x1e, 0x50, 0x32, 0x10, 0xeb, 0xd9, 0x19, 0xc7, 0x29, 0x88, 0x5b, 0x3b, + 0xc7, 0xed, 0x72, 0xb2, 0x88, 0x68, 0x78, 0x85, 0x23, 0xd7, 0x2b, 0x22, 0x4e, 0x06, 0x61, 0xb9, + 0xe0, 0xc7, 0x40, 0xb2, 0x20, 0xb9, 0x62, 0x09, 0x91, 0x6a, 0x06, 0x29, 0x96, 0xdc, 0x01, 0x18, + 0xfb, 0xb3, 0x99, 0x1b, 0x8f, 0xfd, 0x28, 0x96, 0x5f, 0xc8, 0xda, 0x39, 0x63, 0x8f, 0x62, 0x6e, + 0xf1, 0x33, 0x37, 0xe6, 0x8f, 0x34, 0x83, 0x26, 0x3b, 0x70, 0xcf, 0x0e, 0x82, 0xd0, 0x7f, 0xe9, + 0xce, 0xec, 0x98, 0x59, 0xdc, 0x73, 0xed, 0x63, 0x66, 0x39, 0x6c, 0x1a, 0xdb, 0xd8, 0x13, 0x95, + 0xe9, 0xdd, 0x0c, 0x60, 0x24, 0xf8, 0x3d, 0xce, 0xe6, 0x71, 0xd7, 0x75, 0xac, 0x88, 0xfd, 0x30, + 0xe7, 0x1e, 0x60, 0xcd, 0x03, 0xc7, 0xe6, 0x62, 0xd6, 0x45, 0x96, 0x72, 0x9d, 0x91, 0xe4, 0x1c, + 0x09, 0x46, 0xfb, 0x15, 0xc0, 0xb9, 0x14, 0x64, 0x1b, 0x6e, 0xf3, 0x3a, 0x9e, 0x45, 0x31, 0x73, + 0x2c, 0xb9, 0xdb, 0x60, 0x1e, 0x47, 0x18, 0xe2, 0xcb, 0x74, 0x23, 0x65, 0xca, 0x9b, 0xc2, 0x79, + 0x1c, 0x91, 0x9f, 0x40, 0xeb, 0xc2, 0x1c, 0x87, 0x4d, 0x19, 0x7f, 0x6d, 0x15, 0xa7, 0xdd, 0x59, + 0x98, 0xd6, 0x13, 0x5c, 0xed, 0x4f, 0x14, 0x80, 0x7d, 0x16, 0x53, 0xc1, 0xcd, 0x34, 0xb6, 0x95, + 0xeb, 0x36, 0xb6, 0xef, 0x27, 0x17, 0x08, 0xc5, 0xab, 0x63, 0xc0, 0x42, 0x97, 0xa1, 0xdc, 0xa4, + 0xcb, 0xc8, 0x35, 0x11, 0xc5, 0x2b, 0x9a, 0x88, 0x52, 0xae, 0x89, 0xf8, 0x18, 0x9a, 0xf6, 0x74, + 0xea, 0xbf, 0xe0, 0x05, 0x0d, 0x0b, 0x43, 0xe6, 0xa0, 0x11, 0x9c, 0xd7, 0xdb, 0xc8, 0xec, 0x49, + 0x9e, 0xf6, 0xe7, 0x0a, 0x34, 0x50, 0x15, 0x51, 0xe0, 0x7b, 0x11, 0x23, 0x5f, 0x42, 0x45, 0x5e, + 0x44, 0x8b, 0x8b, 0xfc, 0xb7, 0x33, 0xb2, 0x66, 0x70, 0xb2, 0x68, 0xa0, 0x12, 0xcc, 0x33, 0x42, + 0xe6, 0x75, 0x97, 0x2b, 0x25, 0x45, 0x91, 0xfb, 0x50, 0x73, 0x3d, 0x4b, 0xb4, 0xd4, 0x95, 0x4c, + 0x58, 0xac, 0xba, 0x1e, 0xd6, 0xb2, 0xed, 0x57, 0x50, 0x11, 0x2f, 0x21, 0x9d, 0x54, 0xa6, 0x8b, + 0xfa, 0xcb, 0xdc, 0x1c, 0xa7, 0xc2, 0xc8, 0xc3, 0x29, 0xbd, 0x2e, 0x40, 0xb7, 0xa0, 0x7a, 0xca, + 0x9b, 0x0f, 0xbc, 0xf4, 0xe3, 0xea, 0x4d, 0x86, 0xda, 0x1f, 0x97, 0x00, 0x0e, 0xe7, 0x4b, 0x0c, + 0xa4, 0x71, 0x5d, 0x03, 0xe9, 0xe4, 0xf4, 0xf8, 0x7a, 0x99, 0x7f, 0x75, 0x43, 0x59, 0xd2, 0x69, + 0x17, 0x6f, 0xda, 0x69, 0xdf, 0x87, 0x6a, 0x1c, 0xce, 0xb9, 0xa3, 0x08, 0x63, 0x4a, 0x5b, 0x5a, + 0x49, 0x25, 0x6f, 0x42, 0x79, 0xe2, 0x87, 0x63, 0x86, 0x8e, 0x95, 0xb2, 0x05, 0xed, 0xc2, 0x65, + 0x52, 0xed, 0xb2, 0xcb, 0x24, 0xde, 0xa0, 0x45, 0xf2, 0x1e, 0x0d, 0x0b, 0x99, 0x7c, 0x83, 0x96, + 0x5c, 0xb1, 0xd1, 0x14, 0x44, 0xbe, 0x81, 0xa6, 0x3d, 0x8f, 0x7d, 0xcb, 0xe5, 0x15, 0xda, 0xd4, + 0x1d, 0x9f, 0x61, 0xd9, 0xdd, 0xcc, 0x7f, 0xaf, 0x4f, 0x0f, 0xaa, 0xd3, 0x9d, 0xc7, 0xbe, 0xe1, + 0x1c, 0x22, 0x72, 0xa7, 0x2a, 0x93, 0x12, 0x5d, 0xb1, 0x33, 0x64, 0xed, 0xc7, 0xb0, 0x92, 0x85, + 0xf1, 0x04, 0x24, 0x81, 0xea, 0x1b, 0x3c, 0x3b, 0x8d, 0x78, 0x6a, 0x1b, 0x98, 0x46, 0xb7, 0xaf, + 0x16, 0xb4, 0x18, 0x1a, 0xb8, 0xbc, 0xf4, 0x8e, 0xeb, 0xba, 0xfd, 0x03, 0x28, 0x61, 0xf8, 0x55, + 0x2e, 0x7c, 0x0f, 0xc1, 0x98, 0x8b, 0xcc, 0xbc, 0xf9, 0x15, 0xb3, 0xe6, 0xf7, 0xdf, 0x05, 0x58, + 0x31, 0xfd, 0xf9, 0xf8, 0xe4, 0xa2, 0x01, 0xc2, 0xaf, 0x3b, 0x42, 0x2d, 0x31, 0x1f, 0xe5, 0xa6, + 0xe6, 0x93, 0x5a, 0x47, 0x71, 0x89, 0x75, 0xdc, 0xf4, 0xcc, 0xb5, 0x2f, 0x60, 0x55, 0x6e, 0x5e, + 0x6a, 0x3d, 0xd1, 0x66, 0xe1, 0x0a, 0x6d, 0x6a, 0xbf, 0x50, 0x60, 0x55, 0xc4, 0xf7, 0xff, 0xbb, + 0xd2, 0x2a, 0x37, 0x0c, 0xeb, 0xe5, 0x1b, 0x5d, 0x1e, 0xfd, 0xbf, 0xf4, 0x34, 0x6d, 0x08, 0xcd, + 0x44, 0x7d, 0x37, 0x50, 0xfb, 0x15, 0x46, 0xfc, 0x8b, 0x02, 0x34, 0x06, 0xec, 0xe5, 0x92, 0x20, + 0x5a, 0xbe, 0xee, 0x71, 0x7c, 0x98, 0x2b, 0x57, 0x1b, 0xdb, 0xeb, 0x59, 0x19, 0xc4, 0xd5, 0x63, + 0x52, 0xc1, 0xa6, 0xb7, 0xa8, 0xca, 0xf2, 0x5b, 0xd4, 0xd2, 0x62, 0xb7, 0x9e, 0xb9, 0xc5, 0x2b, + 0x2e, 0xbb, 0xc5, 0xd3, 0xfe, 0xad, 0x08, 0x0d, 0x6c, 0x90, 0x29, 0x8b, 0xe6, 0xd3, 0x38, 0x27, + 0x4c, 0xe1, 0x6a, 0x61, 0x3a, 0x50, 0x09, 0x71, 0x92, 0x74, 0xa5, 0x4b, 0x83, 0xbf, 0x40, 0x61, + 0x6b, 0xfc, 0xdc, 0x0d, 0x02, 0xe6, 0x58, 0x82, 0x92, 0x14, 0x30, 0x4d, 0x49, 0x16, 0x22, 0x44, + 0xbc, 0xfc, 0x9c, 0xf9, 0x21, 0x4b, 0x51, 0x45, 0xbc, 0x4f, 0x68, 0x70, 0x5a, 0x02, 0xc9, 0xdd, + 0x37, 0x88, 0xca, 0xe0, 0xfc, 0xbe, 0x21, 0xed, 0x35, 0x91, 0x5b, 0x47, 0xae, 0xe8, 0x35, 0x91, + 0xcd, 0xbb, 0xa8, 0x99, 0x3d, 0x9d, 0x5a, 0x7e, 0x10, 0xa1, 0xd3, 0xd4, 0x68, 0x0d, 0x09, 0xc3, + 0x20, 0x22, 0x5f, 0x43, 0x7a, 0x5d, 0x2c, 0x6f, 0xc9, 0xc5, 0x39, 0xb6, 0x2e, 0xbb, 0x58, 0xa0, + 0xab, 0xe3, 0xdc, 0xfd, 0xcf, 0x92, 0x1b, 0xea, 0xca, 0x4d, 0x6f, 0xa8, 0x1f, 0x42, 0x59, 0xc4, + 0xa8, 0xda, 0xeb, 0x62, 0x94, 0xc0, 0x65, 0xed, 0xb3, 0x91, 0xb7, 0xcf, 0x5f, 0x16, 0x80, 0x74, + 0xa7, 0x53, 0x7f, 0x6c, 0xc7, 0xcc, 0x70, 0xa2, 0x8b, 0x66, 0x7a, 0xed, 0xcf, 0x2e, 0x9f, 0x41, + 0x7d, 0xe6, 0x3b, 0x6c, 0x6a, 0x25, 0xdf, 0x94, 0x2e, 0xad, 0x7e, 0x10, 0xc6, 0x5b, 0x52, 0x02, + 0x25, 0xbc, 0xc4, 0x51, 0xb0, 0xee, 0xc0, 0x67, 0xde, 0x84, 0xcd, 0xec, 0x97, 0xb2, 0x14, 0xe1, + 0x8f, 0xa4, 0x03, 0xd5, 0x90, 0x45, 0x2c, 0x3c, 0x65, 0x57, 0x16, 0x55, 0x09, 0x48, 0x7b, 0x06, + 0x1b, 0xb9, 0x1d, 0x49, 0x47, 0xbe, 0x85, 0x5f, 0x2b, 0xc3, 0x58, 0x7e, 0xb4, 0x12, 0x03, 0xfe, + 0x3a, 0xe6, 0x25, 0x9f, 0x41, 0xf9, 0x63, 0xea, 0xf0, 0xc5, 0xab, 0xe2, 0xec, 0x1e, 0xa8, 0x59, + 0x4d, 0xbb, 0x63, 0x0c, 0x36, 0xf2, 0x54, 0x0a, 0xd7, 0x3b, 0x15, 0xed, 0xef, 0x0a, 0xb0, 0xde, + 0x75, 0x1c, 0xf1, 0x77, 0xc3, 0x25, 0xaa, 0x2f, 0x5e, 0x57, 0xf5, 0x0b, 0x81, 0x58, 0x84, 0x89, + 0x6b, 0x05, 0xe2, 0x0f, 0xa1, 0x92, 0xd6, 0x5a, 0xc5, 0x05, 0x77, 0x16, 0x72, 0x51, 0x09, 0xd0, + 0x6e, 0x01, 0xc9, 0x0a, 0x2b, 0xb4, 0xaa, 0xfd, 0x69, 0x11, 0xee, 0xee, 0xb2, 0x63, 0xd7, 0xcb, + 0xbe, 0xe2, 0x57, 0xdf, 0xc9, 0xc5, 0x4f, 0x65, 0x9f, 0xc1, 0xba, 0x28, 0xe4, 0x93, 0x7f, 0x62, + 0x59, 0xec, 0x58, 0x7e, 0x9d, 0x94, 0xb1, 0x6a, 0x0d, 0xf9, 0x07, 0x92, 0xad, 0xe3, 0x7f, 0xc5, + 0x1c, 0x3b, 0xb6, 0x9f, 0xd9, 0x11, 0xb3, 0x5c, 0x47, 0xfe, 0x59, 0x06, 0x12, 0x92, 0xe1, 0x90, + 0x21, 0x94, 0xb8, 0x0d, 0xa2, 0xeb, 0x36, 0xb7, 0xb7, 0x33, 0x62, 0x5d, 0xb2, 0x95, 0xac, 0x02, + 0x0f, 0x7c, 0x87, 0xed, 0x54, 0x8f, 0x06, 0x4f, 0x06, 0xc3, 0xef, 0x06, 0x14, 0x17, 0x22, 0x06, + 0xdc, 0x0a, 0x42, 0x76, 0xea, 0xfa, 0xf3, 0xc8, 0xca, 0x9e, 0x44, 0xf5, 0xca, 0x94, 0xb8, 0x91, + 0xcc, 0xc9, 0x10, 0xb5, 0x9f, 0xc2, 0xda, 0xc2, 0xcb, 0x78, 0x6d, 0x26, 0x5f, 0xa7, 0xbe, 0x41, + 0x56, 0xa1, 0x8e, 0x1f, 0xbb, 0x97, 0x7f, 0xfb, 0xd6, 0xfe, 0xb5, 0x80, 0x57, 0x4c, 0x33, 0x37, + 0xbe, 0x59, 0x06, 0xfb, 0xcd, 0x7c, 0x06, 0x83, 0xed, 0x77, 0xf3, 0xe6, 0x9b, 0x59, 0xb0, 0xf3, + 0xad, 0x00, 0xa6, 0x41, 0xa4, 0x6d, 0x43, 0x55, 0xd2, 0xc8, 0x6f, 0xc1, 0x5a, 0xe8, 0xfb, 0x71, + 0xd2, 0x89, 0x8a, 0x0e, 0xe4, 0xf2, 0x3f, 0xdb, 0xac, 0x72, 0xb0, 0x48, 0x06, 0x4f, 0xf2, 0xbd, + 0x48, 0x59, 0xfc, 0x0d, 0x44, 0x0e, 0x77, 0x1b, 0xbf, 0x5b, 0x4f, 0xff, 0xb7, 0xfb, 0xbf, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x35, 0x9f, 0x30, 0x98, 0xf2, 0x2b, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto new file mode 100755 index 0000000000..497b4d9a9a --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto @@ -0,0 +1,551 @@ +syntax = "proto2"; +option go_package = "datastore"; + +package appengine; + +message Action{} + +message PropertyValue { + optional int64 int64Value = 1; + optional bool booleanValue = 2; + optional string stringValue = 3; + optional double doubleValue = 4; + + optional group PointValue = 5 { + required double x = 6; + required double y = 7; + } + + optional group UserValue = 8 { + required string email = 9; + required string auth_domain = 10; + optional string nickname = 11; + optional string federated_identity = 21; + optional string federated_provider = 22; + } + + optional group ReferenceValue = 12 { + required string app = 13; + optional string name_space = 20; + repeated group PathElement = 14 { + required string type = 15; + optional int64 id = 16; + optional string name = 17; + } + } +} + +message Property { + enum Meaning { + NO_MEANING = 0; + BLOB = 14; + TEXT = 15; + BYTESTRING = 16; + + ATOM_CATEGORY = 1; + ATOM_LINK = 2; + ATOM_TITLE = 3; + ATOM_CONTENT = 4; + ATOM_SUMMARY = 5; + ATOM_AUTHOR = 6; + + GD_WHEN = 7; + GD_EMAIL = 8; + GEORSS_POINT = 9; + GD_IM = 10; + + GD_PHONENUMBER = 11; + GD_POSTALADDRESS = 12; + + GD_RATING = 13; + + BLOBKEY = 17; + ENTITY_PROTO = 19; + + INDEX_VALUE = 18; + }; + + optional Meaning meaning = 1 [default = NO_MEANING]; + optional string meaning_uri = 2; + + required string name = 3; + + required PropertyValue value = 5; + + required bool multiple = 4; + + optional bool searchable = 6 [default=false]; + + enum FtsTokenizationOption { + HTML = 1; + ATOM = 2; + } + + optional FtsTokenizationOption fts_tokenization_option = 8; + + optional string locale = 9 [default = "en"]; +} + +message Path { + repeated group Element = 1 { + required string type = 2; + optional int64 id = 3; + optional string name = 4; + } +} + +message Reference { + required string app = 13; + optional string name_space = 20; + required Path path = 14; +} + +message User { + required string email = 1; + required string auth_domain = 2; + optional string nickname = 3; + optional string federated_identity = 6; + optional string federated_provider = 7; +} + +message EntityProto { + required Reference key = 13; + required Path entity_group = 16; + optional User owner = 17; + + enum Kind { + GD_CONTACT = 1; + GD_EVENT = 2; + GD_MESSAGE = 3; + } + optional Kind kind = 4; + optional string kind_uri = 5; + + repeated Property property = 14; + repeated Property raw_property = 15; + + optional int32 rank = 18; +} + +message CompositeProperty { + required int64 index_id = 1; + repeated string value = 2; +} + +message Index { + required string entity_type = 1; + required bool ancestor = 5; + repeated group Property = 2 { + required string name = 3; + enum Direction { + ASCENDING = 1; + DESCENDING = 2; + } + optional Direction direction = 4 [default = ASCENDING]; + } +} + +message CompositeIndex { + required string app_id = 1; + required int64 id = 2; + required Index definition = 3; + + enum State { + WRITE_ONLY = 1; + READ_WRITE = 2; + DELETED = 3; + ERROR = 4; + } + required State state = 4; + + optional bool only_use_if_required = 6 [default = false]; +} + +message IndexPostfix { + message IndexValue { + required string property_name = 1; + required PropertyValue value = 2; + } + + repeated IndexValue index_value = 1; + + optional Reference key = 2; + + optional bool before = 3 [default=true]; +} + +message IndexPosition { + optional string key = 1; + + optional bool before = 2 [default=true]; +} + +message Snapshot { + enum Status { + INACTIVE = 0; + ACTIVE = 1; + } + + required int64 ts = 1; +} + +message InternalHeader { + optional string qos = 1; +} + +message Transaction { + optional InternalHeader header = 4; + required fixed64 handle = 1; + required string app = 2; + optional bool mark_changes = 3 [default = false]; +} + +message Query { + optional InternalHeader header = 39; + + required string app = 1; + optional string name_space = 29; + + optional string kind = 3; + optional Reference ancestor = 17; + + repeated group Filter = 4 { + enum Operator { + LESS_THAN = 1; + LESS_THAN_OR_EQUAL = 2; + GREATER_THAN = 3; + GREATER_THAN_OR_EQUAL = 4; + EQUAL = 5; + IN = 6; + EXISTS = 7; + } + + required Operator op = 6; + repeated Property property = 14; + } + + optional string search_query = 8; + + repeated group Order = 9 { + enum Direction { + ASCENDING = 1; + DESCENDING = 2; + } + + required string property = 10; + optional Direction direction = 11 [default = ASCENDING]; + } + + enum Hint { + ORDER_FIRST = 1; + ANCESTOR_FIRST = 2; + FILTER_FIRST = 3; + } + optional Hint hint = 18; + + optional int32 count = 23; + + optional int32 offset = 12 [default = 0]; + + optional int32 limit = 16; + + optional CompiledCursor compiled_cursor = 30; + optional CompiledCursor end_compiled_cursor = 31; + + repeated CompositeIndex composite_index = 19; + + optional bool require_perfect_plan = 20 [default = false]; + + optional bool keys_only = 21 [default = false]; + + optional Transaction transaction = 22; + + optional bool compile = 25 [default = false]; + + optional int64 failover_ms = 26; + + optional bool strong = 32; + + repeated string property_name = 33; + + repeated string group_by_property_name = 34; + + optional bool distinct = 24; + + optional int64 min_safe_time_seconds = 35; + + repeated string safe_replica_name = 36; + + optional bool persist_offset = 37 [default=false]; +} + +message CompiledQuery { + required group PrimaryScan = 1 { + optional string index_name = 2; + + optional string start_key = 3; + optional bool start_inclusive = 4; + optional string end_key = 5; + optional bool end_inclusive = 6; + + repeated string start_postfix_value = 22; + repeated string end_postfix_value = 23; + + optional int64 end_unapplied_log_timestamp_us = 19; + } + + repeated group MergeJoinScan = 7 { + required string index_name = 8; + + repeated string prefix_value = 9; + + optional bool value_prefix = 20 [default=false]; + } + + optional Index index_def = 21; + + optional int32 offset = 10 [default = 0]; + + optional int32 limit = 11; + + required bool keys_only = 12; + + repeated string property_name = 24; + + optional int32 distinct_infix_size = 25; + + optional group EntityFilter = 13 { + optional bool distinct = 14 [default=false]; + + optional string kind = 17; + optional Reference ancestor = 18; + } +} + +message CompiledCursor { + optional group Position = 2 { + optional string start_key = 27; + + repeated group IndexValue = 29 { + optional string property = 30; + required PropertyValue value = 31; + } + + optional Reference key = 32; + + optional bool start_inclusive = 28 [default=true]; + } +} + +message Cursor { + required fixed64 cursor = 1; + + optional string app = 2; +} + +message Error { + enum ErrorCode { + BAD_REQUEST = 1; + CONCURRENT_TRANSACTION = 2; + INTERNAL_ERROR = 3; + NEED_INDEX = 4; + TIMEOUT = 5; + PERMISSION_DENIED = 6; + BIGTABLE_ERROR = 7; + COMMITTED_BUT_STILL_APPLYING = 8; + CAPABILITY_DISABLED = 9; + TRY_ALTERNATE_BACKEND = 10; + SAFE_TIME_TOO_OLD = 11; + } +} + +message Cost { + optional int32 index_writes = 1; + optional int32 index_write_bytes = 2; + optional int32 entity_writes = 3; + optional int32 entity_write_bytes = 4; + optional group CommitCost = 5 { + optional int32 requested_entity_puts = 6; + optional int32 requested_entity_deletes = 7; + }; + optional int32 approximate_storage_delta = 8; + optional int32 id_sequence_updates = 9; +} + +message GetRequest { + optional InternalHeader header = 6; + + repeated Reference key = 1; + optional Transaction transaction = 2; + + optional int64 failover_ms = 3; + + optional bool strong = 4; + + optional bool allow_deferred = 5 [default=false]; +} + +message GetResponse { + repeated group Entity = 1 { + optional EntityProto entity = 2; + optional Reference key = 4; + + optional int64 version = 3; + } + + repeated Reference deferred = 5; + + optional bool in_order = 6 [default=true]; +} + +message PutRequest { + optional InternalHeader header = 11; + + repeated EntityProto entity = 1; + optional Transaction transaction = 2; + repeated CompositeIndex composite_index = 3; + + optional bool trusted = 4 [default = false]; + + optional bool force = 7 [default = false]; + + optional bool mark_changes = 8 [default = false]; + repeated Snapshot snapshot = 9; + + enum AutoIdPolicy { + CURRENT = 0; + SEQUENTIAL = 1; + } + optional AutoIdPolicy auto_id_policy = 10 [default = CURRENT]; +} + +message PutResponse { + repeated Reference key = 1; + optional Cost cost = 2; + repeated int64 version = 3; +} + +message TouchRequest { + optional InternalHeader header = 10; + + repeated Reference key = 1; + repeated CompositeIndex composite_index = 2; + optional bool force = 3 [default = false]; + repeated Snapshot snapshot = 9; +} + +message TouchResponse { + optional Cost cost = 1; +} + +message DeleteRequest { + optional InternalHeader header = 10; + + repeated Reference key = 6; + optional Transaction transaction = 5; + + optional bool trusted = 4 [default = false]; + + optional bool force = 7 [default = false]; + + optional bool mark_changes = 8 [default = false]; + repeated Snapshot snapshot = 9; +} + +message DeleteResponse { + optional Cost cost = 1; + repeated int64 version = 3; +} + +message NextRequest { + optional InternalHeader header = 5; + + required Cursor cursor = 1; + optional int32 count = 2; + + optional int32 offset = 4 [default = 0]; + + optional bool compile = 3 [default = false]; +} + +message QueryResult { + optional Cursor cursor = 1; + + repeated EntityProto result = 2; + + optional int32 skipped_results = 7; + + required bool more_results = 3; + + optional bool keys_only = 4; + + optional bool index_only = 9; + + optional bool small_ops = 10; + + optional CompiledQuery compiled_query = 5; + + optional CompiledCursor compiled_cursor = 6; + + repeated CompositeIndex index = 8; + + repeated int64 version = 11; +} + +message AllocateIdsRequest { + optional InternalHeader header = 4; + + optional Reference model_key = 1; + + optional int64 size = 2; + + optional int64 max = 3; + + repeated Reference reserve = 5; +} + +message AllocateIdsResponse { + required int64 start = 1; + required int64 end = 2; + optional Cost cost = 3; +} + +message CompositeIndices { + repeated CompositeIndex index = 1; +} + +message AddActionsRequest { + optional InternalHeader header = 3; + + required Transaction transaction = 1; + repeated Action action = 2; +} + +message AddActionsResponse { +} + +message BeginTransactionRequest { + optional InternalHeader header = 3; + + required string app = 1; + optional bool allow_multiple_eg = 2 [default = false]; + optional string database_id = 4; + + enum TransactionMode { + UNKNOWN = 0; + READ_ONLY = 1; + READ_WRITE = 2; + } + optional TransactionMode mode = 5 [default = UNKNOWN]; + + optional Transaction previous_transaction = 7; +} + +message CommitResponse { + optional Cost cost = 1; + + repeated group Version = 3 { + required Reference root_entity_key = 4; + required int64 version = 5; + } +} diff --git a/vendor/google.golang.org/appengine/internal/identity.go b/vendor/google.golang.org/appengine/internal/identity.go new file mode 100644 index 0000000000..d538701ab3 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/identity.go @@ -0,0 +1,14 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package internal + +import netcontext "golang.org/x/net/context" + +// These functions are implementations of the wrapper functions +// in ../appengine/identity.go. See that file for commentary. + +func AppID(c netcontext.Context) string { + return appID(FullyQualifiedAppID(c)) +} diff --git a/vendor/google.golang.org/appengine/internal/identity_classic.go b/vendor/google.golang.org/appengine/internal/identity_classic.go new file mode 100644 index 0000000000..b59603f132 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/identity_classic.go @@ -0,0 +1,57 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build appengine + +package internal + +import ( + "appengine" + + netcontext "golang.org/x/net/context" +) + +func DefaultVersionHostname(ctx netcontext.Context) string { + c := fromContext(ctx) + if c == nil { + panic(errNotAppEngineContext) + } + return appengine.DefaultVersionHostname(c) +} + +func Datacenter(_ netcontext.Context) string { return appengine.Datacenter() } +func ServerSoftware() string { return appengine.ServerSoftware() } +func InstanceID() string { return appengine.InstanceID() } +func IsDevAppServer() bool { return appengine.IsDevAppServer() } + +func RequestID(ctx netcontext.Context) string { + c := fromContext(ctx) + if c == nil { + panic(errNotAppEngineContext) + } + return appengine.RequestID(c) +} + +func ModuleName(ctx netcontext.Context) string { + c := fromContext(ctx) + if c == nil { + panic(errNotAppEngineContext) + } + return appengine.ModuleName(c) +} +func VersionID(ctx netcontext.Context) string { + c := fromContext(ctx) + if c == nil { + panic(errNotAppEngineContext) + } + return appengine.VersionID(c) +} + +func fullyQualifiedAppID(ctx netcontext.Context) string { + c := fromContext(ctx) + if c == nil { + panic(errNotAppEngineContext) + } + return c.FullyQualifiedAppID() +} diff --git a/vendor/google.golang.org/appengine/internal/identity_vm.go b/vendor/google.golang.org/appengine/internal/identity_vm.go new file mode 100644 index 0000000000..d5fa75be78 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/identity_vm.go @@ -0,0 +1,101 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package internal + +import ( + "net/http" + "os" + + netcontext "golang.org/x/net/context" +) + +// These functions are implementations of the wrapper functions +// in ../appengine/identity.go. See that file for commentary. + +const ( + hDefaultVersionHostname = "X-AppEngine-Default-Version-Hostname" + hRequestLogId = "X-AppEngine-Request-Log-Id" + hDatacenter = "X-AppEngine-Datacenter" +) + +func ctxHeaders(ctx netcontext.Context) http.Header { + c := fromContext(ctx) + if c == nil { + return nil + } + return c.Request().Header +} + +func DefaultVersionHostname(ctx netcontext.Context) string { + return ctxHeaders(ctx).Get(hDefaultVersionHostname) +} + +func RequestID(ctx netcontext.Context) string { + return ctxHeaders(ctx).Get(hRequestLogId) +} + +func Datacenter(ctx netcontext.Context) string { + return ctxHeaders(ctx).Get(hDatacenter) +} + +func ServerSoftware() string { + // TODO(dsymonds): Remove fallback when we've verified this. + if s := os.Getenv("SERVER_SOFTWARE"); s != "" { + return s + } + return "Google App Engine/1.x.x" +} + +// TODO(dsymonds): Remove the metadata fetches. + +func ModuleName(_ netcontext.Context) string { + if s := os.Getenv("GAE_MODULE_NAME"); s != "" { + return s + } + return string(mustGetMetadata("instance/attributes/gae_backend_name")) +} + +func VersionID(_ netcontext.Context) string { + if s1, s2 := os.Getenv("GAE_MODULE_VERSION"), os.Getenv("GAE_MINOR_VERSION"); s1 != "" && s2 != "" { + return s1 + "." + s2 + } + return string(mustGetMetadata("instance/attributes/gae_backend_version")) + "." + string(mustGetMetadata("instance/attributes/gae_backend_minor_version")) +} + +func InstanceID() string { + if s := os.Getenv("GAE_MODULE_INSTANCE"); s != "" { + return s + } + return string(mustGetMetadata("instance/attributes/gae_backend_instance")) +} + +func partitionlessAppID() string { + // gae_project has everything except the partition prefix. + appID := os.Getenv("GAE_LONG_APP_ID") + if appID == "" { + appID = string(mustGetMetadata("instance/attributes/gae_project")) + } + return appID +} + +func fullyQualifiedAppID(_ netcontext.Context) string { + appID := partitionlessAppID() + + part := os.Getenv("GAE_PARTITION") + if part == "" { + part = string(mustGetMetadata("instance/attributes/gae_partition")) + } + + if part != "" { + appID = part + "~" + appID + } + return appID +} + +func IsDevAppServer() bool { + return os.Getenv("RUN_WITH_DEVAPPSERVER") != "" +} diff --git a/vendor/google.golang.org/appengine/internal/image/images_service.pb.go b/vendor/google.golang.org/appengine/internal/image/images_service.pb.go new file mode 100644 index 0000000000..fd2f25d36b --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/image/images_service.pb.go @@ -0,0 +1,1001 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/image/images_service.proto + +/* +Package image is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/image/images_service.proto + +It has these top-level messages: + ImagesServiceError + ImagesServiceTransform + Transform + ImageData + InputSettings + OutputSettings + ImagesTransformRequest + ImagesTransformResponse + CompositeImageOptions + ImagesCanvas + ImagesCompositeRequest + ImagesCompositeResponse + ImagesHistogramRequest + ImagesHistogram + ImagesHistogramResponse + ImagesGetUrlBaseRequest + ImagesGetUrlBaseResponse + ImagesDeleteUrlBaseRequest + ImagesDeleteUrlBaseResponse +*/ +package image + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type ImagesServiceError_ErrorCode int32 + +const ( + ImagesServiceError_UNSPECIFIED_ERROR ImagesServiceError_ErrorCode = 1 + ImagesServiceError_BAD_TRANSFORM_DATA ImagesServiceError_ErrorCode = 2 + ImagesServiceError_NOT_IMAGE ImagesServiceError_ErrorCode = 3 + ImagesServiceError_BAD_IMAGE_DATA ImagesServiceError_ErrorCode = 4 + ImagesServiceError_IMAGE_TOO_LARGE ImagesServiceError_ErrorCode = 5 + ImagesServiceError_INVALID_BLOB_KEY ImagesServiceError_ErrorCode = 6 + ImagesServiceError_ACCESS_DENIED ImagesServiceError_ErrorCode = 7 + ImagesServiceError_OBJECT_NOT_FOUND ImagesServiceError_ErrorCode = 8 +) + +var ImagesServiceError_ErrorCode_name = map[int32]string{ + 1: "UNSPECIFIED_ERROR", + 2: "BAD_TRANSFORM_DATA", + 3: "NOT_IMAGE", + 4: "BAD_IMAGE_DATA", + 5: "IMAGE_TOO_LARGE", + 6: "INVALID_BLOB_KEY", + 7: "ACCESS_DENIED", + 8: "OBJECT_NOT_FOUND", +} +var ImagesServiceError_ErrorCode_value = map[string]int32{ + "UNSPECIFIED_ERROR": 1, + "BAD_TRANSFORM_DATA": 2, + "NOT_IMAGE": 3, + "BAD_IMAGE_DATA": 4, + "IMAGE_TOO_LARGE": 5, + "INVALID_BLOB_KEY": 6, + "ACCESS_DENIED": 7, + "OBJECT_NOT_FOUND": 8, +} + +func (x ImagesServiceError_ErrorCode) Enum() *ImagesServiceError_ErrorCode { + p := new(ImagesServiceError_ErrorCode) + *p = x + return p +} +func (x ImagesServiceError_ErrorCode) String() string { + return proto.EnumName(ImagesServiceError_ErrorCode_name, int32(x)) +} +func (x *ImagesServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ImagesServiceError_ErrorCode_value, data, "ImagesServiceError_ErrorCode") + if err != nil { + return err + } + *x = ImagesServiceError_ErrorCode(value) + return nil +} +func (ImagesServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type ImagesServiceTransform_Type int32 + +const ( + ImagesServiceTransform_RESIZE ImagesServiceTransform_Type = 1 + ImagesServiceTransform_ROTATE ImagesServiceTransform_Type = 2 + ImagesServiceTransform_HORIZONTAL_FLIP ImagesServiceTransform_Type = 3 + ImagesServiceTransform_VERTICAL_FLIP ImagesServiceTransform_Type = 4 + ImagesServiceTransform_CROP ImagesServiceTransform_Type = 5 + ImagesServiceTransform_IM_FEELING_LUCKY ImagesServiceTransform_Type = 6 +) + +var ImagesServiceTransform_Type_name = map[int32]string{ + 1: "RESIZE", + 2: "ROTATE", + 3: "HORIZONTAL_FLIP", + 4: "VERTICAL_FLIP", + 5: "CROP", + 6: "IM_FEELING_LUCKY", +} +var ImagesServiceTransform_Type_value = map[string]int32{ + "RESIZE": 1, + "ROTATE": 2, + "HORIZONTAL_FLIP": 3, + "VERTICAL_FLIP": 4, + "CROP": 5, + "IM_FEELING_LUCKY": 6, +} + +func (x ImagesServiceTransform_Type) Enum() *ImagesServiceTransform_Type { + p := new(ImagesServiceTransform_Type) + *p = x + return p +} +func (x ImagesServiceTransform_Type) String() string { + return proto.EnumName(ImagesServiceTransform_Type_name, int32(x)) +} +func (x *ImagesServiceTransform_Type) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ImagesServiceTransform_Type_value, data, "ImagesServiceTransform_Type") + if err != nil { + return err + } + *x = ImagesServiceTransform_Type(value) + return nil +} +func (ImagesServiceTransform_Type) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{1, 0} +} + +type InputSettings_ORIENTATION_CORRECTION_TYPE int32 + +const ( + InputSettings_UNCHANGED_ORIENTATION InputSettings_ORIENTATION_CORRECTION_TYPE = 0 + InputSettings_CORRECT_ORIENTATION InputSettings_ORIENTATION_CORRECTION_TYPE = 1 +) + +var InputSettings_ORIENTATION_CORRECTION_TYPE_name = map[int32]string{ + 0: "UNCHANGED_ORIENTATION", + 1: "CORRECT_ORIENTATION", +} +var InputSettings_ORIENTATION_CORRECTION_TYPE_value = map[string]int32{ + "UNCHANGED_ORIENTATION": 0, + "CORRECT_ORIENTATION": 1, +} + +func (x InputSettings_ORIENTATION_CORRECTION_TYPE) Enum() *InputSettings_ORIENTATION_CORRECTION_TYPE { + p := new(InputSettings_ORIENTATION_CORRECTION_TYPE) + *p = x + return p +} +func (x InputSettings_ORIENTATION_CORRECTION_TYPE) String() string { + return proto.EnumName(InputSettings_ORIENTATION_CORRECTION_TYPE_name, int32(x)) +} +func (x *InputSettings_ORIENTATION_CORRECTION_TYPE) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(InputSettings_ORIENTATION_CORRECTION_TYPE_value, data, "InputSettings_ORIENTATION_CORRECTION_TYPE") + if err != nil { + return err + } + *x = InputSettings_ORIENTATION_CORRECTION_TYPE(value) + return nil +} +func (InputSettings_ORIENTATION_CORRECTION_TYPE) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{4, 0} +} + +type OutputSettings_MIME_TYPE int32 + +const ( + OutputSettings_PNG OutputSettings_MIME_TYPE = 0 + OutputSettings_JPEG OutputSettings_MIME_TYPE = 1 + OutputSettings_WEBP OutputSettings_MIME_TYPE = 2 +) + +var OutputSettings_MIME_TYPE_name = map[int32]string{ + 0: "PNG", + 1: "JPEG", + 2: "WEBP", +} +var OutputSettings_MIME_TYPE_value = map[string]int32{ + "PNG": 0, + "JPEG": 1, + "WEBP": 2, +} + +func (x OutputSettings_MIME_TYPE) Enum() *OutputSettings_MIME_TYPE { + p := new(OutputSettings_MIME_TYPE) + *p = x + return p +} +func (x OutputSettings_MIME_TYPE) String() string { + return proto.EnumName(OutputSettings_MIME_TYPE_name, int32(x)) +} +func (x *OutputSettings_MIME_TYPE) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(OutputSettings_MIME_TYPE_value, data, "OutputSettings_MIME_TYPE") + if err != nil { + return err + } + *x = OutputSettings_MIME_TYPE(value) + return nil +} +func (OutputSettings_MIME_TYPE) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{5, 0} } + +type CompositeImageOptions_ANCHOR int32 + +const ( + CompositeImageOptions_TOP_LEFT CompositeImageOptions_ANCHOR = 0 + CompositeImageOptions_TOP CompositeImageOptions_ANCHOR = 1 + CompositeImageOptions_TOP_RIGHT CompositeImageOptions_ANCHOR = 2 + CompositeImageOptions_LEFT CompositeImageOptions_ANCHOR = 3 + CompositeImageOptions_CENTER CompositeImageOptions_ANCHOR = 4 + CompositeImageOptions_RIGHT CompositeImageOptions_ANCHOR = 5 + CompositeImageOptions_BOTTOM_LEFT CompositeImageOptions_ANCHOR = 6 + CompositeImageOptions_BOTTOM CompositeImageOptions_ANCHOR = 7 + CompositeImageOptions_BOTTOM_RIGHT CompositeImageOptions_ANCHOR = 8 +) + +var CompositeImageOptions_ANCHOR_name = map[int32]string{ + 0: "TOP_LEFT", + 1: "TOP", + 2: "TOP_RIGHT", + 3: "LEFT", + 4: "CENTER", + 5: "RIGHT", + 6: "BOTTOM_LEFT", + 7: "BOTTOM", + 8: "BOTTOM_RIGHT", +} +var CompositeImageOptions_ANCHOR_value = map[string]int32{ + "TOP_LEFT": 0, + "TOP": 1, + "TOP_RIGHT": 2, + "LEFT": 3, + "CENTER": 4, + "RIGHT": 5, + "BOTTOM_LEFT": 6, + "BOTTOM": 7, + "BOTTOM_RIGHT": 8, +} + +func (x CompositeImageOptions_ANCHOR) Enum() *CompositeImageOptions_ANCHOR { + p := new(CompositeImageOptions_ANCHOR) + *p = x + return p +} +func (x CompositeImageOptions_ANCHOR) String() string { + return proto.EnumName(CompositeImageOptions_ANCHOR_name, int32(x)) +} +func (x *CompositeImageOptions_ANCHOR) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(CompositeImageOptions_ANCHOR_value, data, "CompositeImageOptions_ANCHOR") + if err != nil { + return err + } + *x = CompositeImageOptions_ANCHOR(value) + return nil +} +func (CompositeImageOptions_ANCHOR) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{8, 0} +} + +type ImagesServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesServiceError) Reset() { *m = ImagesServiceError{} } +func (m *ImagesServiceError) String() string { return proto.CompactTextString(m) } +func (*ImagesServiceError) ProtoMessage() {} +func (*ImagesServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type ImagesServiceTransform struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesServiceTransform) Reset() { *m = ImagesServiceTransform{} } +func (m *ImagesServiceTransform) String() string { return proto.CompactTextString(m) } +func (*ImagesServiceTransform) ProtoMessage() {} +func (*ImagesServiceTransform) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type Transform struct { + Width *int32 `protobuf:"varint,1,opt,name=width" json:"width,omitempty"` + Height *int32 `protobuf:"varint,2,opt,name=height" json:"height,omitempty"` + CropToFit *bool `protobuf:"varint,11,opt,name=crop_to_fit,json=cropToFit,def=0" json:"crop_to_fit,omitempty"` + CropOffsetX *float32 `protobuf:"fixed32,12,opt,name=crop_offset_x,json=cropOffsetX,def=0.5" json:"crop_offset_x,omitempty"` + CropOffsetY *float32 `protobuf:"fixed32,13,opt,name=crop_offset_y,json=cropOffsetY,def=0.5" json:"crop_offset_y,omitempty"` + Rotate *int32 `protobuf:"varint,3,opt,name=rotate,def=0" json:"rotate,omitempty"` + HorizontalFlip *bool `protobuf:"varint,4,opt,name=horizontal_flip,json=horizontalFlip,def=0" json:"horizontal_flip,omitempty"` + VerticalFlip *bool `protobuf:"varint,5,opt,name=vertical_flip,json=verticalFlip,def=0" json:"vertical_flip,omitempty"` + CropLeftX *float32 `protobuf:"fixed32,6,opt,name=crop_left_x,json=cropLeftX,def=0" json:"crop_left_x,omitempty"` + CropTopY *float32 `protobuf:"fixed32,7,opt,name=crop_top_y,json=cropTopY,def=0" json:"crop_top_y,omitempty"` + CropRightX *float32 `protobuf:"fixed32,8,opt,name=crop_right_x,json=cropRightX,def=1" json:"crop_right_x,omitempty"` + CropBottomY *float32 `protobuf:"fixed32,9,opt,name=crop_bottom_y,json=cropBottomY,def=1" json:"crop_bottom_y,omitempty"` + Autolevels *bool `protobuf:"varint,10,opt,name=autolevels,def=0" json:"autolevels,omitempty"` + AllowStretch *bool `protobuf:"varint,14,opt,name=allow_stretch,json=allowStretch,def=0" json:"allow_stretch,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Transform) Reset() { *m = Transform{} } +func (m *Transform) String() string { return proto.CompactTextString(m) } +func (*Transform) ProtoMessage() {} +func (*Transform) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +const Default_Transform_CropToFit bool = false +const Default_Transform_CropOffsetX float32 = 0.5 +const Default_Transform_CropOffsetY float32 = 0.5 +const Default_Transform_Rotate int32 = 0 +const Default_Transform_HorizontalFlip bool = false +const Default_Transform_VerticalFlip bool = false +const Default_Transform_CropLeftX float32 = 0 +const Default_Transform_CropTopY float32 = 0 +const Default_Transform_CropRightX float32 = 1 +const Default_Transform_CropBottomY float32 = 1 +const Default_Transform_Autolevels bool = false +const Default_Transform_AllowStretch bool = false + +func (m *Transform) GetWidth() int32 { + if m != nil && m.Width != nil { + return *m.Width + } + return 0 +} + +func (m *Transform) GetHeight() int32 { + if m != nil && m.Height != nil { + return *m.Height + } + return 0 +} + +func (m *Transform) GetCropToFit() bool { + if m != nil && m.CropToFit != nil { + return *m.CropToFit + } + return Default_Transform_CropToFit +} + +func (m *Transform) GetCropOffsetX() float32 { + if m != nil && m.CropOffsetX != nil { + return *m.CropOffsetX + } + return Default_Transform_CropOffsetX +} + +func (m *Transform) GetCropOffsetY() float32 { + if m != nil && m.CropOffsetY != nil { + return *m.CropOffsetY + } + return Default_Transform_CropOffsetY +} + +func (m *Transform) GetRotate() int32 { + if m != nil && m.Rotate != nil { + return *m.Rotate + } + return Default_Transform_Rotate +} + +func (m *Transform) GetHorizontalFlip() bool { + if m != nil && m.HorizontalFlip != nil { + return *m.HorizontalFlip + } + return Default_Transform_HorizontalFlip +} + +func (m *Transform) GetVerticalFlip() bool { + if m != nil && m.VerticalFlip != nil { + return *m.VerticalFlip + } + return Default_Transform_VerticalFlip +} + +func (m *Transform) GetCropLeftX() float32 { + if m != nil && m.CropLeftX != nil { + return *m.CropLeftX + } + return Default_Transform_CropLeftX +} + +func (m *Transform) GetCropTopY() float32 { + if m != nil && m.CropTopY != nil { + return *m.CropTopY + } + return Default_Transform_CropTopY +} + +func (m *Transform) GetCropRightX() float32 { + if m != nil && m.CropRightX != nil { + return *m.CropRightX + } + return Default_Transform_CropRightX +} + +func (m *Transform) GetCropBottomY() float32 { + if m != nil && m.CropBottomY != nil { + return *m.CropBottomY + } + return Default_Transform_CropBottomY +} + +func (m *Transform) GetAutolevels() bool { + if m != nil && m.Autolevels != nil { + return *m.Autolevels + } + return Default_Transform_Autolevels +} + +func (m *Transform) GetAllowStretch() bool { + if m != nil && m.AllowStretch != nil { + return *m.AllowStretch + } + return Default_Transform_AllowStretch +} + +type ImageData struct { + Content []byte `protobuf:"bytes,1,req,name=content" json:"content,omitempty"` + BlobKey *string `protobuf:"bytes,2,opt,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + Width *int32 `protobuf:"varint,3,opt,name=width" json:"width,omitempty"` + Height *int32 `protobuf:"varint,4,opt,name=height" json:"height,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImageData) Reset() { *m = ImageData{} } +func (m *ImageData) String() string { return proto.CompactTextString(m) } +func (*ImageData) ProtoMessage() {} +func (*ImageData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *ImageData) GetContent() []byte { + if m != nil { + return m.Content + } + return nil +} + +func (m *ImageData) GetBlobKey() string { + if m != nil && m.BlobKey != nil { + return *m.BlobKey + } + return "" +} + +func (m *ImageData) GetWidth() int32 { + if m != nil && m.Width != nil { + return *m.Width + } + return 0 +} + +func (m *ImageData) GetHeight() int32 { + if m != nil && m.Height != nil { + return *m.Height + } + return 0 +} + +type InputSettings struct { + CorrectExifOrientation *InputSettings_ORIENTATION_CORRECTION_TYPE `protobuf:"varint,1,opt,name=correct_exif_orientation,json=correctExifOrientation,enum=appengine.InputSettings_ORIENTATION_CORRECTION_TYPE,def=0" json:"correct_exif_orientation,omitempty"` + ParseMetadata *bool `protobuf:"varint,2,opt,name=parse_metadata,json=parseMetadata,def=0" json:"parse_metadata,omitempty"` + TransparentSubstitutionRgb *int32 `protobuf:"varint,3,opt,name=transparent_substitution_rgb,json=transparentSubstitutionRgb" json:"transparent_substitution_rgb,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *InputSettings) Reset() { *m = InputSettings{} } +func (m *InputSettings) String() string { return proto.CompactTextString(m) } +func (*InputSettings) ProtoMessage() {} +func (*InputSettings) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +const Default_InputSettings_CorrectExifOrientation InputSettings_ORIENTATION_CORRECTION_TYPE = InputSettings_UNCHANGED_ORIENTATION +const Default_InputSettings_ParseMetadata bool = false + +func (m *InputSettings) GetCorrectExifOrientation() InputSettings_ORIENTATION_CORRECTION_TYPE { + if m != nil && m.CorrectExifOrientation != nil { + return *m.CorrectExifOrientation + } + return Default_InputSettings_CorrectExifOrientation +} + +func (m *InputSettings) GetParseMetadata() bool { + if m != nil && m.ParseMetadata != nil { + return *m.ParseMetadata + } + return Default_InputSettings_ParseMetadata +} + +func (m *InputSettings) GetTransparentSubstitutionRgb() int32 { + if m != nil && m.TransparentSubstitutionRgb != nil { + return *m.TransparentSubstitutionRgb + } + return 0 +} + +type OutputSettings struct { + MimeType *OutputSettings_MIME_TYPE `protobuf:"varint,1,opt,name=mime_type,json=mimeType,enum=appengine.OutputSettings_MIME_TYPE,def=0" json:"mime_type,omitempty"` + Quality *int32 `protobuf:"varint,2,opt,name=quality" json:"quality,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *OutputSettings) Reset() { *m = OutputSettings{} } +func (m *OutputSettings) String() string { return proto.CompactTextString(m) } +func (*OutputSettings) ProtoMessage() {} +func (*OutputSettings) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +const Default_OutputSettings_MimeType OutputSettings_MIME_TYPE = OutputSettings_PNG + +func (m *OutputSettings) GetMimeType() OutputSettings_MIME_TYPE { + if m != nil && m.MimeType != nil { + return *m.MimeType + } + return Default_OutputSettings_MimeType +} + +func (m *OutputSettings) GetQuality() int32 { + if m != nil && m.Quality != nil { + return *m.Quality + } + return 0 +} + +type ImagesTransformRequest struct { + Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"` + Transform []*Transform `protobuf:"bytes,2,rep,name=transform" json:"transform,omitempty"` + Output *OutputSettings `protobuf:"bytes,3,req,name=output" json:"output,omitempty"` + Input *InputSettings `protobuf:"bytes,4,opt,name=input" json:"input,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesTransformRequest) Reset() { *m = ImagesTransformRequest{} } +func (m *ImagesTransformRequest) String() string { return proto.CompactTextString(m) } +func (*ImagesTransformRequest) ProtoMessage() {} +func (*ImagesTransformRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *ImagesTransformRequest) GetImage() *ImageData { + if m != nil { + return m.Image + } + return nil +} + +func (m *ImagesTransformRequest) GetTransform() []*Transform { + if m != nil { + return m.Transform + } + return nil +} + +func (m *ImagesTransformRequest) GetOutput() *OutputSettings { + if m != nil { + return m.Output + } + return nil +} + +func (m *ImagesTransformRequest) GetInput() *InputSettings { + if m != nil { + return m.Input + } + return nil +} + +type ImagesTransformResponse struct { + Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"` + SourceMetadata *string `protobuf:"bytes,2,opt,name=source_metadata,json=sourceMetadata" json:"source_metadata,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesTransformResponse) Reset() { *m = ImagesTransformResponse{} } +func (m *ImagesTransformResponse) String() string { return proto.CompactTextString(m) } +func (*ImagesTransformResponse) ProtoMessage() {} +func (*ImagesTransformResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *ImagesTransformResponse) GetImage() *ImageData { + if m != nil { + return m.Image + } + return nil +} + +func (m *ImagesTransformResponse) GetSourceMetadata() string { + if m != nil && m.SourceMetadata != nil { + return *m.SourceMetadata + } + return "" +} + +type CompositeImageOptions struct { + SourceIndex *int32 `protobuf:"varint,1,req,name=source_index,json=sourceIndex" json:"source_index,omitempty"` + XOffset *int32 `protobuf:"varint,2,req,name=x_offset,json=xOffset" json:"x_offset,omitempty"` + YOffset *int32 `protobuf:"varint,3,req,name=y_offset,json=yOffset" json:"y_offset,omitempty"` + Opacity *float32 `protobuf:"fixed32,4,req,name=opacity" json:"opacity,omitempty"` + Anchor *CompositeImageOptions_ANCHOR `protobuf:"varint,5,req,name=anchor,enum=appengine.CompositeImageOptions_ANCHOR" json:"anchor,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CompositeImageOptions) Reset() { *m = CompositeImageOptions{} } +func (m *CompositeImageOptions) String() string { return proto.CompactTextString(m) } +func (*CompositeImageOptions) ProtoMessage() {} +func (*CompositeImageOptions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *CompositeImageOptions) GetSourceIndex() int32 { + if m != nil && m.SourceIndex != nil { + return *m.SourceIndex + } + return 0 +} + +func (m *CompositeImageOptions) GetXOffset() int32 { + if m != nil && m.XOffset != nil { + return *m.XOffset + } + return 0 +} + +func (m *CompositeImageOptions) GetYOffset() int32 { + if m != nil && m.YOffset != nil { + return *m.YOffset + } + return 0 +} + +func (m *CompositeImageOptions) GetOpacity() float32 { + if m != nil && m.Opacity != nil { + return *m.Opacity + } + return 0 +} + +func (m *CompositeImageOptions) GetAnchor() CompositeImageOptions_ANCHOR { + if m != nil && m.Anchor != nil { + return *m.Anchor + } + return CompositeImageOptions_TOP_LEFT +} + +type ImagesCanvas struct { + Width *int32 `protobuf:"varint,1,req,name=width" json:"width,omitempty"` + Height *int32 `protobuf:"varint,2,req,name=height" json:"height,omitempty"` + Output *OutputSettings `protobuf:"bytes,3,req,name=output" json:"output,omitempty"` + Color *int32 `protobuf:"varint,4,opt,name=color,def=-1" json:"color,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesCanvas) Reset() { *m = ImagesCanvas{} } +func (m *ImagesCanvas) String() string { return proto.CompactTextString(m) } +func (*ImagesCanvas) ProtoMessage() {} +func (*ImagesCanvas) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +const Default_ImagesCanvas_Color int32 = -1 + +func (m *ImagesCanvas) GetWidth() int32 { + if m != nil && m.Width != nil { + return *m.Width + } + return 0 +} + +func (m *ImagesCanvas) GetHeight() int32 { + if m != nil && m.Height != nil { + return *m.Height + } + return 0 +} + +func (m *ImagesCanvas) GetOutput() *OutputSettings { + if m != nil { + return m.Output + } + return nil +} + +func (m *ImagesCanvas) GetColor() int32 { + if m != nil && m.Color != nil { + return *m.Color + } + return Default_ImagesCanvas_Color +} + +type ImagesCompositeRequest struct { + Image []*ImageData `protobuf:"bytes,1,rep,name=image" json:"image,omitempty"` + Options []*CompositeImageOptions `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"` + Canvas *ImagesCanvas `protobuf:"bytes,3,req,name=canvas" json:"canvas,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesCompositeRequest) Reset() { *m = ImagesCompositeRequest{} } +func (m *ImagesCompositeRequest) String() string { return proto.CompactTextString(m) } +func (*ImagesCompositeRequest) ProtoMessage() {} +func (*ImagesCompositeRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *ImagesCompositeRequest) GetImage() []*ImageData { + if m != nil { + return m.Image + } + return nil +} + +func (m *ImagesCompositeRequest) GetOptions() []*CompositeImageOptions { + if m != nil { + return m.Options + } + return nil +} + +func (m *ImagesCompositeRequest) GetCanvas() *ImagesCanvas { + if m != nil { + return m.Canvas + } + return nil +} + +type ImagesCompositeResponse struct { + Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesCompositeResponse) Reset() { *m = ImagesCompositeResponse{} } +func (m *ImagesCompositeResponse) String() string { return proto.CompactTextString(m) } +func (*ImagesCompositeResponse) ProtoMessage() {} +func (*ImagesCompositeResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *ImagesCompositeResponse) GetImage() *ImageData { + if m != nil { + return m.Image + } + return nil +} + +type ImagesHistogramRequest struct { + Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesHistogramRequest) Reset() { *m = ImagesHistogramRequest{} } +func (m *ImagesHistogramRequest) String() string { return proto.CompactTextString(m) } +func (*ImagesHistogramRequest) ProtoMessage() {} +func (*ImagesHistogramRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *ImagesHistogramRequest) GetImage() *ImageData { + if m != nil { + return m.Image + } + return nil +} + +type ImagesHistogram struct { + Red []int32 `protobuf:"varint,1,rep,name=red" json:"red,omitempty"` + Green []int32 `protobuf:"varint,2,rep,name=green" json:"green,omitempty"` + Blue []int32 `protobuf:"varint,3,rep,name=blue" json:"blue,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesHistogram) Reset() { *m = ImagesHistogram{} } +func (m *ImagesHistogram) String() string { return proto.CompactTextString(m) } +func (*ImagesHistogram) ProtoMessage() {} +func (*ImagesHistogram) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *ImagesHistogram) GetRed() []int32 { + if m != nil { + return m.Red + } + return nil +} + +func (m *ImagesHistogram) GetGreen() []int32 { + if m != nil { + return m.Green + } + return nil +} + +func (m *ImagesHistogram) GetBlue() []int32 { + if m != nil { + return m.Blue + } + return nil +} + +type ImagesHistogramResponse struct { + Histogram *ImagesHistogram `protobuf:"bytes,1,req,name=histogram" json:"histogram,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesHistogramResponse) Reset() { *m = ImagesHistogramResponse{} } +func (m *ImagesHistogramResponse) String() string { return proto.CompactTextString(m) } +func (*ImagesHistogramResponse) ProtoMessage() {} +func (*ImagesHistogramResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *ImagesHistogramResponse) GetHistogram() *ImagesHistogram { + if m != nil { + return m.Histogram + } + return nil +} + +type ImagesGetUrlBaseRequest struct { + BlobKey *string `protobuf:"bytes,1,req,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + CreateSecureUrl *bool `protobuf:"varint,2,opt,name=create_secure_url,json=createSecureUrl,def=0" json:"create_secure_url,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesGetUrlBaseRequest) Reset() { *m = ImagesGetUrlBaseRequest{} } +func (m *ImagesGetUrlBaseRequest) String() string { return proto.CompactTextString(m) } +func (*ImagesGetUrlBaseRequest) ProtoMessage() {} +func (*ImagesGetUrlBaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +const Default_ImagesGetUrlBaseRequest_CreateSecureUrl bool = false + +func (m *ImagesGetUrlBaseRequest) GetBlobKey() string { + if m != nil && m.BlobKey != nil { + return *m.BlobKey + } + return "" +} + +func (m *ImagesGetUrlBaseRequest) GetCreateSecureUrl() bool { + if m != nil && m.CreateSecureUrl != nil { + return *m.CreateSecureUrl + } + return Default_ImagesGetUrlBaseRequest_CreateSecureUrl +} + +type ImagesGetUrlBaseResponse struct { + Url *string `protobuf:"bytes,1,req,name=url" json:"url,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesGetUrlBaseResponse) Reset() { *m = ImagesGetUrlBaseResponse{} } +func (m *ImagesGetUrlBaseResponse) String() string { return proto.CompactTextString(m) } +func (*ImagesGetUrlBaseResponse) ProtoMessage() {} +func (*ImagesGetUrlBaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *ImagesGetUrlBaseResponse) GetUrl() string { + if m != nil && m.Url != nil { + return *m.Url + } + return "" +} + +type ImagesDeleteUrlBaseRequest struct { + BlobKey *string `protobuf:"bytes,1,req,name=blob_key,json=blobKey" json:"blob_key,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesDeleteUrlBaseRequest) Reset() { *m = ImagesDeleteUrlBaseRequest{} } +func (m *ImagesDeleteUrlBaseRequest) String() string { return proto.CompactTextString(m) } +func (*ImagesDeleteUrlBaseRequest) ProtoMessage() {} +func (*ImagesDeleteUrlBaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *ImagesDeleteUrlBaseRequest) GetBlobKey() string { + if m != nil && m.BlobKey != nil { + return *m.BlobKey + } + return "" +} + +type ImagesDeleteUrlBaseResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *ImagesDeleteUrlBaseResponse) Reset() { *m = ImagesDeleteUrlBaseResponse{} } +func (m *ImagesDeleteUrlBaseResponse) String() string { return proto.CompactTextString(m) } +func (*ImagesDeleteUrlBaseResponse) ProtoMessage() {} +func (*ImagesDeleteUrlBaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func init() { + proto.RegisterType((*ImagesServiceError)(nil), "appengine.ImagesServiceError") + proto.RegisterType((*ImagesServiceTransform)(nil), "appengine.ImagesServiceTransform") + proto.RegisterType((*Transform)(nil), "appengine.Transform") + proto.RegisterType((*ImageData)(nil), "appengine.ImageData") + proto.RegisterType((*InputSettings)(nil), "appengine.InputSettings") + proto.RegisterType((*OutputSettings)(nil), "appengine.OutputSettings") + proto.RegisterType((*ImagesTransformRequest)(nil), "appengine.ImagesTransformRequest") + proto.RegisterType((*ImagesTransformResponse)(nil), "appengine.ImagesTransformResponse") + proto.RegisterType((*CompositeImageOptions)(nil), "appengine.CompositeImageOptions") + proto.RegisterType((*ImagesCanvas)(nil), "appengine.ImagesCanvas") + proto.RegisterType((*ImagesCompositeRequest)(nil), "appengine.ImagesCompositeRequest") + proto.RegisterType((*ImagesCompositeResponse)(nil), "appengine.ImagesCompositeResponse") + proto.RegisterType((*ImagesHistogramRequest)(nil), "appengine.ImagesHistogramRequest") + proto.RegisterType((*ImagesHistogram)(nil), "appengine.ImagesHistogram") + proto.RegisterType((*ImagesHistogramResponse)(nil), "appengine.ImagesHistogramResponse") + proto.RegisterType((*ImagesGetUrlBaseRequest)(nil), "appengine.ImagesGetUrlBaseRequest") + proto.RegisterType((*ImagesGetUrlBaseResponse)(nil), "appengine.ImagesGetUrlBaseResponse") + proto.RegisterType((*ImagesDeleteUrlBaseRequest)(nil), "appengine.ImagesDeleteUrlBaseRequest") + proto.RegisterType((*ImagesDeleteUrlBaseResponse)(nil), "appengine.ImagesDeleteUrlBaseResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/image/images_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 1460 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x6e, 0xe3, 0xc6, + 0x15, 0x5e, 0x52, 0xff, 0xc7, 0xb2, 0xcc, 0x9d, 0xec, 0x0f, 0x77, 0x93, 0xa2, 0x0a, 0x83, 0xc5, + 0x1a, 0x41, 0x2a, 0xaf, 0x8d, 0x16, 0x2d, 0x7c, 0x93, 0xea, 0x87, 0x92, 0x99, 0x95, 0x44, 0x75, + 0x44, 0xa7, 0xeb, 0xbd, 0x19, 0xd0, 0xf2, 0x48, 0x26, 0x4a, 0x73, 0x98, 0xe1, 0xc8, 0xb1, 0x7a, + 0x51, 0xf4, 0xa6, 0x17, 0x05, 0xfa, 0x06, 0x7d, 0x8a, 0xbe, 0x45, 0x81, 0xbe, 0x41, 0xfb, 0x32, + 0xc5, 0x0c, 0x49, 0x99, 0xf6, 0x3a, 0x4d, 0xb3, 0x37, 0xc2, 0xcc, 0x39, 0xdf, 0xf9, 0x9d, 0x8f, + 0xe7, 0x08, 0xbe, 0x5e, 0x31, 0xb6, 0x0a, 0x69, 0x67, 0xc5, 0x42, 0x3f, 0x5a, 0x75, 0x18, 0x5f, + 0x1d, 0xf8, 0x71, 0x4c, 0xa3, 0x55, 0x10, 0xd1, 0x83, 0x20, 0x12, 0x94, 0x47, 0x7e, 0x78, 0x10, + 0x5c, 0xf9, 0x2b, 0x9a, 0xfe, 0x26, 0x24, 0xa1, 0xfc, 0x3a, 0x58, 0xd0, 0x4e, 0xcc, 0x99, 0x60, + 0xa8, 0xb1, 0x85, 0x5b, 0xff, 0xd4, 0x00, 0x39, 0x0a, 0x33, 0x4f, 0x21, 0x36, 0xe7, 0x8c, 0x5b, + 0xff, 0xd0, 0xa0, 0xa1, 0x4e, 0x7d, 0x76, 0x41, 0xd1, 0x53, 0x78, 0x7c, 0x3a, 0x9d, 0xcf, 0xec, + 0xbe, 0x33, 0x74, 0xec, 0x01, 0xb1, 0x31, 0x76, 0xb1, 0xa1, 0xa1, 0x67, 0x80, 0x7a, 0xdd, 0x01, + 0xf1, 0x70, 0x77, 0x3a, 0x1f, 0xba, 0x78, 0x42, 0x06, 0x5d, 0xaf, 0x6b, 0xe8, 0x68, 0x17, 0x1a, + 0x53, 0xd7, 0x23, 0xce, 0xa4, 0x3b, 0xb2, 0x8d, 0x12, 0x42, 0xd0, 0x92, 0x30, 0x75, 0x4d, 0x21, + 0x65, 0xf4, 0x09, 0xec, 0xa5, 0x77, 0xcf, 0x75, 0xc9, 0xb8, 0x8b, 0x47, 0xb6, 0x51, 0x41, 0x4f, + 0xc0, 0x70, 0xa6, 0xdf, 0x76, 0xc7, 0xce, 0x80, 0xf4, 0xc6, 0x6e, 0x8f, 0xbc, 0xb5, 0xcf, 0x8c, + 0x2a, 0x7a, 0x0c, 0xbb, 0xdd, 0x7e, 0xdf, 0x9e, 0xcf, 0xc9, 0xc0, 0x9e, 0x3a, 0xf6, 0xc0, 0xa8, + 0x49, 0xa0, 0xdb, 0xfb, 0xc6, 0xee, 0x7b, 0x44, 0xc6, 0x19, 0xba, 0xa7, 0xd3, 0x81, 0x51, 0xb7, + 0xfe, 0xac, 0xc1, 0xb3, 0x3b, 0xa5, 0x78, 0xdc, 0x8f, 0x92, 0x25, 0xe3, 0x57, 0xd6, 0x12, 0xca, + 0xde, 0x26, 0xa6, 0x08, 0xa0, 0x8a, 0xed, 0xb9, 0xf3, 0xde, 0x36, 0x34, 0x75, 0x76, 0xbd, 0xae, + 0x67, 0x1b, 0xba, 0x4c, 0xe7, 0xc4, 0xc5, 0xce, 0x7b, 0x77, 0xea, 0x75, 0xc7, 0x64, 0x38, 0x76, + 0x66, 0x46, 0x49, 0x06, 0xfe, 0xd6, 0xc6, 0x9e, 0xd3, 0xcf, 0x45, 0x65, 0x54, 0x87, 0x72, 0x1f, + 0xbb, 0xb3, 0x2c, 0xd7, 0x09, 0x19, 0xda, 0xf6, 0xd8, 0x99, 0x8e, 0xc8, 0xf8, 0xb4, 0xff, 0xf6, + 0xcc, 0xa8, 0x5a, 0x7f, 0x2b, 0x43, 0x63, 0x1b, 0x15, 0x3d, 0x81, 0xca, 0xf7, 0xc1, 0x85, 0xb8, + 0x34, 0xb5, 0xb6, 0xb6, 0x5f, 0xc1, 0xe9, 0x05, 0x3d, 0x83, 0xea, 0x25, 0x0d, 0x56, 0x97, 0xc2, + 0xd4, 0x95, 0x38, 0xbb, 0xa1, 0x57, 0xb0, 0xb3, 0xe0, 0x2c, 0x26, 0x82, 0x91, 0x65, 0x20, 0xcc, + 0x9d, 0xb6, 0xb6, 0x5f, 0x3f, 0xae, 0x2c, 0xfd, 0x30, 0xa1, 0xb8, 0x21, 0x35, 0x1e, 0x1b, 0x06, + 0x02, 0xbd, 0x86, 0x5d, 0x05, 0x63, 0xcb, 0x65, 0x42, 0x05, 0xb9, 0x31, 0x9b, 0x6d, 0x6d, 0x5f, + 0x3f, 0x2e, 0xbd, 0xe9, 0xfc, 0x0a, 0x2b, 0x07, 0xae, 0x52, 0xbc, 0xbb, 0x0f, 0xdc, 0x98, 0xbb, + 0x0f, 0x02, 0xcf, 0xd0, 0x0b, 0xa8, 0x72, 0x26, 0x7c, 0x41, 0xcd, 0x92, 0x4c, 0xe8, 0x58, 0x7b, + 0x83, 0x33, 0x01, 0xea, 0xc0, 0xde, 0x25, 0xe3, 0xc1, 0x1f, 0x59, 0x24, 0xfc, 0x90, 0x2c, 0xc3, + 0x20, 0x36, 0xcb, 0xc5, 0xbc, 0x5a, 0xb7, 0xda, 0x61, 0x18, 0xc4, 0xe8, 0x4b, 0xd8, 0xbd, 0xa6, + 0x5c, 0x04, 0x8b, 0x1c, 0x5d, 0x29, 0xa2, 0x9b, 0xb9, 0x4e, 0x61, 0x3f, 0xcf, 0xea, 0x0d, 0xe9, + 0x52, 0x96, 0x51, 0x55, 0xd9, 0x69, 0x6f, 0xd2, 0x5a, 0xc7, 0x74, 0x29, 0xde, 0xa1, 0x9f, 0x03, + 0x64, 0x2d, 0x89, 0xc9, 0xc6, 0xac, 0xe5, 0x88, 0x7a, 0xda, 0x8d, 0xf8, 0x0c, 0x7d, 0x01, 0x4d, + 0x05, 0xe0, 0xb2, 0x83, 0xe4, 0xc6, 0xac, 0xa7, 0x90, 0x43, 0xac, 0xec, 0xb0, 0x94, 0xbe, 0x43, + 0xaf, 0xb2, 0x46, 0x9c, 0x33, 0x21, 0xd8, 0x15, 0xd9, 0x98, 0x8d, 0x1c, 0xa5, 0x12, 0xe8, 0x29, + 0xf1, 0x19, 0x7a, 0x05, 0xe0, 0xaf, 0x05, 0x0b, 0xe9, 0x35, 0x0d, 0x13, 0x13, 0x8a, 0x89, 0x17, + 0x14, 0xb2, 0x44, 0x3f, 0x0c, 0xd9, 0xf7, 0x24, 0x11, 0x9c, 0x8a, 0xc5, 0xa5, 0xd9, 0xba, 0x53, + 0xa2, 0xd2, 0xcd, 0x53, 0x95, 0xc5, 0xa1, 0xa1, 0x08, 0x39, 0xf0, 0x85, 0x8f, 0x3e, 0x83, 0xda, + 0x82, 0x45, 0x82, 0x46, 0xc2, 0xd4, 0xda, 0xfa, 0x7e, 0xb3, 0xa7, 0xd7, 0x35, 0x9c, 0x8b, 0xd0, + 0x0b, 0xa8, 0x9f, 0x87, 0xec, 0x9c, 0xfc, 0x81, 0x6e, 0x14, 0x2f, 0x1a, 0xb8, 0x26, 0xef, 0x6f, + 0xe9, 0xe6, 0x96, 0x46, 0xa5, 0x87, 0x69, 0x54, 0x2e, 0xd2, 0xc8, 0xfa, 0xb7, 0x0e, 0xbb, 0x4e, + 0x14, 0xaf, 0xc5, 0x9c, 0x0a, 0x11, 0x44, 0xab, 0x04, 0xfd, 0x45, 0x03, 0x73, 0xc1, 0x38, 0xa7, + 0x0b, 0x41, 0xe8, 0x4d, 0xb0, 0x24, 0x8c, 0x07, 0x34, 0x12, 0xbe, 0x08, 0x58, 0xa4, 0xa8, 0xd9, + 0x3a, 0xfa, 0x65, 0x67, 0x3b, 0x11, 0x3a, 0x77, 0x8c, 0x3b, 0x2e, 0x76, 0xec, 0xa9, 0xd7, 0xf5, + 0x1c, 0x77, 0x4a, 0xfa, 0x2e, 0xc6, 0x76, 0x5f, 0x1d, 0xbd, 0xb3, 0x99, 0x7d, 0xfc, 0xf4, 0x74, + 0xda, 0x3f, 0xe9, 0x4e, 0x47, 0xf6, 0x80, 0x14, 0x60, 0xf8, 0x59, 0x16, 0xcc, 0xbe, 0x09, 0x96, + 0xee, 0x6d, 0x28, 0xf4, 0x15, 0xb4, 0x62, 0x9f, 0x27, 0x94, 0x5c, 0x51, 0xe1, 0x5f, 0xf8, 0xc2, + 0x57, 0x85, 0x6e, 0x5b, 0xb7, 0xab, 0x94, 0x93, 0x4c, 0x87, 0x7e, 0x0b, 0x9f, 0x09, 0xf9, 0x25, + 0xc5, 0x3e, 0xa7, 0x91, 0x20, 0xc9, 0xfa, 0x3c, 0x11, 0x81, 0x58, 0x4b, 0x4f, 0x84, 0xaf, 0xce, + 0xb3, 0x66, 0xbc, 0x2c, 0x60, 0xe6, 0x05, 0x08, 0x5e, 0x9d, 0x5b, 0xbf, 0x83, 0x4f, 0xff, 0x47, + 0xf6, 0xe8, 0x05, 0x3c, 0x9c, 0xbf, 0xf1, 0x08, 0x3d, 0x87, 0x4f, 0x32, 0xf4, 0x1d, 0x85, 0x66, + 0xfd, 0x5d, 0x83, 0x96, 0xbb, 0x16, 0xc5, 0xee, 0xda, 0xd0, 0xb8, 0x0a, 0xae, 0x28, 0x11, 0x9b, + 0x98, 0x66, 0xdd, 0xfc, 0xa2, 0xd0, 0xcd, 0xbb, 0xe8, 0xce, 0xc4, 0x99, 0xd8, 0x69, 0xf3, 0x4a, + 0xb3, 0xe9, 0x08, 0xd7, 0xa5, 0xa9, 0x9a, 0x4c, 0x26, 0xd4, 0xbe, 0x5b, 0xfb, 0x61, 0x20, 0x36, + 0xd9, 0x58, 0xc8, 0xaf, 0xd6, 0x3e, 0x34, 0xb6, 0x56, 0xa8, 0x06, 0xd2, 0xce, 0x78, 0x24, 0x27, + 0xd1, 0x37, 0x33, 0x7b, 0x64, 0x68, 0xf2, 0xf4, 0x7b, 0xbb, 0x37, 0x33, 0x74, 0xeb, 0x3f, 0xdb, + 0x01, 0xb8, 0x9d, 0x41, 0x98, 0x7e, 0xb7, 0xa6, 0x89, 0x40, 0x5f, 0x42, 0x45, 0x6d, 0x02, 0x45, + 0xbd, 0x9d, 0xa3, 0x27, 0xc5, 0xf7, 0xce, 0x19, 0x8a, 0x53, 0x08, 0x3a, 0x82, 0x86, 0xc8, 0xed, + 0x4d, 0xbd, 0x5d, 0xba, 0x87, 0xbf, 0xf5, 0x7d, 0x0b, 0x43, 0x87, 0x50, 0x65, 0xaa, 0x52, 0xb3, + 0xa4, 0x02, 0xbc, 0xf8, 0xc1, 0x16, 0xe0, 0x0c, 0x88, 0x3a, 0x50, 0x09, 0x24, 0xd5, 0x14, 0x7f, + 0x77, 0x8e, 0xcc, 0x1f, 0xa2, 0x20, 0x4e, 0x61, 0x56, 0x04, 0xcf, 0x3f, 0x28, 0x2e, 0x89, 0x59, + 0x94, 0xd0, 0x9f, 0x54, 0xdd, 0x6b, 0xd8, 0x4b, 0xd8, 0x9a, 0x2f, 0xee, 0xd1, 0xb0, 0x81, 0x5b, + 0xa9, 0x38, 0x27, 0xa0, 0xf5, 0x2f, 0x1d, 0x9e, 0xf6, 0xd9, 0x55, 0xcc, 0x92, 0x40, 0x50, 0xe5, + 0xc6, 0x8d, 0x25, 0xb5, 0x12, 0xf4, 0x39, 0x34, 0x33, 0x17, 0x41, 0x74, 0x41, 0x6f, 0x54, 0xd4, + 0x0a, 0xde, 0x49, 0x65, 0x8e, 0x14, 0xc9, 0xcf, 0xf9, 0x26, 0x9b, 0xbc, 0xa6, 0xae, 0xd4, 0xb5, + 0x9b, 0x74, 0xde, 0x4a, 0xd5, 0x26, 0x57, 0x95, 0x52, 0xd5, 0x26, 0x53, 0x99, 0x50, 0x63, 0xb1, + 0xbf, 0x90, 0x24, 0x28, 0xb7, 0xf5, 0x7d, 0x1d, 0xe7, 0x57, 0xf4, 0x35, 0x54, 0xfd, 0x68, 0x71, + 0xc9, 0xb8, 0x59, 0x69, 0xeb, 0xfb, 0xad, 0xa3, 0xd7, 0x85, 0x12, 0x1f, 0x4c, 0xb2, 0xd3, 0x9d, + 0xf6, 0x4f, 0x5c, 0x8c, 0x33, 0x33, 0xeb, 0x4f, 0x50, 0x4d, 0x25, 0xa8, 0x09, 0x75, 0xcf, 0x9d, + 0x91, 0xb1, 0x3d, 0xf4, 0x8c, 0x47, 0x92, 0x50, 0x9e, 0x3b, 0x33, 0x34, 0xb9, 0xb4, 0xa5, 0x18, + 0x3b, 0xa3, 0x13, 0xcf, 0xd0, 0x25, 0xab, 0x14, 0xa2, 0x24, 0xf7, 0x64, 0xdf, 0x9e, 0x7a, 0x36, + 0x36, 0xca, 0xa8, 0x01, 0x95, 0x14, 0x50, 0x41, 0x7b, 0xb0, 0xd3, 0x73, 0x3d, 0xcf, 0x9d, 0xa4, + 0x9e, 0xaa, 0x12, 0x97, 0x0a, 0x8c, 0x1a, 0x32, 0xa0, 0x99, 0x29, 0x53, 0x78, 0xdd, 0xfa, 0xab, + 0x06, 0xcd, 0xf4, 0xf9, 0xfa, 0x7e, 0x74, 0xed, 0x27, 0xc5, 0xe5, 0xa8, 0x3f, 0xbc, 0x1c, 0xf5, + 0xc2, 0x72, 0xfc, 0x08, 0x7e, 0x99, 0x50, 0x59, 0xb0, 0x90, 0xf1, 0x74, 0x3e, 0x1e, 0xeb, 0xbf, + 0x38, 0xc4, 0xa9, 0x40, 0xfe, 0xb9, 0xc9, 0xbe, 0x93, 0x6d, 0xeb, 0x1e, 0xf8, 0x4e, 0x4a, 0x3f, + 0xc6, 0xa4, 0x63, 0xf9, 0x5a, 0xaa, 0xd9, 0xd9, 0x57, 0xd2, 0xfe, 0xb1, 0x47, 0xc1, 0xb9, 0x01, + 0x3a, 0x80, 0xea, 0x42, 0xf5, 0x21, 0xab, 0xe7, 0xf9, 0xfd, 0x40, 0x59, 0x9b, 0x70, 0x06, 0xb3, + 0xec, 0x9c, 0xfd, 0x85, 0x94, 0x7f, 0x3a, 0xfb, 0xad, 0x41, 0x5e, 0xf9, 0x49, 0x90, 0x08, 0xb6, + 0xe2, 0xfe, 0xc7, 0x4c, 0x08, 0x6b, 0x02, 0x7b, 0xf7, 0xbc, 0x20, 0x03, 0x4a, 0x9c, 0x5e, 0xa8, + 0xb6, 0x55, 0xb0, 0x3c, 0xca, 0x07, 0x5e, 0x71, 0x4a, 0x23, 0xd5, 0x9c, 0x0a, 0x4e, 0x2f, 0x08, + 0x41, 0xf9, 0x3c, 0x5c, 0xcb, 0xbf, 0x1a, 0x52, 0xa8, 0xce, 0xd6, 0x3c, 0xaf, 0xad, 0x90, 0x54, + 0x56, 0xdb, 0x6f, 0xa0, 0x71, 0x99, 0x0b, 0xb3, 0xcc, 0x5e, 0x7e, 0xd0, 0xaa, 0x5b, 0xb3, 0x5b, + 0xb0, 0xb5, 0xca, 0x9d, 0x8e, 0xa8, 0x38, 0xe5, 0x61, 0xcf, 0x4f, 0xb6, 0x8f, 0x5c, 0xdc, 0xb5, + 0xd2, 0x67, 0x61, 0xd7, 0x1e, 0xc2, 0xe3, 0x05, 0xa7, 0xbe, 0xa0, 0x24, 0xa1, 0x8b, 0x35, 0xa7, + 0x64, 0xcd, 0xc3, 0xbb, 0x6b, 0x6a, 0x2f, 0xd5, 0xcf, 0x95, 0xfa, 0x94, 0x87, 0xd6, 0x57, 0x60, + 0x7e, 0x18, 0x28, 0x4b, 0xdf, 0x80, 0x92, 0x74, 0x90, 0x06, 0x91, 0x47, 0xeb, 0xd7, 0xf0, 0x32, + 0x45, 0x0f, 0x68, 0x48, 0x05, 0xfd, 0xbf, 0x33, 0xb3, 0x7e, 0x06, 0x9f, 0x3e, 0x68, 0x98, 0x46, + 0xea, 0xd5, 0xde, 0xa7, 0x6f, 0xf3, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfa, 0x74, 0x30, 0x89, + 0x1d, 0x0c, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/image/images_service.proto b/vendor/google.golang.org/appengine/internal/image/images_service.proto new file mode 100644 index 0000000000..f0d2ed5d36 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/image/images_service.proto @@ -0,0 +1,162 @@ +syntax = "proto2"; +option go_package = "image"; + +package appengine; + +message ImagesServiceError { + enum ErrorCode { + UNSPECIFIED_ERROR = 1; + BAD_TRANSFORM_DATA = 2; + NOT_IMAGE = 3; + BAD_IMAGE_DATA = 4; + IMAGE_TOO_LARGE = 5; + INVALID_BLOB_KEY = 6; + ACCESS_DENIED = 7; + OBJECT_NOT_FOUND = 8; + } +} + +message ImagesServiceTransform { + enum Type { + RESIZE = 1; + ROTATE = 2; + HORIZONTAL_FLIP = 3; + VERTICAL_FLIP = 4; + CROP = 5; + IM_FEELING_LUCKY = 6; + } +} + +message Transform { + optional int32 width = 1; + optional int32 height = 2; + optional bool crop_to_fit = 11 [default = false]; + optional float crop_offset_x = 12 [default = 0.5]; + optional float crop_offset_y = 13 [default = 0.5]; + + optional int32 rotate = 3 [default = 0]; + + optional bool horizontal_flip = 4 [default = false]; + + optional bool vertical_flip = 5 [default = false]; + + optional float crop_left_x = 6 [default = 0.0]; + optional float crop_top_y = 7 [default = 0.0]; + optional float crop_right_x = 8 [default = 1.0]; + optional float crop_bottom_y = 9 [default = 1.0]; + + optional bool autolevels = 10 [default = false]; + + optional bool allow_stretch = 14 [default = false]; +} + +message ImageData { + required bytes content = 1 [ctype=CORD]; + optional string blob_key = 2; + + optional int32 width = 3; + optional int32 height = 4; +} + +message InputSettings { + enum ORIENTATION_CORRECTION_TYPE { + UNCHANGED_ORIENTATION = 0; + CORRECT_ORIENTATION = 1; + } + optional ORIENTATION_CORRECTION_TYPE correct_exif_orientation = 1 + [default=UNCHANGED_ORIENTATION]; + optional bool parse_metadata = 2 [default=false]; + optional int32 transparent_substitution_rgb = 3; +} + +message OutputSettings { + enum MIME_TYPE { + PNG = 0; + JPEG = 1; + WEBP = 2; + } + + optional MIME_TYPE mime_type = 1 [default=PNG]; + optional int32 quality = 2; +} + +message ImagesTransformRequest { + required ImageData image = 1; + repeated Transform transform = 2; + required OutputSettings output = 3; + optional InputSettings input = 4; +} + +message ImagesTransformResponse { + required ImageData image = 1; + optional string source_metadata = 2; +} + +message CompositeImageOptions { + required int32 source_index = 1; + required int32 x_offset = 2; + required int32 y_offset = 3; + required float opacity = 4; + + enum ANCHOR { + TOP_LEFT = 0; + TOP = 1; + TOP_RIGHT = 2; + LEFT = 3; + CENTER = 4; + RIGHT = 5; + BOTTOM_LEFT = 6; + BOTTOM = 7; + BOTTOM_RIGHT = 8; + } + + required ANCHOR anchor = 5; +} + +message ImagesCanvas { + required int32 width = 1; + required int32 height = 2; + required OutputSettings output = 3; + optional int32 color = 4 [default=-1]; +} + +message ImagesCompositeRequest { + repeated ImageData image = 1; + repeated CompositeImageOptions options = 2; + required ImagesCanvas canvas = 3; +} + +message ImagesCompositeResponse { + required ImageData image = 1; +} + +message ImagesHistogramRequest { + required ImageData image = 1; +} + +message ImagesHistogram { + repeated int32 red = 1; + repeated int32 green = 2; + repeated int32 blue = 3; +} + +message ImagesHistogramResponse { + required ImagesHistogram histogram = 1; +} + +message ImagesGetUrlBaseRequest { + required string blob_key = 1; + + optional bool create_secure_url = 2 [default = false]; +} + +message ImagesGetUrlBaseResponse { + required string url = 1; +} + +message ImagesDeleteUrlBaseRequest { + required string blob_key = 1; +} + +message ImagesDeleteUrlBaseResponse { +} diff --git a/vendor/google.golang.org/appengine/internal/internal.go b/vendor/google.golang.org/appengine/internal/internal.go new file mode 100644 index 0000000000..051ea3980a --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/internal.go @@ -0,0 +1,110 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package internal provides support for package appengine. +// +// Programs should not use this package directly. Its API is not stable. +// Use packages appengine and appengine/* instead. +package internal + +import ( + "fmt" + + "github.com/golang/protobuf/proto" + + remotepb "google.golang.org/appengine/internal/remote_api" +) + +// errorCodeMaps is a map of service name to the error code map for the service. +var errorCodeMaps = make(map[string]map[int32]string) + +// RegisterErrorCodeMap is called from API implementations to register their +// error code map. This should only be called from init functions. +func RegisterErrorCodeMap(service string, m map[int32]string) { + errorCodeMaps[service] = m +} + +type timeoutCodeKey struct { + service string + code int32 +} + +// timeoutCodes is the set of service+code pairs that represent timeouts. +var timeoutCodes = make(map[timeoutCodeKey]bool) + +func RegisterTimeoutErrorCode(service string, code int32) { + timeoutCodes[timeoutCodeKey{service, code}] = true +} + +// APIError is the type returned by appengine.Context's Call method +// when an API call fails in an API-specific way. This may be, for instance, +// a taskqueue API call failing with TaskQueueServiceError::UNKNOWN_QUEUE. +type APIError struct { + Service string + Detail string + Code int32 // API-specific error code +} + +func (e *APIError) Error() string { + if e.Code == 0 { + if e.Detail == "" { + return "APIError " + } + return e.Detail + } + s := fmt.Sprintf("API error %d", e.Code) + if m, ok := errorCodeMaps[e.Service]; ok { + s += " (" + e.Service + ": " + m[e.Code] + ")" + } else { + // Shouldn't happen, but provide a bit more detail if it does. + s = e.Service + " " + s + } + if e.Detail != "" { + s += ": " + e.Detail + } + return s +} + +func (e *APIError) IsTimeout() bool { + return timeoutCodes[timeoutCodeKey{e.Service, e.Code}] +} + +// CallError is the type returned by appengine.Context's Call method when an +// API call fails in a generic way, such as RpcError::CAPABILITY_DISABLED. +type CallError struct { + Detail string + Code int32 + // TODO: Remove this if we get a distinguishable error code. + Timeout bool +} + +func (e *CallError) Error() string { + var msg string + switch remotepb.RpcError_ErrorCode(e.Code) { + case remotepb.RpcError_UNKNOWN: + return e.Detail + case remotepb.RpcError_OVER_QUOTA: + msg = "Over quota" + case remotepb.RpcError_CAPABILITY_DISABLED: + msg = "Capability disabled" + case remotepb.RpcError_CANCELLED: + msg = "Canceled" + default: + msg = fmt.Sprintf("Call error %d", e.Code) + } + s := msg + ": " + e.Detail + if e.Timeout { + s += " (timeout)" + } + return s +} + +func (e *CallError) IsTimeout() bool { + return e.Timeout +} + +// NamespaceMods is a map from API service to a function that will mutate an RPC request to attach a namespace. +// The function should be prepared to be called on the same message more than once; it should only modify the +// RPC request the first time. +var NamespaceMods = make(map[string]func(m proto.Message, namespace string)) diff --git a/vendor/google.golang.org/appengine/internal/internal_vm_test.go b/vendor/google.golang.org/appengine/internal/internal_vm_test.go new file mode 100644 index 0000000000..f8097616b9 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/internal_vm_test.go @@ -0,0 +1,60 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package internal + +import ( + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" +) + +func TestInstallingHealthChecker(t *testing.T) { + try := func(desc string, mux *http.ServeMux, wantCode int, wantBody string) { + installHealthChecker(mux) + srv := httptest.NewServer(mux) + defer srv.Close() + + resp, err := http.Get(srv.URL + "/_ah/health") + if err != nil { + t.Errorf("%s: http.Get: %v", desc, err) + return + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("%s: reading body: %v", desc, err) + return + } + + if resp.StatusCode != wantCode { + t.Errorf("%s: got HTTP %d, want %d", desc, resp.StatusCode, wantCode) + return + } + if wantBody != "" && string(body) != wantBody { + t.Errorf("%s: got HTTP body %q, want %q", desc, body, wantBody) + return + } + } + + // If there's no handlers, or only a root handler, a health checker should be installed. + try("empty mux", http.NewServeMux(), 200, "ok") + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, "root handler") + }) + try("mux with root handler", mux, 200, "ok") + + // If there's a custom health check handler, one should not be installed. + mux = http.NewServeMux() + mux.HandleFunc("/_ah/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(418) + io.WriteString(w, "I'm short and stout!") + }) + try("mux with custom health checker", mux, 418, "I'm short and stout!") +} diff --git a/vendor/google.golang.org/appengine/internal/log/log_service.pb.go b/vendor/google.golang.org/appengine/internal/log/log_service.pb.go new file mode 100644 index 0000000000..5549605ad7 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/log/log_service.pb.go @@ -0,0 +1,1039 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/log/log_service.proto + +/* +Package log is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/log/log_service.proto + +It has these top-level messages: + LogServiceError + UserAppLogLine + UserAppLogGroup + FlushRequest + SetStatusRequest + LogOffset + LogLine + RequestLog + LogModuleVersion + LogReadRequest + LogReadResponse + LogUsageRecord + LogUsageRequest + LogUsageResponse +*/ +package log + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type LogServiceError_ErrorCode int32 + +const ( + LogServiceError_OK LogServiceError_ErrorCode = 0 + LogServiceError_INVALID_REQUEST LogServiceError_ErrorCode = 1 + LogServiceError_STORAGE_ERROR LogServiceError_ErrorCode = 2 +) + +var LogServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INVALID_REQUEST", + 2: "STORAGE_ERROR", +} +var LogServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INVALID_REQUEST": 1, + "STORAGE_ERROR": 2, +} + +func (x LogServiceError_ErrorCode) Enum() *LogServiceError_ErrorCode { + p := new(LogServiceError_ErrorCode) + *p = x + return p +} +func (x LogServiceError_ErrorCode) String() string { + return proto.EnumName(LogServiceError_ErrorCode_name, int32(x)) +} +func (x *LogServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(LogServiceError_ErrorCode_value, data, "LogServiceError_ErrorCode") + if err != nil { + return err + } + *x = LogServiceError_ErrorCode(value) + return nil +} +func (LogServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } + +type LogServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogServiceError) Reset() { *m = LogServiceError{} } +func (m *LogServiceError) String() string { return proto.CompactTextString(m) } +func (*LogServiceError) ProtoMessage() {} +func (*LogServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type UserAppLogLine struct { + TimestampUsec *int64 `protobuf:"varint,1,req,name=timestamp_usec,json=timestampUsec" json:"timestamp_usec,omitempty"` + Level *int64 `protobuf:"varint,2,req,name=level" json:"level,omitempty"` + Message *string `protobuf:"bytes,3,req,name=message" json:"message,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *UserAppLogLine) Reset() { *m = UserAppLogLine{} } +func (m *UserAppLogLine) String() string { return proto.CompactTextString(m) } +func (*UserAppLogLine) ProtoMessage() {} +func (*UserAppLogLine) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *UserAppLogLine) GetTimestampUsec() int64 { + if m != nil && m.TimestampUsec != nil { + return *m.TimestampUsec + } + return 0 +} + +func (m *UserAppLogLine) GetLevel() int64 { + if m != nil && m.Level != nil { + return *m.Level + } + return 0 +} + +func (m *UserAppLogLine) GetMessage() string { + if m != nil && m.Message != nil { + return *m.Message + } + return "" +} + +type UserAppLogGroup struct { + LogLine []*UserAppLogLine `protobuf:"bytes,2,rep,name=log_line,json=logLine" json:"log_line,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *UserAppLogGroup) Reset() { *m = UserAppLogGroup{} } +func (m *UserAppLogGroup) String() string { return proto.CompactTextString(m) } +func (*UserAppLogGroup) ProtoMessage() {} +func (*UserAppLogGroup) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *UserAppLogGroup) GetLogLine() []*UserAppLogLine { + if m != nil { + return m.LogLine + } + return nil +} + +type FlushRequest struct { + Logs []byte `protobuf:"bytes,1,opt,name=logs" json:"logs,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FlushRequest) Reset() { *m = FlushRequest{} } +func (m *FlushRequest) String() string { return proto.CompactTextString(m) } +func (*FlushRequest) ProtoMessage() {} +func (*FlushRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *FlushRequest) GetLogs() []byte { + if m != nil { + return m.Logs + } + return nil +} + +type SetStatusRequest struct { + Status *string `protobuf:"bytes,1,req,name=status" json:"status,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SetStatusRequest) Reset() { *m = SetStatusRequest{} } +func (m *SetStatusRequest) String() string { return proto.CompactTextString(m) } +func (*SetStatusRequest) ProtoMessage() {} +func (*SetStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *SetStatusRequest) GetStatus() string { + if m != nil && m.Status != nil { + return *m.Status + } + return "" +} + +type LogOffset struct { + RequestId []byte `protobuf:"bytes,1,opt,name=request_id,json=requestId" json:"request_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogOffset) Reset() { *m = LogOffset{} } +func (m *LogOffset) String() string { return proto.CompactTextString(m) } +func (*LogOffset) ProtoMessage() {} +func (*LogOffset) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *LogOffset) GetRequestId() []byte { + if m != nil { + return m.RequestId + } + return nil +} + +type LogLine struct { + Time *int64 `protobuf:"varint,1,req,name=time" json:"time,omitempty"` + Level *int32 `protobuf:"varint,2,req,name=level" json:"level,omitempty"` + LogMessage *string `protobuf:"bytes,3,req,name=log_message,json=logMessage" json:"log_message,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogLine) Reset() { *m = LogLine{} } +func (m *LogLine) String() string { return proto.CompactTextString(m) } +func (*LogLine) ProtoMessage() {} +func (*LogLine) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *LogLine) GetTime() int64 { + if m != nil && m.Time != nil { + return *m.Time + } + return 0 +} + +func (m *LogLine) GetLevel() int32 { + if m != nil && m.Level != nil { + return *m.Level + } + return 0 +} + +func (m *LogLine) GetLogMessage() string { + if m != nil && m.LogMessage != nil { + return *m.LogMessage + } + return "" +} + +type RequestLog struct { + AppId *string `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + ModuleId *string `protobuf:"bytes,37,opt,name=module_id,json=moduleId,def=default" json:"module_id,omitempty"` + VersionId *string `protobuf:"bytes,2,req,name=version_id,json=versionId" json:"version_id,omitempty"` + RequestId []byte `protobuf:"bytes,3,req,name=request_id,json=requestId" json:"request_id,omitempty"` + Offset *LogOffset `protobuf:"bytes,35,opt,name=offset" json:"offset,omitempty"` + Ip *string `protobuf:"bytes,4,req,name=ip" json:"ip,omitempty"` + Nickname *string `protobuf:"bytes,5,opt,name=nickname" json:"nickname,omitempty"` + StartTime *int64 `protobuf:"varint,6,req,name=start_time,json=startTime" json:"start_time,omitempty"` + EndTime *int64 `protobuf:"varint,7,req,name=end_time,json=endTime" json:"end_time,omitempty"` + Latency *int64 `protobuf:"varint,8,req,name=latency" json:"latency,omitempty"` + Mcycles *int64 `protobuf:"varint,9,req,name=mcycles" json:"mcycles,omitempty"` + Method *string `protobuf:"bytes,10,req,name=method" json:"method,omitempty"` + Resource *string `protobuf:"bytes,11,req,name=resource" json:"resource,omitempty"` + HttpVersion *string `protobuf:"bytes,12,req,name=http_version,json=httpVersion" json:"http_version,omitempty"` + Status *int32 `protobuf:"varint,13,req,name=status" json:"status,omitempty"` + ResponseSize *int64 `protobuf:"varint,14,req,name=response_size,json=responseSize" json:"response_size,omitempty"` + Referrer *string `protobuf:"bytes,15,opt,name=referrer" json:"referrer,omitempty"` + UserAgent *string `protobuf:"bytes,16,opt,name=user_agent,json=userAgent" json:"user_agent,omitempty"` + UrlMapEntry *string `protobuf:"bytes,17,req,name=url_map_entry,json=urlMapEntry" json:"url_map_entry,omitempty"` + Combined *string `protobuf:"bytes,18,req,name=combined" json:"combined,omitempty"` + ApiMcycles *int64 `protobuf:"varint,19,opt,name=api_mcycles,json=apiMcycles" json:"api_mcycles,omitempty"` + Host *string `protobuf:"bytes,20,opt,name=host" json:"host,omitempty"` + Cost *float64 `protobuf:"fixed64,21,opt,name=cost" json:"cost,omitempty"` + TaskQueueName *string `protobuf:"bytes,22,opt,name=task_queue_name,json=taskQueueName" json:"task_queue_name,omitempty"` + TaskName *string `protobuf:"bytes,23,opt,name=task_name,json=taskName" json:"task_name,omitempty"` + WasLoadingRequest *bool `protobuf:"varint,24,opt,name=was_loading_request,json=wasLoadingRequest" json:"was_loading_request,omitempty"` + PendingTime *int64 `protobuf:"varint,25,opt,name=pending_time,json=pendingTime" json:"pending_time,omitempty"` + ReplicaIndex *int32 `protobuf:"varint,26,opt,name=replica_index,json=replicaIndex,def=-1" json:"replica_index,omitempty"` + Finished *bool `protobuf:"varint,27,opt,name=finished,def=1" json:"finished,omitempty"` + CloneKey []byte `protobuf:"bytes,28,opt,name=clone_key,json=cloneKey" json:"clone_key,omitempty"` + Line []*LogLine `protobuf:"bytes,29,rep,name=line" json:"line,omitempty"` + LinesIncomplete *bool `protobuf:"varint,36,opt,name=lines_incomplete,json=linesIncomplete" json:"lines_incomplete,omitempty"` + AppEngineRelease []byte `protobuf:"bytes,38,opt,name=app_engine_release,json=appEngineRelease" json:"app_engine_release,omitempty"` + ExitReason *int32 `protobuf:"varint,30,opt,name=exit_reason,json=exitReason" json:"exit_reason,omitempty"` + WasThrottledForTime *bool `protobuf:"varint,31,opt,name=was_throttled_for_time,json=wasThrottledForTime" json:"was_throttled_for_time,omitempty"` + WasThrottledForRequests *bool `protobuf:"varint,32,opt,name=was_throttled_for_requests,json=wasThrottledForRequests" json:"was_throttled_for_requests,omitempty"` + ThrottledTime *int64 `protobuf:"varint,33,opt,name=throttled_time,json=throttledTime" json:"throttled_time,omitempty"` + ServerName []byte `protobuf:"bytes,34,opt,name=server_name,json=serverName" json:"server_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *RequestLog) Reset() { *m = RequestLog{} } +func (m *RequestLog) String() string { return proto.CompactTextString(m) } +func (*RequestLog) ProtoMessage() {} +func (*RequestLog) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +const Default_RequestLog_ModuleId string = "default" +const Default_RequestLog_ReplicaIndex int32 = -1 +const Default_RequestLog_Finished bool = true + +func (m *RequestLog) GetAppId() string { + if m != nil && m.AppId != nil { + return *m.AppId + } + return "" +} + +func (m *RequestLog) GetModuleId() string { + if m != nil && m.ModuleId != nil { + return *m.ModuleId + } + return Default_RequestLog_ModuleId +} + +func (m *RequestLog) GetVersionId() string { + if m != nil && m.VersionId != nil { + return *m.VersionId + } + return "" +} + +func (m *RequestLog) GetRequestId() []byte { + if m != nil { + return m.RequestId + } + return nil +} + +func (m *RequestLog) GetOffset() *LogOffset { + if m != nil { + return m.Offset + } + return nil +} + +func (m *RequestLog) GetIp() string { + if m != nil && m.Ip != nil { + return *m.Ip + } + return "" +} + +func (m *RequestLog) GetNickname() string { + if m != nil && m.Nickname != nil { + return *m.Nickname + } + return "" +} + +func (m *RequestLog) GetStartTime() int64 { + if m != nil && m.StartTime != nil { + return *m.StartTime + } + return 0 +} + +func (m *RequestLog) GetEndTime() int64 { + if m != nil && m.EndTime != nil { + return *m.EndTime + } + return 0 +} + +func (m *RequestLog) GetLatency() int64 { + if m != nil && m.Latency != nil { + return *m.Latency + } + return 0 +} + +func (m *RequestLog) GetMcycles() int64 { + if m != nil && m.Mcycles != nil { + return *m.Mcycles + } + return 0 +} + +func (m *RequestLog) GetMethod() string { + if m != nil && m.Method != nil { + return *m.Method + } + return "" +} + +func (m *RequestLog) GetResource() string { + if m != nil && m.Resource != nil { + return *m.Resource + } + return "" +} + +func (m *RequestLog) GetHttpVersion() string { + if m != nil && m.HttpVersion != nil { + return *m.HttpVersion + } + return "" +} + +func (m *RequestLog) GetStatus() int32 { + if m != nil && m.Status != nil { + return *m.Status + } + return 0 +} + +func (m *RequestLog) GetResponseSize() int64 { + if m != nil && m.ResponseSize != nil { + return *m.ResponseSize + } + return 0 +} + +func (m *RequestLog) GetReferrer() string { + if m != nil && m.Referrer != nil { + return *m.Referrer + } + return "" +} + +func (m *RequestLog) GetUserAgent() string { + if m != nil && m.UserAgent != nil { + return *m.UserAgent + } + return "" +} + +func (m *RequestLog) GetUrlMapEntry() string { + if m != nil && m.UrlMapEntry != nil { + return *m.UrlMapEntry + } + return "" +} + +func (m *RequestLog) GetCombined() string { + if m != nil && m.Combined != nil { + return *m.Combined + } + return "" +} + +func (m *RequestLog) GetApiMcycles() int64 { + if m != nil && m.ApiMcycles != nil { + return *m.ApiMcycles + } + return 0 +} + +func (m *RequestLog) GetHost() string { + if m != nil && m.Host != nil { + return *m.Host + } + return "" +} + +func (m *RequestLog) GetCost() float64 { + if m != nil && m.Cost != nil { + return *m.Cost + } + return 0 +} + +func (m *RequestLog) GetTaskQueueName() string { + if m != nil && m.TaskQueueName != nil { + return *m.TaskQueueName + } + return "" +} + +func (m *RequestLog) GetTaskName() string { + if m != nil && m.TaskName != nil { + return *m.TaskName + } + return "" +} + +func (m *RequestLog) GetWasLoadingRequest() bool { + if m != nil && m.WasLoadingRequest != nil { + return *m.WasLoadingRequest + } + return false +} + +func (m *RequestLog) GetPendingTime() int64 { + if m != nil && m.PendingTime != nil { + return *m.PendingTime + } + return 0 +} + +func (m *RequestLog) GetReplicaIndex() int32 { + if m != nil && m.ReplicaIndex != nil { + return *m.ReplicaIndex + } + return Default_RequestLog_ReplicaIndex +} + +func (m *RequestLog) GetFinished() bool { + if m != nil && m.Finished != nil { + return *m.Finished + } + return Default_RequestLog_Finished +} + +func (m *RequestLog) GetCloneKey() []byte { + if m != nil { + return m.CloneKey + } + return nil +} + +func (m *RequestLog) GetLine() []*LogLine { + if m != nil { + return m.Line + } + return nil +} + +func (m *RequestLog) GetLinesIncomplete() bool { + if m != nil && m.LinesIncomplete != nil { + return *m.LinesIncomplete + } + return false +} + +func (m *RequestLog) GetAppEngineRelease() []byte { + if m != nil { + return m.AppEngineRelease + } + return nil +} + +func (m *RequestLog) GetExitReason() int32 { + if m != nil && m.ExitReason != nil { + return *m.ExitReason + } + return 0 +} + +func (m *RequestLog) GetWasThrottledForTime() bool { + if m != nil && m.WasThrottledForTime != nil { + return *m.WasThrottledForTime + } + return false +} + +func (m *RequestLog) GetWasThrottledForRequests() bool { + if m != nil && m.WasThrottledForRequests != nil { + return *m.WasThrottledForRequests + } + return false +} + +func (m *RequestLog) GetThrottledTime() int64 { + if m != nil && m.ThrottledTime != nil { + return *m.ThrottledTime + } + return 0 +} + +func (m *RequestLog) GetServerName() []byte { + if m != nil { + return m.ServerName + } + return nil +} + +type LogModuleVersion struct { + ModuleId *string `protobuf:"bytes,1,opt,name=module_id,json=moduleId,def=default" json:"module_id,omitempty"` + VersionId *string `protobuf:"bytes,2,opt,name=version_id,json=versionId" json:"version_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogModuleVersion) Reset() { *m = LogModuleVersion{} } +func (m *LogModuleVersion) String() string { return proto.CompactTextString(m) } +func (*LogModuleVersion) ProtoMessage() {} +func (*LogModuleVersion) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +const Default_LogModuleVersion_ModuleId string = "default" + +func (m *LogModuleVersion) GetModuleId() string { + if m != nil && m.ModuleId != nil { + return *m.ModuleId + } + return Default_LogModuleVersion_ModuleId +} + +func (m *LogModuleVersion) GetVersionId() string { + if m != nil && m.VersionId != nil { + return *m.VersionId + } + return "" +} + +type LogReadRequest struct { + AppId *string `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + VersionId []string `protobuf:"bytes,2,rep,name=version_id,json=versionId" json:"version_id,omitempty"` + ModuleVersion []*LogModuleVersion `protobuf:"bytes,19,rep,name=module_version,json=moduleVersion" json:"module_version,omitempty"` + StartTime *int64 `protobuf:"varint,3,opt,name=start_time,json=startTime" json:"start_time,omitempty"` + EndTime *int64 `protobuf:"varint,4,opt,name=end_time,json=endTime" json:"end_time,omitempty"` + Offset *LogOffset `protobuf:"bytes,5,opt,name=offset" json:"offset,omitempty"` + RequestId [][]byte `protobuf:"bytes,6,rep,name=request_id,json=requestId" json:"request_id,omitempty"` + MinimumLogLevel *int32 `protobuf:"varint,7,opt,name=minimum_log_level,json=minimumLogLevel" json:"minimum_log_level,omitempty"` + IncludeIncomplete *bool `protobuf:"varint,8,opt,name=include_incomplete,json=includeIncomplete" json:"include_incomplete,omitempty"` + Count *int64 `protobuf:"varint,9,opt,name=count" json:"count,omitempty"` + CombinedLogRegex *string `protobuf:"bytes,14,opt,name=combined_log_regex,json=combinedLogRegex" json:"combined_log_regex,omitempty"` + HostRegex *string `protobuf:"bytes,15,opt,name=host_regex,json=hostRegex" json:"host_regex,omitempty"` + ReplicaIndex *int32 `protobuf:"varint,16,opt,name=replica_index,json=replicaIndex" json:"replica_index,omitempty"` + IncludeAppLogs *bool `protobuf:"varint,10,opt,name=include_app_logs,json=includeAppLogs" json:"include_app_logs,omitempty"` + AppLogsPerRequest *int32 `protobuf:"varint,17,opt,name=app_logs_per_request,json=appLogsPerRequest" json:"app_logs_per_request,omitempty"` + IncludeHost *bool `protobuf:"varint,11,opt,name=include_host,json=includeHost" json:"include_host,omitempty"` + IncludeAll *bool `protobuf:"varint,12,opt,name=include_all,json=includeAll" json:"include_all,omitempty"` + CacheIterator *bool `protobuf:"varint,13,opt,name=cache_iterator,json=cacheIterator" json:"cache_iterator,omitempty"` + NumShards *int32 `protobuf:"varint,18,opt,name=num_shards,json=numShards" json:"num_shards,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogReadRequest) Reset() { *m = LogReadRequest{} } +func (m *LogReadRequest) String() string { return proto.CompactTextString(m) } +func (*LogReadRequest) ProtoMessage() {} +func (*LogReadRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *LogReadRequest) GetAppId() string { + if m != nil && m.AppId != nil { + return *m.AppId + } + return "" +} + +func (m *LogReadRequest) GetVersionId() []string { + if m != nil { + return m.VersionId + } + return nil +} + +func (m *LogReadRequest) GetModuleVersion() []*LogModuleVersion { + if m != nil { + return m.ModuleVersion + } + return nil +} + +func (m *LogReadRequest) GetStartTime() int64 { + if m != nil && m.StartTime != nil { + return *m.StartTime + } + return 0 +} + +func (m *LogReadRequest) GetEndTime() int64 { + if m != nil && m.EndTime != nil { + return *m.EndTime + } + return 0 +} + +func (m *LogReadRequest) GetOffset() *LogOffset { + if m != nil { + return m.Offset + } + return nil +} + +func (m *LogReadRequest) GetRequestId() [][]byte { + if m != nil { + return m.RequestId + } + return nil +} + +func (m *LogReadRequest) GetMinimumLogLevel() int32 { + if m != nil && m.MinimumLogLevel != nil { + return *m.MinimumLogLevel + } + return 0 +} + +func (m *LogReadRequest) GetIncludeIncomplete() bool { + if m != nil && m.IncludeIncomplete != nil { + return *m.IncludeIncomplete + } + return false +} + +func (m *LogReadRequest) GetCount() int64 { + if m != nil && m.Count != nil { + return *m.Count + } + return 0 +} + +func (m *LogReadRequest) GetCombinedLogRegex() string { + if m != nil && m.CombinedLogRegex != nil { + return *m.CombinedLogRegex + } + return "" +} + +func (m *LogReadRequest) GetHostRegex() string { + if m != nil && m.HostRegex != nil { + return *m.HostRegex + } + return "" +} + +func (m *LogReadRequest) GetReplicaIndex() int32 { + if m != nil && m.ReplicaIndex != nil { + return *m.ReplicaIndex + } + return 0 +} + +func (m *LogReadRequest) GetIncludeAppLogs() bool { + if m != nil && m.IncludeAppLogs != nil { + return *m.IncludeAppLogs + } + return false +} + +func (m *LogReadRequest) GetAppLogsPerRequest() int32 { + if m != nil && m.AppLogsPerRequest != nil { + return *m.AppLogsPerRequest + } + return 0 +} + +func (m *LogReadRequest) GetIncludeHost() bool { + if m != nil && m.IncludeHost != nil { + return *m.IncludeHost + } + return false +} + +func (m *LogReadRequest) GetIncludeAll() bool { + if m != nil && m.IncludeAll != nil { + return *m.IncludeAll + } + return false +} + +func (m *LogReadRequest) GetCacheIterator() bool { + if m != nil && m.CacheIterator != nil { + return *m.CacheIterator + } + return false +} + +func (m *LogReadRequest) GetNumShards() int32 { + if m != nil && m.NumShards != nil { + return *m.NumShards + } + return 0 +} + +type LogReadResponse struct { + Log []*RequestLog `protobuf:"bytes,1,rep,name=log" json:"log,omitempty"` + Offset *LogOffset `protobuf:"bytes,2,opt,name=offset" json:"offset,omitempty"` + LastEndTime *int64 `protobuf:"varint,3,opt,name=last_end_time,json=lastEndTime" json:"last_end_time,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogReadResponse) Reset() { *m = LogReadResponse{} } +func (m *LogReadResponse) String() string { return proto.CompactTextString(m) } +func (*LogReadResponse) ProtoMessage() {} +func (*LogReadResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *LogReadResponse) GetLog() []*RequestLog { + if m != nil { + return m.Log + } + return nil +} + +func (m *LogReadResponse) GetOffset() *LogOffset { + if m != nil { + return m.Offset + } + return nil +} + +func (m *LogReadResponse) GetLastEndTime() int64 { + if m != nil && m.LastEndTime != nil { + return *m.LastEndTime + } + return 0 +} + +type LogUsageRecord struct { + VersionId *string `protobuf:"bytes,1,opt,name=version_id,json=versionId" json:"version_id,omitempty"` + StartTime *int32 `protobuf:"varint,2,opt,name=start_time,json=startTime" json:"start_time,omitempty"` + EndTime *int32 `protobuf:"varint,3,opt,name=end_time,json=endTime" json:"end_time,omitempty"` + Count *int64 `protobuf:"varint,4,opt,name=count" json:"count,omitempty"` + TotalSize *int64 `protobuf:"varint,5,opt,name=total_size,json=totalSize" json:"total_size,omitempty"` + Records *int32 `protobuf:"varint,6,opt,name=records" json:"records,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogUsageRecord) Reset() { *m = LogUsageRecord{} } +func (m *LogUsageRecord) String() string { return proto.CompactTextString(m) } +func (*LogUsageRecord) ProtoMessage() {} +func (*LogUsageRecord) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *LogUsageRecord) GetVersionId() string { + if m != nil && m.VersionId != nil { + return *m.VersionId + } + return "" +} + +func (m *LogUsageRecord) GetStartTime() int32 { + if m != nil && m.StartTime != nil { + return *m.StartTime + } + return 0 +} + +func (m *LogUsageRecord) GetEndTime() int32 { + if m != nil && m.EndTime != nil { + return *m.EndTime + } + return 0 +} + +func (m *LogUsageRecord) GetCount() int64 { + if m != nil && m.Count != nil { + return *m.Count + } + return 0 +} + +func (m *LogUsageRecord) GetTotalSize() int64 { + if m != nil && m.TotalSize != nil { + return *m.TotalSize + } + return 0 +} + +func (m *LogUsageRecord) GetRecords() int32 { + if m != nil && m.Records != nil { + return *m.Records + } + return 0 +} + +type LogUsageRequest struct { + AppId *string `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + VersionId []string `protobuf:"bytes,2,rep,name=version_id,json=versionId" json:"version_id,omitempty"` + StartTime *int32 `protobuf:"varint,3,opt,name=start_time,json=startTime" json:"start_time,omitempty"` + EndTime *int32 `protobuf:"varint,4,opt,name=end_time,json=endTime" json:"end_time,omitempty"` + ResolutionHours *uint32 `protobuf:"varint,5,opt,name=resolution_hours,json=resolutionHours,def=1" json:"resolution_hours,omitempty"` + CombineVersions *bool `protobuf:"varint,6,opt,name=combine_versions,json=combineVersions" json:"combine_versions,omitempty"` + UsageVersion *int32 `protobuf:"varint,7,opt,name=usage_version,json=usageVersion" json:"usage_version,omitempty"` + VersionsOnly *bool `protobuf:"varint,8,opt,name=versions_only,json=versionsOnly" json:"versions_only,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogUsageRequest) Reset() { *m = LogUsageRequest{} } +func (m *LogUsageRequest) String() string { return proto.CompactTextString(m) } +func (*LogUsageRequest) ProtoMessage() {} +func (*LogUsageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +const Default_LogUsageRequest_ResolutionHours uint32 = 1 + +func (m *LogUsageRequest) GetAppId() string { + if m != nil && m.AppId != nil { + return *m.AppId + } + return "" +} + +func (m *LogUsageRequest) GetVersionId() []string { + if m != nil { + return m.VersionId + } + return nil +} + +func (m *LogUsageRequest) GetStartTime() int32 { + if m != nil && m.StartTime != nil { + return *m.StartTime + } + return 0 +} + +func (m *LogUsageRequest) GetEndTime() int32 { + if m != nil && m.EndTime != nil { + return *m.EndTime + } + return 0 +} + +func (m *LogUsageRequest) GetResolutionHours() uint32 { + if m != nil && m.ResolutionHours != nil { + return *m.ResolutionHours + } + return Default_LogUsageRequest_ResolutionHours +} + +func (m *LogUsageRequest) GetCombineVersions() bool { + if m != nil && m.CombineVersions != nil { + return *m.CombineVersions + } + return false +} + +func (m *LogUsageRequest) GetUsageVersion() int32 { + if m != nil && m.UsageVersion != nil { + return *m.UsageVersion + } + return 0 +} + +func (m *LogUsageRequest) GetVersionsOnly() bool { + if m != nil && m.VersionsOnly != nil { + return *m.VersionsOnly + } + return false +} + +type LogUsageResponse struct { + Usage []*LogUsageRecord `protobuf:"bytes,1,rep,name=usage" json:"usage,omitempty"` + Summary *LogUsageRecord `protobuf:"bytes,2,opt,name=summary" json:"summary,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogUsageResponse) Reset() { *m = LogUsageResponse{} } +func (m *LogUsageResponse) String() string { return proto.CompactTextString(m) } +func (*LogUsageResponse) ProtoMessage() {} +func (*LogUsageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *LogUsageResponse) GetUsage() []*LogUsageRecord { + if m != nil { + return m.Usage + } + return nil +} + +func (m *LogUsageResponse) GetSummary() *LogUsageRecord { + if m != nil { + return m.Summary + } + return nil +} + +func init() { + proto.RegisterType((*LogServiceError)(nil), "appengine.LogServiceError") + proto.RegisterType((*UserAppLogLine)(nil), "appengine.UserAppLogLine") + proto.RegisterType((*UserAppLogGroup)(nil), "appengine.UserAppLogGroup") + proto.RegisterType((*FlushRequest)(nil), "appengine.FlushRequest") + proto.RegisterType((*SetStatusRequest)(nil), "appengine.SetStatusRequest") + proto.RegisterType((*LogOffset)(nil), "appengine.LogOffset") + proto.RegisterType((*LogLine)(nil), "appengine.LogLine") + proto.RegisterType((*RequestLog)(nil), "appengine.RequestLog") + proto.RegisterType((*LogModuleVersion)(nil), "appengine.LogModuleVersion") + proto.RegisterType((*LogReadRequest)(nil), "appengine.LogReadRequest") + proto.RegisterType((*LogReadResponse)(nil), "appengine.LogReadResponse") + proto.RegisterType((*LogUsageRecord)(nil), "appengine.LogUsageRecord") + proto.RegisterType((*LogUsageRequest)(nil), "appengine.LogUsageRequest") + proto.RegisterType((*LogUsageResponse)(nil), "appengine.LogUsageResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/log/log_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 1553 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xdd, 0x72, 0xdb, 0xc6, + 0x15, 0x2e, 0x48, 0x51, 0x24, 0x0f, 0x49, 0x91, 0x5a, 0xcb, 0xce, 0xda, 0xae, 0x6b, 0x1a, 0x4e, + 0x1c, 0xd6, 0x93, 0x48, 0x93, 0xa4, 0x57, 0xca, 0x95, 0xd3, 0x2a, 0x8e, 0x26, 0xb4, 0xd5, 0x40, + 0x72, 0x3a, 0xd3, 0x1b, 0x0c, 0x0a, 0x1c, 0x81, 0x18, 0x2f, 0xb1, 0xc8, 0xee, 0xc2, 0x91, 0x72, + 0xdb, 0xdb, 0x3e, 0x46, 0x1f, 0xa2, 0xaf, 0xd2, 0xb7, 0xe9, 0xec, 0xd9, 0x05, 0x44, 0x2a, 0x4d, + 0xc6, 0x33, 0xb9, 0xe0, 0x10, 0xfb, 0x9d, 0x83, 0xdd, 0xf3, 0xf3, 0x9d, 0x6f, 0x01, 0xc7, 0xb9, + 0x94, 0xb9, 0xc0, 0xc3, 0x5c, 0x8a, 0xa4, 0xcc, 0x0f, 0xa5, 0xca, 0x8f, 0x92, 0xaa, 0xc2, 0x32, + 0x2f, 0x4a, 0x3c, 0x2a, 0x4a, 0x83, 0xaa, 0x4c, 0xc4, 0x91, 0x90, 0xb9, 0xfd, 0xc5, 0x1a, 0xd5, + 0xbb, 0x22, 0xc5, 0xc3, 0x4a, 0x49, 0x23, 0xd9, 0xb0, 0xf5, 0x0c, 0x5f, 0xc3, 0x74, 0x29, 0xf3, + 0x73, 0x67, 0x3e, 0x51, 0x4a, 0xaa, 0xf0, 0x4b, 0x18, 0xd2, 0xc3, 0x9f, 0x65, 0x86, 0x6c, 0x17, + 0x3a, 0x67, 0xdf, 0xce, 0x7e, 0xc7, 0xee, 0xc0, 0xf4, 0xf4, 0xf5, 0xf7, 0x2f, 0x96, 0xa7, 0x7f, + 0x89, 0xa3, 0x93, 0xef, 0xde, 0x9c, 0x9c, 0x5f, 0xcc, 0x02, 0xb6, 0x0f, 0x93, 0xf3, 0x8b, 0xb3, + 0xe8, 0xc5, 0xcb, 0x93, 0xf8, 0x24, 0x8a, 0xce, 0xa2, 0x59, 0x27, 0xcc, 0x61, 0xef, 0x8d, 0x46, + 0xf5, 0xa2, 0xaa, 0x96, 0x32, 0x5f, 0x16, 0x25, 0xb2, 0x8f, 0x60, 0xcf, 0x14, 0x6b, 0xd4, 0x26, + 0x59, 0x57, 0x71, 0xad, 0x31, 0xe5, 0xc1, 0xbc, 0xb3, 0xe8, 0x46, 0x93, 0x16, 0x7d, 0xa3, 0x31, + 0x65, 0x07, 0xd0, 0x13, 0xf8, 0x0e, 0x05, 0xef, 0x90, 0xd5, 0x2d, 0x18, 0x87, 0xfe, 0x1a, 0xb5, + 0x4e, 0x72, 0xe4, 0xdd, 0x79, 0x67, 0x31, 0x8c, 0x9a, 0x65, 0xf8, 0x12, 0xa6, 0x37, 0x07, 0xbd, + 0x54, 0xb2, 0xae, 0xd8, 0x9f, 0x60, 0x60, 0x73, 0x15, 0x45, 0x89, 0xbc, 0x33, 0xef, 0x2e, 0x46, + 0x9f, 0xdf, 0x3f, 0x6c, 0x33, 0x3d, 0xdc, 0x0e, 0x2b, 0xea, 0x0b, 0xf7, 0x10, 0x86, 0x30, 0xfe, + 0x5a, 0xd4, 0x7a, 0x15, 0xe1, 0x0f, 0x35, 0x6a, 0xc3, 0x18, 0xec, 0x08, 0x99, 0x6b, 0x1e, 0xcc, + 0x83, 0xc5, 0x38, 0xa2, 0xe7, 0xf0, 0x39, 0xcc, 0xce, 0xd1, 0x9c, 0x9b, 0xc4, 0xd4, 0xba, 0xf1, + 0xbb, 0x07, 0xbb, 0x9a, 0x00, 0xca, 0x67, 0x18, 0xf9, 0x55, 0xf8, 0x1c, 0x86, 0x4b, 0x99, 0x9f, + 0x5d, 0x5e, 0x6a, 0x34, 0xec, 0x11, 0x80, 0x72, 0xfe, 0x71, 0x91, 0xf9, 0x2d, 0x87, 0x1e, 0x39, + 0xcd, 0xc2, 0x0b, 0xe8, 0x37, 0x65, 0x62, 0xb0, 0x63, 0x0b, 0xe2, 0x8b, 0x43, 0xcf, 0xdb, 0x35, + 0xe9, 0x35, 0x35, 0x79, 0x0c, 0x23, 0x9b, 0xe6, 0x76, 0x5d, 0x40, 0xc8, 0xfc, 0x95, 0x2f, 0xcd, + 0x3f, 0x01, 0xc0, 0x47, 0xb9, 0x94, 0x39, 0xbb, 0x0b, 0xbb, 0x49, 0x55, 0xb9, 0xf3, 0xad, 0x6b, + 0x2f, 0xa9, 0xaa, 0xd3, 0x8c, 0x7d, 0x08, 0xc3, 0xb5, 0xcc, 0x6a, 0x81, 0xd6, 0xf2, 0xd1, 0x3c, + 0x58, 0x0c, 0x8f, 0xfb, 0x19, 0x5e, 0x26, 0xb5, 0x30, 0xd1, 0xc0, 0x59, 0x4e, 0x33, 0x9b, 0xc0, + 0x3b, 0x54, 0xba, 0x90, 0xa5, 0x75, 0xeb, 0xd0, 0x06, 0x43, 0x8f, 0x38, 0xf3, 0x46, 0x7e, 0x36, + 0x94, 0xcd, 0xfc, 0xd8, 0x27, 0xb0, 0x2b, 0xa9, 0x10, 0xfc, 0xe9, 0x3c, 0x58, 0x8c, 0x3e, 0x3f, + 0xd8, 0xe8, 0x47, 0x5b, 0xa4, 0xc8, 0xfb, 0xb0, 0x3d, 0xe8, 0x14, 0x15, 0xdf, 0xa1, 0x33, 0x3a, + 0x45, 0xc5, 0x1e, 0xc0, 0xa0, 0x2c, 0xd2, 0xb7, 0x65, 0xb2, 0x46, 0xde, 0xb3, 0x01, 0x46, 0xed, + 0xda, 0x1e, 0xac, 0x4d, 0xa2, 0x4c, 0x4c, 0x45, 0xdb, 0xa5, 0xa2, 0x0d, 0x09, 0xb9, 0xb0, 0x95, + 0xbb, 0x0f, 0x03, 0x2c, 0x33, 0x67, 0xec, 0x93, 0xb1, 0x8f, 0x65, 0x46, 0x26, 0x0e, 0x7d, 0x91, + 0x18, 0x2c, 0xd3, 0x6b, 0x3e, 0x70, 0x16, 0xbf, 0x24, 0xb2, 0xa5, 0xd7, 0xa9, 0x40, 0xcd, 0x87, + 0xce, 0xe2, 0x97, 0xb6, 0xd7, 0x6b, 0x34, 0x2b, 0x99, 0x71, 0x70, 0xbd, 0x76, 0x2b, 0x1b, 0xa1, + 0x42, 0x2d, 0x6b, 0x95, 0x22, 0x1f, 0x91, 0xa5, 0x5d, 0xb3, 0x27, 0x30, 0x5e, 0x19, 0x53, 0xc5, + 0xbe, 0x58, 0x7c, 0x4c, 0xf6, 0x91, 0xc5, 0xbe, 0x77, 0xd0, 0x06, 0x85, 0x26, 0xd4, 0x60, 0xbf, + 0x62, 0x4f, 0x61, 0xa2, 0x50, 0x57, 0xb2, 0xd4, 0x18, 0xeb, 0xe2, 0x27, 0xe4, 0x7b, 0x14, 0xce, + 0xb8, 0x01, 0xcf, 0x8b, 0x9f, 0xd0, 0x9d, 0x7d, 0x89, 0x4a, 0xa1, 0xe2, 0x53, 0x57, 0x9d, 0x66, + 0x6d, 0xab, 0x53, 0x6b, 0x54, 0x71, 0x92, 0x63, 0x69, 0xf8, 0x8c, 0xac, 0x43, 0x8b, 0xbc, 0xb0, + 0x00, 0x0b, 0x61, 0x52, 0x2b, 0x11, 0xaf, 0x93, 0x2a, 0xc6, 0xd2, 0xa8, 0x6b, 0xbe, 0xef, 0x62, + 0xab, 0x95, 0x78, 0x95, 0x54, 0x27, 0x16, 0xb2, 0xdb, 0xa7, 0x72, 0xfd, 0x8f, 0xa2, 0xc4, 0x8c, + 0x33, 0x97, 0x5a, 0xb3, 0xb6, 0x0c, 0x4c, 0xaa, 0x22, 0x6e, 0x8a, 0x75, 0x67, 0x1e, 0x2c, 0xba, + 0x11, 0x24, 0x55, 0xf1, 0xca, 0xd7, 0x8b, 0xc1, 0xce, 0x4a, 0x6a, 0xc3, 0x0f, 0xe8, 0x64, 0x7a, + 0xb6, 0x58, 0x6a, 0xb1, 0xbb, 0xf3, 0x60, 0x11, 0x44, 0xf4, 0xcc, 0x9e, 0xc1, 0xd4, 0x24, 0xfa, + 0x6d, 0xfc, 0x43, 0x8d, 0x35, 0xc6, 0xd4, 0xe8, 0x7b, 0xf4, 0xca, 0xc4, 0xc2, 0xdf, 0x59, 0xf4, + 0xb5, 0xed, 0xf6, 0x43, 0x18, 0x92, 0x1f, 0x79, 0x7c, 0xe0, 0x92, 0xb5, 0x00, 0x19, 0x0f, 0xe1, + 0xce, 0x8f, 0x89, 0x8e, 0x85, 0x4c, 0xb2, 0xa2, 0xcc, 0x63, 0xcf, 0x3e, 0xce, 0xe7, 0xc1, 0x62, + 0x10, 0xed, 0xff, 0x98, 0xe8, 0xa5, 0xb3, 0x34, 0x83, 0xfb, 0x04, 0xc6, 0x15, 0x96, 0xe4, 0x4b, + 0xfc, 0xb8, 0x4f, 0xe1, 0x8f, 0x3c, 0x46, 0x1c, 0xf9, 0xd8, 0x36, 0xa0, 0x12, 0x45, 0x9a, 0xc4, + 0x45, 0x99, 0xe1, 0x15, 0x7f, 0x30, 0x0f, 0x16, 0xbd, 0xe3, 0xce, 0xa7, 0x9f, 0xd9, 0x26, 0x90, + 0xe1, 0xd4, 0xe2, 0x6c, 0x0e, 0x83, 0xcb, 0xa2, 0x2c, 0xf4, 0x0a, 0x33, 0xfe, 0xd0, 0x1e, 0x78, + 0xbc, 0x63, 0x54, 0x8d, 0x51, 0x8b, 0xda, 0xd0, 0x53, 0x21, 0x4b, 0x8c, 0xdf, 0xe2, 0x35, 0xff, + 0x3d, 0x09, 0xc0, 0x80, 0x80, 0x6f, 0xf1, 0x9a, 0x3d, 0x83, 0x1d, 0x52, 0xab, 0x47, 0xa4, 0x56, + 0x6c, 0x7b, 0x3a, 0x48, 0xa6, 0xc8, 0xce, 0xfe, 0x08, 0x33, 0xfb, 0xaf, 0xe3, 0xa2, 0x4c, 0xe5, + 0xba, 0x12, 0x68, 0x90, 0x7f, 0x48, 0xf9, 0x4d, 0x09, 0x3f, 0x6d, 0x61, 0xf6, 0x09, 0x30, 0x3b, + 0xed, 0x6e, 0x9b, 0x58, 0xa1, 0xc0, 0x44, 0x23, 0x7f, 0x46, 0x07, 0xcf, 0x92, 0xaa, 0x3a, 0x21, + 0x43, 0xe4, 0x70, 0xdb, 0x49, 0xbc, 0x2a, 0x4c, 0xac, 0x30, 0xd1, 0xb2, 0xe4, 0x7f, 0xb0, 0x69, + 0x46, 0x60, 0xa1, 0x88, 0x10, 0xf6, 0x05, 0xdc, 0xb3, 0xc5, 0x35, 0x2b, 0x25, 0x8d, 0x11, 0x98, + 0xc5, 0x97, 0x52, 0xb9, 0xb2, 0x3d, 0xa6, 0xf3, 0x6d, 0xe9, 0x2f, 0x1a, 0xe3, 0xd7, 0x52, 0x51, + 0xf9, 0xbe, 0x84, 0x07, 0x3f, 0x7f, 0xc9, 0xf7, 0x45, 0xf3, 0x39, 0xbd, 0xf8, 0xc1, 0xad, 0x17, + 0x7d, 0x77, 0x34, 0xdd, 0x17, 0xed, 0x8b, 0x74, 0xd2, 0x13, 0x6a, 0xd0, 0xa4, 0x45, 0xe9, 0x8c, + 0xc7, 0x30, 0xb2, 0x97, 0x1a, 0x2a, 0x47, 0x8a, 0x90, 0x12, 0x04, 0x07, 0x59, 0x5a, 0x84, 0x7f, + 0x83, 0xd9, 0x52, 0xe6, 0xaf, 0x48, 0xc8, 0x9a, 0x81, 0xdb, 0xd2, 0xbc, 0xe0, 0x7d, 0x35, 0x2f, + 0xd8, 0xd2, 0xbc, 0xf0, 0xbf, 0x3d, 0xd8, 0x5b, 0xca, 0x3c, 0xc2, 0x24, 0x6b, 0x28, 0xf5, 0x0b, + 0x12, 0x7b, 0x7b, 0xa3, 0xee, 0xb6, 0x78, 0x7e, 0x05, 0x7b, 0x3e, 0x9a, 0x46, 0x23, 0xee, 0x10, + 0x0f, 0x1e, 0x6e, 0xf3, 0x60, 0x2b, 0x85, 0x68, 0xb2, 0xde, 0xca, 0x68, 0x5b, 0x07, 0xbb, 0x54, + 0xa9, 0x5f, 0xd0, 0xc1, 0x1d, 0x32, 0xb6, 0x3a, 0x78, 0xa3, 0xcd, 0xbd, 0xf7, 0xd0, 0xe6, 0x6d, + 0xa1, 0xdf, 0x9d, 0x77, 0xb7, 0x85, 0xfe, 0x39, 0xec, 0xaf, 0x8b, 0xb2, 0x58, 0xd7, 0xeb, 0x98, + 0xae, 0x60, 0xba, 0xb5, 0xfa, 0xc4, 0xa6, 0xa9, 0x37, 0x58, 0x46, 0xd3, 0xfd, 0xf5, 0x29, 0xb0, + 0xa2, 0x4c, 0x45, 0x9d, 0xe1, 0x26, 0x9d, 0x07, 0x6e, 0x5c, 0xbd, 0x65, 0x83, 0xd0, 0x07, 0xd0, + 0x4b, 0x65, 0x5d, 0x1a, 0x3e, 0xa4, 0xf8, 0xdd, 0xc2, 0xd2, 0xbc, 0x91, 0x23, 0x3a, 0x51, 0x61, + 0x8e, 0x57, 0x7c, 0x8f, 0x7a, 0x35, 0x6b, 0x2c, 0xd4, 0xa5, 0x1c, 0xaf, 0x6c, 0xf4, 0x56, 0x83, + 0xbc, 0x97, 0x53, 0xcb, 0xa1, 0x45, 0x9c, 0xf9, 0xe9, 0xed, 0x71, 0x9f, 0x51, 0xe4, 0xdb, 0xa3, + 0xbe, 0x80, 0x59, 0x13, 0xb6, 0xed, 0x35, 0x7d, 0x23, 0x00, 0x05, 0xbd, 0xe7, 0x71, 0xf7, 0x75, + 0xa1, 0xd9, 0x11, 0x1c, 0x34, 0x1e, 0x71, 0x85, 0x2d, 0xf3, 0xf9, 0x3e, 0xed, 0xba, 0x9f, 0x38, + 0xb7, 0xbf, 0xa2, 0xda, 0x50, 0xa4, 0x66, 0x6b, 0x92, 0xcd, 0x11, 0x6d, 0x3b, 0xf2, 0xd8, 0x37, + 0x56, 0x29, 0x1f, 0xc3, 0xa8, 0x3d, 0x5d, 0x08, 0x3e, 0x26, 0x0f, 0x68, 0x0e, 0x16, 0xc2, 0x8e, + 0x4d, 0x9a, 0xa4, 0x2b, 0x8c, 0x0b, 0x83, 0x2a, 0x31, 0x52, 0xf1, 0x09, 0xf9, 0x4c, 0x08, 0x3d, + 0xf5, 0xa0, 0xad, 0x44, 0x59, 0xaf, 0x63, 0xbd, 0x4a, 0x54, 0xa6, 0x39, 0xa3, 0x88, 0x86, 0x65, + 0xbd, 0x3e, 0x27, 0x20, 0xfc, 0x57, 0x40, 0xdf, 0x83, 0x8e, 0xdb, 0xee, 0xb2, 0x61, 0x1f, 0x43, + 0x57, 0xc8, 0x9c, 0x07, 0xc4, 0xcd, 0xbb, 0x1b, 0x2c, 0xb9, 0xf9, 0xc6, 0x88, 0xac, 0xc7, 0x06, + 0xa3, 0x3a, 0xef, 0xc1, 0xa8, 0x10, 0x26, 0x22, 0xd1, 0x26, 0x6e, 0xf9, 0xe9, 0xc8, 0x3b, 0xb2, + 0xe0, 0x89, 0xe3, 0x68, 0xf8, 0x9f, 0x80, 0x46, 0xed, 0x8d, 0xfd, 0xac, 0x89, 0x30, 0x95, 0xea, + 0xf6, 0x4c, 0x05, 0xb7, 0x86, 0xf3, 0xd6, 0x3c, 0x74, 0x5c, 0x7e, 0xff, 0x7f, 0x1e, 0xba, 0x64, + 0x6c, 0xe7, 0xa1, 0xe5, 0xd9, 0xce, 0x26, 0xcf, 0x1e, 0x01, 0x18, 0x69, 0x12, 0xe1, 0xee, 0xe1, + 0x9e, 0x9b, 0x2f, 0x42, 0xe8, 0x12, 0xe6, 0xd0, 0x57, 0x14, 0x97, 0xe6, 0xbb, 0x6e, 0x3b, 0xbf, + 0x0c, 0xff, 0xdd, 0xa1, 0x4a, 0xfa, 0xd0, 0x7f, 0x8b, 0x4c, 0xfc, 0x7c, 0xc4, 0x7b, 0xbf, 0x36, + 0xe2, 0xbd, 0xcd, 0x11, 0x9f, 0xd9, 0xcf, 0x11, 0x51, 0x1b, 0xbb, 0xf7, 0x4a, 0xd6, 0x4a, 0x53, + 0x0a, 0x93, 0xe3, 0xe0, 0xb3, 0x68, 0x7a, 0x63, 0xfa, 0xc6, 0x5a, 0xec, 0x25, 0xe3, 0x07, 0xa7, + 0xd1, 0x23, 0x97, 0xd4, 0x20, 0x9a, 0x7a, 0xdc, 0x8b, 0x0e, 0x7d, 0xa0, 0xd4, 0x36, 0xb1, 0x56, + 0xb8, 0xdc, 0xa8, 0x8f, 0x09, 0x6c, 0xa4, 0xe9, 0x29, 0x4c, 0x9a, 0x7d, 0x62, 0x59, 0x8a, 0x6b, + 0x3f, 0xe2, 0xe3, 0x06, 0x3c, 0x2b, 0xc5, 0x75, 0x78, 0x45, 0x2a, 0xed, 0xab, 0xe4, 0x09, 0x77, + 0x04, 0x3d, 0xda, 0xc8, 0x53, 0xee, 0xfe, 0x36, 0x8d, 0x36, 0xc8, 0x10, 0x39, 0x3f, 0xf6, 0x05, + 0xf4, 0x75, 0xbd, 0x5e, 0x27, 0xea, 0xda, 0x33, 0xef, 0x57, 0x5e, 0x69, 0x3c, 0xbf, 0xea, 0xfd, + 0xdd, 0x92, 0xf6, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x70, 0xd9, 0xa0, 0xf8, 0x48, 0x0d, 0x00, + 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/log/log_service.proto b/vendor/google.golang.org/appengine/internal/log/log_service.proto new file mode 100644 index 0000000000..8981dc4757 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/log/log_service.proto @@ -0,0 +1,150 @@ +syntax = "proto2"; +option go_package = "log"; + +package appengine; + +message LogServiceError { + enum ErrorCode { + OK = 0; + INVALID_REQUEST = 1; + STORAGE_ERROR = 2; + } +} + +message UserAppLogLine { + required int64 timestamp_usec = 1; + required int64 level = 2; + required string message = 3; +} + +message UserAppLogGroup { + repeated UserAppLogLine log_line = 2; +} + +message FlushRequest { + optional bytes logs = 1; +} + +message SetStatusRequest { + required string status = 1; +} + + +message LogOffset { + optional bytes request_id = 1; +} + +message LogLine { + required int64 time = 1; + required int32 level = 2; + required string log_message = 3; +} + +message RequestLog { + required string app_id = 1; + optional string module_id = 37 [default="default"]; + required string version_id = 2; + required bytes request_id = 3; + optional LogOffset offset = 35; + required string ip = 4; + optional string nickname = 5; + required int64 start_time = 6; + required int64 end_time = 7; + required int64 latency = 8; + required int64 mcycles = 9; + required string method = 10; + required string resource = 11; + required string http_version = 12; + required int32 status = 13; + required int64 response_size = 14; + optional string referrer = 15; + optional string user_agent = 16; + required string url_map_entry = 17; + required string combined = 18; + optional int64 api_mcycles = 19; + optional string host = 20; + optional double cost = 21; + + optional string task_queue_name = 22; + optional string task_name = 23; + + optional bool was_loading_request = 24; + optional int64 pending_time = 25; + optional int32 replica_index = 26 [default = -1]; + optional bool finished = 27 [default = true]; + optional bytes clone_key = 28; + + repeated LogLine line = 29; + + optional bool lines_incomplete = 36; + optional bytes app_engine_release = 38; + + optional int32 exit_reason = 30; + optional bool was_throttled_for_time = 31; + optional bool was_throttled_for_requests = 32; + optional int64 throttled_time = 33; + + optional bytes server_name = 34; +} + +message LogModuleVersion { + optional string module_id = 1 [default="default"]; + optional string version_id = 2; +} + +message LogReadRequest { + required string app_id = 1; + repeated string version_id = 2; + repeated LogModuleVersion module_version = 19; + + optional int64 start_time = 3; + optional int64 end_time = 4; + optional LogOffset offset = 5; + repeated bytes request_id = 6; + + optional int32 minimum_log_level = 7; + optional bool include_incomplete = 8; + optional int64 count = 9; + + optional string combined_log_regex = 14; + optional string host_regex = 15; + optional int32 replica_index = 16; + + optional bool include_app_logs = 10; + optional int32 app_logs_per_request = 17; + optional bool include_host = 11; + optional bool include_all = 12; + optional bool cache_iterator = 13; + optional int32 num_shards = 18; +} + +message LogReadResponse { + repeated RequestLog log = 1; + optional LogOffset offset = 2; + optional int64 last_end_time = 3; +} + +message LogUsageRecord { + optional string version_id = 1; + optional int32 start_time = 2; + optional int32 end_time = 3; + optional int64 count = 4; + optional int64 total_size = 5; + optional int32 records = 6; +} + +message LogUsageRequest { + required string app_id = 1; + repeated string version_id = 2; + optional int32 start_time = 3; + optional int32 end_time = 4; + optional uint32 resolution_hours = 5 [default = 1]; + optional bool combine_versions = 6; + optional int32 usage_version = 7; + optional bool versions_only = 8; +} + +message LogUsageResponse { + repeated LogUsageRecord usage = 1; + optional LogUsageRecord summary = 2; +} diff --git a/vendor/google.golang.org/appengine/internal/mail/mail_service.pb.go b/vendor/google.golang.org/appengine/internal/mail/mail_service.pb.go new file mode 100644 index 0000000000..349aab0ff5 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/mail/mail_service.pb.go @@ -0,0 +1,283 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/mail/mail_service.proto + +/* +Package mail is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/mail/mail_service.proto + +It has these top-level messages: + MailServiceError + MailAttachment + MailHeader + MailMessage +*/ +package mail + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type MailServiceError_ErrorCode int32 + +const ( + MailServiceError_OK MailServiceError_ErrorCode = 0 + MailServiceError_INTERNAL_ERROR MailServiceError_ErrorCode = 1 + MailServiceError_BAD_REQUEST MailServiceError_ErrorCode = 2 + MailServiceError_UNAUTHORIZED_SENDER MailServiceError_ErrorCode = 3 + MailServiceError_INVALID_ATTACHMENT_TYPE MailServiceError_ErrorCode = 4 + MailServiceError_INVALID_HEADER_NAME MailServiceError_ErrorCode = 5 + MailServiceError_INVALID_CONTENT_ID MailServiceError_ErrorCode = 6 +) + +var MailServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INTERNAL_ERROR", + 2: "BAD_REQUEST", + 3: "UNAUTHORIZED_SENDER", + 4: "INVALID_ATTACHMENT_TYPE", + 5: "INVALID_HEADER_NAME", + 6: "INVALID_CONTENT_ID", +} +var MailServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INTERNAL_ERROR": 1, + "BAD_REQUEST": 2, + "UNAUTHORIZED_SENDER": 3, + "INVALID_ATTACHMENT_TYPE": 4, + "INVALID_HEADER_NAME": 5, + "INVALID_CONTENT_ID": 6, +} + +func (x MailServiceError_ErrorCode) Enum() *MailServiceError_ErrorCode { + p := new(MailServiceError_ErrorCode) + *p = x + return p +} +func (x MailServiceError_ErrorCode) String() string { + return proto.EnumName(MailServiceError_ErrorCode_name, int32(x)) +} +func (x *MailServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MailServiceError_ErrorCode_value, data, "MailServiceError_ErrorCode") + if err != nil { + return err + } + *x = MailServiceError_ErrorCode(value) + return nil +} +func (MailServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type MailServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *MailServiceError) Reset() { *m = MailServiceError{} } +func (m *MailServiceError) String() string { return proto.CompactTextString(m) } +func (*MailServiceError) ProtoMessage() {} +func (*MailServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type MailAttachment struct { + FileName *string `protobuf:"bytes,1,req,name=FileName" json:"FileName,omitempty"` + Data []byte `protobuf:"bytes,2,req,name=Data" json:"Data,omitempty"` + ContentID *string `protobuf:"bytes,3,opt,name=ContentID" json:"ContentID,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MailAttachment) Reset() { *m = MailAttachment{} } +func (m *MailAttachment) String() string { return proto.CompactTextString(m) } +func (*MailAttachment) ProtoMessage() {} +func (*MailAttachment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *MailAttachment) GetFileName() string { + if m != nil && m.FileName != nil { + return *m.FileName + } + return "" +} + +func (m *MailAttachment) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *MailAttachment) GetContentID() string { + if m != nil && m.ContentID != nil { + return *m.ContentID + } + return "" +} + +type MailHeader struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Value *string `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MailHeader) Reset() { *m = MailHeader{} } +func (m *MailHeader) String() string { return proto.CompactTextString(m) } +func (*MailHeader) ProtoMessage() {} +func (*MailHeader) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *MailHeader) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *MailHeader) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +type MailMessage struct { + Sender *string `protobuf:"bytes,1,req,name=Sender" json:"Sender,omitempty"` + ReplyTo *string `protobuf:"bytes,2,opt,name=ReplyTo" json:"ReplyTo,omitempty"` + To []string `protobuf:"bytes,3,rep,name=To" json:"To,omitempty"` + Cc []string `protobuf:"bytes,4,rep,name=Cc" json:"Cc,omitempty"` + Bcc []string `protobuf:"bytes,5,rep,name=Bcc" json:"Bcc,omitempty"` + Subject *string `protobuf:"bytes,6,req,name=Subject" json:"Subject,omitempty"` + TextBody *string `protobuf:"bytes,7,opt,name=TextBody" json:"TextBody,omitempty"` + HtmlBody *string `protobuf:"bytes,8,opt,name=HtmlBody" json:"HtmlBody,omitempty"` + Attachment []*MailAttachment `protobuf:"bytes,9,rep,name=Attachment" json:"Attachment,omitempty"` + Header []*MailHeader `protobuf:"bytes,10,rep,name=Header" json:"Header,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MailMessage) Reset() { *m = MailMessage{} } +func (m *MailMessage) String() string { return proto.CompactTextString(m) } +func (*MailMessage) ProtoMessage() {} +func (*MailMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *MailMessage) GetSender() string { + if m != nil && m.Sender != nil { + return *m.Sender + } + return "" +} + +func (m *MailMessage) GetReplyTo() string { + if m != nil && m.ReplyTo != nil { + return *m.ReplyTo + } + return "" +} + +func (m *MailMessage) GetTo() []string { + if m != nil { + return m.To + } + return nil +} + +func (m *MailMessage) GetCc() []string { + if m != nil { + return m.Cc + } + return nil +} + +func (m *MailMessage) GetBcc() []string { + if m != nil { + return m.Bcc + } + return nil +} + +func (m *MailMessage) GetSubject() string { + if m != nil && m.Subject != nil { + return *m.Subject + } + return "" +} + +func (m *MailMessage) GetTextBody() string { + if m != nil && m.TextBody != nil { + return *m.TextBody + } + return "" +} + +func (m *MailMessage) GetHtmlBody() string { + if m != nil && m.HtmlBody != nil { + return *m.HtmlBody + } + return "" +} + +func (m *MailMessage) GetAttachment() []*MailAttachment { + if m != nil { + return m.Attachment + } + return nil +} + +func (m *MailMessage) GetHeader() []*MailHeader { + if m != nil { + return m.Header + } + return nil +} + +func init() { + proto.RegisterType((*MailServiceError)(nil), "appengine.MailServiceError") + proto.RegisterType((*MailAttachment)(nil), "appengine.MailAttachment") + proto.RegisterType((*MailHeader)(nil), "appengine.MailHeader") + proto.RegisterType((*MailMessage)(nil), "appengine.MailMessage") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/mail/mail_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 480 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x92, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0xc6, 0x89, 0x9d, 0xb8, 0xf5, 0x04, 0x05, 0x6b, 0x81, 0x76, 0xf9, 0x73, 0x88, 0x72, 0xca, + 0x85, 0x44, 0xe2, 0x80, 0x84, 0xc4, 0xc5, 0xb1, 0x17, 0xc5, 0xa2, 0x71, 0x60, 0xb3, 0x41, 0xa2, + 0x07, 0xac, 0xc5, 0x19, 0x19, 0x23, 0xc7, 0x1b, 0x39, 0xdb, 0x8a, 0x3e, 0x0d, 0x4f, 0xc0, 0x8d, + 0x07, 0x44, 0x6b, 0xc7, 0x09, 0xf4, 0x62, 0xcd, 0x6f, 0xbf, 0xf9, 0x66, 0xac, 0x4f, 0x03, 0xef, + 0x32, 0xa5, 0xb2, 0x02, 0x27, 0x99, 0x2a, 0x64, 0x99, 0x4d, 0x54, 0x95, 0x4d, 0xe5, 0x6e, 0x87, + 0x65, 0x96, 0x97, 0x38, 0xcd, 0x4b, 0x8d, 0x55, 0x29, 0x8b, 0xe9, 0x56, 0xe6, 0xcd, 0x27, 0xd9, + 0x63, 0x75, 0x9b, 0xa7, 0x38, 0xd9, 0x55, 0x4a, 0x2b, 0xe2, 0x1e, 0x7b, 0x47, 0x7f, 0x3a, 0xe0, + 0x2d, 0x64, 0x5e, 0xac, 0x9a, 0x06, 0x56, 0x55, 0xaa, 0x1a, 0xfd, 0xea, 0x80, 0x5b, 0x57, 0x81, + 0xda, 0x20, 0x71, 0xc0, 0x5a, 0x7e, 0xf0, 0x1e, 0x10, 0x02, 0x83, 0x28, 0x16, 0x8c, 0xc7, 0xfe, + 0x55, 0xc2, 0x38, 0x5f, 0x72, 0xaf, 0x43, 0x1e, 0x41, 0x7f, 0xe6, 0x87, 0x09, 0x67, 0x9f, 0xd6, + 0x6c, 0x25, 0x3c, 0x8b, 0x5c, 0xc2, 0xe3, 0x75, 0xec, 0xaf, 0xc5, 0x7c, 0xc9, 0xa3, 0x6b, 0x16, + 0x26, 0x2b, 0x16, 0x87, 0x8c, 0x7b, 0x36, 0x79, 0x01, 0x97, 0x51, 0xfc, 0xd9, 0xbf, 0x8a, 0xc2, + 0xc4, 0x17, 0xc2, 0x0f, 0xe6, 0x0b, 0x16, 0x8b, 0x44, 0x7c, 0xf9, 0xc8, 0xbc, 0xae, 0x71, 0xb5, + 0xe2, 0x9c, 0xf9, 0x21, 0xe3, 0x49, 0xec, 0x2f, 0x98, 0xd7, 0x23, 0x17, 0x40, 0x5a, 0x21, 0x58, + 0xc6, 0xc2, 0x58, 0xa2, 0xd0, 0x73, 0x46, 0x5f, 0x61, 0x60, 0xfe, 0xda, 0xd7, 0x5a, 0xa6, 0xdf, + 0xb7, 0x58, 0x6a, 0xf2, 0x1c, 0xce, 0xdf, 0xe7, 0x05, 0xc6, 0x72, 0x8b, 0xb4, 0x33, 0xb4, 0xc6, + 0x2e, 0x3f, 0x32, 0x21, 0xd0, 0x0d, 0xa5, 0x96, 0xd4, 0x1a, 0x5a, 0xe3, 0x87, 0xbc, 0xae, 0xc9, + 0x4b, 0x70, 0x03, 0x55, 0x6a, 0x2c, 0x75, 0x14, 0x52, 0x7b, 0xd8, 0x19, 0xbb, 0xfc, 0xf4, 0x30, + 0x7a, 0x03, 0x60, 0xe6, 0xcf, 0x51, 0x6e, 0xb0, 0x32, 0xfe, 0xf2, 0x34, 0xb7, 0xae, 0xc9, 0x13, + 0xe8, 0xdd, 0xca, 0xe2, 0x06, 0xeb, 0xa1, 0x2e, 0x6f, 0x60, 0xf4, 0xdb, 0x82, 0xbe, 0x31, 0x2e, + 0x70, 0xbf, 0x97, 0x19, 0x92, 0x0b, 0x70, 0x56, 0x58, 0x6e, 0xb0, 0x3a, 0x78, 0x0f, 0x44, 0x28, + 0x9c, 0x71, 0xdc, 0x15, 0x77, 0x42, 0x51, 0xab, 0xde, 0xdd, 0x22, 0x19, 0x80, 0x25, 0x14, 0xb5, + 0x87, 0xf6, 0xd8, 0xe5, 0x56, 0xc3, 0x41, 0x4a, 0xbb, 0x0d, 0x07, 0x29, 0xf1, 0xc0, 0x9e, 0xa5, + 0x29, 0xed, 0xd5, 0x0f, 0xa6, 0x34, 0xb3, 0x56, 0x37, 0xdf, 0x7e, 0x60, 0xaa, 0xa9, 0x53, 0x2f, + 0x69, 0xd1, 0x64, 0x22, 0xf0, 0xa7, 0x9e, 0xa9, 0xcd, 0x1d, 0x3d, 0xab, 0xd7, 0x1c, 0xd9, 0x68, + 0x73, 0xbd, 0x2d, 0x6a, 0xed, 0xbc, 0xd1, 0x5a, 0x26, 0x6f, 0x01, 0x4e, 0xc9, 0x52, 0x77, 0x68, + 0x8f, 0xfb, 0xaf, 0x9f, 0x4d, 0x8e, 0x47, 0x33, 0xf9, 0x3f, 0x7a, 0xfe, 0x4f, 0x33, 0x79, 0x05, + 0x4e, 0x13, 0x1a, 0x85, 0xda, 0xf6, 0xf4, 0x9e, 0xad, 0x11, 0xf9, 0xa1, 0x69, 0xe6, 0x5c, 0x77, + 0xcd, 0x7d, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xd3, 0x01, 0x27, 0xd0, 0x02, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/mail/mail_service.proto b/vendor/google.golang.org/appengine/internal/mail/mail_service.proto new file mode 100644 index 0000000000..4e57b7aa51 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/mail/mail_service.proto @@ -0,0 +1,45 @@ +syntax = "proto2"; +option go_package = "mail"; + +package appengine; + +message MailServiceError { + enum ErrorCode { + OK = 0; + INTERNAL_ERROR = 1; + BAD_REQUEST = 2; + UNAUTHORIZED_SENDER = 3; + INVALID_ATTACHMENT_TYPE = 4; + INVALID_HEADER_NAME = 5; + INVALID_CONTENT_ID = 6; + } +} + +message MailAttachment { + required string FileName = 1; + required bytes Data = 2; + optional string ContentID = 3; +} + +message MailHeader { + required string name = 1; + required string value = 2; +} + +message MailMessage { + required string Sender = 1; + optional string ReplyTo = 2; + + repeated string To = 3; + repeated string Cc = 4; + repeated string Bcc = 5; + + required string Subject = 6; + + optional string TextBody = 7; + optional string HtmlBody = 8; + + repeated MailAttachment Attachment = 9; + + repeated MailHeader Header = 10; +} diff --git a/vendor/google.golang.org/appengine/internal/main.go b/vendor/google.golang.org/appengine/internal/main.go new file mode 100644 index 0000000000..49036163c2 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/main.go @@ -0,0 +1,15 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build appengine + +package internal + +import ( + "appengine_internal" +) + +func Main() { + appengine_internal.Main() +} diff --git a/vendor/google.golang.org/appengine/internal/main_vm.go b/vendor/google.golang.org/appengine/internal/main_vm.go new file mode 100644 index 0000000000..822e784a45 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/main_vm.go @@ -0,0 +1,48 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package internal + +import ( + "io" + "log" + "net/http" + "net/url" + "os" +) + +func Main() { + installHealthChecker(http.DefaultServeMux) + + port := "8080" + if s := os.Getenv("PORT"); s != "" { + port = s + } + + host := "" + if IsDevAppServer() { + host = "127.0.0.1" + } + if err := http.ListenAndServe(host+":"+port, http.HandlerFunc(handleHTTP)); err != nil { + log.Fatalf("http.ListenAndServe: %v", err) + } +} + +func installHealthChecker(mux *http.ServeMux) { + // If no health check handler has been installed by this point, add a trivial one. + const healthPath = "/_ah/health" + hreq := &http.Request{ + Method: "GET", + URL: &url.URL{ + Path: healthPath, + }, + } + if _, pat := mux.Handler(hreq); pat != healthPath { + mux.HandleFunc(healthPath, func(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, "ok") + }) + } +} diff --git a/vendor/google.golang.org/appengine/internal/memcache/memcache_service.pb.go b/vendor/google.golang.org/appengine/internal/memcache/memcache_service.pb.go new file mode 100644 index 0000000000..dfb00964c1 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/memcache/memcache_service.pb.go @@ -0,0 +1,1104 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/memcache/memcache_service.proto + +/* +Package memcache is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/memcache/memcache_service.proto + +It has these top-level messages: + MemcacheServiceError + AppOverride + MemcacheGetRequest + MemcacheGetResponse + MemcacheSetRequest + MemcacheSetResponse + MemcacheDeleteRequest + MemcacheDeleteResponse + MemcacheIncrementRequest + MemcacheIncrementResponse + MemcacheBatchIncrementRequest + MemcacheBatchIncrementResponse + MemcacheFlushRequest + MemcacheFlushResponse + MemcacheStatsRequest + MergedNamespaceStats + MemcacheStatsResponse + MemcacheGrabTailRequest + MemcacheGrabTailResponse +*/ +package memcache + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type MemcacheServiceError_ErrorCode int32 + +const ( + MemcacheServiceError_OK MemcacheServiceError_ErrorCode = 0 + MemcacheServiceError_UNSPECIFIED_ERROR MemcacheServiceError_ErrorCode = 1 + MemcacheServiceError_NAMESPACE_NOT_SET MemcacheServiceError_ErrorCode = 2 + MemcacheServiceError_PERMISSION_DENIED MemcacheServiceError_ErrorCode = 3 + MemcacheServiceError_INVALID_VALUE MemcacheServiceError_ErrorCode = 6 +) + +var MemcacheServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "UNSPECIFIED_ERROR", + 2: "NAMESPACE_NOT_SET", + 3: "PERMISSION_DENIED", + 6: "INVALID_VALUE", +} +var MemcacheServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "UNSPECIFIED_ERROR": 1, + "NAMESPACE_NOT_SET": 2, + "PERMISSION_DENIED": 3, + "INVALID_VALUE": 6, +} + +func (x MemcacheServiceError_ErrorCode) Enum() *MemcacheServiceError_ErrorCode { + p := new(MemcacheServiceError_ErrorCode) + *p = x + return p +} +func (x MemcacheServiceError_ErrorCode) String() string { + return proto.EnumName(MemcacheServiceError_ErrorCode_name, int32(x)) +} +func (x *MemcacheServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MemcacheServiceError_ErrorCode_value, data, "MemcacheServiceError_ErrorCode") + if err != nil { + return err + } + *x = MemcacheServiceError_ErrorCode(value) + return nil +} +func (MemcacheServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type MemcacheSetRequest_SetPolicy int32 + +const ( + MemcacheSetRequest_SET MemcacheSetRequest_SetPolicy = 1 + MemcacheSetRequest_ADD MemcacheSetRequest_SetPolicy = 2 + MemcacheSetRequest_REPLACE MemcacheSetRequest_SetPolicy = 3 + MemcacheSetRequest_CAS MemcacheSetRequest_SetPolicy = 4 +) + +var MemcacheSetRequest_SetPolicy_name = map[int32]string{ + 1: "SET", + 2: "ADD", + 3: "REPLACE", + 4: "CAS", +} +var MemcacheSetRequest_SetPolicy_value = map[string]int32{ + "SET": 1, + "ADD": 2, + "REPLACE": 3, + "CAS": 4, +} + +func (x MemcacheSetRequest_SetPolicy) Enum() *MemcacheSetRequest_SetPolicy { + p := new(MemcacheSetRequest_SetPolicy) + *p = x + return p +} +func (x MemcacheSetRequest_SetPolicy) String() string { + return proto.EnumName(MemcacheSetRequest_SetPolicy_name, int32(x)) +} +func (x *MemcacheSetRequest_SetPolicy) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MemcacheSetRequest_SetPolicy_value, data, "MemcacheSetRequest_SetPolicy") + if err != nil { + return err + } + *x = MemcacheSetRequest_SetPolicy(value) + return nil +} +func (MemcacheSetRequest_SetPolicy) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{4, 0} +} + +type MemcacheSetResponse_SetStatusCode int32 + +const ( + MemcacheSetResponse_STORED MemcacheSetResponse_SetStatusCode = 1 + MemcacheSetResponse_NOT_STORED MemcacheSetResponse_SetStatusCode = 2 + MemcacheSetResponse_ERROR MemcacheSetResponse_SetStatusCode = 3 + MemcacheSetResponse_EXISTS MemcacheSetResponse_SetStatusCode = 4 +) + +var MemcacheSetResponse_SetStatusCode_name = map[int32]string{ + 1: "STORED", + 2: "NOT_STORED", + 3: "ERROR", + 4: "EXISTS", +} +var MemcacheSetResponse_SetStatusCode_value = map[string]int32{ + "STORED": 1, + "NOT_STORED": 2, + "ERROR": 3, + "EXISTS": 4, +} + +func (x MemcacheSetResponse_SetStatusCode) Enum() *MemcacheSetResponse_SetStatusCode { + p := new(MemcacheSetResponse_SetStatusCode) + *p = x + return p +} +func (x MemcacheSetResponse_SetStatusCode) String() string { + return proto.EnumName(MemcacheSetResponse_SetStatusCode_name, int32(x)) +} +func (x *MemcacheSetResponse_SetStatusCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MemcacheSetResponse_SetStatusCode_value, data, "MemcacheSetResponse_SetStatusCode") + if err != nil { + return err + } + *x = MemcacheSetResponse_SetStatusCode(value) + return nil +} +func (MemcacheSetResponse_SetStatusCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{5, 0} +} + +type MemcacheDeleteResponse_DeleteStatusCode int32 + +const ( + MemcacheDeleteResponse_DELETED MemcacheDeleteResponse_DeleteStatusCode = 1 + MemcacheDeleteResponse_NOT_FOUND MemcacheDeleteResponse_DeleteStatusCode = 2 +) + +var MemcacheDeleteResponse_DeleteStatusCode_name = map[int32]string{ + 1: "DELETED", + 2: "NOT_FOUND", +} +var MemcacheDeleteResponse_DeleteStatusCode_value = map[string]int32{ + "DELETED": 1, + "NOT_FOUND": 2, +} + +func (x MemcacheDeleteResponse_DeleteStatusCode) Enum() *MemcacheDeleteResponse_DeleteStatusCode { + p := new(MemcacheDeleteResponse_DeleteStatusCode) + *p = x + return p +} +func (x MemcacheDeleteResponse_DeleteStatusCode) String() string { + return proto.EnumName(MemcacheDeleteResponse_DeleteStatusCode_name, int32(x)) +} +func (x *MemcacheDeleteResponse_DeleteStatusCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MemcacheDeleteResponse_DeleteStatusCode_value, data, "MemcacheDeleteResponse_DeleteStatusCode") + if err != nil { + return err + } + *x = MemcacheDeleteResponse_DeleteStatusCode(value) + return nil +} +func (MemcacheDeleteResponse_DeleteStatusCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{7, 0} +} + +type MemcacheIncrementRequest_Direction int32 + +const ( + MemcacheIncrementRequest_INCREMENT MemcacheIncrementRequest_Direction = 1 + MemcacheIncrementRequest_DECREMENT MemcacheIncrementRequest_Direction = 2 +) + +var MemcacheIncrementRequest_Direction_name = map[int32]string{ + 1: "INCREMENT", + 2: "DECREMENT", +} +var MemcacheIncrementRequest_Direction_value = map[string]int32{ + "INCREMENT": 1, + "DECREMENT": 2, +} + +func (x MemcacheIncrementRequest_Direction) Enum() *MemcacheIncrementRequest_Direction { + p := new(MemcacheIncrementRequest_Direction) + *p = x + return p +} +func (x MemcacheIncrementRequest_Direction) String() string { + return proto.EnumName(MemcacheIncrementRequest_Direction_name, int32(x)) +} +func (x *MemcacheIncrementRequest_Direction) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MemcacheIncrementRequest_Direction_value, data, "MemcacheIncrementRequest_Direction") + if err != nil { + return err + } + *x = MemcacheIncrementRequest_Direction(value) + return nil +} +func (MemcacheIncrementRequest_Direction) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{8, 0} +} + +type MemcacheIncrementResponse_IncrementStatusCode int32 + +const ( + MemcacheIncrementResponse_OK MemcacheIncrementResponse_IncrementStatusCode = 1 + MemcacheIncrementResponse_NOT_CHANGED MemcacheIncrementResponse_IncrementStatusCode = 2 + MemcacheIncrementResponse_ERROR MemcacheIncrementResponse_IncrementStatusCode = 3 +) + +var MemcacheIncrementResponse_IncrementStatusCode_name = map[int32]string{ + 1: "OK", + 2: "NOT_CHANGED", + 3: "ERROR", +} +var MemcacheIncrementResponse_IncrementStatusCode_value = map[string]int32{ + "OK": 1, + "NOT_CHANGED": 2, + "ERROR": 3, +} + +func (x MemcacheIncrementResponse_IncrementStatusCode) Enum() *MemcacheIncrementResponse_IncrementStatusCode { + p := new(MemcacheIncrementResponse_IncrementStatusCode) + *p = x + return p +} +func (x MemcacheIncrementResponse_IncrementStatusCode) String() string { + return proto.EnumName(MemcacheIncrementResponse_IncrementStatusCode_name, int32(x)) +} +func (x *MemcacheIncrementResponse_IncrementStatusCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MemcacheIncrementResponse_IncrementStatusCode_value, data, "MemcacheIncrementResponse_IncrementStatusCode") + if err != nil { + return err + } + *x = MemcacheIncrementResponse_IncrementStatusCode(value) + return nil +} +func (MemcacheIncrementResponse_IncrementStatusCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{9, 0} +} + +type MemcacheServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheServiceError) Reset() { *m = MemcacheServiceError{} } +func (m *MemcacheServiceError) String() string { return proto.CompactTextString(m) } +func (*MemcacheServiceError) ProtoMessage() {} +func (*MemcacheServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type AppOverride struct { + AppId *string `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + NumMemcachegBackends *int32 `protobuf:"varint,2,opt,name=num_memcacheg_backends,json=numMemcachegBackends" json:"num_memcacheg_backends,omitempty"` + IgnoreShardlock *bool `protobuf:"varint,3,opt,name=ignore_shardlock,json=ignoreShardlock" json:"ignore_shardlock,omitempty"` + MemcachePoolHint *string `protobuf:"bytes,4,opt,name=memcache_pool_hint,json=memcachePoolHint" json:"memcache_pool_hint,omitempty"` + MemcacheShardingStrategy []byte `protobuf:"bytes,5,opt,name=memcache_sharding_strategy,json=memcacheShardingStrategy" json:"memcache_sharding_strategy,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AppOverride) Reset() { *m = AppOverride{} } +func (m *AppOverride) String() string { return proto.CompactTextString(m) } +func (*AppOverride) ProtoMessage() {} +func (*AppOverride) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *AppOverride) GetAppId() string { + if m != nil && m.AppId != nil { + return *m.AppId + } + return "" +} + +func (m *AppOverride) GetNumMemcachegBackends() int32 { + if m != nil && m.NumMemcachegBackends != nil { + return *m.NumMemcachegBackends + } + return 0 +} + +func (m *AppOverride) GetIgnoreShardlock() bool { + if m != nil && m.IgnoreShardlock != nil { + return *m.IgnoreShardlock + } + return false +} + +func (m *AppOverride) GetMemcachePoolHint() string { + if m != nil && m.MemcachePoolHint != nil { + return *m.MemcachePoolHint + } + return "" +} + +func (m *AppOverride) GetMemcacheShardingStrategy() []byte { + if m != nil { + return m.MemcacheShardingStrategy + } + return nil +} + +type MemcacheGetRequest struct { + Key [][]byte `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"` + NameSpace *string `protobuf:"bytes,2,opt,name=name_space,json=nameSpace,def=" json:"name_space,omitempty"` + ForCas *bool `protobuf:"varint,4,opt,name=for_cas,json=forCas" json:"for_cas,omitempty"` + Override *AppOverride `protobuf:"bytes,5,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheGetRequest) Reset() { *m = MemcacheGetRequest{} } +func (m *MemcacheGetRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheGetRequest) ProtoMessage() {} +func (*MemcacheGetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *MemcacheGetRequest) GetKey() [][]byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *MemcacheGetRequest) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *MemcacheGetRequest) GetForCas() bool { + if m != nil && m.ForCas != nil { + return *m.ForCas + } + return false +} + +func (m *MemcacheGetRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MemcacheGetResponse struct { + Item []*MemcacheGetResponse_Item `protobuf:"group,1,rep,name=Item,json=item" json:"item,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheGetResponse) Reset() { *m = MemcacheGetResponse{} } +func (m *MemcacheGetResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheGetResponse) ProtoMessage() {} +func (*MemcacheGetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *MemcacheGetResponse) GetItem() []*MemcacheGetResponse_Item { + if m != nil { + return m.Item + } + return nil +} + +type MemcacheGetResponse_Item struct { + Key []byte `protobuf:"bytes,2,req,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,req,name=value" json:"value,omitempty"` + Flags *uint32 `protobuf:"fixed32,4,opt,name=flags" json:"flags,omitempty"` + CasId *uint64 `protobuf:"fixed64,5,opt,name=cas_id,json=casId" json:"cas_id,omitempty"` + ExpiresInSeconds *int32 `protobuf:"varint,6,opt,name=expires_in_seconds,json=expiresInSeconds" json:"expires_in_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheGetResponse_Item) Reset() { *m = MemcacheGetResponse_Item{} } +func (m *MemcacheGetResponse_Item) String() string { return proto.CompactTextString(m) } +func (*MemcacheGetResponse_Item) ProtoMessage() {} +func (*MemcacheGetResponse_Item) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} } + +func (m *MemcacheGetResponse_Item) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *MemcacheGetResponse_Item) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *MemcacheGetResponse_Item) GetFlags() uint32 { + if m != nil && m.Flags != nil { + return *m.Flags + } + return 0 +} + +func (m *MemcacheGetResponse_Item) GetCasId() uint64 { + if m != nil && m.CasId != nil { + return *m.CasId + } + return 0 +} + +func (m *MemcacheGetResponse_Item) GetExpiresInSeconds() int32 { + if m != nil && m.ExpiresInSeconds != nil { + return *m.ExpiresInSeconds + } + return 0 +} + +type MemcacheSetRequest struct { + Item []*MemcacheSetRequest_Item `protobuf:"group,1,rep,name=Item,json=item" json:"item,omitempty"` + NameSpace *string `protobuf:"bytes,7,opt,name=name_space,json=nameSpace,def=" json:"name_space,omitempty"` + Override *AppOverride `protobuf:"bytes,10,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheSetRequest) Reset() { *m = MemcacheSetRequest{} } +func (m *MemcacheSetRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheSetRequest) ProtoMessage() {} +func (*MemcacheSetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *MemcacheSetRequest) GetItem() []*MemcacheSetRequest_Item { + if m != nil { + return m.Item + } + return nil +} + +func (m *MemcacheSetRequest) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *MemcacheSetRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MemcacheSetRequest_Item struct { + Key []byte `protobuf:"bytes,2,req,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,req,name=value" json:"value,omitempty"` + Flags *uint32 `protobuf:"fixed32,4,opt,name=flags" json:"flags,omitempty"` + SetPolicy *MemcacheSetRequest_SetPolicy `protobuf:"varint,5,opt,name=set_policy,json=setPolicy,enum=appengine.MemcacheSetRequest_SetPolicy,def=1" json:"set_policy,omitempty"` + ExpirationTime *uint32 `protobuf:"fixed32,6,opt,name=expiration_time,json=expirationTime,def=0" json:"expiration_time,omitempty"` + CasId *uint64 `protobuf:"fixed64,8,opt,name=cas_id,json=casId" json:"cas_id,omitempty"` + ForCas *bool `protobuf:"varint,9,opt,name=for_cas,json=forCas" json:"for_cas,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheSetRequest_Item) Reset() { *m = MemcacheSetRequest_Item{} } +func (m *MemcacheSetRequest_Item) String() string { return proto.CompactTextString(m) } +func (*MemcacheSetRequest_Item) ProtoMessage() {} +func (*MemcacheSetRequest_Item) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4, 0} } + +const Default_MemcacheSetRequest_Item_SetPolicy MemcacheSetRequest_SetPolicy = MemcacheSetRequest_SET +const Default_MemcacheSetRequest_Item_ExpirationTime uint32 = 0 + +func (m *MemcacheSetRequest_Item) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *MemcacheSetRequest_Item) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *MemcacheSetRequest_Item) GetFlags() uint32 { + if m != nil && m.Flags != nil { + return *m.Flags + } + return 0 +} + +func (m *MemcacheSetRequest_Item) GetSetPolicy() MemcacheSetRequest_SetPolicy { + if m != nil && m.SetPolicy != nil { + return *m.SetPolicy + } + return Default_MemcacheSetRequest_Item_SetPolicy +} + +func (m *MemcacheSetRequest_Item) GetExpirationTime() uint32 { + if m != nil && m.ExpirationTime != nil { + return *m.ExpirationTime + } + return Default_MemcacheSetRequest_Item_ExpirationTime +} + +func (m *MemcacheSetRequest_Item) GetCasId() uint64 { + if m != nil && m.CasId != nil { + return *m.CasId + } + return 0 +} + +func (m *MemcacheSetRequest_Item) GetForCas() bool { + if m != nil && m.ForCas != nil { + return *m.ForCas + } + return false +} + +type MemcacheSetResponse struct { + SetStatus []MemcacheSetResponse_SetStatusCode `protobuf:"varint,1,rep,name=set_status,json=setStatus,enum=appengine.MemcacheSetResponse_SetStatusCode" json:"set_status,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheSetResponse) Reset() { *m = MemcacheSetResponse{} } +func (m *MemcacheSetResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheSetResponse) ProtoMessage() {} +func (*MemcacheSetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *MemcacheSetResponse) GetSetStatus() []MemcacheSetResponse_SetStatusCode { + if m != nil { + return m.SetStatus + } + return nil +} + +type MemcacheDeleteRequest struct { + Item []*MemcacheDeleteRequest_Item `protobuf:"group,1,rep,name=Item,json=item" json:"item,omitempty"` + NameSpace *string `protobuf:"bytes,4,opt,name=name_space,json=nameSpace,def=" json:"name_space,omitempty"` + Override *AppOverride `protobuf:"bytes,5,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheDeleteRequest) Reset() { *m = MemcacheDeleteRequest{} } +func (m *MemcacheDeleteRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheDeleteRequest) ProtoMessage() {} +func (*MemcacheDeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *MemcacheDeleteRequest) GetItem() []*MemcacheDeleteRequest_Item { + if m != nil { + return m.Item + } + return nil +} + +func (m *MemcacheDeleteRequest) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *MemcacheDeleteRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MemcacheDeleteRequest_Item struct { + Key []byte `protobuf:"bytes,2,req,name=key" json:"key,omitempty"` + DeleteTime *uint32 `protobuf:"fixed32,3,opt,name=delete_time,json=deleteTime,def=0" json:"delete_time,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheDeleteRequest_Item) Reset() { *m = MemcacheDeleteRequest_Item{} } +func (m *MemcacheDeleteRequest_Item) String() string { return proto.CompactTextString(m) } +func (*MemcacheDeleteRequest_Item) ProtoMessage() {} +func (*MemcacheDeleteRequest_Item) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6, 0} } + +const Default_MemcacheDeleteRequest_Item_DeleteTime uint32 = 0 + +func (m *MemcacheDeleteRequest_Item) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *MemcacheDeleteRequest_Item) GetDeleteTime() uint32 { + if m != nil && m.DeleteTime != nil { + return *m.DeleteTime + } + return Default_MemcacheDeleteRequest_Item_DeleteTime +} + +type MemcacheDeleteResponse struct { + DeleteStatus []MemcacheDeleteResponse_DeleteStatusCode `protobuf:"varint,1,rep,name=delete_status,json=deleteStatus,enum=appengine.MemcacheDeleteResponse_DeleteStatusCode" json:"delete_status,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheDeleteResponse) Reset() { *m = MemcacheDeleteResponse{} } +func (m *MemcacheDeleteResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheDeleteResponse) ProtoMessage() {} +func (*MemcacheDeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *MemcacheDeleteResponse) GetDeleteStatus() []MemcacheDeleteResponse_DeleteStatusCode { + if m != nil { + return m.DeleteStatus + } + return nil +} + +type MemcacheIncrementRequest struct { + Key []byte `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` + NameSpace *string `protobuf:"bytes,4,opt,name=name_space,json=nameSpace,def=" json:"name_space,omitempty"` + Delta *uint64 `protobuf:"varint,2,opt,name=delta,def=1" json:"delta,omitempty"` + Direction *MemcacheIncrementRequest_Direction `protobuf:"varint,3,opt,name=direction,enum=appengine.MemcacheIncrementRequest_Direction,def=1" json:"direction,omitempty"` + InitialValue *uint64 `protobuf:"varint,5,opt,name=initial_value,json=initialValue" json:"initial_value,omitempty"` + InitialFlags *uint32 `protobuf:"fixed32,6,opt,name=initial_flags,json=initialFlags" json:"initial_flags,omitempty"` + Override *AppOverride `protobuf:"bytes,7,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheIncrementRequest) Reset() { *m = MemcacheIncrementRequest{} } +func (m *MemcacheIncrementRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheIncrementRequest) ProtoMessage() {} +func (*MemcacheIncrementRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +const Default_MemcacheIncrementRequest_Delta uint64 = 1 +const Default_MemcacheIncrementRequest_Direction MemcacheIncrementRequest_Direction = MemcacheIncrementRequest_INCREMENT + +func (m *MemcacheIncrementRequest) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *MemcacheIncrementRequest) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *MemcacheIncrementRequest) GetDelta() uint64 { + if m != nil && m.Delta != nil { + return *m.Delta + } + return Default_MemcacheIncrementRequest_Delta +} + +func (m *MemcacheIncrementRequest) GetDirection() MemcacheIncrementRequest_Direction { + if m != nil && m.Direction != nil { + return *m.Direction + } + return Default_MemcacheIncrementRequest_Direction +} + +func (m *MemcacheIncrementRequest) GetInitialValue() uint64 { + if m != nil && m.InitialValue != nil { + return *m.InitialValue + } + return 0 +} + +func (m *MemcacheIncrementRequest) GetInitialFlags() uint32 { + if m != nil && m.InitialFlags != nil { + return *m.InitialFlags + } + return 0 +} + +func (m *MemcacheIncrementRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MemcacheIncrementResponse struct { + NewValue *uint64 `protobuf:"varint,1,opt,name=new_value,json=newValue" json:"new_value,omitempty"` + IncrementStatus *MemcacheIncrementResponse_IncrementStatusCode `protobuf:"varint,2,opt,name=increment_status,json=incrementStatus,enum=appengine.MemcacheIncrementResponse_IncrementStatusCode" json:"increment_status,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheIncrementResponse) Reset() { *m = MemcacheIncrementResponse{} } +func (m *MemcacheIncrementResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheIncrementResponse) ProtoMessage() {} +func (*MemcacheIncrementResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *MemcacheIncrementResponse) GetNewValue() uint64 { + if m != nil && m.NewValue != nil { + return *m.NewValue + } + return 0 +} + +func (m *MemcacheIncrementResponse) GetIncrementStatus() MemcacheIncrementResponse_IncrementStatusCode { + if m != nil && m.IncrementStatus != nil { + return *m.IncrementStatus + } + return MemcacheIncrementResponse_OK +} + +type MemcacheBatchIncrementRequest struct { + NameSpace *string `protobuf:"bytes,1,opt,name=name_space,json=nameSpace,def=" json:"name_space,omitempty"` + Item []*MemcacheIncrementRequest `protobuf:"bytes,2,rep,name=item" json:"item,omitempty"` + Override *AppOverride `protobuf:"bytes,3,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheBatchIncrementRequest) Reset() { *m = MemcacheBatchIncrementRequest{} } +func (m *MemcacheBatchIncrementRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheBatchIncrementRequest) ProtoMessage() {} +func (*MemcacheBatchIncrementRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *MemcacheBatchIncrementRequest) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *MemcacheBatchIncrementRequest) GetItem() []*MemcacheIncrementRequest { + if m != nil { + return m.Item + } + return nil +} + +func (m *MemcacheBatchIncrementRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MemcacheBatchIncrementResponse struct { + Item []*MemcacheIncrementResponse `protobuf:"bytes,1,rep,name=item" json:"item,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheBatchIncrementResponse) Reset() { *m = MemcacheBatchIncrementResponse{} } +func (m *MemcacheBatchIncrementResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheBatchIncrementResponse) ProtoMessage() {} +func (*MemcacheBatchIncrementResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *MemcacheBatchIncrementResponse) GetItem() []*MemcacheIncrementResponse { + if m != nil { + return m.Item + } + return nil +} + +type MemcacheFlushRequest struct { + Override *AppOverride `protobuf:"bytes,1,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheFlushRequest) Reset() { *m = MemcacheFlushRequest{} } +func (m *MemcacheFlushRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheFlushRequest) ProtoMessage() {} +func (*MemcacheFlushRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *MemcacheFlushRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MemcacheFlushResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheFlushResponse) Reset() { *m = MemcacheFlushResponse{} } +func (m *MemcacheFlushResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheFlushResponse) ProtoMessage() {} +func (*MemcacheFlushResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +type MemcacheStatsRequest struct { + Override *AppOverride `protobuf:"bytes,1,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheStatsRequest) Reset() { *m = MemcacheStatsRequest{} } +func (m *MemcacheStatsRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheStatsRequest) ProtoMessage() {} +func (*MemcacheStatsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *MemcacheStatsRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MergedNamespaceStats struct { + Hits *uint64 `protobuf:"varint,1,req,name=hits" json:"hits,omitempty"` + Misses *uint64 `protobuf:"varint,2,req,name=misses" json:"misses,omitempty"` + ByteHits *uint64 `protobuf:"varint,3,req,name=byte_hits,json=byteHits" json:"byte_hits,omitempty"` + Items *uint64 `protobuf:"varint,4,req,name=items" json:"items,omitempty"` + Bytes *uint64 `protobuf:"varint,5,req,name=bytes" json:"bytes,omitempty"` + OldestItemAge *uint32 `protobuf:"fixed32,6,req,name=oldest_item_age,json=oldestItemAge" json:"oldest_item_age,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MergedNamespaceStats) Reset() { *m = MergedNamespaceStats{} } +func (m *MergedNamespaceStats) String() string { return proto.CompactTextString(m) } +func (*MergedNamespaceStats) ProtoMessage() {} +func (*MergedNamespaceStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +func (m *MergedNamespaceStats) GetHits() uint64 { + if m != nil && m.Hits != nil { + return *m.Hits + } + return 0 +} + +func (m *MergedNamespaceStats) GetMisses() uint64 { + if m != nil && m.Misses != nil { + return *m.Misses + } + return 0 +} + +func (m *MergedNamespaceStats) GetByteHits() uint64 { + if m != nil && m.ByteHits != nil { + return *m.ByteHits + } + return 0 +} + +func (m *MergedNamespaceStats) GetItems() uint64 { + if m != nil && m.Items != nil { + return *m.Items + } + return 0 +} + +func (m *MergedNamespaceStats) GetBytes() uint64 { + if m != nil && m.Bytes != nil { + return *m.Bytes + } + return 0 +} + +func (m *MergedNamespaceStats) GetOldestItemAge() uint32 { + if m != nil && m.OldestItemAge != nil { + return *m.OldestItemAge + } + return 0 +} + +type MemcacheStatsResponse struct { + Stats *MergedNamespaceStats `protobuf:"bytes,1,opt,name=stats" json:"stats,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheStatsResponse) Reset() { *m = MemcacheStatsResponse{} } +func (m *MemcacheStatsResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheStatsResponse) ProtoMessage() {} +func (*MemcacheStatsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *MemcacheStatsResponse) GetStats() *MergedNamespaceStats { + if m != nil { + return m.Stats + } + return nil +} + +type MemcacheGrabTailRequest struct { + ItemCount *int32 `protobuf:"varint,1,req,name=item_count,json=itemCount" json:"item_count,omitempty"` + NameSpace *string `protobuf:"bytes,2,opt,name=name_space,json=nameSpace,def=" json:"name_space,omitempty"` + Override *AppOverride `protobuf:"bytes,3,opt,name=override" json:"override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheGrabTailRequest) Reset() { *m = MemcacheGrabTailRequest{} } +func (m *MemcacheGrabTailRequest) String() string { return proto.CompactTextString(m) } +func (*MemcacheGrabTailRequest) ProtoMessage() {} +func (*MemcacheGrabTailRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *MemcacheGrabTailRequest) GetItemCount() int32 { + if m != nil && m.ItemCount != nil { + return *m.ItemCount + } + return 0 +} + +func (m *MemcacheGrabTailRequest) GetNameSpace() string { + if m != nil && m.NameSpace != nil { + return *m.NameSpace + } + return "" +} + +func (m *MemcacheGrabTailRequest) GetOverride() *AppOverride { + if m != nil { + return m.Override + } + return nil +} + +type MemcacheGrabTailResponse struct { + Item []*MemcacheGrabTailResponse_Item `protobuf:"group,1,rep,name=Item,json=item" json:"item,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheGrabTailResponse) Reset() { *m = MemcacheGrabTailResponse{} } +func (m *MemcacheGrabTailResponse) String() string { return proto.CompactTextString(m) } +func (*MemcacheGrabTailResponse) ProtoMessage() {} +func (*MemcacheGrabTailResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *MemcacheGrabTailResponse) GetItem() []*MemcacheGrabTailResponse_Item { + if m != nil { + return m.Item + } + return nil +} + +type MemcacheGrabTailResponse_Item struct { + Value []byte `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` + Flags *uint32 `protobuf:"fixed32,3,opt,name=flags" json:"flags,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MemcacheGrabTailResponse_Item) Reset() { *m = MemcacheGrabTailResponse_Item{} } +func (m *MemcacheGrabTailResponse_Item) String() string { return proto.CompactTextString(m) } +func (*MemcacheGrabTailResponse_Item) ProtoMessage() {} +func (*MemcacheGrabTailResponse_Item) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{18, 0} +} + +func (m *MemcacheGrabTailResponse_Item) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *MemcacheGrabTailResponse_Item) GetFlags() uint32 { + if m != nil && m.Flags != nil { + return *m.Flags + } + return 0 +} + +func init() { + proto.RegisterType((*MemcacheServiceError)(nil), "appengine.MemcacheServiceError") + proto.RegisterType((*AppOverride)(nil), "appengine.AppOverride") + proto.RegisterType((*MemcacheGetRequest)(nil), "appengine.MemcacheGetRequest") + proto.RegisterType((*MemcacheGetResponse)(nil), "appengine.MemcacheGetResponse") + proto.RegisterType((*MemcacheGetResponse_Item)(nil), "appengine.MemcacheGetResponse.Item") + proto.RegisterType((*MemcacheSetRequest)(nil), "appengine.MemcacheSetRequest") + proto.RegisterType((*MemcacheSetRequest_Item)(nil), "appengine.MemcacheSetRequest.Item") + proto.RegisterType((*MemcacheSetResponse)(nil), "appengine.MemcacheSetResponse") + proto.RegisterType((*MemcacheDeleteRequest)(nil), "appengine.MemcacheDeleteRequest") + proto.RegisterType((*MemcacheDeleteRequest_Item)(nil), "appengine.MemcacheDeleteRequest.Item") + proto.RegisterType((*MemcacheDeleteResponse)(nil), "appengine.MemcacheDeleteResponse") + proto.RegisterType((*MemcacheIncrementRequest)(nil), "appengine.MemcacheIncrementRequest") + proto.RegisterType((*MemcacheIncrementResponse)(nil), "appengine.MemcacheIncrementResponse") + proto.RegisterType((*MemcacheBatchIncrementRequest)(nil), "appengine.MemcacheBatchIncrementRequest") + proto.RegisterType((*MemcacheBatchIncrementResponse)(nil), "appengine.MemcacheBatchIncrementResponse") + proto.RegisterType((*MemcacheFlushRequest)(nil), "appengine.MemcacheFlushRequest") + proto.RegisterType((*MemcacheFlushResponse)(nil), "appengine.MemcacheFlushResponse") + proto.RegisterType((*MemcacheStatsRequest)(nil), "appengine.MemcacheStatsRequest") + proto.RegisterType((*MergedNamespaceStats)(nil), "appengine.MergedNamespaceStats") + proto.RegisterType((*MemcacheStatsResponse)(nil), "appengine.MemcacheStatsResponse") + proto.RegisterType((*MemcacheGrabTailRequest)(nil), "appengine.MemcacheGrabTailRequest") + proto.RegisterType((*MemcacheGrabTailResponse)(nil), "appengine.MemcacheGrabTailResponse") + proto.RegisterType((*MemcacheGrabTailResponse_Item)(nil), "appengine.MemcacheGrabTailResponse.Item") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/memcache/memcache_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 1379 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcd, 0x92, 0xdb, 0xc4, + 0x16, 0x8e, 0x24, 0xff, 0xe9, 0x78, 0x7e, 0x94, 0xce, 0x64, 0xe2, 0x3b, 0xb7, 0x72, 0xe3, 0x52, + 0xee, 0xbd, 0x18, 0x2a, 0x71, 0x82, 0x29, 0x20, 0x99, 0xca, 0x02, 0x8f, 0xad, 0x49, 0x44, 0x66, + 0xec, 0xa9, 0x96, 0x33, 0x50, 0xd9, 0xa8, 0x3a, 0x72, 0x47, 0xa3, 0x1a, 0x59, 0x12, 0x6a, 0x39, + 0x21, 0x4b, 0x8a, 0x15, 0x55, 0xb0, 0xe3, 0x05, 0xd8, 0xb0, 0x63, 0xc5, 0x3b, 0xf0, 0x0c, 0x14, + 0x7b, 0x8a, 0x15, 0xef, 0x40, 0x75, 0x4b, 0xb2, 0x65, 0x8f, 0x67, 0x98, 0x02, 0x76, 0x3a, 0xa7, + 0x4f, 0xab, 0xcf, 0x77, 0xbe, 0xaf, 0x4f, 0x1f, 0xe8, 0xbb, 0x61, 0xe8, 0xfa, 0xb4, 0xed, 0x86, + 0x3e, 0x09, 0xdc, 0x76, 0x18, 0xbb, 0xf7, 0x48, 0x14, 0xd1, 0xc0, 0xf5, 0x02, 0x7a, 0xcf, 0x0b, + 0x12, 0x1a, 0x07, 0xc4, 0xbf, 0x37, 0xa1, 0x13, 0x87, 0x38, 0x27, 0x74, 0xf6, 0x61, 0x33, 0x1a, + 0xbf, 0xf2, 0x1c, 0xda, 0x8e, 0xe2, 0x30, 0x09, 0x91, 0x3a, 0xdb, 0xa3, 0x7f, 0x29, 0xc1, 0xd6, + 0x61, 0x16, 0x65, 0xa5, 0x41, 0x46, 0x1c, 0x87, 0xb1, 0x7e, 0x0a, 0xaa, 0xf8, 0xe8, 0x85, 0x63, + 0x8a, 0x2a, 0x20, 0x0f, 0x9f, 0x6a, 0x57, 0xd0, 0x75, 0xb8, 0xfa, 0x6c, 0x60, 0x1d, 0x19, 0x3d, + 0x73, 0xdf, 0x34, 0xfa, 0xb6, 0x81, 0xf1, 0x10, 0x6b, 0x12, 0x77, 0x0f, 0xba, 0x87, 0x86, 0x75, + 0xd4, 0xed, 0x19, 0xf6, 0x60, 0x38, 0xb2, 0x2d, 0x63, 0xa4, 0xc9, 0xdc, 0x7d, 0x64, 0xe0, 0x43, + 0xd3, 0xb2, 0xcc, 0xe1, 0xc0, 0xee, 0x1b, 0x03, 0xd3, 0xe8, 0x6b, 0x0a, 0xba, 0x0a, 0xeb, 0xe6, + 0xe0, 0xb8, 0x7b, 0x60, 0xf6, 0xed, 0xe3, 0xee, 0xc1, 0x33, 0x43, 0xab, 0xe8, 0x5f, 0xc8, 0x50, + 0xef, 0x46, 0xd1, 0xf0, 0x15, 0x8d, 0x63, 0x6f, 0x4c, 0xd1, 0x75, 0xa8, 0x90, 0x28, 0xb2, 0xbd, + 0x71, 0x43, 0x6a, 0xca, 0x2d, 0x15, 0x97, 0x49, 0x14, 0x99, 0x63, 0xf4, 0x00, 0xb6, 0x83, 0xe9, + 0xc4, 0xce, 0x51, 0xb9, 0xf6, 0x0b, 0xe2, 0x9c, 0xd2, 0x60, 0xcc, 0x1a, 0x72, 0x53, 0x6a, 0x95, + 0xf7, 0xe4, 0x86, 0x84, 0xb7, 0x82, 0xe9, 0x24, 0x07, 0xe4, 0xee, 0x65, 0xeb, 0xe8, 0x2e, 0x68, + 0x9e, 0x1b, 0x84, 0x31, 0xb5, 0xd9, 0x09, 0x89, 0xc7, 0x7e, 0xe8, 0x9c, 0x36, 0x94, 0xa6, 0xd4, + 0xaa, 0x89, 0x3d, 0x9b, 0xe9, 0x9a, 0x95, 0x2f, 0xa1, 0xfb, 0x80, 0x66, 0xa5, 0x8b, 0xc2, 0xd0, + 0xb7, 0x4f, 0xbc, 0x20, 0x69, 0x94, 0x9a, 0x52, 0x4b, 0x15, 0x1b, 0xb4, 0x7c, 0xf5, 0x28, 0x0c, + 0xfd, 0x27, 0x5e, 0x90, 0xa0, 0x8f, 0x60, 0x67, 0x5e, 0x6c, 0xfe, 0x1f, 0x2f, 0x70, 0x6d, 0x96, + 0xc4, 0x24, 0xa1, 0xee, 0x9b, 0x46, 0xb9, 0x29, 0xb5, 0xd6, 0xc4, 0xce, 0x46, 0x1e, 0x65, 0x65, + 0x41, 0x56, 0x16, 0xa3, 0x7f, 0x2b, 0x01, 0xca, 0x13, 0x7f, 0x4c, 0x13, 0x4c, 0x3f, 0x9b, 0x52, + 0x96, 0x20, 0x0d, 0x94, 0x53, 0xfa, 0xa6, 0x21, 0x35, 0x95, 0xd6, 0x1a, 0xe6, 0x9f, 0xe8, 0x16, + 0x40, 0x40, 0x26, 0xd4, 0x66, 0x11, 0x71, 0xa8, 0x40, 0xae, 0xee, 0x5e, 0xc1, 0x2a, 0xf7, 0x59, + 0xdc, 0x85, 0x6e, 0x40, 0xf5, 0x65, 0x18, 0xdb, 0x0e, 0x61, 0x22, 0xe5, 0x1a, 0xae, 0xbc, 0x0c, + 0xe3, 0x1e, 0x61, 0xa8, 0x03, 0xb5, 0x30, 0x2b, 0xb1, 0x48, 0xa9, 0xde, 0xd9, 0x6e, 0xcf, 0xa4, + 0xd0, 0x2e, 0x10, 0x80, 0x67, 0x71, 0xfa, 0x2f, 0x12, 0x5c, 0x5b, 0x48, 0x8b, 0x45, 0x61, 0xc0, + 0x28, 0xfa, 0x10, 0x4a, 0x5e, 0x42, 0x27, 0x22, 0x31, 0xe8, 0xdc, 0x2e, 0xfc, 0x67, 0x45, 0x74, + 0xdb, 0x4c, 0xe8, 0x04, 0x8b, 0x0d, 0x3b, 0x5f, 0x49, 0x50, 0xe2, 0x66, 0x8e, 0x4c, 0x6e, 0xca, + 0x39, 0xb2, 0x2d, 0x28, 0xbf, 0x22, 0xfe, 0x94, 0x36, 0x14, 0xe1, 0x4b, 0x0d, 0xee, 0x7d, 0xe9, + 0x13, 0x37, 0x05, 0x53, 0xc5, 0xa9, 0xc1, 0x25, 0xe2, 0x10, 0xc6, 0x25, 0xc2, 0x91, 0x54, 0x70, + 0xd9, 0x21, 0xcc, 0x1c, 0xa3, 0x3b, 0x80, 0xe8, 0xe7, 0x91, 0x17, 0x53, 0x66, 0x7b, 0x81, 0xcd, + 0xa8, 0x13, 0x72, 0x79, 0x54, 0xb8, 0x3c, 0xb0, 0x96, 0xad, 0x98, 0x81, 0x95, 0xfa, 0xf5, 0x9f, + 0x94, 0x79, 0xcd, 0xad, 0x79, 0xcd, 0x3f, 0x58, 0xc0, 0xa6, 0xaf, 0xc0, 0x36, 0x0f, 0x2e, 0x40, + 0x5b, 0x62, 0xa6, 0x7a, 0x96, 0x99, 0x22, 0x01, 0x70, 0x39, 0x02, 0x76, 0x7e, 0xff, 0x67, 0xea, + 0xf5, 0x14, 0x80, 0xd1, 0xc4, 0x8e, 0x42, 0xdf, 0x73, 0x52, 0x41, 0x6e, 0x74, 0xde, 0xba, 0x18, + 0x99, 0x45, 0x93, 0x23, 0x11, 0xbe, 0xab, 0x58, 0xc6, 0x08, 0xab, 0x2c, 0xb7, 0xd1, 0x3b, 0xb0, + 0x29, 0x6a, 0x49, 0x12, 0x2f, 0x0c, 0xec, 0xc4, 0x9b, 0x50, 0x51, 0xe2, 0xea, 0xae, 0x74, 0x1f, + 0x6f, 0xcc, 0x57, 0x46, 0xde, 0x84, 0x16, 0x88, 0xaa, 0x15, 0x89, 0x2a, 0x88, 0x54, 0x2d, 0x8a, + 0x54, 0x7f, 0x0f, 0xd4, 0xd9, 0xc1, 0xa8, 0x0a, 0xfc, 0x68, 0x4d, 0xe2, 0x1f, 0xdd, 0x7e, 0x5f, + 0x93, 0x51, 0x1d, 0xaa, 0xd8, 0x38, 0x3a, 0xe8, 0xf6, 0x0c, 0x4d, 0xe1, 0xde, 0x5e, 0xd7, 0xd2, + 0x4a, 0xfa, 0xf7, 0x05, 0x95, 0x5a, 0x05, 0x95, 0x66, 0xa8, 0x59, 0x42, 0x92, 0x29, 0x13, 0x7c, + 0x6e, 0x74, 0xee, 0x9c, 0x87, 0x3a, 0xd3, 0xaa, 0x45, 0x13, 0x4b, 0xc4, 0xf3, 0xd6, 0x27, 0x50, + 0xa7, 0xa6, 0xbe, 0x07, 0xeb, 0x0b, 0x6b, 0x08, 0xa0, 0x62, 0x8d, 0x86, 0xd8, 0xe8, 0x6b, 0x12, + 0xda, 0x00, 0x10, 0x9d, 0x2f, 0xb5, 0x65, 0xa4, 0x42, 0x39, 0x6d, 0x8f, 0x0a, 0x0f, 0x33, 0x3e, + 0x35, 0xad, 0x11, 0x4f, 0xf4, 0x57, 0x09, 0xae, 0xe7, 0x87, 0xf6, 0xa9, 0x4f, 0x13, 0x9a, 0x8b, + 0xee, 0xe1, 0x82, 0xe8, 0xfe, 0xb7, 0x22, 0xc9, 0x85, 0xf8, 0xf3, 0x75, 0x57, 0xba, 0x58, 0x77, + 0x97, 0xbc, 0xf8, 0x3b, 0x8f, 0xce, 0x95, 0x9d, 0x0e, 0xf5, 0xb1, 0x48, 0x25, 0x65, 0x5e, 0xc9, + 0x99, 0x87, 0xd4, 0xcb, 0x59, 0xd7, 0xbf, 0x93, 0x60, 0x7b, 0x39, 0xef, 0x8c, 0x93, 0x4f, 0x60, + 0x3d, 0xdb, 0xbe, 0x40, 0x4b, 0xe7, 0x02, 0xc4, 0x19, 0x33, 0xa9, 0x59, 0x20, 0x67, 0x6d, 0x5c, + 0xf0, 0xe8, 0x6d, 0xd0, 0x96, 0x23, 0xb8, 0x5c, 0xfa, 0xc6, 0x81, 0x31, 0x12, 0x1c, 0xad, 0x83, + 0xca, 0x39, 0xda, 0x1f, 0x3e, 0x1b, 0xf4, 0x35, 0x59, 0xff, 0x4d, 0x86, 0x46, 0x7e, 0x92, 0x19, + 0x38, 0x31, 0x9d, 0xd0, 0xe0, 0x6c, 0xdf, 0x95, 0x57, 0xf7, 0xdd, 0xd2, 0xaa, 0xbe, 0x5b, 0x1e, + 0x53, 0x3f, 0x21, 0xa2, 0x27, 0x97, 0x76, 0xa5, 0x77, 0x71, 0x6a, 0xa3, 0x63, 0x50, 0xc7, 0x5e, + 0x4c, 0x1d, 0x7e, 0x27, 0x44, 0xb9, 0x36, 0x3a, 0x77, 0x57, 0xa0, 0x5d, 0xce, 0xa1, 0xdd, 0xcf, + 0x37, 0xed, 0xaa, 0xe6, 0xa0, 0x87, 0x8d, 0x43, 0x63, 0x30, 0xc2, 0xf3, 0x5f, 0xa1, 0xdb, 0xb0, + 0xee, 0x05, 0x5e, 0xe2, 0x11, 0xdf, 0x4e, 0xfb, 0x00, 0xe7, 0xb6, 0x84, 0xd7, 0x32, 0xe7, 0xb1, + 0x68, 0x07, 0x85, 0xa0, 0xb4, 0x2d, 0x88, 0x9b, 0x3a, 0x0b, 0xda, 0x17, 0xdd, 0xa1, 0x28, 0x90, + 0xea, 0x25, 0x5f, 0x86, 0xb7, 0x41, 0x9d, 0x25, 0xc8, 0x4b, 0x3b, 0x4b, 0x31, 0xad, 0x74, 0xdf, + 0xc8, 0x4d, 0x59, 0xff, 0x59, 0x82, 0x7f, 0xad, 0x40, 0x99, 0x09, 0xe2, 0xdf, 0xa0, 0x06, 0xf4, + 0x75, 0x06, 0x41, 0x12, 0x10, 0x6a, 0x01, 0x7d, 0x9d, 0xa6, 0xef, 0x80, 0xe6, 0xe5, 0x3b, 0x72, + 0xc1, 0xc8, 0xa2, 0x84, 0x0f, 0x2e, 0x2e, 0x61, 0xfe, 0xf2, 0xe4, 0x9e, 0x82, 0x6c, 0x36, 0xbd, + 0x45, 0xa7, 0xfe, 0x10, 0xae, 0xad, 0x88, 0xcb, 0xc6, 0x1e, 0x09, 0x6d, 0x42, 0x9d, 0xeb, 0xa6, + 0xf7, 0xa4, 0x3b, 0x78, 0xbc, 0x74, 0xb9, 0xf5, 0x1f, 0x24, 0xb8, 0x99, 0x9f, 0xbe, 0x47, 0x12, + 0xe7, 0xe4, 0x8c, 0x92, 0x16, 0x75, 0x23, 0x9d, 0xd5, 0x4d, 0xfe, 0x94, 0xca, 0x4d, 0xa5, 0x55, + 0x5f, 0xf9, 0x94, 0x2e, 0xff, 0x33, 0xbb, 0xf7, 0x45, 0xd6, 0x94, 0x4b, 0xb2, 0xf6, 0x1c, 0xfe, + 0x73, 0x5e, 0xba, 0x19, 0x1d, 0x0f, 0x0a, 0x8d, 0xa8, 0xde, 0xf9, 0xef, 0x65, 0xaa, 0x9c, 0xe6, + 0xa3, 0x7f, 0x3c, 0x9f, 0x25, 0xf7, 0xfd, 0x29, 0x3b, 0xc9, 0x2b, 0x50, 0xcc, 0x53, 0xba, 0x64, + 0x9e, 0x37, 0xe6, 0x7d, 0x32, 0xfb, 0x57, 0x7a, 0x54, 0xf1, 0x10, 0x4e, 0x15, 0xfb, 0x3b, 0x87, + 0xfc, 0x28, 0xa6, 0xdf, 0xd8, 0xa5, 0xe3, 0x01, 0x99, 0x50, 0x41, 0x90, 0xf8, 0x27, 0x42, 0x50, + 0x3a, 0xf1, 0x12, 0x26, 0xae, 0x7f, 0x09, 0x8b, 0x6f, 0xb4, 0x0d, 0x95, 0x89, 0xc7, 0x18, 0x65, + 0xa2, 0x17, 0x96, 0x70, 0x66, 0x71, 0xf9, 0xbe, 0x78, 0x93, 0x50, 0x5b, 0x6c, 0x50, 0xc4, 0x52, + 0x8d, 0x3b, 0x9e, 0xf0, 0x4d, 0x5b, 0x50, 0xe6, 0xa5, 0xe1, 0x8f, 0x31, 0x5f, 0x48, 0x0d, 0xee, + 0xe5, 0x11, 0xac, 0x51, 0x4e, 0xbd, 0xc2, 0x40, 0xff, 0x87, 0xcd, 0xd0, 0x1f, 0x53, 0x96, 0xd8, + 0x3c, 0xca, 0x26, 0x2e, 0x7f, 0x55, 0xe5, 0x56, 0x15, 0xaf, 0xa7, 0x6e, 0xde, 0x8e, 0xbb, 0x2e, + 0xd5, 0x07, 0xf3, 0xd2, 0x64, 0x15, 0xc8, 0x98, 0x7b, 0x1f, 0xca, 0xfc, 0x86, 0xb0, 0x0c, 0xff, + 0xad, 0x05, 0xea, 0xce, 0xa2, 0xc4, 0x69, 0xb4, 0xfe, 0x8d, 0x04, 0x37, 0x66, 0x43, 0x5b, 0x4c, + 0x5e, 0x8c, 0x88, 0xe7, 0xe7, 0x55, 0xbd, 0x09, 0x20, 0x92, 0x71, 0xc2, 0x69, 0x90, 0x88, 0x72, + 0x94, 0xb1, 0xca, 0x3d, 0x3d, 0xee, 0xf8, 0xf3, 0x59, 0xf4, 0xaf, 0x48, 0xf4, 0x6b, 0x69, 0xde, + 0x97, 0xe7, 0xf9, 0x64, 0x18, 0x1f, 0x2d, 0x3c, 0x93, 0xad, 0x55, 0x73, 0xe7, 0xd2, 0x96, 0xe2, + 0xf0, 0xd9, 0xc9, 0x1e, 0xb5, 0xd9, 0xe4, 0x24, 0xaf, 0x9c, 0x9c, 0x94, 0xc2, 0xe4, 0xb4, 0x07, + 0xcf, 0x6b, 0xf9, 0xd0, 0xfe, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0x76, 0x8b, 0xe6, 0x6b, 0x80, + 0x0d, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/memcache/memcache_service.proto b/vendor/google.golang.org/appengine/internal/memcache/memcache_service.proto new file mode 100644 index 0000000000..5f0edcdc7b --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/memcache/memcache_service.proto @@ -0,0 +1,165 @@ +syntax = "proto2"; +option go_package = "memcache"; + +package appengine; + +message MemcacheServiceError { + enum ErrorCode { + OK = 0; + UNSPECIFIED_ERROR = 1; + NAMESPACE_NOT_SET = 2; + PERMISSION_DENIED = 3; + INVALID_VALUE = 6; + } +} + +message AppOverride { + required string app_id = 1; + + optional int32 num_memcacheg_backends = 2 [deprecated=true]; + optional bool ignore_shardlock = 3 [deprecated=true]; + optional string memcache_pool_hint = 4 [deprecated=true]; + optional bytes memcache_sharding_strategy = 5 [deprecated=true]; +} + +message MemcacheGetRequest { + repeated bytes key = 1; + optional string name_space = 2 [default = ""]; + optional bool for_cas = 4; + optional AppOverride override = 5; +} + +message MemcacheGetResponse { + repeated group Item = 1 { + required bytes key = 2; + required bytes value = 3; + optional fixed32 flags = 4; + optional fixed64 cas_id = 5; + optional int32 expires_in_seconds = 6; + } +} + +message MemcacheSetRequest { + enum SetPolicy { + SET = 1; + ADD = 2; + REPLACE = 3; + CAS = 4; + } + repeated group Item = 1 { + required bytes key = 2; + required bytes value = 3; + + optional fixed32 flags = 4; + optional SetPolicy set_policy = 5 [default = SET]; + optional fixed32 expiration_time = 6 [default = 0]; + + optional fixed64 cas_id = 8; + optional bool for_cas = 9; + } + optional string name_space = 7 [default = ""]; + optional AppOverride override = 10; +} + +message MemcacheSetResponse { + enum SetStatusCode { + STORED = 1; + NOT_STORED = 2; + ERROR = 3; + EXISTS = 4; + } + repeated SetStatusCode set_status = 1; +} + +message MemcacheDeleteRequest { + repeated group Item = 1 { + required bytes key = 2; + optional fixed32 delete_time = 3 [default = 0]; + } + optional string name_space = 4 [default = ""]; + optional AppOverride override = 5; +} + +message MemcacheDeleteResponse { + enum DeleteStatusCode { + DELETED = 1; + NOT_FOUND = 2; + } + repeated DeleteStatusCode delete_status = 1; +} + +message MemcacheIncrementRequest { + enum Direction { + INCREMENT = 1; + DECREMENT = 2; + } + required bytes key = 1; + optional string name_space = 4 [default = ""]; + + optional uint64 delta = 2 [default = 1]; + optional Direction direction = 3 [default = INCREMENT]; + + optional uint64 initial_value = 5; + optional fixed32 initial_flags = 6; + optional AppOverride override = 7; +} + +message MemcacheIncrementResponse { + enum IncrementStatusCode { + OK = 1; + NOT_CHANGED = 2; + ERROR = 3; + } + + optional uint64 new_value = 1; + optional IncrementStatusCode increment_status = 2; +} + +message MemcacheBatchIncrementRequest { + optional string name_space = 1 [default = ""]; + repeated MemcacheIncrementRequest item = 2; + optional AppOverride override = 3; +} + +message MemcacheBatchIncrementResponse { + repeated MemcacheIncrementResponse item = 1; +} + +message MemcacheFlushRequest { + optional AppOverride override = 1; +} + +message MemcacheFlushResponse { +} + +message MemcacheStatsRequest { + optional AppOverride override = 1; +} + +message MergedNamespaceStats { + required uint64 hits = 1; + required uint64 misses = 2; + required uint64 byte_hits = 3; + + required uint64 items = 4; + required uint64 bytes = 5; + + required fixed32 oldest_item_age = 6; +} + +message MemcacheStatsResponse { + optional MergedNamespaceStats stats = 1; +} + +message MemcacheGrabTailRequest { + required int32 item_count = 1; + optional string name_space = 2 [default = ""]; + optional AppOverride override = 3; +} + +message MemcacheGrabTailResponse { + repeated group Item = 1 { + required bytes value = 2; + optional fixed32 flags = 3; + } +} diff --git a/vendor/google.golang.org/appengine/internal/metadata.go b/vendor/google.golang.org/appengine/internal/metadata.go new file mode 100644 index 0000000000..9cc1f71d10 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/metadata.go @@ -0,0 +1,61 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package internal + +// This file has code for accessing metadata. +// +// References: +// https://cloud.google.com/compute/docs/metadata + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" +) + +const ( + metadataHost = "metadata" + metadataPath = "/computeMetadata/v1/" +) + +var ( + metadataRequestHeaders = http.Header{ + "Metadata-Flavor": []string{"Google"}, + } +) + +// TODO(dsymonds): Do we need to support default values, like Python? +func mustGetMetadata(key string) []byte { + b, err := getMetadata(key) + if err != nil { + log.Fatalf("Metadata fetch failed: %v", err) + } + return b +} + +func getMetadata(key string) ([]byte, error) { + // TODO(dsymonds): May need to use url.Parse to support keys with query args. + req := &http.Request{ + Method: "GET", + URL: &url.URL{ + Scheme: "http", + Host: metadataHost, + Path: metadataPath + key, + }, + Header: metadataRequestHeaders, + Host: metadataHost, + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + return nil, fmt.Errorf("metadata server returned HTTP %d", resp.StatusCode) + } + return ioutil.ReadAll(resp.Body) +} diff --git a/vendor/google.golang.org/appengine/internal/modules/modules_service.pb.go b/vendor/google.golang.org/appengine/internal/modules/modules_service.pb.go new file mode 100644 index 0000000000..518005254b --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/modules/modules_service.pb.go @@ -0,0 +1,454 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/modules/modules_service.proto + +/* +Package modules is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/modules/modules_service.proto + +It has these top-level messages: + ModulesServiceError + GetModulesRequest + GetModulesResponse + GetVersionsRequest + GetVersionsResponse + GetDefaultVersionRequest + GetDefaultVersionResponse + GetNumInstancesRequest + GetNumInstancesResponse + SetNumInstancesRequest + SetNumInstancesResponse + StartModuleRequest + StartModuleResponse + StopModuleRequest + StopModuleResponse + GetHostnameRequest + GetHostnameResponse +*/ +package modules + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type ModulesServiceError_ErrorCode int32 + +const ( + ModulesServiceError_OK ModulesServiceError_ErrorCode = 0 + ModulesServiceError_INVALID_MODULE ModulesServiceError_ErrorCode = 1 + ModulesServiceError_INVALID_VERSION ModulesServiceError_ErrorCode = 2 + ModulesServiceError_INVALID_INSTANCES ModulesServiceError_ErrorCode = 3 + ModulesServiceError_TRANSIENT_ERROR ModulesServiceError_ErrorCode = 4 + ModulesServiceError_UNEXPECTED_STATE ModulesServiceError_ErrorCode = 5 +) + +var ModulesServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INVALID_MODULE", + 2: "INVALID_VERSION", + 3: "INVALID_INSTANCES", + 4: "TRANSIENT_ERROR", + 5: "UNEXPECTED_STATE", +} +var ModulesServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INVALID_MODULE": 1, + "INVALID_VERSION": 2, + "INVALID_INSTANCES": 3, + "TRANSIENT_ERROR": 4, + "UNEXPECTED_STATE": 5, +} + +func (x ModulesServiceError_ErrorCode) Enum() *ModulesServiceError_ErrorCode { + p := new(ModulesServiceError_ErrorCode) + *p = x + return p +} +func (x ModulesServiceError_ErrorCode) String() string { + return proto.EnumName(ModulesServiceError_ErrorCode_name, int32(x)) +} +func (x *ModulesServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ModulesServiceError_ErrorCode_value, data, "ModulesServiceError_ErrorCode") + if err != nil { + return err + } + *x = ModulesServiceError_ErrorCode(value) + return nil +} +func (ModulesServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type ModulesServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *ModulesServiceError) Reset() { *m = ModulesServiceError{} } +func (m *ModulesServiceError) String() string { return proto.CompactTextString(m) } +func (*ModulesServiceError) ProtoMessage() {} +func (*ModulesServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type GetModulesRequest struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetModulesRequest) Reset() { *m = GetModulesRequest{} } +func (m *GetModulesRequest) String() string { return proto.CompactTextString(m) } +func (*GetModulesRequest) ProtoMessage() {} +func (*GetModulesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type GetModulesResponse struct { + Module []string `protobuf:"bytes,1,rep,name=module" json:"module,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetModulesResponse) Reset() { *m = GetModulesResponse{} } +func (m *GetModulesResponse) String() string { return proto.CompactTextString(m) } +func (*GetModulesResponse) ProtoMessage() {} +func (*GetModulesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *GetModulesResponse) GetModule() []string { + if m != nil { + return m.Module + } + return nil +} + +type GetVersionsRequest struct { + Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetVersionsRequest) Reset() { *m = GetVersionsRequest{} } +func (m *GetVersionsRequest) String() string { return proto.CompactTextString(m) } +func (*GetVersionsRequest) ProtoMessage() {} +func (*GetVersionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *GetVersionsRequest) GetModule() string { + if m != nil && m.Module != nil { + return *m.Module + } + return "" +} + +type GetVersionsResponse struct { + Version []string `protobuf:"bytes,1,rep,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetVersionsResponse) Reset() { *m = GetVersionsResponse{} } +func (m *GetVersionsResponse) String() string { return proto.CompactTextString(m) } +func (*GetVersionsResponse) ProtoMessage() {} +func (*GetVersionsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *GetVersionsResponse) GetVersion() []string { + if m != nil { + return m.Version + } + return nil +} + +type GetDefaultVersionRequest struct { + Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetDefaultVersionRequest) Reset() { *m = GetDefaultVersionRequest{} } +func (m *GetDefaultVersionRequest) String() string { return proto.CompactTextString(m) } +func (*GetDefaultVersionRequest) ProtoMessage() {} +func (*GetDefaultVersionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *GetDefaultVersionRequest) GetModule() string { + if m != nil && m.Module != nil { + return *m.Module + } + return "" +} + +type GetDefaultVersionResponse struct { + Version *string `protobuf:"bytes,1,req,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetDefaultVersionResponse) Reset() { *m = GetDefaultVersionResponse{} } +func (m *GetDefaultVersionResponse) String() string { return proto.CompactTextString(m) } +func (*GetDefaultVersionResponse) ProtoMessage() {} +func (*GetDefaultVersionResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *GetDefaultVersionResponse) GetVersion() string { + if m != nil && m.Version != nil { + return *m.Version + } + return "" +} + +type GetNumInstancesRequest struct { + Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"` + Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetNumInstancesRequest) Reset() { *m = GetNumInstancesRequest{} } +func (m *GetNumInstancesRequest) String() string { return proto.CompactTextString(m) } +func (*GetNumInstancesRequest) ProtoMessage() {} +func (*GetNumInstancesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *GetNumInstancesRequest) GetModule() string { + if m != nil && m.Module != nil { + return *m.Module + } + return "" +} + +func (m *GetNumInstancesRequest) GetVersion() string { + if m != nil && m.Version != nil { + return *m.Version + } + return "" +} + +type GetNumInstancesResponse struct { + Instances *int64 `protobuf:"varint,1,req,name=instances" json:"instances,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetNumInstancesResponse) Reset() { *m = GetNumInstancesResponse{} } +func (m *GetNumInstancesResponse) String() string { return proto.CompactTextString(m) } +func (*GetNumInstancesResponse) ProtoMessage() {} +func (*GetNumInstancesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *GetNumInstancesResponse) GetInstances() int64 { + if m != nil && m.Instances != nil { + return *m.Instances + } + return 0 +} + +type SetNumInstancesRequest struct { + Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"` + Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` + Instances *int64 `protobuf:"varint,3,req,name=instances" json:"instances,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SetNumInstancesRequest) Reset() { *m = SetNumInstancesRequest{} } +func (m *SetNumInstancesRequest) String() string { return proto.CompactTextString(m) } +func (*SetNumInstancesRequest) ProtoMessage() {} +func (*SetNumInstancesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *SetNumInstancesRequest) GetModule() string { + if m != nil && m.Module != nil { + return *m.Module + } + return "" +} + +func (m *SetNumInstancesRequest) GetVersion() string { + if m != nil && m.Version != nil { + return *m.Version + } + return "" +} + +func (m *SetNumInstancesRequest) GetInstances() int64 { + if m != nil && m.Instances != nil { + return *m.Instances + } + return 0 +} + +type SetNumInstancesResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *SetNumInstancesResponse) Reset() { *m = SetNumInstancesResponse{} } +func (m *SetNumInstancesResponse) String() string { return proto.CompactTextString(m) } +func (*SetNumInstancesResponse) ProtoMessage() {} +func (*SetNumInstancesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +type StartModuleRequest struct { + Module *string `protobuf:"bytes,1,req,name=module" json:"module,omitempty"` + Version *string `protobuf:"bytes,2,req,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *StartModuleRequest) Reset() { *m = StartModuleRequest{} } +func (m *StartModuleRequest) String() string { return proto.CompactTextString(m) } +func (*StartModuleRequest) ProtoMessage() {} +func (*StartModuleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *StartModuleRequest) GetModule() string { + if m != nil && m.Module != nil { + return *m.Module + } + return "" +} + +func (m *StartModuleRequest) GetVersion() string { + if m != nil && m.Version != nil { + return *m.Version + } + return "" +} + +type StartModuleResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *StartModuleResponse) Reset() { *m = StartModuleResponse{} } +func (m *StartModuleResponse) String() string { return proto.CompactTextString(m) } +func (*StartModuleResponse) ProtoMessage() {} +func (*StartModuleResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +type StopModuleRequest struct { + Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"` + Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *StopModuleRequest) Reset() { *m = StopModuleRequest{} } +func (m *StopModuleRequest) String() string { return proto.CompactTextString(m) } +func (*StopModuleRequest) ProtoMessage() {} +func (*StopModuleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *StopModuleRequest) GetModule() string { + if m != nil && m.Module != nil { + return *m.Module + } + return "" +} + +func (m *StopModuleRequest) GetVersion() string { + if m != nil && m.Version != nil { + return *m.Version + } + return "" +} + +type StopModuleResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *StopModuleResponse) Reset() { *m = StopModuleResponse{} } +func (m *StopModuleResponse) String() string { return proto.CompactTextString(m) } +func (*StopModuleResponse) ProtoMessage() {} +func (*StopModuleResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +type GetHostnameRequest struct { + Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"` + Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` + Instance *string `protobuf:"bytes,3,opt,name=instance" json:"instance,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetHostnameRequest) Reset() { *m = GetHostnameRequest{} } +func (m *GetHostnameRequest) String() string { return proto.CompactTextString(m) } +func (*GetHostnameRequest) ProtoMessage() {} +func (*GetHostnameRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +func (m *GetHostnameRequest) GetModule() string { + if m != nil && m.Module != nil { + return *m.Module + } + return "" +} + +func (m *GetHostnameRequest) GetVersion() string { + if m != nil && m.Version != nil { + return *m.Version + } + return "" +} + +func (m *GetHostnameRequest) GetInstance() string { + if m != nil && m.Instance != nil { + return *m.Instance + } + return "" +} + +type GetHostnameResponse struct { + Hostname *string `protobuf:"bytes,1,req,name=hostname" json:"hostname,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetHostnameResponse) Reset() { *m = GetHostnameResponse{} } +func (m *GetHostnameResponse) String() string { return proto.CompactTextString(m) } +func (*GetHostnameResponse) ProtoMessage() {} +func (*GetHostnameResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *GetHostnameResponse) GetHostname() string { + if m != nil && m.Hostname != nil { + return *m.Hostname + } + return "" +} + +func init() { + proto.RegisterType((*ModulesServiceError)(nil), "appengine.ModulesServiceError") + proto.RegisterType((*GetModulesRequest)(nil), "appengine.GetModulesRequest") + proto.RegisterType((*GetModulesResponse)(nil), "appengine.GetModulesResponse") + proto.RegisterType((*GetVersionsRequest)(nil), "appengine.GetVersionsRequest") + proto.RegisterType((*GetVersionsResponse)(nil), "appengine.GetVersionsResponse") + proto.RegisterType((*GetDefaultVersionRequest)(nil), "appengine.GetDefaultVersionRequest") + proto.RegisterType((*GetDefaultVersionResponse)(nil), "appengine.GetDefaultVersionResponse") + proto.RegisterType((*GetNumInstancesRequest)(nil), "appengine.GetNumInstancesRequest") + proto.RegisterType((*GetNumInstancesResponse)(nil), "appengine.GetNumInstancesResponse") + proto.RegisterType((*SetNumInstancesRequest)(nil), "appengine.SetNumInstancesRequest") + proto.RegisterType((*SetNumInstancesResponse)(nil), "appengine.SetNumInstancesResponse") + proto.RegisterType((*StartModuleRequest)(nil), "appengine.StartModuleRequest") + proto.RegisterType((*StartModuleResponse)(nil), "appengine.StartModuleResponse") + proto.RegisterType((*StopModuleRequest)(nil), "appengine.StopModuleRequest") + proto.RegisterType((*StopModuleResponse)(nil), "appengine.StopModuleResponse") + proto.RegisterType((*GetHostnameRequest)(nil), "appengine.GetHostnameRequest") + proto.RegisterType((*GetHostnameResponse)(nil), "appengine.GetHostnameResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/modules/modules_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 457 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xc1, 0x6f, 0xd3, 0x30, + 0x14, 0xc6, 0x69, 0x02, 0xdb, 0xf2, 0x0e, 0x90, 0x3a, 0x5b, 0xd7, 0x4d, 0x1c, 0x50, 0x4e, 0x1c, + 0x50, 0x2b, 0x90, 0x10, 0xe7, 0xae, 0x35, 0x25, 0xb0, 0xa5, 0x28, 0xce, 0x2a, 0xc4, 0xa5, 0x0a, + 0xdb, 0x23, 0x8b, 0x94, 0xda, 0xc1, 0x76, 0x77, 0xe4, 0xbf, 0xe0, 0xff, 0x45, 0x4b, 0xed, 0xb6, + 0x81, 0x4e, 0x45, 0x68, 0xa7, 0xe4, 0x7d, 0xfe, 0xfc, 0x7b, 0x9f, 0x5f, 0xac, 0xc0, 0x59, 0x2e, + 0x44, 0x5e, 0x62, 0x2f, 0x17, 0x65, 0xc6, 0xf3, 0x9e, 0x90, 0x79, 0x3f, 0xab, 0x2a, 0xe4, 0x79, + 0xc1, 0xb1, 0x5f, 0x70, 0x8d, 0x92, 0x67, 0x65, 0x7f, 0x2e, 0xae, 0x17, 0x25, 0x2a, 0xfb, 0x9c, + 0x29, 0x94, 0xb7, 0xc5, 0x15, 0xf6, 0x2a, 0x29, 0xb4, 0x20, 0xde, 0x6a, 0x47, 0xf8, 0xab, 0x05, + 0xc1, 0xc5, 0xd2, 0xc4, 0x96, 0x1e, 0x2a, 0xa5, 0x90, 0xe1, 0x4f, 0xf0, 0xea, 0x97, 0xa1, 0xb8, + 0x46, 0xb2, 0x07, 0xce, 0xe4, 0x93, 0xff, 0x88, 0x10, 0x78, 0x1a, 0xc5, 0xd3, 0xc1, 0x79, 0x34, + 0x9a, 0x5d, 0x4c, 0x46, 0x97, 0xe7, 0xd4, 0x6f, 0x91, 0x00, 0x9e, 0x59, 0x6d, 0x4a, 0x13, 0x16, + 0x4d, 0x62, 0xdf, 0x21, 0x47, 0xd0, 0xb6, 0x62, 0x14, 0xb3, 0x74, 0x10, 0x0f, 0x29, 0xf3, 0xdd, + 0x3b, 0x6f, 0x9a, 0x0c, 0x62, 0x16, 0xd1, 0x38, 0x9d, 0xd1, 0x24, 0x99, 0x24, 0xfe, 0x63, 0x72, + 0x08, 0xfe, 0x65, 0x4c, 0xbf, 0x7c, 0xa6, 0xc3, 0x94, 0x8e, 0x66, 0x2c, 0x1d, 0xa4, 0xd4, 0x7f, + 0x12, 0x06, 0xd0, 0x1e, 0xa3, 0x36, 0xc9, 0x12, 0xfc, 0xb1, 0x40, 0xa5, 0xc3, 0x57, 0x40, 0x36, + 0x45, 0x55, 0x09, 0xae, 0x90, 0x74, 0x60, 0x6f, 0x79, 0xcc, 0x6e, 0xeb, 0x85, 0xfb, 0xd2, 0x4b, + 0x4c, 0x65, 0xdc, 0x53, 0x94, 0xaa, 0x10, 0xdc, 0x32, 0x1a, 0xee, 0xd6, 0x86, 0xbb, 0x0f, 0x41, + 0xc3, 0x6d, 0xe0, 0x5d, 0xd8, 0xbf, 0x5d, 0x6a, 0x86, 0x6e, 0xcb, 0xf0, 0x0d, 0x74, 0xc7, 0xa8, + 0x47, 0xf8, 0x3d, 0x5b, 0x94, 0x76, 0xdf, 0xae, 0x26, 0x6f, 0xe1, 0x64, 0xcb, 0x9e, 0x6d, 0xad, + 0x9c, 0xcd, 0x56, 0x1f, 0xa1, 0x33, 0x46, 0x1d, 0x2f, 0xe6, 0x11, 0x57, 0x3a, 0xe3, 0x57, 0xb8, + 0xeb, 0x34, 0x9b, 0x2c, 0xa7, 0x5e, 0x58, 0xb1, 0xde, 0xc1, 0xf1, 0x5f, 0x2c, 0x13, 0xe0, 0x39, + 0x78, 0x85, 0x15, 0xeb, 0x08, 0x6e, 0xb2, 0x16, 0xc2, 0x1b, 0xe8, 0xb0, 0x07, 0x0a, 0xd1, 0xec, + 0xe4, 0xfe, 0xd9, 0xe9, 0x04, 0x8e, 0xd9, 0xf6, 0x88, 0xe1, 0x7b, 0x20, 0x4c, 0x67, 0xd2, 0xdc, + 0x81, 0x6d, 0x01, 0x9c, 0xfb, 0x02, 0x34, 0x26, 0x7a, 0x04, 0x41, 0x83, 0x63, 0xf0, 0x14, 0xda, + 0x4c, 0x8b, 0xea, 0x7e, 0xfa, 0xbf, 0xcd, 0xf8, 0xf0, 0x2e, 0xe5, 0x1a, 0x63, 0xe0, 0xdf, 0xea, + 0xfb, 0xf8, 0x41, 0x28, 0xcd, 0xb3, 0xf9, 0xff, 0xd3, 0xc9, 0x29, 0x1c, 0xd8, 0x59, 0x75, 0xdd, + 0x7a, 0x69, 0x55, 0x87, 0xaf, 0xeb, 0x5b, 0xbc, 0xee, 0x61, 0xbe, 0xec, 0x29, 0x1c, 0xdc, 0x18, + 0xcd, 0x8c, 0x68, 0x55, 0x9f, 0x79, 0x5f, 0xf7, 0xcd, 0x5f, 0xe2, 0x77, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x6e, 0xbc, 0xe0, 0x61, 0x5c, 0x04, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/modules/modules_service.proto b/vendor/google.golang.org/appengine/internal/modules/modules_service.proto new file mode 100644 index 0000000000..d29f0065a2 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/modules/modules_service.proto @@ -0,0 +1,80 @@ +syntax = "proto2"; +option go_package = "modules"; + +package appengine; + +message ModulesServiceError { + enum ErrorCode { + OK = 0; + INVALID_MODULE = 1; + INVALID_VERSION = 2; + INVALID_INSTANCES = 3; + TRANSIENT_ERROR = 4; + UNEXPECTED_STATE = 5; + } +} + +message GetModulesRequest { +} + +message GetModulesResponse { + repeated string module = 1; +} + +message GetVersionsRequest { + optional string module = 1; +} + +message GetVersionsResponse { + repeated string version = 1; +} + +message GetDefaultVersionRequest { + optional string module = 1; +} + +message GetDefaultVersionResponse { + required string version = 1; +} + +message GetNumInstancesRequest { + optional string module = 1; + optional string version = 2; +} + +message GetNumInstancesResponse { + required int64 instances = 1; +} + +message SetNumInstancesRequest { + optional string module = 1; + optional string version = 2; + required int64 instances = 3; +} + +message SetNumInstancesResponse {} + +message StartModuleRequest { + required string module = 1; + required string version = 2; +} + +message StartModuleResponse {} + +message StopModuleRequest { + optional string module = 1; + optional string version = 2; +} + +message StopModuleResponse {} + +message GetHostnameRequest { + optional string module = 1; + optional string version = 2; + optional string instance = 3; +} + +message GetHostnameResponse { + required string hostname = 1; +} + diff --git a/vendor/google.golang.org/appengine/internal/net.go b/vendor/google.golang.org/appengine/internal/net.go new file mode 100644 index 0000000000..3b94cf0c6a --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/net.go @@ -0,0 +1,56 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package internal + +// This file implements a network dialer that limits the number of concurrent connections. +// It is only used for API calls. + +import ( + "log" + "net" + "runtime" + "sync" + "time" +) + +var limitSem = make(chan int, 100) // TODO(dsymonds): Use environment variable. + +func limitRelease() { + // non-blocking + select { + case <-limitSem: + default: + // This should not normally happen. + log.Print("appengine: unbalanced limitSem release!") + } +} + +func limitDial(network, addr string) (net.Conn, error) { + limitSem <- 1 + + // Dial with a timeout in case the API host is MIA. + // The connection should normally be very fast. + conn, err := net.DialTimeout(network, addr, 500*time.Millisecond) + if err != nil { + limitRelease() + return nil, err + } + lc := &limitConn{Conn: conn} + runtime.SetFinalizer(lc, (*limitConn).Close) // shouldn't usually be required + return lc, nil +} + +type limitConn struct { + close sync.Once + net.Conn +} + +func (lc *limitConn) Close() error { + defer lc.close.Do(func() { + limitRelease() + runtime.SetFinalizer(lc, nil) + }) + return lc.Conn.Close() +} diff --git a/vendor/google.golang.org/appengine/internal/net_test.go b/vendor/google.golang.org/appengine/internal/net_test.go new file mode 100644 index 0000000000..24da8bb2b1 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/net_test.go @@ -0,0 +1,58 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package internal + +import ( + "sync" + "testing" + "time" + + netcontext "golang.org/x/net/context" + + basepb "google.golang.org/appengine/internal/base" +) + +func TestDialLimit(t *testing.T) { + // Fill up semaphore with false acquisitions to permit only two TCP connections at a time. + // We don't replace limitSem because that results in a data race when net/http lazily closes connections. + nFake := cap(limitSem) - 2 + for i := 0; i < nFake; i++ { + limitSem <- 1 + } + defer func() { + for i := 0; i < nFake; i++ { + <-limitSem + } + }() + + f, c, cleanup := setup() // setup is in api_test.go + defer cleanup() + f.hang = make(chan int) + + // If we make two RunSlowly RPCs (which will wait for f.hang to be strobed), + // then the simple Non200 RPC should hang. + var wg sync.WaitGroup + wg.Add(2) + for i := 0; i < 2; i++ { + go func() { + defer wg.Done() + Call(toContext(c), "errors", "RunSlowly", &basepb.VoidProto{}, &basepb.VoidProto{}) + }() + } + time.Sleep(50 * time.Millisecond) // let those two RPCs start + + ctx, _ := netcontext.WithTimeout(toContext(c), 50*time.Millisecond) + err := Call(ctx, "errors", "Non200", &basepb.VoidProto{}, &basepb.VoidProto{}) + if err != errTimeout { + t.Errorf("Non200 RPC returned with err %v, want errTimeout", err) + } + + // Drain the two RunSlowly calls. + f.hang <- 1 + f.hang <- 1 + wg.Wait() +} diff --git a/vendor/google.golang.org/appengine/internal/regen.sh b/vendor/google.golang.org/appengine/internal/regen.sh new file mode 100755 index 0000000000..2fdb546a63 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/regen.sh @@ -0,0 +1,40 @@ +#!/bin/bash -e +# +# This script rebuilds the generated code for the protocol buffers. +# To run this you will need protoc and goprotobuf installed; +# see https://github.com/golang/protobuf for instructions. + +PKG=google.golang.org/appengine + +function die() { + echo 1>&2 $* + exit 1 +} + +# Sanity check that the right tools are accessible. +for tool in go protoc protoc-gen-go; do + q=$(which $tool) || die "didn't find $tool" + echo 1>&2 "$tool: $q" +done + +echo -n 1>&2 "finding package dir... " +pkgdir=$(go list -f '{{.Dir}}' $PKG) +echo 1>&2 $pkgdir +base=$(echo $pkgdir | sed "s,/$PKG\$,,") +echo 1>&2 "base: $base" +cd $base + +# Run protoc once per package. +for dir in $(find $PKG/internal -name '*.proto' | xargs dirname | sort | uniq); do + echo 1>&2 "* $dir" + protoc --go_out=. $dir/*.proto +done + +for f in $(find $PKG/internal -name '*.pb.go'); do + # Remove proto.RegisterEnum calls. + # These cause duplicate registration panics when these packages + # are used on classic App Engine. proto.RegisterEnum only affects + # parsing the text format; we don't care about that. + # https://code.google.com/p/googleappengine/issues/detail?id=11670#c17 + sed -i '/proto.RegisterEnum/d' $f +done diff --git a/vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go new file mode 100644 index 0000000000..172aebe8e4 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go @@ -0,0 +1,287 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/remote_api/remote_api.proto + +/* +Package remote_api is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/remote_api/remote_api.proto + +It has these top-level messages: + Request + ApplicationError + RpcError + Response +*/ +package remote_api + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type RpcError_ErrorCode int32 + +const ( + RpcError_UNKNOWN RpcError_ErrorCode = 0 + RpcError_CALL_NOT_FOUND RpcError_ErrorCode = 1 + RpcError_PARSE_ERROR RpcError_ErrorCode = 2 + RpcError_SECURITY_VIOLATION RpcError_ErrorCode = 3 + RpcError_OVER_QUOTA RpcError_ErrorCode = 4 + RpcError_REQUEST_TOO_LARGE RpcError_ErrorCode = 5 + RpcError_CAPABILITY_DISABLED RpcError_ErrorCode = 6 + RpcError_FEATURE_DISABLED RpcError_ErrorCode = 7 + RpcError_BAD_REQUEST RpcError_ErrorCode = 8 + RpcError_RESPONSE_TOO_LARGE RpcError_ErrorCode = 9 + RpcError_CANCELLED RpcError_ErrorCode = 10 + RpcError_REPLAY_ERROR RpcError_ErrorCode = 11 + RpcError_DEADLINE_EXCEEDED RpcError_ErrorCode = 12 +) + +var RpcError_ErrorCode_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CALL_NOT_FOUND", + 2: "PARSE_ERROR", + 3: "SECURITY_VIOLATION", + 4: "OVER_QUOTA", + 5: "REQUEST_TOO_LARGE", + 6: "CAPABILITY_DISABLED", + 7: "FEATURE_DISABLED", + 8: "BAD_REQUEST", + 9: "RESPONSE_TOO_LARGE", + 10: "CANCELLED", + 11: "REPLAY_ERROR", + 12: "DEADLINE_EXCEEDED", +} +var RpcError_ErrorCode_value = map[string]int32{ + "UNKNOWN": 0, + "CALL_NOT_FOUND": 1, + "PARSE_ERROR": 2, + "SECURITY_VIOLATION": 3, + "OVER_QUOTA": 4, + "REQUEST_TOO_LARGE": 5, + "CAPABILITY_DISABLED": 6, + "FEATURE_DISABLED": 7, + "BAD_REQUEST": 8, + "RESPONSE_TOO_LARGE": 9, + "CANCELLED": 10, + "REPLAY_ERROR": 11, + "DEADLINE_EXCEEDED": 12, +} + +func (x RpcError_ErrorCode) Enum() *RpcError_ErrorCode { + p := new(RpcError_ErrorCode) + *p = x + return p +} +func (x RpcError_ErrorCode) String() string { + return proto.EnumName(RpcError_ErrorCode_name, int32(x)) +} +func (x *RpcError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(RpcError_ErrorCode_value, data, "RpcError_ErrorCode") + if err != nil { + return err + } + *x = RpcError_ErrorCode(value) + return nil +} +func (RpcError_ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } + +type Request struct { + ServiceName *string `protobuf:"bytes,2,req,name=service_name,json=serviceName" json:"service_name,omitempty"` + Method *string `protobuf:"bytes,3,req,name=method" json:"method,omitempty"` + Request []byte `protobuf:"bytes,4,req,name=request" json:"request,omitempty"` + RequestId *string `protobuf:"bytes,5,opt,name=request_id,json=requestId" json:"request_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Request) Reset() { *m = Request{} } +func (m *Request) String() string { return proto.CompactTextString(m) } +func (*Request) ProtoMessage() {} +func (*Request) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Request) GetServiceName() string { + if m != nil && m.ServiceName != nil { + return *m.ServiceName + } + return "" +} + +func (m *Request) GetMethod() string { + if m != nil && m.Method != nil { + return *m.Method + } + return "" +} + +func (m *Request) GetRequest() []byte { + if m != nil { + return m.Request + } + return nil +} + +func (m *Request) GetRequestId() string { + if m != nil && m.RequestId != nil { + return *m.RequestId + } + return "" +} + +type ApplicationError struct { + Code *int32 `protobuf:"varint,1,req,name=code" json:"code,omitempty"` + Detail *string `protobuf:"bytes,2,req,name=detail" json:"detail,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ApplicationError) Reset() { *m = ApplicationError{} } +func (m *ApplicationError) String() string { return proto.CompactTextString(m) } +func (*ApplicationError) ProtoMessage() {} +func (*ApplicationError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *ApplicationError) GetCode() int32 { + if m != nil && m.Code != nil { + return *m.Code + } + return 0 +} + +func (m *ApplicationError) GetDetail() string { + if m != nil && m.Detail != nil { + return *m.Detail + } + return "" +} + +type RpcError struct { + Code *int32 `protobuf:"varint,1,req,name=code" json:"code,omitempty"` + Detail *string `protobuf:"bytes,2,opt,name=detail" json:"detail,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *RpcError) Reset() { *m = RpcError{} } +func (m *RpcError) String() string { return proto.CompactTextString(m) } +func (*RpcError) ProtoMessage() {} +func (*RpcError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *RpcError) GetCode() int32 { + if m != nil && m.Code != nil { + return *m.Code + } + return 0 +} + +func (m *RpcError) GetDetail() string { + if m != nil && m.Detail != nil { + return *m.Detail + } + return "" +} + +type Response struct { + Response []byte `protobuf:"bytes,1,opt,name=response" json:"response,omitempty"` + Exception []byte `protobuf:"bytes,2,opt,name=exception" json:"exception,omitempty"` + ApplicationError *ApplicationError `protobuf:"bytes,3,opt,name=application_error,json=applicationError" json:"application_error,omitempty"` + JavaException []byte `protobuf:"bytes,4,opt,name=java_exception,json=javaException" json:"java_exception,omitempty"` + RpcError *RpcError `protobuf:"bytes,5,opt,name=rpc_error,json=rpcError" json:"rpc_error,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *Response) GetResponse() []byte { + if m != nil { + return m.Response + } + return nil +} + +func (m *Response) GetException() []byte { + if m != nil { + return m.Exception + } + return nil +} + +func (m *Response) GetApplicationError() *ApplicationError { + if m != nil { + return m.ApplicationError + } + return nil +} + +func (m *Response) GetJavaException() []byte { + if m != nil { + return m.JavaException + } + return nil +} + +func (m *Response) GetRpcError() *RpcError { + if m != nil { + return m.RpcError + } + return nil +} + +func init() { + proto.RegisterType((*Request)(nil), "remote_api.Request") + proto.RegisterType((*ApplicationError)(nil), "remote_api.ApplicationError") + proto.RegisterType((*RpcError)(nil), "remote_api.RpcError") + proto.RegisterType((*Response)(nil), "remote_api.Response") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/remote_api/remote_api.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 531 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x51, 0x6e, 0xd3, 0x40, + 0x10, 0x86, 0xb1, 0x9b, 0x34, 0xf1, 0xc4, 0x2d, 0xdb, 0xa5, 0x14, 0x0b, 0x15, 0x29, 0x44, 0x42, + 0xca, 0x53, 0x2a, 0x38, 0x00, 0x62, 0x63, 0x6f, 0x91, 0x85, 0x65, 0xa7, 0x6b, 0xbb, 0x50, 0x5e, + 0x56, 0x2b, 0x67, 0x65, 0x8c, 0x12, 0xaf, 0xd9, 0x98, 0x8a, 0x17, 0x6e, 0xc0, 0xb5, 0x38, 0x0c, + 0xb7, 0x40, 0x36, 0x6e, 0x63, 0xf5, 0x89, 0xb7, 0x7f, 0x7e, 0x7b, 0xe6, 0x1b, 0xcd, 0xcc, 0xc2, + 0xbb, 0x5c, 0xa9, 0x7c, 0x23, 0x17, 0xb9, 0xda, 0x88, 0x32, 0x5f, 0x28, 0x9d, 0x5f, 0x88, 0xaa, + 0x92, 0x65, 0x5e, 0x94, 0xf2, 0xa2, 0x28, 0x6b, 0xa9, 0x4b, 0xb1, 0xb9, 0xd0, 0x72, 0xab, 0x6a, + 0xc9, 0x45, 0x55, 0xf4, 0xe4, 0xa2, 0xd2, 0xaa, 0x56, 0x18, 0xf6, 0xce, 0xec, 0x27, 0x8c, 0x98, + 0xfc, 0xf6, 0x5d, 0xee, 0x6a, 0xfc, 0x12, 0xec, 0x9d, 0xd4, 0xb7, 0x45, 0x26, 0x79, 0x29, 0xb6, + 0xd2, 0x31, 0xa7, 0xe6, 0xdc, 0x62, 0x93, 0xce, 0x0b, 0xc5, 0x56, 0xe2, 0x33, 0x38, 0xdc, 0xca, + 0xfa, 0x8b, 0x5a, 0x3b, 0x07, 0xed, 0xc7, 0x2e, 0xc2, 0x0e, 0x8c, 0xf4, 0xbf, 0x2a, 0xce, 0x60, + 0x6a, 0xce, 0x6d, 0x76, 0x17, 0xe2, 0x17, 0x00, 0x9d, 0xe4, 0xc5, 0xda, 0x19, 0x4e, 0x8d, 0xb9, + 0xc5, 0xac, 0xce, 0xf1, 0xd7, 0xb3, 0xb7, 0x80, 0x48, 0x55, 0x6d, 0x8a, 0x4c, 0xd4, 0x85, 0x2a, + 0xa9, 0xd6, 0x4a, 0x63, 0x0c, 0x83, 0x4c, 0xad, 0xa5, 0x63, 0x4c, 0xcd, 0xf9, 0x90, 0xb5, 0xba, + 0x01, 0xaf, 0x65, 0x2d, 0x8a, 0x4d, 0xd7, 0x55, 0x17, 0xcd, 0x7e, 0x9b, 0x30, 0x66, 0x55, 0xf6, + 0x7f, 0x89, 0x46, 0x2f, 0xf1, 0x97, 0x09, 0x56, 0x9b, 0xe5, 0x36, 0x7f, 0x4d, 0x60, 0x94, 0x86, + 0x1f, 0xc2, 0xe8, 0x63, 0x88, 0x1e, 0x61, 0x0c, 0xc7, 0x2e, 0x09, 0x02, 0x1e, 0x46, 0x09, 0xbf, + 0x8c, 0xd2, 0xd0, 0x43, 0x06, 0x7e, 0x0c, 0x93, 0x15, 0x61, 0x31, 0xe5, 0x94, 0xb1, 0x88, 0x21, + 0x13, 0x9f, 0x01, 0x8e, 0xa9, 0x9b, 0x32, 0x3f, 0xb9, 0xe1, 0xd7, 0x7e, 0x14, 0x90, 0xc4, 0x8f, + 0x42, 0x74, 0x80, 0x8f, 0x01, 0xa2, 0x6b, 0xca, 0xf8, 0x55, 0x1a, 0x25, 0x04, 0x0d, 0xf0, 0x53, + 0x38, 0x61, 0xf4, 0x2a, 0xa5, 0x71, 0xc2, 0x93, 0x28, 0xe2, 0x01, 0x61, 0xef, 0x29, 0x1a, 0xe2, + 0x67, 0xf0, 0xc4, 0x25, 0x2b, 0xb2, 0xf4, 0x83, 0xa6, 0x80, 0xe7, 0xc7, 0x64, 0x19, 0x50, 0x0f, + 0x1d, 0xe2, 0x53, 0x40, 0x97, 0x94, 0x24, 0x29, 0xa3, 0x7b, 0x77, 0xd4, 0xe0, 0x97, 0xc4, 0xe3, + 0x5d, 0x25, 0x34, 0x6e, 0xf0, 0x8c, 0xc6, 0xab, 0x28, 0x8c, 0x69, 0xaf, 0xae, 0x85, 0x8f, 0xc0, + 0x72, 0x49, 0xe8, 0xd2, 0xa0, 0xc9, 0x03, 0x8c, 0xc0, 0x66, 0x74, 0x15, 0x90, 0x9b, 0xae, 0xef, + 0x49, 0xd3, 0x8f, 0x47, 0x89, 0x17, 0xf8, 0x21, 0xe5, 0xf4, 0x93, 0x4b, 0xa9, 0x47, 0x3d, 0x64, + 0xcf, 0xfe, 0x18, 0x30, 0x66, 0x72, 0x57, 0xa9, 0x72, 0x27, 0xf1, 0x73, 0x18, 0xeb, 0x4e, 0x3b, + 0xc6, 0xd4, 0x98, 0xdb, 0xec, 0x3e, 0xc6, 0xe7, 0x60, 0xc9, 0x1f, 0x99, 0xac, 0x9a, 0x75, 0xb5, + 0x23, 0xb5, 0xd9, 0xde, 0xc0, 0x3e, 0x9c, 0x88, 0xfd, 0x3a, 0xb9, 0x6c, 0x06, 0xec, 0x1c, 0x4c, + 0x8d, 0xf9, 0xe4, 0xcd, 0xf9, 0xa2, 0x77, 0x87, 0x0f, 0x77, 0xce, 0x90, 0x78, 0x78, 0x05, 0xaf, + 0xe0, 0xf8, 0xab, 0xb8, 0x15, 0x7c, 0x4f, 0x1b, 0xb4, 0xb4, 0xa3, 0xc6, 0xa5, 0xf7, 0xc4, 0xd7, + 0x60, 0xe9, 0x2a, 0xeb, 0x48, 0xc3, 0x96, 0x74, 0xda, 0x27, 0xdd, 0x1d, 0x07, 0x1b, 0xeb, 0x4e, + 0x2d, 0xed, 0xcf, 0xbd, 0x07, 0xf0, 0x37, 0x00, 0x00, 0xff, 0xff, 0x38, 0xd1, 0x0f, 0x22, 0x4f, + 0x03, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto new file mode 100644 index 0000000000..f21763a4e2 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto @@ -0,0 +1,44 @@ +syntax = "proto2"; +option go_package = "remote_api"; + +package remote_api; + +message Request { + required string service_name = 2; + required string method = 3; + required bytes request = 4; + optional string request_id = 5; +} + +message ApplicationError { + required int32 code = 1; + required string detail = 2; +} + +message RpcError { + enum ErrorCode { + UNKNOWN = 0; + CALL_NOT_FOUND = 1; + PARSE_ERROR = 2; + SECURITY_VIOLATION = 3; + OVER_QUOTA = 4; + REQUEST_TOO_LARGE = 5; + CAPABILITY_DISABLED = 6; + FEATURE_DISABLED = 7; + BAD_REQUEST = 8; + RESPONSE_TOO_LARGE = 9; + CANCELLED = 10; + REPLAY_ERROR = 11; + DEADLINE_EXCEEDED = 12; + } + required int32 code = 1; + optional string detail = 2; +} + +message Response { + optional bytes response = 1; + optional bytes exception = 2; + optional ApplicationError application_error = 3; + optional bytes java_exception = 4; + optional RpcError rpc_error = 5; +} diff --git a/vendor/google.golang.org/appengine/internal/search/search.pb.go b/vendor/google.golang.org/appengine/internal/search/search.pb.go new file mode 100644 index 0000000000..3c53183aaa --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/search/search.pb.go @@ -0,0 +1,2478 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/search/search.proto + +/* +Package search is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/search/search.proto + +It has these top-level messages: + Scope + Entry + AccessControlList + FieldValue + Field + FieldTypes + IndexShardSettings + FacetValue + Facet + DocumentMetadata + Document + SearchServiceError + RequestStatus + IndexSpec + IndexMetadata + IndexDocumentParams + IndexDocumentRequest + IndexDocumentResponse + DeleteDocumentParams + DeleteDocumentRequest + DeleteDocumentResponse + ListDocumentsParams + ListDocumentsRequest + ListDocumentsResponse + ListIndexesParams + ListIndexesRequest + ListIndexesResponse + DeleteSchemaParams + DeleteSchemaRequest + DeleteSchemaResponse + SortSpec + ScorerSpec + FieldSpec + FacetRange + FacetRequestParam + FacetAutoDetectParam + FacetRequest + FacetRefinement + SearchParams + SearchRequest + FacetResultValue + FacetResult + SearchResult + SearchResponse +*/ +package search + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Scope_Type int32 + +const ( + Scope_USER_BY_CANONICAL_ID Scope_Type = 1 + Scope_USER_BY_EMAIL Scope_Type = 2 + Scope_GROUP_BY_CANONICAL_ID Scope_Type = 3 + Scope_GROUP_BY_EMAIL Scope_Type = 4 + Scope_GROUP_BY_DOMAIN Scope_Type = 5 + Scope_ALL_USERS Scope_Type = 6 + Scope_ALL_AUTHENTICATED_USERS Scope_Type = 7 +) + +var Scope_Type_name = map[int32]string{ + 1: "USER_BY_CANONICAL_ID", + 2: "USER_BY_EMAIL", + 3: "GROUP_BY_CANONICAL_ID", + 4: "GROUP_BY_EMAIL", + 5: "GROUP_BY_DOMAIN", + 6: "ALL_USERS", + 7: "ALL_AUTHENTICATED_USERS", +} +var Scope_Type_value = map[string]int32{ + "USER_BY_CANONICAL_ID": 1, + "USER_BY_EMAIL": 2, + "GROUP_BY_CANONICAL_ID": 3, + "GROUP_BY_EMAIL": 4, + "GROUP_BY_DOMAIN": 5, + "ALL_USERS": 6, + "ALL_AUTHENTICATED_USERS": 7, +} + +func (x Scope_Type) Enum() *Scope_Type { + p := new(Scope_Type) + *p = x + return p +} +func (x Scope_Type) String() string { + return proto.EnumName(Scope_Type_name, int32(x)) +} +func (x *Scope_Type) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Scope_Type_value, data, "Scope_Type") + if err != nil { + return err + } + *x = Scope_Type(value) + return nil +} +func (Scope_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } + +type Entry_Permission int32 + +const ( + Entry_READ Entry_Permission = 1 + Entry_WRITE Entry_Permission = 2 + Entry_FULL_CONTROL Entry_Permission = 3 +) + +var Entry_Permission_name = map[int32]string{ + 1: "READ", + 2: "WRITE", + 3: "FULL_CONTROL", +} +var Entry_Permission_value = map[string]int32{ + "READ": 1, + "WRITE": 2, + "FULL_CONTROL": 3, +} + +func (x Entry_Permission) Enum() *Entry_Permission { + p := new(Entry_Permission) + *p = x + return p +} +func (x Entry_Permission) String() string { + return proto.EnumName(Entry_Permission_name, int32(x)) +} +func (x *Entry_Permission) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Entry_Permission_value, data, "Entry_Permission") + if err != nil { + return err + } + *x = Entry_Permission(value) + return nil +} +func (Entry_Permission) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} } + +type FieldValue_ContentType int32 + +const ( + FieldValue_TEXT FieldValue_ContentType = 0 + FieldValue_HTML FieldValue_ContentType = 1 + FieldValue_ATOM FieldValue_ContentType = 2 + FieldValue_DATE FieldValue_ContentType = 3 + FieldValue_NUMBER FieldValue_ContentType = 4 + FieldValue_GEO FieldValue_ContentType = 5 +) + +var FieldValue_ContentType_name = map[int32]string{ + 0: "TEXT", + 1: "HTML", + 2: "ATOM", + 3: "DATE", + 4: "NUMBER", + 5: "GEO", +} +var FieldValue_ContentType_value = map[string]int32{ + "TEXT": 0, + "HTML": 1, + "ATOM": 2, + "DATE": 3, + "NUMBER": 4, + "GEO": 5, +} + +func (x FieldValue_ContentType) Enum() *FieldValue_ContentType { + p := new(FieldValue_ContentType) + *p = x + return p +} +func (x FieldValue_ContentType) String() string { + return proto.EnumName(FieldValue_ContentType_name, int32(x)) +} +func (x *FieldValue_ContentType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FieldValue_ContentType_value, data, "FieldValue_ContentType") + if err != nil { + return err + } + *x = FieldValue_ContentType(value) + return nil +} +func (FieldValue_ContentType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} } + +type FacetValue_ContentType int32 + +const ( + FacetValue_ATOM FacetValue_ContentType = 2 + FacetValue_NUMBER FacetValue_ContentType = 4 +) + +var FacetValue_ContentType_name = map[int32]string{ + 2: "ATOM", + 4: "NUMBER", +} +var FacetValue_ContentType_value = map[string]int32{ + "ATOM": 2, + "NUMBER": 4, +} + +func (x FacetValue_ContentType) Enum() *FacetValue_ContentType { + p := new(FacetValue_ContentType) + *p = x + return p +} +func (x FacetValue_ContentType) String() string { + return proto.EnumName(FacetValue_ContentType_name, int32(x)) +} +func (x *FacetValue_ContentType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FacetValue_ContentType_value, data, "FacetValue_ContentType") + if err != nil { + return err + } + *x = FacetValue_ContentType(value) + return nil +} +func (FacetValue_ContentType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{7, 0} } + +type Document_OrderIdSource int32 + +const ( + Document_DEFAULTED Document_OrderIdSource = 0 + Document_SUPPLIED Document_OrderIdSource = 1 +) + +var Document_OrderIdSource_name = map[int32]string{ + 0: "DEFAULTED", + 1: "SUPPLIED", +} +var Document_OrderIdSource_value = map[string]int32{ + "DEFAULTED": 0, + "SUPPLIED": 1, +} + +func (x Document_OrderIdSource) Enum() *Document_OrderIdSource { + p := new(Document_OrderIdSource) + *p = x + return p +} +func (x Document_OrderIdSource) String() string { + return proto.EnumName(Document_OrderIdSource_name, int32(x)) +} +func (x *Document_OrderIdSource) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Document_OrderIdSource_value, data, "Document_OrderIdSource") + if err != nil { + return err + } + *x = Document_OrderIdSource(value) + return nil +} +func (Document_OrderIdSource) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{10, 0} } + +type Document_Storage int32 + +const ( + Document_DISK Document_Storage = 0 +) + +var Document_Storage_name = map[int32]string{ + 0: "DISK", +} +var Document_Storage_value = map[string]int32{ + "DISK": 0, +} + +func (x Document_Storage) Enum() *Document_Storage { + p := new(Document_Storage) + *p = x + return p +} +func (x Document_Storage) String() string { + return proto.EnumName(Document_Storage_name, int32(x)) +} +func (x *Document_Storage) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Document_Storage_value, data, "Document_Storage") + if err != nil { + return err + } + *x = Document_Storage(value) + return nil +} +func (Document_Storage) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{10, 1} } + +type SearchServiceError_ErrorCode int32 + +const ( + SearchServiceError_OK SearchServiceError_ErrorCode = 0 + SearchServiceError_INVALID_REQUEST SearchServiceError_ErrorCode = 1 + SearchServiceError_TRANSIENT_ERROR SearchServiceError_ErrorCode = 2 + SearchServiceError_INTERNAL_ERROR SearchServiceError_ErrorCode = 3 + SearchServiceError_PERMISSION_DENIED SearchServiceError_ErrorCode = 4 + SearchServiceError_TIMEOUT SearchServiceError_ErrorCode = 5 + SearchServiceError_CONCURRENT_TRANSACTION SearchServiceError_ErrorCode = 6 +) + +var SearchServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INVALID_REQUEST", + 2: "TRANSIENT_ERROR", + 3: "INTERNAL_ERROR", + 4: "PERMISSION_DENIED", + 5: "TIMEOUT", + 6: "CONCURRENT_TRANSACTION", +} +var SearchServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INVALID_REQUEST": 1, + "TRANSIENT_ERROR": 2, + "INTERNAL_ERROR": 3, + "PERMISSION_DENIED": 4, + "TIMEOUT": 5, + "CONCURRENT_TRANSACTION": 6, +} + +func (x SearchServiceError_ErrorCode) Enum() *SearchServiceError_ErrorCode { + p := new(SearchServiceError_ErrorCode) + *p = x + return p +} +func (x SearchServiceError_ErrorCode) String() string { + return proto.EnumName(SearchServiceError_ErrorCode_name, int32(x)) +} +func (x *SearchServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(SearchServiceError_ErrorCode_value, data, "SearchServiceError_ErrorCode") + if err != nil { + return err + } + *x = SearchServiceError_ErrorCode(value) + return nil +} +func (SearchServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{11, 0} +} + +type IndexSpec_Consistency int32 + +const ( + IndexSpec_GLOBAL IndexSpec_Consistency = 0 + IndexSpec_PER_DOCUMENT IndexSpec_Consistency = 1 +) + +var IndexSpec_Consistency_name = map[int32]string{ + 0: "GLOBAL", + 1: "PER_DOCUMENT", +} +var IndexSpec_Consistency_value = map[string]int32{ + "GLOBAL": 0, + "PER_DOCUMENT": 1, +} + +func (x IndexSpec_Consistency) Enum() *IndexSpec_Consistency { + p := new(IndexSpec_Consistency) + *p = x + return p +} +func (x IndexSpec_Consistency) String() string { + return proto.EnumName(IndexSpec_Consistency_name, int32(x)) +} +func (x *IndexSpec_Consistency) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IndexSpec_Consistency_value, data, "IndexSpec_Consistency") + if err != nil { + return err + } + *x = IndexSpec_Consistency(value) + return nil +} +func (IndexSpec_Consistency) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{13, 0} } + +type IndexSpec_Source int32 + +const ( + IndexSpec_SEARCH IndexSpec_Source = 0 + IndexSpec_DATASTORE IndexSpec_Source = 1 + IndexSpec_CLOUD_STORAGE IndexSpec_Source = 2 +) + +var IndexSpec_Source_name = map[int32]string{ + 0: "SEARCH", + 1: "DATASTORE", + 2: "CLOUD_STORAGE", +} +var IndexSpec_Source_value = map[string]int32{ + "SEARCH": 0, + "DATASTORE": 1, + "CLOUD_STORAGE": 2, +} + +func (x IndexSpec_Source) Enum() *IndexSpec_Source { + p := new(IndexSpec_Source) + *p = x + return p +} +func (x IndexSpec_Source) String() string { + return proto.EnumName(IndexSpec_Source_name, int32(x)) +} +func (x *IndexSpec_Source) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IndexSpec_Source_value, data, "IndexSpec_Source") + if err != nil { + return err + } + *x = IndexSpec_Source(value) + return nil +} +func (IndexSpec_Source) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{13, 1} } + +type IndexSpec_Mode int32 + +const ( + IndexSpec_PRIORITY IndexSpec_Mode = 0 + IndexSpec_BACKGROUND IndexSpec_Mode = 1 +) + +var IndexSpec_Mode_name = map[int32]string{ + 0: "PRIORITY", + 1: "BACKGROUND", +} +var IndexSpec_Mode_value = map[string]int32{ + "PRIORITY": 0, + "BACKGROUND": 1, +} + +func (x IndexSpec_Mode) Enum() *IndexSpec_Mode { + p := new(IndexSpec_Mode) + *p = x + return p +} +func (x IndexSpec_Mode) String() string { + return proto.EnumName(IndexSpec_Mode_name, int32(x)) +} +func (x *IndexSpec_Mode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IndexSpec_Mode_value, data, "IndexSpec_Mode") + if err != nil { + return err + } + *x = IndexSpec_Mode(value) + return nil +} +func (IndexSpec_Mode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{13, 2} } + +type IndexDocumentParams_Freshness int32 + +const ( + IndexDocumentParams_SYNCHRONOUSLY IndexDocumentParams_Freshness = 0 + IndexDocumentParams_WHEN_CONVENIENT IndexDocumentParams_Freshness = 1 +) + +var IndexDocumentParams_Freshness_name = map[int32]string{ + 0: "SYNCHRONOUSLY", + 1: "WHEN_CONVENIENT", +} +var IndexDocumentParams_Freshness_value = map[string]int32{ + "SYNCHRONOUSLY": 0, + "WHEN_CONVENIENT": 1, +} + +func (x IndexDocumentParams_Freshness) Enum() *IndexDocumentParams_Freshness { + p := new(IndexDocumentParams_Freshness) + *p = x + return p +} +func (x IndexDocumentParams_Freshness) String() string { + return proto.EnumName(IndexDocumentParams_Freshness_name, int32(x)) +} +func (x *IndexDocumentParams_Freshness) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IndexDocumentParams_Freshness_value, data, "IndexDocumentParams_Freshness") + if err != nil { + return err + } + *x = IndexDocumentParams_Freshness(value) + return nil +} +func (IndexDocumentParams_Freshness) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{15, 0} +} + +type ScorerSpec_Scorer int32 + +const ( + ScorerSpec_RESCORING_MATCH_SCORER ScorerSpec_Scorer = 0 + ScorerSpec_MATCH_SCORER ScorerSpec_Scorer = 2 +) + +var ScorerSpec_Scorer_name = map[int32]string{ + 0: "RESCORING_MATCH_SCORER", + 2: "MATCH_SCORER", +} +var ScorerSpec_Scorer_value = map[string]int32{ + "RESCORING_MATCH_SCORER": 0, + "MATCH_SCORER": 2, +} + +func (x ScorerSpec_Scorer) Enum() *ScorerSpec_Scorer { + p := new(ScorerSpec_Scorer) + *p = x + return p +} +func (x ScorerSpec_Scorer) String() string { + return proto.EnumName(ScorerSpec_Scorer_name, int32(x)) +} +func (x *ScorerSpec_Scorer) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ScorerSpec_Scorer_value, data, "ScorerSpec_Scorer") + if err != nil { + return err + } + *x = ScorerSpec_Scorer(value) + return nil +} +func (ScorerSpec_Scorer) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{31, 0} } + +type SearchParams_CursorType int32 + +const ( + SearchParams_NONE SearchParams_CursorType = 0 + SearchParams_SINGLE SearchParams_CursorType = 1 + SearchParams_PER_RESULT SearchParams_CursorType = 2 +) + +var SearchParams_CursorType_name = map[int32]string{ + 0: "NONE", + 1: "SINGLE", + 2: "PER_RESULT", +} +var SearchParams_CursorType_value = map[string]int32{ + "NONE": 0, + "SINGLE": 1, + "PER_RESULT": 2, +} + +func (x SearchParams_CursorType) Enum() *SearchParams_CursorType { + p := new(SearchParams_CursorType) + *p = x + return p +} +func (x SearchParams_CursorType) String() string { + return proto.EnumName(SearchParams_CursorType_name, int32(x)) +} +func (x *SearchParams_CursorType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(SearchParams_CursorType_value, data, "SearchParams_CursorType") + if err != nil { + return err + } + *x = SearchParams_CursorType(value) + return nil +} +func (SearchParams_CursorType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{38, 0} } + +type SearchParams_ParsingMode int32 + +const ( + SearchParams_STRICT SearchParams_ParsingMode = 0 + SearchParams_RELAXED SearchParams_ParsingMode = 1 +) + +var SearchParams_ParsingMode_name = map[int32]string{ + 0: "STRICT", + 1: "RELAXED", +} +var SearchParams_ParsingMode_value = map[string]int32{ + "STRICT": 0, + "RELAXED": 1, +} + +func (x SearchParams_ParsingMode) Enum() *SearchParams_ParsingMode { + p := new(SearchParams_ParsingMode) + *p = x + return p +} +func (x SearchParams_ParsingMode) String() string { + return proto.EnumName(SearchParams_ParsingMode_name, int32(x)) +} +func (x *SearchParams_ParsingMode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(SearchParams_ParsingMode_value, data, "SearchParams_ParsingMode") + if err != nil { + return err + } + *x = SearchParams_ParsingMode(value) + return nil +} +func (SearchParams_ParsingMode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{38, 1} } + +type Scope struct { + Type *Scope_Type `protobuf:"varint,1,opt,name=type,enum=search.Scope_Type" json:"type,omitempty"` + Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Scope) Reset() { *m = Scope{} } +func (m *Scope) String() string { return proto.CompactTextString(m) } +func (*Scope) ProtoMessage() {} +func (*Scope) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Scope) GetType() Scope_Type { + if m != nil && m.Type != nil { + return *m.Type + } + return Scope_USER_BY_CANONICAL_ID +} + +func (m *Scope) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +type Entry struct { + Scope *Scope `protobuf:"bytes,1,opt,name=scope" json:"scope,omitempty"` + Permission *Entry_Permission `protobuf:"varint,2,opt,name=permission,enum=search.Entry_Permission" json:"permission,omitempty"` + DisplayName *string `protobuf:"bytes,3,opt,name=display_name,json=displayName" json:"display_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Entry) Reset() { *m = Entry{} } +func (m *Entry) String() string { return proto.CompactTextString(m) } +func (*Entry) ProtoMessage() {} +func (*Entry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *Entry) GetScope() *Scope { + if m != nil { + return m.Scope + } + return nil +} + +func (m *Entry) GetPermission() Entry_Permission { + if m != nil && m.Permission != nil { + return *m.Permission + } + return Entry_READ +} + +func (m *Entry) GetDisplayName() string { + if m != nil && m.DisplayName != nil { + return *m.DisplayName + } + return "" +} + +type AccessControlList struct { + Owner *string `protobuf:"bytes,1,opt,name=owner" json:"owner,omitempty"` + Entries []*Entry `protobuf:"bytes,2,rep,name=entries" json:"entries,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AccessControlList) Reset() { *m = AccessControlList{} } +func (m *AccessControlList) String() string { return proto.CompactTextString(m) } +func (*AccessControlList) ProtoMessage() {} +func (*AccessControlList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *AccessControlList) GetOwner() string { + if m != nil && m.Owner != nil { + return *m.Owner + } + return "" +} + +func (m *AccessControlList) GetEntries() []*Entry { + if m != nil { + return m.Entries + } + return nil +} + +type FieldValue struct { + Type *FieldValue_ContentType `protobuf:"varint,1,opt,name=type,enum=search.FieldValue_ContentType,def=0" json:"type,omitempty"` + Language *string `protobuf:"bytes,2,opt,name=language,def=en" json:"language,omitempty"` + StringValue *string `protobuf:"bytes,3,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` + Geo *FieldValue_Geo `protobuf:"group,4,opt,name=Geo,json=geo" json:"geo,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FieldValue) Reset() { *m = FieldValue{} } +func (m *FieldValue) String() string { return proto.CompactTextString(m) } +func (*FieldValue) ProtoMessage() {} +func (*FieldValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +const Default_FieldValue_Type FieldValue_ContentType = FieldValue_TEXT +const Default_FieldValue_Language string = "en" + +func (m *FieldValue) GetType() FieldValue_ContentType { + if m != nil && m.Type != nil { + return *m.Type + } + return Default_FieldValue_Type +} + +func (m *FieldValue) GetLanguage() string { + if m != nil && m.Language != nil { + return *m.Language + } + return Default_FieldValue_Language +} + +func (m *FieldValue) GetStringValue() string { + if m != nil && m.StringValue != nil { + return *m.StringValue + } + return "" +} + +func (m *FieldValue) GetGeo() *FieldValue_Geo { + if m != nil { + return m.Geo + } + return nil +} + +type FieldValue_Geo struct { + Lat *float64 `protobuf:"fixed64,5,req,name=lat" json:"lat,omitempty"` + Lng *float64 `protobuf:"fixed64,6,req,name=lng" json:"lng,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FieldValue_Geo) Reset() { *m = FieldValue_Geo{} } +func (m *FieldValue_Geo) String() string { return proto.CompactTextString(m) } +func (*FieldValue_Geo) ProtoMessage() {} +func (*FieldValue_Geo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} } + +func (m *FieldValue_Geo) GetLat() float64 { + if m != nil && m.Lat != nil { + return *m.Lat + } + return 0 +} + +func (m *FieldValue_Geo) GetLng() float64 { + if m != nil && m.Lng != nil { + return *m.Lng + } + return 0 +} + +type Field struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Value *FieldValue `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Field) Reset() { *m = Field{} } +func (m *Field) String() string { return proto.CompactTextString(m) } +func (*Field) ProtoMessage() {} +func (*Field) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *Field) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *Field) GetValue() *FieldValue { + if m != nil { + return m.Value + } + return nil +} + +type FieldTypes struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Type []FieldValue_ContentType `protobuf:"varint,2,rep,name=type,enum=search.FieldValue_ContentType" json:"type,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FieldTypes) Reset() { *m = FieldTypes{} } +func (m *FieldTypes) String() string { return proto.CompactTextString(m) } +func (*FieldTypes) ProtoMessage() {} +func (*FieldTypes) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *FieldTypes) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FieldTypes) GetType() []FieldValue_ContentType { + if m != nil { + return m.Type + } + return nil +} + +type IndexShardSettings struct { + PrevNumShards []int32 `protobuf:"varint,1,rep,name=prev_num_shards,json=prevNumShards" json:"prev_num_shards,omitempty"` + NumShards *int32 `protobuf:"varint,2,req,name=num_shards,json=numShards,def=1" json:"num_shards,omitempty"` + PrevNumShardsSearchFalse []int32 `protobuf:"varint,3,rep,name=prev_num_shards_search_false,json=prevNumShardsSearchFalse" json:"prev_num_shards_search_false,omitempty"` + LocalReplica *string `protobuf:"bytes,4,opt,name=local_replica,json=localReplica,def=" json:"local_replica,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexShardSettings) Reset() { *m = IndexShardSettings{} } +func (m *IndexShardSettings) String() string { return proto.CompactTextString(m) } +func (*IndexShardSettings) ProtoMessage() {} +func (*IndexShardSettings) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +const Default_IndexShardSettings_NumShards int32 = 1 + +func (m *IndexShardSettings) GetPrevNumShards() []int32 { + if m != nil { + return m.PrevNumShards + } + return nil +} + +func (m *IndexShardSettings) GetNumShards() int32 { + if m != nil && m.NumShards != nil { + return *m.NumShards + } + return Default_IndexShardSettings_NumShards +} + +func (m *IndexShardSettings) GetPrevNumShardsSearchFalse() []int32 { + if m != nil { + return m.PrevNumShardsSearchFalse + } + return nil +} + +func (m *IndexShardSettings) GetLocalReplica() string { + if m != nil && m.LocalReplica != nil { + return *m.LocalReplica + } + return "" +} + +type FacetValue struct { + Type *FacetValue_ContentType `protobuf:"varint,1,opt,name=type,enum=search.FacetValue_ContentType,def=2" json:"type,omitempty"` + StringValue *string `protobuf:"bytes,3,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetValue) Reset() { *m = FacetValue{} } +func (m *FacetValue) String() string { return proto.CompactTextString(m) } +func (*FacetValue) ProtoMessage() {} +func (*FacetValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +const Default_FacetValue_Type FacetValue_ContentType = FacetValue_ATOM + +func (m *FacetValue) GetType() FacetValue_ContentType { + if m != nil && m.Type != nil { + return *m.Type + } + return Default_FacetValue_Type +} + +func (m *FacetValue) GetStringValue() string { + if m != nil && m.StringValue != nil { + return *m.StringValue + } + return "" +} + +type Facet struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Value *FacetValue `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Facet) Reset() { *m = Facet{} } +func (m *Facet) String() string { return proto.CompactTextString(m) } +func (*Facet) ProtoMessage() {} +func (*Facet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *Facet) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *Facet) GetValue() *FacetValue { + if m != nil { + return m.Value + } + return nil +} + +type DocumentMetadata struct { + Version *int64 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` + CommittedStVersion *int64 `protobuf:"varint,2,opt,name=committed_st_version,json=committedStVersion" json:"committed_st_version,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DocumentMetadata) Reset() { *m = DocumentMetadata{} } +func (m *DocumentMetadata) String() string { return proto.CompactTextString(m) } +func (*DocumentMetadata) ProtoMessage() {} +func (*DocumentMetadata) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *DocumentMetadata) GetVersion() int64 { + if m != nil && m.Version != nil { + return *m.Version + } + return 0 +} + +func (m *DocumentMetadata) GetCommittedStVersion() int64 { + if m != nil && m.CommittedStVersion != nil { + return *m.CommittedStVersion + } + return 0 +} + +type Document struct { + Id *string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Language *string `protobuf:"bytes,2,opt,name=language,def=en" json:"language,omitempty"` + Field []*Field `protobuf:"bytes,3,rep,name=field" json:"field,omitempty"` + OrderId *int32 `protobuf:"varint,4,opt,name=order_id,json=orderId" json:"order_id,omitempty"` + OrderIdSource *Document_OrderIdSource `protobuf:"varint,6,opt,name=order_id_source,json=orderIdSource,enum=search.Document_OrderIdSource,def=1" json:"order_id_source,omitempty"` + Storage *Document_Storage `protobuf:"varint,5,opt,name=storage,enum=search.Document_Storage,def=0" json:"storage,omitempty"` + Facet []*Facet `protobuf:"bytes,8,rep,name=facet" json:"facet,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Document) Reset() { *m = Document{} } +func (m *Document) String() string { return proto.CompactTextString(m) } +func (*Document) ProtoMessage() {} +func (*Document) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +const Default_Document_Language string = "en" +const Default_Document_OrderIdSource Document_OrderIdSource = Document_SUPPLIED +const Default_Document_Storage Document_Storage = Document_DISK + +func (m *Document) GetId() string { + if m != nil && m.Id != nil { + return *m.Id + } + return "" +} + +func (m *Document) GetLanguage() string { + if m != nil && m.Language != nil { + return *m.Language + } + return Default_Document_Language +} + +func (m *Document) GetField() []*Field { + if m != nil { + return m.Field + } + return nil +} + +func (m *Document) GetOrderId() int32 { + if m != nil && m.OrderId != nil { + return *m.OrderId + } + return 0 +} + +func (m *Document) GetOrderIdSource() Document_OrderIdSource { + if m != nil && m.OrderIdSource != nil { + return *m.OrderIdSource + } + return Default_Document_OrderIdSource +} + +func (m *Document) GetStorage() Document_Storage { + if m != nil && m.Storage != nil { + return *m.Storage + } + return Default_Document_Storage +} + +func (m *Document) GetFacet() []*Facet { + if m != nil { + return m.Facet + } + return nil +} + +type SearchServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *SearchServiceError) Reset() { *m = SearchServiceError{} } +func (m *SearchServiceError) String() string { return proto.CompactTextString(m) } +func (*SearchServiceError) ProtoMessage() {} +func (*SearchServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +type RequestStatus struct { + Code *SearchServiceError_ErrorCode `protobuf:"varint,1,req,name=code,enum=search.SearchServiceError_ErrorCode" json:"code,omitempty"` + ErrorDetail *string `protobuf:"bytes,2,opt,name=error_detail,json=errorDetail" json:"error_detail,omitempty"` + CanonicalCode *int32 `protobuf:"varint,3,opt,name=canonical_code,json=canonicalCode" json:"canonical_code,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *RequestStatus) Reset() { *m = RequestStatus{} } +func (m *RequestStatus) String() string { return proto.CompactTextString(m) } +func (*RequestStatus) ProtoMessage() {} +func (*RequestStatus) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *RequestStatus) GetCode() SearchServiceError_ErrorCode { + if m != nil && m.Code != nil { + return *m.Code + } + return SearchServiceError_OK +} + +func (m *RequestStatus) GetErrorDetail() string { + if m != nil && m.ErrorDetail != nil { + return *m.ErrorDetail + } + return "" +} + +func (m *RequestStatus) GetCanonicalCode() int32 { + if m != nil && m.CanonicalCode != nil { + return *m.CanonicalCode + } + return 0 +} + +type IndexSpec struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Consistency *IndexSpec_Consistency `protobuf:"varint,2,opt,name=consistency,enum=search.IndexSpec_Consistency,def=1" json:"consistency,omitempty"` + Namespace *string `protobuf:"bytes,3,opt,name=namespace" json:"namespace,omitempty"` + Version *int32 `protobuf:"varint,4,opt,name=version" json:"version,omitempty"` + Source *IndexSpec_Source `protobuf:"varint,5,opt,name=source,enum=search.IndexSpec_Source,def=0" json:"source,omitempty"` + Mode *IndexSpec_Mode `protobuf:"varint,6,opt,name=mode,enum=search.IndexSpec_Mode,def=0" json:"mode,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexSpec) Reset() { *m = IndexSpec{} } +func (m *IndexSpec) String() string { return proto.CompactTextString(m) } +func (*IndexSpec) ProtoMessage() {} +func (*IndexSpec) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +const Default_IndexSpec_Consistency IndexSpec_Consistency = IndexSpec_PER_DOCUMENT +const Default_IndexSpec_Source IndexSpec_Source = IndexSpec_SEARCH +const Default_IndexSpec_Mode IndexSpec_Mode = IndexSpec_PRIORITY + +func (m *IndexSpec) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *IndexSpec) GetConsistency() IndexSpec_Consistency { + if m != nil && m.Consistency != nil { + return *m.Consistency + } + return Default_IndexSpec_Consistency +} + +func (m *IndexSpec) GetNamespace() string { + if m != nil && m.Namespace != nil { + return *m.Namespace + } + return "" +} + +func (m *IndexSpec) GetVersion() int32 { + if m != nil && m.Version != nil { + return *m.Version + } + return 0 +} + +func (m *IndexSpec) GetSource() IndexSpec_Source { + if m != nil && m.Source != nil { + return *m.Source + } + return Default_IndexSpec_Source +} + +func (m *IndexSpec) GetMode() IndexSpec_Mode { + if m != nil && m.Mode != nil { + return *m.Mode + } + return Default_IndexSpec_Mode +} + +type IndexMetadata struct { + IndexSpec *IndexSpec `protobuf:"bytes,1,req,name=index_spec,json=indexSpec" json:"index_spec,omitempty"` + Field []*FieldTypes `protobuf:"bytes,2,rep,name=field" json:"field,omitempty"` + Storage *IndexMetadata_Storage `protobuf:"bytes,3,opt,name=storage" json:"storage,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexMetadata) Reset() { *m = IndexMetadata{} } +func (m *IndexMetadata) String() string { return proto.CompactTextString(m) } +func (*IndexMetadata) ProtoMessage() {} +func (*IndexMetadata) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *IndexMetadata) GetIndexSpec() *IndexSpec { + if m != nil { + return m.IndexSpec + } + return nil +} + +func (m *IndexMetadata) GetField() []*FieldTypes { + if m != nil { + return m.Field + } + return nil +} + +func (m *IndexMetadata) GetStorage() *IndexMetadata_Storage { + if m != nil { + return m.Storage + } + return nil +} + +type IndexMetadata_Storage struct { + AmountUsed *int64 `protobuf:"varint,1,opt,name=amount_used,json=amountUsed" json:"amount_used,omitempty"` + Limit *int64 `protobuf:"varint,2,opt,name=limit" json:"limit,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexMetadata_Storage) Reset() { *m = IndexMetadata_Storage{} } +func (m *IndexMetadata_Storage) String() string { return proto.CompactTextString(m) } +func (*IndexMetadata_Storage) ProtoMessage() {} +func (*IndexMetadata_Storage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14, 0} } + +func (m *IndexMetadata_Storage) GetAmountUsed() int64 { + if m != nil && m.AmountUsed != nil { + return *m.AmountUsed + } + return 0 +} + +func (m *IndexMetadata_Storage) GetLimit() int64 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return 0 +} + +type IndexDocumentParams struct { + Document []*Document `protobuf:"bytes,1,rep,name=document" json:"document,omitempty"` + Freshness *IndexDocumentParams_Freshness `protobuf:"varint,2,opt,name=freshness,enum=search.IndexDocumentParams_Freshness,def=0" json:"freshness,omitempty"` + IndexSpec *IndexSpec `protobuf:"bytes,3,req,name=index_spec,json=indexSpec" json:"index_spec,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexDocumentParams) Reset() { *m = IndexDocumentParams{} } +func (m *IndexDocumentParams) String() string { return proto.CompactTextString(m) } +func (*IndexDocumentParams) ProtoMessage() {} +func (*IndexDocumentParams) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +const Default_IndexDocumentParams_Freshness IndexDocumentParams_Freshness = IndexDocumentParams_SYNCHRONOUSLY + +func (m *IndexDocumentParams) GetDocument() []*Document { + if m != nil { + return m.Document + } + return nil +} + +func (m *IndexDocumentParams) GetFreshness() IndexDocumentParams_Freshness { + if m != nil && m.Freshness != nil { + return *m.Freshness + } + return Default_IndexDocumentParams_Freshness +} + +func (m *IndexDocumentParams) GetIndexSpec() *IndexSpec { + if m != nil { + return m.IndexSpec + } + return nil +} + +type IndexDocumentRequest struct { + Params *IndexDocumentParams `protobuf:"bytes,1,req,name=params" json:"params,omitempty"` + AppId []byte `protobuf:"bytes,3,opt,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexDocumentRequest) Reset() { *m = IndexDocumentRequest{} } +func (m *IndexDocumentRequest) String() string { return proto.CompactTextString(m) } +func (*IndexDocumentRequest) ProtoMessage() {} +func (*IndexDocumentRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *IndexDocumentRequest) GetParams() *IndexDocumentParams { + if m != nil { + return m.Params + } + return nil +} + +func (m *IndexDocumentRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type IndexDocumentResponse struct { + Status []*RequestStatus `protobuf:"bytes,1,rep,name=status" json:"status,omitempty"` + DocId []string `protobuf:"bytes,2,rep,name=doc_id,json=docId" json:"doc_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IndexDocumentResponse) Reset() { *m = IndexDocumentResponse{} } +func (m *IndexDocumentResponse) String() string { return proto.CompactTextString(m) } +func (*IndexDocumentResponse) ProtoMessage() {} +func (*IndexDocumentResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *IndexDocumentResponse) GetStatus() []*RequestStatus { + if m != nil { + return m.Status + } + return nil +} + +func (m *IndexDocumentResponse) GetDocId() []string { + if m != nil { + return m.DocId + } + return nil +} + +type DeleteDocumentParams struct { + DocId []string `protobuf:"bytes,1,rep,name=doc_id,json=docId" json:"doc_id,omitempty"` + IndexSpec *IndexSpec `protobuf:"bytes,2,req,name=index_spec,json=indexSpec" json:"index_spec,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteDocumentParams) Reset() { *m = DeleteDocumentParams{} } +func (m *DeleteDocumentParams) String() string { return proto.CompactTextString(m) } +func (*DeleteDocumentParams) ProtoMessage() {} +func (*DeleteDocumentParams) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *DeleteDocumentParams) GetDocId() []string { + if m != nil { + return m.DocId + } + return nil +} + +func (m *DeleteDocumentParams) GetIndexSpec() *IndexSpec { + if m != nil { + return m.IndexSpec + } + return nil +} + +type DeleteDocumentRequest struct { + Params *DeleteDocumentParams `protobuf:"bytes,1,req,name=params" json:"params,omitempty"` + AppId []byte `protobuf:"bytes,3,opt,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteDocumentRequest) Reset() { *m = DeleteDocumentRequest{} } +func (m *DeleteDocumentRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteDocumentRequest) ProtoMessage() {} +func (*DeleteDocumentRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +func (m *DeleteDocumentRequest) GetParams() *DeleteDocumentParams { + if m != nil { + return m.Params + } + return nil +} + +func (m *DeleteDocumentRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type DeleteDocumentResponse struct { + Status []*RequestStatus `protobuf:"bytes,1,rep,name=status" json:"status,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteDocumentResponse) Reset() { *m = DeleteDocumentResponse{} } +func (m *DeleteDocumentResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteDocumentResponse) ProtoMessage() {} +func (*DeleteDocumentResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } + +func (m *DeleteDocumentResponse) GetStatus() []*RequestStatus { + if m != nil { + return m.Status + } + return nil +} + +type ListDocumentsParams struct { + IndexSpec *IndexSpec `protobuf:"bytes,1,req,name=index_spec,json=indexSpec" json:"index_spec,omitempty"` + StartDocId *string `protobuf:"bytes,2,opt,name=start_doc_id,json=startDocId" json:"start_doc_id,omitempty"` + IncludeStartDoc *bool `protobuf:"varint,3,opt,name=include_start_doc,json=includeStartDoc,def=1" json:"include_start_doc,omitempty"` + Limit *int32 `protobuf:"varint,4,opt,name=limit,def=100" json:"limit,omitempty"` + KeysOnly *bool `protobuf:"varint,5,opt,name=keys_only,json=keysOnly" json:"keys_only,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListDocumentsParams) Reset() { *m = ListDocumentsParams{} } +func (m *ListDocumentsParams) String() string { return proto.CompactTextString(m) } +func (*ListDocumentsParams) ProtoMessage() {} +func (*ListDocumentsParams) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +const Default_ListDocumentsParams_IncludeStartDoc bool = true +const Default_ListDocumentsParams_Limit int32 = 100 + +func (m *ListDocumentsParams) GetIndexSpec() *IndexSpec { + if m != nil { + return m.IndexSpec + } + return nil +} + +func (m *ListDocumentsParams) GetStartDocId() string { + if m != nil && m.StartDocId != nil { + return *m.StartDocId + } + return "" +} + +func (m *ListDocumentsParams) GetIncludeStartDoc() bool { + if m != nil && m.IncludeStartDoc != nil { + return *m.IncludeStartDoc + } + return Default_ListDocumentsParams_IncludeStartDoc +} + +func (m *ListDocumentsParams) GetLimit() int32 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return Default_ListDocumentsParams_Limit +} + +func (m *ListDocumentsParams) GetKeysOnly() bool { + if m != nil && m.KeysOnly != nil { + return *m.KeysOnly + } + return false +} + +type ListDocumentsRequest struct { + Params *ListDocumentsParams `protobuf:"bytes,1,req,name=params" json:"params,omitempty"` + AppId []byte `protobuf:"bytes,2,opt,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListDocumentsRequest) Reset() { *m = ListDocumentsRequest{} } +func (m *ListDocumentsRequest) String() string { return proto.CompactTextString(m) } +func (*ListDocumentsRequest) ProtoMessage() {} +func (*ListDocumentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } + +func (m *ListDocumentsRequest) GetParams() *ListDocumentsParams { + if m != nil { + return m.Params + } + return nil +} + +func (m *ListDocumentsRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type ListDocumentsResponse struct { + Status *RequestStatus `protobuf:"bytes,1,req,name=status" json:"status,omitempty"` + Document []*Document `protobuf:"bytes,2,rep,name=document" json:"document,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListDocumentsResponse) Reset() { *m = ListDocumentsResponse{} } +func (m *ListDocumentsResponse) String() string { return proto.CompactTextString(m) } +func (*ListDocumentsResponse) ProtoMessage() {} +func (*ListDocumentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +func (m *ListDocumentsResponse) GetStatus() *RequestStatus { + if m != nil { + return m.Status + } + return nil +} + +func (m *ListDocumentsResponse) GetDocument() []*Document { + if m != nil { + return m.Document + } + return nil +} + +type ListIndexesParams struct { + FetchSchema *bool `protobuf:"varint,1,opt,name=fetch_schema,json=fetchSchema" json:"fetch_schema,omitempty"` + Limit *int32 `protobuf:"varint,2,opt,name=limit,def=20" json:"limit,omitempty"` + Namespace *string `protobuf:"bytes,3,opt,name=namespace" json:"namespace,omitempty"` + StartIndexName *string `protobuf:"bytes,4,opt,name=start_index_name,json=startIndexName" json:"start_index_name,omitempty"` + IncludeStartIndex *bool `protobuf:"varint,5,opt,name=include_start_index,json=includeStartIndex,def=1" json:"include_start_index,omitempty"` + IndexNamePrefix *string `protobuf:"bytes,6,opt,name=index_name_prefix,json=indexNamePrefix" json:"index_name_prefix,omitempty"` + Offset *int32 `protobuf:"varint,7,opt,name=offset" json:"offset,omitempty"` + Source *IndexSpec_Source `protobuf:"varint,8,opt,name=source,enum=search.IndexSpec_Source,def=0" json:"source,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListIndexesParams) Reset() { *m = ListIndexesParams{} } +func (m *ListIndexesParams) String() string { return proto.CompactTextString(m) } +func (*ListIndexesParams) ProtoMessage() {} +func (*ListIndexesParams) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +const Default_ListIndexesParams_Limit int32 = 20 +const Default_ListIndexesParams_IncludeStartIndex bool = true +const Default_ListIndexesParams_Source IndexSpec_Source = IndexSpec_SEARCH + +func (m *ListIndexesParams) GetFetchSchema() bool { + if m != nil && m.FetchSchema != nil { + return *m.FetchSchema + } + return false +} + +func (m *ListIndexesParams) GetLimit() int32 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return Default_ListIndexesParams_Limit +} + +func (m *ListIndexesParams) GetNamespace() string { + if m != nil && m.Namespace != nil { + return *m.Namespace + } + return "" +} + +func (m *ListIndexesParams) GetStartIndexName() string { + if m != nil && m.StartIndexName != nil { + return *m.StartIndexName + } + return "" +} + +func (m *ListIndexesParams) GetIncludeStartIndex() bool { + if m != nil && m.IncludeStartIndex != nil { + return *m.IncludeStartIndex + } + return Default_ListIndexesParams_IncludeStartIndex +} + +func (m *ListIndexesParams) GetIndexNamePrefix() string { + if m != nil && m.IndexNamePrefix != nil { + return *m.IndexNamePrefix + } + return "" +} + +func (m *ListIndexesParams) GetOffset() int32 { + if m != nil && m.Offset != nil { + return *m.Offset + } + return 0 +} + +func (m *ListIndexesParams) GetSource() IndexSpec_Source { + if m != nil && m.Source != nil { + return *m.Source + } + return Default_ListIndexesParams_Source +} + +type ListIndexesRequest struct { + Params *ListIndexesParams `protobuf:"bytes,1,req,name=params" json:"params,omitempty"` + AppId []byte `protobuf:"bytes,3,opt,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListIndexesRequest) Reset() { *m = ListIndexesRequest{} } +func (m *ListIndexesRequest) String() string { return proto.CompactTextString(m) } +func (*ListIndexesRequest) ProtoMessage() {} +func (*ListIndexesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } + +func (m *ListIndexesRequest) GetParams() *ListIndexesParams { + if m != nil { + return m.Params + } + return nil +} + +func (m *ListIndexesRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type ListIndexesResponse struct { + Status *RequestStatus `protobuf:"bytes,1,req,name=status" json:"status,omitempty"` + IndexMetadata []*IndexMetadata `protobuf:"bytes,2,rep,name=index_metadata,json=indexMetadata" json:"index_metadata,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListIndexesResponse) Reset() { *m = ListIndexesResponse{} } +func (m *ListIndexesResponse) String() string { return proto.CompactTextString(m) } +func (*ListIndexesResponse) ProtoMessage() {} +func (*ListIndexesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } + +func (m *ListIndexesResponse) GetStatus() *RequestStatus { + if m != nil { + return m.Status + } + return nil +} + +func (m *ListIndexesResponse) GetIndexMetadata() []*IndexMetadata { + if m != nil { + return m.IndexMetadata + } + return nil +} + +type DeleteSchemaParams struct { + Source *IndexSpec_Source `protobuf:"varint,1,opt,name=source,enum=search.IndexSpec_Source,def=0" json:"source,omitempty"` + IndexSpec []*IndexSpec `protobuf:"bytes,2,rep,name=index_spec,json=indexSpec" json:"index_spec,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteSchemaParams) Reset() { *m = DeleteSchemaParams{} } +func (m *DeleteSchemaParams) String() string { return proto.CompactTextString(m) } +func (*DeleteSchemaParams) ProtoMessage() {} +func (*DeleteSchemaParams) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } + +const Default_DeleteSchemaParams_Source IndexSpec_Source = IndexSpec_SEARCH + +func (m *DeleteSchemaParams) GetSource() IndexSpec_Source { + if m != nil && m.Source != nil { + return *m.Source + } + return Default_DeleteSchemaParams_Source +} + +func (m *DeleteSchemaParams) GetIndexSpec() []*IndexSpec { + if m != nil { + return m.IndexSpec + } + return nil +} + +type DeleteSchemaRequest struct { + Params *DeleteSchemaParams `protobuf:"bytes,1,req,name=params" json:"params,omitempty"` + AppId []byte `protobuf:"bytes,3,opt,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteSchemaRequest) Reset() { *m = DeleteSchemaRequest{} } +func (m *DeleteSchemaRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteSchemaRequest) ProtoMessage() {} +func (*DeleteSchemaRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +func (m *DeleteSchemaRequest) GetParams() *DeleteSchemaParams { + if m != nil { + return m.Params + } + return nil +} + +func (m *DeleteSchemaRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type DeleteSchemaResponse struct { + Status []*RequestStatus `protobuf:"bytes,1,rep,name=status" json:"status,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DeleteSchemaResponse) Reset() { *m = DeleteSchemaResponse{} } +func (m *DeleteSchemaResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteSchemaResponse) ProtoMessage() {} +func (*DeleteSchemaResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } + +func (m *DeleteSchemaResponse) GetStatus() []*RequestStatus { + if m != nil { + return m.Status + } + return nil +} + +type SortSpec struct { + SortExpression *string `protobuf:"bytes,1,req,name=sort_expression,json=sortExpression" json:"sort_expression,omitempty"` + SortDescending *bool `protobuf:"varint,2,opt,name=sort_descending,json=sortDescending,def=1" json:"sort_descending,omitempty"` + DefaultValueText *string `protobuf:"bytes,4,opt,name=default_value_text,json=defaultValueText" json:"default_value_text,omitempty"` + DefaultValueNumeric *float64 `protobuf:"fixed64,5,opt,name=default_value_numeric,json=defaultValueNumeric" json:"default_value_numeric,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SortSpec) Reset() { *m = SortSpec{} } +func (m *SortSpec) String() string { return proto.CompactTextString(m) } +func (*SortSpec) ProtoMessage() {} +func (*SortSpec) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } + +const Default_SortSpec_SortDescending bool = true + +func (m *SortSpec) GetSortExpression() string { + if m != nil && m.SortExpression != nil { + return *m.SortExpression + } + return "" +} + +func (m *SortSpec) GetSortDescending() bool { + if m != nil && m.SortDescending != nil { + return *m.SortDescending + } + return Default_SortSpec_SortDescending +} + +func (m *SortSpec) GetDefaultValueText() string { + if m != nil && m.DefaultValueText != nil { + return *m.DefaultValueText + } + return "" +} + +func (m *SortSpec) GetDefaultValueNumeric() float64 { + if m != nil && m.DefaultValueNumeric != nil { + return *m.DefaultValueNumeric + } + return 0 +} + +type ScorerSpec struct { + Scorer *ScorerSpec_Scorer `protobuf:"varint,1,opt,name=scorer,enum=search.ScorerSpec_Scorer,def=2" json:"scorer,omitempty"` + Limit *int32 `protobuf:"varint,2,opt,name=limit,def=1000" json:"limit,omitempty"` + MatchScorerParameters *string `protobuf:"bytes,9,opt,name=match_scorer_parameters,json=matchScorerParameters" json:"match_scorer_parameters,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ScorerSpec) Reset() { *m = ScorerSpec{} } +func (m *ScorerSpec) String() string { return proto.CompactTextString(m) } +func (*ScorerSpec) ProtoMessage() {} +func (*ScorerSpec) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +const Default_ScorerSpec_Scorer ScorerSpec_Scorer = ScorerSpec_MATCH_SCORER +const Default_ScorerSpec_Limit int32 = 1000 + +func (m *ScorerSpec) GetScorer() ScorerSpec_Scorer { + if m != nil && m.Scorer != nil { + return *m.Scorer + } + return Default_ScorerSpec_Scorer +} + +func (m *ScorerSpec) GetLimit() int32 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return Default_ScorerSpec_Limit +} + +func (m *ScorerSpec) GetMatchScorerParameters() string { + if m != nil && m.MatchScorerParameters != nil { + return *m.MatchScorerParameters + } + return "" +} + +type FieldSpec struct { + Name []string `protobuf:"bytes,1,rep,name=name" json:"name,omitempty"` + Expression []*FieldSpec_Expression `protobuf:"group,2,rep,name=Expression,json=expression" json:"expression,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FieldSpec) Reset() { *m = FieldSpec{} } +func (m *FieldSpec) String() string { return proto.CompactTextString(m) } +func (*FieldSpec) ProtoMessage() {} +func (*FieldSpec) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +func (m *FieldSpec) GetName() []string { + if m != nil { + return m.Name + } + return nil +} + +func (m *FieldSpec) GetExpression() []*FieldSpec_Expression { + if m != nil { + return m.Expression + } + return nil +} + +type FieldSpec_Expression struct { + Name *string `protobuf:"bytes,3,req,name=name" json:"name,omitempty"` + Expression *string `protobuf:"bytes,4,req,name=expression" json:"expression,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FieldSpec_Expression) Reset() { *m = FieldSpec_Expression{} } +func (m *FieldSpec_Expression) String() string { return proto.CompactTextString(m) } +func (*FieldSpec_Expression) ProtoMessage() {} +func (*FieldSpec_Expression) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32, 0} } + +func (m *FieldSpec_Expression) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FieldSpec_Expression) GetExpression() string { + if m != nil && m.Expression != nil { + return *m.Expression + } + return "" +} + +type FacetRange struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Start *string `protobuf:"bytes,2,opt,name=start" json:"start,omitempty"` + End *string `protobuf:"bytes,3,opt,name=end" json:"end,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetRange) Reset() { *m = FacetRange{} } +func (m *FacetRange) String() string { return proto.CompactTextString(m) } +func (*FacetRange) ProtoMessage() {} +func (*FacetRange) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } + +func (m *FacetRange) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FacetRange) GetStart() string { + if m != nil && m.Start != nil { + return *m.Start + } + return "" +} + +func (m *FacetRange) GetEnd() string { + if m != nil && m.End != nil { + return *m.End + } + return "" +} + +type FacetRequestParam struct { + ValueLimit *int32 `protobuf:"varint,1,opt,name=value_limit,json=valueLimit" json:"value_limit,omitempty"` + Range []*FacetRange `protobuf:"bytes,2,rep,name=range" json:"range,omitempty"` + ValueConstraint []string `protobuf:"bytes,3,rep,name=value_constraint,json=valueConstraint" json:"value_constraint,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetRequestParam) Reset() { *m = FacetRequestParam{} } +func (m *FacetRequestParam) String() string { return proto.CompactTextString(m) } +func (*FacetRequestParam) ProtoMessage() {} +func (*FacetRequestParam) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } + +func (m *FacetRequestParam) GetValueLimit() int32 { + if m != nil && m.ValueLimit != nil { + return *m.ValueLimit + } + return 0 +} + +func (m *FacetRequestParam) GetRange() []*FacetRange { + if m != nil { + return m.Range + } + return nil +} + +func (m *FacetRequestParam) GetValueConstraint() []string { + if m != nil { + return m.ValueConstraint + } + return nil +} + +type FacetAutoDetectParam struct { + ValueLimit *int32 `protobuf:"varint,1,opt,name=value_limit,json=valueLimit,def=10" json:"value_limit,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetAutoDetectParam) Reset() { *m = FacetAutoDetectParam{} } +func (m *FacetAutoDetectParam) String() string { return proto.CompactTextString(m) } +func (*FacetAutoDetectParam) ProtoMessage() {} +func (*FacetAutoDetectParam) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} } + +const Default_FacetAutoDetectParam_ValueLimit int32 = 10 + +func (m *FacetAutoDetectParam) GetValueLimit() int32 { + if m != nil && m.ValueLimit != nil { + return *m.ValueLimit + } + return Default_FacetAutoDetectParam_ValueLimit +} + +type FacetRequest struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Params *FacetRequestParam `protobuf:"bytes,2,opt,name=params" json:"params,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetRequest) Reset() { *m = FacetRequest{} } +func (m *FacetRequest) String() string { return proto.CompactTextString(m) } +func (*FacetRequest) ProtoMessage() {} +func (*FacetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} } + +func (m *FacetRequest) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FacetRequest) GetParams() *FacetRequestParam { + if m != nil { + return m.Params + } + return nil +} + +type FacetRefinement struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` + Range *FacetRefinement_Range `protobuf:"bytes,3,opt,name=range" json:"range,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetRefinement) Reset() { *m = FacetRefinement{} } +func (m *FacetRefinement) String() string { return proto.CompactTextString(m) } +func (*FacetRefinement) ProtoMessage() {} +func (*FacetRefinement) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} } + +func (m *FacetRefinement) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FacetRefinement) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +func (m *FacetRefinement) GetRange() *FacetRefinement_Range { + if m != nil { + return m.Range + } + return nil +} + +type FacetRefinement_Range struct { + Start *string `protobuf:"bytes,1,opt,name=start" json:"start,omitempty"` + End *string `protobuf:"bytes,2,opt,name=end" json:"end,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetRefinement_Range) Reset() { *m = FacetRefinement_Range{} } +func (m *FacetRefinement_Range) String() string { return proto.CompactTextString(m) } +func (*FacetRefinement_Range) ProtoMessage() {} +func (*FacetRefinement_Range) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37, 0} } + +func (m *FacetRefinement_Range) GetStart() string { + if m != nil && m.Start != nil { + return *m.Start + } + return "" +} + +func (m *FacetRefinement_Range) GetEnd() string { + if m != nil && m.End != nil { + return *m.End + } + return "" +} + +type SearchParams struct { + IndexSpec *IndexSpec `protobuf:"bytes,1,req,name=index_spec,json=indexSpec" json:"index_spec,omitempty"` + Query *string `protobuf:"bytes,2,req,name=query" json:"query,omitempty"` + Cursor *string `protobuf:"bytes,4,opt,name=cursor" json:"cursor,omitempty"` + Offset *int32 `protobuf:"varint,11,opt,name=offset" json:"offset,omitempty"` + CursorType *SearchParams_CursorType `protobuf:"varint,5,opt,name=cursor_type,json=cursorType,enum=search.SearchParams_CursorType,def=0" json:"cursor_type,omitempty"` + Limit *int32 `protobuf:"varint,6,opt,name=limit,def=20" json:"limit,omitempty"` + MatchedCountAccuracy *int32 `protobuf:"varint,7,opt,name=matched_count_accuracy,json=matchedCountAccuracy" json:"matched_count_accuracy,omitempty"` + SortSpec []*SortSpec `protobuf:"bytes,8,rep,name=sort_spec,json=sortSpec" json:"sort_spec,omitempty"` + ScorerSpec *ScorerSpec `protobuf:"bytes,9,opt,name=scorer_spec,json=scorerSpec" json:"scorer_spec,omitempty"` + FieldSpec *FieldSpec `protobuf:"bytes,10,opt,name=field_spec,json=fieldSpec" json:"field_spec,omitempty"` + KeysOnly *bool `protobuf:"varint,12,opt,name=keys_only,json=keysOnly" json:"keys_only,omitempty"` + ParsingMode *SearchParams_ParsingMode `protobuf:"varint,13,opt,name=parsing_mode,json=parsingMode,enum=search.SearchParams_ParsingMode,def=0" json:"parsing_mode,omitempty"` + AutoDiscoverFacetCount *int32 `protobuf:"varint,15,opt,name=auto_discover_facet_count,json=autoDiscoverFacetCount,def=0" json:"auto_discover_facet_count,omitempty"` + IncludeFacet []*FacetRequest `protobuf:"bytes,16,rep,name=include_facet,json=includeFacet" json:"include_facet,omitempty"` + FacetRefinement []*FacetRefinement `protobuf:"bytes,17,rep,name=facet_refinement,json=facetRefinement" json:"facet_refinement,omitempty"` + FacetAutoDetectParam *FacetAutoDetectParam `protobuf:"bytes,18,opt,name=facet_auto_detect_param,json=facetAutoDetectParam" json:"facet_auto_detect_param,omitempty"` + FacetDepth *int32 `protobuf:"varint,19,opt,name=facet_depth,json=facetDepth,def=1000" json:"facet_depth,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SearchParams) Reset() { *m = SearchParams{} } +func (m *SearchParams) String() string { return proto.CompactTextString(m) } +func (*SearchParams) ProtoMessage() {} +func (*SearchParams) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{38} } + +const Default_SearchParams_CursorType SearchParams_CursorType = SearchParams_NONE +const Default_SearchParams_Limit int32 = 20 +const Default_SearchParams_ParsingMode SearchParams_ParsingMode = SearchParams_STRICT +const Default_SearchParams_AutoDiscoverFacetCount int32 = 0 +const Default_SearchParams_FacetDepth int32 = 1000 + +func (m *SearchParams) GetIndexSpec() *IndexSpec { + if m != nil { + return m.IndexSpec + } + return nil +} + +func (m *SearchParams) GetQuery() string { + if m != nil && m.Query != nil { + return *m.Query + } + return "" +} + +func (m *SearchParams) GetCursor() string { + if m != nil && m.Cursor != nil { + return *m.Cursor + } + return "" +} + +func (m *SearchParams) GetOffset() int32 { + if m != nil && m.Offset != nil { + return *m.Offset + } + return 0 +} + +func (m *SearchParams) GetCursorType() SearchParams_CursorType { + if m != nil && m.CursorType != nil { + return *m.CursorType + } + return Default_SearchParams_CursorType +} + +func (m *SearchParams) GetLimit() int32 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return Default_SearchParams_Limit +} + +func (m *SearchParams) GetMatchedCountAccuracy() int32 { + if m != nil && m.MatchedCountAccuracy != nil { + return *m.MatchedCountAccuracy + } + return 0 +} + +func (m *SearchParams) GetSortSpec() []*SortSpec { + if m != nil { + return m.SortSpec + } + return nil +} + +func (m *SearchParams) GetScorerSpec() *ScorerSpec { + if m != nil { + return m.ScorerSpec + } + return nil +} + +func (m *SearchParams) GetFieldSpec() *FieldSpec { + if m != nil { + return m.FieldSpec + } + return nil +} + +func (m *SearchParams) GetKeysOnly() bool { + if m != nil && m.KeysOnly != nil { + return *m.KeysOnly + } + return false +} + +func (m *SearchParams) GetParsingMode() SearchParams_ParsingMode { + if m != nil && m.ParsingMode != nil { + return *m.ParsingMode + } + return Default_SearchParams_ParsingMode +} + +func (m *SearchParams) GetAutoDiscoverFacetCount() int32 { + if m != nil && m.AutoDiscoverFacetCount != nil { + return *m.AutoDiscoverFacetCount + } + return Default_SearchParams_AutoDiscoverFacetCount +} + +func (m *SearchParams) GetIncludeFacet() []*FacetRequest { + if m != nil { + return m.IncludeFacet + } + return nil +} + +func (m *SearchParams) GetFacetRefinement() []*FacetRefinement { + if m != nil { + return m.FacetRefinement + } + return nil +} + +func (m *SearchParams) GetFacetAutoDetectParam() *FacetAutoDetectParam { + if m != nil { + return m.FacetAutoDetectParam + } + return nil +} + +func (m *SearchParams) GetFacetDepth() int32 { + if m != nil && m.FacetDepth != nil { + return *m.FacetDepth + } + return Default_SearchParams_FacetDepth +} + +type SearchRequest struct { + Params *SearchParams `protobuf:"bytes,1,req,name=params" json:"params,omitempty"` + AppId []byte `protobuf:"bytes,3,opt,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SearchRequest) Reset() { *m = SearchRequest{} } +func (m *SearchRequest) String() string { return proto.CompactTextString(m) } +func (*SearchRequest) ProtoMessage() {} +func (*SearchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} } + +func (m *SearchRequest) GetParams() *SearchParams { + if m != nil { + return m.Params + } + return nil +} + +func (m *SearchRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type FacetResultValue struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Count *int32 `protobuf:"varint,2,req,name=count" json:"count,omitempty"` + Refinement *FacetRefinement `protobuf:"bytes,3,req,name=refinement" json:"refinement,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetResultValue) Reset() { *m = FacetResultValue{} } +func (m *FacetResultValue) String() string { return proto.CompactTextString(m) } +func (*FacetResultValue) ProtoMessage() {} +func (*FacetResultValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{40} } + +func (m *FacetResultValue) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FacetResultValue) GetCount() int32 { + if m != nil && m.Count != nil { + return *m.Count + } + return 0 +} + +func (m *FacetResultValue) GetRefinement() *FacetRefinement { + if m != nil { + return m.Refinement + } + return nil +} + +type FacetResult struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Value []*FacetResultValue `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FacetResult) Reset() { *m = FacetResult{} } +func (m *FacetResult) String() string { return proto.CompactTextString(m) } +func (*FacetResult) ProtoMessage() {} +func (*FacetResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} } + +func (m *FacetResult) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FacetResult) GetValue() []*FacetResultValue { + if m != nil { + return m.Value + } + return nil +} + +type SearchResult struct { + Document *Document `protobuf:"bytes,1,req,name=document" json:"document,omitempty"` + Expression []*Field `protobuf:"bytes,4,rep,name=expression" json:"expression,omitempty"` + Score []float64 `protobuf:"fixed64,2,rep,name=score" json:"score,omitempty"` + Cursor *string `protobuf:"bytes,3,opt,name=cursor" json:"cursor,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SearchResult) Reset() { *m = SearchResult{} } +func (m *SearchResult) String() string { return proto.CompactTextString(m) } +func (*SearchResult) ProtoMessage() {} +func (*SearchResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{42} } + +func (m *SearchResult) GetDocument() *Document { + if m != nil { + return m.Document + } + return nil +} + +func (m *SearchResult) GetExpression() []*Field { + if m != nil { + return m.Expression + } + return nil +} + +func (m *SearchResult) GetScore() []float64 { + if m != nil { + return m.Score + } + return nil +} + +func (m *SearchResult) GetCursor() string { + if m != nil && m.Cursor != nil { + return *m.Cursor + } + return "" +} + +type SearchResponse struct { + Result []*SearchResult `protobuf:"bytes,1,rep,name=result" json:"result,omitempty"` + MatchedCount *int64 `protobuf:"varint,2,req,name=matched_count,json=matchedCount" json:"matched_count,omitempty"` + Status *RequestStatus `protobuf:"bytes,3,req,name=status" json:"status,omitempty"` + Cursor *string `protobuf:"bytes,4,opt,name=cursor" json:"cursor,omitempty"` + FacetResult []*FacetResult `protobuf:"bytes,5,rep,name=facet_result,json=facetResult" json:"facet_result,omitempty"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SearchResponse) Reset() { *m = SearchResponse{} } +func (m *SearchResponse) String() string { return proto.CompactTextString(m) } +func (*SearchResponse) ProtoMessage() {} +func (*SearchResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{43} } + +var extRange_SearchResponse = []proto.ExtensionRange{ + {1000, 9999}, +} + +func (*SearchResponse) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_SearchResponse +} + +func (m *SearchResponse) GetResult() []*SearchResult { + if m != nil { + return m.Result + } + return nil +} + +func (m *SearchResponse) GetMatchedCount() int64 { + if m != nil && m.MatchedCount != nil { + return *m.MatchedCount + } + return 0 +} + +func (m *SearchResponse) GetStatus() *RequestStatus { + if m != nil { + return m.Status + } + return nil +} + +func (m *SearchResponse) GetCursor() string { + if m != nil && m.Cursor != nil { + return *m.Cursor + } + return "" +} + +func (m *SearchResponse) GetFacetResult() []*FacetResult { + if m != nil { + return m.FacetResult + } + return nil +} + +func init() { + proto.RegisterType((*Scope)(nil), "search.Scope") + proto.RegisterType((*Entry)(nil), "search.Entry") + proto.RegisterType((*AccessControlList)(nil), "search.AccessControlList") + proto.RegisterType((*FieldValue)(nil), "search.FieldValue") + proto.RegisterType((*FieldValue_Geo)(nil), "search.FieldValue.Geo") + proto.RegisterType((*Field)(nil), "search.Field") + proto.RegisterType((*FieldTypes)(nil), "search.FieldTypes") + proto.RegisterType((*IndexShardSettings)(nil), "search.IndexShardSettings") + proto.RegisterType((*FacetValue)(nil), "search.FacetValue") + proto.RegisterType((*Facet)(nil), "search.Facet") + proto.RegisterType((*DocumentMetadata)(nil), "search.DocumentMetadata") + proto.RegisterType((*Document)(nil), "search.Document") + proto.RegisterType((*SearchServiceError)(nil), "search.SearchServiceError") + proto.RegisterType((*RequestStatus)(nil), "search.RequestStatus") + proto.RegisterType((*IndexSpec)(nil), "search.IndexSpec") + proto.RegisterType((*IndexMetadata)(nil), "search.IndexMetadata") + proto.RegisterType((*IndexMetadata_Storage)(nil), "search.IndexMetadata.Storage") + proto.RegisterType((*IndexDocumentParams)(nil), "search.IndexDocumentParams") + proto.RegisterType((*IndexDocumentRequest)(nil), "search.IndexDocumentRequest") + proto.RegisterType((*IndexDocumentResponse)(nil), "search.IndexDocumentResponse") + proto.RegisterType((*DeleteDocumentParams)(nil), "search.DeleteDocumentParams") + proto.RegisterType((*DeleteDocumentRequest)(nil), "search.DeleteDocumentRequest") + proto.RegisterType((*DeleteDocumentResponse)(nil), "search.DeleteDocumentResponse") + proto.RegisterType((*ListDocumentsParams)(nil), "search.ListDocumentsParams") + proto.RegisterType((*ListDocumentsRequest)(nil), "search.ListDocumentsRequest") + proto.RegisterType((*ListDocumentsResponse)(nil), "search.ListDocumentsResponse") + proto.RegisterType((*ListIndexesParams)(nil), "search.ListIndexesParams") + proto.RegisterType((*ListIndexesRequest)(nil), "search.ListIndexesRequest") + proto.RegisterType((*ListIndexesResponse)(nil), "search.ListIndexesResponse") + proto.RegisterType((*DeleteSchemaParams)(nil), "search.DeleteSchemaParams") + proto.RegisterType((*DeleteSchemaRequest)(nil), "search.DeleteSchemaRequest") + proto.RegisterType((*DeleteSchemaResponse)(nil), "search.DeleteSchemaResponse") + proto.RegisterType((*SortSpec)(nil), "search.SortSpec") + proto.RegisterType((*ScorerSpec)(nil), "search.ScorerSpec") + proto.RegisterType((*FieldSpec)(nil), "search.FieldSpec") + proto.RegisterType((*FieldSpec_Expression)(nil), "search.FieldSpec.Expression") + proto.RegisterType((*FacetRange)(nil), "search.FacetRange") + proto.RegisterType((*FacetRequestParam)(nil), "search.FacetRequestParam") + proto.RegisterType((*FacetAutoDetectParam)(nil), "search.FacetAutoDetectParam") + proto.RegisterType((*FacetRequest)(nil), "search.FacetRequest") + proto.RegisterType((*FacetRefinement)(nil), "search.FacetRefinement") + proto.RegisterType((*FacetRefinement_Range)(nil), "search.FacetRefinement.Range") + proto.RegisterType((*SearchParams)(nil), "search.SearchParams") + proto.RegisterType((*SearchRequest)(nil), "search.SearchRequest") + proto.RegisterType((*FacetResultValue)(nil), "search.FacetResultValue") + proto.RegisterType((*FacetResult)(nil), "search.FacetResult") + proto.RegisterType((*SearchResult)(nil), "search.SearchResult") + proto.RegisterType((*SearchResponse)(nil), "search.SearchResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/search/search.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 2994 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x59, 0x4f, 0x73, 0x1b, 0xc7, + 0x95, 0xe7, 0x0c, 0x08, 0x10, 0x78, 0x20, 0xc8, 0x61, 0xf3, 0x8f, 0x20, 0x59, 0x6b, 0xd3, 0x23, + 0xcb, 0xa6, 0xbd, 0x12, 0x45, 0x51, 0x2a, 0x5b, 0xcb, 0x75, 0xed, 0x1a, 0x02, 0x46, 0x14, 0x56, + 0x20, 0x40, 0x37, 0x06, 0xb2, 0xb5, 0x55, 0xeb, 0xd9, 0xc9, 0x4c, 0x13, 0x9a, 0x0a, 0x30, 0x03, + 0xcf, 0x0c, 0x14, 0xf1, 0x96, 0xf2, 0x2d, 0x97, 0x54, 0x52, 0x39, 0xe5, 0x94, 0x72, 0xe5, 0x92, + 0xca, 0x35, 0xf7, 0x9c, 0x92, 0x5b, 0x6e, 0x39, 0xe5, 0x0b, 0xa4, 0x52, 0x49, 0x55, 0x3e, 0x43, + 0xaa, 0x5f, 0xf7, 0x0c, 0x66, 0x40, 0xc8, 0xb4, 0x74, 0x22, 0xe6, 0xf5, 0xeb, 0xd7, 0xaf, 0xdf, + 0xef, 0xbd, 0x5f, 0xbf, 0x6e, 0xc2, 0x83, 0x61, 0x10, 0x0c, 0x47, 0x6c, 0x7f, 0x18, 0x8c, 0x6c, + 0x7f, 0xb8, 0x1f, 0x84, 0xc3, 0x3b, 0xf6, 0x64, 0xc2, 0xfc, 0xa1, 0xe7, 0xb3, 0x3b, 0x9e, 0x1f, + 0xb3, 0xd0, 0xb7, 0x47, 0x77, 0x22, 0x66, 0x87, 0xce, 0x73, 0xf9, 0x67, 0x7f, 0x12, 0x06, 0x71, + 0x40, 0x4a, 0xe2, 0x4b, 0xff, 0x87, 0x02, 0xc5, 0xbe, 0x13, 0x4c, 0x18, 0x79, 0x1f, 0x96, 0xe3, + 0xf3, 0x09, 0xab, 0x2b, 0xbb, 0xca, 0xde, 0xda, 0x21, 0xd9, 0x97, 0xea, 0x38, 0xb8, 0x6f, 0x9e, + 0x4f, 0x18, 0xc5, 0x71, 0xb2, 0x05, 0xc5, 0x17, 0xf6, 0x68, 0xca, 0xea, 0xea, 0xae, 0xb2, 0x57, + 0xa1, 0xe2, 0x43, 0xff, 0xb5, 0x02, 0xcb, 0x5c, 0x89, 0xd4, 0x61, 0x6b, 0xd0, 0x37, 0xa8, 0xf5, + 0xf0, 0x99, 0xd5, 0x6c, 0x74, 0x7b, 0xdd, 0x76, 0xb3, 0xd1, 0xb1, 0xda, 0x2d, 0x4d, 0x21, 0x1b, + 0x50, 0x4b, 0x46, 0x8c, 0x93, 0x46, 0xbb, 0xa3, 0xa9, 0xe4, 0x2a, 0x6c, 0x1f, 0xd3, 0xde, 0xe0, + 0xf4, 0x82, 0x76, 0x81, 0x10, 0x58, 0x4b, 0x87, 0x84, 0xfa, 0x32, 0xd9, 0x84, 0xf5, 0x54, 0xd6, + 0xea, 0x9d, 0x34, 0xda, 0x5d, 0xad, 0x48, 0x6a, 0x50, 0x69, 0x74, 0x3a, 0x16, 0x37, 0xdd, 0xd7, + 0x4a, 0xe4, 0x2d, 0xb8, 0xc2, 0x3f, 0x1b, 0x03, 0xf3, 0xb1, 0xd1, 0x35, 0xdb, 0xcd, 0x86, 0x69, + 0xb4, 0xe4, 0xe0, 0x8a, 0xfe, 0x7b, 0x05, 0x8a, 0x86, 0x1f, 0x87, 0xe7, 0xe4, 0x06, 0x14, 0x23, + 0xbe, 0x33, 0xdc, 0x6e, 0xf5, 0xb0, 0x96, 0xdb, 0x2e, 0x15, 0x63, 0xe4, 0x01, 0xc0, 0x84, 0x85, + 0x63, 0x2f, 0x8a, 0xbc, 0xc0, 0xc7, 0xfd, 0xae, 0x1d, 0xd6, 0x13, 0x4d, 0xb4, 0xb3, 0x7f, 0x9a, + 0x8e, 0xd3, 0x8c, 0x2e, 0x79, 0x17, 0x56, 0x5d, 0x2f, 0x9a, 0x8c, 0xec, 0x73, 0xcb, 0xb7, 0xc7, + 0xac, 0x5e, 0xc0, 0x58, 0x55, 0xa5, 0xac, 0x6b, 0x8f, 0x99, 0x7e, 0x0f, 0x60, 0x36, 0x99, 0x94, + 0x61, 0x99, 0x1a, 0x0d, 0x1e, 0xa6, 0x0a, 0x14, 0xbf, 0xa0, 0x6d, 0xd3, 0xd0, 0x54, 0xa2, 0xc1, + 0xea, 0xa3, 0x41, 0xa7, 0x63, 0x35, 0x7b, 0x5d, 0x93, 0xf6, 0x3a, 0x5a, 0x41, 0xa7, 0xb0, 0xd1, + 0x70, 0x1c, 0x16, 0x45, 0xcd, 0xc0, 0x8f, 0xc3, 0x60, 0xd4, 0xf1, 0xa2, 0x98, 0x23, 0x12, 0xfc, + 0xc8, 0x67, 0x21, 0xee, 0xa5, 0x42, 0xc5, 0x07, 0xf9, 0x00, 0x56, 0x98, 0x1f, 0x87, 0x1e, 0x8b, + 0xea, 0xea, 0x6e, 0x21, 0xbb, 0x47, 0xf4, 0x9c, 0x26, 0xa3, 0xfa, 0x6f, 0x55, 0x80, 0x47, 0x1e, + 0x1b, 0xb9, 0x4f, 0x39, 0x92, 0xe4, 0x41, 0x2e, 0x0f, 0xde, 0x4e, 0x26, 0xcd, 0x34, 0xf6, 0xf9, + 0xda, 0xcc, 0x8f, 0x39, 0xdc, 0x47, 0xcb, 0xa6, 0xf1, 0xa5, 0x29, 0x33, 0xe3, 0x6d, 0x28, 0xf3, + 0x34, 0x9c, 0xda, 0x43, 0x99, 0x1c, 0x47, 0x2a, 0xf3, 0x69, 0x2a, 0xe3, 0x41, 0x89, 0xe2, 0xd0, + 0xf3, 0x87, 0x96, 0x48, 0x20, 0x19, 0x14, 0x21, 0x13, 0x8b, 0xef, 0x41, 0x61, 0xc8, 0x82, 0xfa, + 0xf2, 0xae, 0xb2, 0x07, 0x87, 0x3b, 0x0b, 0xd6, 0x3e, 0x66, 0x01, 0xe5, 0x2a, 0xd7, 0x3e, 0x84, + 0xc2, 0x31, 0x0b, 0x88, 0x06, 0x85, 0x91, 0x1d, 0xd7, 0x8b, 0xbb, 0xea, 0x9e, 0x42, 0xf9, 0x4f, + 0x94, 0xf8, 0xc3, 0x7a, 0x49, 0x4a, 0xfc, 0xa1, 0xfe, 0x3f, 0x50, 0xcd, 0xb8, 0xcc, 0x43, 0xcd, + 0x9d, 0xd6, 0x96, 0xf8, 0xaf, 0xc7, 0xe6, 0x49, 0x47, 0x53, 0xf8, 0xaf, 0x86, 0xd9, 0x3b, 0xd1, + 0x54, 0xfe, 0xab, 0xd5, 0x30, 0x0d, 0xad, 0x40, 0x00, 0x4a, 0xdd, 0xc1, 0xc9, 0x43, 0x83, 0x6a, + 0xcb, 0x64, 0x05, 0x0a, 0xc7, 0x46, 0x4f, 0x2b, 0xea, 0x06, 0x14, 0xd1, 0x1b, 0x42, 0x60, 0x19, + 0x91, 0x55, 0x76, 0xd5, 0xbd, 0x0a, 0xc5, 0xdf, 0x64, 0x6f, 0x56, 0x1a, 0xea, 0x5e, 0x75, 0x56, + 0x43, 0x33, 0xff, 0x93, 0x72, 0x31, 0x65, 0xc8, 0xb9, 0x43, 0xd1, 0x42, 0x5b, 0x87, 0x12, 0x06, + 0x8e, 0xdd, 0xa5, 0x30, 0x08, 0x00, 0xf4, 0x3f, 0x2a, 0x40, 0xda, 0xbe, 0xcb, 0x5e, 0xf6, 0x9f, + 0xdb, 0xa1, 0xdb, 0x67, 0x71, 0xec, 0xf9, 0xc3, 0x88, 0xbc, 0x0f, 0xeb, 0x93, 0x90, 0xbd, 0xb0, + 0xfc, 0xe9, 0xd8, 0x8a, 0xf8, 0x48, 0x54, 0x57, 0x76, 0x0b, 0x7b, 0x45, 0x5a, 0xe3, 0xe2, 0xee, + 0x74, 0x8c, 0xea, 0x11, 0xd9, 0x05, 0xc8, 0xa8, 0xf0, 0x3d, 0x14, 0x8f, 0x94, 0xbb, 0xb4, 0xe2, + 0xa7, 0x1a, 0xff, 0x05, 0xd7, 0xe7, 0x2c, 0x59, 0xc2, 0x2f, 0xeb, 0xcc, 0x1e, 0x45, 0x1c, 0x51, + 0x6e, 0xb6, 0x9e, 0x33, 0xdb, 0x47, 0x85, 0x47, 0x7c, 0x9c, 0xdc, 0x84, 0xda, 0x28, 0x70, 0xec, + 0x91, 0x15, 0xb2, 0xc9, 0xc8, 0x73, 0x6c, 0x04, 0xba, 0x72, 0xb4, 0x44, 0x57, 0x51, 0x4c, 0x85, + 0x54, 0xff, 0xa9, 0x02, 0xf0, 0xc8, 0x76, 0x58, 0xfc, 0xdd, 0x19, 0x99, 0x6a, 0xe4, 0x33, 0x92, + 0x03, 0x29, 0x33, 0xf2, 0xf2, 0x8c, 0xd3, 0x6f, 0x5c, 0x48, 0x0e, 0x99, 0x08, 0x19, 0xf8, 0x11, + 0x75, 0xbe, 0xda, 0xeb, 0xa1, 0x9e, 0xfa, 0x97, 0xa0, 0xfe, 0x15, 0x68, 0xad, 0xc0, 0x99, 0x8e, + 0x99, 0x1f, 0x9f, 0xb0, 0xd8, 0x76, 0xed, 0xd8, 0x26, 0x75, 0x58, 0x79, 0xc1, 0x42, 0x24, 0x18, + 0xbe, 0xbf, 0x02, 0x4d, 0x3e, 0xc9, 0x01, 0x6c, 0x39, 0xc1, 0x78, 0xec, 0xc5, 0x31, 0x73, 0xad, + 0x28, 0xb6, 0x12, 0x35, 0x15, 0xd5, 0x48, 0x3a, 0xd6, 0x8f, 0x9f, 0x8a, 0x11, 0xfd, 0x9f, 0x2a, + 0x94, 0x93, 0x05, 0xc8, 0x1a, 0xa8, 0x9e, 0x2b, 0x29, 0x41, 0xf5, 0xdc, 0x4b, 0xab, 0xf3, 0x06, + 0x14, 0xcf, 0x78, 0x72, 0x21, 0x88, 0x19, 0xb6, 0xc0, 0x8c, 0xa3, 0x62, 0x8c, 0x5c, 0x85, 0x72, + 0x10, 0xba, 0x2c, 0xb4, 0x3c, 0x17, 0xb1, 0x2b, 0xd2, 0x15, 0xfc, 0x6e, 0xbb, 0xe4, 0x14, 0xd6, + 0x93, 0x21, 0x2b, 0x0a, 0xa6, 0xa1, 0xc3, 0xea, 0xa5, 0x3c, 0x60, 0x89, 0x6b, 0xfb, 0x3d, 0x31, + 0xa5, 0x8f, 0x5a, 0x47, 0xe5, 0xfe, 0xe0, 0xf4, 0xb4, 0xd3, 0x36, 0x5a, 0xb4, 0x16, 0x64, 0x07, + 0xc8, 0x03, 0x58, 0x89, 0xe2, 0x20, 0xe4, 0x0e, 0x17, 0xf3, 0xdc, 0x9b, 0x5a, 0xea, 0x8b, 0xf1, + 0xa3, 0xe5, 0x56, 0xbb, 0xff, 0x84, 0x26, 0xea, 0xb8, 0x17, 0x1e, 0xfd, 0x7a, 0x79, 0x6e, 0x2f, + 0x5c, 0x48, 0xc5, 0x98, 0x7e, 0x0b, 0x6a, 0x39, 0x47, 0xf8, 0x49, 0xd2, 0x32, 0x1e, 0x35, 0x06, + 0x1d, 0xd3, 0x68, 0x69, 0x4b, 0x64, 0x15, 0x52, 0xcf, 0x34, 0x45, 0xdf, 0x84, 0x15, 0xb9, 0x18, + 0x52, 0x44, 0xbb, 0xff, 0x44, 0x5b, 0xd2, 0x7f, 0xa3, 0x00, 0x11, 0xf9, 0xdd, 0x67, 0xe1, 0x0b, + 0xcf, 0x61, 0x46, 0x18, 0x06, 0xa1, 0xfe, 0x73, 0x05, 0x2a, 0xf8, 0xab, 0x19, 0xb8, 0x8c, 0x94, + 0x40, 0xed, 0x3d, 0xd1, 0x96, 0xf8, 0xe9, 0xd5, 0xee, 0x3e, 0x6d, 0x74, 0xda, 0x2d, 0x8b, 0x1a, + 0x9f, 0x0f, 0x8c, 0xbe, 0xa9, 0x29, 0x5c, 0x68, 0xd2, 0x46, 0xb7, 0xdf, 0x36, 0xba, 0xa6, 0x65, + 0x50, 0xda, 0xa3, 0x9a, 0xca, 0xcf, 0xbe, 0x76, 0xd7, 0x34, 0x68, 0xb7, 0xd1, 0x91, 0xb2, 0x02, + 0xd9, 0x86, 0x8d, 0x53, 0x83, 0x9e, 0xb4, 0xfb, 0xfd, 0x76, 0xaf, 0x6b, 0xb5, 0x8c, 0x2e, 0x77, + 0x6b, 0x99, 0x54, 0x61, 0xc5, 0x6c, 0x9f, 0x18, 0xbd, 0x81, 0xa9, 0x15, 0xc9, 0x35, 0xd8, 0x69, + 0xf6, 0xba, 0xcd, 0x01, 0xa5, 0xdc, 0x1a, 0xda, 0x6d, 0x34, 0xcd, 0x76, 0xaf, 0xab, 0x95, 0xf4, + 0x5f, 0x28, 0x50, 0xa3, 0xec, 0xeb, 0x29, 0x8b, 0xe2, 0x7e, 0x6c, 0xc7, 0xd3, 0x88, 0x97, 0x95, + 0x13, 0xb8, 0x22, 0x97, 0xd7, 0x0e, 0xdf, 0x4b, 0x4f, 0xc0, 0x0b, 0xfb, 0xd9, 0x4f, 0xf7, 0x42, + 0x71, 0x06, 0x2f, 0x2b, 0xc6, 0x45, 0x96, 0xcb, 0x62, 0xdb, 0x1b, 0xc9, 0x4e, 0xa0, 0x8a, 0xb2, + 0x16, 0x8a, 0xc8, 0x4d, 0x58, 0x73, 0x6c, 0x3f, 0xf0, 0x3d, 0x5e, 0xed, 0xb8, 0x4c, 0x01, 0xd3, + 0xa5, 0x96, 0x4a, 0xb9, 0x3d, 0xfd, 0xdb, 0x02, 0x54, 0x04, 0x63, 0x4d, 0x98, 0xb3, 0xb0, 0xba, + 0x4e, 0xa0, 0xea, 0x04, 0x7e, 0xe4, 0x45, 0x31, 0xf3, 0x9d, 0x73, 0x79, 0x08, 0xff, 0x5b, 0xe2, + 0x6c, 0x3a, 0x97, 0x53, 0x40, 0xa2, 0x74, 0xb4, 0x7a, 0x6a, 0x50, 0xab, 0xd5, 0x6b, 0x0e, 0x4e, + 0x8c, 0xae, 0x49, 0xb3, 0xf3, 0xc9, 0x75, 0xa8, 0x70, 0xb3, 0xd1, 0xc4, 0x76, 0x12, 0x3a, 0x98, + 0x09, 0xb2, 0xc5, 0x28, 0xb3, 0x3b, 0x29, 0xc6, 0x07, 0x50, 0x92, 0x49, 0x3d, 0x97, 0x8a, 0x33, + 0x0f, 0x64, 0x3a, 0x97, 0xfa, 0x46, 0x83, 0x36, 0x1f, 0x53, 0xa9, 0x4f, 0xee, 0xc3, 0xf2, 0x98, + 0xef, 0x5f, 0x14, 0xc3, 0xce, 0xc5, 0x79, 0x27, 0x81, 0xcb, 0x8e, 0xca, 0xa7, 0xb4, 0xdd, 0xa3, + 0x6d, 0xf3, 0x19, 0x45, 0x6d, 0xfd, 0xdf, 0x91, 0x96, 0x52, 0xb7, 0x01, 0x4a, 0xc7, 0x9d, 0xde, + 0xc3, 0x46, 0x47, 0x5b, 0xe2, 0x5d, 0x41, 0x76, 0x7f, 0x9a, 0xa2, 0x7f, 0x0c, 0x25, 0x99, 0xc2, + 0x00, 0x72, 0x79, 0x6d, 0x09, 0xd3, 0xb9, 0x61, 0x36, 0xfa, 0x66, 0x8f, 0x1a, 0xa2, 0xfd, 0x6a, + 0x76, 0x7a, 0x83, 0x96, 0xc5, 0x05, 0x8d, 0x63, 0x43, 0x53, 0xf5, 0xf7, 0x60, 0x99, 0x2f, 0xce, + 0x33, 0x3d, 0x59, 0x5e, 0x5b, 0x22, 0x6b, 0x00, 0x0f, 0x1b, 0xcd, 0x27, 0xbc, 0xd3, 0xea, 0xf2, + 0xcc, 0xff, 0xab, 0x02, 0x35, 0xf4, 0x36, 0xe5, 0xac, 0x03, 0x00, 0x8f, 0x0b, 0xac, 0x68, 0xc2, + 0x1c, 0x44, 0xab, 0x7a, 0xb8, 0x71, 0x61, 0x63, 0xb4, 0xe2, 0xa5, 0xc8, 0xee, 0x25, 0xe4, 0x22, + 0x5a, 0x91, 0xfc, 0xc9, 0x88, 0x87, 0x60, 0xc2, 0x30, 0x9f, 0xcc, 0x8a, 0xbe, 0x80, 0xad, 0x59, + 0x1e, 0xeb, 0xc4, 0x87, 0xa4, 0xf2, 0xd3, 0x9a, 0xbf, 0xf6, 0xd9, 0xac, 0x40, 0xdf, 0x81, 0xaa, + 0x3d, 0x0e, 0xa6, 0x7e, 0x6c, 0x4d, 0x23, 0xe6, 0x4a, 0x5e, 0x05, 0x21, 0x1a, 0x44, 0xcc, 0xe5, + 0x1d, 0xd3, 0xc8, 0x1b, 0x7b, 0xb1, 0xe4, 0x52, 0xf1, 0xa1, 0x7f, 0xa3, 0xc2, 0x26, 0x2e, 0x92, + 0xd0, 0xcb, 0xa9, 0x1d, 0xda, 0xe3, 0x88, 0xdc, 0x82, 0xb2, 0x2b, 0x25, 0x78, 0x70, 0x56, 0x0f, + 0xb5, 0x79, 0x22, 0xa2, 0xa9, 0x06, 0x79, 0x0a, 0x95, 0xb3, 0x90, 0x45, 0xcf, 0x7d, 0x16, 0x45, + 0x32, 0x5d, 0x6f, 0xe6, 0xb6, 0x90, 0xb7, 0xbe, 0xff, 0x28, 0x51, 0x3e, 0xaa, 0xf5, 0x9f, 0x75, + 0x9b, 0x8f, 0x69, 0xaf, 0xdb, 0x1b, 0xf4, 0x3b, 0xcf, 0x1e, 0xaa, 0x75, 0x85, 0xce, 0x4c, 0xcd, + 0x05, 0xbd, 0x70, 0x79, 0xd0, 0xf5, 0x7b, 0x50, 0x49, 0x8d, 0x73, 0xf8, 0x73, 0xe6, 0x05, 0x21, + 0x7d, 0xf1, 0xd8, 0xe8, 0xf2, 0xf6, 0xf2, 0x29, 0xe7, 0x13, 0xcc, 0xa5, 0x1f, 0xc0, 0x56, 0xce, + 0x4b, 0xc9, 0x19, 0xe4, 0x1e, 0x94, 0x26, 0xe8, 0xb0, 0xc4, 0xfb, 0xad, 0xef, 0xd8, 0x13, 0x95, + 0xaa, 0x64, 0x1b, 0x4a, 0xf6, 0x64, 0xc2, 0x0f, 0x0b, 0x8e, 0xe5, 0x2a, 0x2d, 0xda, 0x93, 0x49, + 0xdb, 0xd5, 0xff, 0x0f, 0xb6, 0xe7, 0xd6, 0x88, 0x26, 0x81, 0x1f, 0x31, 0x72, 0x1b, 0x4a, 0x11, + 0x92, 0x93, 0x8c, 0xf3, 0x76, 0xb2, 0x48, 0x8e, 0xb9, 0xa8, 0x54, 0xe2, 0xe6, 0xdd, 0xc0, 0xe1, + 0xe6, 0x79, 0x5a, 0x55, 0x68, 0xd1, 0x0d, 0x9c, 0xb6, 0xab, 0x5b, 0xb0, 0xd5, 0x62, 0x23, 0x16, + 0xb3, 0x39, 0x1c, 0x67, 0xea, 0x4a, 0x46, 0x7d, 0x2e, 0xb0, 0xea, 0xf7, 0x08, 0xac, 0x0b, 0xdb, + 0xf9, 0x05, 0x92, 0x20, 0xdd, 0x9f, 0x0b, 0xd2, 0xf5, 0x34, 0x4f, 0x16, 0xf8, 0x73, 0x59, 0x94, + 0x8e, 0x61, 0x67, 0x7e, 0x95, 0x37, 0x0a, 0x93, 0xfe, 0x67, 0x05, 0x36, 0xf9, 0x45, 0x21, 0xb1, + 0x13, 0xc9, 0x78, 0xbc, 0x7e, 0x19, 0xef, 0xf2, 0x7e, 0xca, 0x0e, 0x63, 0x2b, 0x0d, 0x3b, 0x27, + 0x50, 0x40, 0x59, 0x4b, 0x06, 0x73, 0xc3, 0xf3, 0x9d, 0xd1, 0xd4, 0x65, 0x56, 0xaa, 0x89, 0xdb, + 0x2a, 0x1f, 0x2d, 0xc7, 0xe1, 0x94, 0xd1, 0x75, 0x39, 0xdc, 0x97, 0x73, 0xc8, 0xd5, 0xa4, 0x16, + 0x91, 0x71, 0x8f, 0x0a, 0x77, 0x0f, 0x0e, 0x64, 0x41, 0x92, 0xb7, 0xa0, 0xf2, 0x43, 0x76, 0x1e, + 0x59, 0x81, 0x3f, 0x3a, 0x47, 0xde, 0x2d, 0xd3, 0x32, 0x17, 0xf4, 0xfc, 0xd1, 0x39, 0x4f, 0xd4, + 0xdc, 0xa6, 0x2e, 0x4d, 0xd4, 0x05, 0x21, 0x58, 0x00, 0x81, 0x9a, 0x85, 0x20, 0x86, 0xed, 0xb9, + 0x35, 0x16, 0x20, 0xa0, 0x5e, 0x9e, 0xa8, 0x59, 0x06, 0x51, 0x2f, 0x63, 0x10, 0xfd, 0x4f, 0x2a, + 0x6c, 0xf0, 0x65, 0x11, 0x02, 0x96, 0xa0, 0xf5, 0x2e, 0xac, 0x9e, 0xb1, 0xd8, 0x79, 0x6e, 0x45, + 0xce, 0x73, 0x36, 0xb6, 0x91, 0xd5, 0xca, 0xb4, 0x8a, 0xb2, 0x3e, 0x8a, 0x48, 0x3d, 0x4b, 0x6b, + 0xc5, 0x23, 0xf5, 0x30, 0x8d, 0xe4, 0x77, 0x1f, 0x7b, 0x7b, 0xa0, 0x09, 0xb0, 0x44, 0x3a, 0xe0, + 0x19, 0x8c, 0x9d, 0x39, 0x5d, 0x43, 0x39, 0x3a, 0xc2, 0x2f, 0xad, 0xe4, 0x3e, 0x6c, 0xe6, 0xe1, + 0xc5, 0x19, 0x02, 0x1b, 0x09, 0xf0, 0x46, 0x16, 0x60, 0x9c, 0x49, 0x3e, 0xe2, 0x49, 0x91, 0x58, + 0xb6, 0x26, 0x21, 0x3b, 0xf3, 0x5e, 0xe2, 0x79, 0x58, 0xe1, 0xe9, 0x20, 0x6d, 0x9f, 0xa2, 0x98, + 0xec, 0x40, 0x29, 0x38, 0x3b, 0x8b, 0x58, 0x5c, 0x5f, 0xc1, 0x13, 0x58, 0x7e, 0x65, 0x0e, 0xe0, + 0xf2, 0xeb, 0x1d, 0xc0, 0xfa, 0x57, 0x40, 0x32, 0xd1, 0x4c, 0xd2, 0xe4, 0xee, 0x5c, 0x9a, 0x5c, + 0xcd, 0xa6, 0x49, 0x2e, 0xf2, 0x97, 0xd5, 0xe9, 0x37, 0xb2, 0xbc, 0xd2, 0x05, 0xde, 0x2c, 0x47, + 0x3e, 0x85, 0x35, 0x11, 0xa4, 0xb1, 0x3c, 0xe2, 0x64, 0xa6, 0x6c, 0x2f, 0x3c, 0xff, 0x68, 0xcd, + 0xcb, 0x7e, 0xea, 0x3f, 0x56, 0x80, 0x08, 0xb6, 0x10, 0xb9, 0x20, 0x93, 0x66, 0x16, 0x35, 0xe5, + 0x35, 0xdb, 0x96, 0x79, 0x56, 0x2c, 0x5c, 0xca, 0x8a, 0xff, 0x0f, 0x9b, 0x59, 0x0f, 0x92, 0x40, + 0x1f, 0xce, 0x05, 0xfa, 0x5a, 0x9e, 0x13, 0xb3, 0xee, 0x5e, 0x16, 0x69, 0x23, 0x21, 0xf6, 0x64, + 0x85, 0x37, 0xe3, 0xc3, 0x3f, 0x28, 0x50, 0xee, 0x07, 0x61, 0x8c, 0x94, 0xf6, 0x01, 0xac, 0x47, + 0x41, 0x18, 0x5b, 0xec, 0xe5, 0x24, 0x64, 0x91, 0xbc, 0x87, 0xa9, 0x98, 0xfa, 0x41, 0x18, 0x1b, + 0xa9, 0x94, 0xdc, 0x96, 0x8a, 0x2e, 0x8b, 0x1c, 0xe6, 0xbb, 0x9e, 0x3f, 0xc4, 0x32, 0x4b, 0xd2, + 0x1e, 0xd5, 0x5b, 0xe9, 0x18, 0xb9, 0x05, 0xc4, 0x65, 0x67, 0xf6, 0x74, 0x14, 0x8b, 0xbb, 0xa7, + 0x15, 0xb3, 0x97, 0xb1, 0xac, 0x2a, 0x4d, 0x8e, 0xe0, 0xe5, 0xd0, 0x64, 0x2f, 0x79, 0x90, 0xb6, + 0xf3, 0xda, 0xfe, 0x74, 0xcc, 0x42, 0xcf, 0xc1, 0xca, 0x52, 0xe8, 0x66, 0x76, 0x42, 0x57, 0x0c, + 0xe9, 0x7f, 0x51, 0x00, 0xfa, 0x4e, 0x10, 0xb2, 0x10, 0x37, 0xf2, 0xdf, 0x50, 0x8a, 0xf0, 0x4b, + 0x42, 0x7d, 0x35, 0xf3, 0xa4, 0x25, 0x75, 0xe4, 0xcf, 0xa3, 0xd5, 0x93, 0x86, 0xd9, 0x7c, 0x6c, + 0xf5, 0x9b, 0x3d, 0x6a, 0x50, 0x2a, 0xa7, 0x91, 0x6b, 0x79, 0xf6, 0x58, 0xbe, 0x7b, 0x30, 0x63, + 0xe2, 0x8f, 0xe1, 0xca, 0xd8, 0x16, 0xe4, 0xc3, 0x75, 0x2d, 0xc4, 0x89, 0xc5, 0x2c, 0x8c, 0xea, + 0x15, 0xdc, 0xd2, 0x36, 0x0e, 0x0b, 0xfb, 0xa7, 0xe9, 0x20, 0x76, 0xa6, 0x89, 0xf5, 0x1d, 0x6a, + 0xf0, 0x15, 0xdb, 0xdd, 0x63, 0x2b, 0xbb, 0xbe, 0xe8, 0x68, 0x73, 0x12, 0x55, 0xff, 0x95, 0x02, + 0x15, 0xec, 0x0d, 0xe7, 0xee, 0x05, 0x85, 0xf4, 0x5e, 0xf0, 0x29, 0x40, 0x06, 0x32, 0x9e, 0x9f, + 0x30, 0x3b, 0x6e, 0xd3, 0xa9, 0xfb, 0x33, 0x00, 0x69, 0x46, 0xff, 0xda, 0x67, 0x00, 0x19, 0x68, + 0x13, 0xfb, 0x85, 0xcc, 0xbd, 0xe3, 0xed, 0x9c, 0xfd, 0x65, 0x1c, 0xc9, 0x48, 0xf4, 0xc7, 0xf2, + 0x89, 0x82, 0xda, 0xfe, 0x90, 0x65, 0x3c, 0x54, 0x52, 0x0b, 0x5b, 0x50, 0x44, 0x8e, 0x4c, 0x1e, + 0x4a, 0xf1, 0x83, 0x68, 0x50, 0x60, 0xbe, 0x2b, 0x39, 0x98, 0xff, 0xd4, 0x7f, 0xa2, 0xc0, 0x86, + 0x30, 0x25, 0xb2, 0x15, 0xc3, 0xc7, 0x7b, 0x58, 0x91, 0x09, 0x02, 0x13, 0x05, 0xc9, 0x10, 0x50, + 0xd4, 0x41, 0x48, 0xf6, 0xa0, 0x18, 0xf2, 0xb5, 0x2f, 0xb4, 0xd4, 0xa9, 0x57, 0x54, 0x28, 0x90, + 0x0f, 0x41, 0x13, 0xa6, 0xf8, 0x45, 0x28, 0x0e, 0x6d, 0xcf, 0x8f, 0xf1, 0x92, 0x5f, 0xa1, 0xeb, + 0x28, 0x6f, 0xa6, 0x62, 0xfd, 0x3f, 0x61, 0x0b, 0xe7, 0x37, 0xa6, 0x71, 0xd0, 0x62, 0x31, 0x73, + 0xa4, 0x37, 0x37, 0x16, 0x78, 0x73, 0xa4, 0xde, 0x3d, 0xc8, 0x7a, 0xa4, 0x0f, 0x60, 0x35, 0xbb, + 0x8f, 0x85, 0xd7, 0xb9, 0x19, 0xed, 0xaa, 0xd8, 0xdd, 0x5f, 0xcd, 0xbb, 0x9d, 0x89, 0x40, 0x42, + 0x06, 0xfa, 0xb7, 0x0a, 0xac, 0xcb, 0xd1, 0x33, 0xcf, 0x67, 0xd8, 0x64, 0x2f, 0x32, 0xbd, 0xf0, + 0x61, 0x9a, 0xdc, 0x4b, 0xc2, 0x34, 0x77, 0x9b, 0x98, 0xb3, 0xb8, 0x9f, 0x8d, 0xd8, 0xb5, 0x3b, + 0x50, 0x14, 0xb8, 0xa6, 0x18, 0x2a, 0x0b, 0x30, 0x54, 0x67, 0x18, 0xfe, 0x6e, 0x05, 0x56, 0xc5, + 0xc5, 0xf9, 0x8d, 0x7b, 0xab, 0x2d, 0x28, 0x7e, 0x3d, 0x65, 0xe1, 0x39, 0x76, 0xa0, 0x15, 0x2a, + 0x3e, 0xf8, 0x71, 0xe8, 0x4c, 0xc3, 0x28, 0x08, 0x25, 0x75, 0xc8, 0xaf, 0xcc, 0x31, 0x59, 0xcd, + 0x1d, 0x93, 0x8f, 0xa0, 0x2a, 0x34, 0x2c, 0x7c, 0x32, 0x13, 0x97, 0xd5, 0x77, 0xf2, 0x77, 0x7b, + 0x79, 0xf1, 0x68, 0xa2, 0x9e, 0x78, 0x33, 0xeb, 0xf6, 0xba, 0x06, 0x05, 0x27, 0x95, 0xcc, 0x5a, + 0x89, 0xd2, 0x7c, 0x2b, 0x71, 0x1f, 0x76, 0xb0, 0xd6, 0x99, 0x6b, 0x39, 0x78, 0xc7, 0xb2, 0x1d, + 0x67, 0x1a, 0xda, 0xce, 0xb9, 0x3c, 0xb0, 0xb7, 0xe4, 0x68, 0x93, 0x0f, 0x36, 0xe4, 0x18, 0xb9, + 0x0d, 0x15, 0x64, 0x4f, 0x0c, 0x47, 0x39, 0xdf, 0x02, 0x25, 0x5c, 0x4c, 0xcb, 0x51, 0xc2, 0xca, + 0xf7, 0xa0, 0x2a, 0x99, 0x06, 0x27, 0x54, 0x10, 0x3b, 0x72, 0x91, 0xd1, 0x28, 0x44, 0x33, 0x06, + 0x3c, 0x00, 0xc0, 0x3b, 0xa4, 0x98, 0x03, 0x38, 0x67, 0xe3, 0x02, 0x25, 0xd0, 0xca, 0x59, 0x4a, + 0x2c, 0xb9, 0x06, 0x73, 0x35, 0xdf, 0x60, 0x92, 0x27, 0xb0, 0x3a, 0xb1, 0xc3, 0xc8, 0xf3, 0x87, + 0x16, 0x5e, 0xe0, 0x6b, 0x18, 0xcb, 0xdd, 0x85, 0xb1, 0x3c, 0x15, 0x8a, 0x78, 0x95, 0x2f, 0xf5, + 0x4d, 0xda, 0x6e, 0x9a, 0xb4, 0x3a, 0x99, 0x09, 0xc9, 0xa7, 0x70, 0xd5, 0x9e, 0xc6, 0x81, 0xe5, + 0x7a, 0x91, 0x13, 0xbc, 0x60, 0xa1, 0x85, 0x6f, 0x50, 0x22, 0x82, 0xf5, 0x75, 0x8c, 0xb1, 0x72, + 0x40, 0x77, 0xb8, 0x4e, 0x4b, 0xaa, 0x60, 0x86, 0x62, 0x14, 0xc9, 0x7f, 0x40, 0x2d, 0x69, 0xbb, + 0xc4, 0xbb, 0x96, 0x86, 0x11, 0xdc, 0x5a, 0x54, 0x3c, 0x74, 0x55, 0xaa, 0x8a, 0x17, 0xcb, 0x87, + 0xa0, 0x89, 0xa5, 0xc2, 0x34, 0xd7, 0xeb, 0x1b, 0x38, 0xfb, 0xca, 0x2b, 0x4a, 0x81, 0xae, 0x9f, + 0xcd, 0x55, 0x5b, 0x1f, 0xae, 0x08, 0x1b, 0x62, 0x0b, 0xc8, 0x0b, 0xe2, 0x08, 0xa8, 0x13, 0x8c, + 0xf2, 0xf5, 0x9c, 0xa9, 0x39, 0xf2, 0xa0, 0x5b, 0x67, 0x8b, 0x28, 0xe5, 0x26, 0x54, 0x85, 0x51, + 0x97, 0x4d, 0xe2, 0xe7, 0xf5, 0xcd, 0xcc, 0xa1, 0x03, 0x38, 0xd0, 0xe2, 0x72, 0xfd, 0x10, 0x60, + 0x96, 0xa8, 0xa4, 0x0c, 0x98, 0xaa, 0xda, 0x12, 0xbe, 0x74, 0xb4, 0xbb, 0xc7, 0x1d, 0x43, 0x53, + 0xc8, 0x1a, 0xc0, 0xa9, 0x41, 0x2d, 0x6a, 0xf4, 0x07, 0x1d, 0x53, 0x53, 0xf5, 0xf7, 0xa1, 0x9a, + 0x01, 0x04, 0x55, 0x11, 0x12, 0x6d, 0x89, 0x54, 0x61, 0x85, 0x1a, 0x9d, 0xc6, 0x97, 0xf8, 0xa6, + 0x67, 0x42, 0x4d, 0xa0, 0x98, 0x30, 0xd6, 0xad, 0xb9, 0x5e, 0x65, 0x6b, 0x11, 0xd8, 0x97, 0x75, + 0x29, 0x53, 0xd0, 0x64, 0x44, 0xa3, 0xe4, 0xc8, 0x7e, 0x15, 0x5f, 0x09, 0xf8, 0xf1, 0xa5, 0x9d, + 0x8a, 0x0f, 0xf2, 0x09, 0x40, 0x06, 0x29, 0x71, 0xcd, 0x7f, 0x25, 0x52, 0x19, 0x55, 0xfd, 0x73, + 0xa8, 0x66, 0x96, 0x5d, 0xb8, 0xe2, 0xfe, 0x8c, 0x21, 0x79, 0x02, 0xd4, 0xe7, 0xcc, 0xa6, 0xee, + 0x26, 0xef, 0xd5, 0xbf, 0x54, 0x12, 0x56, 0x93, 0x46, 0xf3, 0x2f, 0x21, 0xea, 0x25, 0x2f, 0x21, + 0xb7, 0xe7, 0x8e, 0xd0, 0x05, 0xcf, 0xca, 0x19, 0x05, 0xe4, 0x5a, 0x5e, 0xcc, 0xe8, 0x9d, 0x42, + 0xc5, 0x47, 0x86, 0x00, 0x0b, 0x59, 0x02, 0xd4, 0xff, 0xae, 0xc0, 0x5a, 0xea, 0x9b, 0x68, 0x03, + 0x6f, 0x41, 0x29, 0x44, 0x3f, 0x65, 0x1b, 0x38, 0x87, 0x9e, 0xd8, 0x03, 0x95, 0x3a, 0xe4, 0x06, + 0xd4, 0x72, 0x3c, 0x86, 0x30, 0x14, 0xe8, 0x6a, 0x96, 0xbe, 0x32, 0x9d, 0x65, 0xe1, 0xfb, 0xf4, + 0xf0, 0xaf, 0x62, 0xeb, 0x8f, 0x61, 0x35, 0x29, 0x42, 0xf4, 0xaf, 0x88, 0xfe, 0x6d, 0x2e, 0x88, + 0x3f, 0xad, 0x9e, 0xcd, 0x3e, 0x3e, 0x2a, 0x95, 0xff, 0xb6, 0xa2, 0xfd, 0xac, 0xfb, 0xb0, 0xfc, + 0xbf, 0xf2, 0xff, 0xb5, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x09, 0x6f, 0x4d, 0x63, 0xf2, 0x1d, + 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/search/search.proto b/vendor/google.golang.org/appengine/internal/search/search.proto new file mode 100644 index 0000000000..61df6508b1 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/search/search.proto @@ -0,0 +1,394 @@ +syntax = "proto2"; +option go_package = "search"; + +package search; + +message Scope { + enum Type { + USER_BY_CANONICAL_ID = 1; + USER_BY_EMAIL = 2; + GROUP_BY_CANONICAL_ID = 3; + GROUP_BY_EMAIL = 4; + GROUP_BY_DOMAIN = 5; + ALL_USERS = 6; + ALL_AUTHENTICATED_USERS = 7; + } + + optional Type type = 1; + optional string value = 2; +} + +message Entry { + enum Permission { + READ = 1; + WRITE = 2; + FULL_CONTROL = 3; + } + + optional Scope scope = 1; + optional Permission permission = 2; + optional string display_name = 3; +} + +message AccessControlList { + optional string owner = 1; + repeated Entry entries = 2; +} + +message FieldValue { + enum ContentType { + TEXT = 0; + HTML = 1; + ATOM = 2; + DATE = 3; + NUMBER = 4; + GEO = 5; + } + + optional ContentType type = 1 [default = TEXT]; + + optional string language = 2 [default = "en"]; + + optional string string_value = 3; + + optional group Geo = 4 { + required double lat = 5; + required double lng = 6; + } +} + +message Field { + required string name = 1; + required FieldValue value = 2; +} + +message FieldTypes { + required string name = 1; + repeated FieldValue.ContentType type = 2; +} + +message IndexShardSettings { + repeated int32 prev_num_shards = 1; + required int32 num_shards = 2 [default=1]; + repeated int32 prev_num_shards_search_false = 3; + optional string local_replica = 4 [default = ""]; +} + +message FacetValue { + enum ContentType { + ATOM = 2; + NUMBER = 4; + } + + optional ContentType type = 1 [default = ATOM]; + optional string string_value = 3; +} + +message Facet { + required string name = 1; + required FacetValue value = 2; +} + +message DocumentMetadata { + optional int64 version = 1; + optional int64 committed_st_version = 2; +} + +message Document { + optional string id = 1; + optional string language = 2 [default = "en"]; + repeated Field field = 3; + optional int32 order_id = 4; + optional OrderIdSource order_id_source = 6 [default = SUPPLIED]; + + enum OrderIdSource { + DEFAULTED = 0; + SUPPLIED = 1; + } + + enum Storage { + DISK = 0; + } + + optional Storage storage = 5 [default = DISK]; + repeated Facet facet = 8; +} + +message SearchServiceError { + enum ErrorCode { + OK = 0; + INVALID_REQUEST = 1; + TRANSIENT_ERROR = 2; + INTERNAL_ERROR = 3; + PERMISSION_DENIED = 4; + TIMEOUT = 5; + CONCURRENT_TRANSACTION = 6; + } +} + +message RequestStatus { + required SearchServiceError.ErrorCode code = 1; + optional string error_detail = 2; + optional int32 canonical_code = 3; +} + +message IndexSpec { + required string name = 1; + + enum Consistency { + GLOBAL = 0; + PER_DOCUMENT = 1; + } + optional Consistency consistency = 2 [default = PER_DOCUMENT]; + + optional string namespace = 3; + optional int32 version = 4; + + enum Source { + SEARCH = 0; + DATASTORE = 1; + CLOUD_STORAGE = 2; + } + optional Source source = 5 [default = SEARCH]; + + enum Mode { + PRIORITY = 0; + BACKGROUND = 1; + } + optional Mode mode = 6 [default = PRIORITY]; +} + +message IndexMetadata { + required IndexSpec index_spec = 1; + + repeated FieldTypes field = 2; + + message Storage { + optional int64 amount_used = 1; + optional int64 limit = 2; + } + optional Storage storage = 3; +} + +message IndexDocumentParams { + repeated Document document = 1; + + enum Freshness { + SYNCHRONOUSLY = 0; + WHEN_CONVENIENT = 1; + } + optional Freshness freshness = 2 [default = SYNCHRONOUSLY, deprecated=true]; + + required IndexSpec index_spec = 3; +} + +message IndexDocumentRequest { + required IndexDocumentParams params = 1; + + optional bytes app_id = 3; +} + +message IndexDocumentResponse { + repeated RequestStatus status = 1; + + repeated string doc_id = 2; +} + +message DeleteDocumentParams { + repeated string doc_id = 1; + + required IndexSpec index_spec = 2; +} + +message DeleteDocumentRequest { + required DeleteDocumentParams params = 1; + + optional bytes app_id = 3; +} + +message DeleteDocumentResponse { + repeated RequestStatus status = 1; +} + +message ListDocumentsParams { + required IndexSpec index_spec = 1; + optional string start_doc_id = 2; + optional bool include_start_doc = 3 [default = true]; + optional int32 limit = 4 [default = 100]; + optional bool keys_only = 5; +} + +message ListDocumentsRequest { + required ListDocumentsParams params = 1; + + optional bytes app_id = 2; +} + +message ListDocumentsResponse { + required RequestStatus status = 1; + + repeated Document document = 2; +} + +message ListIndexesParams { + optional bool fetch_schema = 1; + optional int32 limit = 2 [default = 20]; + optional string namespace = 3; + optional string start_index_name = 4; + optional bool include_start_index = 5 [default = true]; + optional string index_name_prefix = 6; + optional int32 offset = 7; + optional IndexSpec.Source source = 8 [default = SEARCH]; +} + +message ListIndexesRequest { + required ListIndexesParams params = 1; + + optional bytes app_id = 3; +} + +message ListIndexesResponse { + required RequestStatus status = 1; + repeated IndexMetadata index_metadata = 2; +} + +message DeleteSchemaParams { + optional IndexSpec.Source source = 1 [default = SEARCH]; + repeated IndexSpec index_spec = 2; +} + +message DeleteSchemaRequest { + required DeleteSchemaParams params = 1; + + optional bytes app_id = 3; +} + +message DeleteSchemaResponse { + repeated RequestStatus status = 1; +} + +message SortSpec { + required string sort_expression = 1; + optional bool sort_descending = 2 [default = true]; + optional string default_value_text = 4; + optional double default_value_numeric = 5; +} + +message ScorerSpec { + enum Scorer { + RESCORING_MATCH_SCORER = 0; + MATCH_SCORER = 2; + } + optional Scorer scorer = 1 [default = MATCH_SCORER]; + + optional int32 limit = 2 [default = 1000]; + optional string match_scorer_parameters = 9; +} + +message FieldSpec { + repeated string name = 1; + + repeated group Expression = 2 { + required string name = 3; + required string expression = 4; + } +} + +message FacetRange { + optional string name = 1; + optional string start = 2; + optional string end = 3; +} + +message FacetRequestParam { + optional int32 value_limit = 1; + repeated FacetRange range = 2; + repeated string value_constraint = 3; +} + +message FacetAutoDetectParam { + optional int32 value_limit = 1 [default = 10]; +} + +message FacetRequest { + required string name = 1; + optional FacetRequestParam params = 2; +} + +message FacetRefinement { + required string name = 1; + optional string value = 2; + + message Range { + optional string start = 1; + optional string end = 2; + } + optional Range range = 3; +} + +message SearchParams { + required IndexSpec index_spec = 1; + required string query = 2; + optional string cursor = 4; + optional int32 offset = 11; + + enum CursorType { + NONE = 0; + SINGLE = 1; + PER_RESULT = 2; + } + optional CursorType cursor_type = 5 [default = NONE]; + + optional int32 limit = 6 [default = 20]; + optional int32 matched_count_accuracy = 7; + repeated SortSpec sort_spec = 8; + optional ScorerSpec scorer_spec = 9; + optional FieldSpec field_spec = 10; + optional bool keys_only = 12; + + enum ParsingMode { + STRICT = 0; + RELAXED = 1; + } + optional ParsingMode parsing_mode = 13 [default = STRICT]; + + optional int32 auto_discover_facet_count = 15 [default = 0]; + repeated FacetRequest include_facet = 16; + repeated FacetRefinement facet_refinement = 17; + optional FacetAutoDetectParam facet_auto_detect_param = 18; + optional int32 facet_depth = 19 [default=1000]; +} + +message SearchRequest { + required SearchParams params = 1; + + optional bytes app_id = 3; +} + +message FacetResultValue { + required string name = 1; + required int32 count = 2; + required FacetRefinement refinement = 3; +} + +message FacetResult { + required string name = 1; + repeated FacetResultValue value = 2; +} + +message SearchResult { + required Document document = 1; + repeated Field expression = 4; + repeated double score = 2; + optional string cursor = 3; +} + +message SearchResponse { + repeated SearchResult result = 1; + required int64 matched_count = 2; + required RequestStatus status = 3; + optional string cursor = 4; + repeated FacetResult facet_result = 5; + + extensions 1000 to 9999; +} diff --git a/vendor/google.golang.org/appengine/internal/socket/socket_service.pb.go b/vendor/google.golang.org/appengine/internal/socket/socket_service.pb.go new file mode 100644 index 0000000000..323a6dd3ee --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/socket/socket_service.pb.go @@ -0,0 +1,2142 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/socket/socket_service.proto + +/* +Package socket is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/socket/socket_service.proto + +It has these top-level messages: + RemoteSocketServiceError + AddressPort + CreateSocketRequest + CreateSocketReply + BindRequest + BindReply + GetSocketNameRequest + GetSocketNameReply + GetPeerNameRequest + GetPeerNameReply + SocketOption + SetSocketOptionsRequest + SetSocketOptionsReply + GetSocketOptionsRequest + GetSocketOptionsReply + ConnectRequest + ConnectReply + ListenRequest + ListenReply + AcceptRequest + AcceptReply + ShutDownRequest + ShutDownReply + CloseRequest + CloseReply + SendRequest + SendReply + ReceiveRequest + ReceiveReply + PollEvent + PollRequest + PollReply + ResolveRequest + ResolveReply +*/ +package socket + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type RemoteSocketServiceError_ErrorCode int32 + +const ( + RemoteSocketServiceError_SYSTEM_ERROR RemoteSocketServiceError_ErrorCode = 1 + RemoteSocketServiceError_GAI_ERROR RemoteSocketServiceError_ErrorCode = 2 + RemoteSocketServiceError_FAILURE RemoteSocketServiceError_ErrorCode = 4 + RemoteSocketServiceError_PERMISSION_DENIED RemoteSocketServiceError_ErrorCode = 5 + RemoteSocketServiceError_INVALID_REQUEST RemoteSocketServiceError_ErrorCode = 6 + RemoteSocketServiceError_SOCKET_CLOSED RemoteSocketServiceError_ErrorCode = 7 +) + +var RemoteSocketServiceError_ErrorCode_name = map[int32]string{ + 1: "SYSTEM_ERROR", + 2: "GAI_ERROR", + 4: "FAILURE", + 5: "PERMISSION_DENIED", + 6: "INVALID_REQUEST", + 7: "SOCKET_CLOSED", +} +var RemoteSocketServiceError_ErrorCode_value = map[string]int32{ + "SYSTEM_ERROR": 1, + "GAI_ERROR": 2, + "FAILURE": 4, + "PERMISSION_DENIED": 5, + "INVALID_REQUEST": 6, + "SOCKET_CLOSED": 7, +} + +func (x RemoteSocketServiceError_ErrorCode) Enum() *RemoteSocketServiceError_ErrorCode { + p := new(RemoteSocketServiceError_ErrorCode) + *p = x + return p +} +func (x RemoteSocketServiceError_ErrorCode) String() string { + return proto.EnumName(RemoteSocketServiceError_ErrorCode_name, int32(x)) +} +func (x *RemoteSocketServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(RemoteSocketServiceError_ErrorCode_value, data, "RemoteSocketServiceError_ErrorCode") + if err != nil { + return err + } + *x = RemoteSocketServiceError_ErrorCode(value) + return nil +} +func (RemoteSocketServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type RemoteSocketServiceError_SystemError int32 + +const ( + RemoteSocketServiceError_SYS_SUCCESS RemoteSocketServiceError_SystemError = 0 + RemoteSocketServiceError_SYS_EPERM RemoteSocketServiceError_SystemError = 1 + RemoteSocketServiceError_SYS_ENOENT RemoteSocketServiceError_SystemError = 2 + RemoteSocketServiceError_SYS_ESRCH RemoteSocketServiceError_SystemError = 3 + RemoteSocketServiceError_SYS_EINTR RemoteSocketServiceError_SystemError = 4 + RemoteSocketServiceError_SYS_EIO RemoteSocketServiceError_SystemError = 5 + RemoteSocketServiceError_SYS_ENXIO RemoteSocketServiceError_SystemError = 6 + RemoteSocketServiceError_SYS_E2BIG RemoteSocketServiceError_SystemError = 7 + RemoteSocketServiceError_SYS_ENOEXEC RemoteSocketServiceError_SystemError = 8 + RemoteSocketServiceError_SYS_EBADF RemoteSocketServiceError_SystemError = 9 + RemoteSocketServiceError_SYS_ECHILD RemoteSocketServiceError_SystemError = 10 + RemoteSocketServiceError_SYS_EAGAIN RemoteSocketServiceError_SystemError = 11 + RemoteSocketServiceError_SYS_EWOULDBLOCK RemoteSocketServiceError_SystemError = 11 + RemoteSocketServiceError_SYS_ENOMEM RemoteSocketServiceError_SystemError = 12 + RemoteSocketServiceError_SYS_EACCES RemoteSocketServiceError_SystemError = 13 + RemoteSocketServiceError_SYS_EFAULT RemoteSocketServiceError_SystemError = 14 + RemoteSocketServiceError_SYS_ENOTBLK RemoteSocketServiceError_SystemError = 15 + RemoteSocketServiceError_SYS_EBUSY RemoteSocketServiceError_SystemError = 16 + RemoteSocketServiceError_SYS_EEXIST RemoteSocketServiceError_SystemError = 17 + RemoteSocketServiceError_SYS_EXDEV RemoteSocketServiceError_SystemError = 18 + RemoteSocketServiceError_SYS_ENODEV RemoteSocketServiceError_SystemError = 19 + RemoteSocketServiceError_SYS_ENOTDIR RemoteSocketServiceError_SystemError = 20 + RemoteSocketServiceError_SYS_EISDIR RemoteSocketServiceError_SystemError = 21 + RemoteSocketServiceError_SYS_EINVAL RemoteSocketServiceError_SystemError = 22 + RemoteSocketServiceError_SYS_ENFILE RemoteSocketServiceError_SystemError = 23 + RemoteSocketServiceError_SYS_EMFILE RemoteSocketServiceError_SystemError = 24 + RemoteSocketServiceError_SYS_ENOTTY RemoteSocketServiceError_SystemError = 25 + RemoteSocketServiceError_SYS_ETXTBSY RemoteSocketServiceError_SystemError = 26 + RemoteSocketServiceError_SYS_EFBIG RemoteSocketServiceError_SystemError = 27 + RemoteSocketServiceError_SYS_ENOSPC RemoteSocketServiceError_SystemError = 28 + RemoteSocketServiceError_SYS_ESPIPE RemoteSocketServiceError_SystemError = 29 + RemoteSocketServiceError_SYS_EROFS RemoteSocketServiceError_SystemError = 30 + RemoteSocketServiceError_SYS_EMLINK RemoteSocketServiceError_SystemError = 31 + RemoteSocketServiceError_SYS_EPIPE RemoteSocketServiceError_SystemError = 32 + RemoteSocketServiceError_SYS_EDOM RemoteSocketServiceError_SystemError = 33 + RemoteSocketServiceError_SYS_ERANGE RemoteSocketServiceError_SystemError = 34 + RemoteSocketServiceError_SYS_EDEADLK RemoteSocketServiceError_SystemError = 35 + RemoteSocketServiceError_SYS_EDEADLOCK RemoteSocketServiceError_SystemError = 35 + RemoteSocketServiceError_SYS_ENAMETOOLONG RemoteSocketServiceError_SystemError = 36 + RemoteSocketServiceError_SYS_ENOLCK RemoteSocketServiceError_SystemError = 37 + RemoteSocketServiceError_SYS_ENOSYS RemoteSocketServiceError_SystemError = 38 + RemoteSocketServiceError_SYS_ENOTEMPTY RemoteSocketServiceError_SystemError = 39 + RemoteSocketServiceError_SYS_ELOOP RemoteSocketServiceError_SystemError = 40 + RemoteSocketServiceError_SYS_ENOMSG RemoteSocketServiceError_SystemError = 42 + RemoteSocketServiceError_SYS_EIDRM RemoteSocketServiceError_SystemError = 43 + RemoteSocketServiceError_SYS_ECHRNG RemoteSocketServiceError_SystemError = 44 + RemoteSocketServiceError_SYS_EL2NSYNC RemoteSocketServiceError_SystemError = 45 + RemoteSocketServiceError_SYS_EL3HLT RemoteSocketServiceError_SystemError = 46 + RemoteSocketServiceError_SYS_EL3RST RemoteSocketServiceError_SystemError = 47 + RemoteSocketServiceError_SYS_ELNRNG RemoteSocketServiceError_SystemError = 48 + RemoteSocketServiceError_SYS_EUNATCH RemoteSocketServiceError_SystemError = 49 + RemoteSocketServiceError_SYS_ENOCSI RemoteSocketServiceError_SystemError = 50 + RemoteSocketServiceError_SYS_EL2HLT RemoteSocketServiceError_SystemError = 51 + RemoteSocketServiceError_SYS_EBADE RemoteSocketServiceError_SystemError = 52 + RemoteSocketServiceError_SYS_EBADR RemoteSocketServiceError_SystemError = 53 + RemoteSocketServiceError_SYS_EXFULL RemoteSocketServiceError_SystemError = 54 + RemoteSocketServiceError_SYS_ENOANO RemoteSocketServiceError_SystemError = 55 + RemoteSocketServiceError_SYS_EBADRQC RemoteSocketServiceError_SystemError = 56 + RemoteSocketServiceError_SYS_EBADSLT RemoteSocketServiceError_SystemError = 57 + RemoteSocketServiceError_SYS_EBFONT RemoteSocketServiceError_SystemError = 59 + RemoteSocketServiceError_SYS_ENOSTR RemoteSocketServiceError_SystemError = 60 + RemoteSocketServiceError_SYS_ENODATA RemoteSocketServiceError_SystemError = 61 + RemoteSocketServiceError_SYS_ETIME RemoteSocketServiceError_SystemError = 62 + RemoteSocketServiceError_SYS_ENOSR RemoteSocketServiceError_SystemError = 63 + RemoteSocketServiceError_SYS_ENONET RemoteSocketServiceError_SystemError = 64 + RemoteSocketServiceError_SYS_ENOPKG RemoteSocketServiceError_SystemError = 65 + RemoteSocketServiceError_SYS_EREMOTE RemoteSocketServiceError_SystemError = 66 + RemoteSocketServiceError_SYS_ENOLINK RemoteSocketServiceError_SystemError = 67 + RemoteSocketServiceError_SYS_EADV RemoteSocketServiceError_SystemError = 68 + RemoteSocketServiceError_SYS_ESRMNT RemoteSocketServiceError_SystemError = 69 + RemoteSocketServiceError_SYS_ECOMM RemoteSocketServiceError_SystemError = 70 + RemoteSocketServiceError_SYS_EPROTO RemoteSocketServiceError_SystemError = 71 + RemoteSocketServiceError_SYS_EMULTIHOP RemoteSocketServiceError_SystemError = 72 + RemoteSocketServiceError_SYS_EDOTDOT RemoteSocketServiceError_SystemError = 73 + RemoteSocketServiceError_SYS_EBADMSG RemoteSocketServiceError_SystemError = 74 + RemoteSocketServiceError_SYS_EOVERFLOW RemoteSocketServiceError_SystemError = 75 + RemoteSocketServiceError_SYS_ENOTUNIQ RemoteSocketServiceError_SystemError = 76 + RemoteSocketServiceError_SYS_EBADFD RemoteSocketServiceError_SystemError = 77 + RemoteSocketServiceError_SYS_EREMCHG RemoteSocketServiceError_SystemError = 78 + RemoteSocketServiceError_SYS_ELIBACC RemoteSocketServiceError_SystemError = 79 + RemoteSocketServiceError_SYS_ELIBBAD RemoteSocketServiceError_SystemError = 80 + RemoteSocketServiceError_SYS_ELIBSCN RemoteSocketServiceError_SystemError = 81 + RemoteSocketServiceError_SYS_ELIBMAX RemoteSocketServiceError_SystemError = 82 + RemoteSocketServiceError_SYS_ELIBEXEC RemoteSocketServiceError_SystemError = 83 + RemoteSocketServiceError_SYS_EILSEQ RemoteSocketServiceError_SystemError = 84 + RemoteSocketServiceError_SYS_ERESTART RemoteSocketServiceError_SystemError = 85 + RemoteSocketServiceError_SYS_ESTRPIPE RemoteSocketServiceError_SystemError = 86 + RemoteSocketServiceError_SYS_EUSERS RemoteSocketServiceError_SystemError = 87 + RemoteSocketServiceError_SYS_ENOTSOCK RemoteSocketServiceError_SystemError = 88 + RemoteSocketServiceError_SYS_EDESTADDRREQ RemoteSocketServiceError_SystemError = 89 + RemoteSocketServiceError_SYS_EMSGSIZE RemoteSocketServiceError_SystemError = 90 + RemoteSocketServiceError_SYS_EPROTOTYPE RemoteSocketServiceError_SystemError = 91 + RemoteSocketServiceError_SYS_ENOPROTOOPT RemoteSocketServiceError_SystemError = 92 + RemoteSocketServiceError_SYS_EPROTONOSUPPORT RemoteSocketServiceError_SystemError = 93 + RemoteSocketServiceError_SYS_ESOCKTNOSUPPORT RemoteSocketServiceError_SystemError = 94 + RemoteSocketServiceError_SYS_EOPNOTSUPP RemoteSocketServiceError_SystemError = 95 + RemoteSocketServiceError_SYS_ENOTSUP RemoteSocketServiceError_SystemError = 95 + RemoteSocketServiceError_SYS_EPFNOSUPPORT RemoteSocketServiceError_SystemError = 96 + RemoteSocketServiceError_SYS_EAFNOSUPPORT RemoteSocketServiceError_SystemError = 97 + RemoteSocketServiceError_SYS_EADDRINUSE RemoteSocketServiceError_SystemError = 98 + RemoteSocketServiceError_SYS_EADDRNOTAVAIL RemoteSocketServiceError_SystemError = 99 + RemoteSocketServiceError_SYS_ENETDOWN RemoteSocketServiceError_SystemError = 100 + RemoteSocketServiceError_SYS_ENETUNREACH RemoteSocketServiceError_SystemError = 101 + RemoteSocketServiceError_SYS_ENETRESET RemoteSocketServiceError_SystemError = 102 + RemoteSocketServiceError_SYS_ECONNABORTED RemoteSocketServiceError_SystemError = 103 + RemoteSocketServiceError_SYS_ECONNRESET RemoteSocketServiceError_SystemError = 104 + RemoteSocketServiceError_SYS_ENOBUFS RemoteSocketServiceError_SystemError = 105 + RemoteSocketServiceError_SYS_EISCONN RemoteSocketServiceError_SystemError = 106 + RemoteSocketServiceError_SYS_ENOTCONN RemoteSocketServiceError_SystemError = 107 + RemoteSocketServiceError_SYS_ESHUTDOWN RemoteSocketServiceError_SystemError = 108 + RemoteSocketServiceError_SYS_ETOOMANYREFS RemoteSocketServiceError_SystemError = 109 + RemoteSocketServiceError_SYS_ETIMEDOUT RemoteSocketServiceError_SystemError = 110 + RemoteSocketServiceError_SYS_ECONNREFUSED RemoteSocketServiceError_SystemError = 111 + RemoteSocketServiceError_SYS_EHOSTDOWN RemoteSocketServiceError_SystemError = 112 + RemoteSocketServiceError_SYS_EHOSTUNREACH RemoteSocketServiceError_SystemError = 113 + RemoteSocketServiceError_SYS_EALREADY RemoteSocketServiceError_SystemError = 114 + RemoteSocketServiceError_SYS_EINPROGRESS RemoteSocketServiceError_SystemError = 115 + RemoteSocketServiceError_SYS_ESTALE RemoteSocketServiceError_SystemError = 116 + RemoteSocketServiceError_SYS_EUCLEAN RemoteSocketServiceError_SystemError = 117 + RemoteSocketServiceError_SYS_ENOTNAM RemoteSocketServiceError_SystemError = 118 + RemoteSocketServiceError_SYS_ENAVAIL RemoteSocketServiceError_SystemError = 119 + RemoteSocketServiceError_SYS_EISNAM RemoteSocketServiceError_SystemError = 120 + RemoteSocketServiceError_SYS_EREMOTEIO RemoteSocketServiceError_SystemError = 121 + RemoteSocketServiceError_SYS_EDQUOT RemoteSocketServiceError_SystemError = 122 + RemoteSocketServiceError_SYS_ENOMEDIUM RemoteSocketServiceError_SystemError = 123 + RemoteSocketServiceError_SYS_EMEDIUMTYPE RemoteSocketServiceError_SystemError = 124 + RemoteSocketServiceError_SYS_ECANCELED RemoteSocketServiceError_SystemError = 125 + RemoteSocketServiceError_SYS_ENOKEY RemoteSocketServiceError_SystemError = 126 + RemoteSocketServiceError_SYS_EKEYEXPIRED RemoteSocketServiceError_SystemError = 127 + RemoteSocketServiceError_SYS_EKEYREVOKED RemoteSocketServiceError_SystemError = 128 + RemoteSocketServiceError_SYS_EKEYREJECTED RemoteSocketServiceError_SystemError = 129 + RemoteSocketServiceError_SYS_EOWNERDEAD RemoteSocketServiceError_SystemError = 130 + RemoteSocketServiceError_SYS_ENOTRECOVERABLE RemoteSocketServiceError_SystemError = 131 + RemoteSocketServiceError_SYS_ERFKILL RemoteSocketServiceError_SystemError = 132 +) + +var RemoteSocketServiceError_SystemError_name = map[int32]string{ + 0: "SYS_SUCCESS", + 1: "SYS_EPERM", + 2: "SYS_ENOENT", + 3: "SYS_ESRCH", + 4: "SYS_EINTR", + 5: "SYS_EIO", + 6: "SYS_ENXIO", + 7: "SYS_E2BIG", + 8: "SYS_ENOEXEC", + 9: "SYS_EBADF", + 10: "SYS_ECHILD", + 11: "SYS_EAGAIN", + // Duplicate value: 11: "SYS_EWOULDBLOCK", + 12: "SYS_ENOMEM", + 13: "SYS_EACCES", + 14: "SYS_EFAULT", + 15: "SYS_ENOTBLK", + 16: "SYS_EBUSY", + 17: "SYS_EEXIST", + 18: "SYS_EXDEV", + 19: "SYS_ENODEV", + 20: "SYS_ENOTDIR", + 21: "SYS_EISDIR", + 22: "SYS_EINVAL", + 23: "SYS_ENFILE", + 24: "SYS_EMFILE", + 25: "SYS_ENOTTY", + 26: "SYS_ETXTBSY", + 27: "SYS_EFBIG", + 28: "SYS_ENOSPC", + 29: "SYS_ESPIPE", + 30: "SYS_EROFS", + 31: "SYS_EMLINK", + 32: "SYS_EPIPE", + 33: "SYS_EDOM", + 34: "SYS_ERANGE", + 35: "SYS_EDEADLK", + // Duplicate value: 35: "SYS_EDEADLOCK", + 36: "SYS_ENAMETOOLONG", + 37: "SYS_ENOLCK", + 38: "SYS_ENOSYS", + 39: "SYS_ENOTEMPTY", + 40: "SYS_ELOOP", + 42: "SYS_ENOMSG", + 43: "SYS_EIDRM", + 44: "SYS_ECHRNG", + 45: "SYS_EL2NSYNC", + 46: "SYS_EL3HLT", + 47: "SYS_EL3RST", + 48: "SYS_ELNRNG", + 49: "SYS_EUNATCH", + 50: "SYS_ENOCSI", + 51: "SYS_EL2HLT", + 52: "SYS_EBADE", + 53: "SYS_EBADR", + 54: "SYS_EXFULL", + 55: "SYS_ENOANO", + 56: "SYS_EBADRQC", + 57: "SYS_EBADSLT", + 59: "SYS_EBFONT", + 60: "SYS_ENOSTR", + 61: "SYS_ENODATA", + 62: "SYS_ETIME", + 63: "SYS_ENOSR", + 64: "SYS_ENONET", + 65: "SYS_ENOPKG", + 66: "SYS_EREMOTE", + 67: "SYS_ENOLINK", + 68: "SYS_EADV", + 69: "SYS_ESRMNT", + 70: "SYS_ECOMM", + 71: "SYS_EPROTO", + 72: "SYS_EMULTIHOP", + 73: "SYS_EDOTDOT", + 74: "SYS_EBADMSG", + 75: "SYS_EOVERFLOW", + 76: "SYS_ENOTUNIQ", + 77: "SYS_EBADFD", + 78: "SYS_EREMCHG", + 79: "SYS_ELIBACC", + 80: "SYS_ELIBBAD", + 81: "SYS_ELIBSCN", + 82: "SYS_ELIBMAX", + 83: "SYS_ELIBEXEC", + 84: "SYS_EILSEQ", + 85: "SYS_ERESTART", + 86: "SYS_ESTRPIPE", + 87: "SYS_EUSERS", + 88: "SYS_ENOTSOCK", + 89: "SYS_EDESTADDRREQ", + 90: "SYS_EMSGSIZE", + 91: "SYS_EPROTOTYPE", + 92: "SYS_ENOPROTOOPT", + 93: "SYS_EPROTONOSUPPORT", + 94: "SYS_ESOCKTNOSUPPORT", + 95: "SYS_EOPNOTSUPP", + // Duplicate value: 95: "SYS_ENOTSUP", + 96: "SYS_EPFNOSUPPORT", + 97: "SYS_EAFNOSUPPORT", + 98: "SYS_EADDRINUSE", + 99: "SYS_EADDRNOTAVAIL", + 100: "SYS_ENETDOWN", + 101: "SYS_ENETUNREACH", + 102: "SYS_ENETRESET", + 103: "SYS_ECONNABORTED", + 104: "SYS_ECONNRESET", + 105: "SYS_ENOBUFS", + 106: "SYS_EISCONN", + 107: "SYS_ENOTCONN", + 108: "SYS_ESHUTDOWN", + 109: "SYS_ETOOMANYREFS", + 110: "SYS_ETIMEDOUT", + 111: "SYS_ECONNREFUSED", + 112: "SYS_EHOSTDOWN", + 113: "SYS_EHOSTUNREACH", + 114: "SYS_EALREADY", + 115: "SYS_EINPROGRESS", + 116: "SYS_ESTALE", + 117: "SYS_EUCLEAN", + 118: "SYS_ENOTNAM", + 119: "SYS_ENAVAIL", + 120: "SYS_EISNAM", + 121: "SYS_EREMOTEIO", + 122: "SYS_EDQUOT", + 123: "SYS_ENOMEDIUM", + 124: "SYS_EMEDIUMTYPE", + 125: "SYS_ECANCELED", + 126: "SYS_ENOKEY", + 127: "SYS_EKEYEXPIRED", + 128: "SYS_EKEYREVOKED", + 129: "SYS_EKEYREJECTED", + 130: "SYS_EOWNERDEAD", + 131: "SYS_ENOTRECOVERABLE", + 132: "SYS_ERFKILL", +} +var RemoteSocketServiceError_SystemError_value = map[string]int32{ + "SYS_SUCCESS": 0, + "SYS_EPERM": 1, + "SYS_ENOENT": 2, + "SYS_ESRCH": 3, + "SYS_EINTR": 4, + "SYS_EIO": 5, + "SYS_ENXIO": 6, + "SYS_E2BIG": 7, + "SYS_ENOEXEC": 8, + "SYS_EBADF": 9, + "SYS_ECHILD": 10, + "SYS_EAGAIN": 11, + "SYS_EWOULDBLOCK": 11, + "SYS_ENOMEM": 12, + "SYS_EACCES": 13, + "SYS_EFAULT": 14, + "SYS_ENOTBLK": 15, + "SYS_EBUSY": 16, + "SYS_EEXIST": 17, + "SYS_EXDEV": 18, + "SYS_ENODEV": 19, + "SYS_ENOTDIR": 20, + "SYS_EISDIR": 21, + "SYS_EINVAL": 22, + "SYS_ENFILE": 23, + "SYS_EMFILE": 24, + "SYS_ENOTTY": 25, + "SYS_ETXTBSY": 26, + "SYS_EFBIG": 27, + "SYS_ENOSPC": 28, + "SYS_ESPIPE": 29, + "SYS_EROFS": 30, + "SYS_EMLINK": 31, + "SYS_EPIPE": 32, + "SYS_EDOM": 33, + "SYS_ERANGE": 34, + "SYS_EDEADLK": 35, + "SYS_EDEADLOCK": 35, + "SYS_ENAMETOOLONG": 36, + "SYS_ENOLCK": 37, + "SYS_ENOSYS": 38, + "SYS_ENOTEMPTY": 39, + "SYS_ELOOP": 40, + "SYS_ENOMSG": 42, + "SYS_EIDRM": 43, + "SYS_ECHRNG": 44, + "SYS_EL2NSYNC": 45, + "SYS_EL3HLT": 46, + "SYS_EL3RST": 47, + "SYS_ELNRNG": 48, + "SYS_EUNATCH": 49, + "SYS_ENOCSI": 50, + "SYS_EL2HLT": 51, + "SYS_EBADE": 52, + "SYS_EBADR": 53, + "SYS_EXFULL": 54, + "SYS_ENOANO": 55, + "SYS_EBADRQC": 56, + "SYS_EBADSLT": 57, + "SYS_EBFONT": 59, + "SYS_ENOSTR": 60, + "SYS_ENODATA": 61, + "SYS_ETIME": 62, + "SYS_ENOSR": 63, + "SYS_ENONET": 64, + "SYS_ENOPKG": 65, + "SYS_EREMOTE": 66, + "SYS_ENOLINK": 67, + "SYS_EADV": 68, + "SYS_ESRMNT": 69, + "SYS_ECOMM": 70, + "SYS_EPROTO": 71, + "SYS_EMULTIHOP": 72, + "SYS_EDOTDOT": 73, + "SYS_EBADMSG": 74, + "SYS_EOVERFLOW": 75, + "SYS_ENOTUNIQ": 76, + "SYS_EBADFD": 77, + "SYS_EREMCHG": 78, + "SYS_ELIBACC": 79, + "SYS_ELIBBAD": 80, + "SYS_ELIBSCN": 81, + "SYS_ELIBMAX": 82, + "SYS_ELIBEXEC": 83, + "SYS_EILSEQ": 84, + "SYS_ERESTART": 85, + "SYS_ESTRPIPE": 86, + "SYS_EUSERS": 87, + "SYS_ENOTSOCK": 88, + "SYS_EDESTADDRREQ": 89, + "SYS_EMSGSIZE": 90, + "SYS_EPROTOTYPE": 91, + "SYS_ENOPROTOOPT": 92, + "SYS_EPROTONOSUPPORT": 93, + "SYS_ESOCKTNOSUPPORT": 94, + "SYS_EOPNOTSUPP": 95, + "SYS_ENOTSUP": 95, + "SYS_EPFNOSUPPORT": 96, + "SYS_EAFNOSUPPORT": 97, + "SYS_EADDRINUSE": 98, + "SYS_EADDRNOTAVAIL": 99, + "SYS_ENETDOWN": 100, + "SYS_ENETUNREACH": 101, + "SYS_ENETRESET": 102, + "SYS_ECONNABORTED": 103, + "SYS_ECONNRESET": 104, + "SYS_ENOBUFS": 105, + "SYS_EISCONN": 106, + "SYS_ENOTCONN": 107, + "SYS_ESHUTDOWN": 108, + "SYS_ETOOMANYREFS": 109, + "SYS_ETIMEDOUT": 110, + "SYS_ECONNREFUSED": 111, + "SYS_EHOSTDOWN": 112, + "SYS_EHOSTUNREACH": 113, + "SYS_EALREADY": 114, + "SYS_EINPROGRESS": 115, + "SYS_ESTALE": 116, + "SYS_EUCLEAN": 117, + "SYS_ENOTNAM": 118, + "SYS_ENAVAIL": 119, + "SYS_EISNAM": 120, + "SYS_EREMOTEIO": 121, + "SYS_EDQUOT": 122, + "SYS_ENOMEDIUM": 123, + "SYS_EMEDIUMTYPE": 124, + "SYS_ECANCELED": 125, + "SYS_ENOKEY": 126, + "SYS_EKEYEXPIRED": 127, + "SYS_EKEYREVOKED": 128, + "SYS_EKEYREJECTED": 129, + "SYS_EOWNERDEAD": 130, + "SYS_ENOTRECOVERABLE": 131, + "SYS_ERFKILL": 132, +} + +func (x RemoteSocketServiceError_SystemError) Enum() *RemoteSocketServiceError_SystemError { + p := new(RemoteSocketServiceError_SystemError) + *p = x + return p +} +func (x RemoteSocketServiceError_SystemError) String() string { + return proto.EnumName(RemoteSocketServiceError_SystemError_name, int32(x)) +} +func (x *RemoteSocketServiceError_SystemError) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(RemoteSocketServiceError_SystemError_value, data, "RemoteSocketServiceError_SystemError") + if err != nil { + return err + } + *x = RemoteSocketServiceError_SystemError(value) + return nil +} +func (RemoteSocketServiceError_SystemError) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 1} +} + +type CreateSocketRequest_SocketFamily int32 + +const ( + CreateSocketRequest_IPv4 CreateSocketRequest_SocketFamily = 1 + CreateSocketRequest_IPv6 CreateSocketRequest_SocketFamily = 2 +) + +var CreateSocketRequest_SocketFamily_name = map[int32]string{ + 1: "IPv4", + 2: "IPv6", +} +var CreateSocketRequest_SocketFamily_value = map[string]int32{ + "IPv4": 1, + "IPv6": 2, +} + +func (x CreateSocketRequest_SocketFamily) Enum() *CreateSocketRequest_SocketFamily { + p := new(CreateSocketRequest_SocketFamily) + *p = x + return p +} +func (x CreateSocketRequest_SocketFamily) String() string { + return proto.EnumName(CreateSocketRequest_SocketFamily_name, int32(x)) +} +func (x *CreateSocketRequest_SocketFamily) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(CreateSocketRequest_SocketFamily_value, data, "CreateSocketRequest_SocketFamily") + if err != nil { + return err + } + *x = CreateSocketRequest_SocketFamily(value) + return nil +} +func (CreateSocketRequest_SocketFamily) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{2, 0} +} + +type CreateSocketRequest_SocketProtocol int32 + +const ( + CreateSocketRequest_TCP CreateSocketRequest_SocketProtocol = 1 + CreateSocketRequest_UDP CreateSocketRequest_SocketProtocol = 2 +) + +var CreateSocketRequest_SocketProtocol_name = map[int32]string{ + 1: "TCP", + 2: "UDP", +} +var CreateSocketRequest_SocketProtocol_value = map[string]int32{ + "TCP": 1, + "UDP": 2, +} + +func (x CreateSocketRequest_SocketProtocol) Enum() *CreateSocketRequest_SocketProtocol { + p := new(CreateSocketRequest_SocketProtocol) + *p = x + return p +} +func (x CreateSocketRequest_SocketProtocol) String() string { + return proto.EnumName(CreateSocketRequest_SocketProtocol_name, int32(x)) +} +func (x *CreateSocketRequest_SocketProtocol) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(CreateSocketRequest_SocketProtocol_value, data, "CreateSocketRequest_SocketProtocol") + if err != nil { + return err + } + *x = CreateSocketRequest_SocketProtocol(value) + return nil +} +func (CreateSocketRequest_SocketProtocol) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{2, 1} +} + +type SocketOption_SocketOptionLevel int32 + +const ( + SocketOption_SOCKET_SOL_IP SocketOption_SocketOptionLevel = 0 + SocketOption_SOCKET_SOL_SOCKET SocketOption_SocketOptionLevel = 1 + SocketOption_SOCKET_SOL_TCP SocketOption_SocketOptionLevel = 6 + SocketOption_SOCKET_SOL_UDP SocketOption_SocketOptionLevel = 17 +) + +var SocketOption_SocketOptionLevel_name = map[int32]string{ + 0: "SOCKET_SOL_IP", + 1: "SOCKET_SOL_SOCKET", + 6: "SOCKET_SOL_TCP", + 17: "SOCKET_SOL_UDP", +} +var SocketOption_SocketOptionLevel_value = map[string]int32{ + "SOCKET_SOL_IP": 0, + "SOCKET_SOL_SOCKET": 1, + "SOCKET_SOL_TCP": 6, + "SOCKET_SOL_UDP": 17, +} + +func (x SocketOption_SocketOptionLevel) Enum() *SocketOption_SocketOptionLevel { + p := new(SocketOption_SocketOptionLevel) + *p = x + return p +} +func (x SocketOption_SocketOptionLevel) String() string { + return proto.EnumName(SocketOption_SocketOptionLevel_name, int32(x)) +} +func (x *SocketOption_SocketOptionLevel) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(SocketOption_SocketOptionLevel_value, data, "SocketOption_SocketOptionLevel") + if err != nil { + return err + } + *x = SocketOption_SocketOptionLevel(value) + return nil +} +func (SocketOption_SocketOptionLevel) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{10, 0} +} + +type SocketOption_SocketOptionName int32 + +const ( + SocketOption_SOCKET_SO_DEBUG SocketOption_SocketOptionName = 1 + SocketOption_SOCKET_SO_REUSEADDR SocketOption_SocketOptionName = 2 + SocketOption_SOCKET_SO_TYPE SocketOption_SocketOptionName = 3 + SocketOption_SOCKET_SO_ERROR SocketOption_SocketOptionName = 4 + SocketOption_SOCKET_SO_DONTROUTE SocketOption_SocketOptionName = 5 + SocketOption_SOCKET_SO_BROADCAST SocketOption_SocketOptionName = 6 + SocketOption_SOCKET_SO_SNDBUF SocketOption_SocketOptionName = 7 + SocketOption_SOCKET_SO_RCVBUF SocketOption_SocketOptionName = 8 + SocketOption_SOCKET_SO_KEEPALIVE SocketOption_SocketOptionName = 9 + SocketOption_SOCKET_SO_OOBINLINE SocketOption_SocketOptionName = 10 + SocketOption_SOCKET_SO_LINGER SocketOption_SocketOptionName = 13 + SocketOption_SOCKET_SO_RCVTIMEO SocketOption_SocketOptionName = 20 + SocketOption_SOCKET_SO_SNDTIMEO SocketOption_SocketOptionName = 21 + SocketOption_SOCKET_IP_TOS SocketOption_SocketOptionName = 1 + SocketOption_SOCKET_IP_TTL SocketOption_SocketOptionName = 2 + SocketOption_SOCKET_IP_HDRINCL SocketOption_SocketOptionName = 3 + SocketOption_SOCKET_IP_OPTIONS SocketOption_SocketOptionName = 4 + SocketOption_SOCKET_TCP_NODELAY SocketOption_SocketOptionName = 1 + SocketOption_SOCKET_TCP_MAXSEG SocketOption_SocketOptionName = 2 + SocketOption_SOCKET_TCP_CORK SocketOption_SocketOptionName = 3 + SocketOption_SOCKET_TCP_KEEPIDLE SocketOption_SocketOptionName = 4 + SocketOption_SOCKET_TCP_KEEPINTVL SocketOption_SocketOptionName = 5 + SocketOption_SOCKET_TCP_KEEPCNT SocketOption_SocketOptionName = 6 + SocketOption_SOCKET_TCP_SYNCNT SocketOption_SocketOptionName = 7 + SocketOption_SOCKET_TCP_LINGER2 SocketOption_SocketOptionName = 8 + SocketOption_SOCKET_TCP_DEFER_ACCEPT SocketOption_SocketOptionName = 9 + SocketOption_SOCKET_TCP_WINDOW_CLAMP SocketOption_SocketOptionName = 10 + SocketOption_SOCKET_TCP_INFO SocketOption_SocketOptionName = 11 + SocketOption_SOCKET_TCP_QUICKACK SocketOption_SocketOptionName = 12 +) + +var SocketOption_SocketOptionName_name = map[int32]string{ + 1: "SOCKET_SO_DEBUG", + 2: "SOCKET_SO_REUSEADDR", + 3: "SOCKET_SO_TYPE", + 4: "SOCKET_SO_ERROR", + 5: "SOCKET_SO_DONTROUTE", + 6: "SOCKET_SO_BROADCAST", + 7: "SOCKET_SO_SNDBUF", + 8: "SOCKET_SO_RCVBUF", + 9: "SOCKET_SO_KEEPALIVE", + 10: "SOCKET_SO_OOBINLINE", + 13: "SOCKET_SO_LINGER", + 20: "SOCKET_SO_RCVTIMEO", + 21: "SOCKET_SO_SNDTIMEO", + // Duplicate value: 1: "SOCKET_IP_TOS", + // Duplicate value: 2: "SOCKET_IP_TTL", + // Duplicate value: 3: "SOCKET_IP_HDRINCL", + // Duplicate value: 4: "SOCKET_IP_OPTIONS", + // Duplicate value: 1: "SOCKET_TCP_NODELAY", + // Duplicate value: 2: "SOCKET_TCP_MAXSEG", + // Duplicate value: 3: "SOCKET_TCP_CORK", + // Duplicate value: 4: "SOCKET_TCP_KEEPIDLE", + // Duplicate value: 5: "SOCKET_TCP_KEEPINTVL", + // Duplicate value: 6: "SOCKET_TCP_KEEPCNT", + // Duplicate value: 7: "SOCKET_TCP_SYNCNT", + // Duplicate value: 8: "SOCKET_TCP_LINGER2", + // Duplicate value: 9: "SOCKET_TCP_DEFER_ACCEPT", + // Duplicate value: 10: "SOCKET_TCP_WINDOW_CLAMP", + 11: "SOCKET_TCP_INFO", + 12: "SOCKET_TCP_QUICKACK", +} +var SocketOption_SocketOptionName_value = map[string]int32{ + "SOCKET_SO_DEBUG": 1, + "SOCKET_SO_REUSEADDR": 2, + "SOCKET_SO_TYPE": 3, + "SOCKET_SO_ERROR": 4, + "SOCKET_SO_DONTROUTE": 5, + "SOCKET_SO_BROADCAST": 6, + "SOCKET_SO_SNDBUF": 7, + "SOCKET_SO_RCVBUF": 8, + "SOCKET_SO_KEEPALIVE": 9, + "SOCKET_SO_OOBINLINE": 10, + "SOCKET_SO_LINGER": 13, + "SOCKET_SO_RCVTIMEO": 20, + "SOCKET_SO_SNDTIMEO": 21, + "SOCKET_IP_TOS": 1, + "SOCKET_IP_TTL": 2, + "SOCKET_IP_HDRINCL": 3, + "SOCKET_IP_OPTIONS": 4, + "SOCKET_TCP_NODELAY": 1, + "SOCKET_TCP_MAXSEG": 2, + "SOCKET_TCP_CORK": 3, + "SOCKET_TCP_KEEPIDLE": 4, + "SOCKET_TCP_KEEPINTVL": 5, + "SOCKET_TCP_KEEPCNT": 6, + "SOCKET_TCP_SYNCNT": 7, + "SOCKET_TCP_LINGER2": 8, + "SOCKET_TCP_DEFER_ACCEPT": 9, + "SOCKET_TCP_WINDOW_CLAMP": 10, + "SOCKET_TCP_INFO": 11, + "SOCKET_TCP_QUICKACK": 12, +} + +func (x SocketOption_SocketOptionName) Enum() *SocketOption_SocketOptionName { + p := new(SocketOption_SocketOptionName) + *p = x + return p +} +func (x SocketOption_SocketOptionName) String() string { + return proto.EnumName(SocketOption_SocketOptionName_name, int32(x)) +} +func (x *SocketOption_SocketOptionName) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(SocketOption_SocketOptionName_value, data, "SocketOption_SocketOptionName") + if err != nil { + return err + } + *x = SocketOption_SocketOptionName(value) + return nil +} +func (SocketOption_SocketOptionName) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{10, 1} +} + +type ShutDownRequest_How int32 + +const ( + ShutDownRequest_SOCKET_SHUT_RD ShutDownRequest_How = 1 + ShutDownRequest_SOCKET_SHUT_WR ShutDownRequest_How = 2 + ShutDownRequest_SOCKET_SHUT_RDWR ShutDownRequest_How = 3 +) + +var ShutDownRequest_How_name = map[int32]string{ + 1: "SOCKET_SHUT_RD", + 2: "SOCKET_SHUT_WR", + 3: "SOCKET_SHUT_RDWR", +} +var ShutDownRequest_How_value = map[string]int32{ + "SOCKET_SHUT_RD": 1, + "SOCKET_SHUT_WR": 2, + "SOCKET_SHUT_RDWR": 3, +} + +func (x ShutDownRequest_How) Enum() *ShutDownRequest_How { + p := new(ShutDownRequest_How) + *p = x + return p +} +func (x ShutDownRequest_How) String() string { + return proto.EnumName(ShutDownRequest_How_name, int32(x)) +} +func (x *ShutDownRequest_How) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ShutDownRequest_How_value, data, "ShutDownRequest_How") + if err != nil { + return err + } + *x = ShutDownRequest_How(value) + return nil +} +func (ShutDownRequest_How) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{21, 0} } + +type ReceiveRequest_Flags int32 + +const ( + ReceiveRequest_MSG_OOB ReceiveRequest_Flags = 1 + ReceiveRequest_MSG_PEEK ReceiveRequest_Flags = 2 +) + +var ReceiveRequest_Flags_name = map[int32]string{ + 1: "MSG_OOB", + 2: "MSG_PEEK", +} +var ReceiveRequest_Flags_value = map[string]int32{ + "MSG_OOB": 1, + "MSG_PEEK": 2, +} + +func (x ReceiveRequest_Flags) Enum() *ReceiveRequest_Flags { + p := new(ReceiveRequest_Flags) + *p = x + return p +} +func (x ReceiveRequest_Flags) String() string { + return proto.EnumName(ReceiveRequest_Flags_name, int32(x)) +} +func (x *ReceiveRequest_Flags) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ReceiveRequest_Flags_value, data, "ReceiveRequest_Flags") + if err != nil { + return err + } + *x = ReceiveRequest_Flags(value) + return nil +} +func (ReceiveRequest_Flags) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{27, 0} } + +type PollEvent_PollEventFlag int32 + +const ( + PollEvent_SOCKET_POLLNONE PollEvent_PollEventFlag = 0 + PollEvent_SOCKET_POLLIN PollEvent_PollEventFlag = 1 + PollEvent_SOCKET_POLLPRI PollEvent_PollEventFlag = 2 + PollEvent_SOCKET_POLLOUT PollEvent_PollEventFlag = 4 + PollEvent_SOCKET_POLLERR PollEvent_PollEventFlag = 8 + PollEvent_SOCKET_POLLHUP PollEvent_PollEventFlag = 16 + PollEvent_SOCKET_POLLNVAL PollEvent_PollEventFlag = 32 + PollEvent_SOCKET_POLLRDNORM PollEvent_PollEventFlag = 64 + PollEvent_SOCKET_POLLRDBAND PollEvent_PollEventFlag = 128 + PollEvent_SOCKET_POLLWRNORM PollEvent_PollEventFlag = 256 + PollEvent_SOCKET_POLLWRBAND PollEvent_PollEventFlag = 512 + PollEvent_SOCKET_POLLMSG PollEvent_PollEventFlag = 1024 + PollEvent_SOCKET_POLLREMOVE PollEvent_PollEventFlag = 4096 + PollEvent_SOCKET_POLLRDHUP PollEvent_PollEventFlag = 8192 +) + +var PollEvent_PollEventFlag_name = map[int32]string{ + 0: "SOCKET_POLLNONE", + 1: "SOCKET_POLLIN", + 2: "SOCKET_POLLPRI", + 4: "SOCKET_POLLOUT", + 8: "SOCKET_POLLERR", + 16: "SOCKET_POLLHUP", + 32: "SOCKET_POLLNVAL", + 64: "SOCKET_POLLRDNORM", + 128: "SOCKET_POLLRDBAND", + 256: "SOCKET_POLLWRNORM", + 512: "SOCKET_POLLWRBAND", + 1024: "SOCKET_POLLMSG", + 4096: "SOCKET_POLLREMOVE", + 8192: "SOCKET_POLLRDHUP", +} +var PollEvent_PollEventFlag_value = map[string]int32{ + "SOCKET_POLLNONE": 0, + "SOCKET_POLLIN": 1, + "SOCKET_POLLPRI": 2, + "SOCKET_POLLOUT": 4, + "SOCKET_POLLERR": 8, + "SOCKET_POLLHUP": 16, + "SOCKET_POLLNVAL": 32, + "SOCKET_POLLRDNORM": 64, + "SOCKET_POLLRDBAND": 128, + "SOCKET_POLLWRNORM": 256, + "SOCKET_POLLWRBAND": 512, + "SOCKET_POLLMSG": 1024, + "SOCKET_POLLREMOVE": 4096, + "SOCKET_POLLRDHUP": 8192, +} + +func (x PollEvent_PollEventFlag) Enum() *PollEvent_PollEventFlag { + p := new(PollEvent_PollEventFlag) + *p = x + return p +} +func (x PollEvent_PollEventFlag) String() string { + return proto.EnumName(PollEvent_PollEventFlag_name, int32(x)) +} +func (x *PollEvent_PollEventFlag) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(PollEvent_PollEventFlag_value, data, "PollEvent_PollEventFlag") + if err != nil { + return err + } + *x = PollEvent_PollEventFlag(value) + return nil +} +func (PollEvent_PollEventFlag) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{29, 0} } + +type ResolveReply_ErrorCode int32 + +const ( + ResolveReply_SOCKET_EAI_ADDRFAMILY ResolveReply_ErrorCode = 1 + ResolveReply_SOCKET_EAI_AGAIN ResolveReply_ErrorCode = 2 + ResolveReply_SOCKET_EAI_BADFLAGS ResolveReply_ErrorCode = 3 + ResolveReply_SOCKET_EAI_FAIL ResolveReply_ErrorCode = 4 + ResolveReply_SOCKET_EAI_FAMILY ResolveReply_ErrorCode = 5 + ResolveReply_SOCKET_EAI_MEMORY ResolveReply_ErrorCode = 6 + ResolveReply_SOCKET_EAI_NODATA ResolveReply_ErrorCode = 7 + ResolveReply_SOCKET_EAI_NONAME ResolveReply_ErrorCode = 8 + ResolveReply_SOCKET_EAI_SERVICE ResolveReply_ErrorCode = 9 + ResolveReply_SOCKET_EAI_SOCKTYPE ResolveReply_ErrorCode = 10 + ResolveReply_SOCKET_EAI_SYSTEM ResolveReply_ErrorCode = 11 + ResolveReply_SOCKET_EAI_BADHINTS ResolveReply_ErrorCode = 12 + ResolveReply_SOCKET_EAI_PROTOCOL ResolveReply_ErrorCode = 13 + ResolveReply_SOCKET_EAI_OVERFLOW ResolveReply_ErrorCode = 14 + ResolveReply_SOCKET_EAI_MAX ResolveReply_ErrorCode = 15 +) + +var ResolveReply_ErrorCode_name = map[int32]string{ + 1: "SOCKET_EAI_ADDRFAMILY", + 2: "SOCKET_EAI_AGAIN", + 3: "SOCKET_EAI_BADFLAGS", + 4: "SOCKET_EAI_FAIL", + 5: "SOCKET_EAI_FAMILY", + 6: "SOCKET_EAI_MEMORY", + 7: "SOCKET_EAI_NODATA", + 8: "SOCKET_EAI_NONAME", + 9: "SOCKET_EAI_SERVICE", + 10: "SOCKET_EAI_SOCKTYPE", + 11: "SOCKET_EAI_SYSTEM", + 12: "SOCKET_EAI_BADHINTS", + 13: "SOCKET_EAI_PROTOCOL", + 14: "SOCKET_EAI_OVERFLOW", + 15: "SOCKET_EAI_MAX", +} +var ResolveReply_ErrorCode_value = map[string]int32{ + "SOCKET_EAI_ADDRFAMILY": 1, + "SOCKET_EAI_AGAIN": 2, + "SOCKET_EAI_BADFLAGS": 3, + "SOCKET_EAI_FAIL": 4, + "SOCKET_EAI_FAMILY": 5, + "SOCKET_EAI_MEMORY": 6, + "SOCKET_EAI_NODATA": 7, + "SOCKET_EAI_NONAME": 8, + "SOCKET_EAI_SERVICE": 9, + "SOCKET_EAI_SOCKTYPE": 10, + "SOCKET_EAI_SYSTEM": 11, + "SOCKET_EAI_BADHINTS": 12, + "SOCKET_EAI_PROTOCOL": 13, + "SOCKET_EAI_OVERFLOW": 14, + "SOCKET_EAI_MAX": 15, +} + +func (x ResolveReply_ErrorCode) Enum() *ResolveReply_ErrorCode { + p := new(ResolveReply_ErrorCode) + *p = x + return p +} +func (x ResolveReply_ErrorCode) String() string { + return proto.EnumName(ResolveReply_ErrorCode_name, int32(x)) +} +func (x *ResolveReply_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ResolveReply_ErrorCode_value, data, "ResolveReply_ErrorCode") + if err != nil { + return err + } + *x = ResolveReply_ErrorCode(value) + return nil +} +func (ResolveReply_ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{33, 0} } + +type RemoteSocketServiceError struct { + SystemError *int32 `protobuf:"varint,1,opt,name=system_error,json=systemError,def=0" json:"system_error,omitempty"` + ErrorDetail *string `protobuf:"bytes,2,opt,name=error_detail,json=errorDetail" json:"error_detail,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *RemoteSocketServiceError) Reset() { *m = RemoteSocketServiceError{} } +func (m *RemoteSocketServiceError) String() string { return proto.CompactTextString(m) } +func (*RemoteSocketServiceError) ProtoMessage() {} +func (*RemoteSocketServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +const Default_RemoteSocketServiceError_SystemError int32 = 0 + +func (m *RemoteSocketServiceError) GetSystemError() int32 { + if m != nil && m.SystemError != nil { + return *m.SystemError + } + return Default_RemoteSocketServiceError_SystemError +} + +func (m *RemoteSocketServiceError) GetErrorDetail() string { + if m != nil && m.ErrorDetail != nil { + return *m.ErrorDetail + } + return "" +} + +type AddressPort struct { + Port *int32 `protobuf:"varint,1,req,name=port" json:"port,omitempty"` + PackedAddress []byte `protobuf:"bytes,2,opt,name=packed_address,json=packedAddress" json:"packed_address,omitempty"` + HostnameHint *string `protobuf:"bytes,3,opt,name=hostname_hint,json=hostnameHint" json:"hostname_hint,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AddressPort) Reset() { *m = AddressPort{} } +func (m *AddressPort) String() string { return proto.CompactTextString(m) } +func (*AddressPort) ProtoMessage() {} +func (*AddressPort) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *AddressPort) GetPort() int32 { + if m != nil && m.Port != nil { + return *m.Port + } + return 0 +} + +func (m *AddressPort) GetPackedAddress() []byte { + if m != nil { + return m.PackedAddress + } + return nil +} + +func (m *AddressPort) GetHostnameHint() string { + if m != nil && m.HostnameHint != nil { + return *m.HostnameHint + } + return "" +} + +type CreateSocketRequest struct { + Family *CreateSocketRequest_SocketFamily `protobuf:"varint,1,req,name=family,enum=appengine.CreateSocketRequest_SocketFamily" json:"family,omitempty"` + Protocol *CreateSocketRequest_SocketProtocol `protobuf:"varint,2,req,name=protocol,enum=appengine.CreateSocketRequest_SocketProtocol" json:"protocol,omitempty"` + SocketOptions []*SocketOption `protobuf:"bytes,3,rep,name=socket_options,json=socketOptions" json:"socket_options,omitempty"` + ProxyExternalIp *AddressPort `protobuf:"bytes,4,opt,name=proxy_external_ip,json=proxyExternalIp" json:"proxy_external_ip,omitempty"` + ListenBacklog *int32 `protobuf:"varint,5,opt,name=listen_backlog,json=listenBacklog,def=0" json:"listen_backlog,omitempty"` + RemoteIp *AddressPort `protobuf:"bytes,6,opt,name=remote_ip,json=remoteIp" json:"remote_ip,omitempty"` + AppId *string `protobuf:"bytes,9,opt,name=app_id,json=appId" json:"app_id,omitempty"` + ProjectId *int64 `protobuf:"varint,10,opt,name=project_id,json=projectId" json:"project_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateSocketRequest) Reset() { *m = CreateSocketRequest{} } +func (m *CreateSocketRequest) String() string { return proto.CompactTextString(m) } +func (*CreateSocketRequest) ProtoMessage() {} +func (*CreateSocketRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +const Default_CreateSocketRequest_ListenBacklog int32 = 0 + +func (m *CreateSocketRequest) GetFamily() CreateSocketRequest_SocketFamily { + if m != nil && m.Family != nil { + return *m.Family + } + return CreateSocketRequest_IPv4 +} + +func (m *CreateSocketRequest) GetProtocol() CreateSocketRequest_SocketProtocol { + if m != nil && m.Protocol != nil { + return *m.Protocol + } + return CreateSocketRequest_TCP +} + +func (m *CreateSocketRequest) GetSocketOptions() []*SocketOption { + if m != nil { + return m.SocketOptions + } + return nil +} + +func (m *CreateSocketRequest) GetProxyExternalIp() *AddressPort { + if m != nil { + return m.ProxyExternalIp + } + return nil +} + +func (m *CreateSocketRequest) GetListenBacklog() int32 { + if m != nil && m.ListenBacklog != nil { + return *m.ListenBacklog + } + return Default_CreateSocketRequest_ListenBacklog +} + +func (m *CreateSocketRequest) GetRemoteIp() *AddressPort { + if m != nil { + return m.RemoteIp + } + return nil +} + +func (m *CreateSocketRequest) GetAppId() string { + if m != nil && m.AppId != nil { + return *m.AppId + } + return "" +} + +func (m *CreateSocketRequest) GetProjectId() int64 { + if m != nil && m.ProjectId != nil { + return *m.ProjectId + } + return 0 +} + +type CreateSocketReply struct { + SocketDescriptor *string `protobuf:"bytes,1,opt,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + ServerAddress *AddressPort `protobuf:"bytes,3,opt,name=server_address,json=serverAddress" json:"server_address,omitempty"` + ProxyExternalIp *AddressPort `protobuf:"bytes,4,opt,name=proxy_external_ip,json=proxyExternalIp" json:"proxy_external_ip,omitempty"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateSocketReply) Reset() { *m = CreateSocketReply{} } +func (m *CreateSocketReply) String() string { return proto.CompactTextString(m) } +func (*CreateSocketReply) ProtoMessage() {} +func (*CreateSocketReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +var extRange_CreateSocketReply = []proto.ExtensionRange{ + {1000, 536870911}, +} + +func (*CreateSocketReply) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_CreateSocketReply +} + +func (m *CreateSocketReply) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *CreateSocketReply) GetServerAddress() *AddressPort { + if m != nil { + return m.ServerAddress + } + return nil +} + +func (m *CreateSocketReply) GetProxyExternalIp() *AddressPort { + if m != nil { + return m.ProxyExternalIp + } + return nil +} + +type BindRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + ProxyExternalIp *AddressPort `protobuf:"bytes,2,req,name=proxy_external_ip,json=proxyExternalIp" json:"proxy_external_ip,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BindRequest) Reset() { *m = BindRequest{} } +func (m *BindRequest) String() string { return proto.CompactTextString(m) } +func (*BindRequest) ProtoMessage() {} +func (*BindRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *BindRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *BindRequest) GetProxyExternalIp() *AddressPort { + if m != nil { + return m.ProxyExternalIp + } + return nil +} + +type BindReply struct { + ProxyExternalIp *AddressPort `protobuf:"bytes,1,opt,name=proxy_external_ip,json=proxyExternalIp" json:"proxy_external_ip,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BindReply) Reset() { *m = BindReply{} } +func (m *BindReply) String() string { return proto.CompactTextString(m) } +func (*BindReply) ProtoMessage() {} +func (*BindReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *BindReply) GetProxyExternalIp() *AddressPort { + if m != nil { + return m.ProxyExternalIp + } + return nil +} + +type GetSocketNameRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetSocketNameRequest) Reset() { *m = GetSocketNameRequest{} } +func (m *GetSocketNameRequest) String() string { return proto.CompactTextString(m) } +func (*GetSocketNameRequest) ProtoMessage() {} +func (*GetSocketNameRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *GetSocketNameRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +type GetSocketNameReply struct { + ProxyExternalIp *AddressPort `protobuf:"bytes,2,opt,name=proxy_external_ip,json=proxyExternalIp" json:"proxy_external_ip,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetSocketNameReply) Reset() { *m = GetSocketNameReply{} } +func (m *GetSocketNameReply) String() string { return proto.CompactTextString(m) } +func (*GetSocketNameReply) ProtoMessage() {} +func (*GetSocketNameReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *GetSocketNameReply) GetProxyExternalIp() *AddressPort { + if m != nil { + return m.ProxyExternalIp + } + return nil +} + +type GetPeerNameRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetPeerNameRequest) Reset() { *m = GetPeerNameRequest{} } +func (m *GetPeerNameRequest) String() string { return proto.CompactTextString(m) } +func (*GetPeerNameRequest) ProtoMessage() {} +func (*GetPeerNameRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *GetPeerNameRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +type GetPeerNameReply struct { + PeerIp *AddressPort `protobuf:"bytes,2,opt,name=peer_ip,json=peerIp" json:"peer_ip,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetPeerNameReply) Reset() { *m = GetPeerNameReply{} } +func (m *GetPeerNameReply) String() string { return proto.CompactTextString(m) } +func (*GetPeerNameReply) ProtoMessage() {} +func (*GetPeerNameReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *GetPeerNameReply) GetPeerIp() *AddressPort { + if m != nil { + return m.PeerIp + } + return nil +} + +type SocketOption struct { + Level *SocketOption_SocketOptionLevel `protobuf:"varint,1,req,name=level,enum=appengine.SocketOption_SocketOptionLevel" json:"level,omitempty"` + Option *SocketOption_SocketOptionName `protobuf:"varint,2,req,name=option,enum=appengine.SocketOption_SocketOptionName" json:"option,omitempty"` + Value []byte `protobuf:"bytes,3,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SocketOption) Reset() { *m = SocketOption{} } +func (m *SocketOption) String() string { return proto.CompactTextString(m) } +func (*SocketOption) ProtoMessage() {} +func (*SocketOption) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *SocketOption) GetLevel() SocketOption_SocketOptionLevel { + if m != nil && m.Level != nil { + return *m.Level + } + return SocketOption_SOCKET_SOL_IP +} + +func (m *SocketOption) GetOption() SocketOption_SocketOptionName { + if m != nil && m.Option != nil { + return *m.Option + } + return SocketOption_SOCKET_SO_DEBUG +} + +func (m *SocketOption) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type SetSocketOptionsRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + Options []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SetSocketOptionsRequest) Reset() { *m = SetSocketOptionsRequest{} } +func (m *SetSocketOptionsRequest) String() string { return proto.CompactTextString(m) } +func (*SetSocketOptionsRequest) ProtoMessage() {} +func (*SetSocketOptionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *SetSocketOptionsRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *SetSocketOptionsRequest) GetOptions() []*SocketOption { + if m != nil { + return m.Options + } + return nil +} + +type SetSocketOptionsReply struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *SetSocketOptionsReply) Reset() { *m = SetSocketOptionsReply{} } +func (m *SetSocketOptionsReply) String() string { return proto.CompactTextString(m) } +func (*SetSocketOptionsReply) ProtoMessage() {} +func (*SetSocketOptionsReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +type GetSocketOptionsRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + Options []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetSocketOptionsRequest) Reset() { *m = GetSocketOptionsRequest{} } +func (m *GetSocketOptionsRequest) String() string { return proto.CompactTextString(m) } +func (*GetSocketOptionsRequest) ProtoMessage() {} +func (*GetSocketOptionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *GetSocketOptionsRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *GetSocketOptionsRequest) GetOptions() []*SocketOption { + if m != nil { + return m.Options + } + return nil +} + +type GetSocketOptionsReply struct { + Options []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetSocketOptionsReply) Reset() { *m = GetSocketOptionsReply{} } +func (m *GetSocketOptionsReply) String() string { return proto.CompactTextString(m) } +func (*GetSocketOptionsReply) ProtoMessage() {} +func (*GetSocketOptionsReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *GetSocketOptionsReply) GetOptions() []*SocketOption { + if m != nil { + return m.Options + } + return nil +} + +type ConnectRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + RemoteIp *AddressPort `protobuf:"bytes,2,req,name=remote_ip,json=remoteIp" json:"remote_ip,omitempty"` + TimeoutSeconds *float64 `protobuf:"fixed64,3,opt,name=timeout_seconds,json=timeoutSeconds,def=-1" json:"timeout_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ConnectRequest) Reset() { *m = ConnectRequest{} } +func (m *ConnectRequest) String() string { return proto.CompactTextString(m) } +func (*ConnectRequest) ProtoMessage() {} +func (*ConnectRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +const Default_ConnectRequest_TimeoutSeconds float64 = -1 + +func (m *ConnectRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *ConnectRequest) GetRemoteIp() *AddressPort { + if m != nil { + return m.RemoteIp + } + return nil +} + +func (m *ConnectRequest) GetTimeoutSeconds() float64 { + if m != nil && m.TimeoutSeconds != nil { + return *m.TimeoutSeconds + } + return Default_ConnectRequest_TimeoutSeconds +} + +type ConnectReply struct { + ProxyExternalIp *AddressPort `protobuf:"bytes,1,opt,name=proxy_external_ip,json=proxyExternalIp" json:"proxy_external_ip,omitempty"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ConnectReply) Reset() { *m = ConnectReply{} } +func (m *ConnectReply) String() string { return proto.CompactTextString(m) } +func (*ConnectReply) ProtoMessage() {} +func (*ConnectReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +var extRange_ConnectReply = []proto.ExtensionRange{ + {1000, 536870911}, +} + +func (*ConnectReply) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ConnectReply +} + +func (m *ConnectReply) GetProxyExternalIp() *AddressPort { + if m != nil { + return m.ProxyExternalIp + } + return nil +} + +type ListenRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + Backlog *int32 `protobuf:"varint,2,req,name=backlog" json:"backlog,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListenRequest) Reset() { *m = ListenRequest{} } +func (m *ListenRequest) String() string { return proto.CompactTextString(m) } +func (*ListenRequest) ProtoMessage() {} +func (*ListenRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *ListenRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *ListenRequest) GetBacklog() int32 { + if m != nil && m.Backlog != nil { + return *m.Backlog + } + return 0 +} + +type ListenReply struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *ListenReply) Reset() { *m = ListenReply{} } +func (m *ListenReply) String() string { return proto.CompactTextString(m) } +func (*ListenReply) ProtoMessage() {} +func (*ListenReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +type AcceptRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + TimeoutSeconds *float64 `protobuf:"fixed64,2,opt,name=timeout_seconds,json=timeoutSeconds,def=-1" json:"timeout_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AcceptRequest) Reset() { *m = AcceptRequest{} } +func (m *AcceptRequest) String() string { return proto.CompactTextString(m) } +func (*AcceptRequest) ProtoMessage() {} +func (*AcceptRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +const Default_AcceptRequest_TimeoutSeconds float64 = -1 + +func (m *AcceptRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *AcceptRequest) GetTimeoutSeconds() float64 { + if m != nil && m.TimeoutSeconds != nil { + return *m.TimeoutSeconds + } + return Default_AcceptRequest_TimeoutSeconds +} + +type AcceptReply struct { + NewSocketDescriptor []byte `protobuf:"bytes,2,opt,name=new_socket_descriptor,json=newSocketDescriptor" json:"new_socket_descriptor,omitempty"` + RemoteAddress *AddressPort `protobuf:"bytes,3,opt,name=remote_address,json=remoteAddress" json:"remote_address,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *AcceptReply) Reset() { *m = AcceptReply{} } +func (m *AcceptReply) String() string { return proto.CompactTextString(m) } +func (*AcceptReply) ProtoMessage() {} +func (*AcceptReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } + +func (m *AcceptReply) GetNewSocketDescriptor() []byte { + if m != nil { + return m.NewSocketDescriptor + } + return nil +} + +func (m *AcceptReply) GetRemoteAddress() *AddressPort { + if m != nil { + return m.RemoteAddress + } + return nil +} + +type ShutDownRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + How *ShutDownRequest_How `protobuf:"varint,2,req,name=how,enum=appengine.ShutDownRequest_How" json:"how,omitempty"` + SendOffset *int64 `protobuf:"varint,3,req,name=send_offset,json=sendOffset" json:"send_offset,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ShutDownRequest) Reset() { *m = ShutDownRequest{} } +func (m *ShutDownRequest) String() string { return proto.CompactTextString(m) } +func (*ShutDownRequest) ProtoMessage() {} +func (*ShutDownRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +func (m *ShutDownRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *ShutDownRequest) GetHow() ShutDownRequest_How { + if m != nil && m.How != nil { + return *m.How + } + return ShutDownRequest_SOCKET_SHUT_RD +} + +func (m *ShutDownRequest) GetSendOffset() int64 { + if m != nil && m.SendOffset != nil { + return *m.SendOffset + } + return 0 +} + +type ShutDownReply struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *ShutDownReply) Reset() { *m = ShutDownReply{} } +func (m *ShutDownReply) String() string { return proto.CompactTextString(m) } +func (*ShutDownReply) ProtoMessage() {} +func (*ShutDownReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } + +type CloseRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + SendOffset *int64 `protobuf:"varint,2,opt,name=send_offset,json=sendOffset,def=-1" json:"send_offset,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CloseRequest) Reset() { *m = CloseRequest{} } +func (m *CloseRequest) String() string { return proto.CompactTextString(m) } +func (*CloseRequest) ProtoMessage() {} +func (*CloseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +const Default_CloseRequest_SendOffset int64 = -1 + +func (m *CloseRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *CloseRequest) GetSendOffset() int64 { + if m != nil && m.SendOffset != nil { + return *m.SendOffset + } + return Default_CloseRequest_SendOffset +} + +type CloseReply struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *CloseReply) Reset() { *m = CloseReply{} } +func (m *CloseReply) String() string { return proto.CompactTextString(m) } +func (*CloseReply) ProtoMessage() {} +func (*CloseReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +type SendRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + Data []byte `protobuf:"bytes,2,req,name=data" json:"data,omitempty"` + StreamOffset *int64 `protobuf:"varint,3,req,name=stream_offset,json=streamOffset" json:"stream_offset,omitempty"` + Flags *int32 `protobuf:"varint,4,opt,name=flags,def=0" json:"flags,omitempty"` + SendTo *AddressPort `protobuf:"bytes,5,opt,name=send_to,json=sendTo" json:"send_to,omitempty"` + TimeoutSeconds *float64 `protobuf:"fixed64,6,opt,name=timeout_seconds,json=timeoutSeconds,def=-1" json:"timeout_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SendRequest) Reset() { *m = SendRequest{} } +func (m *SendRequest) String() string { return proto.CompactTextString(m) } +func (*SendRequest) ProtoMessage() {} +func (*SendRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } + +const Default_SendRequest_Flags int32 = 0 +const Default_SendRequest_TimeoutSeconds float64 = -1 + +func (m *SendRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *SendRequest) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *SendRequest) GetStreamOffset() int64 { + if m != nil && m.StreamOffset != nil { + return *m.StreamOffset + } + return 0 +} + +func (m *SendRequest) GetFlags() int32 { + if m != nil && m.Flags != nil { + return *m.Flags + } + return Default_SendRequest_Flags +} + +func (m *SendRequest) GetSendTo() *AddressPort { + if m != nil { + return m.SendTo + } + return nil +} + +func (m *SendRequest) GetTimeoutSeconds() float64 { + if m != nil && m.TimeoutSeconds != nil { + return *m.TimeoutSeconds + } + return Default_SendRequest_TimeoutSeconds +} + +type SendReply struct { + DataSent *int32 `protobuf:"varint,1,opt,name=data_sent,json=dataSent" json:"data_sent,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SendReply) Reset() { *m = SendReply{} } +func (m *SendReply) String() string { return proto.CompactTextString(m) } +func (*SendReply) ProtoMessage() {} +func (*SendReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } + +func (m *SendReply) GetDataSent() int32 { + if m != nil && m.DataSent != nil { + return *m.DataSent + } + return 0 +} + +type ReceiveRequest struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + DataSize *int32 `protobuf:"varint,2,req,name=data_size,json=dataSize" json:"data_size,omitempty"` + Flags *int32 `protobuf:"varint,3,opt,name=flags,def=0" json:"flags,omitempty"` + TimeoutSeconds *float64 `protobuf:"fixed64,5,opt,name=timeout_seconds,json=timeoutSeconds,def=-1" json:"timeout_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ReceiveRequest) Reset() { *m = ReceiveRequest{} } +func (m *ReceiveRequest) String() string { return proto.CompactTextString(m) } +func (*ReceiveRequest) ProtoMessage() {} +func (*ReceiveRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } + +const Default_ReceiveRequest_Flags int32 = 0 +const Default_ReceiveRequest_TimeoutSeconds float64 = -1 + +func (m *ReceiveRequest) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *ReceiveRequest) GetDataSize() int32 { + if m != nil && m.DataSize != nil { + return *m.DataSize + } + return 0 +} + +func (m *ReceiveRequest) GetFlags() int32 { + if m != nil && m.Flags != nil { + return *m.Flags + } + return Default_ReceiveRequest_Flags +} + +func (m *ReceiveRequest) GetTimeoutSeconds() float64 { + if m != nil && m.TimeoutSeconds != nil { + return *m.TimeoutSeconds + } + return Default_ReceiveRequest_TimeoutSeconds +} + +type ReceiveReply struct { + StreamOffset *int64 `protobuf:"varint,2,opt,name=stream_offset,json=streamOffset" json:"stream_offset,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"` + ReceivedFrom *AddressPort `protobuf:"bytes,4,opt,name=received_from,json=receivedFrom" json:"received_from,omitempty"` + BufferSize *int32 `protobuf:"varint,5,opt,name=buffer_size,json=bufferSize" json:"buffer_size,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ReceiveReply) Reset() { *m = ReceiveReply{} } +func (m *ReceiveReply) String() string { return proto.CompactTextString(m) } +func (*ReceiveReply) ProtoMessage() {} +func (*ReceiveReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +func (m *ReceiveReply) GetStreamOffset() int64 { + if m != nil && m.StreamOffset != nil { + return *m.StreamOffset + } + return 0 +} + +func (m *ReceiveReply) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *ReceiveReply) GetReceivedFrom() *AddressPort { + if m != nil { + return m.ReceivedFrom + } + return nil +} + +func (m *ReceiveReply) GetBufferSize() int32 { + if m != nil && m.BufferSize != nil { + return *m.BufferSize + } + return 0 +} + +type PollEvent struct { + SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor,json=socketDescriptor" json:"socket_descriptor,omitempty"` + RequestedEvents *int32 `protobuf:"varint,2,req,name=requested_events,json=requestedEvents" json:"requested_events,omitempty"` + ObservedEvents *int32 `protobuf:"varint,3,req,name=observed_events,json=observedEvents" json:"observed_events,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PollEvent) Reset() { *m = PollEvent{} } +func (m *PollEvent) String() string { return proto.CompactTextString(m) } +func (*PollEvent) ProtoMessage() {} +func (*PollEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } + +func (m *PollEvent) GetSocketDescriptor() string { + if m != nil && m.SocketDescriptor != nil { + return *m.SocketDescriptor + } + return "" +} + +func (m *PollEvent) GetRequestedEvents() int32 { + if m != nil && m.RequestedEvents != nil { + return *m.RequestedEvents + } + return 0 +} + +func (m *PollEvent) GetObservedEvents() int32 { + if m != nil && m.ObservedEvents != nil { + return *m.ObservedEvents + } + return 0 +} + +type PollRequest struct { + Events []*PollEvent `protobuf:"bytes,1,rep,name=events" json:"events,omitempty"` + TimeoutSeconds *float64 `protobuf:"fixed64,2,opt,name=timeout_seconds,json=timeoutSeconds,def=-1" json:"timeout_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PollRequest) Reset() { *m = PollRequest{} } +func (m *PollRequest) String() string { return proto.CompactTextString(m) } +func (*PollRequest) ProtoMessage() {} +func (*PollRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } + +const Default_PollRequest_TimeoutSeconds float64 = -1 + +func (m *PollRequest) GetEvents() []*PollEvent { + if m != nil { + return m.Events + } + return nil +} + +func (m *PollRequest) GetTimeoutSeconds() float64 { + if m != nil && m.TimeoutSeconds != nil { + return *m.TimeoutSeconds + } + return Default_PollRequest_TimeoutSeconds +} + +type PollReply struct { + Events []*PollEvent `protobuf:"bytes,2,rep,name=events" json:"events,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PollReply) Reset() { *m = PollReply{} } +func (m *PollReply) String() string { return proto.CompactTextString(m) } +func (*PollReply) ProtoMessage() {} +func (*PollReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +func (m *PollReply) GetEvents() []*PollEvent { + if m != nil { + return m.Events + } + return nil +} + +type ResolveRequest struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + AddressFamilies []CreateSocketRequest_SocketFamily `protobuf:"varint,2,rep,name=address_families,json=addressFamilies,enum=appengine.CreateSocketRequest_SocketFamily" json:"address_families,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ResolveRequest) Reset() { *m = ResolveRequest{} } +func (m *ResolveRequest) String() string { return proto.CompactTextString(m) } +func (*ResolveRequest) ProtoMessage() {} +func (*ResolveRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +func (m *ResolveRequest) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *ResolveRequest) GetAddressFamilies() []CreateSocketRequest_SocketFamily { + if m != nil { + return m.AddressFamilies + } + return nil +} + +type ResolveReply struct { + PackedAddress [][]byte `protobuf:"bytes,2,rep,name=packed_address,json=packedAddress" json:"packed_address,omitempty"` + CanonicalName *string `protobuf:"bytes,3,opt,name=canonical_name,json=canonicalName" json:"canonical_name,omitempty"` + Aliases []string `protobuf:"bytes,4,rep,name=aliases" json:"aliases,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ResolveReply) Reset() { *m = ResolveReply{} } +func (m *ResolveReply) String() string { return proto.CompactTextString(m) } +func (*ResolveReply) ProtoMessage() {} +func (*ResolveReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } + +func (m *ResolveReply) GetPackedAddress() [][]byte { + if m != nil { + return m.PackedAddress + } + return nil +} + +func (m *ResolveReply) GetCanonicalName() string { + if m != nil && m.CanonicalName != nil { + return *m.CanonicalName + } + return "" +} + +func (m *ResolveReply) GetAliases() []string { + if m != nil { + return m.Aliases + } + return nil +} + +func init() { + proto.RegisterType((*RemoteSocketServiceError)(nil), "appengine.RemoteSocketServiceError") + proto.RegisterType((*AddressPort)(nil), "appengine.AddressPort") + proto.RegisterType((*CreateSocketRequest)(nil), "appengine.CreateSocketRequest") + proto.RegisterType((*CreateSocketReply)(nil), "appengine.CreateSocketReply") + proto.RegisterType((*BindRequest)(nil), "appengine.BindRequest") + proto.RegisterType((*BindReply)(nil), "appengine.BindReply") + proto.RegisterType((*GetSocketNameRequest)(nil), "appengine.GetSocketNameRequest") + proto.RegisterType((*GetSocketNameReply)(nil), "appengine.GetSocketNameReply") + proto.RegisterType((*GetPeerNameRequest)(nil), "appengine.GetPeerNameRequest") + proto.RegisterType((*GetPeerNameReply)(nil), "appengine.GetPeerNameReply") + proto.RegisterType((*SocketOption)(nil), "appengine.SocketOption") + proto.RegisterType((*SetSocketOptionsRequest)(nil), "appengine.SetSocketOptionsRequest") + proto.RegisterType((*SetSocketOptionsReply)(nil), "appengine.SetSocketOptionsReply") + proto.RegisterType((*GetSocketOptionsRequest)(nil), "appengine.GetSocketOptionsRequest") + proto.RegisterType((*GetSocketOptionsReply)(nil), "appengine.GetSocketOptionsReply") + proto.RegisterType((*ConnectRequest)(nil), "appengine.ConnectRequest") + proto.RegisterType((*ConnectReply)(nil), "appengine.ConnectReply") + proto.RegisterType((*ListenRequest)(nil), "appengine.ListenRequest") + proto.RegisterType((*ListenReply)(nil), "appengine.ListenReply") + proto.RegisterType((*AcceptRequest)(nil), "appengine.AcceptRequest") + proto.RegisterType((*AcceptReply)(nil), "appengine.AcceptReply") + proto.RegisterType((*ShutDownRequest)(nil), "appengine.ShutDownRequest") + proto.RegisterType((*ShutDownReply)(nil), "appengine.ShutDownReply") + proto.RegisterType((*CloseRequest)(nil), "appengine.CloseRequest") + proto.RegisterType((*CloseReply)(nil), "appengine.CloseReply") + proto.RegisterType((*SendRequest)(nil), "appengine.SendRequest") + proto.RegisterType((*SendReply)(nil), "appengine.SendReply") + proto.RegisterType((*ReceiveRequest)(nil), "appengine.ReceiveRequest") + proto.RegisterType((*ReceiveReply)(nil), "appengine.ReceiveReply") + proto.RegisterType((*PollEvent)(nil), "appengine.PollEvent") + proto.RegisterType((*PollRequest)(nil), "appengine.PollRequest") + proto.RegisterType((*PollReply)(nil), "appengine.PollReply") + proto.RegisterType((*ResolveRequest)(nil), "appengine.ResolveRequest") + proto.RegisterType((*ResolveReply)(nil), "appengine.ResolveReply") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/socket/socket_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 3088 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x5f, 0x77, 0xe3, 0xc6, + 0x75, 0x37, 0x48, 0xfd, 0xe3, 0x90, 0x94, 0xee, 0x62, 0xa5, 0x5d, 0x25, 0x6e, 0x12, 0x05, 0x8e, + 0x1b, 0x25, 0x8e, 0x77, 0x6d, 0x39, 0x4d, 0x9b, 0xa4, 0x49, 0x16, 0x04, 0x86, 0x24, 0x4c, 0x00, + 0x03, 0xcd, 0x0c, 0x25, 0xd1, 0x6d, 0x8a, 0xd0, 0x22, 0xa4, 0x65, 0x4c, 0x11, 0x0c, 0xc9, 0xdd, + 0xf5, 0xba, 0x69, 0xaa, 0xfe, 0x39, 0xfd, 0x12, 0x7d, 0xe8, 0x73, 0x3f, 0x43, 0x4f, 0x4f, 0x5f, + 0xfa, 0xec, 0xc7, 0x7e, 0x84, 0x9e, 0xbe, 0xb4, 0x9f, 0xa1, 0x67, 0x06, 0xe0, 0x60, 0xc8, 0xd5, + 0xae, 0x77, 0x75, 0x72, 0x4e, 0x9e, 0xa4, 0xfb, 0xbb, 0x77, 0xee, 0xff, 0x99, 0xb9, 0x03, 0xa2, + 0x47, 0x97, 0x69, 0x7a, 0x39, 0x4a, 0x1e, 0x5c, 0xa6, 0xa3, 0xfe, 0xf8, 0xf2, 0x41, 0x3a, 0xbd, + 0x7c, 0xd8, 0x9f, 0x4c, 0x92, 0xf1, 0xe5, 0x70, 0x9c, 0x3c, 0x1c, 0x8e, 0xe7, 0xc9, 0x74, 0xdc, + 0x1f, 0x3d, 0x9c, 0xa5, 0xe7, 0x9f, 0x25, 0xf3, 0xfc, 0x4f, 0x3c, 0x4b, 0xa6, 0x4f, 0x87, 0xe7, + 0xc9, 0x83, 0xc9, 0x34, 0x9d, 0xa7, 0x66, 0x45, 0xc9, 0x5b, 0xff, 0xbc, 0x8b, 0xf6, 0x69, 0x72, + 0x95, 0xce, 0x13, 0x26, 0x25, 0x59, 0x26, 0x88, 0xa7, 0xd3, 0x74, 0x6a, 0x7e, 0x07, 0xd5, 0x66, + 0xcf, 0x67, 0xf3, 0xe4, 0x2a, 0x4e, 0x04, 0xbd, 0x6f, 0x1c, 0x18, 0x87, 0xeb, 0x3f, 0x31, 0x3e, + 0xa0, 0xd5, 0x0c, 0xce, 0xa4, 0xbe, 0x8d, 0x6a, 0x92, 0x1d, 0x0f, 0x92, 0x79, 0x7f, 0x38, 0xda, + 0x2f, 0x1d, 0x18, 0x87, 0x15, 0x5a, 0x95, 0x98, 0x2b, 0x21, 0xeb, 0x73, 0x54, 0x91, 0xb2, 0x4e, + 0x3a, 0x48, 0x4c, 0x40, 0x35, 0xd6, 0x63, 0x1c, 0x07, 0x31, 0xa6, 0x94, 0x50, 0x30, 0xcc, 0x3a, + 0xaa, 0xb4, 0x6c, 0x2f, 0x27, 0x4b, 0x66, 0x15, 0x6d, 0x36, 0x6d, 0xcf, 0xef, 0x52, 0x0c, 0x6b, + 0xe6, 0x1e, 0xba, 0x13, 0x61, 0x1a, 0x78, 0x8c, 0x79, 0x24, 0x8c, 0x5d, 0x1c, 0x7a, 0xd8, 0x85, + 0x75, 0xf3, 0x2e, 0xda, 0xf1, 0xc2, 0x13, 0xdb, 0xf7, 0xdc, 0x98, 0xe2, 0xe3, 0x2e, 0x66, 0x1c, + 0x36, 0xcc, 0x3b, 0xa8, 0xce, 0x88, 0xd3, 0xc1, 0x3c, 0x76, 0x7c, 0xc2, 0xb0, 0x0b, 0x9b, 0xd6, + 0xbf, 0x99, 0xa8, 0xca, 0x34, 0x67, 0x77, 0x50, 0x95, 0xf5, 0x58, 0xcc, 0xba, 0x8e, 0x83, 0x19, + 0x83, 0xb7, 0x84, 0x6d, 0x01, 0x60, 0x61, 0x04, 0x0c, 0x73, 0x1b, 0x21, 0x49, 0x86, 0x04, 0x87, + 0x1c, 0x4a, 0x8a, 0xcd, 0xa8, 0xd3, 0x86, 0xb2, 0x22, 0xbd, 0x90, 0x53, 0x58, 0x13, 0x9e, 0x66, + 0x24, 0x81, 0x75, 0xc5, 0x0b, 0xcf, 0x3c, 0x02, 0x1b, 0x8a, 0x3c, 0x6a, 0x78, 0x2d, 0xd8, 0x5c, + 0x18, 0x16, 0x8a, 0xcf, 0xb0, 0x03, 0x5b, 0x8a, 0xdf, 0xb0, 0xdd, 0x26, 0x54, 0x94, 0x61, 0xa7, + 0xed, 0xf9, 0x2e, 0x20, 0x45, 0xdb, 0x2d, 0xdb, 0x0b, 0xa1, 0x2a, 0x02, 0x96, 0xf4, 0x29, 0xe9, + 0xfa, 0x6e, 0xc3, 0x27, 0x4e, 0x07, 0xaa, 0x9a, 0xb7, 0x01, 0x0e, 0xa0, 0x56, 0x2c, 0x12, 0xd1, + 0x41, 0x5d, 0xd1, 0x4d, 0xbb, 0xeb, 0x73, 0xd8, 0xd6, 0x9c, 0xe0, 0x0d, 0xbf, 0x03, 0x3b, 0x85, + 0x13, 0x5d, 0xd6, 0x03, 0x50, 0xf2, 0xf8, 0xcc, 0x63, 0x1c, 0xee, 0x28, 0xf6, 0x99, 0x8b, 0x4f, + 0xc0, 0xd4, 0xcc, 0x09, 0xfa, 0xae, 0xae, 0xce, 0xf5, 0x28, 0xec, 0x2a, 0x01, 0x8f, 0x09, 0x7a, + 0xaf, 0xa0, 0x45, 0xa9, 0xe0, 0x5e, 0xa1, 0xa0, 0xe9, 0xf9, 0x18, 0xee, 0x2b, 0x3a, 0x90, 0xf4, + 0xbe, 0x66, 0x80, 0xf3, 0x1e, 0x7c, 0x4d, 0x19, 0xe0, 0x67, 0xbc, 0xc1, 0x7a, 0xf0, 0x75, 0xe5, + 0x50, 0x53, 0x24, 0xf5, 0x6d, 0x4d, 0x9e, 0x45, 0x0e, 0xfc, 0x91, 0xa2, 0x59, 0xe4, 0x45, 0x18, + 0xbe, 0xa1, 0xc4, 0x29, 0x69, 0x32, 0xf8, 0x66, 0x61, 0xce, 0xf7, 0xc2, 0x0e, 0x7c, 0xab, 0xa8, + 0xbd, 0x90, 0x3e, 0x30, 0x6b, 0x68, 0x4b, 0x92, 0x2e, 0x09, 0xe0, 0xdb, 0x4a, 0x98, 0xda, 0x61, + 0x0b, 0x83, 0xa5, 0x7c, 0x71, 0xb1, 0xed, 0xfa, 0x1d, 0x78, 0x47, 0x76, 0x9b, 0x02, 0x44, 0x3d, + 0xde, 0x31, 0x77, 0x11, 0x64, 0xfe, 0xd8, 0x01, 0xe6, 0x84, 0xf8, 0x24, 0x6c, 0xc1, 0x77, 0x34, + 0x2f, 0x7d, 0xa7, 0x03, 0xef, 0xea, 0x5e, 0xf7, 0x18, 0xfc, 0xb1, 0x52, 0x14, 0x12, 0x8e, 0x83, + 0x88, 0xf7, 0xe0, 0xbb, 0xca, 0x33, 0x9f, 0x90, 0x08, 0x0e, 0xf5, 0x3a, 0xb3, 0x16, 0x7c, 0xbf, + 0x68, 0x43, 0x97, 0x06, 0xf0, 0x9e, 0xd6, 0x3b, 0x34, 0x6c, 0xc1, 0x0f, 0xf2, 0x1d, 0x16, 0x63, + 0xff, 0x28, 0x64, 0xbd, 0xd0, 0x81, 0xf7, 0x95, 0x84, 0xff, 0x51, 0xdb, 0xe7, 0xf0, 0x40, 0xa3, + 0x29, 0xe3, 0xf0, 0xb0, 0xa0, 0x43, 0xa1, 0xe1, 0x03, 0x15, 0x6c, 0x37, 0xb4, 0xb9, 0xd3, 0x86, + 0x0f, 0x35, 0x0f, 0x1c, 0xe6, 0xc1, 0x51, 0xb1, 0xe0, 0x48, 0x28, 0xfc, 0x48, 0xef, 0x66, 0x0c, + 0x3f, 0xd4, 0x49, 0x0a, 0x7f, 0xa2, 0xa4, 0xcf, 0x9a, 0x5d, 0xdf, 0x87, 0x1f, 0x69, 0xda, 0xec, + 0x90, 0xc0, 0x9f, 0x2a, 0x73, 0x42, 0xfc, 0xd8, 0x81, 0x3f, 0xd3, 0x01, 0xe6, 0x73, 0xf8, 0xb1, + 0x5a, 0xd1, 0x68, 0x92, 0x90, 0xc3, 0x4f, 0xf5, 0x1c, 0x72, 0x0a, 0x7f, 0xae, 0xb5, 0xa2, 0x6b, + 0x73, 0x1b, 0x7e, 0xa6, 0x3c, 0xe0, 0x5e, 0x80, 0xe1, 0xe7, 0xc5, 0xe6, 0x24, 0x8c, 0xc2, 0x2f, + 0xb4, 0xe5, 0x21, 0xe6, 0xf0, 0x48, 0xa3, 0xa3, 0x4e, 0x0b, 0x6c, 0xa5, 0x8e, 0xe2, 0x80, 0x70, + 0x0c, 0x0d, 0x4d, 0xbf, 0xec, 0x1d, 0x47, 0x35, 0x8b, 0xed, 0x9e, 0x80, 0x5b, 0x34, 0x1e, 0x0d, + 0x42, 0x0e, 0x58, 0x99, 0x73, 0x48, 0x10, 0x40, 0x53, 0xb1, 0x23, 0x4a, 0x38, 0x81, 0x96, 0xaa, + 0x78, 0xd0, 0xf5, 0xb9, 0xd7, 0x26, 0x11, 0xb4, 0x8b, 0xf6, 0x22, 0xdc, 0x25, 0x1c, 0x3c, 0x3d, + 0x05, 0xa2, 0xe8, 0x1f, 0xab, 0x45, 0xe4, 0x04, 0xd3, 0xa6, 0x4f, 0x4e, 0xa1, 0xa3, 0x0a, 0x1d, + 0x12, 0xde, 0x0d, 0xbd, 0x63, 0xf0, 0x8b, 0x3c, 0xd9, 0x6e, 0xd3, 0x85, 0x40, 0x0f, 0xc4, 0x69, + 0xb7, 0x20, 0x54, 0x80, 0xef, 0x35, 0x6c, 0xc7, 0x01, 0xa2, 0x03, 0x0d, 0xdb, 0x85, 0x48, 0x07, + 0x98, 0x13, 0xc2, 0xb1, 0x0e, 0x04, 0xf6, 0x19, 0xd0, 0xa2, 0xbf, 0xbc, 0x86, 0x3c, 0xcc, 0x58, + 0xb1, 0xd1, 0x7d, 0x86, 0x8f, 0x81, 0x2b, 0x09, 0x8a, 0x19, 0xb7, 0x29, 0x87, 0xae, 0x42, 0x18, + 0xa7, 0x72, 0xbb, 0x9d, 0xa8, 0x35, 0x5d, 0x86, 0x29, 0x83, 0x53, 0x3d, 0x18, 0x71, 0x8a, 0xc3, + 0x99, 0xda, 0x4e, 0xae, 0xd0, 0xe2, 0xba, 0x94, 0xe2, 0x63, 0xe8, 0x29, 0xb9, 0x80, 0xb5, 0x98, + 0xf7, 0x09, 0x86, 0x4f, 0x4c, 0x13, 0x6d, 0x17, 0xe9, 0xe5, 0xbd, 0x08, 0xc3, 0x5f, 0xa8, 0xf3, + 0x32, 0x24, 0x12, 0x25, 0x11, 0x87, 0xbf, 0x34, 0xef, 0xa3, 0xbb, 0x85, 0x60, 0x48, 0x58, 0x37, + 0x8a, 0x08, 0xe5, 0xf0, 0x4b, 0xc5, 0x10, 0x86, 0x79, 0xc1, 0xf8, 0x2b, 0xa5, 0x9a, 0x44, 0xc2, + 0xad, 0x6e, 0x14, 0x41, 0xac, 0x1f, 0x7b, 0xac, 0x2b, 0x80, 0x85, 0x9f, 0x51, 0xb3, 0x58, 0xfa, + 0x2b, 0x85, 0xda, 0x1a, 0xda, 0x57, 0x0a, 0x45, 0x3c, 0x5e, 0xd8, 0x65, 0x18, 0x3e, 0x15, 0x77, + 0x9c, 0xc2, 0x42, 0xc2, 0xed, 0x13, 0xdb, 0xf3, 0xe1, 0xbc, 0x48, 0x08, 0xe6, 0x2e, 0x39, 0x0d, + 0x61, 0x50, 0x04, 0x85, 0x79, 0x37, 0xa4, 0xd8, 0x76, 0xda, 0x90, 0x14, 0xc7, 0x07, 0xe6, 0x14, + 0x33, 0xcc, 0xe1, 0x42, 0x99, 0x76, 0x48, 0x18, 0xda, 0x0d, 0x42, 0x39, 0x76, 0xe1, 0x52, 0x99, + 0x16, 0x68, 0x26, 0xf9, 0x58, 0x8b, 0xa5, 0xd1, 0x6d, 0x32, 0x18, 0x2a, 0xc0, 0x63, 0x42, 0x0c, + 0x7e, 0xad, 0x97, 0x45, 0x22, 0x9f, 0x29, 0x83, 0xac, 0xdd, 0xcd, 0x1c, 0x1b, 0x29, 0x83, 0x9c, + 0x90, 0xc0, 0x0e, 0x7b, 0x14, 0x37, 0x19, 0x5c, 0x29, 0x41, 0xb1, 0x07, 0x5d, 0xd2, 0xe5, 0x30, + 0x5e, 0xf2, 0x8c, 0xe2, 0x66, 0x57, 0xdc, 0xd2, 0xa9, 0x12, 0x6c, 0x13, 0x96, 0x69, 0x9c, 0x28, + 0x41, 0x01, 0x2d, 0x62, 0xfd, 0x8d, 0x72, 0xc6, 0xf6, 0x29, 0xb6, 0xdd, 0x1e, 0x4c, 0x55, 0x4a, + 0xbc, 0x30, 0xa2, 0xa4, 0x45, 0xc5, 0xa5, 0x3e, 0x2b, 0xb6, 0x23, 0xb7, 0x7d, 0x0c, 0xf3, 0xe2, + 0x38, 0x73, 0x7c, 0x6c, 0x87, 0xf0, 0x44, 0x2f, 0x61, 0x68, 0x07, 0xf0, 0xb4, 0x00, 0xb2, 0xe4, + 0x3f, 0xd3, 0xae, 0x32, 0x21, 0xf0, 0xb9, 0x72, 0x31, 0x3b, 0x11, 0x3c, 0x02, 0xcf, 0x95, 0x88, + 0x7b, 0xdc, 0x25, 0x1c, 0xbe, 0xd0, 0xce, 0xf1, 0x00, 0xbb, 0x5e, 0x37, 0x80, 0xbf, 0x56, 0xde, + 0x65, 0x80, 0x6c, 0xcd, 0xdf, 0x2a, 0x39, 0xc7, 0x0e, 0x1d, 0xec, 0x63, 0x17, 0xfe, 0x46, 0x3b, + 0x7f, 0x3a, 0xb8, 0x07, 0xbf, 0x53, 0xeb, 0x3a, 0xb8, 0x87, 0xcf, 0x22, 0x8f, 0x62, 0x17, 0xfe, + 0xd6, 0xdc, 0x2d, 0x40, 0x8a, 0x4f, 0x48, 0x07, 0xbb, 0x70, 0x6d, 0x98, 0x7b, 0x79, 0xa2, 0x24, + 0xfa, 0x31, 0x76, 0x44, 0xad, 0xff, 0xce, 0x30, 0xef, 0x2e, 0x1a, 0xf7, 0x34, 0xc4, 0x54, 0x5c, + 0x51, 0xf0, 0xf7, 0x86, 0xb9, 0x9f, 0xb7, 0x79, 0x48, 0x38, 0xc5, 0x8e, 0x38, 0x48, 0xec, 0x86, + 0x8f, 0xe1, 0x1f, 0x0c, 0x13, 0x16, 0xe7, 0x44, 0xb3, 0xe3, 0xf9, 0x3e, 0xfc, 0xa3, 0xf1, 0xf5, + 0x12, 0x18, 0xd6, 0x15, 0xaa, 0xda, 0x83, 0xc1, 0x34, 0x99, 0xcd, 0xa2, 0x74, 0x3a, 0x37, 0x4d, + 0xb4, 0x36, 0x49, 0xa7, 0xf3, 0x7d, 0xe3, 0xa0, 0x74, 0xb8, 0x4e, 0xe5, 0xff, 0xe6, 0xbb, 0x68, + 0x7b, 0xd2, 0x3f, 0xff, 0x2c, 0x19, 0xc4, 0xfd, 0x4c, 0x52, 0xce, 0x7f, 0x35, 0x5a, 0xcf, 0xd0, + 0x7c, 0xb9, 0xf9, 0x0e, 0xaa, 0x3f, 0x4e, 0x67, 0xf3, 0x71, 0xff, 0x2a, 0x89, 0x1f, 0x0f, 0xc7, + 0xf3, 0xfd, 0xb2, 0x9c, 0x12, 0x6b, 0x0b, 0xb0, 0x3d, 0x1c, 0xcf, 0xad, 0x7f, 0x5a, 0x43, 0x77, + 0x9d, 0x69, 0xd2, 0x5f, 0x0c, 0xa3, 0x34, 0xf9, 0xcd, 0x93, 0x64, 0x36, 0x37, 0x1d, 0xb4, 0x71, + 0xd1, 0xbf, 0x1a, 0x8e, 0x9e, 0x4b, 0xcb, 0xdb, 0x47, 0xef, 0x3d, 0x50, 0x03, 0xec, 0x83, 0x1b, + 0xe4, 0x1f, 0x64, 0x54, 0x53, 0x2e, 0xa1, 0xf9, 0x52, 0xd3, 0x43, 0x5b, 0x72, 0xfa, 0x3d, 0x4f, + 0xc5, 0x88, 0x2a, 0xd4, 0xbc, 0xff, 0x5a, 0x6a, 0xa2, 0x7c, 0x11, 0x55, 0xcb, 0xcd, 0x9f, 0xa3, + 0xed, 0x7c, 0xae, 0x4e, 0x27, 0xf3, 0x61, 0x3a, 0x9e, 0xed, 0x97, 0x0f, 0xca, 0x87, 0xd5, 0xa3, + 0xfb, 0x9a, 0xc2, 0x6c, 0x31, 0x91, 0x7c, 0x5a, 0x9f, 0x69, 0xd4, 0xcc, 0x6c, 0xa0, 0x3b, 0x93, + 0x69, 0xfa, 0xf9, 0xf3, 0x38, 0xf9, 0x3c, 0x9b, 0xd6, 0xe3, 0xe1, 0x64, 0x7f, 0xed, 0xc0, 0x38, + 0xac, 0x1e, 0xdd, 0xd3, 0x54, 0x68, 0xa9, 0xa7, 0x3b, 0x72, 0x01, 0xce, 0xe5, 0xbd, 0x89, 0x79, + 0x88, 0xb6, 0x47, 0xc3, 0xd9, 0x3c, 0x19, 0xc7, 0x9f, 0xf6, 0xcf, 0x3f, 0x1b, 0xa5, 0x97, 0xfb, + 0xeb, 0x8b, 0xe9, 0xbc, 0x9e, 0x31, 0x1a, 0x19, 0x6e, 0x7e, 0x84, 0x2a, 0x53, 0x39, 0xe1, 0x0b, + 0x2b, 0x1b, 0xaf, 0xb4, 0xb2, 0x95, 0x09, 0x7a, 0x13, 0x73, 0x0f, 0x6d, 0xf4, 0x27, 0x93, 0x78, + 0x38, 0xd8, 0xaf, 0xc8, 0x42, 0xad, 0xf7, 0x27, 0x13, 0x6f, 0x60, 0x7e, 0x03, 0xa1, 0xc9, 0x34, + 0xfd, 0x75, 0x72, 0x3e, 0x17, 0x2c, 0x74, 0x60, 0x1c, 0x96, 0x69, 0x25, 0x47, 0xbc, 0x81, 0x65, + 0xa1, 0x9a, 0x9e, 0x7b, 0x73, 0x0b, 0xad, 0x79, 0xd1, 0xd3, 0x1f, 0x82, 0x91, 0xff, 0xf7, 0x23, + 0x28, 0x59, 0x16, 0xda, 0x5e, 0x4e, 0xac, 0xb9, 0x89, 0xca, 0xdc, 0x89, 0xc0, 0x10, 0xff, 0x74, + 0xdd, 0x08, 0x4a, 0xd6, 0x97, 0x06, 0xba, 0xb3, 0x5c, 0x91, 0xc9, 0xe8, 0xb9, 0xf9, 0x1e, 0xba, + 0x93, 0xa7, 0x7d, 0x90, 0xcc, 0xce, 0xa7, 0xc3, 0xc9, 0x3c, 0x7f, 0x93, 0x54, 0x28, 0x64, 0x0c, + 0x57, 0xe1, 0xe6, 0xcf, 0xd0, 0xb6, 0x78, 0xf4, 0x24, 0x53, 0xd5, 0x97, 0xe5, 0x57, 0x86, 0x5e, + 0xcf, 0xa4, 0x17, 0xfd, 0xfa, 0x7b, 0x28, 0xd1, 0xf7, 0x2b, 0x5b, 0xff, 0xb3, 0x09, 0xd7, 0xd7, + 0xd7, 0xd7, 0x25, 0xeb, 0x77, 0xa8, 0xda, 0x18, 0x8e, 0x07, 0x8b, 0x86, 0x7e, 0x49, 0x24, 0xa5, + 0x1b, 0x23, 0xb9, 0xd1, 0x15, 0xd1, 0xc1, 0xaf, 0xef, 0x8a, 0x45, 0x50, 0x25, 0xb3, 0x2f, 0xf2, + 0x78, 0xa3, 0x42, 0xe3, 0x8d, 0x62, 0xb3, 0x1c, 0xb4, 0xdb, 0x4a, 0xe6, 0x59, 0x75, 0xc2, 0xfe, + 0x55, 0x72, 0x9b, 0xc8, 0xac, 0x33, 0x64, 0xae, 0x28, 0x79, 0xa9, 0x7b, 0xa5, 0x37, 0x73, 0xcf, + 0x96, 0x9a, 0xa3, 0x24, 0x99, 0xde, 0xda, 0x39, 0x07, 0xc1, 0x92, 0x0a, 0xe1, 0xda, 0x43, 0xb4, + 0x39, 0x49, 0x92, 0xe9, 0x57, 0x3b, 0xb4, 0x21, 0xc4, 0xbc, 0x89, 0xf5, 0xe5, 0xe6, 0x62, 0x47, + 0x64, 0x7b, 0xdf, 0xfc, 0x05, 0x5a, 0x1f, 0x25, 0x4f, 0x93, 0x51, 0x7e, 0x92, 0x7d, 0xef, 0x25, + 0x27, 0xc6, 0x12, 0xe1, 0x8b, 0x05, 0x34, 0x5b, 0x67, 0x3e, 0x42, 0x1b, 0xd9, 0xa1, 0x93, 0x1f, + 0x62, 0x87, 0xaf, 0xa3, 0x41, 0x46, 0x90, 0xaf, 0x33, 0x77, 0xd1, 0xfa, 0xd3, 0xfe, 0xe8, 0x49, + 0xb2, 0x5f, 0x3e, 0x28, 0x1d, 0xd6, 0x68, 0x46, 0x58, 0x09, 0xba, 0xf3, 0x82, 0x4d, 0xed, 0x41, + 0xcd, 0x88, 0x1f, 0x7b, 0x11, 0xbc, 0x25, 0x67, 0x95, 0x02, 0xca, 0xfe, 0x05, 0x43, 0xce, 0x16, + 0x05, 0x2c, 0xb6, 0xf3, 0xc6, 0x0a, 0x26, 0x76, 0xf6, 0x1d, 0xeb, 0xdf, 0xd7, 0x11, 0xac, 0x7a, + 0x26, 0x6f, 0xbb, 0x85, 0x60, 0xec, 0xe2, 0x46, 0xb7, 0x05, 0x86, 0x1c, 0xc9, 0x14, 0x48, 0xc5, + 0x94, 0x28, 0xc6, 0x23, 0x28, 0x2d, 0xa9, 0x8d, 0xe5, 0x95, 0x5a, 0x5e, 0xd6, 0x90, 0x7d, 0x47, + 0x58, 0x5b, 0xd6, 0xe0, 0x92, 0x90, 0x53, 0xd2, 0xe5, 0x18, 0xd6, 0x97, 0x19, 0x0d, 0x4a, 0x6c, + 0xd7, 0xb1, 0xe5, 0x07, 0x04, 0x31, 0x74, 0x28, 0x06, 0x0b, 0xdd, 0x46, 0xb7, 0x09, 0x9b, 0xcb, + 0x28, 0x75, 0x4e, 0x04, 0xba, 0xb5, 0xac, 0xa4, 0x83, 0x71, 0x64, 0xfb, 0xde, 0x09, 0x86, 0xca, + 0x32, 0x83, 0x90, 0x86, 0x17, 0xfa, 0x5e, 0x88, 0x01, 0x2d, 0xeb, 0xf1, 0xbd, 0xb0, 0x85, 0x29, + 0xd4, 0xcd, 0x7b, 0xc8, 0x5c, 0xd2, 0x2e, 0x86, 0x25, 0x02, 0xbb, 0xcb, 0x38, 0x0b, 0xdd, 0x0c, + 0xdf, 0xd3, 0x6a, 0xe2, 0x45, 0x31, 0x27, 0x0c, 0x8c, 0x15, 0x88, 0xfb, 0x50, 0xd2, 0xca, 0xe4, + 0x45, 0x71, 0x5b, 0x8c, 0x9a, 0x8e, 0x0f, 0xe5, 0x65, 0x98, 0x44, 0xdc, 0x23, 0x21, 0x83, 0x35, + 0xcd, 0x16, 0x77, 0xa2, 0x58, 0x3c, 0xef, 0x7d, 0xbb, 0x07, 0x86, 0x26, 0x2e, 0xf0, 0xc0, 0x3e, + 0x63, 0xb8, 0x05, 0x25, 0x2d, 0xdb, 0x02, 0x76, 0x08, 0xed, 0x40, 0x59, 0x0b, 0x5b, 0x80, 0x22, + 0x21, 0x9e, 0xeb, 0x63, 0x58, 0x33, 0xf7, 0xd1, 0xee, 0x2a, 0x23, 0xe4, 0x27, 0x3e, 0xac, 0xaf, + 0x98, 0x15, 0x1c, 0x27, 0x14, 0x65, 0x58, 0x36, 0x2b, 0x9e, 0xb0, 0x21, 0x87, 0xcd, 0x15, 0xf1, + 0x2c, 0x81, 0x47, 0xb0, 0x65, 0xbe, 0x8d, 0xee, 0x6b, 0xb8, 0x8b, 0x9b, 0x98, 0xc6, 0xb6, 0xe3, + 0xe0, 0x88, 0x43, 0x65, 0x85, 0x79, 0xea, 0x85, 0x2e, 0x39, 0x8d, 0x1d, 0xdf, 0x0e, 0x22, 0x40, + 0x2b, 0x81, 0x78, 0x61, 0x93, 0x40, 0x75, 0x25, 0x90, 0xe3, 0xae, 0xe7, 0x74, 0x6c, 0xa7, 0x03, + 0x35, 0x39, 0x11, 0x3d, 0x47, 0xf7, 0xd9, 0xe2, 0xc8, 0xca, 0xaf, 0xf3, 0x5b, 0x1d, 0xea, 0x1f, + 0xa2, 0xcd, 0xc5, 0xec, 0x50, 0x7a, 0xf5, 0xec, 0xb0, 0x90, 0xb3, 0xee, 0xa3, 0xbd, 0x17, 0x4d, + 0x4f, 0x46, 0xcf, 0x85, 0x4f, 0xad, 0x3f, 0x90, 0x4f, 0x1f, 0xa3, 0xbd, 0xd6, 0x4d, 0x3e, 0xdd, + 0x46, 0xd7, 0xbf, 0x18, 0x68, 0xdb, 0x49, 0xc7, 0xe3, 0xe4, 0x7c, 0x7e, 0x2b, 0xf7, 0x97, 0xe6, + 0x9c, 0x57, 0xdf, 0x8f, 0xc5, 0x9c, 0xf3, 0x1e, 0xda, 0x99, 0x0f, 0xaf, 0x92, 0xf4, 0xc9, 0x3c, + 0x9e, 0x25, 0xe7, 0xe9, 0x78, 0x90, 0xcd, 0x09, 0xc6, 0x4f, 0x4a, 0xef, 0x7f, 0x48, 0xb7, 0x73, + 0x16, 0xcb, 0x38, 0xd6, 0x2f, 0x51, 0x4d, 0x39, 0xf8, 0x7b, 0xba, 0x48, 0xf5, 0x21, 0xe1, 0x04, + 0xd5, 0x7d, 0x39, 0xb9, 0xdd, 0x2a, 0xfc, 0x7d, 0xb4, 0xb9, 0x98, 0x04, 0x4b, 0x72, 0x3e, 0x5f, + 0x90, 0x56, 0x1d, 0x55, 0x17, 0x7a, 0x45, 0xbb, 0x0c, 0x51, 0xdd, 0x3e, 0x3f, 0x4f, 0x26, 0xb7, + 0xcb, 0xf2, 0x0d, 0x09, 0x2b, 0xbd, 0x34, 0x61, 0xd7, 0x06, 0xaa, 0x2e, 0x6c, 0x89, 0x84, 0x1d, + 0xa1, 0xbd, 0x71, 0xf2, 0x2c, 0x7e, 0xd1, 0x5a, 0xf6, 0x66, 0xb8, 0x3b, 0x4e, 0x9e, 0xb1, 0x1b, + 0x06, 0xb9, 0xbc, 0xac, 0xaf, 0x39, 0xc8, 0x65, 0xd2, 0x39, 0x64, 0xfd, 0x97, 0x81, 0x76, 0xd8, + 0xe3, 0x27, 0x73, 0x37, 0x7d, 0x76, 0xbb, 0xbc, 0x7e, 0x80, 0xca, 0x8f, 0xd3, 0x67, 0xf9, 0x6d, + 0xfb, 0x4d, 0xbd, 0x8b, 0x97, 0xb5, 0x3e, 0x68, 0xa7, 0xcf, 0xa8, 0x10, 0x35, 0xbf, 0x85, 0xaa, + 0xb3, 0x64, 0x3c, 0x88, 0xd3, 0x8b, 0x8b, 0x59, 0x32, 0x97, 0xd7, 0x6c, 0x99, 0x22, 0x01, 0x11, + 0x89, 0x58, 0x0e, 0x2a, 0xb7, 0xd3, 0x67, 0xfa, 0x45, 0xd6, 0xee, 0xf2, 0x98, 0xba, 0xcb, 0xf7, + 0xa8, 0xc0, 0x4e, 0xc5, 0x85, 0xa7, 0xdd, 0x1b, 0x99, 0xdc, 0x29, 0x85, 0xb2, 0xb5, 0x83, 0xea, + 0x85, 0x07, 0xa2, 0xae, 0xbf, 0x42, 0x35, 0x67, 0x94, 0xce, 0x6e, 0x35, 0xed, 0x98, 0xef, 0x2c, + 0xfb, 0x2c, 0xea, 0x51, 0x96, 0x25, 0xd5, 0xfd, 0xae, 0x21, 0x94, 0x5b, 0x10, 0xf6, 0xfe, 0xcf, + 0x40, 0x55, 0x96, 0xdc, 0x72, 0xa8, 0xbd, 0x87, 0xd6, 0x06, 0xfd, 0x79, 0x5f, 0xa6, 0xb5, 0xd6, + 0x28, 0x6d, 0x19, 0x54, 0xd2, 0xe2, 0x9d, 0x38, 0x9b, 0x4f, 0x93, 0xfe, 0xd5, 0x72, 0xf6, 0x6a, + 0x19, 0x98, 0xf9, 0x61, 0xde, 0x47, 0xeb, 0x17, 0xa3, 0xfe, 0xe5, 0x4c, 0x0e, 0xe4, 0xf2, 0xc9, + 0x93, 0xd1, 0x62, 0x3e, 0x93, 0x51, 0xcc, 0x53, 0xf9, 0x1a, 0x7a, 0xc5, 0x7c, 0x26, 0xc4, 0x78, + 0x7a, 0x53, 0x37, 0x6f, 0xbc, 0xb4, 0x9b, 0x0f, 0x51, 0x25, 0x8b, 0x57, 0xb4, 0xf2, 0xdb, 0xa8, + 0x22, 0x1c, 0x8e, 0x67, 0xc9, 0x78, 0x9e, 0xfd, 0x30, 0x42, 0xb7, 0x04, 0xc0, 0x92, 0xf1, 0xdc, + 0xfa, 0x4f, 0x03, 0x6d, 0xd3, 0xe4, 0x3c, 0x19, 0x3e, 0xbd, 0x5d, 0x35, 0x94, 0xf2, 0xe1, 0x17, + 0x49, 0xbe, 0x9b, 0x33, 0xe5, 0xc3, 0x2f, 0x92, 0x22, 0xfa, 0xf2, 0x4a, 0xf4, 0x37, 0x04, 0xb3, + 0xfe, 0xd2, 0x60, 0x2c, 0xb4, 0xde, 0x94, 0xab, 0xaa, 0x68, 0x33, 0x60, 0x2d, 0x31, 0xa8, 0x80, + 0x61, 0xd6, 0xd0, 0x96, 0x20, 0x22, 0x8c, 0x3b, 0x50, 0xb2, 0xfe, 0xd5, 0x40, 0x35, 0x15, 0x86, + 0x08, 0xfa, 0x85, 0xea, 0xc8, 0x3e, 0x59, 0xa9, 0xce, 0xa2, 0xb4, 0xc2, 0x3d, 0xbd, 0xb4, 0x3f, + 0x45, 0xf5, 0x69, 0xa6, 0x6c, 0x10, 0x5f, 0x4c, 0xd3, 0xab, 0xaf, 0x78, 0x4e, 0xd5, 0x16, 0xc2, + 0xcd, 0x69, 0x7a, 0x25, 0xf6, 0xd4, 0xa7, 0x4f, 0x2e, 0x2e, 0x92, 0x69, 0x96, 0x13, 0xf9, 0xd6, + 0xa5, 0x28, 0x83, 0x44, 0x56, 0xac, 0x2f, 0xcb, 0xa8, 0x12, 0xa5, 0xa3, 0x11, 0x7e, 0x9a, 0x8c, + 0xdf, 0x30, 0xdb, 0xdf, 0x43, 0x30, 0xcd, 0xaa, 0x94, 0x0c, 0xe2, 0x44, 0xac, 0x9f, 0xe5, 0x49, + 0xdf, 0x51, 0xb8, 0x54, 0x3b, 0x33, 0xbf, 0x8b, 0x76, 0xd2, 0x4f, 0xe5, 0x4b, 0x51, 0x49, 0x96, + 0xa5, 0xe4, 0xf6, 0x02, 0xce, 0x04, 0xad, 0xff, 0x28, 0xa1, 0xba, 0x72, 0x47, 0x24, 0x5a, 0x9b, + 0x35, 0x22, 0xe2, 0xfb, 0x21, 0x09, 0x31, 0xbc, 0xa5, 0x4d, 0x6e, 0x02, 0xf4, 0xc2, 0xa5, 0x13, + 0x40, 0x40, 0x11, 0xf5, 0x96, 0x46, 0x5e, 0x81, 0x91, 0x2e, 0x87, 0xb5, 0x15, 0x0c, 0x53, 0x0a, + 0x5b, 0x2b, 0x58, 0xbb, 0x1b, 0x01, 0xac, 0xda, 0x3d, 0xb1, 0x7d, 0x38, 0xd0, 0x26, 0x2c, 0x01, + 0x52, 0x37, 0x24, 0x34, 0x80, 0x47, 0xe6, 0xbd, 0x15, 0xb8, 0x61, 0x87, 0xf2, 0x1b, 0xd3, 0x32, + 0x7e, 0x4a, 0xa5, 0xf8, 0x75, 0xe9, 0x05, 0x3c, 0x93, 0x5f, 0x93, 0x1f, 0x9f, 0x0a, 0x3c, 0x60, + 0x2d, 0xb8, 0xde, 0x5a, 0x55, 0x8e, 0x03, 0x72, 0x82, 0xe1, 0xfa, 0x40, 0x7e, 0xc0, 0xd2, 0x8d, + 0x0a, 0xb7, 0xaf, 0x1f, 0x59, 0x8f, 0x51, 0x55, 0x24, 0x70, 0xb1, 0x7f, 0x7e, 0x80, 0x36, 0xf2, + 0x84, 0x1b, 0x72, 0x9e, 0xd8, 0xd5, 0xda, 0x46, 0x25, 0x9a, 0xe6, 0x32, 0x6f, 0x76, 0x4b, 0xfd, + 0x38, 0xeb, 0x9c, 0xac, 0xc5, 0x0b, 0x3b, 0xa5, 0xaf, 0xb6, 0x63, 0xfd, 0x56, 0xec, 0xf3, 0x59, + 0x3a, 0x2a, 0xf6, 0xb9, 0x89, 0xd6, 0xc6, 0xfd, 0xab, 0x24, 0x6f, 0x36, 0xf9, 0xbf, 0x79, 0x82, + 0x20, 0xbf, 0xbb, 0x62, 0xf9, 0x31, 0x6a, 0x98, 0x64, 0xda, 0xdf, 0xf0, 0x4b, 0xd6, 0x4e, 0xae, + 0xa4, 0x99, 0xeb, 0xb0, 0xfe, 0xbb, 0x2c, 0xf6, 0x67, 0x6e, 0x5e, 0x38, 0x7f, 0xd3, 0xc7, 0xb8, + 0xf2, 0x8b, 0x1f, 0xe3, 0xde, 0x45, 0xdb, 0xe7, 0xfd, 0x71, 0x3a, 0x1e, 0x9e, 0xf7, 0x47, 0xb1, + 0xf4, 0x36, 0xfb, 0x1a, 0x57, 0x57, 0xa8, 0x7c, 0x96, 0xed, 0xa3, 0xcd, 0xfe, 0x68, 0xd8, 0x9f, + 0x25, 0xe2, 0xa0, 0x2d, 0x1f, 0x56, 0xe8, 0x82, 0xb4, 0xfe, 0xb7, 0xa4, 0xff, 0xa0, 0xfb, 0x35, + 0xb4, 0x97, 0x17, 0x10, 0xdb, 0x5e, 0x2c, 0x5e, 0x69, 0x4d, 0x3b, 0xf0, 0x7c, 0xf1, 0x80, 0x28, + 0xae, 0x2e, 0xc9, 0x92, 0xbf, 0x65, 0x96, 0xb4, 0x09, 0x5b, 0xa0, 0x0d, 0xdb, 0x6d, 0xfa, 0x76, + 0x8b, 0x2d, 0x3d, 0xe3, 0x04, 0xa3, 0x69, 0x7b, 0x7e, 0xf6, 0x0b, 0xf0, 0x12, 0x28, 0x55, 0xaf, + 0xaf, 0xc0, 0x01, 0x0e, 0x08, 0xed, 0x2d, 0xbd, 0x1d, 0x04, 0x9c, 0xff, 0x1c, 0xb4, 0xf9, 0x02, + 0x1c, 0xda, 0x01, 0x86, 0x2d, 0xed, 0x49, 0x21, 0x60, 0x86, 0xe9, 0x89, 0xe7, 0x2c, 0xbf, 0xe1, + 0x24, 0x4e, 0x9c, 0x8e, 0x7c, 0x68, 0xa2, 0x15, 0x3d, 0xd9, 0xef, 0xd8, 0x4b, 0x6f, 0x86, 0x3c, + 0xa2, 0xb6, 0x17, 0x72, 0x06, 0xb5, 0x15, 0x86, 0xfc, 0xdd, 0xc1, 0x21, 0x3e, 0xd4, 0x57, 0x18, + 0xea, 0x37, 0x9d, 0x6d, 0x6d, 0x0f, 0xcb, 0xb8, 0xec, 0x33, 0xd8, 0x69, 0x6c, 0x7d, 0xb2, 0x91, + 0x9d, 0x5a, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x31, 0x03, 0x4e, 0xbd, 0xfd, 0x1f, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/socket/socket_service.proto b/vendor/google.golang.org/appengine/internal/socket/socket_service.proto new file mode 100644 index 0000000000..2fcc7953dc --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/socket/socket_service.proto @@ -0,0 +1,460 @@ +syntax = "proto2"; +option go_package = "socket"; + +package appengine; + +message RemoteSocketServiceError { + enum ErrorCode { + SYSTEM_ERROR = 1; + GAI_ERROR = 2; + FAILURE = 4; + PERMISSION_DENIED = 5; + INVALID_REQUEST = 6; + SOCKET_CLOSED = 7; + } + + enum SystemError { + option allow_alias = true; + + SYS_SUCCESS = 0; + SYS_EPERM = 1; + SYS_ENOENT = 2; + SYS_ESRCH = 3; + SYS_EINTR = 4; + SYS_EIO = 5; + SYS_ENXIO = 6; + SYS_E2BIG = 7; + SYS_ENOEXEC = 8; + SYS_EBADF = 9; + SYS_ECHILD = 10; + SYS_EAGAIN = 11; + SYS_EWOULDBLOCK = 11; + SYS_ENOMEM = 12; + SYS_EACCES = 13; + SYS_EFAULT = 14; + SYS_ENOTBLK = 15; + SYS_EBUSY = 16; + SYS_EEXIST = 17; + SYS_EXDEV = 18; + SYS_ENODEV = 19; + SYS_ENOTDIR = 20; + SYS_EISDIR = 21; + SYS_EINVAL = 22; + SYS_ENFILE = 23; + SYS_EMFILE = 24; + SYS_ENOTTY = 25; + SYS_ETXTBSY = 26; + SYS_EFBIG = 27; + SYS_ENOSPC = 28; + SYS_ESPIPE = 29; + SYS_EROFS = 30; + SYS_EMLINK = 31; + SYS_EPIPE = 32; + SYS_EDOM = 33; + SYS_ERANGE = 34; + SYS_EDEADLK = 35; + SYS_EDEADLOCK = 35; + SYS_ENAMETOOLONG = 36; + SYS_ENOLCK = 37; + SYS_ENOSYS = 38; + SYS_ENOTEMPTY = 39; + SYS_ELOOP = 40; + SYS_ENOMSG = 42; + SYS_EIDRM = 43; + SYS_ECHRNG = 44; + SYS_EL2NSYNC = 45; + SYS_EL3HLT = 46; + SYS_EL3RST = 47; + SYS_ELNRNG = 48; + SYS_EUNATCH = 49; + SYS_ENOCSI = 50; + SYS_EL2HLT = 51; + SYS_EBADE = 52; + SYS_EBADR = 53; + SYS_EXFULL = 54; + SYS_ENOANO = 55; + SYS_EBADRQC = 56; + SYS_EBADSLT = 57; + SYS_EBFONT = 59; + SYS_ENOSTR = 60; + SYS_ENODATA = 61; + SYS_ETIME = 62; + SYS_ENOSR = 63; + SYS_ENONET = 64; + SYS_ENOPKG = 65; + SYS_EREMOTE = 66; + SYS_ENOLINK = 67; + SYS_EADV = 68; + SYS_ESRMNT = 69; + SYS_ECOMM = 70; + SYS_EPROTO = 71; + SYS_EMULTIHOP = 72; + SYS_EDOTDOT = 73; + SYS_EBADMSG = 74; + SYS_EOVERFLOW = 75; + SYS_ENOTUNIQ = 76; + SYS_EBADFD = 77; + SYS_EREMCHG = 78; + SYS_ELIBACC = 79; + SYS_ELIBBAD = 80; + SYS_ELIBSCN = 81; + SYS_ELIBMAX = 82; + SYS_ELIBEXEC = 83; + SYS_EILSEQ = 84; + SYS_ERESTART = 85; + SYS_ESTRPIPE = 86; + SYS_EUSERS = 87; + SYS_ENOTSOCK = 88; + SYS_EDESTADDRREQ = 89; + SYS_EMSGSIZE = 90; + SYS_EPROTOTYPE = 91; + SYS_ENOPROTOOPT = 92; + SYS_EPROTONOSUPPORT = 93; + SYS_ESOCKTNOSUPPORT = 94; + SYS_EOPNOTSUPP = 95; + SYS_ENOTSUP = 95; + SYS_EPFNOSUPPORT = 96; + SYS_EAFNOSUPPORT = 97; + SYS_EADDRINUSE = 98; + SYS_EADDRNOTAVAIL = 99; + SYS_ENETDOWN = 100; + SYS_ENETUNREACH = 101; + SYS_ENETRESET = 102; + SYS_ECONNABORTED = 103; + SYS_ECONNRESET = 104; + SYS_ENOBUFS = 105; + SYS_EISCONN = 106; + SYS_ENOTCONN = 107; + SYS_ESHUTDOWN = 108; + SYS_ETOOMANYREFS = 109; + SYS_ETIMEDOUT = 110; + SYS_ECONNREFUSED = 111; + SYS_EHOSTDOWN = 112; + SYS_EHOSTUNREACH = 113; + SYS_EALREADY = 114; + SYS_EINPROGRESS = 115; + SYS_ESTALE = 116; + SYS_EUCLEAN = 117; + SYS_ENOTNAM = 118; + SYS_ENAVAIL = 119; + SYS_EISNAM = 120; + SYS_EREMOTEIO = 121; + SYS_EDQUOT = 122; + SYS_ENOMEDIUM = 123; + SYS_EMEDIUMTYPE = 124; + SYS_ECANCELED = 125; + SYS_ENOKEY = 126; + SYS_EKEYEXPIRED = 127; + SYS_EKEYREVOKED = 128; + SYS_EKEYREJECTED = 129; + SYS_EOWNERDEAD = 130; + SYS_ENOTRECOVERABLE = 131; + SYS_ERFKILL = 132; + } + + optional int32 system_error = 1 [default=0]; + optional string error_detail = 2; +} + +message AddressPort { + required int32 port = 1; + optional bytes packed_address = 2; + + optional string hostname_hint = 3; +} + + + +message CreateSocketRequest { + enum SocketFamily { + IPv4 = 1; + IPv6 = 2; + } + + enum SocketProtocol { + TCP = 1; + UDP = 2; + } + + required SocketFamily family = 1; + required SocketProtocol protocol = 2; + + repeated SocketOption socket_options = 3; + + optional AddressPort proxy_external_ip = 4; + + optional int32 listen_backlog = 5 [default=0]; + + optional AddressPort remote_ip = 6; + + optional string app_id = 9; + + optional int64 project_id = 10; +} + +message CreateSocketReply { + optional string socket_descriptor = 1; + + optional AddressPort server_address = 3; + + optional AddressPort proxy_external_ip = 4; + + extensions 1000 to max; +} + + + +message BindRequest { + required string socket_descriptor = 1; + required AddressPort proxy_external_ip = 2; +} + +message BindReply { + optional AddressPort proxy_external_ip = 1; +} + + + +message GetSocketNameRequest { + required string socket_descriptor = 1; +} + +message GetSocketNameReply { + optional AddressPort proxy_external_ip = 2; +} + + + +message GetPeerNameRequest { + required string socket_descriptor = 1; +} + +message GetPeerNameReply { + optional AddressPort peer_ip = 2; +} + + +message SocketOption { + + enum SocketOptionLevel { + SOCKET_SOL_IP = 0; + SOCKET_SOL_SOCKET = 1; + SOCKET_SOL_TCP = 6; + SOCKET_SOL_UDP = 17; + } + + enum SocketOptionName { + option allow_alias = true; + + SOCKET_SO_DEBUG = 1; + SOCKET_SO_REUSEADDR = 2; + SOCKET_SO_TYPE = 3; + SOCKET_SO_ERROR = 4; + SOCKET_SO_DONTROUTE = 5; + SOCKET_SO_BROADCAST = 6; + SOCKET_SO_SNDBUF = 7; + SOCKET_SO_RCVBUF = 8; + SOCKET_SO_KEEPALIVE = 9; + SOCKET_SO_OOBINLINE = 10; + SOCKET_SO_LINGER = 13; + SOCKET_SO_RCVTIMEO = 20; + SOCKET_SO_SNDTIMEO = 21; + + SOCKET_IP_TOS = 1; + SOCKET_IP_TTL = 2; + SOCKET_IP_HDRINCL = 3; + SOCKET_IP_OPTIONS = 4; + + SOCKET_TCP_NODELAY = 1; + SOCKET_TCP_MAXSEG = 2; + SOCKET_TCP_CORK = 3; + SOCKET_TCP_KEEPIDLE = 4; + SOCKET_TCP_KEEPINTVL = 5; + SOCKET_TCP_KEEPCNT = 6; + SOCKET_TCP_SYNCNT = 7; + SOCKET_TCP_LINGER2 = 8; + SOCKET_TCP_DEFER_ACCEPT = 9; + SOCKET_TCP_WINDOW_CLAMP = 10; + SOCKET_TCP_INFO = 11; + SOCKET_TCP_QUICKACK = 12; + } + + required SocketOptionLevel level = 1; + required SocketOptionName option = 2; + required bytes value = 3; +} + + +message SetSocketOptionsRequest { + required string socket_descriptor = 1; + repeated SocketOption options = 2; +} + +message SetSocketOptionsReply { +} + +message GetSocketOptionsRequest { + required string socket_descriptor = 1; + repeated SocketOption options = 2; +} + +message GetSocketOptionsReply { + repeated SocketOption options = 2; +} + + +message ConnectRequest { + required string socket_descriptor = 1; + required AddressPort remote_ip = 2; + optional double timeout_seconds = 3 [default=-1]; +} + +message ConnectReply { + optional AddressPort proxy_external_ip = 1; + + extensions 1000 to max; +} + + +message ListenRequest { + required string socket_descriptor = 1; + required int32 backlog = 2; +} + +message ListenReply { +} + + +message AcceptRequest { + required string socket_descriptor = 1; + optional double timeout_seconds = 2 [default=-1]; +} + +message AcceptReply { + optional bytes new_socket_descriptor = 2; + optional AddressPort remote_address = 3; +} + + + +message ShutDownRequest { + enum How { + SOCKET_SHUT_RD = 1; + SOCKET_SHUT_WR = 2; + SOCKET_SHUT_RDWR = 3; + } + required string socket_descriptor = 1; + required How how = 2; + required int64 send_offset = 3; +} + +message ShutDownReply { +} + + + +message CloseRequest { + required string socket_descriptor = 1; + optional int64 send_offset = 2 [default=-1]; +} + +message CloseReply { +} + + + +message SendRequest { + required string socket_descriptor = 1; + required bytes data = 2 [ctype=CORD]; + required int64 stream_offset = 3; + optional int32 flags = 4 [default=0]; + optional AddressPort send_to = 5; + optional double timeout_seconds = 6 [default=-1]; +} + +message SendReply { + optional int32 data_sent = 1; +} + + +message ReceiveRequest { + enum Flags { + MSG_OOB = 1; + MSG_PEEK = 2; + } + required string socket_descriptor = 1; + required int32 data_size = 2; + optional int32 flags = 3 [default=0]; + optional double timeout_seconds = 5 [default=-1]; +} + +message ReceiveReply { + optional int64 stream_offset = 2; + optional bytes data = 3 [ctype=CORD]; + optional AddressPort received_from = 4; + optional int32 buffer_size = 5; +} + + + +message PollEvent { + + enum PollEventFlag { + SOCKET_POLLNONE = 0; + SOCKET_POLLIN = 1; + SOCKET_POLLPRI = 2; + SOCKET_POLLOUT = 4; + SOCKET_POLLERR = 8; + SOCKET_POLLHUP = 16; + SOCKET_POLLNVAL = 32; + SOCKET_POLLRDNORM = 64; + SOCKET_POLLRDBAND = 128; + SOCKET_POLLWRNORM = 256; + SOCKET_POLLWRBAND = 512; + SOCKET_POLLMSG = 1024; + SOCKET_POLLREMOVE = 4096; + SOCKET_POLLRDHUP = 8192; + }; + + required string socket_descriptor = 1; + required int32 requested_events = 2; + required int32 observed_events = 3; +} + +message PollRequest { + repeated PollEvent events = 1; + optional double timeout_seconds = 2 [default=-1]; +} + +message PollReply { + repeated PollEvent events = 2; +} + +message ResolveRequest { + required string name = 1; + repeated CreateSocketRequest.SocketFamily address_families = 2; +} + +message ResolveReply { + enum ErrorCode { + SOCKET_EAI_ADDRFAMILY = 1; + SOCKET_EAI_AGAIN = 2; + SOCKET_EAI_BADFLAGS = 3; + SOCKET_EAI_FAIL = 4; + SOCKET_EAI_FAMILY = 5; + SOCKET_EAI_MEMORY = 6; + SOCKET_EAI_NODATA = 7; + SOCKET_EAI_NONAME = 8; + SOCKET_EAI_SERVICE = 9; + SOCKET_EAI_SOCKTYPE = 10; + SOCKET_EAI_SYSTEM = 11; + SOCKET_EAI_BADHINTS = 12; + SOCKET_EAI_PROTOCOL = 13; + SOCKET_EAI_OVERFLOW = 14; + SOCKET_EAI_MAX = 15; + }; + + repeated bytes packed_address = 2; + optional string canonical_name = 3; + repeated string aliases = 4; +} diff --git a/vendor/google.golang.org/appengine/internal/system/system_service.pb.go b/vendor/google.golang.org/appengine/internal/system/system_service.pb.go new file mode 100644 index 0000000000..248c333fab --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/system/system_service.pb.go @@ -0,0 +1,250 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/system/system_service.proto + +/* +Package system is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/system/system_service.proto + +It has these top-level messages: + SystemServiceError + SystemStat + GetSystemStatsRequest + GetSystemStatsResponse + StartBackgroundRequestRequest + StartBackgroundRequestResponse +*/ +package system + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type SystemServiceError_ErrorCode int32 + +const ( + SystemServiceError_OK SystemServiceError_ErrorCode = 0 + SystemServiceError_INTERNAL_ERROR SystemServiceError_ErrorCode = 1 + SystemServiceError_BACKEND_REQUIRED SystemServiceError_ErrorCode = 2 + SystemServiceError_LIMIT_REACHED SystemServiceError_ErrorCode = 3 +) + +var SystemServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INTERNAL_ERROR", + 2: "BACKEND_REQUIRED", + 3: "LIMIT_REACHED", +} +var SystemServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INTERNAL_ERROR": 1, + "BACKEND_REQUIRED": 2, + "LIMIT_REACHED": 3, +} + +func (x SystemServiceError_ErrorCode) Enum() *SystemServiceError_ErrorCode { + p := new(SystemServiceError_ErrorCode) + *p = x + return p +} +func (x SystemServiceError_ErrorCode) String() string { + return proto.EnumName(SystemServiceError_ErrorCode_name, int32(x)) +} +func (x *SystemServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(SystemServiceError_ErrorCode_value, data, "SystemServiceError_ErrorCode") + if err != nil { + return err + } + *x = SystemServiceError_ErrorCode(value) + return nil +} +func (SystemServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type SystemServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *SystemServiceError) Reset() { *m = SystemServiceError{} } +func (m *SystemServiceError) String() string { return proto.CompactTextString(m) } +func (*SystemServiceError) ProtoMessage() {} +func (*SystemServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type SystemStat struct { + // Instaneous value of this stat. + Current *float64 `protobuf:"fixed64,1,opt,name=current" json:"current,omitempty"` + // Average over time, if this stat has an instaneous value. + Average1M *float64 `protobuf:"fixed64,3,opt,name=average1m" json:"average1m,omitempty"` + Average10M *float64 `protobuf:"fixed64,4,opt,name=average10m" json:"average10m,omitempty"` + // Total value, if the stat accumulates over time. + Total *float64 `protobuf:"fixed64,2,opt,name=total" json:"total,omitempty"` + // Rate over time, if this stat accumulates. + Rate1M *float64 `protobuf:"fixed64,5,opt,name=rate1m" json:"rate1m,omitempty"` + Rate10M *float64 `protobuf:"fixed64,6,opt,name=rate10m" json:"rate10m,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *SystemStat) Reset() { *m = SystemStat{} } +func (m *SystemStat) String() string { return proto.CompactTextString(m) } +func (*SystemStat) ProtoMessage() {} +func (*SystemStat) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *SystemStat) GetCurrent() float64 { + if m != nil && m.Current != nil { + return *m.Current + } + return 0 +} + +func (m *SystemStat) GetAverage1M() float64 { + if m != nil && m.Average1M != nil { + return *m.Average1M + } + return 0 +} + +func (m *SystemStat) GetAverage10M() float64 { + if m != nil && m.Average10M != nil { + return *m.Average10M + } + return 0 +} + +func (m *SystemStat) GetTotal() float64 { + if m != nil && m.Total != nil { + return *m.Total + } + return 0 +} + +func (m *SystemStat) GetRate1M() float64 { + if m != nil && m.Rate1M != nil { + return *m.Rate1M + } + return 0 +} + +func (m *SystemStat) GetRate10M() float64 { + if m != nil && m.Rate10M != nil { + return *m.Rate10M + } + return 0 +} + +type GetSystemStatsRequest struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetSystemStatsRequest) Reset() { *m = GetSystemStatsRequest{} } +func (m *GetSystemStatsRequest) String() string { return proto.CompactTextString(m) } +func (*GetSystemStatsRequest) ProtoMessage() {} +func (*GetSystemStatsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +type GetSystemStatsResponse struct { + // CPU used by this instance, in mcycles. + Cpu *SystemStat `protobuf:"bytes,1,opt,name=cpu" json:"cpu,omitempty"` + // Physical memory (RAM) used by this instance, in megabytes. + Memory *SystemStat `protobuf:"bytes,2,opt,name=memory" json:"memory,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetSystemStatsResponse) Reset() { *m = GetSystemStatsResponse{} } +func (m *GetSystemStatsResponse) String() string { return proto.CompactTextString(m) } +func (*GetSystemStatsResponse) ProtoMessage() {} +func (*GetSystemStatsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *GetSystemStatsResponse) GetCpu() *SystemStat { + if m != nil { + return m.Cpu + } + return nil +} + +func (m *GetSystemStatsResponse) GetMemory() *SystemStat { + if m != nil { + return m.Memory + } + return nil +} + +type StartBackgroundRequestRequest struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *StartBackgroundRequestRequest) Reset() { *m = StartBackgroundRequestRequest{} } +func (m *StartBackgroundRequestRequest) String() string { return proto.CompactTextString(m) } +func (*StartBackgroundRequestRequest) ProtoMessage() {} +func (*StartBackgroundRequestRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +type StartBackgroundRequestResponse struct { + // Every /_ah/background request will have an X-AppEngine-BackgroundRequest + // header, whose value will be equal to this parameter, the request_id. + RequestId *string `protobuf:"bytes,1,opt,name=request_id,json=requestId" json:"request_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *StartBackgroundRequestResponse) Reset() { *m = StartBackgroundRequestResponse{} } +func (m *StartBackgroundRequestResponse) String() string { return proto.CompactTextString(m) } +func (*StartBackgroundRequestResponse) ProtoMessage() {} +func (*StartBackgroundRequestResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *StartBackgroundRequestResponse) GetRequestId() string { + if m != nil && m.RequestId != nil { + return *m.RequestId + } + return "" +} + +func init() { + proto.RegisterType((*SystemServiceError)(nil), "appengine.SystemServiceError") + proto.RegisterType((*SystemStat)(nil), "appengine.SystemStat") + proto.RegisterType((*GetSystemStatsRequest)(nil), "appengine.GetSystemStatsRequest") + proto.RegisterType((*GetSystemStatsResponse)(nil), "appengine.GetSystemStatsResponse") + proto.RegisterType((*StartBackgroundRequestRequest)(nil), "appengine.StartBackgroundRequestRequest") + proto.RegisterType((*StartBackgroundRequestResponse)(nil), "appengine.StartBackgroundRequestResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/system/system_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 377 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x4f, 0x8f, 0x93, 0x40, + 0x18, 0xc6, 0xa5, 0x75, 0x51, 0x5e, 0xa3, 0xc1, 0xc9, 0xee, 0xca, 0xc1, 0x5d, 0x0d, 0x17, 0xbd, + 0x48, 0x57, 0xbf, 0x80, 0xf6, 0xcf, 0x44, 0x49, 0x6b, 0xab, 0xd3, 0x7a, 0xf1, 0x42, 0x26, 0xf0, + 0x3a, 0x21, 0xc2, 0x0c, 0x0e, 0x43, 0x93, 0x7e, 0x27, 0x3f, 0xa4, 0xe9, 0x30, 0x6d, 0xcd, 0x26, + 0x3d, 0x31, 0xcf, 0xf3, 0xfc, 0x02, 0x3f, 0x08, 0xf0, 0x49, 0x28, 0x25, 0x2a, 0x4c, 0x84, 0xaa, + 0xb8, 0x14, 0x89, 0xd2, 0x62, 0xc4, 0x9b, 0x06, 0xa5, 0x28, 0x25, 0x8e, 0x4a, 0x69, 0x50, 0x4b, + 0x5e, 0x8d, 0xda, 0x5d, 0x6b, 0xb0, 0x76, 0x97, 0xac, 0x45, 0xbd, 0x2d, 0x73, 0x4c, 0x1a, 0xad, + 0x8c, 0x22, 0xc1, 0x91, 0x8f, 0x7f, 0x01, 0x59, 0x5b, 0x64, 0xdd, 0x13, 0x54, 0x6b, 0xa5, 0xe3, + 0x6f, 0x10, 0xd8, 0xc3, 0x54, 0x15, 0x48, 0x7c, 0x18, 0xac, 0xe6, 0xe1, 0x03, 0x42, 0xe0, 0x59, + 0xba, 0xdc, 0x50, 0xb6, 0x1c, 0x2f, 0x32, 0xca, 0xd8, 0x8a, 0x85, 0x1e, 0xb9, 0x84, 0x70, 0x32, + 0x9e, 0xce, 0xe9, 0x72, 0x96, 0x31, 0xfa, 0xfd, 0x47, 0xca, 0xe8, 0x2c, 0x1c, 0x90, 0xe7, 0xf0, + 0x74, 0x91, 0x7e, 0x4d, 0x37, 0x19, 0xa3, 0xe3, 0xe9, 0x17, 0x3a, 0x0b, 0x87, 0xf1, 0x5f, 0x0f, + 0xc0, 0x3d, 0xc8, 0x70, 0x43, 0x22, 0x78, 0x94, 0x77, 0x5a, 0xa3, 0x34, 0x91, 0xf7, 0xda, 0x7b, + 0xeb, 0xb1, 0x43, 0x24, 0x2f, 0x21, 0xe0, 0x5b, 0xd4, 0x5c, 0xe0, 0xfb, 0x3a, 0x1a, 0xda, 0xed, + 0x54, 0x90, 0x5b, 0x80, 0x43, 0xb8, 0xab, 0xa3, 0x87, 0x76, 0xfe, 0xaf, 0x21, 0x97, 0x70, 0x61, + 0x94, 0xe1, 0x55, 0x34, 0xb0, 0x53, 0x1f, 0xc8, 0x35, 0xf8, 0x9a, 0x9b, 0xfd, 0x0d, 0x2f, 0x6c, + 0xed, 0xd2, 0xde, 0xc2, 0x9e, 0xee, 0xea, 0xc8, 0xef, 0x2d, 0x5c, 0x8c, 0x5f, 0xc0, 0xd5, 0x67, + 0x34, 0x27, 0xe1, 0x96, 0xe1, 0x9f, 0x0e, 0x5b, 0x13, 0x37, 0x70, 0x7d, 0x7f, 0x68, 0x1b, 0x25, + 0x5b, 0x24, 0x6f, 0x60, 0x98, 0x37, 0x9d, 0x7d, 0x9d, 0x27, 0x1f, 0xae, 0x92, 0xe3, 0x27, 0x4e, + 0x4e, 0x30, 0xdb, 0x13, 0xe4, 0x1d, 0xf8, 0x35, 0xd6, 0x4a, 0xef, 0xac, 0xe4, 0x59, 0xd6, 0x41, + 0xf1, 0x2b, 0xb8, 0x59, 0x1b, 0xae, 0xcd, 0x84, 0xe7, 0xbf, 0x85, 0x56, 0x9d, 0x2c, 0x9c, 0xcb, + 0x41, 0xe9, 0x23, 0xdc, 0x9e, 0x03, 0x9c, 0xda, 0x0d, 0x80, 0xee, 0xab, 0xac, 0x2c, 0xac, 0x61, + 0xc0, 0x02, 0xd7, 0xa4, 0xc5, 0xe4, 0xf1, 0x4f, 0xbf, 0xff, 0x4d, 0xfe, 0x05, 0x00, 0x00, 0xff, + 0xff, 0x56, 0x5d, 0x5e, 0xc3, 0x5b, 0x02, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/system/system_service.proto b/vendor/google.golang.org/appengine/internal/system/system_service.proto new file mode 100644 index 0000000000..32c0bf8598 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/system/system_service.proto @@ -0,0 +1,49 @@ +syntax = "proto2"; +option go_package = "system"; + +package appengine; + +message SystemServiceError { + enum ErrorCode { + OK = 0; + INTERNAL_ERROR = 1; + BACKEND_REQUIRED = 2; + LIMIT_REACHED = 3; + } +} + +message SystemStat { + // Instaneous value of this stat. + optional double current = 1; + + // Average over time, if this stat has an instaneous value. + optional double average1m = 3; + optional double average10m = 4; + + // Total value, if the stat accumulates over time. + optional double total = 2; + + // Rate over time, if this stat accumulates. + optional double rate1m = 5; + optional double rate10m = 6; +} + +message GetSystemStatsRequest { +} + +message GetSystemStatsResponse { + // CPU used by this instance, in mcycles. + optional SystemStat cpu = 1; + + // Physical memory (RAM) used by this instance, in megabytes. + optional SystemStat memory = 2; +} + +message StartBackgroundRequestRequest { +} + +message StartBackgroundRequestResponse { + // Every /_ah/background request will have an X-AppEngine-BackgroundRequest + // header, whose value will be equal to this parameter, the request_id. + optional string request_id = 1; +} diff --git a/vendor/google.golang.org/appengine/internal/taskqueue/taskqueue_service.pb.go b/vendor/google.golang.org/appengine/internal/taskqueue/taskqueue_service.pb.go new file mode 100644 index 0000000000..040d176a27 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/taskqueue/taskqueue_service.pb.go @@ -0,0 +1,2209 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/taskqueue/taskqueue_service.proto + +/* +Package taskqueue is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/taskqueue/taskqueue_service.proto + +It has these top-level messages: + TaskQueueServiceError + TaskPayload + TaskQueueRetryParameters + TaskQueueAcl + TaskQueueHttpHeader + TaskQueueMode + TaskQueueAddRequest + TaskQueueAddResponse + TaskQueueBulkAddRequest + TaskQueueBulkAddResponse + TaskQueueDeleteRequest + TaskQueueDeleteResponse + TaskQueueForceRunRequest + TaskQueueForceRunResponse + TaskQueueUpdateQueueRequest + TaskQueueUpdateQueueResponse + TaskQueueFetchQueuesRequest + TaskQueueFetchQueuesResponse + TaskQueueFetchQueueStatsRequest + TaskQueueScannerQueueInfo + TaskQueueFetchQueueStatsResponse + TaskQueuePauseQueueRequest + TaskQueuePauseQueueResponse + TaskQueuePurgeQueueRequest + TaskQueuePurgeQueueResponse + TaskQueueDeleteQueueRequest + TaskQueueDeleteQueueResponse + TaskQueueDeleteGroupRequest + TaskQueueDeleteGroupResponse + TaskQueueQueryTasksRequest + TaskQueueQueryTasksResponse + TaskQueueFetchTaskRequest + TaskQueueFetchTaskResponse + TaskQueueUpdateStorageLimitRequest + TaskQueueUpdateStorageLimitResponse + TaskQueueQueryAndOwnTasksRequest + TaskQueueQueryAndOwnTasksResponse + TaskQueueModifyTaskLeaseRequest + TaskQueueModifyTaskLeaseResponse +*/ +package taskqueue + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import appengine "google.golang.org/appengine/internal/datastore" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type TaskQueueServiceError_ErrorCode int32 + +const ( + TaskQueueServiceError_OK TaskQueueServiceError_ErrorCode = 0 + TaskQueueServiceError_UNKNOWN_QUEUE TaskQueueServiceError_ErrorCode = 1 + TaskQueueServiceError_TRANSIENT_ERROR TaskQueueServiceError_ErrorCode = 2 + TaskQueueServiceError_INTERNAL_ERROR TaskQueueServiceError_ErrorCode = 3 + TaskQueueServiceError_TASK_TOO_LARGE TaskQueueServiceError_ErrorCode = 4 + TaskQueueServiceError_INVALID_TASK_NAME TaskQueueServiceError_ErrorCode = 5 + TaskQueueServiceError_INVALID_QUEUE_NAME TaskQueueServiceError_ErrorCode = 6 + TaskQueueServiceError_INVALID_URL TaskQueueServiceError_ErrorCode = 7 + TaskQueueServiceError_INVALID_QUEUE_RATE TaskQueueServiceError_ErrorCode = 8 + TaskQueueServiceError_PERMISSION_DENIED TaskQueueServiceError_ErrorCode = 9 + TaskQueueServiceError_TASK_ALREADY_EXISTS TaskQueueServiceError_ErrorCode = 10 + TaskQueueServiceError_TOMBSTONED_TASK TaskQueueServiceError_ErrorCode = 11 + TaskQueueServiceError_INVALID_ETA TaskQueueServiceError_ErrorCode = 12 + TaskQueueServiceError_INVALID_REQUEST TaskQueueServiceError_ErrorCode = 13 + TaskQueueServiceError_UNKNOWN_TASK TaskQueueServiceError_ErrorCode = 14 + TaskQueueServiceError_TOMBSTONED_QUEUE TaskQueueServiceError_ErrorCode = 15 + TaskQueueServiceError_DUPLICATE_TASK_NAME TaskQueueServiceError_ErrorCode = 16 + TaskQueueServiceError_SKIPPED TaskQueueServiceError_ErrorCode = 17 + TaskQueueServiceError_TOO_MANY_TASKS TaskQueueServiceError_ErrorCode = 18 + TaskQueueServiceError_INVALID_PAYLOAD TaskQueueServiceError_ErrorCode = 19 + TaskQueueServiceError_INVALID_RETRY_PARAMETERS TaskQueueServiceError_ErrorCode = 20 + TaskQueueServiceError_INVALID_QUEUE_MODE TaskQueueServiceError_ErrorCode = 21 + TaskQueueServiceError_ACL_LOOKUP_ERROR TaskQueueServiceError_ErrorCode = 22 + TaskQueueServiceError_TRANSACTIONAL_REQUEST_TOO_LARGE TaskQueueServiceError_ErrorCode = 23 + TaskQueueServiceError_INCORRECT_CREATOR_NAME TaskQueueServiceError_ErrorCode = 24 + TaskQueueServiceError_TASK_LEASE_EXPIRED TaskQueueServiceError_ErrorCode = 25 + TaskQueueServiceError_QUEUE_PAUSED TaskQueueServiceError_ErrorCode = 26 + TaskQueueServiceError_INVALID_TAG TaskQueueServiceError_ErrorCode = 27 + // Reserved range for the Datastore error codes. + // Original Datastore error code is shifted by DATASTORE_ERROR offset. + TaskQueueServiceError_DATASTORE_ERROR TaskQueueServiceError_ErrorCode = 10000 +) + +var TaskQueueServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "UNKNOWN_QUEUE", + 2: "TRANSIENT_ERROR", + 3: "INTERNAL_ERROR", + 4: "TASK_TOO_LARGE", + 5: "INVALID_TASK_NAME", + 6: "INVALID_QUEUE_NAME", + 7: "INVALID_URL", + 8: "INVALID_QUEUE_RATE", + 9: "PERMISSION_DENIED", + 10: "TASK_ALREADY_EXISTS", + 11: "TOMBSTONED_TASK", + 12: "INVALID_ETA", + 13: "INVALID_REQUEST", + 14: "UNKNOWN_TASK", + 15: "TOMBSTONED_QUEUE", + 16: "DUPLICATE_TASK_NAME", + 17: "SKIPPED", + 18: "TOO_MANY_TASKS", + 19: "INVALID_PAYLOAD", + 20: "INVALID_RETRY_PARAMETERS", + 21: "INVALID_QUEUE_MODE", + 22: "ACL_LOOKUP_ERROR", + 23: "TRANSACTIONAL_REQUEST_TOO_LARGE", + 24: "INCORRECT_CREATOR_NAME", + 25: "TASK_LEASE_EXPIRED", + 26: "QUEUE_PAUSED", + 27: "INVALID_TAG", + 10000: "DATASTORE_ERROR", +} +var TaskQueueServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "UNKNOWN_QUEUE": 1, + "TRANSIENT_ERROR": 2, + "INTERNAL_ERROR": 3, + "TASK_TOO_LARGE": 4, + "INVALID_TASK_NAME": 5, + "INVALID_QUEUE_NAME": 6, + "INVALID_URL": 7, + "INVALID_QUEUE_RATE": 8, + "PERMISSION_DENIED": 9, + "TASK_ALREADY_EXISTS": 10, + "TOMBSTONED_TASK": 11, + "INVALID_ETA": 12, + "INVALID_REQUEST": 13, + "UNKNOWN_TASK": 14, + "TOMBSTONED_QUEUE": 15, + "DUPLICATE_TASK_NAME": 16, + "SKIPPED": 17, + "TOO_MANY_TASKS": 18, + "INVALID_PAYLOAD": 19, + "INVALID_RETRY_PARAMETERS": 20, + "INVALID_QUEUE_MODE": 21, + "ACL_LOOKUP_ERROR": 22, + "TRANSACTIONAL_REQUEST_TOO_LARGE": 23, + "INCORRECT_CREATOR_NAME": 24, + "TASK_LEASE_EXPIRED": 25, + "QUEUE_PAUSED": 26, + "INVALID_TAG": 27, + "DATASTORE_ERROR": 10000, +} + +func (x TaskQueueServiceError_ErrorCode) Enum() *TaskQueueServiceError_ErrorCode { + p := new(TaskQueueServiceError_ErrorCode) + *p = x + return p +} +func (x TaskQueueServiceError_ErrorCode) String() string { + return proto.EnumName(TaskQueueServiceError_ErrorCode_name, int32(x)) +} +func (x *TaskQueueServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(TaskQueueServiceError_ErrorCode_value, data, "TaskQueueServiceError_ErrorCode") + if err != nil { + return err + } + *x = TaskQueueServiceError_ErrorCode(value) + return nil +} +func (TaskQueueServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type TaskQueueMode_Mode int32 + +const ( + TaskQueueMode_PUSH TaskQueueMode_Mode = 0 + TaskQueueMode_PULL TaskQueueMode_Mode = 1 +) + +var TaskQueueMode_Mode_name = map[int32]string{ + 0: "PUSH", + 1: "PULL", +} +var TaskQueueMode_Mode_value = map[string]int32{ + "PUSH": 0, + "PULL": 1, +} + +func (x TaskQueueMode_Mode) Enum() *TaskQueueMode_Mode { + p := new(TaskQueueMode_Mode) + *p = x + return p +} +func (x TaskQueueMode_Mode) String() string { + return proto.EnumName(TaskQueueMode_Mode_name, int32(x)) +} +func (x *TaskQueueMode_Mode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(TaskQueueMode_Mode_value, data, "TaskQueueMode_Mode") + if err != nil { + return err + } + *x = TaskQueueMode_Mode(value) + return nil +} +func (TaskQueueMode_Mode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{5, 0} } + +type TaskQueueAddRequest_RequestMethod int32 + +const ( + TaskQueueAddRequest_GET TaskQueueAddRequest_RequestMethod = 1 + TaskQueueAddRequest_POST TaskQueueAddRequest_RequestMethod = 2 + TaskQueueAddRequest_HEAD TaskQueueAddRequest_RequestMethod = 3 + TaskQueueAddRequest_PUT TaskQueueAddRequest_RequestMethod = 4 + TaskQueueAddRequest_DELETE TaskQueueAddRequest_RequestMethod = 5 +) + +var TaskQueueAddRequest_RequestMethod_name = map[int32]string{ + 1: "GET", + 2: "POST", + 3: "HEAD", + 4: "PUT", + 5: "DELETE", +} +var TaskQueueAddRequest_RequestMethod_value = map[string]int32{ + "GET": 1, + "POST": 2, + "HEAD": 3, + "PUT": 4, + "DELETE": 5, +} + +func (x TaskQueueAddRequest_RequestMethod) Enum() *TaskQueueAddRequest_RequestMethod { + p := new(TaskQueueAddRequest_RequestMethod) + *p = x + return p +} +func (x TaskQueueAddRequest_RequestMethod) String() string { + return proto.EnumName(TaskQueueAddRequest_RequestMethod_name, int32(x)) +} +func (x *TaskQueueAddRequest_RequestMethod) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(TaskQueueAddRequest_RequestMethod_value, data, "TaskQueueAddRequest_RequestMethod") + if err != nil { + return err + } + *x = TaskQueueAddRequest_RequestMethod(value) + return nil +} +func (TaskQueueAddRequest_RequestMethod) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{6, 0} +} + +type TaskQueueQueryTasksResponse_Task_RequestMethod int32 + +const ( + TaskQueueQueryTasksResponse_Task_GET TaskQueueQueryTasksResponse_Task_RequestMethod = 1 + TaskQueueQueryTasksResponse_Task_POST TaskQueueQueryTasksResponse_Task_RequestMethod = 2 + TaskQueueQueryTasksResponse_Task_HEAD TaskQueueQueryTasksResponse_Task_RequestMethod = 3 + TaskQueueQueryTasksResponse_Task_PUT TaskQueueQueryTasksResponse_Task_RequestMethod = 4 + TaskQueueQueryTasksResponse_Task_DELETE TaskQueueQueryTasksResponse_Task_RequestMethod = 5 +) + +var TaskQueueQueryTasksResponse_Task_RequestMethod_name = map[int32]string{ + 1: "GET", + 2: "POST", + 3: "HEAD", + 4: "PUT", + 5: "DELETE", +} +var TaskQueueQueryTasksResponse_Task_RequestMethod_value = map[string]int32{ + "GET": 1, + "POST": 2, + "HEAD": 3, + "PUT": 4, + "DELETE": 5, +} + +func (x TaskQueueQueryTasksResponse_Task_RequestMethod) Enum() *TaskQueueQueryTasksResponse_Task_RequestMethod { + p := new(TaskQueueQueryTasksResponse_Task_RequestMethod) + *p = x + return p +} +func (x TaskQueueQueryTasksResponse_Task_RequestMethod) String() string { + return proto.EnumName(TaskQueueQueryTasksResponse_Task_RequestMethod_name, int32(x)) +} +func (x *TaskQueueQueryTasksResponse_Task_RequestMethod) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(TaskQueueQueryTasksResponse_Task_RequestMethod_value, data, "TaskQueueQueryTasksResponse_Task_RequestMethod") + if err != nil { + return err + } + *x = TaskQueueQueryTasksResponse_Task_RequestMethod(value) + return nil +} +func (TaskQueueQueryTasksResponse_Task_RequestMethod) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{30, 0, 0} +} + +type TaskQueueServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueServiceError) Reset() { *m = TaskQueueServiceError{} } +func (m *TaskQueueServiceError) String() string { return proto.CompactTextString(m) } +func (*TaskQueueServiceError) ProtoMessage() {} +func (*TaskQueueServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type TaskPayload struct { + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskPayload) Reset() { *m = TaskPayload{} } +func (m *TaskPayload) String() string { return proto.CompactTextString(m) } +func (*TaskPayload) ProtoMessage() {} +func (*TaskPayload) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *TaskPayload) Marshal() ([]byte, error) { + return proto.MarshalMessageSet(&m.XXX_InternalExtensions) +} +func (m *TaskPayload) Unmarshal(buf []byte) error { + return proto.UnmarshalMessageSet(buf, &m.XXX_InternalExtensions) +} +func (m *TaskPayload) MarshalJSON() ([]byte, error) { + return proto.MarshalMessageSetJSON(&m.XXX_InternalExtensions) +} +func (m *TaskPayload) UnmarshalJSON(buf []byte) error { + return proto.UnmarshalMessageSetJSON(buf, &m.XXX_InternalExtensions) +} + +// ensure TaskPayload satisfies proto.Marshaler and proto.Unmarshaler +var _ proto.Marshaler = (*TaskPayload)(nil) +var _ proto.Unmarshaler = (*TaskPayload)(nil) + +var extRange_TaskPayload = []proto.ExtensionRange{ + {10, 2147483646}, +} + +func (*TaskPayload) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_TaskPayload +} + +type TaskQueueRetryParameters struct { + RetryLimit *int32 `protobuf:"varint,1,opt,name=retry_limit,json=retryLimit" json:"retry_limit,omitempty"` + AgeLimitSec *int64 `protobuf:"varint,2,opt,name=age_limit_sec,json=ageLimitSec" json:"age_limit_sec,omitempty"` + MinBackoffSec *float64 `protobuf:"fixed64,3,opt,name=min_backoff_sec,json=minBackoffSec,def=0.1" json:"min_backoff_sec,omitempty"` + MaxBackoffSec *float64 `protobuf:"fixed64,4,opt,name=max_backoff_sec,json=maxBackoffSec,def=3600" json:"max_backoff_sec,omitempty"` + MaxDoublings *int32 `protobuf:"varint,5,opt,name=max_doublings,json=maxDoublings,def=16" json:"max_doublings,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueRetryParameters) Reset() { *m = TaskQueueRetryParameters{} } +func (m *TaskQueueRetryParameters) String() string { return proto.CompactTextString(m) } +func (*TaskQueueRetryParameters) ProtoMessage() {} +func (*TaskQueueRetryParameters) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +const Default_TaskQueueRetryParameters_MinBackoffSec float64 = 0.1 +const Default_TaskQueueRetryParameters_MaxBackoffSec float64 = 3600 +const Default_TaskQueueRetryParameters_MaxDoublings int32 = 16 + +func (m *TaskQueueRetryParameters) GetRetryLimit() int32 { + if m != nil && m.RetryLimit != nil { + return *m.RetryLimit + } + return 0 +} + +func (m *TaskQueueRetryParameters) GetAgeLimitSec() int64 { + if m != nil && m.AgeLimitSec != nil { + return *m.AgeLimitSec + } + return 0 +} + +func (m *TaskQueueRetryParameters) GetMinBackoffSec() float64 { + if m != nil && m.MinBackoffSec != nil { + return *m.MinBackoffSec + } + return Default_TaskQueueRetryParameters_MinBackoffSec +} + +func (m *TaskQueueRetryParameters) GetMaxBackoffSec() float64 { + if m != nil && m.MaxBackoffSec != nil { + return *m.MaxBackoffSec + } + return Default_TaskQueueRetryParameters_MaxBackoffSec +} + +func (m *TaskQueueRetryParameters) GetMaxDoublings() int32 { + if m != nil && m.MaxDoublings != nil { + return *m.MaxDoublings + } + return Default_TaskQueueRetryParameters_MaxDoublings +} + +type TaskQueueAcl struct { + UserEmail [][]byte `protobuf:"bytes,1,rep,name=user_email,json=userEmail" json:"user_email,omitempty"` + WriterEmail [][]byte `protobuf:"bytes,2,rep,name=writer_email,json=writerEmail" json:"writer_email,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueAcl) Reset() { *m = TaskQueueAcl{} } +func (m *TaskQueueAcl) String() string { return proto.CompactTextString(m) } +func (*TaskQueueAcl) ProtoMessage() {} +func (*TaskQueueAcl) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *TaskQueueAcl) GetUserEmail() [][]byte { + if m != nil { + return m.UserEmail + } + return nil +} + +func (m *TaskQueueAcl) GetWriterEmail() [][]byte { + if m != nil { + return m.WriterEmail + } + return nil +} + +type TaskQueueHttpHeader struct { + Key []byte `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueHttpHeader) Reset() { *m = TaskQueueHttpHeader{} } +func (m *TaskQueueHttpHeader) String() string { return proto.CompactTextString(m) } +func (*TaskQueueHttpHeader) ProtoMessage() {} +func (*TaskQueueHttpHeader) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *TaskQueueHttpHeader) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *TaskQueueHttpHeader) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type TaskQueueMode struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueMode) Reset() { *m = TaskQueueMode{} } +func (m *TaskQueueMode) String() string { return proto.CompactTextString(m) } +func (*TaskQueueMode) ProtoMessage() {} +func (*TaskQueueMode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +type TaskQueueAddRequest struct { + QueueName []byte `protobuf:"bytes,1,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + TaskName []byte `protobuf:"bytes,2,req,name=task_name,json=taskName" json:"task_name,omitempty"` + EtaUsec *int64 `protobuf:"varint,3,req,name=eta_usec,json=etaUsec" json:"eta_usec,omitempty"` + Method *TaskQueueAddRequest_RequestMethod `protobuf:"varint,5,opt,name=method,enum=appengine.TaskQueueAddRequest_RequestMethod,def=2" json:"method,omitempty"` + Url []byte `protobuf:"bytes,4,opt,name=url" json:"url,omitempty"` + Header []*TaskQueueAddRequest_Header `protobuf:"group,6,rep,name=Header,json=header" json:"header,omitempty"` + Body []byte `protobuf:"bytes,9,opt,name=body" json:"body,omitempty"` + Transaction *appengine.Transaction `protobuf:"bytes,10,opt,name=transaction" json:"transaction,omitempty"` + AppId []byte `protobuf:"bytes,11,opt,name=app_id,json=appId" json:"app_id,omitempty"` + Crontimetable *TaskQueueAddRequest_CronTimetable `protobuf:"group,12,opt,name=CronTimetable,json=crontimetable" json:"crontimetable,omitempty"` + Description []byte `protobuf:"bytes,15,opt,name=description" json:"description,omitempty"` + Payload *TaskPayload `protobuf:"bytes,16,opt,name=payload" json:"payload,omitempty"` + RetryParameters *TaskQueueRetryParameters `protobuf:"bytes,17,opt,name=retry_parameters,json=retryParameters" json:"retry_parameters,omitempty"` + Mode *TaskQueueMode_Mode `protobuf:"varint,18,opt,name=mode,enum=appengine.TaskQueueMode_Mode,def=0" json:"mode,omitempty"` + Tag []byte `protobuf:"bytes,19,opt,name=tag" json:"tag,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueAddRequest) Reset() { *m = TaskQueueAddRequest{} } +func (m *TaskQueueAddRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueAddRequest) ProtoMessage() {} +func (*TaskQueueAddRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +const Default_TaskQueueAddRequest_Method TaskQueueAddRequest_RequestMethod = TaskQueueAddRequest_POST +const Default_TaskQueueAddRequest_Mode TaskQueueMode_Mode = TaskQueueMode_PUSH + +func (m *TaskQueueAddRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueAddRequest) GetTaskName() []byte { + if m != nil { + return m.TaskName + } + return nil +} + +func (m *TaskQueueAddRequest) GetEtaUsec() int64 { + if m != nil && m.EtaUsec != nil { + return *m.EtaUsec + } + return 0 +} + +func (m *TaskQueueAddRequest) GetMethod() TaskQueueAddRequest_RequestMethod { + if m != nil && m.Method != nil { + return *m.Method + } + return Default_TaskQueueAddRequest_Method +} + +func (m *TaskQueueAddRequest) GetUrl() []byte { + if m != nil { + return m.Url + } + return nil +} + +func (m *TaskQueueAddRequest) GetHeader() []*TaskQueueAddRequest_Header { + if m != nil { + return m.Header + } + return nil +} + +func (m *TaskQueueAddRequest) GetBody() []byte { + if m != nil { + return m.Body + } + return nil +} + +func (m *TaskQueueAddRequest) GetTransaction() *appengine.Transaction { + if m != nil { + return m.Transaction + } + return nil +} + +func (m *TaskQueueAddRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueAddRequest) GetCrontimetable() *TaskQueueAddRequest_CronTimetable { + if m != nil { + return m.Crontimetable + } + return nil +} + +func (m *TaskQueueAddRequest) GetDescription() []byte { + if m != nil { + return m.Description + } + return nil +} + +func (m *TaskQueueAddRequest) GetPayload() *TaskPayload { + if m != nil { + return m.Payload + } + return nil +} + +func (m *TaskQueueAddRequest) GetRetryParameters() *TaskQueueRetryParameters { + if m != nil { + return m.RetryParameters + } + return nil +} + +func (m *TaskQueueAddRequest) GetMode() TaskQueueMode_Mode { + if m != nil && m.Mode != nil { + return *m.Mode + } + return Default_TaskQueueAddRequest_Mode +} + +func (m *TaskQueueAddRequest) GetTag() []byte { + if m != nil { + return m.Tag + } + return nil +} + +type TaskQueueAddRequest_Header struct { + Key []byte `protobuf:"bytes,7,req,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,8,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueAddRequest_Header) Reset() { *m = TaskQueueAddRequest_Header{} } +func (m *TaskQueueAddRequest_Header) String() string { return proto.CompactTextString(m) } +func (*TaskQueueAddRequest_Header) ProtoMessage() {} +func (*TaskQueueAddRequest_Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6, 0} } + +func (m *TaskQueueAddRequest_Header) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *TaskQueueAddRequest_Header) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type TaskQueueAddRequest_CronTimetable struct { + Schedule []byte `protobuf:"bytes,13,req,name=schedule" json:"schedule,omitempty"` + Timezone []byte `protobuf:"bytes,14,req,name=timezone" json:"timezone,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueAddRequest_CronTimetable) Reset() { *m = TaskQueueAddRequest_CronTimetable{} } +func (m *TaskQueueAddRequest_CronTimetable) String() string { return proto.CompactTextString(m) } +func (*TaskQueueAddRequest_CronTimetable) ProtoMessage() {} +func (*TaskQueueAddRequest_CronTimetable) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{6, 1} +} + +func (m *TaskQueueAddRequest_CronTimetable) GetSchedule() []byte { + if m != nil { + return m.Schedule + } + return nil +} + +func (m *TaskQueueAddRequest_CronTimetable) GetTimezone() []byte { + if m != nil { + return m.Timezone + } + return nil +} + +type TaskQueueAddResponse struct { + ChosenTaskName []byte `protobuf:"bytes,1,opt,name=chosen_task_name,json=chosenTaskName" json:"chosen_task_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueAddResponse) Reset() { *m = TaskQueueAddResponse{} } +func (m *TaskQueueAddResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueAddResponse) ProtoMessage() {} +func (*TaskQueueAddResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *TaskQueueAddResponse) GetChosenTaskName() []byte { + if m != nil { + return m.ChosenTaskName + } + return nil +} + +type TaskQueueBulkAddRequest struct { + AddRequest []*TaskQueueAddRequest `protobuf:"bytes,1,rep,name=add_request,json=addRequest" json:"add_request,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueBulkAddRequest) Reset() { *m = TaskQueueBulkAddRequest{} } +func (m *TaskQueueBulkAddRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueBulkAddRequest) ProtoMessage() {} +func (*TaskQueueBulkAddRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *TaskQueueBulkAddRequest) GetAddRequest() []*TaskQueueAddRequest { + if m != nil { + return m.AddRequest + } + return nil +} + +type TaskQueueBulkAddResponse struct { + Taskresult []*TaskQueueBulkAddResponse_TaskResult `protobuf:"group,1,rep,name=TaskResult,json=taskresult" json:"taskresult,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueBulkAddResponse) Reset() { *m = TaskQueueBulkAddResponse{} } +func (m *TaskQueueBulkAddResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueBulkAddResponse) ProtoMessage() {} +func (*TaskQueueBulkAddResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *TaskQueueBulkAddResponse) GetTaskresult() []*TaskQueueBulkAddResponse_TaskResult { + if m != nil { + return m.Taskresult + } + return nil +} + +type TaskQueueBulkAddResponse_TaskResult struct { + Result *TaskQueueServiceError_ErrorCode `protobuf:"varint,2,req,name=result,enum=appengine.TaskQueueServiceError_ErrorCode" json:"result,omitempty"` + ChosenTaskName []byte `protobuf:"bytes,3,opt,name=chosen_task_name,json=chosenTaskName" json:"chosen_task_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueBulkAddResponse_TaskResult) Reset() { *m = TaskQueueBulkAddResponse_TaskResult{} } +func (m *TaskQueueBulkAddResponse_TaskResult) String() string { return proto.CompactTextString(m) } +func (*TaskQueueBulkAddResponse_TaskResult) ProtoMessage() {} +func (*TaskQueueBulkAddResponse_TaskResult) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{9, 0} +} + +func (m *TaskQueueBulkAddResponse_TaskResult) GetResult() TaskQueueServiceError_ErrorCode { + if m != nil && m.Result != nil { + return *m.Result + } + return TaskQueueServiceError_OK +} + +func (m *TaskQueueBulkAddResponse_TaskResult) GetChosenTaskName() []byte { + if m != nil { + return m.ChosenTaskName + } + return nil +} + +type TaskQueueDeleteRequest struct { + QueueName []byte `protobuf:"bytes,1,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + TaskName [][]byte `protobuf:"bytes,2,rep,name=task_name,json=taskName" json:"task_name,omitempty"` + AppId []byte `protobuf:"bytes,3,opt,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueDeleteRequest) Reset() { *m = TaskQueueDeleteRequest{} } +func (m *TaskQueueDeleteRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueDeleteRequest) ProtoMessage() {} +func (*TaskQueueDeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *TaskQueueDeleteRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueDeleteRequest) GetTaskName() [][]byte { + if m != nil { + return m.TaskName + } + return nil +} + +func (m *TaskQueueDeleteRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type TaskQueueDeleteResponse struct { + Result []TaskQueueServiceError_ErrorCode `protobuf:"varint,3,rep,name=result,enum=appengine.TaskQueueServiceError_ErrorCode" json:"result,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueDeleteResponse) Reset() { *m = TaskQueueDeleteResponse{} } +func (m *TaskQueueDeleteResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueDeleteResponse) ProtoMessage() {} +func (*TaskQueueDeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *TaskQueueDeleteResponse) GetResult() []TaskQueueServiceError_ErrorCode { + if m != nil { + return m.Result + } + return nil +} + +type TaskQueueForceRunRequest struct { + AppId []byte `protobuf:"bytes,1,opt,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + TaskName []byte `protobuf:"bytes,3,req,name=task_name,json=taskName" json:"task_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueForceRunRequest) Reset() { *m = TaskQueueForceRunRequest{} } +func (m *TaskQueueForceRunRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueForceRunRequest) ProtoMessage() {} +func (*TaskQueueForceRunRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *TaskQueueForceRunRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueForceRunRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueForceRunRequest) GetTaskName() []byte { + if m != nil { + return m.TaskName + } + return nil +} + +type TaskQueueForceRunResponse struct { + Result *TaskQueueServiceError_ErrorCode `protobuf:"varint,3,req,name=result,enum=appengine.TaskQueueServiceError_ErrorCode" json:"result,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueForceRunResponse) Reset() { *m = TaskQueueForceRunResponse{} } +func (m *TaskQueueForceRunResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueForceRunResponse) ProtoMessage() {} +func (*TaskQueueForceRunResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *TaskQueueForceRunResponse) GetResult() TaskQueueServiceError_ErrorCode { + if m != nil && m.Result != nil { + return *m.Result + } + return TaskQueueServiceError_OK +} + +type TaskQueueUpdateQueueRequest struct { + AppId []byte `protobuf:"bytes,1,opt,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + BucketRefillPerSecond *float64 `protobuf:"fixed64,3,req,name=bucket_refill_per_second,json=bucketRefillPerSecond" json:"bucket_refill_per_second,omitempty"` + BucketCapacity *int32 `protobuf:"varint,4,req,name=bucket_capacity,json=bucketCapacity" json:"bucket_capacity,omitempty"` + UserSpecifiedRate *string `protobuf:"bytes,5,opt,name=user_specified_rate,json=userSpecifiedRate" json:"user_specified_rate,omitempty"` + RetryParameters *TaskQueueRetryParameters `protobuf:"bytes,6,opt,name=retry_parameters,json=retryParameters" json:"retry_parameters,omitempty"` + MaxConcurrentRequests *int32 `protobuf:"varint,7,opt,name=max_concurrent_requests,json=maxConcurrentRequests" json:"max_concurrent_requests,omitempty"` + Mode *TaskQueueMode_Mode `protobuf:"varint,8,opt,name=mode,enum=appengine.TaskQueueMode_Mode,def=0" json:"mode,omitempty"` + Acl *TaskQueueAcl `protobuf:"bytes,9,opt,name=acl" json:"acl,omitempty"` + HeaderOverride []*TaskQueueHttpHeader `protobuf:"bytes,10,rep,name=header_override,json=headerOverride" json:"header_override,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueUpdateQueueRequest) Reset() { *m = TaskQueueUpdateQueueRequest{} } +func (m *TaskQueueUpdateQueueRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueUpdateQueueRequest) ProtoMessage() {} +func (*TaskQueueUpdateQueueRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +const Default_TaskQueueUpdateQueueRequest_Mode TaskQueueMode_Mode = TaskQueueMode_PUSH + +func (m *TaskQueueUpdateQueueRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueUpdateQueueRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueUpdateQueueRequest) GetBucketRefillPerSecond() float64 { + if m != nil && m.BucketRefillPerSecond != nil { + return *m.BucketRefillPerSecond + } + return 0 +} + +func (m *TaskQueueUpdateQueueRequest) GetBucketCapacity() int32 { + if m != nil && m.BucketCapacity != nil { + return *m.BucketCapacity + } + return 0 +} + +func (m *TaskQueueUpdateQueueRequest) GetUserSpecifiedRate() string { + if m != nil && m.UserSpecifiedRate != nil { + return *m.UserSpecifiedRate + } + return "" +} + +func (m *TaskQueueUpdateQueueRequest) GetRetryParameters() *TaskQueueRetryParameters { + if m != nil { + return m.RetryParameters + } + return nil +} + +func (m *TaskQueueUpdateQueueRequest) GetMaxConcurrentRequests() int32 { + if m != nil && m.MaxConcurrentRequests != nil { + return *m.MaxConcurrentRequests + } + return 0 +} + +func (m *TaskQueueUpdateQueueRequest) GetMode() TaskQueueMode_Mode { + if m != nil && m.Mode != nil { + return *m.Mode + } + return Default_TaskQueueUpdateQueueRequest_Mode +} + +func (m *TaskQueueUpdateQueueRequest) GetAcl() *TaskQueueAcl { + if m != nil { + return m.Acl + } + return nil +} + +func (m *TaskQueueUpdateQueueRequest) GetHeaderOverride() []*TaskQueueHttpHeader { + if m != nil { + return m.HeaderOverride + } + return nil +} + +type TaskQueueUpdateQueueResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueUpdateQueueResponse) Reset() { *m = TaskQueueUpdateQueueResponse{} } +func (m *TaskQueueUpdateQueueResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueUpdateQueueResponse) ProtoMessage() {} +func (*TaskQueueUpdateQueueResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +type TaskQueueFetchQueuesRequest struct { + AppId []byte `protobuf:"bytes,1,opt,name=app_id,json=appId" json:"app_id,omitempty"` + MaxRows *int32 `protobuf:"varint,2,req,name=max_rows,json=maxRows" json:"max_rows,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchQueuesRequest) Reset() { *m = TaskQueueFetchQueuesRequest{} } +func (m *TaskQueueFetchQueuesRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueFetchQueuesRequest) ProtoMessage() {} +func (*TaskQueueFetchQueuesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *TaskQueueFetchQueuesRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueFetchQueuesRequest) GetMaxRows() int32 { + if m != nil && m.MaxRows != nil { + return *m.MaxRows + } + return 0 +} + +type TaskQueueFetchQueuesResponse struct { + Queue []*TaskQueueFetchQueuesResponse_Queue `protobuf:"group,1,rep,name=Queue,json=queue" json:"queue,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchQueuesResponse) Reset() { *m = TaskQueueFetchQueuesResponse{} } +func (m *TaskQueueFetchQueuesResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueFetchQueuesResponse) ProtoMessage() {} +func (*TaskQueueFetchQueuesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *TaskQueueFetchQueuesResponse) GetQueue() []*TaskQueueFetchQueuesResponse_Queue { + if m != nil { + return m.Queue + } + return nil +} + +type TaskQueueFetchQueuesResponse_Queue struct { + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + BucketRefillPerSecond *float64 `protobuf:"fixed64,3,req,name=bucket_refill_per_second,json=bucketRefillPerSecond" json:"bucket_refill_per_second,omitempty"` + BucketCapacity *float64 `protobuf:"fixed64,4,req,name=bucket_capacity,json=bucketCapacity" json:"bucket_capacity,omitempty"` + UserSpecifiedRate *string `protobuf:"bytes,5,opt,name=user_specified_rate,json=userSpecifiedRate" json:"user_specified_rate,omitempty"` + Paused *bool `protobuf:"varint,6,req,name=paused,def=0" json:"paused,omitempty"` + RetryParameters *TaskQueueRetryParameters `protobuf:"bytes,7,opt,name=retry_parameters,json=retryParameters" json:"retry_parameters,omitempty"` + MaxConcurrentRequests *int32 `protobuf:"varint,8,opt,name=max_concurrent_requests,json=maxConcurrentRequests" json:"max_concurrent_requests,omitempty"` + Mode *TaskQueueMode_Mode `protobuf:"varint,9,opt,name=mode,enum=appengine.TaskQueueMode_Mode,def=0" json:"mode,omitempty"` + Acl *TaskQueueAcl `protobuf:"bytes,10,opt,name=acl" json:"acl,omitempty"` + HeaderOverride []*TaskQueueHttpHeader `protobuf:"bytes,11,rep,name=header_override,json=headerOverride" json:"header_override,omitempty"` + CreatorName *string `protobuf:"bytes,12,opt,name=creator_name,json=creatorName,def=apphosting" json:"creator_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchQueuesResponse_Queue) Reset() { *m = TaskQueueFetchQueuesResponse_Queue{} } +func (m *TaskQueueFetchQueuesResponse_Queue) String() string { return proto.CompactTextString(m) } +func (*TaskQueueFetchQueuesResponse_Queue) ProtoMessage() {} +func (*TaskQueueFetchQueuesResponse_Queue) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{17, 0} +} + +const Default_TaskQueueFetchQueuesResponse_Queue_Paused bool = false +const Default_TaskQueueFetchQueuesResponse_Queue_Mode TaskQueueMode_Mode = TaskQueueMode_PUSH +const Default_TaskQueueFetchQueuesResponse_Queue_CreatorName string = "apphosting" + +func (m *TaskQueueFetchQueuesResponse_Queue) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetBucketRefillPerSecond() float64 { + if m != nil && m.BucketRefillPerSecond != nil { + return *m.BucketRefillPerSecond + } + return 0 +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetBucketCapacity() float64 { + if m != nil && m.BucketCapacity != nil { + return *m.BucketCapacity + } + return 0 +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetUserSpecifiedRate() string { + if m != nil && m.UserSpecifiedRate != nil { + return *m.UserSpecifiedRate + } + return "" +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetPaused() bool { + if m != nil && m.Paused != nil { + return *m.Paused + } + return Default_TaskQueueFetchQueuesResponse_Queue_Paused +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetRetryParameters() *TaskQueueRetryParameters { + if m != nil { + return m.RetryParameters + } + return nil +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetMaxConcurrentRequests() int32 { + if m != nil && m.MaxConcurrentRequests != nil { + return *m.MaxConcurrentRequests + } + return 0 +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetMode() TaskQueueMode_Mode { + if m != nil && m.Mode != nil { + return *m.Mode + } + return Default_TaskQueueFetchQueuesResponse_Queue_Mode +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetAcl() *TaskQueueAcl { + if m != nil { + return m.Acl + } + return nil +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetHeaderOverride() []*TaskQueueHttpHeader { + if m != nil { + return m.HeaderOverride + } + return nil +} + +func (m *TaskQueueFetchQueuesResponse_Queue) GetCreatorName() string { + if m != nil && m.CreatorName != nil { + return *m.CreatorName + } + return Default_TaskQueueFetchQueuesResponse_Queue_CreatorName +} + +type TaskQueueFetchQueueStatsRequest struct { + AppId []byte `protobuf:"bytes,1,opt,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName [][]byte `protobuf:"bytes,2,rep,name=queue_name,json=queueName" json:"queue_name,omitempty"` + MaxNumTasks *int32 `protobuf:"varint,3,opt,name=max_num_tasks,json=maxNumTasks,def=0" json:"max_num_tasks,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchQueueStatsRequest) Reset() { *m = TaskQueueFetchQueueStatsRequest{} } +func (m *TaskQueueFetchQueueStatsRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueFetchQueueStatsRequest) ProtoMessage() {} +func (*TaskQueueFetchQueueStatsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{18} +} + +const Default_TaskQueueFetchQueueStatsRequest_MaxNumTasks int32 = 0 + +func (m *TaskQueueFetchQueueStatsRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueFetchQueueStatsRequest) GetQueueName() [][]byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueFetchQueueStatsRequest) GetMaxNumTasks() int32 { + if m != nil && m.MaxNumTasks != nil { + return *m.MaxNumTasks + } + return Default_TaskQueueFetchQueueStatsRequest_MaxNumTasks +} + +type TaskQueueScannerQueueInfo struct { + ExecutedLastMinute *int64 `protobuf:"varint,1,req,name=executed_last_minute,json=executedLastMinute" json:"executed_last_minute,omitempty"` + ExecutedLastHour *int64 `protobuf:"varint,2,req,name=executed_last_hour,json=executedLastHour" json:"executed_last_hour,omitempty"` + SamplingDurationSeconds *float64 `protobuf:"fixed64,3,req,name=sampling_duration_seconds,json=samplingDurationSeconds" json:"sampling_duration_seconds,omitempty"` + RequestsInFlight *int32 `protobuf:"varint,4,opt,name=requests_in_flight,json=requestsInFlight" json:"requests_in_flight,omitempty"` + EnforcedRate *float64 `protobuf:"fixed64,5,opt,name=enforced_rate,json=enforcedRate" json:"enforced_rate,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueScannerQueueInfo) Reset() { *m = TaskQueueScannerQueueInfo{} } +func (m *TaskQueueScannerQueueInfo) String() string { return proto.CompactTextString(m) } +func (*TaskQueueScannerQueueInfo) ProtoMessage() {} +func (*TaskQueueScannerQueueInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +func (m *TaskQueueScannerQueueInfo) GetExecutedLastMinute() int64 { + if m != nil && m.ExecutedLastMinute != nil { + return *m.ExecutedLastMinute + } + return 0 +} + +func (m *TaskQueueScannerQueueInfo) GetExecutedLastHour() int64 { + if m != nil && m.ExecutedLastHour != nil { + return *m.ExecutedLastHour + } + return 0 +} + +func (m *TaskQueueScannerQueueInfo) GetSamplingDurationSeconds() float64 { + if m != nil && m.SamplingDurationSeconds != nil { + return *m.SamplingDurationSeconds + } + return 0 +} + +func (m *TaskQueueScannerQueueInfo) GetRequestsInFlight() int32 { + if m != nil && m.RequestsInFlight != nil { + return *m.RequestsInFlight + } + return 0 +} + +func (m *TaskQueueScannerQueueInfo) GetEnforcedRate() float64 { + if m != nil && m.EnforcedRate != nil { + return *m.EnforcedRate + } + return 0 +} + +type TaskQueueFetchQueueStatsResponse struct { + Queuestats []*TaskQueueFetchQueueStatsResponse_QueueStats `protobuf:"group,1,rep,name=QueueStats,json=queuestats" json:"queuestats,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchQueueStatsResponse) Reset() { *m = TaskQueueFetchQueueStatsResponse{} } +func (m *TaskQueueFetchQueueStatsResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueFetchQueueStatsResponse) ProtoMessage() {} +func (*TaskQueueFetchQueueStatsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{20} +} + +func (m *TaskQueueFetchQueueStatsResponse) GetQueuestats() []*TaskQueueFetchQueueStatsResponse_QueueStats { + if m != nil { + return m.Queuestats + } + return nil +} + +type TaskQueueFetchQueueStatsResponse_QueueStats struct { + NumTasks *int32 `protobuf:"varint,2,req,name=num_tasks,json=numTasks" json:"num_tasks,omitempty"` + OldestEtaUsec *int64 `protobuf:"varint,3,req,name=oldest_eta_usec,json=oldestEtaUsec" json:"oldest_eta_usec,omitempty"` + ScannerInfo *TaskQueueScannerQueueInfo `protobuf:"bytes,4,opt,name=scanner_info,json=scannerInfo" json:"scanner_info,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchQueueStatsResponse_QueueStats) Reset() { + *m = TaskQueueFetchQueueStatsResponse_QueueStats{} +} +func (m *TaskQueueFetchQueueStatsResponse_QueueStats) String() string { + return proto.CompactTextString(m) +} +func (*TaskQueueFetchQueueStatsResponse_QueueStats) ProtoMessage() {} +func (*TaskQueueFetchQueueStatsResponse_QueueStats) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{20, 0} +} + +func (m *TaskQueueFetchQueueStatsResponse_QueueStats) GetNumTasks() int32 { + if m != nil && m.NumTasks != nil { + return *m.NumTasks + } + return 0 +} + +func (m *TaskQueueFetchQueueStatsResponse_QueueStats) GetOldestEtaUsec() int64 { + if m != nil && m.OldestEtaUsec != nil { + return *m.OldestEtaUsec + } + return 0 +} + +func (m *TaskQueueFetchQueueStatsResponse_QueueStats) GetScannerInfo() *TaskQueueScannerQueueInfo { + if m != nil { + return m.ScannerInfo + } + return nil +} + +type TaskQueuePauseQueueRequest struct { + AppId []byte `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + Pause *bool `protobuf:"varint,3,req,name=pause" json:"pause,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueuePauseQueueRequest) Reset() { *m = TaskQueuePauseQueueRequest{} } +func (m *TaskQueuePauseQueueRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueuePauseQueueRequest) ProtoMessage() {} +func (*TaskQueuePauseQueueRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +func (m *TaskQueuePauseQueueRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueuePauseQueueRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueuePauseQueueRequest) GetPause() bool { + if m != nil && m.Pause != nil { + return *m.Pause + } + return false +} + +type TaskQueuePauseQueueResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueuePauseQueueResponse) Reset() { *m = TaskQueuePauseQueueResponse{} } +func (m *TaskQueuePauseQueueResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueuePauseQueueResponse) ProtoMessage() {} +func (*TaskQueuePauseQueueResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } + +type TaskQueuePurgeQueueRequest struct { + AppId []byte `protobuf:"bytes,1,opt,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueuePurgeQueueRequest) Reset() { *m = TaskQueuePurgeQueueRequest{} } +func (m *TaskQueuePurgeQueueRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueuePurgeQueueRequest) ProtoMessage() {} +func (*TaskQueuePurgeQueueRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +func (m *TaskQueuePurgeQueueRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueuePurgeQueueRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +type TaskQueuePurgeQueueResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueuePurgeQueueResponse) Reset() { *m = TaskQueuePurgeQueueResponse{} } +func (m *TaskQueuePurgeQueueResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueuePurgeQueueResponse) ProtoMessage() {} +func (*TaskQueuePurgeQueueResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +type TaskQueueDeleteQueueRequest struct { + AppId []byte `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueDeleteQueueRequest) Reset() { *m = TaskQueueDeleteQueueRequest{} } +func (m *TaskQueueDeleteQueueRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueDeleteQueueRequest) ProtoMessage() {} +func (*TaskQueueDeleteQueueRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } + +func (m *TaskQueueDeleteQueueRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueDeleteQueueRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +type TaskQueueDeleteQueueResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueDeleteQueueResponse) Reset() { *m = TaskQueueDeleteQueueResponse{} } +func (m *TaskQueueDeleteQueueResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueDeleteQueueResponse) ProtoMessage() {} +func (*TaskQueueDeleteQueueResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } + +type TaskQueueDeleteGroupRequest struct { + AppId []byte `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueDeleteGroupRequest) Reset() { *m = TaskQueueDeleteGroupRequest{} } +func (m *TaskQueueDeleteGroupRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueDeleteGroupRequest) ProtoMessage() {} +func (*TaskQueueDeleteGroupRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } + +func (m *TaskQueueDeleteGroupRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +type TaskQueueDeleteGroupResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueDeleteGroupResponse) Reset() { *m = TaskQueueDeleteGroupResponse{} } +func (m *TaskQueueDeleteGroupResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueDeleteGroupResponse) ProtoMessage() {} +func (*TaskQueueDeleteGroupResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +type TaskQueueQueryTasksRequest struct { + AppId []byte `protobuf:"bytes,1,opt,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + StartTaskName []byte `protobuf:"bytes,3,opt,name=start_task_name,json=startTaskName" json:"start_task_name,omitempty"` + StartEtaUsec *int64 `protobuf:"varint,4,opt,name=start_eta_usec,json=startEtaUsec" json:"start_eta_usec,omitempty"` + StartTag []byte `protobuf:"bytes,6,opt,name=start_tag,json=startTag" json:"start_tag,omitempty"` + MaxRows *int32 `protobuf:"varint,5,opt,name=max_rows,json=maxRows,def=1" json:"max_rows,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryTasksRequest) Reset() { *m = TaskQueueQueryTasksRequest{} } +func (m *TaskQueueQueryTasksRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryTasksRequest) ProtoMessage() {} +func (*TaskQueueQueryTasksRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } + +const Default_TaskQueueQueryTasksRequest_MaxRows int32 = 1 + +func (m *TaskQueueQueryTasksRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueQueryTasksRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueQueryTasksRequest) GetStartTaskName() []byte { + if m != nil { + return m.StartTaskName + } + return nil +} + +func (m *TaskQueueQueryTasksRequest) GetStartEtaUsec() int64 { + if m != nil && m.StartEtaUsec != nil { + return *m.StartEtaUsec + } + return 0 +} + +func (m *TaskQueueQueryTasksRequest) GetStartTag() []byte { + if m != nil { + return m.StartTag + } + return nil +} + +func (m *TaskQueueQueryTasksRequest) GetMaxRows() int32 { + if m != nil && m.MaxRows != nil { + return *m.MaxRows + } + return Default_TaskQueueQueryTasksRequest_MaxRows +} + +type TaskQueueQueryTasksResponse struct { + Task []*TaskQueueQueryTasksResponse_Task `protobuf:"group,1,rep,name=Task,json=task" json:"task,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryTasksResponse) Reset() { *m = TaskQueueQueryTasksResponse{} } +func (m *TaskQueueQueryTasksResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryTasksResponse) ProtoMessage() {} +func (*TaskQueueQueryTasksResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } + +func (m *TaskQueueQueryTasksResponse) GetTask() []*TaskQueueQueryTasksResponse_Task { + if m != nil { + return m.Task + } + return nil +} + +type TaskQueueQueryTasksResponse_Task struct { + TaskName []byte `protobuf:"bytes,2,req,name=task_name,json=taskName" json:"task_name,omitempty"` + EtaUsec *int64 `protobuf:"varint,3,req,name=eta_usec,json=etaUsec" json:"eta_usec,omitempty"` + Url []byte `protobuf:"bytes,4,opt,name=url" json:"url,omitempty"` + Method *TaskQueueQueryTasksResponse_Task_RequestMethod `protobuf:"varint,5,opt,name=method,enum=appengine.TaskQueueQueryTasksResponse_Task_RequestMethod" json:"method,omitempty"` + RetryCount *int32 `protobuf:"varint,6,opt,name=retry_count,json=retryCount,def=0" json:"retry_count,omitempty"` + Header []*TaskQueueQueryTasksResponse_Task_Header `protobuf:"group,7,rep,name=Header,json=header" json:"header,omitempty"` + BodySize *int32 `protobuf:"varint,10,opt,name=body_size,json=bodySize" json:"body_size,omitempty"` + Body []byte `protobuf:"bytes,11,opt,name=body" json:"body,omitempty"` + CreationTimeUsec *int64 `protobuf:"varint,12,req,name=creation_time_usec,json=creationTimeUsec" json:"creation_time_usec,omitempty"` + Crontimetable *TaskQueueQueryTasksResponse_Task_CronTimetable `protobuf:"group,13,opt,name=CronTimetable,json=crontimetable" json:"crontimetable,omitempty"` + Runlog *TaskQueueQueryTasksResponse_Task_RunLog `protobuf:"group,16,opt,name=RunLog,json=runlog" json:"runlog,omitempty"` + Description []byte `protobuf:"bytes,21,opt,name=description" json:"description,omitempty"` + Payload *TaskPayload `protobuf:"bytes,22,opt,name=payload" json:"payload,omitempty"` + RetryParameters *TaskQueueRetryParameters `protobuf:"bytes,23,opt,name=retry_parameters,json=retryParameters" json:"retry_parameters,omitempty"` + FirstTryUsec *int64 `protobuf:"varint,24,opt,name=first_try_usec,json=firstTryUsec" json:"first_try_usec,omitempty"` + Tag []byte `protobuf:"bytes,25,opt,name=tag" json:"tag,omitempty"` + ExecutionCount *int32 `protobuf:"varint,26,opt,name=execution_count,json=executionCount,def=0" json:"execution_count,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryTasksResponse_Task) Reset() { *m = TaskQueueQueryTasksResponse_Task{} } +func (m *TaskQueueQueryTasksResponse_Task) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryTasksResponse_Task) ProtoMessage() {} +func (*TaskQueueQueryTasksResponse_Task) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{30, 0} +} + +const Default_TaskQueueQueryTasksResponse_Task_RetryCount int32 = 0 +const Default_TaskQueueQueryTasksResponse_Task_ExecutionCount int32 = 0 + +func (m *TaskQueueQueryTasksResponse_Task) GetTaskName() []byte { + if m != nil { + return m.TaskName + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetEtaUsec() int64 { + if m != nil && m.EtaUsec != nil { + return *m.EtaUsec + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task) GetUrl() []byte { + if m != nil { + return m.Url + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetMethod() TaskQueueQueryTasksResponse_Task_RequestMethod { + if m != nil && m.Method != nil { + return *m.Method + } + return TaskQueueQueryTasksResponse_Task_GET +} + +func (m *TaskQueueQueryTasksResponse_Task) GetRetryCount() int32 { + if m != nil && m.RetryCount != nil { + return *m.RetryCount + } + return Default_TaskQueueQueryTasksResponse_Task_RetryCount +} + +func (m *TaskQueueQueryTasksResponse_Task) GetHeader() []*TaskQueueQueryTasksResponse_Task_Header { + if m != nil { + return m.Header + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetBodySize() int32 { + if m != nil && m.BodySize != nil { + return *m.BodySize + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task) GetBody() []byte { + if m != nil { + return m.Body + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetCreationTimeUsec() int64 { + if m != nil && m.CreationTimeUsec != nil { + return *m.CreationTimeUsec + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task) GetCrontimetable() *TaskQueueQueryTasksResponse_Task_CronTimetable { + if m != nil { + return m.Crontimetable + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetRunlog() *TaskQueueQueryTasksResponse_Task_RunLog { + if m != nil { + return m.Runlog + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetDescription() []byte { + if m != nil { + return m.Description + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetPayload() *TaskPayload { + if m != nil { + return m.Payload + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetRetryParameters() *TaskQueueRetryParameters { + if m != nil { + return m.RetryParameters + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetFirstTryUsec() int64 { + if m != nil && m.FirstTryUsec != nil { + return *m.FirstTryUsec + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task) GetTag() []byte { + if m != nil { + return m.Tag + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task) GetExecutionCount() int32 { + if m != nil && m.ExecutionCount != nil { + return *m.ExecutionCount + } + return Default_TaskQueueQueryTasksResponse_Task_ExecutionCount +} + +type TaskQueueQueryTasksResponse_Task_Header struct { + Key []byte `protobuf:"bytes,8,req,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,9,req,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryTasksResponse_Task_Header) Reset() { + *m = TaskQueueQueryTasksResponse_Task_Header{} +} +func (m *TaskQueueQueryTasksResponse_Task_Header) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryTasksResponse_Task_Header) ProtoMessage() {} +func (*TaskQueueQueryTasksResponse_Task_Header) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{30, 0, 0} +} + +func (m *TaskQueueQueryTasksResponse_Task_Header) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task_Header) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type TaskQueueQueryTasksResponse_Task_CronTimetable struct { + Schedule []byte `protobuf:"bytes,14,req,name=schedule" json:"schedule,omitempty"` + Timezone []byte `protobuf:"bytes,15,req,name=timezone" json:"timezone,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryTasksResponse_Task_CronTimetable) Reset() { + *m = TaskQueueQueryTasksResponse_Task_CronTimetable{} +} +func (m *TaskQueueQueryTasksResponse_Task_CronTimetable) String() string { + return proto.CompactTextString(m) +} +func (*TaskQueueQueryTasksResponse_Task_CronTimetable) ProtoMessage() {} +func (*TaskQueueQueryTasksResponse_Task_CronTimetable) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{30, 0, 1} +} + +func (m *TaskQueueQueryTasksResponse_Task_CronTimetable) GetSchedule() []byte { + if m != nil { + return m.Schedule + } + return nil +} + +func (m *TaskQueueQueryTasksResponse_Task_CronTimetable) GetTimezone() []byte { + if m != nil { + return m.Timezone + } + return nil +} + +type TaskQueueQueryTasksResponse_Task_RunLog struct { + DispatchedUsec *int64 `protobuf:"varint,17,req,name=dispatched_usec,json=dispatchedUsec" json:"dispatched_usec,omitempty"` + LagUsec *int64 `protobuf:"varint,18,req,name=lag_usec,json=lagUsec" json:"lag_usec,omitempty"` + ElapsedUsec *int64 `protobuf:"varint,19,req,name=elapsed_usec,json=elapsedUsec" json:"elapsed_usec,omitempty"` + ResponseCode *int64 `protobuf:"varint,20,opt,name=response_code,json=responseCode" json:"response_code,omitempty"` + RetryReason *string `protobuf:"bytes,27,opt,name=retry_reason,json=retryReason" json:"retry_reason,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryTasksResponse_Task_RunLog) Reset() { + *m = TaskQueueQueryTasksResponse_Task_RunLog{} +} +func (m *TaskQueueQueryTasksResponse_Task_RunLog) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryTasksResponse_Task_RunLog) ProtoMessage() {} +func (*TaskQueueQueryTasksResponse_Task_RunLog) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{30, 0, 2} +} + +func (m *TaskQueueQueryTasksResponse_Task_RunLog) GetDispatchedUsec() int64 { + if m != nil && m.DispatchedUsec != nil { + return *m.DispatchedUsec + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task_RunLog) GetLagUsec() int64 { + if m != nil && m.LagUsec != nil { + return *m.LagUsec + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task_RunLog) GetElapsedUsec() int64 { + if m != nil && m.ElapsedUsec != nil { + return *m.ElapsedUsec + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task_RunLog) GetResponseCode() int64 { + if m != nil && m.ResponseCode != nil { + return *m.ResponseCode + } + return 0 +} + +func (m *TaskQueueQueryTasksResponse_Task_RunLog) GetRetryReason() string { + if m != nil && m.RetryReason != nil { + return *m.RetryReason + } + return "" +} + +type TaskQueueFetchTaskRequest struct { + AppId []byte `protobuf:"bytes,1,opt,name=app_id,json=appId" json:"app_id,omitempty"` + QueueName []byte `protobuf:"bytes,2,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + TaskName []byte `protobuf:"bytes,3,req,name=task_name,json=taskName" json:"task_name,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchTaskRequest) Reset() { *m = TaskQueueFetchTaskRequest{} } +func (m *TaskQueueFetchTaskRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueFetchTaskRequest) ProtoMessage() {} +func (*TaskQueueFetchTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +func (m *TaskQueueFetchTaskRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueFetchTaskRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueFetchTaskRequest) GetTaskName() []byte { + if m != nil { + return m.TaskName + } + return nil +} + +type TaskQueueFetchTaskResponse struct { + Task *TaskQueueQueryTasksResponse `protobuf:"bytes,1,req,name=task" json:"task,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueFetchTaskResponse) Reset() { *m = TaskQueueFetchTaskResponse{} } +func (m *TaskQueueFetchTaskResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueFetchTaskResponse) ProtoMessage() {} +func (*TaskQueueFetchTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +func (m *TaskQueueFetchTaskResponse) GetTask() *TaskQueueQueryTasksResponse { + if m != nil { + return m.Task + } + return nil +} + +type TaskQueueUpdateStorageLimitRequest struct { + AppId []byte `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"` + Limit *int64 `protobuf:"varint,2,req,name=limit" json:"limit,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueUpdateStorageLimitRequest) Reset() { *m = TaskQueueUpdateStorageLimitRequest{} } +func (m *TaskQueueUpdateStorageLimitRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueUpdateStorageLimitRequest) ProtoMessage() {} +func (*TaskQueueUpdateStorageLimitRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{33} +} + +func (m *TaskQueueUpdateStorageLimitRequest) GetAppId() []byte { + if m != nil { + return m.AppId + } + return nil +} + +func (m *TaskQueueUpdateStorageLimitRequest) GetLimit() int64 { + if m != nil && m.Limit != nil { + return *m.Limit + } + return 0 +} + +type TaskQueueUpdateStorageLimitResponse struct { + NewLimit *int64 `protobuf:"varint,1,req,name=new_limit,json=newLimit" json:"new_limit,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueUpdateStorageLimitResponse) Reset() { *m = TaskQueueUpdateStorageLimitResponse{} } +func (m *TaskQueueUpdateStorageLimitResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueUpdateStorageLimitResponse) ProtoMessage() {} +func (*TaskQueueUpdateStorageLimitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{34} +} + +func (m *TaskQueueUpdateStorageLimitResponse) GetNewLimit() int64 { + if m != nil && m.NewLimit != nil { + return *m.NewLimit + } + return 0 +} + +type TaskQueueQueryAndOwnTasksRequest struct { + QueueName []byte `protobuf:"bytes,1,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + LeaseSeconds *float64 `protobuf:"fixed64,2,req,name=lease_seconds,json=leaseSeconds" json:"lease_seconds,omitempty"` + MaxTasks *int64 `protobuf:"varint,3,req,name=max_tasks,json=maxTasks" json:"max_tasks,omitempty"` + GroupByTag *bool `protobuf:"varint,4,opt,name=group_by_tag,json=groupByTag,def=0" json:"group_by_tag,omitempty"` + Tag []byte `protobuf:"bytes,5,opt,name=tag" json:"tag,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryAndOwnTasksRequest) Reset() { *m = TaskQueueQueryAndOwnTasksRequest{} } +func (m *TaskQueueQueryAndOwnTasksRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryAndOwnTasksRequest) ProtoMessage() {} +func (*TaskQueueQueryAndOwnTasksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{35} +} + +const Default_TaskQueueQueryAndOwnTasksRequest_GroupByTag bool = false + +func (m *TaskQueueQueryAndOwnTasksRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueQueryAndOwnTasksRequest) GetLeaseSeconds() float64 { + if m != nil && m.LeaseSeconds != nil { + return *m.LeaseSeconds + } + return 0 +} + +func (m *TaskQueueQueryAndOwnTasksRequest) GetMaxTasks() int64 { + if m != nil && m.MaxTasks != nil { + return *m.MaxTasks + } + return 0 +} + +func (m *TaskQueueQueryAndOwnTasksRequest) GetGroupByTag() bool { + if m != nil && m.GroupByTag != nil { + return *m.GroupByTag + } + return Default_TaskQueueQueryAndOwnTasksRequest_GroupByTag +} + +func (m *TaskQueueQueryAndOwnTasksRequest) GetTag() []byte { + if m != nil { + return m.Tag + } + return nil +} + +type TaskQueueQueryAndOwnTasksResponse struct { + Task []*TaskQueueQueryAndOwnTasksResponse_Task `protobuf:"group,1,rep,name=Task,json=task" json:"task,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryAndOwnTasksResponse) Reset() { *m = TaskQueueQueryAndOwnTasksResponse{} } +func (m *TaskQueueQueryAndOwnTasksResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryAndOwnTasksResponse) ProtoMessage() {} +func (*TaskQueueQueryAndOwnTasksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{36} +} + +func (m *TaskQueueQueryAndOwnTasksResponse) GetTask() []*TaskQueueQueryAndOwnTasksResponse_Task { + if m != nil { + return m.Task + } + return nil +} + +type TaskQueueQueryAndOwnTasksResponse_Task struct { + TaskName []byte `protobuf:"bytes,2,req,name=task_name,json=taskName" json:"task_name,omitempty"` + EtaUsec *int64 `protobuf:"varint,3,req,name=eta_usec,json=etaUsec" json:"eta_usec,omitempty"` + RetryCount *int32 `protobuf:"varint,4,opt,name=retry_count,json=retryCount,def=0" json:"retry_count,omitempty"` + Body []byte `protobuf:"bytes,5,opt,name=body" json:"body,omitempty"` + Tag []byte `protobuf:"bytes,6,opt,name=tag" json:"tag,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueQueryAndOwnTasksResponse_Task) Reset() { + *m = TaskQueueQueryAndOwnTasksResponse_Task{} +} +func (m *TaskQueueQueryAndOwnTasksResponse_Task) String() string { return proto.CompactTextString(m) } +func (*TaskQueueQueryAndOwnTasksResponse_Task) ProtoMessage() {} +func (*TaskQueueQueryAndOwnTasksResponse_Task) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{36, 0} +} + +const Default_TaskQueueQueryAndOwnTasksResponse_Task_RetryCount int32 = 0 + +func (m *TaskQueueQueryAndOwnTasksResponse_Task) GetTaskName() []byte { + if m != nil { + return m.TaskName + } + return nil +} + +func (m *TaskQueueQueryAndOwnTasksResponse_Task) GetEtaUsec() int64 { + if m != nil && m.EtaUsec != nil { + return *m.EtaUsec + } + return 0 +} + +func (m *TaskQueueQueryAndOwnTasksResponse_Task) GetRetryCount() int32 { + if m != nil && m.RetryCount != nil { + return *m.RetryCount + } + return Default_TaskQueueQueryAndOwnTasksResponse_Task_RetryCount +} + +func (m *TaskQueueQueryAndOwnTasksResponse_Task) GetBody() []byte { + if m != nil { + return m.Body + } + return nil +} + +func (m *TaskQueueQueryAndOwnTasksResponse_Task) GetTag() []byte { + if m != nil { + return m.Tag + } + return nil +} + +type TaskQueueModifyTaskLeaseRequest struct { + QueueName []byte `protobuf:"bytes,1,req,name=queue_name,json=queueName" json:"queue_name,omitempty"` + TaskName []byte `protobuf:"bytes,2,req,name=task_name,json=taskName" json:"task_name,omitempty"` + EtaUsec *int64 `protobuf:"varint,3,req,name=eta_usec,json=etaUsec" json:"eta_usec,omitempty"` + LeaseSeconds *float64 `protobuf:"fixed64,4,req,name=lease_seconds,json=leaseSeconds" json:"lease_seconds,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueModifyTaskLeaseRequest) Reset() { *m = TaskQueueModifyTaskLeaseRequest{} } +func (m *TaskQueueModifyTaskLeaseRequest) String() string { return proto.CompactTextString(m) } +func (*TaskQueueModifyTaskLeaseRequest) ProtoMessage() {} +func (*TaskQueueModifyTaskLeaseRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{37} +} + +func (m *TaskQueueModifyTaskLeaseRequest) GetQueueName() []byte { + if m != nil { + return m.QueueName + } + return nil +} + +func (m *TaskQueueModifyTaskLeaseRequest) GetTaskName() []byte { + if m != nil { + return m.TaskName + } + return nil +} + +func (m *TaskQueueModifyTaskLeaseRequest) GetEtaUsec() int64 { + if m != nil && m.EtaUsec != nil { + return *m.EtaUsec + } + return 0 +} + +func (m *TaskQueueModifyTaskLeaseRequest) GetLeaseSeconds() float64 { + if m != nil && m.LeaseSeconds != nil { + return *m.LeaseSeconds + } + return 0 +} + +type TaskQueueModifyTaskLeaseResponse struct { + UpdatedEtaUsec *int64 `protobuf:"varint,1,req,name=updated_eta_usec,json=updatedEtaUsec" json:"updated_eta_usec,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TaskQueueModifyTaskLeaseResponse) Reset() { *m = TaskQueueModifyTaskLeaseResponse{} } +func (m *TaskQueueModifyTaskLeaseResponse) String() string { return proto.CompactTextString(m) } +func (*TaskQueueModifyTaskLeaseResponse) ProtoMessage() {} +func (*TaskQueueModifyTaskLeaseResponse) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{38} +} + +func (m *TaskQueueModifyTaskLeaseResponse) GetUpdatedEtaUsec() int64 { + if m != nil && m.UpdatedEtaUsec != nil { + return *m.UpdatedEtaUsec + } + return 0 +} + +func init() { + proto.RegisterType((*TaskQueueServiceError)(nil), "appengine.TaskQueueServiceError") + proto.RegisterType((*TaskPayload)(nil), "appengine.TaskPayload") + proto.RegisterType((*TaskQueueRetryParameters)(nil), "appengine.TaskQueueRetryParameters") + proto.RegisterType((*TaskQueueAcl)(nil), "appengine.TaskQueueAcl") + proto.RegisterType((*TaskQueueHttpHeader)(nil), "appengine.TaskQueueHttpHeader") + proto.RegisterType((*TaskQueueMode)(nil), "appengine.TaskQueueMode") + proto.RegisterType((*TaskQueueAddRequest)(nil), "appengine.TaskQueueAddRequest") + proto.RegisterType((*TaskQueueAddRequest_Header)(nil), "appengine.TaskQueueAddRequest.Header") + proto.RegisterType((*TaskQueueAddRequest_CronTimetable)(nil), "appengine.TaskQueueAddRequest.CronTimetable") + proto.RegisterType((*TaskQueueAddResponse)(nil), "appengine.TaskQueueAddResponse") + proto.RegisterType((*TaskQueueBulkAddRequest)(nil), "appengine.TaskQueueBulkAddRequest") + proto.RegisterType((*TaskQueueBulkAddResponse)(nil), "appengine.TaskQueueBulkAddResponse") + proto.RegisterType((*TaskQueueBulkAddResponse_TaskResult)(nil), "appengine.TaskQueueBulkAddResponse.TaskResult") + proto.RegisterType((*TaskQueueDeleteRequest)(nil), "appengine.TaskQueueDeleteRequest") + proto.RegisterType((*TaskQueueDeleteResponse)(nil), "appengine.TaskQueueDeleteResponse") + proto.RegisterType((*TaskQueueForceRunRequest)(nil), "appengine.TaskQueueForceRunRequest") + proto.RegisterType((*TaskQueueForceRunResponse)(nil), "appengine.TaskQueueForceRunResponse") + proto.RegisterType((*TaskQueueUpdateQueueRequest)(nil), "appengine.TaskQueueUpdateQueueRequest") + proto.RegisterType((*TaskQueueUpdateQueueResponse)(nil), "appengine.TaskQueueUpdateQueueResponse") + proto.RegisterType((*TaskQueueFetchQueuesRequest)(nil), "appengine.TaskQueueFetchQueuesRequest") + proto.RegisterType((*TaskQueueFetchQueuesResponse)(nil), "appengine.TaskQueueFetchQueuesResponse") + proto.RegisterType((*TaskQueueFetchQueuesResponse_Queue)(nil), "appengine.TaskQueueFetchQueuesResponse.Queue") + proto.RegisterType((*TaskQueueFetchQueueStatsRequest)(nil), "appengine.TaskQueueFetchQueueStatsRequest") + proto.RegisterType((*TaskQueueScannerQueueInfo)(nil), "appengine.TaskQueueScannerQueueInfo") + proto.RegisterType((*TaskQueueFetchQueueStatsResponse)(nil), "appengine.TaskQueueFetchQueueStatsResponse") + proto.RegisterType((*TaskQueueFetchQueueStatsResponse_QueueStats)(nil), "appengine.TaskQueueFetchQueueStatsResponse.QueueStats") + proto.RegisterType((*TaskQueuePauseQueueRequest)(nil), "appengine.TaskQueuePauseQueueRequest") + proto.RegisterType((*TaskQueuePauseQueueResponse)(nil), "appengine.TaskQueuePauseQueueResponse") + proto.RegisterType((*TaskQueuePurgeQueueRequest)(nil), "appengine.TaskQueuePurgeQueueRequest") + proto.RegisterType((*TaskQueuePurgeQueueResponse)(nil), "appengine.TaskQueuePurgeQueueResponse") + proto.RegisterType((*TaskQueueDeleteQueueRequest)(nil), "appengine.TaskQueueDeleteQueueRequest") + proto.RegisterType((*TaskQueueDeleteQueueResponse)(nil), "appengine.TaskQueueDeleteQueueResponse") + proto.RegisterType((*TaskQueueDeleteGroupRequest)(nil), "appengine.TaskQueueDeleteGroupRequest") + proto.RegisterType((*TaskQueueDeleteGroupResponse)(nil), "appengine.TaskQueueDeleteGroupResponse") + proto.RegisterType((*TaskQueueQueryTasksRequest)(nil), "appengine.TaskQueueQueryTasksRequest") + proto.RegisterType((*TaskQueueQueryTasksResponse)(nil), "appengine.TaskQueueQueryTasksResponse") + proto.RegisterType((*TaskQueueQueryTasksResponse_Task)(nil), "appengine.TaskQueueQueryTasksResponse.Task") + proto.RegisterType((*TaskQueueQueryTasksResponse_Task_Header)(nil), "appengine.TaskQueueQueryTasksResponse.Task.Header") + proto.RegisterType((*TaskQueueQueryTasksResponse_Task_CronTimetable)(nil), "appengine.TaskQueueQueryTasksResponse.Task.CronTimetable") + proto.RegisterType((*TaskQueueQueryTasksResponse_Task_RunLog)(nil), "appengine.TaskQueueQueryTasksResponse.Task.RunLog") + proto.RegisterType((*TaskQueueFetchTaskRequest)(nil), "appengine.TaskQueueFetchTaskRequest") + proto.RegisterType((*TaskQueueFetchTaskResponse)(nil), "appengine.TaskQueueFetchTaskResponse") + proto.RegisterType((*TaskQueueUpdateStorageLimitRequest)(nil), "appengine.TaskQueueUpdateStorageLimitRequest") + proto.RegisterType((*TaskQueueUpdateStorageLimitResponse)(nil), "appengine.TaskQueueUpdateStorageLimitResponse") + proto.RegisterType((*TaskQueueQueryAndOwnTasksRequest)(nil), "appengine.TaskQueueQueryAndOwnTasksRequest") + proto.RegisterType((*TaskQueueQueryAndOwnTasksResponse)(nil), "appengine.TaskQueueQueryAndOwnTasksResponse") + proto.RegisterType((*TaskQueueQueryAndOwnTasksResponse_Task)(nil), "appengine.TaskQueueQueryAndOwnTasksResponse.Task") + proto.RegisterType((*TaskQueueModifyTaskLeaseRequest)(nil), "appengine.TaskQueueModifyTaskLeaseRequest") + proto.RegisterType((*TaskQueueModifyTaskLeaseResponse)(nil), "appengine.TaskQueueModifyTaskLeaseResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/taskqueue/taskqueue_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 2747 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x39, 0x4d, 0x73, 0xdb, 0xd6, + 0xb5, 0x01, 0xbf, 0x44, 0x1e, 0x7e, 0xc1, 0xd7, 0xb2, 0x44, 0x51, 0x71, 0x22, 0xc3, 0xf9, 0xd0, + 0x4b, 0xfc, 0x14, 0x59, 0x79, 0xe3, 0xbc, 0xa7, 0x99, 0x4c, 0x1e, 0x24, 0xc2, 0x32, 0x63, 0x8a, + 0xa4, 0x2f, 0xa1, 0x34, 0xce, 0x4c, 0x07, 0x73, 0x45, 0x5c, 0x51, 0x18, 0x81, 0x00, 0x83, 0x0f, + 0x5b, 0xf2, 0xa2, 0xab, 0xae, 0x3a, 0x5d, 0x74, 0xd3, 0xe9, 0x4c, 0x66, 0xba, 0xea, 0xf4, 0x37, + 0x74, 0xd7, 0xfe, 0x90, 0x2e, 0x3b, 0xd3, 0x3f, 0xd0, 0x55, 0xa7, 0x0b, 0x77, 0xee, 0xbd, 0x00, + 0x08, 0x4a, 0xb4, 0x6c, 0x4b, 0x49, 0x37, 0x12, 0x70, 0xce, 0xb9, 0xe7, 0xdc, 0xf3, 0x7d, 0x70, + 0x08, 0x0f, 0x47, 0xae, 0x3b, 0xb2, 0xe9, 0xc6, 0xc8, 0xb5, 0x89, 0x33, 0xda, 0x70, 0xbd, 0xd1, + 0x67, 0x64, 0x32, 0xa1, 0xce, 0xc8, 0x72, 0xe8, 0x67, 0x96, 0x13, 0x50, 0xcf, 0x21, 0xf6, 0x67, + 0x01, 0xf1, 0x4f, 0xbe, 0x0f, 0x69, 0x48, 0xa7, 0x4f, 0x86, 0x4f, 0xbd, 0x67, 0xd6, 0x90, 0x6e, + 0x4c, 0x3c, 0x37, 0x70, 0x51, 0x29, 0x39, 0xd5, 0x54, 0xdf, 0x88, 0xa5, 0x49, 0x02, 0xe2, 0x07, + 0xae, 0x47, 0xa7, 0x4f, 0xc6, 0xb3, 0xcf, 0x05, 0x37, 0xe5, 0xb7, 0x79, 0xb8, 0xa5, 0x13, 0xff, + 0xe4, 0x09, 0x93, 0x34, 0x10, 0x82, 0x34, 0xcf, 0x73, 0x3d, 0xe5, 0x5f, 0x39, 0x28, 0xf1, 0xa7, + 0x5d, 0xd7, 0xa4, 0xa8, 0x00, 0x99, 0xde, 0x63, 0xf9, 0x1d, 0x74, 0x03, 0xaa, 0x07, 0xdd, 0xc7, + 0xdd, 0xde, 0xcf, 0xba, 0xc6, 0x93, 0x03, 0xed, 0x40, 0x93, 0x25, 0x74, 0x13, 0xea, 0x3a, 0x56, + 0xbb, 0x83, 0xb6, 0xd6, 0xd5, 0x0d, 0x0d, 0xe3, 0x1e, 0x96, 0x33, 0x08, 0x41, 0xad, 0xdd, 0xd5, + 0x35, 0xdc, 0x55, 0x3b, 0x11, 0x2c, 0xcb, 0x60, 0xba, 0x3a, 0x78, 0x6c, 0xe8, 0xbd, 0x9e, 0xd1, + 0x51, 0xf1, 0x9e, 0x26, 0xe7, 0xd0, 0x2d, 0xb8, 0xd1, 0xee, 0x7e, 0xa3, 0x76, 0xda, 0x2d, 0x83, + 0xe3, 0xba, 0xea, 0xbe, 0x26, 0xe7, 0xd1, 0x12, 0xa0, 0x18, 0xcc, 0xc5, 0x08, 0x78, 0x01, 0xd5, + 0xa1, 0x1c, 0xc3, 0x0f, 0x70, 0x47, 0x5e, 0xb8, 0x48, 0x88, 0x55, 0x5d, 0x93, 0x8b, 0x8c, 0x6f, + 0x5f, 0xc3, 0xfb, 0xed, 0xc1, 0xa0, 0xdd, 0xeb, 0x1a, 0x2d, 0xad, 0xdb, 0xd6, 0x5a, 0x72, 0x09, + 0x2d, 0xc3, 0x4d, 0x2e, 0x46, 0xed, 0x60, 0x4d, 0x6d, 0x3d, 0x35, 0xb4, 0x6f, 0xdb, 0x03, 0x7d, + 0x20, 0x03, 0x57, 0xa2, 0xb7, 0xbf, 0x33, 0xd0, 0x7b, 0x5d, 0x4d, 0x5c, 0x45, 0x2e, 0xa7, 0xa5, + 0x69, 0xba, 0x2a, 0x57, 0x18, 0x55, 0x0c, 0xc0, 0xda, 0x93, 0x03, 0x6d, 0xa0, 0xcb, 0x55, 0x24, + 0x43, 0x25, 0x36, 0x09, 0x3f, 0x57, 0x43, 0x8b, 0x20, 0xa7, 0x98, 0x09, 0x3b, 0xd5, 0x99, 0xec, + 0xd6, 0x41, 0xbf, 0xd3, 0xde, 0x55, 0x75, 0x2d, 0xa5, 0xac, 0x8c, 0xca, 0xb0, 0x30, 0x78, 0xdc, + 0xee, 0xf7, 0xb5, 0x96, 0x7c, 0x83, 0x1b, 0xa9, 0xd7, 0x33, 0xf6, 0xd5, 0xee, 0x53, 0x4e, 0x34, + 0x90, 0x51, 0x5a, 0x6c, 0x5f, 0x7d, 0xda, 0xe9, 0xa9, 0x2d, 0xf9, 0x26, 0x7a, 0x17, 0x1a, 0xd3, + 0xbb, 0xe8, 0xf8, 0xa9, 0xd1, 0x57, 0xb1, 0xba, 0xaf, 0xe9, 0x1a, 0x1e, 0xc8, 0x8b, 0x17, 0xed, + 0xb2, 0xdf, 0x6b, 0x69, 0xf2, 0x2d, 0x76, 0x35, 0x75, 0xb7, 0x63, 0x74, 0x7a, 0xbd, 0xc7, 0x07, + 0xfd, 0xc8, 0x33, 0x4b, 0xe8, 0x2e, 0xbc, 0xcf, 0x5d, 0xa8, 0xee, 0xea, 0xed, 0x1e, 0x73, 0x59, + 0xa4, 0x5d, 0xca, 0x55, 0xcb, 0xa8, 0x09, 0x4b, 0xed, 0xee, 0x6e, 0x0f, 0x63, 0x6d, 0x57, 0x37, + 0x76, 0xb1, 0xa6, 0xea, 0x3d, 0x2c, 0x54, 0x68, 0x30, 0x71, 0x5c, 0xa3, 0x8e, 0xa6, 0x0e, 0x34, + 0x43, 0xfb, 0xb6, 0xdf, 0xc6, 0x5a, 0x4b, 0x5e, 0x61, 0xb6, 0x11, 0xe2, 0xfb, 0xea, 0xc1, 0x40, + 0x6b, 0xc9, 0xcd, 0xb4, 0x4d, 0x75, 0x75, 0x4f, 0x5e, 0x45, 0x8b, 0x50, 0x6f, 0xa9, 0xba, 0x3a, + 0xd0, 0x7b, 0x58, 0x8b, 0x2e, 0xf4, 0x9b, 0xae, 0xb2, 0x0a, 0x65, 0x16, 0x96, 0x7d, 0x72, 0x66, + 0xbb, 0xc4, 0xfc, 0xa4, 0x58, 0x04, 0xf9, 0xe5, 0xcb, 0x97, 0x2f, 0x17, 0xb6, 0x33, 0x45, 0x49, + 0xf9, 0x9b, 0x04, 0x8d, 0x24, 0x68, 0x31, 0x0d, 0xbc, 0xb3, 0x3e, 0xf1, 0xc8, 0x98, 0x06, 0xd4, + 0xf3, 0xd1, 0xfb, 0x50, 0xf6, 0x18, 0xc8, 0xb0, 0xad, 0xb1, 0x15, 0x34, 0xa4, 0x35, 0x69, 0x3d, + 0x8f, 0x81, 0x83, 0x3a, 0x0c, 0x82, 0x14, 0xa8, 0x92, 0x11, 0x15, 0x68, 0xc3, 0xa7, 0xc3, 0x46, + 0x66, 0x4d, 0x5a, 0xcf, 0xe2, 0x32, 0x19, 0x51, 0x4e, 0x30, 0xa0, 0x43, 0xf4, 0x29, 0xd4, 0xc7, + 0x96, 0x63, 0x1c, 0x92, 0xe1, 0x89, 0x7b, 0x74, 0xc4, 0xa9, 0xb2, 0x6b, 0xd2, 0xba, 0xb4, 0x9d, + 0xdd, 0xdc, 0xb8, 0x8f, 0xab, 0x63, 0xcb, 0xd9, 0x11, 0x28, 0x46, 0x7c, 0x0f, 0xea, 0x63, 0x72, + 0x3a, 0x43, 0x9c, 0xe3, 0xc4, 0xb9, 0xcf, 0x1f, 0x6c, 0x6e, 0xe2, 0xea, 0x98, 0x9c, 0xa6, 0xa8, + 0x3f, 0x06, 0x06, 0x30, 0x4c, 0x37, 0x3c, 0xb4, 0x2d, 0x67, 0xe4, 0x37, 0xf2, 0xec, 0x86, 0xdb, + 0x99, 0xfb, 0x0f, 0x70, 0x65, 0x4c, 0x4e, 0x5b, 0x31, 0x5c, 0xe9, 0x43, 0x25, 0x51, 0x52, 0x1d, + 0xda, 0xe8, 0x36, 0x40, 0xe8, 0x53, 0xcf, 0xa0, 0x63, 0x62, 0xd9, 0x0d, 0x69, 0x2d, 0xbb, 0x5e, + 0xc1, 0x25, 0x06, 0xd1, 0x18, 0x00, 0xdd, 0x81, 0xca, 0x73, 0xcf, 0x0a, 0x12, 0x82, 0x0c, 0x27, + 0x28, 0x0b, 0x18, 0x27, 0x51, 0xbe, 0x84, 0x9b, 0x09, 0xc7, 0x47, 0x41, 0x30, 0x79, 0x44, 0x89, + 0x49, 0x3d, 0x24, 0x43, 0xf6, 0x84, 0x9e, 0x35, 0xa4, 0xb5, 0xcc, 0x7a, 0x05, 0xb3, 0x47, 0xb4, + 0x08, 0xf9, 0x67, 0xc4, 0x0e, 0x69, 0x23, 0xc3, 0x61, 0xe2, 0x45, 0xf9, 0x14, 0xaa, 0xc9, 0xf1, + 0x7d, 0xd7, 0xa4, 0x4a, 0x13, 0x72, 0xec, 0x3f, 0x2a, 0x42, 0xae, 0x7f, 0x30, 0x78, 0x24, 0xbf, + 0x23, 0x9e, 0x3a, 0x1d, 0x59, 0x52, 0xfe, 0x51, 0x48, 0x09, 0x53, 0x4d, 0x13, 0xd3, 0xef, 0x43, + 0xea, 0x07, 0x4c, 0x0b, 0x51, 0xd5, 0x1c, 0x32, 0xa6, 0x91, 0xcc, 0x12, 0x87, 0x74, 0xc9, 0x98, + 0xa2, 0x55, 0x28, 0xb1, 0xc2, 0x27, 0xb0, 0x42, 0x7a, 0x91, 0x01, 0x38, 0x72, 0x05, 0x8a, 0x34, + 0x20, 0x46, 0x28, 0xdc, 0x91, 0x59, 0xcf, 0xe2, 0x05, 0x1a, 0x90, 0x03, 0x9f, 0x0e, 0xd1, 0xd7, + 0x50, 0x18, 0xd3, 0xe0, 0xd8, 0x35, 0xb9, 0x39, 0x6b, 0x5b, 0xf7, 0x36, 0x92, 0x4a, 0xb8, 0x31, + 0xe7, 0x1a, 0x1b, 0xd1, 0xff, 0x7d, 0x7e, 0x66, 0x3b, 0xd7, 0xef, 0x0d, 0x74, 0x1c, 0x71, 0x60, + 0xf6, 0x08, 0x3d, 0x9b, 0xfb, 0xb0, 0x82, 0xd9, 0x23, 0xfa, 0x12, 0x0a, 0xc7, 0xdc, 0x56, 0x8d, + 0xc2, 0x5a, 0x76, 0x1d, 0xb6, 0x3e, 0x7c, 0x0d, 0x77, 0x61, 0x58, 0x1c, 0x1d, 0x42, 0x4b, 0x90, + 0x3b, 0x74, 0xcd, 0xb3, 0x46, 0x89, 0x71, 0xdc, 0xc9, 0x14, 0x25, 0xcc, 0xdf, 0xd1, 0xff, 0x42, + 0x39, 0xf0, 0x88, 0xe3, 0x93, 0x61, 0x60, 0xb9, 0x4e, 0x03, 0xd6, 0xa4, 0xf5, 0xf2, 0xd6, 0x52, + 0x9a, 0xf7, 0x14, 0x8b, 0xd3, 0xa4, 0xe8, 0x16, 0x14, 0xc8, 0x64, 0x62, 0x58, 0x66, 0xa3, 0xcc, + 0x6f, 0x99, 0x27, 0x93, 0x49, 0xdb, 0x44, 0x18, 0xaa, 0x43, 0xcf, 0x75, 0x02, 0x6b, 0x4c, 0x03, + 0x72, 0x68, 0xd3, 0x46, 0x65, 0x4d, 0x5a, 0x87, 0xd7, 0x1a, 0x63, 0xd7, 0x73, 0x1d, 0x3d, 0x3e, + 0x83, 0x67, 0x59, 0xa0, 0x35, 0x28, 0x9b, 0xd4, 0x1f, 0x7a, 0xd6, 0x84, 0x5f, 0xb2, 0xce, 0xe5, + 0xa5, 0x41, 0x68, 0x13, 0x16, 0x26, 0x22, 0x4f, 0x1b, 0xf2, 0x45, 0x15, 0xa6, 0x59, 0x8c, 0x63, + 0x32, 0xd4, 0x05, 0x59, 0xe4, 0xe8, 0x24, 0xc9, 0xdb, 0xc6, 0x0d, 0x7e, 0xf4, 0xee, 0xbc, 0xab, + 0x9e, 0x4b, 0x71, 0x5c, 0xf7, 0xce, 0xe5, 0xfc, 0x17, 0x90, 0x1b, 0xbb, 0x26, 0x6d, 0x20, 0xee, + 0xfb, 0xdb, 0xf3, 0x78, 0xb0, 0x40, 0xdd, 0x60, 0x7f, 0xb6, 0x79, 0xac, 0x62, 0x7e, 0x80, 0xb9, + 0x3a, 0x20, 0xa3, 0xc6, 0x4d, 0xe1, 0xea, 0x80, 0x8c, 0x9a, 0x9b, 0x50, 0x98, 0x4d, 0x8b, 0x85, + 0x39, 0x69, 0x51, 0x4c, 0xa5, 0x45, 0x73, 0x0f, 0xaa, 0x33, 0x06, 0x44, 0x4d, 0x28, 0xfa, 0xc3, + 0x63, 0x6a, 0x86, 0x36, 0x6d, 0x54, 0x45, 0x08, 0xc7, 0xef, 0x0c, 0xc7, 0x4c, 0xfb, 0xc2, 0x75, + 0x68, 0xa3, 0x16, 0x85, 0x77, 0xf4, 0xae, 0xa8, 0x50, 0x9d, 0x09, 0x4b, 0xb4, 0x00, 0xd9, 0x3d, + 0x4d, 0x97, 0x25, 0x9e, 0x56, 0xbd, 0x81, 0x2e, 0x67, 0xd8, 0xd3, 0x23, 0x4d, 0x6d, 0xc9, 0x59, + 0x86, 0xec, 0x1f, 0xe8, 0x72, 0x0e, 0x01, 0x14, 0x5a, 0x5a, 0x47, 0xd3, 0x35, 0x39, 0xaf, 0xfc, + 0x3f, 0x2c, 0xce, 0x3a, 0xd8, 0x9f, 0xb8, 0x8e, 0x4f, 0xd1, 0x3a, 0xc8, 0xc3, 0x63, 0xd7, 0xa7, + 0x8e, 0x31, 0xcd, 0x2e, 0x89, 0x2b, 0x5d, 0x13, 0x70, 0x3d, 0xca, 0x31, 0xe5, 0x3b, 0x58, 0x4e, + 0x38, 0xec, 0x84, 0xf6, 0x49, 0x2a, 0x75, 0xbf, 0x82, 0x32, 0x31, 0x4d, 0xc3, 0x13, 0xaf, 0xbc, + 0x02, 0x95, 0xb7, 0xde, 0xbb, 0x3c, 0xb6, 0x30, 0x90, 0xe4, 0x59, 0xf9, 0x7b, 0xba, 0x6e, 0x27, + 0xcc, 0xa3, 0x2b, 0x76, 0x01, 0xd8, 0xdd, 0x3c, 0xea, 0x87, 0xb6, 0x60, 0x0e, 0x5b, 0x1b, 0xf3, + 0x98, 0x9f, 0x3b, 0xc8, 0x11, 0x98, 0x9f, 0xc2, 0x29, 0x0e, 0xcd, 0x17, 0x00, 0x53, 0x0c, 0xda, + 0x81, 0x42, 0xc4, 0x99, 0x15, 0x95, 0xda, 0xd6, 0x27, 0xf3, 0x38, 0xa7, 0xe7, 0x9f, 0x8d, 0x64, + 0xf6, 0xc1, 0xd1, 0xc9, 0xb9, 0x46, 0xcc, 0xce, 0x35, 0xe2, 0x09, 0x2c, 0x25, 0x4c, 0x5b, 0xd4, + 0xa6, 0x01, 0xbd, 0x5a, 0xf9, 0xcb, 0xce, 0x94, 0xbf, 0x69, 0xd2, 0x67, 0x53, 0x49, 0xaf, 0xfc, + 0x3c, 0xe5, 0xb1, 0x58, 0x58, 0x64, 0xd3, 0xa9, 0xd6, 0xd9, 0xb5, 0xec, 0xd5, 0xb4, 0x56, 0xc6, + 0x29, 0x9f, 0x3d, 0x74, 0xbd, 0x21, 0xc5, 0xa1, 0x13, 0x6b, 0x33, 0xbd, 0x91, 0x94, 0x2e, 0x43, + 0xb3, 0x4a, 0x66, 0x2e, 0x55, 0x32, 0x3b, 0x5b, 0xe3, 0x15, 0x03, 0x56, 0xe6, 0x88, 0x9b, 0xa3, + 0xcf, 0x15, 0xbd, 0xa8, 0xfc, 0x90, 0x83, 0xd5, 0x84, 0xf6, 0x60, 0x62, 0x92, 0x80, 0x46, 0x45, + 0xe6, 0x3a, 0x3a, 0x7d, 0x01, 0x8d, 0xc3, 0x70, 0x78, 0x42, 0x03, 0xc3, 0xa3, 0x47, 0x96, 0x6d, + 0x1b, 0x13, 0xea, 0xb1, 0x49, 0xc0, 0x75, 0x4c, 0x7e, 0x57, 0x09, 0xdf, 0x12, 0x78, 0xcc, 0xd1, + 0x7d, 0xea, 0x0d, 0x38, 0x12, 0x7d, 0x0c, 0xf5, 0xe8, 0xe0, 0x90, 0x4c, 0xc8, 0xd0, 0x0a, 0xce, + 0x1a, 0xb9, 0xb5, 0xcc, 0x7a, 0x1e, 0xd7, 0x04, 0x78, 0x37, 0x82, 0xa2, 0x0d, 0xb8, 0xc9, 0xdb, + 0xbf, 0x3f, 0xa1, 0x43, 0xeb, 0xc8, 0xa2, 0xa6, 0xe1, 0x91, 0x80, 0xf2, 0x76, 0x57, 0xc2, 0x37, + 0x18, 0x6a, 0x10, 0x63, 0x30, 0x09, 0xe8, 0xdc, 0x1a, 0x5b, 0xb8, 0x46, 0x8d, 0x7d, 0x00, 0xcb, + 0x6c, 0x6e, 0x19, 0xba, 0xce, 0x30, 0xf4, 0x3c, 0xea, 0x04, 0x71, 0x21, 0xf0, 0x1b, 0x0b, 0x7c, + 0xc6, 0xba, 0x35, 0x26, 0xa7, 0xbb, 0x09, 0x36, 0x32, 0xe7, 0xb4, 0x36, 0x17, 0xdf, 0xb6, 0x36, + 0xff, 0x17, 0x64, 0xc9, 0xd0, 0xe6, 0x4d, 0xb3, 0xbc, 0xb5, 0x3c, 0xb7, 0xcc, 0x0c, 0x6d, 0xcc, + 0x68, 0xd0, 0x1e, 0xd4, 0x45, 0xab, 0x35, 0xdc, 0x67, 0xd4, 0xf3, 0x2c, 0x93, 0x36, 0xe0, 0xd5, + 0xd5, 0x69, 0x3a, 0xfa, 0xe0, 0x9a, 0x38, 0xd6, 0x8b, 0x4e, 0x29, 0xef, 0xc1, 0xbb, 0xf3, 0x63, + 0x43, 0x04, 0xa0, 0xd2, 0x4b, 0xc5, 0xce, 0x43, 0x1a, 0x0c, 0x8f, 0xf9, 0x93, 0xff, 0x9a, 0xd8, + 0x59, 0x81, 0x22, 0x33, 0x9d, 0xe7, 0x3e, 0xf7, 0x79, 0xe4, 0xe4, 0xf1, 0xc2, 0x98, 0x9c, 0x62, + 0xf7, 0xb9, 0xaf, 0xfc, 0x31, 0x9f, 0x92, 0x38, 0xc3, 0x31, 0x0a, 0xf9, 0x5d, 0xc8, 0xf3, 0x28, + 0x8b, 0x2a, 0xe2, 0x7f, 0xcf, 0x53, 0x68, 0xce, 0xb9, 0x0d, 0x71, 0x6f, 0x71, 0xb6, 0xf9, 0x97, + 0x1c, 0xe4, 0x39, 0xe0, 0x3f, 0x1d, 0xc6, 0xd2, 0xb5, 0xc3, 0xf8, 0x36, 0x14, 0x26, 0x24, 0xf4, + 0xa9, 0xd9, 0x28, 0xac, 0x65, 0xd6, 0x8b, 0xdb, 0xf9, 0x23, 0x62, 0xfb, 0x14, 0x47, 0xc0, 0xb9, + 0x51, 0xbe, 0xf0, 0xd3, 0x44, 0x79, 0xf1, 0x4d, 0xa2, 0xbc, 0x74, 0xc5, 0x28, 0x87, 0xab, 0x45, + 0x79, 0xf9, 0x2a, 0x51, 0x8e, 0xee, 0x43, 0x65, 0xe8, 0x51, 0x12, 0xb8, 0x9e, 0x08, 0x03, 0x36, + 0x25, 0x96, 0xb6, 0x81, 0x4c, 0x26, 0xc7, 0xae, 0x1f, 0x58, 0xce, 0x88, 0xcf, 0xa8, 0xe5, 0x88, + 0x86, 0x97, 0xe5, 0x5f, 0xc0, 0xfb, 0x73, 0xc2, 0x6d, 0x10, 0x90, 0xc0, 0x7f, 0xcb, 0xc2, 0x99, + 0x9d, 0x8d, 0xb8, 0x0f, 0xc5, 0xe7, 0x90, 0x13, 0x8e, 0x79, 0x57, 0xf5, 0x79, 0x6f, 0xcb, 0x6f, + 0x4b, 0x9b, 0xb8, 0x3c, 0x26, 0xa7, 0xdd, 0x70, 0xcc, 0xc4, 0xfa, 0xca, 0xaf, 0x32, 0xa9, 0xbe, + 0x30, 0x18, 0x12, 0xc7, 0xa1, 0x1e, 0x7f, 0x6e, 0x3b, 0x47, 0x2e, 0xda, 0x84, 0x45, 0x7a, 0x4a, + 0x87, 0x61, 0x40, 0x4d, 0xc3, 0x26, 0x7e, 0x60, 0x8c, 0x2d, 0x27, 0x0c, 0x44, 0x7f, 0xcd, 0x62, + 0x14, 0xe3, 0x3a, 0xc4, 0x0f, 0xf6, 0x39, 0x06, 0xdd, 0x03, 0x34, 0x7b, 0xe2, 0xd8, 0x0d, 0x3d, + 0x9e, 0x0f, 0x59, 0x2c, 0xa7, 0xe9, 0x1f, 0xb9, 0xa1, 0x87, 0xb6, 0x61, 0xc5, 0x27, 0xe3, 0x09, + 0xfb, 0x2e, 0x33, 0xcc, 0xd0, 0x23, 0x6c, 0xec, 0x8d, 0xd2, 0xc2, 0x8f, 0xf2, 0x62, 0x39, 0x26, + 0x68, 0x45, 0x78, 0x91, 0x18, 0x3e, 0x93, 0x14, 0x87, 0x90, 0x61, 0x39, 0xc6, 0x91, 0x6d, 0x8d, + 0x8e, 0x03, 0xfe, 0x71, 0x91, 0xc7, 0x72, 0x8c, 0x69, 0x3b, 0x0f, 0x39, 0x1c, 0xdd, 0x85, 0x2a, + 0x75, 0x8e, 0x58, 0xdf, 0x4b, 0x25, 0x86, 0x84, 0x2b, 0x31, 0x90, 0xe5, 0x84, 0xf2, 0xbb, 0x0c, + 0xac, 0xbd, 0xda, 0x1b, 0x51, 0xe1, 0xf8, 0x26, 0xb2, 0xbb, 0xcf, 0xa0, 0x51, 0xf5, 0x78, 0x70, + 0x79, 0xf5, 0x98, 0x61, 0xb0, 0x91, 0x02, 0xa5, 0x38, 0x35, 0x7f, 0x90, 0x00, 0xa6, 0x28, 0xd6, + 0xcc, 0xa7, 0xbe, 0x13, 0xc5, 0xad, 0xe8, 0x44, 0x5e, 0x43, 0x1f, 0x41, 0xdd, 0xb5, 0x4d, 0xea, + 0x07, 0xc6, 0xb9, 0xef, 0xb6, 0xaa, 0x00, 0x6b, 0xd1, 0xd7, 0xdb, 0x1e, 0x54, 0x7c, 0xe1, 0x53, + 0xc3, 0x72, 0x8e, 0x5c, 0x6e, 0x9d, 0xf2, 0xd6, 0x07, 0x73, 0xbb, 0xfb, 0x39, 0xdf, 0xe3, 0x72, + 0x74, 0x92, 0xbd, 0x28, 0xc7, 0xd0, 0x4c, 0x28, 0xfb, 0xac, 0x42, 0xbc, 0xb2, 0xb5, 0x67, 0xde, + 0xb8, 0xb5, 0x2f, 0x42, 0x9e, 0x17, 0x1b, 0x7e, 0xf5, 0x22, 0x16, 0x2f, 0xca, 0xed, 0x54, 0x27, + 0x48, 0x4b, 0x8a, 0x1a, 0x05, 0x4e, 0x5f, 0x24, 0xf4, 0x46, 0x3f, 0xc2, 0x8c, 0x31, 0x2b, 0x32, + 0xc5, 0x33, 0x12, 0x39, 0x48, 0xa1, 0xc5, 0x1c, 0x78, 0x7d, 0xe5, 0x67, 0x1a, 0xe2, 0x0c, 0xd3, + 0x48, 0xe8, 0xff, 0x5c, 0x10, 0xba, 0xe7, 0xb9, 0xe1, 0xe4, 0x72, 0xa1, 0x73, 0xb8, 0x46, 0xa7, + 0x22, 0xae, 0x7f, 0x95, 0x52, 0xe6, 0x7b, 0x12, 0x52, 0xef, 0x8c, 0xc7, 0xd3, 0xf5, 0x46, 0xb4, + 0x8f, 0xa0, 0xee, 0x07, 0xc4, 0x0b, 0x2e, 0x4c, 0xef, 0x55, 0x0e, 0x8e, 0x87, 0x77, 0xf4, 0x01, + 0xd4, 0x04, 0x5d, 0x12, 0xb3, 0x39, 0xbe, 0x20, 0xaa, 0x70, 0x68, 0x1c, 0xb2, 0xab, 0x50, 0x8a, + 0xb9, 0x8d, 0xf8, 0x5c, 0xc5, 0xbe, 0xf2, 0x04, 0x9f, 0x11, 0x7a, 0x37, 0xd5, 0xf0, 0xc5, 0x7a, + 0x47, 0xba, 0x3f, 0xed, 0xf9, 0xbf, 0x84, 0x94, 0xd1, 0xd2, 0xda, 0x45, 0x99, 0xfb, 0x15, 0xe4, + 0xd8, 0x15, 0xa3, 0x9c, 0xfd, 0x74, 0x5e, 0x16, 0x5c, 0x3c, 0x25, 0x3e, 0x83, 0xf8, 0xc1, 0xe6, + 0x1f, 0x4a, 0x90, 0x63, 0xaf, 0x57, 0xde, 0xa6, 0x5c, 0xdc, 0x80, 0x3c, 0x39, 0xb7, 0x5f, 0xf9, + 0xbf, 0xb7, 0xb8, 0xd5, 0xec, 0xb2, 0x25, 0x59, 0xb3, 0x28, 0xf1, 0xa2, 0x6e, 0xe8, 0x86, 0x4e, + 0xc0, 0x6d, 0xc8, 0xeb, 0xbe, 0xd8, 0xd5, 0xed, 0x32, 0x20, 0xfa, 0x3a, 0x59, 0xbc, 0x2c, 0x70, + 0x63, 0x6c, 0xbd, 0x8d, 0xd8, 0x73, 0x5b, 0x98, 0x55, 0x28, 0x1d, 0xba, 0xe6, 0x99, 0xe1, 0x5b, + 0x2f, 0x28, 0xef, 0xb7, 0x79, 0x5c, 0x64, 0x80, 0x81, 0xf5, 0x82, 0x26, 0x2b, 0x9a, 0xf2, 0xb9, + 0x15, 0xcd, 0x3d, 0x40, 0xbc, 0x0d, 0xb2, 0x82, 0xcf, 0x3e, 0xd4, 0x85, 0xb9, 0x2a, 0xa2, 0x4f, + 0xc4, 0x18, 0xf6, 0xe9, 0xcf, 0xed, 0x66, 0x9c, 0xdf, 0xbf, 0x54, 0xf9, 0xfe, 0xe5, 0xad, 0x8c, + 0x75, 0xe9, 0x32, 0xe6, 0x6b, 0x28, 0x78, 0xa1, 0x63, 0xbb, 0x23, 0xbe, 0x69, 0x79, 0x4b, 0x7b, + 0xe0, 0xd0, 0xe9, 0xb8, 0x23, 0x1c, 0x71, 0x38, 0xbf, 0xd8, 0xb9, 0x75, 0xe9, 0x62, 0x67, 0xe9, + 0xea, 0x8b, 0x9d, 0xe5, 0x6b, 0x8c, 0x63, 0x1f, 0x40, 0xed, 0xc8, 0xf2, 0xfc, 0xc0, 0x60, 0x3c, + 0xb9, 0xe9, 0x1b, 0x22, 0x17, 0x39, 0x54, 0xf7, 0xce, 0xe2, 0x70, 0x65, 0x59, 0xb8, 0x92, 0x6c, + 0x71, 0xd0, 0x27, 0x50, 0x17, 0x4d, 0x9c, 0xf9, 0x4d, 0xc4, 0x57, 0x33, 0x8e, 0xaf, 0x5a, 0x82, + 0xe1, 0x31, 0x76, 0x71, 0xe3, 0x53, 0x9c, 0xb3, 0xf1, 0x29, 0xbd, 0xf1, 0xc6, 0xa7, 0x76, 0xc9, + 0xc6, 0xa7, 0x3e, 0xbb, 0xf1, 0x69, 0xfe, 0x49, 0x82, 0x82, 0xf0, 0x0a, 0x1b, 0xa0, 0x4d, 0xcb, + 0x9f, 0x90, 0x80, 0x9d, 0x13, 0xaa, 0xde, 0xe0, 0x51, 0x56, 0x9b, 0x82, 0xb9, 0xb2, 0x2b, 0x50, + 0xb4, 0xc9, 0x48, 0x50, 0x20, 0x91, 0xb6, 0x36, 0x19, 0x71, 0xd4, 0x1d, 0xa8, 0x50, 0x9b, 0x4c, + 0xfc, 0x98, 0xc1, 0x4d, 0x8e, 0x2e, 0x47, 0x30, 0x4e, 0x72, 0x17, 0xaa, 0x5e, 0x14, 0x14, 0xc6, + 0x90, 0x0d, 0xac, 0x8b, 0xc2, 0x9e, 0x31, 0x90, 0xff, 0xd8, 0x73, 0x07, 0x2a, 0xc2, 0x8b, 0x1e, + 0x25, 0xbe, 0xeb, 0x34, 0x56, 0xf9, 0x70, 0x2e, 0xb2, 0x15, 0x73, 0xd0, 0x8f, 0xb1, 0xab, 0x72, + 0xd2, 0x5f, 0xfa, 0x6c, 0x06, 0x11, 0xeb, 0x9a, 0x9f, 0x6c, 0xb3, 0xf0, 0x6d, 0xaa, 0xa7, 0xa4, + 0xe4, 0x45, 0x45, 0x77, 0x3b, 0x29, 0xba, 0x99, 0xf5, 0xf2, 0xd6, 0x47, 0x6f, 0x96, 0x57, 0xa2, + 0xde, 0x2a, 0x4f, 0x40, 0x39, 0xf7, 0xd5, 0x38, 0x08, 0x5c, 0x2f, 0xfe, 0x3d, 0xe1, 0x35, 0x0d, + 0x78, 0x11, 0xf2, 0xe2, 0x97, 0x0a, 0x31, 0x7c, 0x8a, 0x17, 0x65, 0x07, 0xee, 0x5e, 0xca, 0x32, + 0xba, 0x35, 0x9b, 0xbe, 0xe8, 0xf3, 0xe4, 0xa7, 0x0e, 0xc6, 0xa0, 0xe8, 0xd0, 0xe7, 0x9c, 0x48, + 0xf9, 0xb3, 0x94, 0x1a, 0x13, 0xf9, 0xe5, 0x55, 0xc7, 0xec, 0x3d, 0x77, 0x66, 0x7a, 0xe9, 0x6b, + 0x16, 0x52, 0x77, 0xa1, 0x6a, 0x53, 0xe2, 0xd3, 0x64, 0xda, 0xcd, 0xf0, 0x69, 0xb7, 0xc2, 0x81, + 0xf1, 0x88, 0xbb, 0x0a, 0x25, 0xd6, 0xee, 0xe2, 0xf9, 0x9d, 0xdf, 0x62, 0x4c, 0x4e, 0xc5, 0x0c, + 0xf8, 0x31, 0x54, 0x46, 0xac, 0xb9, 0x1b, 0x87, 0x67, 0xbc, 0x57, 0xb2, 0xa6, 0x92, 0x7c, 0xc6, + 0x01, 0x47, 0xed, 0x9c, 0xb1, 0xa6, 0x19, 0x65, 0x71, 0x3e, 0xc9, 0x62, 0xe5, 0x9f, 0x12, 0xdc, + 0xb9, 0x44, 0x81, 0xc8, 0x06, 0xda, 0x4c, 0xbb, 0xbc, 0xff, 0x4a, 0xcf, 0xcd, 0x39, 0x9b, 0x6e, + 0x9a, 0xbf, 0x96, 0xae, 0xd9, 0x34, 0xcf, 0xf5, 0xb3, 0xdc, 0xbc, 0x7e, 0x16, 0xb7, 0x99, 0xfc, + 0xb9, 0x36, 0x13, 0xe9, 0x5e, 0x98, 0xea, 0xfe, 0x7b, 0x29, 0xf5, 0xc5, 0xb5, 0xef, 0x9a, 0xd6, + 0x11, 0x0f, 0xbd, 0x0e, 0xb3, 0xfb, 0x4f, 0xfc, 0x5b, 0xca, 0x05, 0x9f, 0xe7, 0x2e, 0xfa, 0x5c, + 0xe9, 0xa4, 0x62, 0xeb, 0xc2, 0xf5, 0xa6, 0x5b, 0xe7, 0x90, 0xc7, 0xae, 0x39, 0x9d, 0xa5, 0x44, + 0x90, 0xd6, 0x22, 0x78, 0x34, 0x4d, 0xed, 0x94, 0xbf, 0x2b, 0x25, 0xbf, 0x77, 0xff, 0x3b, 0x00, + 0x00, 0xff, 0xff, 0x67, 0xac, 0x35, 0x53, 0x2a, 0x1f, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/taskqueue/taskqueue_service.proto b/vendor/google.golang.org/appengine/internal/taskqueue/taskqueue_service.proto new file mode 100644 index 0000000000..419aaf5702 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/taskqueue/taskqueue_service.proto @@ -0,0 +1,342 @@ +syntax = "proto2"; +option go_package = "taskqueue"; + +import "google.golang.org/appengine/internal/datastore/datastore_v3.proto"; + +package appengine; + +message TaskQueueServiceError { + enum ErrorCode { + OK = 0; + UNKNOWN_QUEUE = 1; + TRANSIENT_ERROR = 2; + INTERNAL_ERROR = 3; + TASK_TOO_LARGE = 4; + INVALID_TASK_NAME = 5; + INVALID_QUEUE_NAME = 6; + INVALID_URL = 7; + INVALID_QUEUE_RATE = 8; + PERMISSION_DENIED = 9; + TASK_ALREADY_EXISTS = 10; + TOMBSTONED_TASK = 11; + INVALID_ETA = 12; + INVALID_REQUEST = 13; + UNKNOWN_TASK = 14; + TOMBSTONED_QUEUE = 15; + DUPLICATE_TASK_NAME = 16; + SKIPPED = 17; + TOO_MANY_TASKS = 18; + INVALID_PAYLOAD = 19; + INVALID_RETRY_PARAMETERS = 20; + INVALID_QUEUE_MODE = 21; + ACL_LOOKUP_ERROR = 22; + TRANSACTIONAL_REQUEST_TOO_LARGE = 23; + INCORRECT_CREATOR_NAME = 24; + TASK_LEASE_EXPIRED = 25; + QUEUE_PAUSED = 26; + INVALID_TAG = 27; + + // Reserved range for the Datastore error codes. + // Original Datastore error code is shifted by DATASTORE_ERROR offset. + DATASTORE_ERROR = 10000; + } +} + +message TaskPayload { + extensions 10 to max; + option message_set_wire_format = true; +} + +message TaskQueueRetryParameters { + optional int32 retry_limit = 1; + optional int64 age_limit_sec = 2; + + optional double min_backoff_sec = 3 [default = 0.1]; + optional double max_backoff_sec = 4 [default = 3600]; + optional int32 max_doublings = 5 [default = 16]; +} + +message TaskQueueAcl { + repeated bytes user_email = 1; + repeated bytes writer_email = 2; +} + +message TaskQueueHttpHeader { + required bytes key = 1; + required bytes value = 2; +} + +message TaskQueueMode { + enum Mode { + PUSH = 0; + PULL = 1; + } +} + +message TaskQueueAddRequest { + required bytes queue_name = 1; + required bytes task_name = 2; + required int64 eta_usec = 3; + + enum RequestMethod { + GET = 1; + POST = 2; + HEAD = 3; + PUT = 4; + DELETE = 5; + } + optional RequestMethod method = 5 [default=POST]; + + optional bytes url = 4; + + repeated group Header = 6 { + required bytes key = 7; + required bytes value = 8; + } + + optional bytes body = 9 [ctype=CORD]; + optional Transaction transaction = 10; + optional bytes app_id = 11; + + optional group CronTimetable = 12 { + required bytes schedule = 13; + required bytes timezone = 14; + } + + optional bytes description = 15; + optional TaskPayload payload = 16; + optional TaskQueueRetryParameters retry_parameters = 17; + optional TaskQueueMode.Mode mode = 18 [default=PUSH]; + optional bytes tag = 19; +} + +message TaskQueueAddResponse { + optional bytes chosen_task_name = 1; +} + +message TaskQueueBulkAddRequest { + repeated TaskQueueAddRequest add_request = 1; +} + +message TaskQueueBulkAddResponse { + repeated group TaskResult = 1 { + required TaskQueueServiceError.ErrorCode result = 2; + optional bytes chosen_task_name = 3; + } +} + +message TaskQueueDeleteRequest { + required bytes queue_name = 1; + repeated bytes task_name = 2; + optional bytes app_id = 3; +} + +message TaskQueueDeleteResponse { + repeated TaskQueueServiceError.ErrorCode result = 3; +} + +message TaskQueueForceRunRequest { + optional bytes app_id = 1; + required bytes queue_name = 2; + required bytes task_name = 3; +} + +message TaskQueueForceRunResponse { + required TaskQueueServiceError.ErrorCode result = 3; +} + +message TaskQueueUpdateQueueRequest { + optional bytes app_id = 1; + required bytes queue_name = 2; + required double bucket_refill_per_second = 3; + required int32 bucket_capacity = 4; + optional string user_specified_rate = 5; + optional TaskQueueRetryParameters retry_parameters = 6; + optional int32 max_concurrent_requests = 7; + optional TaskQueueMode.Mode mode = 8 [default = PUSH]; + optional TaskQueueAcl acl = 9; + repeated TaskQueueHttpHeader header_override = 10; +} + +message TaskQueueUpdateQueueResponse { +} + +message TaskQueueFetchQueuesRequest { + optional bytes app_id = 1; + required int32 max_rows = 2; +} + +message TaskQueueFetchQueuesResponse { + repeated group Queue = 1 { + required bytes queue_name = 2; + required double bucket_refill_per_second = 3; + required double bucket_capacity = 4; + optional string user_specified_rate = 5; + required bool paused = 6 [default=false]; + optional TaskQueueRetryParameters retry_parameters = 7; + optional int32 max_concurrent_requests = 8; + optional TaskQueueMode.Mode mode = 9 [default = PUSH]; + optional TaskQueueAcl acl = 10; + repeated TaskQueueHttpHeader header_override = 11; + optional string creator_name = 12 [ctype=CORD, default="apphosting"]; + } +} + +message TaskQueueFetchQueueStatsRequest { + optional bytes app_id = 1; + repeated bytes queue_name = 2; + optional int32 max_num_tasks = 3 [default = 0]; +} + +message TaskQueueScannerQueueInfo { + required int64 executed_last_minute = 1; + required int64 executed_last_hour = 2; + required double sampling_duration_seconds = 3; + optional int32 requests_in_flight = 4; + optional double enforced_rate = 5; +} + +message TaskQueueFetchQueueStatsResponse { + repeated group QueueStats = 1 { + required int32 num_tasks = 2; + required int64 oldest_eta_usec = 3; + optional TaskQueueScannerQueueInfo scanner_info = 4; + } +} +message TaskQueuePauseQueueRequest { + required bytes app_id = 1; + required bytes queue_name = 2; + required bool pause = 3; +} + +message TaskQueuePauseQueueResponse { +} + +message TaskQueuePurgeQueueRequest { + optional bytes app_id = 1; + required bytes queue_name = 2; +} + +message TaskQueuePurgeQueueResponse { +} + +message TaskQueueDeleteQueueRequest { + required bytes app_id = 1; + required bytes queue_name = 2; +} + +message TaskQueueDeleteQueueResponse { +} + +message TaskQueueDeleteGroupRequest { + required bytes app_id = 1; +} + +message TaskQueueDeleteGroupResponse { +} + +message TaskQueueQueryTasksRequest { + optional bytes app_id = 1; + required bytes queue_name = 2; + + optional bytes start_task_name = 3; + optional int64 start_eta_usec = 4; + optional bytes start_tag = 6; + optional int32 max_rows = 5 [default = 1]; +} + +message TaskQueueQueryTasksResponse { + repeated group Task = 1 { + required bytes task_name = 2; + required int64 eta_usec = 3; + optional bytes url = 4; + + enum RequestMethod { + GET = 1; + POST = 2; + HEAD = 3; + PUT = 4; + DELETE = 5; + } + optional RequestMethod method = 5; + + optional int32 retry_count = 6 [default=0]; + + repeated group Header = 7 { + required bytes key = 8; + required bytes value = 9; + } + + optional int32 body_size = 10; + optional bytes body = 11 [ctype=CORD]; + required int64 creation_time_usec = 12; + + optional group CronTimetable = 13 { + required bytes schedule = 14; + required bytes timezone = 15; + } + + optional group RunLog = 16 { + required int64 dispatched_usec = 17; + required int64 lag_usec = 18; + required int64 elapsed_usec = 19; + optional int64 response_code = 20; + optional string retry_reason = 27; + } + + optional bytes description = 21; + optional TaskPayload payload = 22; + optional TaskQueueRetryParameters retry_parameters = 23; + optional int64 first_try_usec = 24; + optional bytes tag = 25; + optional int32 execution_count = 26 [default=0]; + } +} + +message TaskQueueFetchTaskRequest { + optional bytes app_id = 1; + required bytes queue_name = 2; + required bytes task_name = 3; +} + +message TaskQueueFetchTaskResponse { + required TaskQueueQueryTasksResponse task = 1; +} + +message TaskQueueUpdateStorageLimitRequest { + required bytes app_id = 1; + required int64 limit = 2; +} + +message TaskQueueUpdateStorageLimitResponse { + required int64 new_limit = 1; +} + +message TaskQueueQueryAndOwnTasksRequest { + required bytes queue_name = 1; + required double lease_seconds = 2; + required int64 max_tasks = 3; + optional bool group_by_tag = 4 [default=false]; + optional bytes tag = 5; +} + +message TaskQueueQueryAndOwnTasksResponse { + repeated group Task = 1 { + required bytes task_name = 2; + required int64 eta_usec = 3; + optional int32 retry_count = 4 [default=0]; + optional bytes body = 5 [ctype=CORD]; + optional bytes tag = 6; + } +} + +message TaskQueueModifyTaskLeaseRequest { + required bytes queue_name = 1; + required bytes task_name = 2; + required int64 eta_usec = 3; + required double lease_seconds = 4; +} + +message TaskQueueModifyTaskLeaseResponse { + required int64 updated_eta_usec = 1; +} diff --git a/vendor/google.golang.org/appengine/internal/transaction.go b/vendor/google.golang.org/appengine/internal/transaction.go new file mode 100644 index 0000000000..9006ae6538 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/transaction.go @@ -0,0 +1,115 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package internal + +// This file implements hooks for applying datastore transactions. + +import ( + "errors" + "reflect" + + "github.com/golang/protobuf/proto" + netcontext "golang.org/x/net/context" + + basepb "google.golang.org/appengine/internal/base" + pb "google.golang.org/appengine/internal/datastore" +) + +var transactionSetters = make(map[reflect.Type]reflect.Value) + +// RegisterTransactionSetter registers a function that sets transaction information +// in a protocol buffer message. f should be a function with two arguments, +// the first being a protocol buffer type, and the second being *datastore.Transaction. +func RegisterTransactionSetter(f interface{}) { + v := reflect.ValueOf(f) + transactionSetters[v.Type().In(0)] = v +} + +// applyTransaction applies the transaction t to message pb +// by using the relevant setter passed to RegisterTransactionSetter. +func applyTransaction(pb proto.Message, t *pb.Transaction) { + v := reflect.ValueOf(pb) + if f, ok := transactionSetters[v.Type()]; ok { + f.Call([]reflect.Value{v, reflect.ValueOf(t)}) + } +} + +var transactionKey = "used for *Transaction" + +func transactionFromContext(ctx netcontext.Context) *transaction { + t, _ := ctx.Value(&transactionKey).(*transaction) + return t +} + +func withTransaction(ctx netcontext.Context, t *transaction) netcontext.Context { + return netcontext.WithValue(ctx, &transactionKey, t) +} + +type transaction struct { + transaction pb.Transaction + finished bool +} + +var ErrConcurrentTransaction = errors.New("internal: concurrent transaction") + +func RunTransactionOnce(c netcontext.Context, f func(netcontext.Context) error, xg bool, readOnly bool, previousTransaction *pb.Transaction) (*pb.Transaction, error) { + if transactionFromContext(c) != nil { + return nil, errors.New("nested transactions are not supported") + } + + // Begin the transaction. + t := &transaction{} + req := &pb.BeginTransactionRequest{ + App: proto.String(FullyQualifiedAppID(c)), + } + if xg { + req.AllowMultipleEg = proto.Bool(true) + } + if previousTransaction != nil { + req.PreviousTransaction = previousTransaction + } + if readOnly { + req.Mode = pb.BeginTransactionRequest_READ_ONLY.Enum() + } else { + req.Mode = pb.BeginTransactionRequest_READ_WRITE.Enum() + } + if err := Call(c, "datastore_v3", "BeginTransaction", req, &t.transaction); err != nil { + return nil, err + } + + // Call f, rolling back the transaction if f returns a non-nil error, or panics. + // The panic is not recovered. + defer func() { + if t.finished { + return + } + t.finished = true + // Ignore the error return value, since we are already returning a non-nil + // error (or we're panicking). + Call(c, "datastore_v3", "Rollback", &t.transaction, &basepb.VoidProto{}) + }() + if err := f(withTransaction(c, t)); err != nil { + return &t.transaction, err + } + t.finished = true + + // Commit the transaction. + res := &pb.CommitResponse{} + err := Call(c, "datastore_v3", "Commit", &t.transaction, res) + if ae, ok := err.(*APIError); ok { + /* TODO: restore this conditional + if appengine.IsDevAppServer() { + */ + // The Python Dev AppServer raises an ApplicationError with error code 2 (which is + // Error.CONCURRENT_TRANSACTION) and message "Concurrency exception.". + if ae.Code == int32(pb.Error_BAD_REQUEST) && ae.Detail == "ApplicationError: 2 Concurrency exception." { + return &t.transaction, ErrConcurrentTransaction + } + if ae.Code == int32(pb.Error_CONCURRENT_TRANSACTION) { + return &t.transaction, ErrConcurrentTransaction + } + } + return &t.transaction, err +} diff --git a/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go new file mode 100644 index 0000000000..7c96c9d403 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go @@ -0,0 +1,433 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto + +/* +Package urlfetch is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto + +It has these top-level messages: + URLFetchServiceError + URLFetchRequest + URLFetchResponse +*/ +package urlfetch + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type URLFetchServiceError_ErrorCode int32 + +const ( + URLFetchServiceError_OK URLFetchServiceError_ErrorCode = 0 + URLFetchServiceError_INVALID_URL URLFetchServiceError_ErrorCode = 1 + URLFetchServiceError_FETCH_ERROR URLFetchServiceError_ErrorCode = 2 + URLFetchServiceError_UNSPECIFIED_ERROR URLFetchServiceError_ErrorCode = 3 + URLFetchServiceError_RESPONSE_TOO_LARGE URLFetchServiceError_ErrorCode = 4 + URLFetchServiceError_DEADLINE_EXCEEDED URLFetchServiceError_ErrorCode = 5 + URLFetchServiceError_SSL_CERTIFICATE_ERROR URLFetchServiceError_ErrorCode = 6 + URLFetchServiceError_DNS_ERROR URLFetchServiceError_ErrorCode = 7 + URLFetchServiceError_CLOSED URLFetchServiceError_ErrorCode = 8 + URLFetchServiceError_INTERNAL_TRANSIENT_ERROR URLFetchServiceError_ErrorCode = 9 + URLFetchServiceError_TOO_MANY_REDIRECTS URLFetchServiceError_ErrorCode = 10 + URLFetchServiceError_MALFORMED_REPLY URLFetchServiceError_ErrorCode = 11 + URLFetchServiceError_CONNECTION_ERROR URLFetchServiceError_ErrorCode = 12 +) + +var URLFetchServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "INVALID_URL", + 2: "FETCH_ERROR", + 3: "UNSPECIFIED_ERROR", + 4: "RESPONSE_TOO_LARGE", + 5: "DEADLINE_EXCEEDED", + 6: "SSL_CERTIFICATE_ERROR", + 7: "DNS_ERROR", + 8: "CLOSED", + 9: "INTERNAL_TRANSIENT_ERROR", + 10: "TOO_MANY_REDIRECTS", + 11: "MALFORMED_REPLY", + 12: "CONNECTION_ERROR", +} +var URLFetchServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "INVALID_URL": 1, + "FETCH_ERROR": 2, + "UNSPECIFIED_ERROR": 3, + "RESPONSE_TOO_LARGE": 4, + "DEADLINE_EXCEEDED": 5, + "SSL_CERTIFICATE_ERROR": 6, + "DNS_ERROR": 7, + "CLOSED": 8, + "INTERNAL_TRANSIENT_ERROR": 9, + "TOO_MANY_REDIRECTS": 10, + "MALFORMED_REPLY": 11, + "CONNECTION_ERROR": 12, +} + +func (x URLFetchServiceError_ErrorCode) Enum() *URLFetchServiceError_ErrorCode { + p := new(URLFetchServiceError_ErrorCode) + *p = x + return p +} +func (x URLFetchServiceError_ErrorCode) String() string { + return proto.EnumName(URLFetchServiceError_ErrorCode_name, int32(x)) +} +func (x *URLFetchServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(URLFetchServiceError_ErrorCode_value, data, "URLFetchServiceError_ErrorCode") + if err != nil { + return err + } + *x = URLFetchServiceError_ErrorCode(value) + return nil +} +func (URLFetchServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type URLFetchRequest_RequestMethod int32 + +const ( + URLFetchRequest_GET URLFetchRequest_RequestMethod = 1 + URLFetchRequest_POST URLFetchRequest_RequestMethod = 2 + URLFetchRequest_HEAD URLFetchRequest_RequestMethod = 3 + URLFetchRequest_PUT URLFetchRequest_RequestMethod = 4 + URLFetchRequest_DELETE URLFetchRequest_RequestMethod = 5 + URLFetchRequest_PATCH URLFetchRequest_RequestMethod = 6 +) + +var URLFetchRequest_RequestMethod_name = map[int32]string{ + 1: "GET", + 2: "POST", + 3: "HEAD", + 4: "PUT", + 5: "DELETE", + 6: "PATCH", +} +var URLFetchRequest_RequestMethod_value = map[string]int32{ + "GET": 1, + "POST": 2, + "HEAD": 3, + "PUT": 4, + "DELETE": 5, + "PATCH": 6, +} + +func (x URLFetchRequest_RequestMethod) Enum() *URLFetchRequest_RequestMethod { + p := new(URLFetchRequest_RequestMethod) + *p = x + return p +} +func (x URLFetchRequest_RequestMethod) String() string { + return proto.EnumName(URLFetchRequest_RequestMethod_name, int32(x)) +} +func (x *URLFetchRequest_RequestMethod) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(URLFetchRequest_RequestMethod_value, data, "URLFetchRequest_RequestMethod") + if err != nil { + return err + } + *x = URLFetchRequest_RequestMethod(value) + return nil +} +func (URLFetchRequest_RequestMethod) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{1, 0} +} + +type URLFetchServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *URLFetchServiceError) Reset() { *m = URLFetchServiceError{} } +func (m *URLFetchServiceError) String() string { return proto.CompactTextString(m) } +func (*URLFetchServiceError) ProtoMessage() {} +func (*URLFetchServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type URLFetchRequest struct { + Method *URLFetchRequest_RequestMethod `protobuf:"varint,1,req,name=Method,enum=appengine.URLFetchRequest_RequestMethod" json:"Method,omitempty"` + Url *string `protobuf:"bytes,2,req,name=Url" json:"Url,omitempty"` + Header []*URLFetchRequest_Header `protobuf:"group,3,rep,name=Header,json=header" json:"header,omitempty"` + Payload []byte `protobuf:"bytes,6,opt,name=Payload" json:"Payload,omitempty"` + FollowRedirects *bool `protobuf:"varint,7,opt,name=FollowRedirects,def=1" json:"FollowRedirects,omitempty"` + Deadline *float64 `protobuf:"fixed64,8,opt,name=Deadline" json:"Deadline,omitempty"` + MustValidateServerCertificate *bool `protobuf:"varint,9,opt,name=MustValidateServerCertificate,def=1" json:"MustValidateServerCertificate,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *URLFetchRequest) Reset() { *m = URLFetchRequest{} } +func (m *URLFetchRequest) String() string { return proto.CompactTextString(m) } +func (*URLFetchRequest) ProtoMessage() {} +func (*URLFetchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +const Default_URLFetchRequest_FollowRedirects bool = true +const Default_URLFetchRequest_MustValidateServerCertificate bool = true + +func (m *URLFetchRequest) GetMethod() URLFetchRequest_RequestMethod { + if m != nil && m.Method != nil { + return *m.Method + } + return URLFetchRequest_GET +} + +func (m *URLFetchRequest) GetUrl() string { + if m != nil && m.Url != nil { + return *m.Url + } + return "" +} + +func (m *URLFetchRequest) GetHeader() []*URLFetchRequest_Header { + if m != nil { + return m.Header + } + return nil +} + +func (m *URLFetchRequest) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *URLFetchRequest) GetFollowRedirects() bool { + if m != nil && m.FollowRedirects != nil { + return *m.FollowRedirects + } + return Default_URLFetchRequest_FollowRedirects +} + +func (m *URLFetchRequest) GetDeadline() float64 { + if m != nil && m.Deadline != nil { + return *m.Deadline + } + return 0 +} + +func (m *URLFetchRequest) GetMustValidateServerCertificate() bool { + if m != nil && m.MustValidateServerCertificate != nil { + return *m.MustValidateServerCertificate + } + return Default_URLFetchRequest_MustValidateServerCertificate +} + +type URLFetchRequest_Header struct { + Key *string `protobuf:"bytes,4,req,name=Key" json:"Key,omitempty"` + Value *string `protobuf:"bytes,5,req,name=Value" json:"Value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *URLFetchRequest_Header) Reset() { *m = URLFetchRequest_Header{} } +func (m *URLFetchRequest_Header) String() string { return proto.CompactTextString(m) } +func (*URLFetchRequest_Header) ProtoMessage() {} +func (*URLFetchRequest_Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} } + +func (m *URLFetchRequest_Header) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +func (m *URLFetchRequest_Header) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +type URLFetchResponse struct { + Content []byte `protobuf:"bytes,1,opt,name=Content" json:"Content,omitempty"` + StatusCode *int32 `protobuf:"varint,2,req,name=StatusCode" json:"StatusCode,omitempty"` + Header []*URLFetchResponse_Header `protobuf:"group,3,rep,name=Header,json=header" json:"header,omitempty"` + ContentWasTruncated *bool `protobuf:"varint,6,opt,name=ContentWasTruncated,def=0" json:"ContentWasTruncated,omitempty"` + ExternalBytesSent *int64 `protobuf:"varint,7,opt,name=ExternalBytesSent" json:"ExternalBytesSent,omitempty"` + ExternalBytesReceived *int64 `protobuf:"varint,8,opt,name=ExternalBytesReceived" json:"ExternalBytesReceived,omitempty"` + FinalUrl *string `protobuf:"bytes,9,opt,name=FinalUrl" json:"FinalUrl,omitempty"` + ApiCpuMilliseconds *int64 `protobuf:"varint,10,opt,name=ApiCpuMilliseconds,def=0" json:"ApiCpuMilliseconds,omitempty"` + ApiBytesSent *int64 `protobuf:"varint,11,opt,name=ApiBytesSent,def=0" json:"ApiBytesSent,omitempty"` + ApiBytesReceived *int64 `protobuf:"varint,12,opt,name=ApiBytesReceived,def=0" json:"ApiBytesReceived,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *URLFetchResponse) Reset() { *m = URLFetchResponse{} } +func (m *URLFetchResponse) String() string { return proto.CompactTextString(m) } +func (*URLFetchResponse) ProtoMessage() {} +func (*URLFetchResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +const Default_URLFetchResponse_ContentWasTruncated bool = false +const Default_URLFetchResponse_ApiCpuMilliseconds int64 = 0 +const Default_URLFetchResponse_ApiBytesSent int64 = 0 +const Default_URLFetchResponse_ApiBytesReceived int64 = 0 + +func (m *URLFetchResponse) GetContent() []byte { + if m != nil { + return m.Content + } + return nil +} + +func (m *URLFetchResponse) GetStatusCode() int32 { + if m != nil && m.StatusCode != nil { + return *m.StatusCode + } + return 0 +} + +func (m *URLFetchResponse) GetHeader() []*URLFetchResponse_Header { + if m != nil { + return m.Header + } + return nil +} + +func (m *URLFetchResponse) GetContentWasTruncated() bool { + if m != nil && m.ContentWasTruncated != nil { + return *m.ContentWasTruncated + } + return Default_URLFetchResponse_ContentWasTruncated +} + +func (m *URLFetchResponse) GetExternalBytesSent() int64 { + if m != nil && m.ExternalBytesSent != nil { + return *m.ExternalBytesSent + } + return 0 +} + +func (m *URLFetchResponse) GetExternalBytesReceived() int64 { + if m != nil && m.ExternalBytesReceived != nil { + return *m.ExternalBytesReceived + } + return 0 +} + +func (m *URLFetchResponse) GetFinalUrl() string { + if m != nil && m.FinalUrl != nil { + return *m.FinalUrl + } + return "" +} + +func (m *URLFetchResponse) GetApiCpuMilliseconds() int64 { + if m != nil && m.ApiCpuMilliseconds != nil { + return *m.ApiCpuMilliseconds + } + return Default_URLFetchResponse_ApiCpuMilliseconds +} + +func (m *URLFetchResponse) GetApiBytesSent() int64 { + if m != nil && m.ApiBytesSent != nil { + return *m.ApiBytesSent + } + return Default_URLFetchResponse_ApiBytesSent +} + +func (m *URLFetchResponse) GetApiBytesReceived() int64 { + if m != nil && m.ApiBytesReceived != nil { + return *m.ApiBytesReceived + } + return Default_URLFetchResponse_ApiBytesReceived +} + +type URLFetchResponse_Header struct { + Key *string `protobuf:"bytes,4,req,name=Key" json:"Key,omitempty"` + Value *string `protobuf:"bytes,5,req,name=Value" json:"Value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *URLFetchResponse_Header) Reset() { *m = URLFetchResponse_Header{} } +func (m *URLFetchResponse_Header) String() string { return proto.CompactTextString(m) } +func (*URLFetchResponse_Header) ProtoMessage() {} +func (*URLFetchResponse_Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } + +func (m *URLFetchResponse_Header) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +func (m *URLFetchResponse_Header) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +func init() { + proto.RegisterType((*URLFetchServiceError)(nil), "appengine.URLFetchServiceError") + proto.RegisterType((*URLFetchRequest)(nil), "appengine.URLFetchRequest") + proto.RegisterType((*URLFetchRequest_Header)(nil), "appengine.URLFetchRequest.Header") + proto.RegisterType((*URLFetchResponse)(nil), "appengine.URLFetchResponse") + proto.RegisterType((*URLFetchResponse_Header)(nil), "appengine.URLFetchResponse.Header") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 770 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x6e, 0xe3, 0x54, + 0x10, 0xc6, 0x76, 0x7e, 0xa7, 0x5d, 0x7a, 0x76, 0xb6, 0x45, 0x66, 0xb5, 0xa0, 0x10, 0x09, 0x29, + 0x17, 0x90, 0x2e, 0x2b, 0x24, 0x44, 0xaf, 0x70, 0xed, 0x93, 0xad, 0xa9, 0x63, 0x47, 0xc7, 0x4e, + 0x61, 0xb9, 0xb1, 0xac, 0x78, 0x9a, 0x5a, 0xb2, 0xec, 0x60, 0x9f, 0x2c, 0xf4, 0x35, 0x78, 0x0d, + 0xde, 0x87, 0xa7, 0xe1, 0x02, 0x9d, 0xc4, 0xc9, 0x6e, 0xbb, 0xd1, 0x4a, 0x5c, 0x65, 0xe6, 0x9b, + 0xef, 0xcc, 0x99, 0x7c, 0xdf, 0xf8, 0x80, 0xb3, 0x2c, 0xcb, 0x65, 0x4e, 0xe3, 0x65, 0x99, 0x27, + 0xc5, 0x72, 0x5c, 0x56, 0xcb, 0xf3, 0x64, 0xb5, 0xa2, 0x62, 0x99, 0x15, 0x74, 0x9e, 0x15, 0x92, + 0xaa, 0x22, 0xc9, 0xcf, 0xd7, 0x55, 0x7e, 0x4b, 0x72, 0x71, 0xb7, 0x0f, 0xe2, 0x9a, 0xaa, 0xb7, + 0xd9, 0x82, 0xc6, 0xab, 0xaa, 0x94, 0x25, 0xf6, 0xf7, 0x67, 0x86, 0x7f, 0xeb, 0x70, 0x3a, 0x17, + 0xde, 0x44, 0xb1, 0xc2, 0x2d, 0x89, 0x57, 0x55, 0x59, 0x0d, 0xff, 0xd2, 0xa1, 0xbf, 0x89, 0xec, + 0x32, 0x25, 0xec, 0x80, 0x1e, 0x5c, 0xb3, 0x4f, 0xf0, 0x04, 0x8e, 0x5c, 0xff, 0xc6, 0xf2, 0x5c, + 0x27, 0x9e, 0x0b, 0x8f, 0x69, 0x0a, 0x98, 0xf0, 0xc8, 0xbe, 0x8a, 0xb9, 0x10, 0x81, 0x60, 0x3a, + 0x9e, 0xc1, 0xd3, 0xb9, 0x1f, 0xce, 0xb8, 0xed, 0x4e, 0x5c, 0xee, 0x34, 0xb0, 0x81, 0x9f, 0x01, + 0x0a, 0x1e, 0xce, 0x02, 0x3f, 0xe4, 0x71, 0x14, 0x04, 0xb1, 0x67, 0x89, 0xd7, 0x9c, 0xb5, 0x14, + 0xdd, 0xe1, 0x96, 0xe3, 0xb9, 0x3e, 0x8f, 0xf9, 0xaf, 0x36, 0xe7, 0x0e, 0x77, 0x58, 0x1b, 0x3f, + 0x87, 0xb3, 0x30, 0xf4, 0x62, 0x9b, 0x8b, 0xc8, 0x9d, 0xb8, 0xb6, 0x15, 0xf1, 0xa6, 0x53, 0x07, + 0x9f, 0x40, 0xdf, 0xf1, 0xc3, 0x26, 0xed, 0x22, 0x40, 0xc7, 0xf6, 0x82, 0x90, 0x3b, 0xac, 0x87, + 0x2f, 0xc0, 0x74, 0xfd, 0x88, 0x0b, 0xdf, 0xf2, 0xe2, 0x48, 0x58, 0x7e, 0xe8, 0x72, 0x3f, 0x6a, + 0x98, 0x7d, 0x35, 0x82, 0xba, 0x79, 0x6a, 0xf9, 0x6f, 0x62, 0xc1, 0x1d, 0x57, 0x70, 0x3b, 0x0a, + 0x19, 0xe0, 0x33, 0x38, 0x99, 0x5a, 0xde, 0x24, 0x10, 0x53, 0xee, 0xc4, 0x82, 0xcf, 0xbc, 0x37, + 0xec, 0x08, 0x4f, 0x81, 0xd9, 0x81, 0xef, 0x73, 0x3b, 0x72, 0x03, 0xbf, 0x69, 0x71, 0x3c, 0xfc, + 0xc7, 0x80, 0x93, 0x9d, 0x5a, 0x82, 0x7e, 0x5f, 0x53, 0x2d, 0xf1, 0x27, 0xe8, 0x4c, 0x49, 0xde, + 0x95, 0xa9, 0xa9, 0x0d, 0xf4, 0xd1, 0xa7, 0xaf, 0x46, 0xe3, 0xbd, 0xba, 0xe3, 0x47, 0xdc, 0x71, + 0xf3, 0xbb, 0xe5, 0x8b, 0xe6, 0x1c, 0x32, 0x30, 0xe6, 0x55, 0x6e, 0xea, 0x03, 0x7d, 0xd4, 0x17, + 0x2a, 0xc4, 0x1f, 0xa1, 0x73, 0x47, 0x49, 0x4a, 0x95, 0x69, 0x0c, 0x8c, 0x11, 0xbc, 0xfa, 0xea, + 0x23, 0x3d, 0xaf, 0x36, 0x44, 0xd1, 0x1c, 0xc0, 0x17, 0xd0, 0x9d, 0x25, 0xf7, 0x79, 0x99, 0xa4, + 0x66, 0x67, 0xa0, 0x8d, 0x8e, 0x2f, 0xf5, 0x9e, 0x26, 0x76, 0x10, 0x8e, 0xe1, 0x64, 0x52, 0xe6, + 0x79, 0xf9, 0x87, 0xa0, 0x34, 0xab, 0x68, 0x21, 0x6b, 0xb3, 0x3b, 0xd0, 0x46, 0xbd, 0x8b, 0x96, + 0xac, 0xd6, 0x24, 0x1e, 0x17, 0xf1, 0x39, 0xf4, 0x1c, 0x4a, 0xd2, 0x3c, 0x2b, 0xc8, 0xec, 0x0d, + 0xb4, 0x91, 0x26, 0xf6, 0x39, 0xfe, 0x0c, 0x5f, 0x4c, 0xd7, 0xb5, 0xbc, 0x49, 0xf2, 0x2c, 0x4d, + 0x24, 0xa9, 0xed, 0xa1, 0xca, 0xa6, 0x4a, 0x66, 0xb7, 0xd9, 0x22, 0x91, 0x64, 0xf6, 0xdf, 0xeb, + 0xfc, 0x71, 0xea, 0xf3, 0x97, 0xd0, 0xd9, 0xfe, 0x0f, 0x25, 0xc6, 0x35, 0xdd, 0x9b, 0xad, 0xad, + 0x18, 0xd7, 0x74, 0x8f, 0xa7, 0xd0, 0xbe, 0x49, 0xf2, 0x35, 0x99, 0xed, 0x0d, 0xb6, 0x4d, 0x86, + 0x1e, 0x3c, 0x79, 0xa0, 0x26, 0x76, 0xc1, 0x78, 0xcd, 0x23, 0xa6, 0x61, 0x0f, 0x5a, 0xb3, 0x20, + 0x8c, 0x98, 0xae, 0xa2, 0x2b, 0x6e, 0x39, 0xcc, 0x50, 0xc5, 0xd9, 0x3c, 0x62, 0x2d, 0xb5, 0x2e, + 0x0e, 0xf7, 0x78, 0xc4, 0x59, 0x1b, 0xfb, 0xd0, 0x9e, 0x59, 0x91, 0x7d, 0xc5, 0x3a, 0xc3, 0x7f, + 0x0d, 0x60, 0xef, 0x84, 0xad, 0x57, 0x65, 0x51, 0x13, 0x9a, 0xd0, 0xb5, 0xcb, 0x42, 0x52, 0x21, + 0x4d, 0x4d, 0x49, 0x29, 0x76, 0x29, 0x7e, 0x09, 0x10, 0xca, 0x44, 0xae, 0x6b, 0xf5, 0x71, 0x6c, + 0x8c, 0x6b, 0x8b, 0xf7, 0x10, 0xbc, 0x78, 0xe4, 0xdf, 0xf0, 0xa0, 0x7f, 0xdb, 0x6b, 0x1e, 0x1b, + 0xf8, 0x03, 0x3c, 0x6b, 0xae, 0xf9, 0x25, 0xa9, 0xa3, 0x6a, 0x5d, 0x28, 0x81, 0xb6, 0x66, 0xf6, + 0x2e, 0xda, 0xb7, 0x49, 0x5e, 0x93, 0x38, 0xc4, 0xc0, 0x6f, 0xe0, 0x29, 0xff, 0x73, 0xfb, 0x02, + 0x5c, 0xde, 0x4b, 0xaa, 0x43, 0x35, 0xb8, 0x72, 0xd7, 0x10, 0x1f, 0x16, 0xf0, 0x7b, 0x38, 0x7b, + 0x00, 0x0a, 0x5a, 0x50, 0xf6, 0x96, 0xd2, 0x8d, 0xcd, 0x86, 0x38, 0x5c, 0x54, 0xfb, 0x30, 0xc9, + 0x8a, 0x24, 0x57, 0xfb, 0xaa, 0xec, 0xed, 0x8b, 0x7d, 0x8e, 0xdf, 0x01, 0x5a, 0xab, 0xcc, 0x5e, + 0xad, 0xa7, 0x59, 0x9e, 0x67, 0x35, 0x2d, 0xca, 0x22, 0xad, 0x4d, 0x50, 0xed, 0x2e, 0xb4, 0x97, + 0xe2, 0x40, 0x11, 0xbf, 0x86, 0x63, 0x6b, 0x95, 0xbd, 0x9b, 0xf6, 0x68, 0x47, 0x7e, 0x00, 0xe3, + 0xb7, 0xc0, 0x76, 0xf9, 0x7e, 0xcc, 0xe3, 0x1d, 0xf5, 0x83, 0xd2, 0xff, 0x5f, 0xa6, 0x4b, 0xf8, + 0xad, 0xb7, 0x7b, 0x2a, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x9f, 0x6d, 0x24, 0x63, 0x05, + 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto new file mode 100644 index 0000000000..f695edf6a9 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto @@ -0,0 +1,64 @@ +syntax = "proto2"; +option go_package = "urlfetch"; + +package appengine; + +message URLFetchServiceError { + enum ErrorCode { + OK = 0; + INVALID_URL = 1; + FETCH_ERROR = 2; + UNSPECIFIED_ERROR = 3; + RESPONSE_TOO_LARGE = 4; + DEADLINE_EXCEEDED = 5; + SSL_CERTIFICATE_ERROR = 6; + DNS_ERROR = 7; + CLOSED = 8; + INTERNAL_TRANSIENT_ERROR = 9; + TOO_MANY_REDIRECTS = 10; + MALFORMED_REPLY = 11; + CONNECTION_ERROR = 12; + } +} + +message URLFetchRequest { + enum RequestMethod { + GET = 1; + POST = 2; + HEAD = 3; + PUT = 4; + DELETE = 5; + PATCH = 6; + } + required RequestMethod Method = 1; + required string Url = 2; + repeated group Header = 3 { + required string Key = 4; + required string Value = 5; + } + optional bytes Payload = 6 [ctype=CORD]; + + optional bool FollowRedirects = 7 [default=true]; + + optional double Deadline = 8; + + optional bool MustValidateServerCertificate = 9 [default=true]; +} + +message URLFetchResponse { + optional bytes Content = 1; + required int32 StatusCode = 2; + repeated group Header = 3 { + required string Key = 4; + required string Value = 5; + } + optional bool ContentWasTruncated = 6 [default=false]; + optional int64 ExternalBytesSent = 7; + optional int64 ExternalBytesReceived = 8; + + optional string FinalUrl = 9; + + optional int64 ApiCpuMilliseconds = 10 [default=0]; + optional int64 ApiBytesSent = 11 [default=0]; + optional int64 ApiBytesReceived = 12 [default=0]; +} diff --git a/vendor/google.golang.org/appengine/internal/user/user_service.pb.go b/vendor/google.golang.org/appengine/internal/user/user_service.pb.go new file mode 100644 index 0000000000..f2a61dee32 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/user/user_service.pb.go @@ -0,0 +1,359 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/user/user_service.proto + +/* +Package user is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/user/user_service.proto + +It has these top-level messages: + UserServiceError + CreateLoginURLRequest + CreateLoginURLResponse + CreateLogoutURLRequest + CreateLogoutURLResponse + GetOAuthUserRequest + GetOAuthUserResponse + CheckOAuthSignatureRequest + CheckOAuthSignatureResponse +*/ +package user + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type UserServiceError_ErrorCode int32 + +const ( + UserServiceError_OK UserServiceError_ErrorCode = 0 + UserServiceError_REDIRECT_URL_TOO_LONG UserServiceError_ErrorCode = 1 + UserServiceError_NOT_ALLOWED UserServiceError_ErrorCode = 2 + UserServiceError_OAUTH_INVALID_TOKEN UserServiceError_ErrorCode = 3 + UserServiceError_OAUTH_INVALID_REQUEST UserServiceError_ErrorCode = 4 + UserServiceError_OAUTH_ERROR UserServiceError_ErrorCode = 5 +) + +var UserServiceError_ErrorCode_name = map[int32]string{ + 0: "OK", + 1: "REDIRECT_URL_TOO_LONG", + 2: "NOT_ALLOWED", + 3: "OAUTH_INVALID_TOKEN", + 4: "OAUTH_INVALID_REQUEST", + 5: "OAUTH_ERROR", +} +var UserServiceError_ErrorCode_value = map[string]int32{ + "OK": 0, + "REDIRECT_URL_TOO_LONG": 1, + "NOT_ALLOWED": 2, + "OAUTH_INVALID_TOKEN": 3, + "OAUTH_INVALID_REQUEST": 4, + "OAUTH_ERROR": 5, +} + +func (x UserServiceError_ErrorCode) Enum() *UserServiceError_ErrorCode { + p := new(UserServiceError_ErrorCode) + *p = x + return p +} +func (x UserServiceError_ErrorCode) String() string { + return proto.EnumName(UserServiceError_ErrorCode_name, int32(x)) +} +func (x *UserServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(UserServiceError_ErrorCode_value, data, "UserServiceError_ErrorCode") + if err != nil { + return err + } + *x = UserServiceError_ErrorCode(value) + return nil +} +func (UserServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type UserServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *UserServiceError) Reset() { *m = UserServiceError{} } +func (m *UserServiceError) String() string { return proto.CompactTextString(m) } +func (*UserServiceError) ProtoMessage() {} +func (*UserServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type CreateLoginURLRequest struct { + DestinationUrl *string `protobuf:"bytes,1,req,name=destination_url,json=destinationUrl" json:"destination_url,omitempty"` + AuthDomain *string `protobuf:"bytes,2,opt,name=auth_domain,json=authDomain" json:"auth_domain,omitempty"` + FederatedIdentity *string `protobuf:"bytes,3,opt,name=federated_identity,json=federatedIdentity,def=" json:"federated_identity,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateLoginURLRequest) Reset() { *m = CreateLoginURLRequest{} } +func (m *CreateLoginURLRequest) String() string { return proto.CompactTextString(m) } +func (*CreateLoginURLRequest) ProtoMessage() {} +func (*CreateLoginURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *CreateLoginURLRequest) GetDestinationUrl() string { + if m != nil && m.DestinationUrl != nil { + return *m.DestinationUrl + } + return "" +} + +func (m *CreateLoginURLRequest) GetAuthDomain() string { + if m != nil && m.AuthDomain != nil { + return *m.AuthDomain + } + return "" +} + +func (m *CreateLoginURLRequest) GetFederatedIdentity() string { + if m != nil && m.FederatedIdentity != nil { + return *m.FederatedIdentity + } + return "" +} + +type CreateLoginURLResponse struct { + LoginUrl *string `protobuf:"bytes,1,req,name=login_url,json=loginUrl" json:"login_url,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateLoginURLResponse) Reset() { *m = CreateLoginURLResponse{} } +func (m *CreateLoginURLResponse) String() string { return proto.CompactTextString(m) } +func (*CreateLoginURLResponse) ProtoMessage() {} +func (*CreateLoginURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *CreateLoginURLResponse) GetLoginUrl() string { + if m != nil && m.LoginUrl != nil { + return *m.LoginUrl + } + return "" +} + +type CreateLogoutURLRequest struct { + DestinationUrl *string `protobuf:"bytes,1,req,name=destination_url,json=destinationUrl" json:"destination_url,omitempty"` + AuthDomain *string `protobuf:"bytes,2,opt,name=auth_domain,json=authDomain" json:"auth_domain,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateLogoutURLRequest) Reset() { *m = CreateLogoutURLRequest{} } +func (m *CreateLogoutURLRequest) String() string { return proto.CompactTextString(m) } +func (*CreateLogoutURLRequest) ProtoMessage() {} +func (*CreateLogoutURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *CreateLogoutURLRequest) GetDestinationUrl() string { + if m != nil && m.DestinationUrl != nil { + return *m.DestinationUrl + } + return "" +} + +func (m *CreateLogoutURLRequest) GetAuthDomain() string { + if m != nil && m.AuthDomain != nil { + return *m.AuthDomain + } + return "" +} + +type CreateLogoutURLResponse struct { + LogoutUrl *string `protobuf:"bytes,1,req,name=logout_url,json=logoutUrl" json:"logout_url,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CreateLogoutURLResponse) Reset() { *m = CreateLogoutURLResponse{} } +func (m *CreateLogoutURLResponse) String() string { return proto.CompactTextString(m) } +func (*CreateLogoutURLResponse) ProtoMessage() {} +func (*CreateLogoutURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *CreateLogoutURLResponse) GetLogoutUrl() string { + if m != nil && m.LogoutUrl != nil { + return *m.LogoutUrl + } + return "" +} + +type GetOAuthUserRequest struct { + Scope *string `protobuf:"bytes,1,opt,name=scope" json:"scope,omitempty"` + Scopes []string `protobuf:"bytes,2,rep,name=scopes" json:"scopes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetOAuthUserRequest) Reset() { *m = GetOAuthUserRequest{} } +func (m *GetOAuthUserRequest) String() string { return proto.CompactTextString(m) } +func (*GetOAuthUserRequest) ProtoMessage() {} +func (*GetOAuthUserRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *GetOAuthUserRequest) GetScope() string { + if m != nil && m.Scope != nil { + return *m.Scope + } + return "" +} + +func (m *GetOAuthUserRequest) GetScopes() []string { + if m != nil { + return m.Scopes + } + return nil +} + +type GetOAuthUserResponse struct { + Email *string `protobuf:"bytes,1,req,name=email" json:"email,omitempty"` + UserId *string `protobuf:"bytes,2,req,name=user_id,json=userId" json:"user_id,omitempty"` + AuthDomain *string `protobuf:"bytes,3,req,name=auth_domain,json=authDomain" json:"auth_domain,omitempty"` + UserOrganization *string `protobuf:"bytes,4,opt,name=user_organization,json=userOrganization,def=" json:"user_organization,omitempty"` + IsAdmin *bool `protobuf:"varint,5,opt,name=is_admin,json=isAdmin,def=0" json:"is_admin,omitempty"` + ClientId *string `protobuf:"bytes,6,opt,name=client_id,json=clientId,def=" json:"client_id,omitempty"` + Scopes []string `protobuf:"bytes,7,rep,name=scopes" json:"scopes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetOAuthUserResponse) Reset() { *m = GetOAuthUserResponse{} } +func (m *GetOAuthUserResponse) String() string { return proto.CompactTextString(m) } +func (*GetOAuthUserResponse) ProtoMessage() {} +func (*GetOAuthUserResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +const Default_GetOAuthUserResponse_IsAdmin bool = false + +func (m *GetOAuthUserResponse) GetEmail() string { + if m != nil && m.Email != nil { + return *m.Email + } + return "" +} + +func (m *GetOAuthUserResponse) GetUserId() string { + if m != nil && m.UserId != nil { + return *m.UserId + } + return "" +} + +func (m *GetOAuthUserResponse) GetAuthDomain() string { + if m != nil && m.AuthDomain != nil { + return *m.AuthDomain + } + return "" +} + +func (m *GetOAuthUserResponse) GetUserOrganization() string { + if m != nil && m.UserOrganization != nil { + return *m.UserOrganization + } + return "" +} + +func (m *GetOAuthUserResponse) GetIsAdmin() bool { + if m != nil && m.IsAdmin != nil { + return *m.IsAdmin + } + return Default_GetOAuthUserResponse_IsAdmin +} + +func (m *GetOAuthUserResponse) GetClientId() string { + if m != nil && m.ClientId != nil { + return *m.ClientId + } + return "" +} + +func (m *GetOAuthUserResponse) GetScopes() []string { + if m != nil { + return m.Scopes + } + return nil +} + +type CheckOAuthSignatureRequest struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *CheckOAuthSignatureRequest) Reset() { *m = CheckOAuthSignatureRequest{} } +func (m *CheckOAuthSignatureRequest) String() string { return proto.CompactTextString(m) } +func (*CheckOAuthSignatureRequest) ProtoMessage() {} +func (*CheckOAuthSignatureRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +type CheckOAuthSignatureResponse struct { + OauthConsumerKey *string `protobuf:"bytes,1,req,name=oauth_consumer_key,json=oauthConsumerKey" json:"oauth_consumer_key,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CheckOAuthSignatureResponse) Reset() { *m = CheckOAuthSignatureResponse{} } +func (m *CheckOAuthSignatureResponse) String() string { return proto.CompactTextString(m) } +func (*CheckOAuthSignatureResponse) ProtoMessage() {} +func (*CheckOAuthSignatureResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *CheckOAuthSignatureResponse) GetOauthConsumerKey() string { + if m != nil && m.OauthConsumerKey != nil { + return *m.OauthConsumerKey + } + return "" +} + +func init() { + proto.RegisterType((*UserServiceError)(nil), "appengine.UserServiceError") + proto.RegisterType((*CreateLoginURLRequest)(nil), "appengine.CreateLoginURLRequest") + proto.RegisterType((*CreateLoginURLResponse)(nil), "appengine.CreateLoginURLResponse") + proto.RegisterType((*CreateLogoutURLRequest)(nil), "appengine.CreateLogoutURLRequest") + proto.RegisterType((*CreateLogoutURLResponse)(nil), "appengine.CreateLogoutURLResponse") + proto.RegisterType((*GetOAuthUserRequest)(nil), "appengine.GetOAuthUserRequest") + proto.RegisterType((*GetOAuthUserResponse)(nil), "appengine.GetOAuthUserResponse") + proto.RegisterType((*CheckOAuthSignatureRequest)(nil), "appengine.CheckOAuthSignatureRequest") + proto.RegisterType((*CheckOAuthSignatureResponse)(nil), "appengine.CheckOAuthSignatureResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/user/user_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 573 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x52, 0x4d, 0x6f, 0xdb, 0x38, + 0x10, 0x8d, 0xec, 0xd8, 0xb1, 0x26, 0xc0, 0x46, 0x61, 0xbe, 0xb4, 0x9b, 0x0d, 0xd6, 0xd0, 0x65, + 0x7d, 0x68, 0xe3, 0x53, 0x81, 0x22, 0xe8, 0xc5, 0xb5, 0x85, 0xd4, 0xb0, 0x60, 0xa1, 0x8c, 0xd5, + 0x02, 0xbd, 0x08, 0xac, 0x35, 0x51, 0x88, 0xc8, 0xa4, 0x4b, 0x52, 0x05, 0xd2, 0x73, 0x7f, 0x41, + 0x6f, 0xfd, 0x93, 0xfd, 0x0d, 0x85, 0x68, 0x25, 0x50, 0xd2, 0x5e, 0x7b, 0x11, 0x34, 0xef, 0x0d, + 0xdf, 0xbc, 0x37, 0x24, 0xbc, 0xca, 0xa5, 0xcc, 0x0b, 0x3c, 0xcf, 0x65, 0xc1, 0x44, 0x7e, 0x2e, + 0x55, 0x3e, 0x64, 0xeb, 0x35, 0x8a, 0x9c, 0x0b, 0x1c, 0x72, 0x61, 0x50, 0x09, 0x56, 0x0c, 0x4b, + 0x8d, 0xca, 0x7e, 0x52, 0x8d, 0xea, 0x33, 0x5f, 0xe2, 0xf9, 0x5a, 0x49, 0x23, 0x89, 0xfb, 0xd0, + 0x1b, 0x7c, 0x77, 0xc0, 0x4b, 0x34, 0xaa, 0xab, 0x4d, 0x43, 0xa8, 0x94, 0x54, 0xc1, 0x57, 0x07, + 0x5c, 0xfb, 0x37, 0x96, 0x19, 0x92, 0x2e, 0xb4, 0xe2, 0x99, 0xb7, 0x45, 0xfe, 0x86, 0x23, 0x1a, + 0x4e, 0xa6, 0x34, 0x1c, 0x2f, 0xd2, 0x84, 0x46, 0xe9, 0x22, 0x8e, 0xd3, 0x28, 0x9e, 0x5f, 0x7a, + 0x0e, 0xd9, 0x83, 0xdd, 0x79, 0xbc, 0x48, 0x47, 0x51, 0x14, 0xbf, 0x0f, 0x27, 0x5e, 0x8b, 0x9c, + 0xc0, 0x41, 0x3c, 0x4a, 0x16, 0x6f, 0xd2, 0xe9, 0xfc, 0xdd, 0x28, 0x9a, 0x4e, 0xd2, 0x45, 0x3c, + 0x0b, 0xe7, 0x5e, 0xbb, 0x12, 0x79, 0x4c, 0xd0, 0xf0, 0x6d, 0x12, 0x5e, 0x2d, 0xbc, 0xed, 0x4a, + 0x64, 0x43, 0x85, 0x94, 0xc6, 0xd4, 0xeb, 0x04, 0xdf, 0x1c, 0x38, 0x1a, 0x2b, 0x64, 0x06, 0x23, + 0x99, 0x73, 0x91, 0xd0, 0x88, 0xe2, 0xa7, 0x12, 0xb5, 0x21, 0xff, 0xc3, 0x5e, 0x86, 0xda, 0x70, + 0xc1, 0x0c, 0x97, 0x22, 0x2d, 0x55, 0xe1, 0x3b, 0xfd, 0xd6, 0xc0, 0xa5, 0x7f, 0x35, 0xe0, 0x44, + 0x15, 0xe4, 0x3f, 0xd8, 0x65, 0xa5, 0xb9, 0x49, 0x33, 0xb9, 0x62, 0x5c, 0xf8, 0xad, 0xbe, 0x33, + 0x70, 0x29, 0x54, 0xd0, 0xc4, 0x22, 0x64, 0x08, 0xe4, 0x1a, 0x33, 0x54, 0xcc, 0x60, 0x96, 0xf2, + 0x0c, 0x85, 0xe1, 0xe6, 0xce, 0x6f, 0x57, 0x7d, 0x17, 0x5b, 0x74, 0xff, 0x81, 0x9b, 0xd6, 0x54, + 0xf0, 0x02, 0x8e, 0x9f, 0x7a, 0xd2, 0x6b, 0x29, 0x34, 0x92, 0x53, 0x70, 0x8b, 0x0a, 0x6b, 0xd8, + 0xe9, 0x59, 0x20, 0x51, 0x45, 0xf0, 0xb1, 0x71, 0x4c, 0x96, 0xe6, 0x4f, 0x64, 0x09, 0x5e, 0xc2, + 0xc9, 0x2f, 0x33, 0x6a, 0x6f, 0x67, 0x00, 0x85, 0x05, 0x1b, 0xfa, 0xee, 0x06, 0xa9, 0xdc, 0x8d, + 0xe1, 0xe0, 0x12, 0x4d, 0x3c, 0x2a, 0xcd, 0x4d, 0xf5, 0x18, 0xee, 0xad, 0x1d, 0x42, 0x47, 0x2f, + 0xe5, 0x1a, 0x7d, 0xc7, 0xce, 0xda, 0x14, 0xe4, 0x18, 0xba, 0xf6, 0x47, 0xfb, 0xad, 0x7e, 0x7b, + 0xe0, 0xd2, 0xba, 0x0a, 0x7e, 0x38, 0x70, 0xf8, 0x58, 0xa5, 0x1e, 0x7e, 0x08, 0x1d, 0x5c, 0x31, + 0x7e, 0x3f, 0x77, 0x53, 0x90, 0x13, 0xd8, 0xb1, 0x4f, 0x93, 0x67, 0x7e, 0xcb, 0xe2, 0xdd, 0xaa, + 0x9c, 0x66, 0x4f, 0x73, 0xb6, 0x2d, 0xd9, 0xbc, 0xb3, 0xe7, 0xb0, 0x6f, 0x4f, 0x4a, 0x95, 0x33, + 0xc1, 0xbf, 0xd8, 0x05, 0xf9, 0xdb, 0xf5, 0x95, 0x79, 0x15, 0x15, 0x37, 0x18, 0xd2, 0x87, 0x1e, + 0xd7, 0x29, 0xcb, 0x56, 0x5c, 0xf8, 0x9d, 0xbe, 0x33, 0xe8, 0x5d, 0x74, 0xae, 0x59, 0xa1, 0x91, + 0xee, 0x70, 0x3d, 0xaa, 0x50, 0x72, 0x06, 0xee, 0xb2, 0xe0, 0x28, 0x4c, 0x65, 0xa6, 0x5b, 0x0b, + 0xf5, 0x36, 0xd0, 0x34, 0x6b, 0x04, 0xde, 0x79, 0x14, 0xf8, 0x5f, 0xf8, 0x67, 0x7c, 0x83, 0xcb, + 0x5b, 0x9b, 0xf8, 0x8a, 0xe7, 0x82, 0x99, 0x52, 0x61, 0xbd, 0xbc, 0x60, 0x06, 0xa7, 0xbf, 0x65, + 0xeb, 0xa5, 0x3c, 0x03, 0x22, 0x6d, 0xcc, 0xa5, 0x14, 0xba, 0x5c, 0xa1, 0x4a, 0x6f, 0xf1, 0xae, + 0xde, 0x90, 0x67, 0x99, 0x71, 0x4d, 0xcc, 0xf0, 0xee, 0x75, 0xf7, 0xc3, 0x76, 0x95, 0xeb, 0x67, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x58, 0x04, 0x53, 0xcc, 0xf8, 0x03, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/user/user_service.proto b/vendor/google.golang.org/appengine/internal/user/user_service.proto new file mode 100644 index 0000000000..f3e9693462 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/user/user_service.proto @@ -0,0 +1,58 @@ +syntax = "proto2"; +option go_package = "user"; + +package appengine; + +message UserServiceError { + enum ErrorCode { + OK = 0; + REDIRECT_URL_TOO_LONG = 1; + NOT_ALLOWED = 2; + OAUTH_INVALID_TOKEN = 3; + OAUTH_INVALID_REQUEST = 4; + OAUTH_ERROR = 5; + } +} + +message CreateLoginURLRequest { + required string destination_url = 1; + optional string auth_domain = 2; + optional string federated_identity = 3 [default = ""]; +} + +message CreateLoginURLResponse { + required string login_url = 1; +} + +message CreateLogoutURLRequest { + required string destination_url = 1; + optional string auth_domain = 2; +} + +message CreateLogoutURLResponse { + required string logout_url = 1; +} + +message GetOAuthUserRequest { + optional string scope = 1; + + repeated string scopes = 2; +} + +message GetOAuthUserResponse { + required string email = 1; + required string user_id = 2; + required string auth_domain = 3; + optional string user_organization = 4 [default = ""]; + optional bool is_admin = 5 [default = false]; + optional string client_id = 6 [default = ""]; + + repeated string scopes = 7; +} + +message CheckOAuthSignatureRequest { +} + +message CheckOAuthSignatureResponse { + required string oauth_consumer_key = 1; +} diff --git a/vendor/google.golang.org/appengine/internal/xmpp/xmpp_service.pb.go b/vendor/google.golang.org/appengine/internal/xmpp/xmpp_service.pb.go new file mode 100644 index 0000000000..f8d203ab7c --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/xmpp/xmpp_service.pb.go @@ -0,0 +1,512 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google.golang.org/appengine/internal/xmpp/xmpp_service.proto + +/* +Package xmpp is a generated protocol buffer package. + +It is generated from these files: + google.golang.org/appengine/internal/xmpp/xmpp_service.proto + +It has these top-level messages: + XmppServiceError + PresenceRequest + PresenceResponse + BulkPresenceRequest + BulkPresenceResponse + XmppMessageRequest + XmppMessageResponse + XmppSendPresenceRequest + XmppSendPresenceResponse + XmppInviteRequest + XmppInviteResponse +*/ +package xmpp + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type XmppServiceError_ErrorCode int32 + +const ( + XmppServiceError_UNSPECIFIED_ERROR XmppServiceError_ErrorCode = 1 + XmppServiceError_INVALID_JID XmppServiceError_ErrorCode = 2 + XmppServiceError_NO_BODY XmppServiceError_ErrorCode = 3 + XmppServiceError_INVALID_XML XmppServiceError_ErrorCode = 4 + XmppServiceError_INVALID_TYPE XmppServiceError_ErrorCode = 5 + XmppServiceError_INVALID_SHOW XmppServiceError_ErrorCode = 6 + XmppServiceError_EXCEEDED_MAX_SIZE XmppServiceError_ErrorCode = 7 + XmppServiceError_APPID_ALIAS_REQUIRED XmppServiceError_ErrorCode = 8 + XmppServiceError_NONDEFAULT_MODULE XmppServiceError_ErrorCode = 9 +) + +var XmppServiceError_ErrorCode_name = map[int32]string{ + 1: "UNSPECIFIED_ERROR", + 2: "INVALID_JID", + 3: "NO_BODY", + 4: "INVALID_XML", + 5: "INVALID_TYPE", + 6: "INVALID_SHOW", + 7: "EXCEEDED_MAX_SIZE", + 8: "APPID_ALIAS_REQUIRED", + 9: "NONDEFAULT_MODULE", +} +var XmppServiceError_ErrorCode_value = map[string]int32{ + "UNSPECIFIED_ERROR": 1, + "INVALID_JID": 2, + "NO_BODY": 3, + "INVALID_XML": 4, + "INVALID_TYPE": 5, + "INVALID_SHOW": 6, + "EXCEEDED_MAX_SIZE": 7, + "APPID_ALIAS_REQUIRED": 8, + "NONDEFAULT_MODULE": 9, +} + +func (x XmppServiceError_ErrorCode) Enum() *XmppServiceError_ErrorCode { + p := new(XmppServiceError_ErrorCode) + *p = x + return p +} +func (x XmppServiceError_ErrorCode) String() string { + return proto.EnumName(XmppServiceError_ErrorCode_name, int32(x)) +} +func (x *XmppServiceError_ErrorCode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(XmppServiceError_ErrorCode_value, data, "XmppServiceError_ErrorCode") + if err != nil { + return err + } + *x = XmppServiceError_ErrorCode(value) + return nil +} +func (XmppServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{0, 0} +} + +type PresenceResponse_SHOW int32 + +const ( + PresenceResponse_NORMAL PresenceResponse_SHOW = 0 + PresenceResponse_AWAY PresenceResponse_SHOW = 1 + PresenceResponse_DO_NOT_DISTURB PresenceResponse_SHOW = 2 + PresenceResponse_CHAT PresenceResponse_SHOW = 3 + PresenceResponse_EXTENDED_AWAY PresenceResponse_SHOW = 4 +) + +var PresenceResponse_SHOW_name = map[int32]string{ + 0: "NORMAL", + 1: "AWAY", + 2: "DO_NOT_DISTURB", + 3: "CHAT", + 4: "EXTENDED_AWAY", +} +var PresenceResponse_SHOW_value = map[string]int32{ + "NORMAL": 0, + "AWAY": 1, + "DO_NOT_DISTURB": 2, + "CHAT": 3, + "EXTENDED_AWAY": 4, +} + +func (x PresenceResponse_SHOW) Enum() *PresenceResponse_SHOW { + p := new(PresenceResponse_SHOW) + *p = x + return p +} +func (x PresenceResponse_SHOW) String() string { + return proto.EnumName(PresenceResponse_SHOW_name, int32(x)) +} +func (x *PresenceResponse_SHOW) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(PresenceResponse_SHOW_value, data, "PresenceResponse_SHOW") + if err != nil { + return err + } + *x = PresenceResponse_SHOW(value) + return nil +} +func (PresenceResponse_SHOW) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } + +type XmppMessageResponse_XmppMessageStatus int32 + +const ( + XmppMessageResponse_NO_ERROR XmppMessageResponse_XmppMessageStatus = 0 + XmppMessageResponse_INVALID_JID XmppMessageResponse_XmppMessageStatus = 1 + XmppMessageResponse_OTHER_ERROR XmppMessageResponse_XmppMessageStatus = 2 +) + +var XmppMessageResponse_XmppMessageStatus_name = map[int32]string{ + 0: "NO_ERROR", + 1: "INVALID_JID", + 2: "OTHER_ERROR", +} +var XmppMessageResponse_XmppMessageStatus_value = map[string]int32{ + "NO_ERROR": 0, + "INVALID_JID": 1, + "OTHER_ERROR": 2, +} + +func (x XmppMessageResponse_XmppMessageStatus) Enum() *XmppMessageResponse_XmppMessageStatus { + p := new(XmppMessageResponse_XmppMessageStatus) + *p = x + return p +} +func (x XmppMessageResponse_XmppMessageStatus) String() string { + return proto.EnumName(XmppMessageResponse_XmppMessageStatus_name, int32(x)) +} +func (x *XmppMessageResponse_XmppMessageStatus) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(XmppMessageResponse_XmppMessageStatus_value, data, "XmppMessageResponse_XmppMessageStatus") + if err != nil { + return err + } + *x = XmppMessageResponse_XmppMessageStatus(value) + return nil +} +func (XmppMessageResponse_XmppMessageStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{6, 0} +} + +type XmppServiceError struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *XmppServiceError) Reset() { *m = XmppServiceError{} } +func (m *XmppServiceError) String() string { return proto.CompactTextString(m) } +func (*XmppServiceError) ProtoMessage() {} +func (*XmppServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type PresenceRequest struct { + Jid *string `protobuf:"bytes,1,req,name=jid" json:"jid,omitempty"` + FromJid *string `protobuf:"bytes,2,opt,name=from_jid,json=fromJid" json:"from_jid,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PresenceRequest) Reset() { *m = PresenceRequest{} } +func (m *PresenceRequest) String() string { return proto.CompactTextString(m) } +func (*PresenceRequest) ProtoMessage() {} +func (*PresenceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *PresenceRequest) GetJid() string { + if m != nil && m.Jid != nil { + return *m.Jid + } + return "" +} + +func (m *PresenceRequest) GetFromJid() string { + if m != nil && m.FromJid != nil { + return *m.FromJid + } + return "" +} + +type PresenceResponse struct { + IsAvailable *bool `protobuf:"varint,1,req,name=is_available,json=isAvailable" json:"is_available,omitempty"` + Presence *PresenceResponse_SHOW `protobuf:"varint,2,opt,name=presence,enum=appengine.PresenceResponse_SHOW" json:"presence,omitempty"` + Valid *bool `protobuf:"varint,3,opt,name=valid" json:"valid,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PresenceResponse) Reset() { *m = PresenceResponse{} } +func (m *PresenceResponse) String() string { return proto.CompactTextString(m) } +func (*PresenceResponse) ProtoMessage() {} +func (*PresenceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *PresenceResponse) GetIsAvailable() bool { + if m != nil && m.IsAvailable != nil { + return *m.IsAvailable + } + return false +} + +func (m *PresenceResponse) GetPresence() PresenceResponse_SHOW { + if m != nil && m.Presence != nil { + return *m.Presence + } + return PresenceResponse_NORMAL +} + +func (m *PresenceResponse) GetValid() bool { + if m != nil && m.Valid != nil { + return *m.Valid + } + return false +} + +type BulkPresenceRequest struct { + Jid []string `protobuf:"bytes,1,rep,name=jid" json:"jid,omitempty"` + FromJid *string `protobuf:"bytes,2,opt,name=from_jid,json=fromJid" json:"from_jid,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BulkPresenceRequest) Reset() { *m = BulkPresenceRequest{} } +func (m *BulkPresenceRequest) String() string { return proto.CompactTextString(m) } +func (*BulkPresenceRequest) ProtoMessage() {} +func (*BulkPresenceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *BulkPresenceRequest) GetJid() []string { + if m != nil { + return m.Jid + } + return nil +} + +func (m *BulkPresenceRequest) GetFromJid() string { + if m != nil && m.FromJid != nil { + return *m.FromJid + } + return "" +} + +type BulkPresenceResponse struct { + PresenceResponse []*PresenceResponse `protobuf:"bytes,1,rep,name=presence_response,json=presenceResponse" json:"presence_response,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BulkPresenceResponse) Reset() { *m = BulkPresenceResponse{} } +func (m *BulkPresenceResponse) String() string { return proto.CompactTextString(m) } +func (*BulkPresenceResponse) ProtoMessage() {} +func (*BulkPresenceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *BulkPresenceResponse) GetPresenceResponse() []*PresenceResponse { + if m != nil { + return m.PresenceResponse + } + return nil +} + +type XmppMessageRequest struct { + Jid []string `protobuf:"bytes,1,rep,name=jid" json:"jid,omitempty"` + Body *string `protobuf:"bytes,2,req,name=body" json:"body,omitempty"` + RawXml *bool `protobuf:"varint,3,opt,name=raw_xml,json=rawXml,def=0" json:"raw_xml,omitempty"` + Type *string `protobuf:"bytes,4,opt,name=type,def=chat" json:"type,omitempty"` + FromJid *string `protobuf:"bytes,5,opt,name=from_jid,json=fromJid" json:"from_jid,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *XmppMessageRequest) Reset() { *m = XmppMessageRequest{} } +func (m *XmppMessageRequest) String() string { return proto.CompactTextString(m) } +func (*XmppMessageRequest) ProtoMessage() {} +func (*XmppMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +const Default_XmppMessageRequest_RawXml bool = false +const Default_XmppMessageRequest_Type string = "chat" + +func (m *XmppMessageRequest) GetJid() []string { + if m != nil { + return m.Jid + } + return nil +} + +func (m *XmppMessageRequest) GetBody() string { + if m != nil && m.Body != nil { + return *m.Body + } + return "" +} + +func (m *XmppMessageRequest) GetRawXml() bool { + if m != nil && m.RawXml != nil { + return *m.RawXml + } + return Default_XmppMessageRequest_RawXml +} + +func (m *XmppMessageRequest) GetType() string { + if m != nil && m.Type != nil { + return *m.Type + } + return Default_XmppMessageRequest_Type +} + +func (m *XmppMessageRequest) GetFromJid() string { + if m != nil && m.FromJid != nil { + return *m.FromJid + } + return "" +} + +type XmppMessageResponse struct { + Status []XmppMessageResponse_XmppMessageStatus `protobuf:"varint,1,rep,name=status,enum=appengine.XmppMessageResponse_XmppMessageStatus" json:"status,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *XmppMessageResponse) Reset() { *m = XmppMessageResponse{} } +func (m *XmppMessageResponse) String() string { return proto.CompactTextString(m) } +func (*XmppMessageResponse) ProtoMessage() {} +func (*XmppMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *XmppMessageResponse) GetStatus() []XmppMessageResponse_XmppMessageStatus { + if m != nil { + return m.Status + } + return nil +} + +type XmppSendPresenceRequest struct { + Jid *string `protobuf:"bytes,1,req,name=jid" json:"jid,omitempty"` + Type *string `protobuf:"bytes,2,opt,name=type" json:"type,omitempty"` + Show *string `protobuf:"bytes,3,opt,name=show" json:"show,omitempty"` + Status *string `protobuf:"bytes,4,opt,name=status" json:"status,omitempty"` + FromJid *string `protobuf:"bytes,5,opt,name=from_jid,json=fromJid" json:"from_jid,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *XmppSendPresenceRequest) Reset() { *m = XmppSendPresenceRequest{} } +func (m *XmppSendPresenceRequest) String() string { return proto.CompactTextString(m) } +func (*XmppSendPresenceRequest) ProtoMessage() {} +func (*XmppSendPresenceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *XmppSendPresenceRequest) GetJid() string { + if m != nil && m.Jid != nil { + return *m.Jid + } + return "" +} + +func (m *XmppSendPresenceRequest) GetType() string { + if m != nil && m.Type != nil { + return *m.Type + } + return "" +} + +func (m *XmppSendPresenceRequest) GetShow() string { + if m != nil && m.Show != nil { + return *m.Show + } + return "" +} + +func (m *XmppSendPresenceRequest) GetStatus() string { + if m != nil && m.Status != nil { + return *m.Status + } + return "" +} + +func (m *XmppSendPresenceRequest) GetFromJid() string { + if m != nil && m.FromJid != nil { + return *m.FromJid + } + return "" +} + +type XmppSendPresenceResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *XmppSendPresenceResponse) Reset() { *m = XmppSendPresenceResponse{} } +func (m *XmppSendPresenceResponse) String() string { return proto.CompactTextString(m) } +func (*XmppSendPresenceResponse) ProtoMessage() {} +func (*XmppSendPresenceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +type XmppInviteRequest struct { + Jid *string `protobuf:"bytes,1,req,name=jid" json:"jid,omitempty"` + FromJid *string `protobuf:"bytes,2,opt,name=from_jid,json=fromJid" json:"from_jid,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *XmppInviteRequest) Reset() { *m = XmppInviteRequest{} } +func (m *XmppInviteRequest) String() string { return proto.CompactTextString(m) } +func (*XmppInviteRequest) ProtoMessage() {} +func (*XmppInviteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *XmppInviteRequest) GetJid() string { + if m != nil && m.Jid != nil { + return *m.Jid + } + return "" +} + +func (m *XmppInviteRequest) GetFromJid() string { + if m != nil && m.FromJid != nil { + return *m.FromJid + } + return "" +} + +type XmppInviteResponse struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *XmppInviteResponse) Reset() { *m = XmppInviteResponse{} } +func (m *XmppInviteResponse) String() string { return proto.CompactTextString(m) } +func (*XmppInviteResponse) ProtoMessage() {} +func (*XmppInviteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func init() { + proto.RegisterType((*XmppServiceError)(nil), "appengine.XmppServiceError") + proto.RegisterType((*PresenceRequest)(nil), "appengine.PresenceRequest") + proto.RegisterType((*PresenceResponse)(nil), "appengine.PresenceResponse") + proto.RegisterType((*BulkPresenceRequest)(nil), "appengine.BulkPresenceRequest") + proto.RegisterType((*BulkPresenceResponse)(nil), "appengine.BulkPresenceResponse") + proto.RegisterType((*XmppMessageRequest)(nil), "appengine.XmppMessageRequest") + proto.RegisterType((*XmppMessageResponse)(nil), "appengine.XmppMessageResponse") + proto.RegisterType((*XmppSendPresenceRequest)(nil), "appengine.XmppSendPresenceRequest") + proto.RegisterType((*XmppSendPresenceResponse)(nil), "appengine.XmppSendPresenceResponse") + proto.RegisterType((*XmppInviteRequest)(nil), "appengine.XmppInviteRequest") + proto.RegisterType((*XmppInviteResponse)(nil), "appengine.XmppInviteResponse") +} + +func init() { + proto.RegisterFile("google.golang.org/appengine/internal/xmpp/xmpp_service.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 681 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xcd, 0x72, 0xda, 0x48, + 0x10, 0xb6, 0x40, 0xfc, 0x35, 0x5e, 0x7b, 0x18, 0xb3, 0xbb, 0xec, 0xa6, 0x2a, 0x45, 0x74, 0xf2, + 0x09, 0xa7, 0x7c, 0x74, 0xb9, 0x52, 0x11, 0x68, 0x5c, 0xc8, 0x05, 0x12, 0x19, 0x20, 0xc6, 0xbe, + 0x4c, 0x64, 0x33, 0x96, 0x95, 0x08, 0x49, 0x91, 0x64, 0x6c, 0xbf, 0x40, 0xae, 0x79, 0x89, 0xbc, + 0x46, 0x5e, 0x22, 0xa7, 0x3c, 0x4e, 0x4a, 0x23, 0x41, 0xc0, 0x4e, 0x9c, 0x54, 0x2e, 0x54, 0xcf, + 0x37, 0xdd, 0x1f, 0xfd, 0x7d, 0x3d, 0x2d, 0x38, 0xb4, 0x7d, 0xdf, 0x76, 0x79, 0xcb, 0xf6, 0x5d, + 0xcb, 0xb3, 0x5b, 0x7e, 0x68, 0xef, 0x59, 0x41, 0xc0, 0x3d, 0xdb, 0xf1, 0xf8, 0x9e, 0xe3, 0xc5, + 0x3c, 0xf4, 0x2c, 0x77, 0xef, 0x76, 0x16, 0x04, 0xe2, 0x87, 0x45, 0x3c, 0x9c, 0x3b, 0x17, 0xbc, + 0x15, 0x84, 0x7e, 0xec, 0xe3, 0xca, 0x32, 0x57, 0xf9, 0x22, 0x01, 0x9a, 0xcc, 0x82, 0x60, 0x98, + 0x26, 0x90, 0x30, 0xf4, 0x43, 0xe5, 0xb3, 0x04, 0x15, 0x11, 0x75, 0xfc, 0x29, 0xc7, 0x7f, 0x43, + 0x6d, 0x6c, 0x0c, 0x07, 0xa4, 0xa3, 0x1f, 0xe9, 0x44, 0x63, 0x84, 0x52, 0x93, 0x22, 0x09, 0x6f, + 0x43, 0x55, 0x37, 0x5e, 0xab, 0x3d, 0x5d, 0x63, 0xc7, 0xba, 0x86, 0x72, 0xb8, 0x0a, 0x25, 0xc3, + 0x64, 0x6d, 0x53, 0x3b, 0x45, 0xf9, 0xd5, 0xdb, 0x49, 0xbf, 0x87, 0x64, 0x8c, 0x60, 0x73, 0x01, + 0x8c, 0x4e, 0x07, 0x04, 0x15, 0x56, 0x91, 0x61, 0xd7, 0x3c, 0x41, 0xc5, 0xe4, 0x9f, 0xc8, 0xa4, + 0x43, 0x88, 0x46, 0x34, 0xd6, 0x57, 0x27, 0x6c, 0xa8, 0x9f, 0x11, 0x54, 0xc2, 0x0d, 0xa8, 0xab, + 0x83, 0x81, 0xae, 0x31, 0xb5, 0xa7, 0xab, 0x43, 0x46, 0xc9, 0xab, 0xb1, 0x4e, 0x89, 0x86, 0xca, + 0x49, 0x81, 0x61, 0x1a, 0x1a, 0x39, 0x52, 0xc7, 0xbd, 0x11, 0xeb, 0x9b, 0xda, 0xb8, 0x47, 0x50, + 0x45, 0x79, 0x01, 0xdb, 0x83, 0x90, 0x47, 0xdc, 0xbb, 0xe0, 0x94, 0xbf, 0xbf, 0xe6, 0x51, 0x8c, + 0x11, 0xe4, 0xdf, 0x3a, 0xd3, 0x86, 0xd4, 0xcc, 0xed, 0x56, 0x68, 0x12, 0xe2, 0xff, 0xa0, 0x7c, + 0x19, 0xfa, 0x33, 0x96, 0xc0, 0xb9, 0xa6, 0xb4, 0x5b, 0xa1, 0xa5, 0xe4, 0x7c, 0xec, 0x4c, 0x95, + 0xaf, 0x12, 0xa0, 0xef, 0x04, 0x51, 0xe0, 0x7b, 0x11, 0xc7, 0xcf, 0x60, 0xd3, 0x89, 0x98, 0x35, + 0xb7, 0x1c, 0xd7, 0x3a, 0x77, 0xb9, 0xa0, 0x2a, 0xd3, 0xaa, 0x13, 0xa9, 0x0b, 0x08, 0x1f, 0x42, + 0x39, 0xc8, 0xca, 0x04, 0xe5, 0xd6, 0x7e, 0xb3, 0xb5, 0xb4, 0xba, 0x75, 0x9f, 0xb1, 0x95, 0xa8, + 0xa6, 0xcb, 0x0a, 0x5c, 0x87, 0xc2, 0xdc, 0x72, 0x9d, 0x69, 0x23, 0xdf, 0x94, 0x76, 0xcb, 0x34, + 0x3d, 0x28, 0x7d, 0x90, 0x93, 0x3c, 0x0c, 0x50, 0x34, 0x4c, 0xda, 0x57, 0x7b, 0x68, 0x03, 0x97, + 0x41, 0x56, 0x4f, 0xd4, 0x53, 0x24, 0x61, 0x0c, 0x5b, 0x9a, 0xc9, 0x0c, 0x73, 0xc4, 0x34, 0x7d, + 0x38, 0x1a, 0xd3, 0x36, 0xca, 0x25, 0xb7, 0x9d, 0xae, 0x3a, 0x42, 0x79, 0x5c, 0x83, 0xbf, 0xc8, + 0x64, 0x44, 0x8c, 0xc4, 0x4f, 0x51, 0x20, 0x2b, 0x6d, 0xd8, 0x69, 0x5f, 0xbb, 0xef, 0x7e, 0x6a, + 0x4f, 0xfe, 0x37, 0xec, 0x79, 0x03, 0xf5, 0x75, 0x8e, 0xcc, 0xa1, 0x2e, 0xd4, 0x16, 0x62, 0x58, + 0x98, 0x81, 0x82, 0xb2, 0xba, 0xff, 0xe4, 0x11, 0x1f, 0x28, 0x0a, 0xee, 0x21, 0xca, 0x47, 0x09, + 0x70, 0xf2, 0x2a, 0xfb, 0x3c, 0x8a, 0x2c, 0xfb, 0x91, 0x2e, 0x31, 0xc8, 0xe7, 0xfe, 0xf4, 0xae, + 0x91, 0x13, 0x73, 0x15, 0x31, 0x7e, 0x0a, 0xa5, 0xd0, 0xba, 0x61, 0xb7, 0x33, 0x37, 0x75, 0xf2, + 0xa0, 0x70, 0x69, 0xb9, 0x11, 0xa7, 0xc5, 0xd0, 0xba, 0x99, 0xcc, 0x5c, 0xdc, 0x00, 0x39, 0xbe, + 0x0b, 0x78, 0x43, 0x4e, 0x54, 0x1d, 0xc8, 0x17, 0x57, 0x56, 0x4c, 0x05, 0xb2, 0xa6, 0xb9, 0xb0, + 0xae, 0xf9, 0x93, 0x04, 0x3b, 0x6b, 0x1d, 0x2d, 0x35, 0x17, 0xa3, 0xd8, 0x8a, 0xaf, 0x23, 0xd1, + 0xd5, 0xd6, 0xfe, 0xf3, 0x15, 0xa1, 0x3f, 0xc8, 0x5f, 0xc5, 0x86, 0xa2, 0x8e, 0x66, 0xf5, 0x4a, + 0x07, 0x6a, 0x0f, 0x2e, 0xf1, 0x26, 0x94, 0x0d, 0x33, 0x5b, 0xb9, 0x8d, 0xfb, 0x2b, 0x27, 0x76, + 0xd0, 0x1c, 0x75, 0x09, 0xcd, 0x32, 0x72, 0xca, 0x07, 0x09, 0xfe, 0x4d, 0xd7, 0xd9, 0x9b, 0xfe, + 0x7a, 0x05, 0x70, 0xe6, 0x44, 0x3a, 0xdf, 0xd4, 0x03, 0x0c, 0x72, 0x74, 0xe5, 0xdf, 0x08, 0xeb, + 0x2a, 0x54, 0xc4, 0xf8, 0x9f, 0xa5, 0x48, 0xe1, 0xd9, 0xa2, 0xe5, 0xc7, 0xfc, 0xfa, 0x1f, 0x1a, + 0x0f, 0xfb, 0xc8, 0xa6, 0xfb, 0x32, 0x55, 0xaa, 0x7b, 0x73, 0x27, 0xfe, 0xb3, 0x05, 0xad, 0xa7, + 0xcf, 0x63, 0xc1, 0x90, 0xf2, 0xb6, 0x8b, 0x67, 0x72, 0xf2, 0xb1, 0xfb, 0x16, 0x00, 0x00, 0xff, + 0xff, 0x4e, 0x58, 0x2e, 0xb1, 0x1d, 0x05, 0x00, 0x00, +} diff --git a/vendor/google.golang.org/appengine/internal/xmpp/xmpp_service.proto b/vendor/google.golang.org/appengine/internal/xmpp/xmpp_service.proto new file mode 100644 index 0000000000..472d52ebf4 --- /dev/null +++ b/vendor/google.golang.org/appengine/internal/xmpp/xmpp_service.proto @@ -0,0 +1,83 @@ +syntax = "proto2"; +option go_package = "xmpp"; + +package appengine; + +message XmppServiceError { + enum ErrorCode { + UNSPECIFIED_ERROR = 1; + INVALID_JID = 2; + NO_BODY = 3; + INVALID_XML = 4; + INVALID_TYPE = 5; + INVALID_SHOW = 6; + EXCEEDED_MAX_SIZE = 7; + APPID_ALIAS_REQUIRED = 8; + NONDEFAULT_MODULE = 9; + } +} + +message PresenceRequest { + required string jid = 1; + optional string from_jid = 2; +} + +message PresenceResponse { + enum SHOW { + NORMAL = 0; + AWAY = 1; + DO_NOT_DISTURB = 2; + CHAT = 3; + EXTENDED_AWAY = 4; + } + + required bool is_available = 1; + optional SHOW presence = 2; + optional bool valid = 3; +} + +message BulkPresenceRequest { + repeated string jid = 1; + optional string from_jid = 2; +} + +message BulkPresenceResponse { + repeated PresenceResponse presence_response = 1; +} + +message XmppMessageRequest { + repeated string jid = 1; + required string body = 2; + optional bool raw_xml = 3 [ default = false ]; + optional string type = 4 [ default = "chat" ]; + optional string from_jid = 5; +} + +message XmppMessageResponse { + enum XmppMessageStatus { + NO_ERROR = 0; + INVALID_JID = 1; + OTHER_ERROR = 2; + } + + repeated XmppMessageStatus status = 1; +} + +message XmppSendPresenceRequest { + required string jid = 1; + optional string type = 2; + optional string show = 3; + optional string status = 4; + optional string from_jid = 5; +} + +message XmppSendPresenceResponse { +} + +message XmppInviteRequest { + required string jid = 1; + optional string from_jid = 2; +} + +message XmppInviteResponse { +} diff --git a/vendor/google.golang.org/appengine/log/api.go b/vendor/google.golang.org/appengine/log/api.go new file mode 100644 index 0000000000..24d58601ba --- /dev/null +++ b/vendor/google.golang.org/appengine/log/api.go @@ -0,0 +1,40 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package log + +// This file implements the logging API. + +import ( + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +// Debugf formats its arguments according to the format, analogous to fmt.Printf, +// and records the text as a log message at Debug level. The message will be associated +// with the request linked with the provided context. +func Debugf(ctx context.Context, format string, args ...interface{}) { + internal.Logf(ctx, 0, format, args...) +} + +// Infof is like Debugf, but at Info level. +func Infof(ctx context.Context, format string, args ...interface{}) { + internal.Logf(ctx, 1, format, args...) +} + +// Warningf is like Debugf, but at Warning level. +func Warningf(ctx context.Context, format string, args ...interface{}) { + internal.Logf(ctx, 2, format, args...) +} + +// Errorf is like Debugf, but at Error level. +func Errorf(ctx context.Context, format string, args ...interface{}) { + internal.Logf(ctx, 3, format, args...) +} + +// Criticalf is like Debugf, but at Critical level. +func Criticalf(ctx context.Context, format string, args ...interface{}) { + internal.Logf(ctx, 4, format, args...) +} diff --git a/vendor/google.golang.org/appengine/log/log.go b/vendor/google.golang.org/appengine/log/log.go new file mode 100644 index 0000000000..731ad8c367 --- /dev/null +++ b/vendor/google.golang.org/appengine/log/log.go @@ -0,0 +1,323 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package log provides the means of writing and querying an application's logs +from within an App Engine application. + +Example: + c := appengine.NewContext(r) + query := &log.Query{ + AppLogs: true, + Versions: []string{"1"}, + } + + for results := query.Run(c); ; { + record, err := results.Next() + if err == log.Done { + log.Infof(c, "Done processing results") + break + } + if err != nil { + log.Errorf(c, "Failed to retrieve next log: %v", err) + break + } + log.Infof(c, "Saw record %v", record) + } +*/ +package log // import "google.golang.org/appengine/log" + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/log" +) + +// Query defines a logs query. +type Query struct { + // Start time specifies the earliest log to return (inclusive). + StartTime time.Time + + // End time specifies the latest log to return (exclusive). + EndTime time.Time + + // Offset specifies a position within the log stream to resume reading from, + // and should come from a previously returned Record's field of the same name. + Offset []byte + + // Incomplete controls whether active (incomplete) requests should be included. + Incomplete bool + + // AppLogs indicates if application-level logs should be included. + AppLogs bool + + // ApplyMinLevel indicates if MinLevel should be used to filter results. + ApplyMinLevel bool + + // If ApplyMinLevel is true, only logs for requests with at least one + // application log of MinLevel or higher will be returned. + MinLevel int + + // Versions is the major version IDs whose logs should be retrieved. + // Logs for specific modules can be retrieved by the specifying versions + // in the form "module:version"; the default module is used if no module + // is specified. + Versions []string + + // A list of requests to search for instead of a time-based scan. Cannot be + // combined with filtering options such as StartTime, EndTime, Offset, + // Incomplete, ApplyMinLevel, or Versions. + RequestIDs []string +} + +// AppLog represents a single application-level log. +type AppLog struct { + Time time.Time + Level int + Message string +} + +// Record contains all the information for a single web request. +type Record struct { + AppID string + ModuleID string + VersionID string + RequestID []byte + IP string + Nickname string + AppEngineRelease string + + // The time when this request started. + StartTime time.Time + + // The time when this request finished. + EndTime time.Time + + // Opaque cursor into the result stream. + Offset []byte + + // The time required to process the request. + Latency time.Duration + MCycles int64 + Method string + Resource string + HTTPVersion string + Status int32 + + // The size of the request sent back to the client, in bytes. + ResponseSize int64 + Referrer string + UserAgent string + URLMapEntry string + Combined string + Host string + + // The estimated cost of this request, in dollars. + Cost float64 + TaskQueueName string + TaskName string + WasLoadingRequest bool + PendingTime time.Duration + Finished bool + AppLogs []AppLog + + // Mostly-unique identifier for the instance that handled the request if available. + InstanceID string +} + +// Result represents the result of a query. +type Result struct { + logs []*Record + context context.Context + request *pb.LogReadRequest + resultsSeen bool + err error +} + +// Next returns the next log record, +func (qr *Result) Next() (*Record, error) { + if qr.err != nil { + return nil, qr.err + } + if len(qr.logs) > 0 { + lr := qr.logs[0] + qr.logs = qr.logs[1:] + return lr, nil + } + + if qr.request.Offset == nil && qr.resultsSeen { + return nil, Done + } + + if err := qr.run(); err != nil { + // Errors here may be retried, so don't store the error. + return nil, err + } + + return qr.Next() +} + +// Done is returned when a query iteration has completed. +var Done = errors.New("log: query has no more results") + +// protoToAppLogs takes as input an array of pointers to LogLines, the internal +// Protocol Buffer representation of a single application-level log, +// and converts it to an array of AppLogs, the external representation +// of an application-level log. +func protoToAppLogs(logLines []*pb.LogLine) []AppLog { + appLogs := make([]AppLog, len(logLines)) + + for i, line := range logLines { + appLogs[i] = AppLog{ + Time: time.Unix(0, *line.Time*1e3), + Level: int(*line.Level), + Message: *line.LogMessage, + } + } + + return appLogs +} + +// protoToRecord converts a RequestLog, the internal Protocol Buffer +// representation of a single request-level log, to a Record, its +// corresponding external representation. +func protoToRecord(rl *pb.RequestLog) *Record { + offset, err := proto.Marshal(rl.Offset) + if err != nil { + offset = nil + } + return &Record{ + AppID: *rl.AppId, + ModuleID: rl.GetModuleId(), + VersionID: *rl.VersionId, + RequestID: rl.RequestId, + Offset: offset, + IP: *rl.Ip, + Nickname: rl.GetNickname(), + AppEngineRelease: string(rl.GetAppEngineRelease()), + StartTime: time.Unix(0, *rl.StartTime*1e3), + EndTime: time.Unix(0, *rl.EndTime*1e3), + Latency: time.Duration(*rl.Latency) * time.Microsecond, + MCycles: *rl.Mcycles, + Method: *rl.Method, + Resource: *rl.Resource, + HTTPVersion: *rl.HttpVersion, + Status: *rl.Status, + ResponseSize: *rl.ResponseSize, + Referrer: rl.GetReferrer(), + UserAgent: rl.GetUserAgent(), + URLMapEntry: *rl.UrlMapEntry, + Combined: *rl.Combined, + Host: rl.GetHost(), + Cost: rl.GetCost(), + TaskQueueName: rl.GetTaskQueueName(), + TaskName: rl.GetTaskName(), + WasLoadingRequest: rl.GetWasLoadingRequest(), + PendingTime: time.Duration(rl.GetPendingTime()) * time.Microsecond, + Finished: rl.GetFinished(), + AppLogs: protoToAppLogs(rl.Line), + InstanceID: string(rl.GetCloneKey()), + } +} + +// Run starts a query for log records, which contain request and application +// level log information. +func (params *Query) Run(c context.Context) *Result { + req, err := makeRequest(params, internal.FullyQualifiedAppID(c), appengine.VersionID(c)) + return &Result{ + context: c, + request: req, + err: err, + } +} + +func makeRequest(params *Query, appID, versionID string) (*pb.LogReadRequest, error) { + req := &pb.LogReadRequest{} + req.AppId = &appID + if !params.StartTime.IsZero() { + req.StartTime = proto.Int64(params.StartTime.UnixNano() / 1e3) + } + if !params.EndTime.IsZero() { + req.EndTime = proto.Int64(params.EndTime.UnixNano() / 1e3) + } + if len(params.Offset) > 0 { + var offset pb.LogOffset + if err := proto.Unmarshal(params.Offset, &offset); err != nil { + return nil, fmt.Errorf("bad Offset: %v", err) + } + req.Offset = &offset + } + if params.Incomplete { + req.IncludeIncomplete = ¶ms.Incomplete + } + if params.AppLogs { + req.IncludeAppLogs = ¶ms.AppLogs + } + if params.ApplyMinLevel { + req.MinimumLogLevel = proto.Int32(int32(params.MinLevel)) + } + if params.Versions == nil { + // If no versions were specified, default to the default module at + // the major version being used by this module. + if i := strings.Index(versionID, "."); i >= 0 { + versionID = versionID[:i] + } + req.VersionId = []string{versionID} + } else { + req.ModuleVersion = make([]*pb.LogModuleVersion, 0, len(params.Versions)) + for _, v := range params.Versions { + var m *string + if i := strings.Index(v, ":"); i >= 0 { + m, v = proto.String(v[:i]), v[i+1:] + } + req.ModuleVersion = append(req.ModuleVersion, &pb.LogModuleVersion{ + ModuleId: m, + VersionId: proto.String(v), + }) + } + } + if params.RequestIDs != nil { + ids := make([][]byte, len(params.RequestIDs)) + for i, v := range params.RequestIDs { + ids[i] = []byte(v) + } + req.RequestId = ids + } + + return req, nil +} + +// run takes the query Result produced by a call to Run and updates it with +// more Records. The updated Result contains a new set of logs as well as an +// offset to where more logs can be found. We also convert the items in the +// response from their internal representations to external versions of the +// same structs. +func (r *Result) run() error { + res := &pb.LogReadResponse{} + if err := internal.Call(r.context, "logservice", "Read", r.request, res); err != nil { + return err + } + + r.logs = make([]*Record, len(res.Log)) + r.request.Offset = res.Offset + r.resultsSeen = true + + for i, log := range res.Log { + r.logs[i] = protoToRecord(log) + } + + return nil +} + +func init() { + internal.RegisterErrorCodeMap("logservice", pb.LogServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/log/log_test.go b/vendor/google.golang.org/appengine/log/log_test.go new file mode 100644 index 0000000000..726468e237 --- /dev/null +++ b/vendor/google.golang.org/appengine/log/log_test.go @@ -0,0 +1,112 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package log + +import ( + "reflect" + "testing" + "time" + + "github.com/golang/protobuf/proto" + + pb "google.golang.org/appengine/internal/log" +) + +func TestQueryToRequest(t *testing.T) { + testCases := []struct { + desc string + query *Query + want *pb.LogReadRequest + }{ + { + desc: "Empty", + query: &Query{}, + want: &pb.LogReadRequest{ + AppId: proto.String("s~fake"), + VersionId: []string{"v12"}, + }, + }, + { + desc: "Versions", + query: &Query{ + Versions: []string{"alpha", "backend:beta"}, + }, + want: &pb.LogReadRequest{ + AppId: proto.String("s~fake"), + ModuleVersion: []*pb.LogModuleVersion{ + { + VersionId: proto.String("alpha"), + }, { + ModuleId: proto.String("backend"), + VersionId: proto.String("beta"), + }, + }, + }, + }, + } + + for _, tt := range testCases { + req, err := makeRequest(tt.query, "s~fake", "v12") + + if err != nil { + t.Errorf("%s: got err %v, want nil", tt.desc, err) + continue + } + if !proto.Equal(req, tt.want) { + t.Errorf("%s request:\ngot %v\nwant %v", tt.desc, req, tt.want) + } + } +} + +func TestProtoToRecord(t *testing.T) { + // We deliberately leave ModuleId and other optional fields unset. + p := &pb.RequestLog{ + AppId: proto.String("s~fake"), + VersionId: proto.String("1"), + RequestId: []byte("deadbeef"), + Ip: proto.String("127.0.0.1"), + StartTime: proto.Int64(431044244000000), + EndTime: proto.Int64(431044724000000), + Latency: proto.Int64(480000000), + Mcycles: proto.Int64(7), + Method: proto.String("GET"), + Resource: proto.String("/app"), + HttpVersion: proto.String("1.1"), + Status: proto.Int32(418), + ResponseSize: proto.Int64(1337), + UrlMapEntry: proto.String("_go_app"), + Combined: proto.String("apache log"), + } + // Sanity check that all required fields are set. + if _, err := proto.Marshal(p); err != nil { + t.Fatalf("proto.Marshal: %v", err) + } + want := &Record{ + AppID: "s~fake", + ModuleID: "default", + VersionID: "1", + RequestID: []byte("deadbeef"), + IP: "127.0.0.1", + StartTime: time.Date(1983, 8, 29, 22, 30, 44, 0, time.UTC), + EndTime: time.Date(1983, 8, 29, 22, 38, 44, 0, time.UTC), + Latency: 8 * time.Minute, + MCycles: 7, + Method: "GET", + Resource: "/app", + HTTPVersion: "1.1", + Status: 418, + ResponseSize: 1337, + URLMapEntry: "_go_app", + Combined: "apache log", + Finished: true, + AppLogs: []AppLog{}, + } + got := protoToRecord(p) + // Coerce locations to UTC since otherwise they will be in local. + got.StartTime, got.EndTime = got.StartTime.UTC(), got.EndTime.UTC() + if !reflect.DeepEqual(got, want) { + t.Errorf("protoToRecord:\ngot: %v\nwant: %v", got, want) + } +} diff --git a/vendor/google.golang.org/appengine/mail/mail.go b/vendor/google.golang.org/appengine/mail/mail.go new file mode 100644 index 0000000000..1ce1e87067 --- /dev/null +++ b/vendor/google.golang.org/appengine/mail/mail.go @@ -0,0 +1,123 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package mail provides the means of sending email from an +App Engine application. + +Example: + msg := &mail.Message{ + Sender: "romeo@montague.com", + To: []string{"Juliet "}, + Subject: "See you tonight", + Body: "Don't forget our plans. Hark, 'til later.", + } + if err := mail.Send(c, msg); err != nil { + log.Errorf(c, "Alas, my user, the email failed to sendeth: %v", err) + } +*/ +package mail // import "google.golang.org/appengine/mail" + +import ( + "net/mail" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + bpb "google.golang.org/appengine/internal/base" + pb "google.golang.org/appengine/internal/mail" +) + +// A Message represents an email message. +// Addresses may be of any form permitted by RFC 822. +type Message struct { + // Sender must be set, and must be either an application admin + // or the currently signed-in user. + Sender string + ReplyTo string // may be empty + + // At least one of these slices must have a non-zero length, + // except when calling SendToAdmins. + To, Cc, Bcc []string + + Subject string + + // At least one of Body or HTMLBody must be non-empty. + Body string + HTMLBody string + + Attachments []Attachment + + // Extra mail headers. + // See https://cloud.google.com/appengine/docs/standard/go/mail/ + // for permissible headers. + Headers mail.Header +} + +// An Attachment represents an email attachment. +type Attachment struct { + // Name must be set to a valid file name. + Name string + Data []byte + ContentID string +} + +// Send sends an email message. +func Send(c context.Context, msg *Message) error { + return send(c, "Send", msg) +} + +// SendToAdmins sends an email message to the application's administrators. +func SendToAdmins(c context.Context, msg *Message) error { + return send(c, "SendToAdmins", msg) +} + +func send(c context.Context, method string, msg *Message) error { + req := &pb.MailMessage{ + Sender: &msg.Sender, + To: msg.To, + Cc: msg.Cc, + Bcc: msg.Bcc, + Subject: &msg.Subject, + } + if msg.ReplyTo != "" { + req.ReplyTo = &msg.ReplyTo + } + if msg.Body != "" { + req.TextBody = &msg.Body + } + if msg.HTMLBody != "" { + req.HtmlBody = &msg.HTMLBody + } + if len(msg.Attachments) > 0 { + req.Attachment = make([]*pb.MailAttachment, len(msg.Attachments)) + for i, att := range msg.Attachments { + req.Attachment[i] = &pb.MailAttachment{ + FileName: proto.String(att.Name), + Data: att.Data, + } + if att.ContentID != "" { + req.Attachment[i].ContentID = proto.String(att.ContentID) + } + } + } + for key, vs := range msg.Headers { + for _, v := range vs { + req.Header = append(req.Header, &pb.MailHeader{ + Name: proto.String(key), + Value: proto.String(v), + }) + } + } + res := &bpb.VoidProto{} + if err := internal.Call(c, "mail", method, req, res); err != nil { + return err + } + return nil +} + +func init() { + internal.RegisterErrorCodeMap("mail", pb.MailServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/mail/mail_test.go b/vendor/google.golang.org/appengine/mail/mail_test.go new file mode 100644 index 0000000000..7502c5973a --- /dev/null +++ b/vendor/google.golang.org/appengine/mail/mail_test.go @@ -0,0 +1,65 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package mail + +import ( + "testing" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine/internal/aetesting" + basepb "google.golang.org/appengine/internal/base" + pb "google.golang.org/appengine/internal/mail" +) + +func TestMessageConstruction(t *testing.T) { + var got *pb.MailMessage + c := aetesting.FakeSingleContext(t, "mail", "Send", func(in *pb.MailMessage, out *basepb.VoidProto) error { + got = in + return nil + }) + + msg := &Message{ + Sender: "dsymonds@example.com", + To: []string{"nigeltao@example.com"}, + Body: "Hey, lunch time?", + Attachments: []Attachment{ + // Regression test for a prod bug. The address of a range variable was used when + // constructing the outgoing proto, so multiple attachments used the same name. + { + Name: "att1.txt", + Data: []byte("data1"), + ContentID: "", + }, + { + Name: "att2.txt", + Data: []byte("data2"), + }, + }, + } + if err := Send(c, msg); err != nil { + t.Fatalf("Send: %v", err) + } + want := &pb.MailMessage{ + Sender: proto.String("dsymonds@example.com"), + To: []string{"nigeltao@example.com"}, + Subject: proto.String(""), + TextBody: proto.String("Hey, lunch time?"), + Attachment: []*pb.MailAttachment{ + { + FileName: proto.String("att1.txt"), + Data: []byte("data1"), + ContentID: proto.String(""), + }, + { + FileName: proto.String("att2.txt"), + Data: []byte("data2"), + }, + }, + } + if !proto.Equal(got, want) { + t.Errorf("Bad proto for %+v\n got %v\nwant %v", msg, got, want) + } +} diff --git a/vendor/google.golang.org/appengine/memcache/memcache.go b/vendor/google.golang.org/appengine/memcache/memcache.go new file mode 100644 index 0000000000..d8eed4be7e --- /dev/null +++ b/vendor/google.golang.org/appengine/memcache/memcache.go @@ -0,0 +1,526 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package memcache provides a client for App Engine's distributed in-memory +// key-value store for small chunks of arbitrary data. +// +// The fundamental operations get and set items, keyed by a string. +// +// item0, err := memcache.Get(c, "key") +// if err != nil && err != memcache.ErrCacheMiss { +// return err +// } +// if err == nil { +// fmt.Fprintf(w, "memcache hit: Key=%q Val=[% x]\n", item0.Key, item0.Value) +// } else { +// fmt.Fprintf(w, "memcache miss\n") +// } +// +// and +// +// item1 := &memcache.Item{ +// Key: "foo", +// Value: []byte("bar"), +// } +// if err := memcache.Set(c, item1); err != nil { +// return err +// } +package memcache // import "google.golang.org/appengine/memcache" + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "errors" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/memcache" +) + +var ( + // ErrCacheMiss means that an operation failed + // because the item wasn't present. + ErrCacheMiss = errors.New("memcache: cache miss") + // ErrCASConflict means that a CompareAndSwap call failed due to the + // cached value being modified between the Get and the CompareAndSwap. + // If the cached value was simply evicted rather than replaced, + // ErrNotStored will be returned instead. + ErrCASConflict = errors.New("memcache: compare-and-swap conflict") + // ErrNoStats means that no statistics were available. + ErrNoStats = errors.New("memcache: no statistics available") + // ErrNotStored means that a conditional write operation (i.e. Add or + // CompareAndSwap) failed because the condition was not satisfied. + ErrNotStored = errors.New("memcache: item not stored") + // ErrServerError means that a server error occurred. + ErrServerError = errors.New("memcache: server error") +) + +// Item is the unit of memcache gets and sets. +type Item struct { + // Key is the Item's key (250 bytes maximum). + Key string + // Value is the Item's value. + Value []byte + // Object is the Item's value for use with a Codec. + Object interface{} + // Flags are server-opaque flags whose semantics are entirely up to the + // App Engine app. + Flags uint32 + // Expiration is the maximum duration that the item will stay + // in the cache. + // The zero value means the Item has no expiration time. + // Subsecond precision is ignored. + // This is not set when getting items. + Expiration time.Duration + // casID is a client-opaque value used for compare-and-swap operations. + // Zero means that compare-and-swap is not used. + casID uint64 +} + +const ( + secondsIn30Years = 60 * 60 * 24 * 365 * 30 // from memcache server code + thirtyYears = time.Duration(secondsIn30Years) * time.Second +) + +// protoToItem converts a protocol buffer item to a Go struct. +func protoToItem(p *pb.MemcacheGetResponse_Item) *Item { + return &Item{ + Key: string(p.Key), + Value: p.Value, + Flags: p.GetFlags(), + casID: p.GetCasId(), + } +} + +// If err is an appengine.MultiError, return its first element. Otherwise, return err. +func singleError(err error) error { + if me, ok := err.(appengine.MultiError); ok { + return me[0] + } + return err +} + +// Get gets the item for the given key. ErrCacheMiss is returned for a memcache +// cache miss. The key must be at most 250 bytes in length. +func Get(c context.Context, key string) (*Item, error) { + m, err := GetMulti(c, []string{key}) + if err != nil { + return nil, err + } + if _, ok := m[key]; !ok { + return nil, ErrCacheMiss + } + return m[key], nil +} + +// GetMulti is a batch version of Get. The returned map from keys to items may +// have fewer elements than the input slice, due to memcache cache misses. +// Each key must be at most 250 bytes in length. +func GetMulti(c context.Context, key []string) (map[string]*Item, error) { + if len(key) == 0 { + return nil, nil + } + keyAsBytes := make([][]byte, len(key)) + for i, k := range key { + keyAsBytes[i] = []byte(k) + } + req := &pb.MemcacheGetRequest{ + Key: keyAsBytes, + ForCas: proto.Bool(true), + } + res := &pb.MemcacheGetResponse{} + if err := internal.Call(c, "memcache", "Get", req, res); err != nil { + return nil, err + } + m := make(map[string]*Item, len(res.Item)) + for _, p := range res.Item { + t := protoToItem(p) + m[t.Key] = t + } + return m, nil +} + +// Delete deletes the item for the given key. +// ErrCacheMiss is returned if the specified item can not be found. +// The key must be at most 250 bytes in length. +func Delete(c context.Context, key string) error { + return singleError(DeleteMulti(c, []string{key})) +} + +// DeleteMulti is a batch version of Delete. +// If any keys cannot be found, an appengine.MultiError is returned. +// Each key must be at most 250 bytes in length. +func DeleteMulti(c context.Context, key []string) error { + if len(key) == 0 { + return nil + } + req := &pb.MemcacheDeleteRequest{ + Item: make([]*pb.MemcacheDeleteRequest_Item, len(key)), + } + for i, k := range key { + req.Item[i] = &pb.MemcacheDeleteRequest_Item{Key: []byte(k)} + } + res := &pb.MemcacheDeleteResponse{} + if err := internal.Call(c, "memcache", "Delete", req, res); err != nil { + return err + } + if len(res.DeleteStatus) != len(key) { + return ErrServerError + } + me, any := make(appengine.MultiError, len(key)), false + for i, s := range res.DeleteStatus { + switch s { + case pb.MemcacheDeleteResponse_DELETED: + // OK + case pb.MemcacheDeleteResponse_NOT_FOUND: + me[i] = ErrCacheMiss + any = true + default: + me[i] = ErrServerError + any = true + } + } + if any { + return me + } + return nil +} + +// Increment atomically increments the decimal value in the given key +// by delta and returns the new value. The value must fit in a uint64. +// Overflow wraps around, and underflow is capped to zero. The +// provided delta may be negative. If the key doesn't exist in +// memcache, the provided initial value is used to atomically +// populate it before the delta is applied. +// The key must be at most 250 bytes in length. +func Increment(c context.Context, key string, delta int64, initialValue uint64) (newValue uint64, err error) { + return incr(c, key, delta, &initialValue) +} + +// IncrementExisting works like Increment but assumes that the key +// already exists in memcache and doesn't take an initial value. +// IncrementExisting can save work if calculating the initial value is +// expensive. +// An error is returned if the specified item can not be found. +func IncrementExisting(c context.Context, key string, delta int64) (newValue uint64, err error) { + return incr(c, key, delta, nil) +} + +func incr(c context.Context, key string, delta int64, initialValue *uint64) (newValue uint64, err error) { + req := &pb.MemcacheIncrementRequest{ + Key: []byte(key), + InitialValue: initialValue, + } + if delta >= 0 { + req.Delta = proto.Uint64(uint64(delta)) + } else { + req.Delta = proto.Uint64(uint64(-delta)) + req.Direction = pb.MemcacheIncrementRequest_DECREMENT.Enum() + } + res := &pb.MemcacheIncrementResponse{} + err = internal.Call(c, "memcache", "Increment", req, res) + if err != nil { + return + } + if res.NewValue == nil { + return 0, ErrCacheMiss + } + return *res.NewValue, nil +} + +// set sets the given items using the given conflict resolution policy. +// appengine.MultiError may be returned. +func set(c context.Context, item []*Item, value [][]byte, policy pb.MemcacheSetRequest_SetPolicy) error { + if len(item) == 0 { + return nil + } + req := &pb.MemcacheSetRequest{ + Item: make([]*pb.MemcacheSetRequest_Item, len(item)), + } + for i, t := range item { + p := &pb.MemcacheSetRequest_Item{ + Key: []byte(t.Key), + } + if value == nil { + p.Value = t.Value + } else { + p.Value = value[i] + } + if t.Flags != 0 { + p.Flags = proto.Uint32(t.Flags) + } + if t.Expiration != 0 { + // In the .proto file, MemcacheSetRequest_Item uses a fixed32 (i.e. unsigned) + // for expiration time, while MemcacheGetRequest_Item uses int32 (i.e. signed). + // Throughout this .go file, we use int32. + // Also, in the proto, the expiration value is either a duration (in seconds) + // or an absolute Unix timestamp (in seconds), depending on whether the + // value is less than or greater than or equal to 30 years, respectively. + if t.Expiration < time.Second { + // Because an Expiration of 0 means no expiration, we take + // care here to translate an item with an expiration + // Duration between 0-1 seconds as immediately expiring + // (saying it expired a few seconds ago), rather than + // rounding it down to 0 and making it live forever. + p.ExpirationTime = proto.Uint32(uint32(time.Now().Unix()) - 5) + } else if t.Expiration >= thirtyYears { + p.ExpirationTime = proto.Uint32(uint32(time.Now().Unix()) + uint32(t.Expiration/time.Second)) + } else { + p.ExpirationTime = proto.Uint32(uint32(t.Expiration / time.Second)) + } + } + if t.casID != 0 { + p.CasId = proto.Uint64(t.casID) + p.ForCas = proto.Bool(true) + } + p.SetPolicy = policy.Enum() + req.Item[i] = p + } + res := &pb.MemcacheSetResponse{} + if err := internal.Call(c, "memcache", "Set", req, res); err != nil { + return err + } + if len(res.SetStatus) != len(item) { + return ErrServerError + } + me, any := make(appengine.MultiError, len(item)), false + for i, st := range res.SetStatus { + var err error + switch st { + case pb.MemcacheSetResponse_STORED: + // OK + case pb.MemcacheSetResponse_NOT_STORED: + err = ErrNotStored + case pb.MemcacheSetResponse_EXISTS: + err = ErrCASConflict + default: + err = ErrServerError + } + if err != nil { + me[i] = err + any = true + } + } + if any { + return me + } + return nil +} + +// Set writes the given item, unconditionally. +func Set(c context.Context, item *Item) error { + return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_SET)) +} + +// SetMulti is a batch version of Set. +// appengine.MultiError may be returned. +func SetMulti(c context.Context, item []*Item) error { + return set(c, item, nil, pb.MemcacheSetRequest_SET) +} + +// Add writes the given item, if no value already exists for its key. +// ErrNotStored is returned if that condition is not met. +func Add(c context.Context, item *Item) error { + return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_ADD)) +} + +// AddMulti is a batch version of Add. +// appengine.MultiError may be returned. +func AddMulti(c context.Context, item []*Item) error { + return set(c, item, nil, pb.MemcacheSetRequest_ADD) +} + +// CompareAndSwap writes the given item that was previously returned by Get, +// if the value was neither modified or evicted between the Get and the +// CompareAndSwap calls. The item's Key should not change between calls but +// all other item fields may differ. +// ErrCASConflict is returned if the value was modified in between the calls. +// ErrNotStored is returned if the value was evicted in between the calls. +func CompareAndSwap(c context.Context, item *Item) error { + return singleError(set(c, []*Item{item}, nil, pb.MemcacheSetRequest_CAS)) +} + +// CompareAndSwapMulti is a batch version of CompareAndSwap. +// appengine.MultiError may be returned. +func CompareAndSwapMulti(c context.Context, item []*Item) error { + return set(c, item, nil, pb.MemcacheSetRequest_CAS) +} + +// Codec represents a symmetric pair of functions that implement a codec. +// Items stored into or retrieved from memcache using a Codec have their +// values marshaled or unmarshaled. +// +// All the methods provided for Codec behave analogously to the package level +// function with same name. +type Codec struct { + Marshal func(interface{}) ([]byte, error) + Unmarshal func([]byte, interface{}) error +} + +// Get gets the item for the given key and decodes the obtained value into v. +// ErrCacheMiss is returned for a memcache cache miss. +// The key must be at most 250 bytes in length. +func (cd Codec) Get(c context.Context, key string, v interface{}) (*Item, error) { + i, err := Get(c, key) + if err != nil { + return nil, err + } + if err := cd.Unmarshal(i.Value, v); err != nil { + return nil, err + } + return i, nil +} + +func (cd Codec) set(c context.Context, items []*Item, policy pb.MemcacheSetRequest_SetPolicy) error { + var vs [][]byte + var me appengine.MultiError + for i, item := range items { + v, err := cd.Marshal(item.Object) + if err != nil { + if me == nil { + me = make(appengine.MultiError, len(items)) + } + me[i] = err + continue + } + if me == nil { + vs = append(vs, v) + } + } + if me != nil { + return me + } + + return set(c, items, vs, policy) +} + +// Set writes the given item, unconditionally. +func (cd Codec) Set(c context.Context, item *Item) error { + return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_SET)) +} + +// SetMulti is a batch version of Set. +// appengine.MultiError may be returned. +func (cd Codec) SetMulti(c context.Context, items []*Item) error { + return cd.set(c, items, pb.MemcacheSetRequest_SET) +} + +// Add writes the given item, if no value already exists for its key. +// ErrNotStored is returned if that condition is not met. +func (cd Codec) Add(c context.Context, item *Item) error { + return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_ADD)) +} + +// AddMulti is a batch version of Add. +// appengine.MultiError may be returned. +func (cd Codec) AddMulti(c context.Context, items []*Item) error { + return cd.set(c, items, pb.MemcacheSetRequest_ADD) +} + +// CompareAndSwap writes the given item that was previously returned by Get, +// if the value was neither modified or evicted between the Get and the +// CompareAndSwap calls. The item's Key should not change between calls but +// all other item fields may differ. +// ErrCASConflict is returned if the value was modified in between the calls. +// ErrNotStored is returned if the value was evicted in between the calls. +func (cd Codec) CompareAndSwap(c context.Context, item *Item) error { + return singleError(cd.set(c, []*Item{item}, pb.MemcacheSetRequest_CAS)) +} + +// CompareAndSwapMulti is a batch version of CompareAndSwap. +// appengine.MultiError may be returned. +func (cd Codec) CompareAndSwapMulti(c context.Context, items []*Item) error { + return cd.set(c, items, pb.MemcacheSetRequest_CAS) +} + +var ( + // Gob is a Codec that uses the gob package. + Gob = Codec{gobMarshal, gobUnmarshal} + // JSON is a Codec that uses the json package. + JSON = Codec{json.Marshal, json.Unmarshal} +) + +func gobMarshal(v interface{}) ([]byte, error) { + var buf bytes.Buffer + if err := gob.NewEncoder(&buf).Encode(v); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func gobUnmarshal(data []byte, v interface{}) error { + return gob.NewDecoder(bytes.NewBuffer(data)).Decode(v) +} + +// Statistics represents a set of statistics about the memcache cache. +// This may include items that have expired but have not yet been removed from the cache. +type Statistics struct { + Hits uint64 // Counter of cache hits + Misses uint64 // Counter of cache misses + ByteHits uint64 // Counter of bytes transferred for gets + + Items uint64 // Items currently in the cache + Bytes uint64 // Size of all items currently in the cache + + Oldest int64 // Age of access of the oldest item, in seconds +} + +// Stats retrieves the current memcache statistics. +func Stats(c context.Context) (*Statistics, error) { + req := &pb.MemcacheStatsRequest{} + res := &pb.MemcacheStatsResponse{} + if err := internal.Call(c, "memcache", "Stats", req, res); err != nil { + return nil, err + } + if res.Stats == nil { + return nil, ErrNoStats + } + return &Statistics{ + Hits: *res.Stats.Hits, + Misses: *res.Stats.Misses, + ByteHits: *res.Stats.ByteHits, + Items: *res.Stats.Items, + Bytes: *res.Stats.Bytes, + Oldest: int64(*res.Stats.OldestItemAge), + }, nil +} + +// Flush flushes all items from memcache. +func Flush(c context.Context) error { + req := &pb.MemcacheFlushRequest{} + res := &pb.MemcacheFlushResponse{} + return internal.Call(c, "memcache", "FlushAll", req, res) +} + +func namespaceMod(m proto.Message, namespace string) { + switch m := m.(type) { + case *pb.MemcacheDeleteRequest: + if m.NameSpace == nil { + m.NameSpace = &namespace + } + case *pb.MemcacheGetRequest: + if m.NameSpace == nil { + m.NameSpace = &namespace + } + case *pb.MemcacheIncrementRequest: + if m.NameSpace == nil { + m.NameSpace = &namespace + } + case *pb.MemcacheSetRequest: + if m.NameSpace == nil { + m.NameSpace = &namespace + } + // MemcacheFlushRequest, MemcacheStatsRequest do not apply namespace. + } +} + +func init() { + internal.RegisterErrorCodeMap("memcache", pb.MemcacheServiceError_ErrorCode_name) + internal.NamespaceMods["memcache"] = namespaceMod +} diff --git a/vendor/google.golang.org/appengine/memcache/memcache_test.go b/vendor/google.golang.org/appengine/memcache/memcache_test.go new file mode 100644 index 0000000000..1dc7da471f --- /dev/null +++ b/vendor/google.golang.org/appengine/memcache/memcache_test.go @@ -0,0 +1,263 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package memcache + +import ( + "fmt" + "testing" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/memcache" +) + +var errRPC = fmt.Errorf("RPC error") + +func TestGetRequest(t *testing.T) { + serviceCalled := false + apiKey := "lyric" + + c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error { + // Test request. + if n := len(req.Key); n != 1 { + t.Errorf("got %d want 1", n) + return nil + } + if k := string(req.Key[0]); k != apiKey { + t.Errorf("got %q want %q", k, apiKey) + } + + serviceCalled = true + return nil + }) + + // Test the "forward" path from the API call parameters to the + // protobuf request object. (The "backward" path from the + // protobuf response object to the API call response, + // including the error response, are handled in the next few + // tests). + Get(c, apiKey) + if !serviceCalled { + t.Error("Service was not called as expected") + } +} + +func TestGetResponseHit(t *testing.T) { + key := "lyric" + value := "Where the buffalo roam" + + c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { + res.Item = []*pb.MemcacheGetResponse_Item{ + {Key: []byte(key), Value: []byte(value)}, + } + return nil + }) + apiItem, err := Get(c, key) + if apiItem == nil || apiItem.Key != key || string(apiItem.Value) != value { + t.Errorf("got %q, %q want {%q,%q}, nil", apiItem, err, key, value) + } +} + +func TestGetResponseMiss(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { + // don't fill in any of the response + return nil + }) + _, err := Get(c, "something") + if err != ErrCacheMiss { + t.Errorf("got %v want ErrCacheMiss", err) + } +} + +func TestGetResponseRPCError(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { + return errRPC + }) + + if _, err := Get(c, "something"); err != errRPC { + t.Errorf("got %v want errRPC", err) + } +} + +func TestAddRequest(t *testing.T) { + var apiItem = &Item{ + Key: "lyric", + Value: []byte("Oh, give me a home"), + } + + serviceCalled := false + + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error { + // Test request. + pbItem := req.Item[0] + if k := string(pbItem.Key); k != apiItem.Key { + t.Errorf("got %q want %q", k, apiItem.Key) + } + if v := string(apiItem.Value); v != string(pbItem.Value) { + t.Errorf("got %q want %q", v, string(pbItem.Value)) + } + if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_ADD { + t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_ADD) + } + + serviceCalled = true + return nil + }) + + Add(c, apiItem) + if !serviceCalled { + t.Error("Service was not called as expected") + } +} + +func TestAddResponseStored(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { + res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_STORED} + return nil + }) + + if err := Add(c, &Item{}); err != nil { + t.Errorf("got %v want nil", err) + } +} + +func TestAddResponseNotStored(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { + res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_NOT_STORED} + return nil + }) + + if err := Add(c, &Item{}); err != ErrNotStored { + t.Errorf("got %v want ErrNotStored", err) + } +} + +func TestAddResponseError(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { + res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_ERROR} + return nil + }) + + if err := Add(c, &Item{}); err != ErrServerError { + t.Errorf("got %v want ErrServerError", err) + } +} + +func TestAddResponseRPCError(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { + return errRPC + }) + + if err := Add(c, &Item{}); err != errRPC { + t.Errorf("got %v want errRPC", err) + } +} + +func TestSetRequest(t *testing.T) { + var apiItem = &Item{ + Key: "lyric", + Value: []byte("Where the buffalo roam"), + } + + serviceCalled := false + + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error { + // Test request. + if n := len(req.Item); n != 1 { + t.Errorf("got %d want 1", n) + return nil + } + pbItem := req.Item[0] + if k := string(pbItem.Key); k != apiItem.Key { + t.Errorf("got %q want %q", k, apiItem.Key) + } + if v := string(pbItem.Value); v != string(apiItem.Value) { + t.Errorf("got %q want %q", v, string(apiItem.Value)) + } + if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_SET { + t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_SET) + } + + serviceCalled = true + return nil + }) + + Set(c, apiItem) + if !serviceCalled { + t.Error("Service was not called as expected") + } +} + +func TestSetResponse(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { + res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_STORED} + return nil + }) + + if err := Set(c, &Item{}); err != nil { + t.Errorf("got %v want nil", err) + } +} + +func TestSetResponseError(t *testing.T) { + c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { + res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_ERROR} + return nil + }) + + if err := Set(c, &Item{}); err != ErrServerError { + t.Errorf("got %v want ErrServerError", err) + } +} + +func TestNamespaceResetting(t *testing.T) { + namec := make(chan *string, 1) + c0 := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { + namec <- req.NameSpace + return errRPC + }) + + // Check that wrapping c0 in a namespace twice works correctly. + c1, err := appengine.Namespace(c0, "A") + if err != nil { + t.Fatalf("appengine.Namespace: %v", err) + } + c2, err := appengine.Namespace(c1, "") // should act as the original context + if err != nil { + t.Fatalf("appengine.Namespace: %v", err) + } + + Get(c0, "key") + if ns := <-namec; ns != nil { + t.Errorf(`Get with c0: ns = %q, want nil`, *ns) + } + + Get(c1, "key") + if ns := <-namec; ns == nil { + t.Error(`Get with c1: ns = nil, want "A"`) + } else if *ns != "A" { + t.Errorf(`Get with c1: ns = %q, want "A"`, *ns) + } + + Get(c2, "key") + if ns := <-namec; ns != nil { + t.Errorf(`Get with c2: ns = %q, want nil`, *ns) + } +} + +func TestGetMultiEmpty(t *testing.T) { + serviceCalled := false + c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error { + serviceCalled = true + return nil + }) + + // Test that the Memcache service is not called when + // GetMulti is passed an empty slice of keys. + GetMulti(c, []string{}) + if serviceCalled { + t.Error("Service was called but should not have been") + } +} diff --git a/vendor/google.golang.org/appengine/module/module.go b/vendor/google.golang.org/appengine/module/module.go new file mode 100644 index 0000000000..88e6629acb --- /dev/null +++ b/vendor/google.golang.org/appengine/module/module.go @@ -0,0 +1,113 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package module provides functions for interacting with modules. + +The appengine package contains functions that report the identity of the app, +including the module name. +*/ +package module // import "google.golang.org/appengine/module" + +import ( + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/modules" +) + +// List returns the names of modules belonging to this application. +func List(c context.Context) ([]string, error) { + req := &pb.GetModulesRequest{} + res := &pb.GetModulesResponse{} + err := internal.Call(c, "modules", "GetModules", req, res) + return res.Module, err +} + +// NumInstances returns the number of instances of the given module/version. +// If either argument is the empty string it means the default. +func NumInstances(c context.Context, module, version string) (int, error) { + req := &pb.GetNumInstancesRequest{} + if module != "" { + req.Module = &module + } + if version != "" { + req.Version = &version + } + res := &pb.GetNumInstancesResponse{} + + if err := internal.Call(c, "modules", "GetNumInstances", req, res); err != nil { + return 0, err + } + return int(*res.Instances), nil +} + +// SetNumInstances sets the number of instances of the given module.version to the +// specified value. If either module or version are the empty string it means the +// default. +func SetNumInstances(c context.Context, module, version string, instances int) error { + req := &pb.SetNumInstancesRequest{} + if module != "" { + req.Module = &module + } + if version != "" { + req.Version = &version + } + req.Instances = proto.Int64(int64(instances)) + res := &pb.SetNumInstancesResponse{} + return internal.Call(c, "modules", "SetNumInstances", req, res) +} + +// Versions returns the names of the versions that belong to the specified module. +// If module is the empty string, it means the default module. +func Versions(c context.Context, module string) ([]string, error) { + req := &pb.GetVersionsRequest{} + if module != "" { + req.Module = &module + } + res := &pb.GetVersionsResponse{} + err := internal.Call(c, "modules", "GetVersions", req, res) + return res.GetVersion(), err +} + +// DefaultVersion returns the default version of the specified module. +// If module is the empty string, it means the default module. +func DefaultVersion(c context.Context, module string) (string, error) { + req := &pb.GetDefaultVersionRequest{} + if module != "" { + req.Module = &module + } + res := &pb.GetDefaultVersionResponse{} + err := internal.Call(c, "modules", "GetDefaultVersion", req, res) + return res.GetVersion(), err +} + +// Start starts the specified version of the specified module. +// If either module or version are the empty string, it means the default. +func Start(c context.Context, module, version string) error { + req := &pb.StartModuleRequest{} + if module != "" { + req.Module = &module + } + if version != "" { + req.Version = &version + } + res := &pb.StartModuleResponse{} + return internal.Call(c, "modules", "StartModule", req, res) +} + +// Stop stops the specified version of the specified module. +// If either module or version are the empty string, it means the default. +func Stop(c context.Context, module, version string) error { + req := &pb.StopModuleRequest{} + if module != "" { + req.Module = &module + } + if version != "" { + req.Version = &version + } + res := &pb.StopModuleResponse{} + return internal.Call(c, "modules", "StopModule", req, res) +} diff --git a/vendor/google.golang.org/appengine/module/module_test.go b/vendor/google.golang.org/appengine/module/module_test.go new file mode 100644 index 0000000000..73e8971dc6 --- /dev/null +++ b/vendor/google.golang.org/appengine/module/module_test.go @@ -0,0 +1,124 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package module + +import ( + "reflect" + "testing" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/modules" +) + +const version = "test-version" +const module = "test-module" +const instances = 3 + +func TestList(t *testing.T) { + c := aetesting.FakeSingleContext(t, "modules", "GetModules", func(req *pb.GetModulesRequest, res *pb.GetModulesResponse) error { + res.Module = []string{"default", "mod1"} + return nil + }) + got, err := List(c) + if err != nil { + t.Fatalf("List: %v", err) + } + want := []string{"default", "mod1"} + if !reflect.DeepEqual(got, want) { + t.Errorf("List = %v, want %v", got, want) + } +} + +func TestSetNumInstances(t *testing.T) { + c := aetesting.FakeSingleContext(t, "modules", "SetNumInstances", func(req *pb.SetNumInstancesRequest, res *pb.SetNumInstancesResponse) error { + if *req.Module != module { + t.Errorf("Module = %v, want %v", req.Module, module) + } + if *req.Version != version { + t.Errorf("Version = %v, want %v", req.Version, version) + } + if *req.Instances != instances { + t.Errorf("Instances = %v, want %d", req.Instances, instances) + } + return nil + }) + err := SetNumInstances(c, module, version, instances) + if err != nil { + t.Fatalf("SetNumInstances: %v", err) + } +} + +func TestVersions(t *testing.T) { + c := aetesting.FakeSingleContext(t, "modules", "GetVersions", func(req *pb.GetVersionsRequest, res *pb.GetVersionsResponse) error { + if *req.Module != module { + t.Errorf("Module = %v, want %v", req.Module, module) + } + res.Version = []string{"v1", "v2", "v3"} + return nil + }) + got, err := Versions(c, module) + if err != nil { + t.Fatalf("Versions: %v", err) + } + want := []string{"v1", "v2", "v3"} + if !reflect.DeepEqual(got, want) { + t.Errorf("Versions = %v, want %v", got, want) + } +} + +func TestDefaultVersion(t *testing.T) { + c := aetesting.FakeSingleContext(t, "modules", "GetDefaultVersion", func(req *pb.GetDefaultVersionRequest, res *pb.GetDefaultVersionResponse) error { + if *req.Module != module { + t.Errorf("Module = %v, want %v", req.Module, module) + } + res.Version = proto.String(version) + return nil + }) + got, err := DefaultVersion(c, module) + if err != nil { + t.Fatalf("DefaultVersion: %v", err) + } + if got != version { + t.Errorf("Version = %v, want %v", got, version) + } +} + +func TestStart(t *testing.T) { + c := aetesting.FakeSingleContext(t, "modules", "StartModule", func(req *pb.StartModuleRequest, res *pb.StartModuleResponse) error { + if *req.Module != module { + t.Errorf("Module = %v, want %v", req.Module, module) + } + if *req.Version != version { + t.Errorf("Version = %v, want %v", req.Version, version) + } + return nil + }) + + err := Start(c, module, version) + if err != nil { + t.Fatalf("Start: %v", err) + } +} + +func TestStop(t *testing.T) { + c := aetesting.FakeSingleContext(t, "modules", "StopModule", func(req *pb.StopModuleRequest, res *pb.StopModuleResponse) error { + version := "test-version" + module := "test-module" + if *req.Module != module { + t.Errorf("Module = %v, want %v", req.Module, module) + } + if *req.Version != version { + t.Errorf("Version = %v, want %v", req.Version, version) + } + return nil + }) + + err := Stop(c, module, version) + if err != nil { + t.Fatalf("Stop: %v", err) + } +} diff --git a/vendor/google.golang.org/appengine/namespace.go b/vendor/google.golang.org/appengine/namespace.go new file mode 100644 index 0000000000..21860ca082 --- /dev/null +++ b/vendor/google.golang.org/appengine/namespace.go @@ -0,0 +1,25 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package appengine + +import ( + "fmt" + "regexp" + + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +// Namespace returns a replacement context that operates within the given namespace. +func Namespace(c context.Context, namespace string) (context.Context, error) { + if !validNamespace.MatchString(namespace) { + return nil, fmt.Errorf("appengine: namespace %q does not match /%s/", namespace, validNamespace) + } + return internal.NamespacedContext(c, namespace), nil +} + +// validNamespace matches valid namespace names. +var validNamespace = regexp.MustCompile(`^[0-9A-Za-z._-]{0,100}$`) diff --git a/vendor/google.golang.org/appengine/namespace_test.go b/vendor/google.golang.org/appengine/namespace_test.go new file mode 100644 index 0000000000..847f640bd0 --- /dev/null +++ b/vendor/google.golang.org/appengine/namespace_test.go @@ -0,0 +1,39 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package appengine + +import ( + "testing" + + "golang.org/x/net/context" +) + +func TestNamespaceValidity(t *testing.T) { + testCases := []struct { + namespace string + ok bool + }{ + // data from Python's namespace_manager_test.py + {"", true}, + {"__a.namespace.123__", true}, + {"-_A....NAMESPACE-_", true}, + {"-", true}, + {".", true}, + {".-", true}, + + {"?", false}, + {"+", false}, + {"!", false}, + {" ", false}, + } + for _, tc := range testCases { + _, err := Namespace(context.Background(), tc.namespace) + if err == nil && !tc.ok { + t.Errorf("Namespace %q should be rejected, but wasn't", tc.namespace) + } else if err != nil && tc.ok { + t.Errorf("Namespace %q should be accepted, but wasn't", tc.namespace) + } + } +} diff --git a/vendor/google.golang.org/appengine/remote_api/client.go b/vendor/google.golang.org/appengine/remote_api/client.go new file mode 100644 index 0000000000..ce8aab562f --- /dev/null +++ b/vendor/google.golang.org/appengine/remote_api/client.go @@ -0,0 +1,194 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package remote_api + +// This file provides the client for connecting remotely to a user's production +// application. + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "math/rand" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/remote_api" +) + +// Client is a connection to the production APIs for an application. +type Client struct { + hc *http.Client + url string + appID string +} + +// NewClient returns a client for the given host. All communication will +// be performed over SSL unless the host is localhost. +func NewClient(host string, client *http.Client) (*Client, error) { + // Add an appcfg header to outgoing requests. + wrapClient := new(http.Client) + *wrapClient = *client + t := client.Transport + if t == nil { + t = http.DefaultTransport + } + wrapClient.Transport = &headerAddingRoundTripper{t} + + url := url.URL{ + Scheme: "https", + Host: host, + Path: "/_ah/remote_api", + } + if host == "localhost" || strings.HasPrefix(host, "localhost:") { + url.Scheme = "http" + } + u := url.String() + appID, err := getAppID(wrapClient, u) + if err != nil { + return nil, fmt.Errorf("unable to contact server: %v", err) + } + return &Client{ + hc: wrapClient, + url: u, + appID: appID, + }, nil +} + +// NewContext returns a copy of parent that will cause App Engine API +// calls to be sent to the client's remote host. +func (c *Client) NewContext(parent context.Context) context.Context { + ctx := internal.WithCallOverride(parent, c.call) + ctx = internal.WithLogOverride(ctx, c.logf) + ctx = internal.WithAppIDOverride(ctx, c.appID) + return ctx +} + +// NewRemoteContext returns a context that gives access to the production +// APIs for the application at the given host. All communication will be +// performed over SSL unless the host is localhost. +func NewRemoteContext(host string, client *http.Client) (context.Context, error) { + c, err := NewClient(host, client) + if err != nil { + return nil, err + } + return c.NewContext(context.Background()), nil +} + +var logLevels = map[int64]string{ + 0: "DEBUG", + 1: "INFO", + 2: "WARNING", + 3: "ERROR", + 4: "CRITICAL", +} + +func (c *Client) logf(level int64, format string, args ...interface{}) { + log.Printf(logLevels[level]+": "+format, args...) +} + +func (c *Client) call(ctx context.Context, service, method string, in, out proto.Message) error { + req, err := proto.Marshal(in) + if err != nil { + return fmt.Errorf("error marshalling request: %v", err) + } + + remReq := &pb.Request{ + ServiceName: proto.String(service), + Method: proto.String(method), + Request: req, + // NOTE(djd): RequestId is unused in the server. + } + + req, err = proto.Marshal(remReq) + if err != nil { + return fmt.Errorf("proto.Marshal: %v", err) + } + + // TODO(djd): Respect ctx.Deadline()? + resp, err := c.hc.Post(c.url, "application/octet-stream", bytes.NewReader(req)) + if err != nil { + return fmt.Errorf("error sending request: %v", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("bad response %d; body: %q", resp.StatusCode, body) + } + if err != nil { + return fmt.Errorf("failed reading response: %v", err) + } + remResp := &pb.Response{} + if err := proto.Unmarshal(body, remResp); err != nil { + return fmt.Errorf("error unmarshalling response: %v", err) + } + + if ae := remResp.GetApplicationError(); ae != nil { + return &internal.APIError{ + Code: ae.GetCode(), + Detail: ae.GetDetail(), + Service: service, + } + } + + if remResp.Response == nil { + return fmt.Errorf("unexpected response: %s", proto.MarshalTextString(remResp)) + } + + return proto.Unmarshal(remResp.Response, out) +} + +// This is a forgiving regexp designed to parse the app ID from YAML. +var appIDRE = regexp.MustCompile(`app_id["']?\s*:\s*['"]?([-a-z0-9.:~]+)`) + +func getAppID(client *http.Client, url string) (string, error) { + // Generate a pseudo-random token for handshaking. + token := strconv.Itoa(rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + + resp, err := client.Get(fmt.Sprintf("%s?rtok=%s", url, token)) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("bad response %d; body: %q", resp.StatusCode, body) + } + if err != nil { + return "", fmt.Errorf("failed reading response: %v", err) + } + + // Check the token is present in response. + if !bytes.Contains(body, []byte(token)) { + return "", fmt.Errorf("token not found: want %q; body %q", token, body) + } + + match := appIDRE.FindSubmatch(body) + if match == nil { + return "", fmt.Errorf("app ID not found: body %q", body) + } + + return string(match[1]), nil +} + +type headerAddingRoundTripper struct { + Wrapped http.RoundTripper +} + +func (t *headerAddingRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { + r.Header.Set("X-Appcfg-Api-Version", "1") + return t.Wrapped.RoundTrip(r) +} diff --git a/vendor/google.golang.org/appengine/remote_api/client_test.go b/vendor/google.golang.org/appengine/remote_api/client_test.go new file mode 100644 index 0000000000..7f4bdcf3c8 --- /dev/null +++ b/vendor/google.golang.org/appengine/remote_api/client_test.go @@ -0,0 +1,43 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package remote_api + +import ( + "log" + "net/http" + "testing" + + "golang.org/x/net/context" + "google.golang.org/appengine/datastore" +) + +func TestAppIDRE(t *testing.T) { + appID := "s~my-appid-539" + tests := []string{ + "{rtok: 8306111115908860449, app_id: s~my-appid-539}\n", + "{rtok: 8306111115908860449, app_id: 's~my-appid-539'}\n", + `{rtok: 8306111115908860449, app_id: "s~my-appid-539"}`, + `{rtok: 8306111115908860449, "app_id":"s~my-appid-539"}`, + } + for _, v := range tests { + if g := appIDRE.FindStringSubmatch(v); g == nil || g[1] != appID { + t.Errorf("appIDRE.FindStringSubmatch(%s) got %q, want %q", v, g, appID) + } + } +} + +func ExampleClient() { + c, err := NewClient("example.appspot.com", http.DefaultClient) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() // or from a request + ctx = c.NewContext(ctx) + _, err = datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Foo", nil), struct{ Bar int }{42}) + if err != nil { + log.Fatal(err) + } +} diff --git a/vendor/google.golang.org/appengine/remote_api/remote_api.go b/vendor/google.golang.org/appengine/remote_api/remote_api.go new file mode 100644 index 0000000000..3d2880d64d --- /dev/null +++ b/vendor/google.golang.org/appengine/remote_api/remote_api.go @@ -0,0 +1,152 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package remote_api implements the /_ah/remote_api endpoint. +This endpoint is used by offline tools such as the bulk loader. +*/ +package remote_api // import "google.golang.org/appengine/remote_api" + +import ( + "fmt" + "io" + "io/ioutil" + "net/http" + "strconv" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/remote_api" + "google.golang.org/appengine/log" + "google.golang.org/appengine/user" +) + +func init() { + http.HandleFunc("/_ah/remote_api", handle) +} + +func handle(w http.ResponseWriter, req *http.Request) { + c := appengine.NewContext(req) + + u := user.Current(c) + if u == nil { + u, _ = user.CurrentOAuth(c, + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/appengine.apis", + ) + } + + if !appengine.IsDevAppServer() && (u == nil || !u.Admin) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, "You must be logged in as an administrator to access this.\n") + return + } + if req.Header.Get("X-Appcfg-Api-Version") == "" { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + io.WriteString(w, "This request did not contain a necessary header.\n") + return + } + + if req.Method != "POST" { + // Response must be YAML. + rtok := req.FormValue("rtok") + if rtok == "" { + rtok = "0" + } + w.Header().Set("Content-Type", "text/yaml; charset=utf-8") + fmt.Fprintf(w, `{app_id: %q, rtok: %q}`, internal.FullyQualifiedAppID(c), rtok) + return + } + + defer req.Body.Close() + body, err := ioutil.ReadAll(req.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + log.Errorf(c, "Failed reading body: %v", err) + return + } + remReq := &pb.Request{} + if err := proto.Unmarshal(body, remReq); err != nil { + w.WriteHeader(http.StatusBadRequest) + log.Errorf(c, "Bad body: %v", err) + return + } + + service, method := *remReq.ServiceName, *remReq.Method + if !requestSupported(service, method) { + w.WriteHeader(http.StatusBadRequest) + log.Errorf(c, "Unsupported RPC /%s.%s", service, method) + return + } + + rawReq := &rawMessage{remReq.Request} + rawRes := &rawMessage{} + err = internal.Call(c, service, method, rawReq, rawRes) + + remRes := &pb.Response{} + if err == nil { + remRes.Response = rawRes.buf + } else if ae, ok := err.(*internal.APIError); ok { + remRes.ApplicationError = &pb.ApplicationError{ + Code: &ae.Code, + Detail: &ae.Detail, + } + } else { + // This shouldn't normally happen. + log.Errorf(c, "appengine/remote_api: Unexpected error of type %T: %v", err, err) + remRes.ApplicationError = &pb.ApplicationError{ + Code: proto.Int32(0), + Detail: proto.String(err.Error()), + } + } + out, err := proto.Marshal(remRes) + if err != nil { + // This should not be possible. + w.WriteHeader(500) + log.Errorf(c, "proto.Marshal: %v", err) + return + } + + log.Infof(c, "Spooling %d bytes of response to /%s.%s", len(out), service, method) + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Length", strconv.Itoa(len(out))) + w.Write(out) +} + +// rawMessage is a protocol buffer type that is already serialised. +// This allows the remote_api code here to handle messages +// without having to know the real type. +type rawMessage struct { + buf []byte +} + +func (rm *rawMessage) Marshal() ([]byte, error) { + return rm.buf, nil +} + +func (rm *rawMessage) Unmarshal(buf []byte) error { + rm.buf = make([]byte, len(buf)) + copy(rm.buf, buf) + return nil +} + +func requestSupported(service, method string) bool { + // This list of supported services is taken from SERVICE_PB_MAP in remote_api_services.py + switch service { + case "app_identity_service", "blobstore", "capability_service", "channel", "datastore_v3", + "datastore_v4", "file", "images", "logservice", "mail", "matcher", "memcache", "remote_datastore", + "remote_socket", "search", "modules", "system", "taskqueue", "urlfetch", "user", "xmpp": + return true + } + return false +} + +// Methods to satisfy proto.Message. +func (rm *rawMessage) Reset() { rm.buf = nil } +func (rm *rawMessage) String() string { return strconv.Quote(string(rm.buf)) } +func (*rawMessage) ProtoMessage() {} diff --git a/vendor/google.golang.org/appengine/runtime/runtime.go b/vendor/google.golang.org/appengine/runtime/runtime.go new file mode 100644 index 0000000000..fa6c12b79c --- /dev/null +++ b/vendor/google.golang.org/appengine/runtime/runtime.go @@ -0,0 +1,148 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package runtime exposes information about the resource usage of the application. +It also provides a way to run code in a new background context of a module. + +This package does not work on App Engine "flexible environment". +*/ +package runtime // import "google.golang.org/appengine/runtime" + +import ( + "net/http" + + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/system" +) + +// Statistics represents the system's statistics. +type Statistics struct { + // CPU records the CPU consumed by this instance, in megacycles. + CPU struct { + Total float64 + Rate1M float64 // consumption rate over one minute + Rate10M float64 // consumption rate over ten minutes + } + // RAM records the memory used by the instance, in megabytes. + RAM struct { + Current float64 + Average1M float64 // average usage over one minute + Average10M float64 // average usage over ten minutes + } +} + +func Stats(c context.Context) (*Statistics, error) { + req := &pb.GetSystemStatsRequest{} + res := &pb.GetSystemStatsResponse{} + if err := internal.Call(c, "system", "GetSystemStats", req, res); err != nil { + return nil, err + } + s := &Statistics{} + if res.Cpu != nil { + s.CPU.Total = res.Cpu.GetTotal() + s.CPU.Rate1M = res.Cpu.GetRate1M() + s.CPU.Rate10M = res.Cpu.GetRate10M() + } + if res.Memory != nil { + s.RAM.Current = res.Memory.GetCurrent() + s.RAM.Average1M = res.Memory.GetAverage1M() + s.RAM.Average10M = res.Memory.GetAverage10M() + } + return s, nil +} + +/* +RunInBackground makes an API call that triggers an /_ah/background request. + +There are two independent code paths that need to make contact: +the RunInBackground code, and the /_ah/background handler. The matchmaker +loop arranges for the two paths to meet. The RunInBackground code passes +a send to the matchmaker, the /_ah/background passes a recv to the matchmaker, +and the matchmaker hooks them up. +*/ + +func init() { + http.HandleFunc("/_ah/background", handleBackground) + + sc := make(chan send) + rc := make(chan recv) + sendc, recvc = sc, rc + go matchmaker(sc, rc) +} + +var ( + sendc chan<- send // RunInBackground sends to this + recvc chan<- recv // handleBackground sends to this +) + +type send struct { + id string + f func(context.Context) +} + +type recv struct { + id string + ch chan<- func(context.Context) +} + +func matchmaker(sendc <-chan send, recvc <-chan recv) { + // When one side of the match arrives before the other + // it is inserted in the corresponding map. + waitSend := make(map[string]send) + waitRecv := make(map[string]recv) + + for { + select { + case s := <-sendc: + if r, ok := waitRecv[s.id]; ok { + // meet! + delete(waitRecv, s.id) + r.ch <- s.f + } else { + // waiting for r + waitSend[s.id] = s + } + case r := <-recvc: + if s, ok := waitSend[r.id]; ok { + // meet! + delete(waitSend, r.id) + r.ch <- s.f + } else { + // waiting for s + waitRecv[r.id] = r + } + } + } +} + +var newContext = appengine.NewContext // for testing + +func handleBackground(w http.ResponseWriter, req *http.Request) { + id := req.Header.Get("X-AppEngine-BackgroundRequest") + + ch := make(chan func(context.Context)) + recvc <- recv{id, ch} + (<-ch)(newContext(req)) +} + +// RunInBackground runs f in a background goroutine in this process. +// f is provided a context that may outlast the context provided to RunInBackground. +// This is only valid to invoke from a service set to basic or manual scaling. +func RunInBackground(c context.Context, f func(c context.Context)) error { + req := &pb.StartBackgroundRequestRequest{} + res := &pb.StartBackgroundRequestResponse{} + if err := internal.Call(c, "system", "StartBackgroundRequest", req, res); err != nil { + return err + } + sendc <- send{res.GetRequestId(), f} + return nil +} + +func init() { + internal.RegisterErrorCodeMap("system", pb.SystemServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/runtime/runtime_test.go b/vendor/google.golang.org/appengine/runtime/runtime_test.go new file mode 100644 index 0000000000..8f3a124d27 --- /dev/null +++ b/vendor/google.golang.org/appengine/runtime/runtime_test.go @@ -0,0 +1,101 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/system" +) + +func TestRunInBackgroundSendFirst(t *testing.T) { testRunInBackground(t, true) } +func TestRunInBackgroundRecvFirst(t *testing.T) { testRunInBackground(t, false) } + +func testRunInBackground(t *testing.T, sendFirst bool) { + srv := httptest.NewServer(nil) + defer srv.Close() + + const id = "f00bar" + sendWait, recvWait := make(chan bool), make(chan bool) + sbr := make(chan bool) // strobed when system.StartBackgroundRequest has started + + calls := 0 + c := aetesting.FakeSingleContext(t, "system", "StartBackgroundRequest", func(req *pb.StartBackgroundRequestRequest, res *pb.StartBackgroundRequestResponse) error { + calls++ + if calls > 1 { + t.Errorf("Too many calls to system.StartBackgroundRequest") + } + sbr <- true + res.RequestId = proto.String(id) + <-sendWait + return nil + }) + + var c2 context.Context // a fake + newContext = func(*http.Request) context.Context { + return c2 + } + + var fRun int + f := func(c3 context.Context) { + fRun++ + if c3 != c2 { + t.Errorf("f got a different context than expected") + } + } + + ribErrc := make(chan error) + go func() { + ribErrc <- RunInBackground(c, f) + }() + + brErrc := make(chan error) + go func() { + <-sbr + req, err := http.NewRequest("GET", srv.URL+"/_ah/background", nil) + if err != nil { + brErrc <- fmt.Errorf("http.NewRequest: %v", err) + return + } + req.Header.Set("X-AppEngine-BackgroundRequest", id) + client := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, + } + + <-recvWait + _, err = client.Do(req) + brErrc <- err + }() + + // Send and receive are both waiting at this point. + waits := [2]chan bool{sendWait, recvWait} + if !sendFirst { + waits[0], waits[1] = waits[1], waits[0] + } + waits[0] <- true + time.Sleep(100 * time.Millisecond) + waits[1] <- true + + if err := <-ribErrc; err != nil { + t.Fatalf("RunInBackground: %v", err) + } + if err := <-brErrc; err != nil { + t.Fatalf("background request: %v", err) + } + + if fRun != 1 { + t.Errorf("Got %d runs of f, want 1", fRun) + } +} diff --git a/vendor/google.golang.org/appengine/search/doc.go b/vendor/google.golang.org/appengine/search/doc.go new file mode 100644 index 0000000000..5208f18f69 --- /dev/null +++ b/vendor/google.golang.org/appengine/search/doc.go @@ -0,0 +1,209 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package search provides a client for App Engine's search service. + + +Basic Operations + +Indexes contain documents. Each index is identified by its name: a +human-readable ASCII string. + +Within an index, documents are associated with an ID, which is also +a human-readable ASCII string. A document's contents are a mapping from +case-sensitive field names to values. Valid types for field values are: + - string, + - search.Atom, + - search.HTML, + - time.Time (stored with millisecond precision), + - float64 (value between -2,147,483,647 and 2,147,483,647 inclusive), + - appengine.GeoPoint. + +The Get and Put methods on an Index load and save a document. +A document's contents are typically represented by a struct pointer. + +Example code: + + type Doc struct { + Author string + Comment string + Creation time.Time + } + + index, err := search.Open("comments") + if err != nil { + return err + } + newID, err := index.Put(ctx, "", &Doc{ + Author: "gopher", + Comment: "the truth of the matter", + Creation: time.Now(), + }) + if err != nil { + return err + } + +A single document can be retrieved by its ID. Pass a destination struct +to Get to hold the resulting document. + + var doc Doc + err := index.Get(ctx, id, &doc) + if err != nil { + return err + } + + +Search and Listing Documents + +Indexes have two methods for retrieving multiple documents at once: Search and +List. + +Searching an index for a query will result in an iterator. As with an iterator +from package datastore, pass a destination struct to Next to decode the next +result. Next will return Done when the iterator is exhausted. + + for t := index.Search(ctx, "Comment:truth", nil); ; { + var doc Doc + id, err := t.Next(&doc) + if err == search.Done { + break + } + if err != nil { + return err + } + fmt.Fprintf(w, "%s -> %#v\n", id, doc) + } + +Search takes a string query to determine which documents to return. The query +can be simple, such as a single word to match, or complex. The query +language is described at +https://cloud.google.com/appengine/docs/standard/go/search/query_strings + +Search also takes an optional SearchOptions struct which gives much more +control over how results are calculated and returned. + +Call List to iterate over all documents in an index. + + for t := index.List(ctx, nil); ; { + var doc Doc + id, err := t.Next(&doc) + if err == search.Done { + break + } + if err != nil { + return err + } + fmt.Fprintf(w, "%s -> %#v\n", id, doc) + } + + +Fields and Facets + +A document's contents can be represented by a variety of types. These are +typically struct pointers, but they can also be represented by any type +implementing the FieldLoadSaver interface. The FieldLoadSaver allows metadata +to be set for the document with the DocumentMetadata type. Struct pointers are +more strongly typed and are easier to use; FieldLoadSavers are more flexible. + +A document's contents can be expressed in two ways: fields and facets. + +Fields are the most common way of providing content for documents. Fields can +store data in multiple types and can be matched in searches using query +strings. + +Facets provide a way to attach categorical information to a document. The only +valid types for facets are search.Atom and float64. Facets allow search +results to contain summaries of the categories matched in a search, and to +restrict searches to only match against specific categories. + +By default, for struct pointers, all of the struct fields are used as document +fields, and the field name used is the same as on the struct (and hence must +start with an upper case letter). Struct fields may have a +`search:"name,options"` tag. The name must start with a letter and be +composed only of word characters. A "-" tag name means that the field will be +ignored. If options is "facet" then the struct field will be used as a +document facet. If options is "" then the comma may be omitted. There are no +other recognized options. + +Example code: + + // A and B are renamed to a and b. + // A, C and I are facets. + // D's tag is equivalent to having no tag at all (E). + // F and G are ignored entirely by the search package. + // I has tag information for both the search and json packages. + type TaggedStruct struct { + A float64 `search:"a,facet"` + B float64 `search:"b"` + C float64 `search:",facet"` + D float64 `search:""` + E float64 + F float64 `search:"-"` + G float64 `search:"-,facet"` + I float64 `search:",facet" json:"i"` + } + + +The FieldLoadSaver Interface + +A document's contents can also be represented by any type that implements the +FieldLoadSaver interface. This type may be a struct pointer, but it +does not have to be. The search package will call Load when loading the +document's contents, and Save when saving them. In addition to a slice of +Fields, the Load and Save methods also use the DocumentMetadata type to +provide additional information about a document (such as its Rank, or set of +Facets). Possible uses for this interface include deriving non-stored fields, +verifying fields or setting specific languages for string and HTML fields. + +Example code: + + type CustomFieldsExample struct { + // Item's title and which language it is in. + Title string + Lang string + // Mass, in grams. + Mass int + } + + func (x *CustomFieldsExample) Load(fields []search.Field, meta *search.DocumentMetadata) error { + // Load the title field, failing if any other field is found. + for _, f := range fields { + if f.Name != "title" { + return fmt.Errorf("unknown field %q", f.Name) + } + s, ok := f.Value.(string) + if !ok { + return fmt.Errorf("unsupported type %T for field %q", f.Value, f.Name) + } + x.Title = s + x.Lang = f.Language + } + // Load the mass facet, failing if any other facet is found. + for _, f := range meta.Facets { + if f.Name != "mass" { + return fmt.Errorf("unknown facet %q", f.Name) + } + m, ok := f.Value.(float64) + if !ok { + return fmt.Errorf("unsupported type %T for facet %q", f.Value, f.Name) + } + x.Mass = int(m) + } + return nil + } + + func (x *CustomFieldsExample) Save() ([]search.Field, *search.DocumentMetadata, error) { + fields := []search.Field{ + {Name: "title", Value: x.Title, Language: x.Lang}, + } + meta := &search.DocumentMetadata{ + Facets: { + {Name: "mass", Value: float64(x.Mass)}, + }, + } + return fields, meta, nil + } +*/ +package search diff --git a/vendor/google.golang.org/appengine/search/field.go b/vendor/google.golang.org/appengine/search/field.go new file mode 100644 index 0000000000..707c2d8c0d --- /dev/null +++ b/vendor/google.golang.org/appengine/search/field.go @@ -0,0 +1,82 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package search + +// Field is a name/value pair. A search index's document can be loaded and +// saved as a sequence of Fields. +type Field struct { + // Name is the field name. A valid field name matches /[A-Za-z][A-Za-z0-9_]*/. + Name string + // Value is the field value. The valid types are: + // - string, + // - search.Atom, + // - search.HTML, + // - time.Time (stored with millisecond precision), + // - float64, + // - GeoPoint. + Value interface{} + // Language is a two-letter ISO 639-1 code for the field's language, + // defaulting to "en" if nothing is specified. It may only be specified for + // fields of type string and search.HTML. + Language string + // Derived marks fields that were calculated as a result of a + // FieldExpression provided to Search. This field is ignored when saving a + // document. + Derived bool +} + +// Facet is a name/value pair which is used to add categorical information to a +// document. +type Facet struct { + // Name is the facet name. A valid facet name matches /[A-Za-z][A-Za-z0-9_]*/. + // A facet name cannot be longer than 500 characters. + Name string + // Value is the facet value. + // + // When being used in documents (for example, in + // DocumentMetadata.Facets), the valid types are: + // - search.Atom, + // - float64. + // + // When being used in SearchOptions.Refinements or being returned + // in FacetResult, the valid types are: + // - search.Atom, + // - search.Range. + Value interface{} +} + +// DocumentMetadata is a struct containing information describing a given document. +type DocumentMetadata struct { + // Rank is an integer specifying the order the document will be returned in + // search results. If zero, the rank will be set to the number of seconds since + // 2011-01-01 00:00:00 UTC when being Put into an index. + Rank int + // Facets is the set of facets for this document. + Facets []Facet +} + +// FieldLoadSaver can be converted from and to a slice of Fields +// with additional document metadata. +type FieldLoadSaver interface { + Load([]Field, *DocumentMetadata) error + Save() ([]Field, *DocumentMetadata, error) +} + +// FieldList converts a []Field to implement FieldLoadSaver. +type FieldList []Field + +// Load loads all of the provided fields into l. +// It does not first reset *l to an empty slice. +func (l *FieldList) Load(f []Field, _ *DocumentMetadata) error { + *l = append(*l, f...) + return nil +} + +// Save returns all of l's fields as a slice of Fields. +func (l *FieldList) Save() ([]Field, *DocumentMetadata, error) { + return *l, nil, nil +} + +var _ FieldLoadSaver = (*FieldList)(nil) diff --git a/vendor/google.golang.org/appengine/search/search.go b/vendor/google.golang.org/appengine/search/search.go new file mode 100644 index 0000000000..35a567d62e --- /dev/null +++ b/vendor/google.golang.org/appengine/search/search.go @@ -0,0 +1,1189 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package search // import "google.golang.org/appengine/search" + +// TODO: let Put specify the document language: "en", "fr", etc. Also: order_id?? storage?? +// TODO: Index.GetAll (or Iterator.GetAll)? +// TODO: struct <-> protobuf tests. +// TODO: enforce Python's MIN_NUMBER_VALUE and MIN_DATE (which would disallow a zero +// time.Time)? _MAXIMUM_STRING_LENGTH? + +import ( + "errors" + "fmt" + "math" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/search" +) + +const maxDocumentsPerPutDelete = 200 + +var ( + // ErrInvalidDocumentType is returned when methods like Put, Get or Next + // are passed a dst or src argument of invalid type. + ErrInvalidDocumentType = errors.New("search: invalid document type") + + // ErrNoSuchDocument is returned when no document was found for a given ID. + ErrNoSuchDocument = errors.New("search: no such document") + + // ErrTooManyDocuments is returned when the user passes too many documents to + // PutMulti or DeleteMulti. + ErrTooManyDocuments = fmt.Errorf("search: too many documents given to put or delete (max is %d)", maxDocumentsPerPutDelete) +) + +// Atom is a document field whose contents are indexed as a single indivisible +// string. +type Atom string + +// HTML is a document field whose contents are indexed as HTML. Only text nodes +// are indexed: "foobar" will be treated as "foobar". +type HTML string + +// validIndexNameOrDocID is the Go equivalent of Python's +// _ValidateVisiblePrintableAsciiNotReserved. +func validIndexNameOrDocID(s string) bool { + if strings.HasPrefix(s, "!") { + return false + } + for _, c := range s { + if c < 0x21 || 0x7f <= c { + return false + } + } + return true +} + +var ( + fieldNameRE = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`) + languageRE = regexp.MustCompile(`^[a-z]{2}$`) +) + +// validFieldName is the Go equivalent of Python's _CheckFieldName. It checks +// the validity of both field and facet names. +func validFieldName(s string) bool { + return len(s) <= 500 && fieldNameRE.MatchString(s) +} + +// validDocRank checks that the ranks is in the range [0, 2^31). +func validDocRank(r int) bool { + return 0 <= r && r <= (1<<31-1) +} + +// validLanguage checks that a language looks like ISO 639-1. +func validLanguage(s string) bool { + return languageRE.MatchString(s) +} + +// validFloat checks that f is in the range [-2147483647, 2147483647]. +func validFloat(f float64) bool { + return -(1<<31-1) <= f && f <= (1<<31-1) +} + +// Index is an index of documents. +type Index struct { + spec pb.IndexSpec +} + +// orderIDEpoch forms the basis for populating OrderId on documents. +var orderIDEpoch = time.Date(2011, 1, 1, 0, 0, 0, 0, time.UTC) + +// Open opens the index with the given name. The index is created if it does +// not already exist. +// +// The name is a human-readable ASCII string. It must contain no whitespace +// characters and not start with "!". +func Open(name string) (*Index, error) { + if !validIndexNameOrDocID(name) { + return nil, fmt.Errorf("search: invalid index name %q", name) + } + return &Index{ + spec: pb.IndexSpec{ + Name: &name, + }, + }, nil +} + +// Put saves src to the index. If id is empty, a new ID is allocated by the +// service and returned. If id is not empty, any existing index entry for that +// ID is replaced. +// +// The ID is a human-readable ASCII string. It must contain no whitespace +// characters and not start with "!". +// +// src must be a non-nil struct pointer or implement the FieldLoadSaver +// interface. +func (x *Index) Put(c context.Context, id string, src interface{}) (string, error) { + ids, err := x.PutMulti(c, []string{id}, []interface{}{src}) + if err != nil { + return "", err + } + return ids[0], nil +} + +// PutMulti is like Put, but is more efficient for adding multiple documents to +// the index at once. +// +// Up to 200 documents can be added at once. ErrTooManyDocuments is returned if +// you try to add more. +// +// ids can either be an empty slice (which means new IDs will be allocated for +// each of the documents added) or a slice the same size as srcs. +// +// The error may be an instance of appengine.MultiError, in which case it will +// be the same size as srcs and the individual errors inside will correspond +// with the items in srcs. +func (x *Index) PutMulti(c context.Context, ids []string, srcs []interface{}) ([]string, error) { + if len(ids) != 0 && len(srcs) != len(ids) { + return nil, fmt.Errorf("search: PutMulti expects ids and srcs slices of the same length") + } + if len(srcs) > maxDocumentsPerPutDelete { + return nil, ErrTooManyDocuments + } + + docs := make([]*pb.Document, len(srcs)) + for i, s := range srcs { + var err error + docs[i], err = saveDoc(s) + if err != nil { + return nil, err + } + + if len(ids) != 0 && ids[i] != "" { + if !validIndexNameOrDocID(ids[i]) { + return nil, fmt.Errorf("search: invalid ID %q", ids[i]) + } + docs[i].Id = proto.String(ids[i]) + } + } + + // spec is modified by Call when applying the current Namespace, so copy it to + // avoid retaining the namespace beyond the scope of the Call. + spec := x.spec + req := &pb.IndexDocumentRequest{ + Params: &pb.IndexDocumentParams{ + Document: docs, + IndexSpec: &spec, + }, + } + res := &pb.IndexDocumentResponse{} + if err := internal.Call(c, "search", "IndexDocument", req, res); err != nil { + return nil, err + } + multiErr, hasErr := make(appengine.MultiError, len(res.Status)), false + for i, s := range res.Status { + if s.GetCode() != pb.SearchServiceError_OK { + multiErr[i] = fmt.Errorf("search: %s: %s", s.GetCode(), s.GetErrorDetail()) + hasErr = true + } + } + if hasErr { + return res.DocId, multiErr + } + + if len(res.Status) != len(docs) || len(res.DocId) != len(docs) { + return nil, fmt.Errorf("search: internal error: wrong number of results (%d Statuses, %d DocIDs, expected %d)", + len(res.Status), len(res.DocId), len(docs)) + } + return res.DocId, nil +} + +// Get loads the document with the given ID into dst. +// +// The ID is a human-readable ASCII string. It must be non-empty, contain no +// whitespace characters and not start with "!". +// +// dst must be a non-nil struct pointer or implement the FieldLoadSaver +// interface. +// +// ErrFieldMismatch is returned when a field is to be loaded into a different +// type than the one it was stored from, or when a field is missing or +// unexported in the destination struct. ErrFieldMismatch is only returned if +// dst is a struct pointer. It is up to the callee to decide whether this error +// is fatal, recoverable, or ignorable. +func (x *Index) Get(c context.Context, id string, dst interface{}) error { + if id == "" || !validIndexNameOrDocID(id) { + return fmt.Errorf("search: invalid ID %q", id) + } + req := &pb.ListDocumentsRequest{ + Params: &pb.ListDocumentsParams{ + IndexSpec: &x.spec, + StartDocId: proto.String(id), + Limit: proto.Int32(1), + }, + } + res := &pb.ListDocumentsResponse{} + if err := internal.Call(c, "search", "ListDocuments", req, res); err != nil { + return err + } + if res.Status == nil || res.Status.GetCode() != pb.SearchServiceError_OK { + return fmt.Errorf("search: %s: %s", res.Status.GetCode(), res.Status.GetErrorDetail()) + } + if len(res.Document) != 1 || res.Document[0].GetId() != id { + return ErrNoSuchDocument + } + return loadDoc(dst, res.Document[0], nil) +} + +// Delete deletes a document from the index. +func (x *Index) Delete(c context.Context, id string) error { + return x.DeleteMulti(c, []string{id}) +} + +// DeleteMulti deletes multiple documents from the index. +// +// The returned error may be an instance of appengine.MultiError, in which case +// it will be the same size as srcs and the individual errors inside will +// correspond with the items in srcs. +func (x *Index) DeleteMulti(c context.Context, ids []string) error { + if len(ids) > maxDocumentsPerPutDelete { + return ErrTooManyDocuments + } + + req := &pb.DeleteDocumentRequest{ + Params: &pb.DeleteDocumentParams{ + DocId: ids, + IndexSpec: &x.spec, + }, + } + res := &pb.DeleteDocumentResponse{} + if err := internal.Call(c, "search", "DeleteDocument", req, res); err != nil { + return err + } + if len(res.Status) != len(ids) { + return fmt.Errorf("search: internal error: wrong number of results (%d, expected %d)", + len(res.Status), len(ids)) + } + multiErr, hasErr := make(appengine.MultiError, len(ids)), false + for i, s := range res.Status { + if s.GetCode() != pb.SearchServiceError_OK { + multiErr[i] = fmt.Errorf("search: %s: %s", s.GetCode(), s.GetErrorDetail()) + hasErr = true + } + } + if hasErr { + return multiErr + } + return nil +} + +// List lists all of the documents in an index. The documents are returned in +// increasing ID order. +func (x *Index) List(c context.Context, opts *ListOptions) *Iterator { + t := &Iterator{ + c: c, + index: x, + count: -1, + listInclusive: true, + more: moreList, + } + if opts != nil { + t.listStartID = opts.StartID + t.limit = opts.Limit + t.idsOnly = opts.IDsOnly + } + return t +} + +func moreList(t *Iterator) error { + req := &pb.ListDocumentsRequest{ + Params: &pb.ListDocumentsParams{ + IndexSpec: &t.index.spec, + }, + } + if t.listStartID != "" { + req.Params.StartDocId = &t.listStartID + req.Params.IncludeStartDoc = &t.listInclusive + } + if t.limit > 0 { + req.Params.Limit = proto.Int32(int32(t.limit)) + } + if t.idsOnly { + req.Params.KeysOnly = &t.idsOnly + } + + res := &pb.ListDocumentsResponse{} + if err := internal.Call(t.c, "search", "ListDocuments", req, res); err != nil { + return err + } + if res.Status == nil || res.Status.GetCode() != pb.SearchServiceError_OK { + return fmt.Errorf("search: %s: %s", res.Status.GetCode(), res.Status.GetErrorDetail()) + } + t.listRes = res.Document + t.listStartID, t.listInclusive, t.more = "", false, nil + if len(res.Document) != 0 && t.limit <= 0 { + if id := res.Document[len(res.Document)-1].GetId(); id != "" { + t.listStartID, t.more = id, moreList + } + } + return nil +} + +// ListOptions are the options for listing documents in an index. Passing a nil +// *ListOptions is equivalent to using the default values. +type ListOptions struct { + // StartID is the inclusive lower bound for the ID of the returned + // documents. The zero value means all documents will be returned. + StartID string + + // Limit is the maximum number of documents to return. The zero value + // indicates no limit. + Limit int + + // IDsOnly indicates that only document IDs should be returned for the list + // operation; no document fields are populated. + IDsOnly bool +} + +// Search searches the index for the given query. +func (x *Index) Search(c context.Context, query string, opts *SearchOptions) *Iterator { + t := &Iterator{ + c: c, + index: x, + searchQuery: query, + more: moreSearch, + } + if opts != nil { + if opts.Cursor != "" { + if opts.Offset != 0 { + return errIter("at most one of Cursor and Offset may be specified") + } + t.searchCursor = proto.String(string(opts.Cursor)) + } + t.limit = opts.Limit + t.fields = opts.Fields + t.idsOnly = opts.IDsOnly + t.sort = opts.Sort + t.exprs = opts.Expressions + t.refinements = opts.Refinements + t.facetOpts = opts.Facets + t.searchOffset = opts.Offset + t.countAccuracy = opts.CountAccuracy + } + return t +} + +func moreSearch(t *Iterator) error { + // We use per-result (rather than single/per-page) cursors since this + // lets us return a Cursor for every iterator document. The two cursor + // types are largely interchangeable: a page cursor is the same as the + // last per-result cursor in a given search response. + req := &pb.SearchRequest{ + Params: &pb.SearchParams{ + IndexSpec: &t.index.spec, + Query: &t.searchQuery, + Cursor: t.searchCursor, + CursorType: pb.SearchParams_PER_RESULT.Enum(), + FieldSpec: &pb.FieldSpec{ + Name: t.fields, + }, + }, + } + if t.limit > 0 { + req.Params.Limit = proto.Int32(int32(t.limit)) + } + if t.searchOffset > 0 { + req.Params.Offset = proto.Int32(int32(t.searchOffset)) + t.searchOffset = 0 + } + if t.countAccuracy > 0 { + req.Params.MatchedCountAccuracy = proto.Int32(int32(t.countAccuracy)) + } + if t.idsOnly { + req.Params.KeysOnly = &t.idsOnly + } + if t.sort != nil { + if err := sortToProto(t.sort, req.Params); err != nil { + return err + } + } + if t.refinements != nil { + if err := refinementsToProto(t.refinements, req.Params); err != nil { + return err + } + } + for _, e := range t.exprs { + req.Params.FieldSpec.Expression = append(req.Params.FieldSpec.Expression, &pb.FieldSpec_Expression{ + Name: proto.String(e.Name), + Expression: proto.String(e.Expr), + }) + } + for _, f := range t.facetOpts { + if err := f.setParams(req.Params); err != nil { + return fmt.Errorf("bad FacetSearchOption: %v", err) + } + } + // Don't repeat facet search. + t.facetOpts = nil + + res := &pb.SearchResponse{} + if err := internal.Call(t.c, "search", "Search", req, res); err != nil { + return err + } + if res.Status == nil || res.Status.GetCode() != pb.SearchServiceError_OK { + return fmt.Errorf("search: %s: %s", res.Status.GetCode(), res.Status.GetErrorDetail()) + } + t.searchRes = res.Result + if len(res.FacetResult) > 0 { + t.facetRes = res.FacetResult + } + t.count = int(*res.MatchedCount) + if t.limit > 0 { + t.more = nil + } else { + t.more = moreSearch + } + return nil +} + +// SearchOptions are the options for searching an index. Passing a nil +// *SearchOptions is equivalent to using the default values. +type SearchOptions struct { + // Limit is the maximum number of documents to return. The zero value + // indicates no limit. + Limit int + + // IDsOnly indicates that only document IDs should be returned for the search + // operation; no document fields are populated. + IDsOnly bool + + // Sort controls the ordering of search results. + Sort *SortOptions + + // Fields specifies which document fields to include in the results. If omitted, + // all document fields are returned. No more than 100 fields may be specified. + Fields []string + + // Expressions specifies additional computed fields to add to each returned + // document. + Expressions []FieldExpression + + // Facets controls what facet information is returned for these search results. + // If no options are specified, no facet results will be returned. + Facets []FacetSearchOption + + // Refinements filters the returned documents by requiring them to contain facets + // with specific values. Refinements are applied in conjunction for facets with + // different names, and in disjunction otherwise. + Refinements []Facet + + // Cursor causes the results to commence with the first document after + // the document associated with the cursor. + Cursor Cursor + + // Offset specifies the number of documents to skip over before returning results. + // When specified, Cursor must be nil. + Offset int + + // CountAccuracy specifies the maximum result count that can be expected to + // be accurate. If zero, the count accuracy defaults to 20. + CountAccuracy int +} + +// Cursor represents an iterator's position. +// +// The string value of a cursor is web-safe. It can be saved and restored +// for later use. +type Cursor string + +// FieldExpression defines a custom expression to evaluate for each result. +type FieldExpression struct { + // Name is the name to use for the computed field. + Name string + + // Expr is evaluated to provide a custom content snippet for each document. + // See https://cloud.google.com/appengine/docs/standard/go/search/options for + // the supported expression syntax. + Expr string +} + +// FacetSearchOption controls what facet information is returned in search results. +type FacetSearchOption interface { + setParams(*pb.SearchParams) error +} + +// AutoFacetDiscovery returns a FacetSearchOption which enables automatic facet +// discovery for the search. Automatic facet discovery looks for the facets +// which appear the most often in the aggregate in the matched documents. +// +// The maximum number of facets returned is controlled by facetLimit, and the +// maximum number of values per facet by facetLimit. A limit of zero indicates +// a default limit should be used. +func AutoFacetDiscovery(facetLimit, valueLimit int) FacetSearchOption { + return &autoFacetOpt{facetLimit, valueLimit} +} + +type autoFacetOpt struct { + facetLimit, valueLimit int +} + +const defaultAutoFacetLimit = 10 // As per python runtime search.py. + +func (o *autoFacetOpt) setParams(params *pb.SearchParams) error { + lim := int32(o.facetLimit) + if lim == 0 { + lim = defaultAutoFacetLimit + } + params.AutoDiscoverFacetCount = &lim + if o.valueLimit > 0 { + params.FacetAutoDetectParam = &pb.FacetAutoDetectParam{ + ValueLimit: proto.Int32(int32(o.valueLimit)), + } + } + return nil +} + +// FacetDiscovery returns a FacetSearchOption which selects a facet to be +// returned with the search results. By default, the most frequently +// occurring values for that facet will be returned. However, you can also +// specify a list of particular Atoms or specific Ranges to return. +func FacetDiscovery(name string, value ...interface{}) FacetSearchOption { + return &facetOpt{name, value} +} + +type facetOpt struct { + name string + values []interface{} +} + +func (o *facetOpt) setParams(params *pb.SearchParams) error { + req := &pb.FacetRequest{Name: &o.name} + params.IncludeFacet = append(params.IncludeFacet, req) + if len(o.values) == 0 { + return nil + } + vtype := reflect.TypeOf(o.values[0]) + reqParam := &pb.FacetRequestParam{} + for _, v := range o.values { + if reflect.TypeOf(v) != vtype { + return errors.New("values must all be Atom, or must all be Range") + } + switch v := v.(type) { + case Atom: + reqParam.ValueConstraint = append(reqParam.ValueConstraint, string(v)) + case Range: + rng, err := rangeToProto(v) + if err != nil { + return fmt.Errorf("invalid range: %v", err) + } + reqParam.Range = append(reqParam.Range, rng) + default: + return fmt.Errorf("unsupported value type %T", v) + } + } + req.Params = reqParam + return nil +} + +// FacetDocumentDepth returns a FacetSearchOption which controls the number of +// documents to be evaluated with preparing facet results. +func FacetDocumentDepth(depth int) FacetSearchOption { + return facetDepthOpt(depth) +} + +type facetDepthOpt int + +func (o facetDepthOpt) setParams(params *pb.SearchParams) error { + params.FacetDepth = proto.Int32(int32(o)) + return nil +} + +// FacetResult represents the number of times a particular facet and value +// appeared in the documents matching a search request. +type FacetResult struct { + Facet + + // Count is the number of times this specific facet and value appeared in the + // matching documents. + Count int +} + +// Range represents a numeric range with inclusive start and exclusive end. +// Start may be specified as math.Inf(-1) to indicate there is no minimum +// value, and End may similarly be specified as math.Inf(1); at least one of +// Start or End must be a finite number. +type Range struct { + Start, End float64 +} + +var ( + negInf = math.Inf(-1) + posInf = math.Inf(1) +) + +// AtLeast returns a Range matching any value greater than, or equal to, min. +func AtLeast(min float64) Range { + return Range{Start: min, End: posInf} +} + +// LessThan returns a Range matching any value less than max. +func LessThan(max float64) Range { + return Range{Start: negInf, End: max} +} + +// SortOptions control the ordering and scoring of search results. +type SortOptions struct { + // Expressions is a slice of expressions representing a multi-dimensional + // sort. + Expressions []SortExpression + + // Scorer, when specified, will cause the documents to be scored according to + // search term frequency. + Scorer Scorer + + // Limit is the maximum number of objects to score and/or sort. Limit cannot + // be more than 10,000. The zero value indicates a default limit. + Limit int +} + +// SortExpression defines a single dimension for sorting a document. +type SortExpression struct { + // Expr is evaluated to provide a sorting value for each document. + // See https://cloud.google.com/appengine/docs/standard/go/search/options for + // the supported expression syntax. + Expr string + + // Reverse causes the documents to be sorted in ascending order. + Reverse bool + + // The default value to use when no field is present or the expresion + // cannot be calculated for a document. For text sorts, Default must + // be of type string; for numeric sorts, float64. + Default interface{} +} + +// A Scorer defines how a document is scored. +type Scorer interface { + toProto(*pb.ScorerSpec) +} + +type enumScorer struct { + enum pb.ScorerSpec_Scorer +} + +func (e enumScorer) toProto(spec *pb.ScorerSpec) { + spec.Scorer = e.enum.Enum() +} + +var ( + // MatchScorer assigns a score based on term frequency in a document. + MatchScorer Scorer = enumScorer{pb.ScorerSpec_MATCH_SCORER} + + // RescoringMatchScorer assigns a score based on the quality of the query + // match. It is similar to a MatchScorer but uses a more complex scoring + // algorithm based on match term frequency and other factors like field type. + // Please be aware that this algorithm is continually refined and can change + // over time without notice. This means that the ordering of search results + // that use this scorer can also change without notice. + RescoringMatchScorer Scorer = enumScorer{pb.ScorerSpec_RESCORING_MATCH_SCORER} +) + +func sortToProto(sort *SortOptions, params *pb.SearchParams) error { + for _, e := range sort.Expressions { + spec := &pb.SortSpec{ + SortExpression: proto.String(e.Expr), + } + if e.Reverse { + spec.SortDescending = proto.Bool(false) + } + if e.Default != nil { + switch d := e.Default.(type) { + case float64: + spec.DefaultValueNumeric = &d + case string: + spec.DefaultValueText = &d + default: + return fmt.Errorf("search: invalid Default type %T for expression %q", d, e.Expr) + } + } + params.SortSpec = append(params.SortSpec, spec) + } + + spec := &pb.ScorerSpec{} + if sort.Limit > 0 { + spec.Limit = proto.Int32(int32(sort.Limit)) + params.ScorerSpec = spec + } + if sort.Scorer != nil { + sort.Scorer.toProto(spec) + params.ScorerSpec = spec + } + + return nil +} + +func refinementsToProto(refinements []Facet, params *pb.SearchParams) error { + for _, r := range refinements { + ref := &pb.FacetRefinement{ + Name: proto.String(r.Name), + } + switch v := r.Value.(type) { + case Atom: + ref.Value = proto.String(string(v)) + case Range: + rng, err := rangeToProto(v) + if err != nil { + return fmt.Errorf("search: refinement for facet %q: %v", r.Name, err) + } + // Unfortunately there are two identical messages for identify Facet ranges. + ref.Range = &pb.FacetRefinement_Range{Start: rng.Start, End: rng.End} + default: + return fmt.Errorf("search: unsupported refinement for facet %q of type %T", r.Name, v) + } + params.FacetRefinement = append(params.FacetRefinement, ref) + } + return nil +} + +func rangeToProto(r Range) (*pb.FacetRange, error) { + rng := &pb.FacetRange{} + if r.Start != negInf { + if !validFloat(r.Start) { + return nil, errors.New("invalid value for Start") + } + rng.Start = proto.String(strconv.FormatFloat(r.Start, 'e', -1, 64)) + } else if r.End == posInf { + return nil, errors.New("either Start or End must be finite") + } + if r.End != posInf { + if !validFloat(r.End) { + return nil, errors.New("invalid value for End") + } + rng.End = proto.String(strconv.FormatFloat(r.End, 'e', -1, 64)) + } + return rng, nil +} + +func protoToRange(rng *pb.FacetRefinement_Range) Range { + r := Range{Start: negInf, End: posInf} + if x, err := strconv.ParseFloat(rng.GetStart(), 64); err != nil { + r.Start = x + } + if x, err := strconv.ParseFloat(rng.GetEnd(), 64); err != nil { + r.End = x + } + return r +} + +// Iterator is the result of searching an index for a query or listing an +// index. +type Iterator struct { + c context.Context + index *Index + err error + + listRes []*pb.Document + listStartID string + listInclusive bool + + searchRes []*pb.SearchResult + facetRes []*pb.FacetResult + searchQuery string + searchCursor *string + searchOffset int + sort *SortOptions + + fields []string + exprs []FieldExpression + refinements []Facet + facetOpts []FacetSearchOption + + more func(*Iterator) error + + count int + countAccuracy int + limit int // items left to return; 0 for unlimited. + idsOnly bool +} + +// errIter returns an iterator that only returns the given error. +func errIter(err string) *Iterator { + return &Iterator{ + err: errors.New(err), + } +} + +// Done is returned when a query iteration has completed. +var Done = errors.New("search: query has no more results") + +// Count returns an approximation of the number of documents matched by the +// query. It is only valid to call for iterators returned by Search. +func (t *Iterator) Count() int { return t.count } + +// fetchMore retrieves more results, if there are no errors or pending results. +func (t *Iterator) fetchMore() { + if t.err == nil && len(t.listRes)+len(t.searchRes) == 0 && t.more != nil { + t.err = t.more(t) + } +} + +// Next returns the ID of the next result. When there are no more results, +// Done is returned as the error. +// +// dst must be a non-nil struct pointer, implement the FieldLoadSaver +// interface, or be a nil interface value. If a non-nil dst is provided, it +// will be filled with the indexed fields. dst is ignored if this iterator was +// created with an IDsOnly option. +func (t *Iterator) Next(dst interface{}) (string, error) { + t.fetchMore() + if t.err != nil { + return "", t.err + } + + var doc *pb.Document + var exprs []*pb.Field + switch { + case len(t.listRes) != 0: + doc = t.listRes[0] + t.listRes = t.listRes[1:] + case len(t.searchRes) != 0: + doc = t.searchRes[0].Document + exprs = t.searchRes[0].Expression + t.searchCursor = t.searchRes[0].Cursor + t.searchRes = t.searchRes[1:] + default: + return "", Done + } + if doc == nil { + return "", errors.New("search: internal error: no document returned") + } + if !t.idsOnly && dst != nil { + if err := loadDoc(dst, doc, exprs); err != nil { + return "", err + } + } + return doc.GetId(), nil +} + +// Cursor returns the cursor associated with the current document (that is, +// the document most recently returned by a call to Next). +// +// Passing this cursor in a future call to Search will cause those results +// to commence with the first document after the current document. +func (t *Iterator) Cursor() Cursor { + if t.searchCursor == nil { + return "" + } + return Cursor(*t.searchCursor) +} + +// Facets returns the facets found within the search results, if any facets +// were requested in the SearchOptions. +func (t *Iterator) Facets() ([][]FacetResult, error) { + t.fetchMore() + if t.err != nil && t.err != Done { + return nil, t.err + } + + var facets [][]FacetResult + for _, f := range t.facetRes { + fres := make([]FacetResult, 0, len(f.Value)) + for _, v := range f.Value { + ref := v.Refinement + facet := FacetResult{ + Facet: Facet{Name: ref.GetName()}, + Count: int(v.GetCount()), + } + if ref.Value != nil { + facet.Value = Atom(*ref.Value) + } else { + facet.Value = protoToRange(ref.Range) + } + fres = append(fres, facet) + } + facets = append(facets, fres) + } + return facets, nil +} + +// saveDoc converts from a struct pointer or +// FieldLoadSaver/FieldMetadataLoadSaver to the Document protobuf. +func saveDoc(src interface{}) (*pb.Document, error) { + var err error + var fields []Field + var meta *DocumentMetadata + switch x := src.(type) { + case FieldLoadSaver: + fields, meta, err = x.Save() + default: + fields, meta, err = saveStructWithMeta(src) + } + if err != nil { + return nil, err + } + + fieldsProto, err := fieldsToProto(fields) + if err != nil { + return nil, err + } + d := &pb.Document{ + Field: fieldsProto, + OrderId: proto.Int32(int32(time.Since(orderIDEpoch).Seconds())), + OrderIdSource: pb.Document_DEFAULTED.Enum(), + } + if meta != nil { + if meta.Rank != 0 { + if !validDocRank(meta.Rank) { + return nil, fmt.Errorf("search: invalid rank %d, must be [0, 2^31)", meta.Rank) + } + *d.OrderId = int32(meta.Rank) + d.OrderIdSource = pb.Document_SUPPLIED.Enum() + } + if len(meta.Facets) > 0 { + facets, err := facetsToProto(meta.Facets) + if err != nil { + return nil, err + } + d.Facet = facets + } + } + return d, nil +} + +func fieldsToProto(src []Field) ([]*pb.Field, error) { + // Maps to catch duplicate time or numeric fields. + timeFields, numericFields := make(map[string]bool), make(map[string]bool) + dst := make([]*pb.Field, 0, len(src)) + for _, f := range src { + if !validFieldName(f.Name) { + return nil, fmt.Errorf("search: invalid field name %q", f.Name) + } + fieldValue := &pb.FieldValue{} + switch x := f.Value.(type) { + case string: + fieldValue.Type = pb.FieldValue_TEXT.Enum() + fieldValue.StringValue = proto.String(x) + case Atom: + fieldValue.Type = pb.FieldValue_ATOM.Enum() + fieldValue.StringValue = proto.String(string(x)) + case HTML: + fieldValue.Type = pb.FieldValue_HTML.Enum() + fieldValue.StringValue = proto.String(string(x)) + case time.Time: + if timeFields[f.Name] { + return nil, fmt.Errorf("search: duplicate time field %q", f.Name) + } + timeFields[f.Name] = true + fieldValue.Type = pb.FieldValue_DATE.Enum() + fieldValue.StringValue = proto.String(strconv.FormatInt(x.UnixNano()/1e6, 10)) + case float64: + if numericFields[f.Name] { + return nil, fmt.Errorf("search: duplicate numeric field %q", f.Name) + } + if !validFloat(x) { + return nil, fmt.Errorf("search: numeric field %q with invalid value %f", f.Name, x) + } + numericFields[f.Name] = true + fieldValue.Type = pb.FieldValue_NUMBER.Enum() + fieldValue.StringValue = proto.String(strconv.FormatFloat(x, 'e', -1, 64)) + case appengine.GeoPoint: + if !x.Valid() { + return nil, fmt.Errorf( + "search: GeoPoint field %q with invalid value %v", + f.Name, x) + } + fieldValue.Type = pb.FieldValue_GEO.Enum() + fieldValue.Geo = &pb.FieldValue_Geo{ + Lat: proto.Float64(x.Lat), + Lng: proto.Float64(x.Lng), + } + default: + return nil, fmt.Errorf("search: unsupported field type: %v", reflect.TypeOf(f.Value)) + } + if f.Language != "" { + switch f.Value.(type) { + case string, HTML: + if !validLanguage(f.Language) { + return nil, fmt.Errorf("search: invalid language for field %q: %q", f.Name, f.Language) + } + fieldValue.Language = proto.String(f.Language) + default: + return nil, fmt.Errorf("search: setting language not supported for field %q of type %T", f.Name, f.Value) + } + } + if p := fieldValue.StringValue; p != nil && !utf8.ValidString(*p) { + return nil, fmt.Errorf("search: %q field is invalid UTF-8: %q", f.Name, *p) + } + dst = append(dst, &pb.Field{ + Name: proto.String(f.Name), + Value: fieldValue, + }) + } + return dst, nil +} + +func facetsToProto(src []Facet) ([]*pb.Facet, error) { + dst := make([]*pb.Facet, 0, len(src)) + for _, f := range src { + if !validFieldName(f.Name) { + return nil, fmt.Errorf("search: invalid facet name %q", f.Name) + } + facetValue := &pb.FacetValue{} + switch x := f.Value.(type) { + case Atom: + if !utf8.ValidString(string(x)) { + return nil, fmt.Errorf("search: %q facet is invalid UTF-8: %q", f.Name, x) + } + facetValue.Type = pb.FacetValue_ATOM.Enum() + facetValue.StringValue = proto.String(string(x)) + case float64: + if !validFloat(x) { + return nil, fmt.Errorf("search: numeric facet %q with invalid value %f", f.Name, x) + } + facetValue.Type = pb.FacetValue_NUMBER.Enum() + facetValue.StringValue = proto.String(strconv.FormatFloat(x, 'e', -1, 64)) + default: + return nil, fmt.Errorf("search: unsupported facet type: %v", reflect.TypeOf(f.Value)) + } + dst = append(dst, &pb.Facet{ + Name: proto.String(f.Name), + Value: facetValue, + }) + } + return dst, nil +} + +// loadDoc converts from protobufs to a struct pointer or +// FieldLoadSaver/FieldMetadataLoadSaver. The src param provides the document's +// stored fields and facets, and any document metadata. An additional slice of +// fields, exprs, may optionally be provided to contain any derived expressions +// requested by the developer. +func loadDoc(dst interface{}, src *pb.Document, exprs []*pb.Field) (err error) { + fields, err := protoToFields(src.Field) + if err != nil { + return err + } + facets, err := protoToFacets(src.Facet) + if err != nil { + return err + } + if len(exprs) > 0 { + exprFields, err := protoToFields(exprs) + if err != nil { + return err + } + // Mark each field as derived. + for i := range exprFields { + exprFields[i].Derived = true + } + fields = append(fields, exprFields...) + } + meta := &DocumentMetadata{ + Rank: int(src.GetOrderId()), + Facets: facets, + } + switch x := dst.(type) { + case FieldLoadSaver: + return x.Load(fields, meta) + default: + return loadStructWithMeta(dst, fields, meta) + } +} + +func protoToFields(fields []*pb.Field) ([]Field, error) { + dst := make([]Field, 0, len(fields)) + for _, field := range fields { + fieldValue := field.GetValue() + f := Field{ + Name: field.GetName(), + } + switch fieldValue.GetType() { + case pb.FieldValue_TEXT: + f.Value = fieldValue.GetStringValue() + f.Language = fieldValue.GetLanguage() + case pb.FieldValue_ATOM: + f.Value = Atom(fieldValue.GetStringValue()) + case pb.FieldValue_HTML: + f.Value = HTML(fieldValue.GetStringValue()) + f.Language = fieldValue.GetLanguage() + case pb.FieldValue_DATE: + sv := fieldValue.GetStringValue() + millis, err := strconv.ParseInt(sv, 10, 64) + if err != nil { + return nil, fmt.Errorf("search: internal error: bad time.Time encoding %q: %v", sv, err) + } + f.Value = time.Unix(0, millis*1e6) + case pb.FieldValue_NUMBER: + sv := fieldValue.GetStringValue() + x, err := strconv.ParseFloat(sv, 64) + if err != nil { + return nil, err + } + f.Value = x + case pb.FieldValue_GEO: + geoValue := fieldValue.GetGeo() + geoPoint := appengine.GeoPoint{geoValue.GetLat(), geoValue.GetLng()} + if !geoPoint.Valid() { + return nil, fmt.Errorf("search: internal error: invalid GeoPoint encoding: %v", geoPoint) + } + f.Value = geoPoint + default: + return nil, fmt.Errorf("search: internal error: unknown data type %s", fieldValue.GetType()) + } + dst = append(dst, f) + } + return dst, nil +} + +func protoToFacets(facets []*pb.Facet) ([]Facet, error) { + if len(facets) == 0 { + return nil, nil + } + dst := make([]Facet, 0, len(facets)) + for _, facet := range facets { + facetValue := facet.GetValue() + f := Facet{ + Name: facet.GetName(), + } + switch facetValue.GetType() { + case pb.FacetValue_ATOM: + f.Value = Atom(facetValue.GetStringValue()) + case pb.FacetValue_NUMBER: + sv := facetValue.GetStringValue() + x, err := strconv.ParseFloat(sv, 64) + if err != nil { + return nil, err + } + f.Value = x + default: + return nil, fmt.Errorf("search: internal error: unknown data type %s", facetValue.GetType()) + } + dst = append(dst, f) + } + return dst, nil +} + +func namespaceMod(m proto.Message, namespace string) { + set := func(s **string) { + if *s == nil { + *s = &namespace + } + } + switch m := m.(type) { + case *pb.IndexDocumentRequest: + set(&m.Params.IndexSpec.Namespace) + case *pb.ListDocumentsRequest: + set(&m.Params.IndexSpec.Namespace) + case *pb.DeleteDocumentRequest: + set(&m.Params.IndexSpec.Namespace) + case *pb.SearchRequest: + set(&m.Params.IndexSpec.Namespace) + } +} + +func init() { + internal.RegisterErrorCodeMap("search", pb.SearchServiceError_ErrorCode_name) + internal.NamespaceMods["search"] = namespaceMod +} diff --git a/vendor/google.golang.org/appengine/search/search_test.go b/vendor/google.golang.org/appengine/search/search_test.go new file mode 100644 index 0000000000..0459cd7492 --- /dev/null +++ b/vendor/google.golang.org/appengine/search/search_test.go @@ -0,0 +1,1270 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package search + +import ( + "errors" + "fmt" + "reflect" + "strings" + "testing" + "time" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/search" +) + +type TestDoc struct { + String string + Atom Atom + HTML HTML + Float float64 + Location appengine.GeoPoint + Time time.Time +} + +type FieldListWithMeta struct { + Fields FieldList + Meta *DocumentMetadata +} + +func (f *FieldListWithMeta) Load(fields []Field, meta *DocumentMetadata) error { + f.Meta = meta + return f.Fields.Load(fields, nil) +} + +func (f *FieldListWithMeta) Save() ([]Field, *DocumentMetadata, error) { + fields, _, err := f.Fields.Save() + return fields, f.Meta, err +} + +// Assert that FieldListWithMeta satisfies FieldLoadSaver +var _ FieldLoadSaver = &FieldListWithMeta{} + +var ( + float = 3.14159 + floatOut = "3.14159e+00" + latitude = 37.3894 + longitude = 122.0819 + testGeo = appengine.GeoPoint{latitude, longitude} + testString = "foobar" + testTime = time.Unix(1337324400, 0) + testTimeOut = "1337324400000" + searchMeta = &DocumentMetadata{ + Rank: 42, + } + searchDoc = TestDoc{ + String: testString, + Atom: Atom(testString), + HTML: HTML(testString), + Float: float, + Location: testGeo, + Time: testTime, + } + searchFields = FieldList{ + Field{Name: "String", Value: testString}, + Field{Name: "Atom", Value: Atom(testString)}, + Field{Name: "HTML", Value: HTML(testString)}, + Field{Name: "Float", Value: float}, + Field{Name: "Location", Value: testGeo}, + Field{Name: "Time", Value: testTime}, + } + // searchFieldsWithLang is a copy of the searchFields with the Language field + // set on text/HTML Fields. + searchFieldsWithLang = FieldList{} + protoFields = []*pb.Field{ + newStringValueField("String", testString, pb.FieldValue_TEXT), + newStringValueField("Atom", testString, pb.FieldValue_ATOM), + newStringValueField("HTML", testString, pb.FieldValue_HTML), + newStringValueField("Float", floatOut, pb.FieldValue_NUMBER), + { + Name: proto.String("Location"), + Value: &pb.FieldValue{ + Geo: &pb.FieldValue_Geo{ + Lat: proto.Float64(latitude), + Lng: proto.Float64(longitude), + }, + Type: pb.FieldValue_GEO.Enum(), + }, + }, + newStringValueField("Time", testTimeOut, pb.FieldValue_DATE), + } +) + +func init() { + for _, f := range searchFields { + if f.Name == "String" || f.Name == "HTML" { + f.Language = "en" + } + searchFieldsWithLang = append(searchFieldsWithLang, f) + } +} + +func newStringValueField(name, value string, valueType pb.FieldValue_ContentType) *pb.Field { + return &pb.Field{ + Name: proto.String(name), + Value: &pb.FieldValue{ + StringValue: proto.String(value), + Type: valueType.Enum(), + }, + } +} + +func newFacet(name, value string, valueType pb.FacetValue_ContentType) *pb.Facet { + return &pb.Facet{ + Name: proto.String(name), + Value: &pb.FacetValue{ + StringValue: proto.String(value), + Type: valueType.Enum(), + }, + } +} + +func TestValidIndexNameOrDocID(t *testing.T) { + testCases := []struct { + s string + want bool + }{ + {"", true}, + {"!", false}, + {"$", true}, + {"!bad", false}, + {"good!", true}, + {"alsoGood", true}, + {"has spaces", false}, + {"is_inva\xffid_UTF-8", false}, + {"is_non-ASCïI", false}, + {"underscores_are_ok", true}, + } + for _, tc := range testCases { + if got := validIndexNameOrDocID(tc.s); got != tc.want { + t.Errorf("%q: got %v, want %v", tc.s, got, tc.want) + } + } +} + +func TestLoadDoc(t *testing.T) { + got, want := TestDoc{}, searchDoc + if err := loadDoc(&got, &pb.Document{Field: protoFields}, nil); err != nil { + t.Fatalf("loadDoc: %v", err) + } + if got != want { + t.Errorf("loadDoc: got %v, wanted %v", got, want) + } +} + +func TestSaveDoc(t *testing.T) { + got, err := saveDoc(&searchDoc) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + want := protoFields + if !reflect.DeepEqual(got.Field, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } +} + +func TestSaveDocUsesDefaultedRankIfNotSpecified(t *testing.T) { + got, err := saveDoc(&searchDoc) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + orderIdSource := got.GetOrderIdSource() + if orderIdSource != pb.Document_DEFAULTED { + t.Errorf("OrderIdSource: got %v, wanted DEFAULTED", orderIdSource) + } +} + +func TestLoadFieldList(t *testing.T) { + var got FieldList + want := searchFieldsWithLang + if err := loadDoc(&got, &pb.Document{Field: protoFields}, nil); err != nil { + t.Fatalf("loadDoc: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } +} + +func TestLangFields(t *testing.T) { + fl := &FieldList{ + {Name: "Foo", Value: "I am English", Language: "en"}, + {Name: "Bar", Value: "私は日本人だ", Language: "ja"}, + } + var got FieldList + doc, err := saveDoc(fl) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + if err := loadDoc(&got, doc, nil); err != nil { + t.Fatalf("loadDoc: %v", err) + } + if want := fl; !reflect.DeepEqual(&got, want) { + t.Errorf("got %v\nwant %v", got, want) + } +} + +func TestSaveFieldList(t *testing.T) { + got, err := saveDoc(&searchFields) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + want := protoFields + if !reflect.DeepEqual(got.Field, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } +} + +func TestLoadFieldAndExprList(t *testing.T) { + var got, want FieldList + for i, f := range searchFieldsWithLang { + f.Derived = (i >= 2) // First 2 elements are "fields", next are "expressions". + want = append(want, f) + } + doc, expr := &pb.Document{Field: protoFields[:2]}, protoFields[2:] + if err := loadDoc(&got, doc, expr); err != nil { + t.Fatalf("loadDoc: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v\nwant %v", got, want) + } +} + +func TestLoadMeta(t *testing.T) { + var got FieldListWithMeta + want := FieldListWithMeta{ + Meta: searchMeta, + Fields: searchFieldsWithLang, + } + doc := &pb.Document{ + Field: protoFields, + OrderId: proto.Int32(42), + OrderIdSource: pb.Document_SUPPLIED.Enum(), + } + if err := loadDoc(&got, doc, nil); err != nil { + t.Fatalf("loadDoc: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } +} + +func TestSaveMeta(t *testing.T) { + got, err := saveDoc(&FieldListWithMeta{ + Meta: searchMeta, + Fields: searchFields, + }) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + want := &pb.Document{ + Field: protoFields, + OrderId: proto.Int32(42), + OrderIdSource: pb.Document_SUPPLIED.Enum(), + } + if !proto.Equal(got, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } +} + +func TestSaveMetaWithDefaultedRank(t *testing.T) { + metaWithoutRank := &DocumentMetadata{ + Rank: 0, + } + got, err := saveDoc(&FieldListWithMeta{ + Meta: metaWithoutRank, + Fields: searchFields, + }) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + want := &pb.Document{ + Field: protoFields, + OrderId: got.OrderId, + OrderIdSource: pb.Document_DEFAULTED.Enum(), + } + if !proto.Equal(got, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } +} + +func TestSaveWithoutMetaUsesDefaultedRank(t *testing.T) { + got, err := saveDoc(&FieldListWithMeta{ + Fields: searchFields, + }) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + want := &pb.Document{ + Field: protoFields, + OrderId: got.OrderId, + OrderIdSource: pb.Document_DEFAULTED.Enum(), + } + if !proto.Equal(got, want) { + t.Errorf("\ngot %v\nwant %v", got, want) + } +} + +func TestLoadSaveWithStruct(t *testing.T) { + type gopher struct { + Name string + Info string `search:"about"` + Legs float64 `search:",facet"` + Fuzz Atom `search:"Fur,facet"` + } + + doc := gopher{"Gopher", "Likes slide rules.", 4, Atom("furry")} + pb := &pb.Document{ + Field: []*pb.Field{ + newStringValueField("Name", "Gopher", pb.FieldValue_TEXT), + newStringValueField("about", "Likes slide rules.", pb.FieldValue_TEXT), + }, + Facet: []*pb.Facet{ + newFacet("Legs", "4e+00", pb.FacetValue_NUMBER), + newFacet("Fur", "furry", pb.FacetValue_ATOM), + }, + } + + var gotDoc gopher + if err := loadDoc(&gotDoc, pb, nil); err != nil { + t.Fatalf("loadDoc: %v", err) + } + if !reflect.DeepEqual(gotDoc, doc) { + t.Errorf("loading doc\ngot %v\nwant %v", gotDoc, doc) + } + + gotPB, err := saveDoc(&doc) + if err != nil { + t.Fatalf("saveDoc: %v", err) + } + gotPB.OrderId = nil // Don't test: it's time dependent. + gotPB.OrderIdSource = nil // Don't test because it's contingent on OrderId. + if !proto.Equal(gotPB, pb) { + t.Errorf("saving doc\ngot %v\nwant %v", gotPB, pb) + } +} + +func TestValidFieldNames(t *testing.T) { + testCases := []struct { + name string + valid bool + }{ + {"Normal", true}, + {"Also_OK_123", true}, + {"Not so great", false}, + {"lower_case", true}, + {"Exclaim!", false}, + {"Hello세상아 안녕", false}, + {"", false}, + {"Hεllo", false}, + {strings.Repeat("A", 500), true}, + {strings.Repeat("A", 501), false}, + } + + for _, tc := range testCases { + _, err := saveDoc(&FieldList{ + Field{Name: tc.name, Value: "val"}, + }) + if err != nil && !strings.Contains(err.Error(), "invalid field name") { + t.Errorf("unexpected err %q for field name %q", err, tc.name) + } + if (err == nil) != tc.valid { + t.Errorf("field %q: expected valid %t, received err %v", tc.name, tc.valid, err) + } + } +} + +func TestValidLangs(t *testing.T) { + testCases := []struct { + field Field + valid bool + }{ + {Field{Name: "Foo", Value: "String", Language: ""}, true}, + {Field{Name: "Foo", Value: "String", Language: "en"}, true}, + {Field{Name: "Foo", Value: "String", Language: "aussie"}, false}, + {Field{Name: "Foo", Value: "String", Language: "12"}, false}, + {Field{Name: "Foo", Value: HTML("String"), Language: "en"}, true}, + {Field{Name: "Foo", Value: Atom("String"), Language: "en"}, false}, + {Field{Name: "Foo", Value: 42, Language: "en"}, false}, + } + + for _, tt := range testCases { + _, err := saveDoc(&FieldList{tt.field}) + if err == nil != tt.valid { + t.Errorf("Field %v, got error %v, wanted valid %t", tt.field, err, tt.valid) + } + } +} + +func TestDuplicateFields(t *testing.T) { + testCases := []struct { + desc string + fields FieldList + errMsg string // Non-empty if we expect an error + }{ + { + desc: "multi string", + fields: FieldList{{Name: "FieldA", Value: "val1"}, {Name: "FieldA", Value: "val2"}, {Name: "FieldA", Value: "val3"}}, + }, + { + desc: "multi atom", + fields: FieldList{{Name: "FieldA", Value: Atom("val1")}, {Name: "FieldA", Value: Atom("val2")}, {Name: "FieldA", Value: Atom("val3")}}, + }, + { + desc: "mixed", + fields: FieldList{{Name: "FieldA", Value: testString}, {Name: "FieldA", Value: testTime}, {Name: "FieldA", Value: float}}, + }, + { + desc: "multi time", + fields: FieldList{{Name: "FieldA", Value: testTime}, {Name: "FieldA", Value: testTime}}, + errMsg: `duplicate time field "FieldA"`, + }, + { + desc: "multi num", + fields: FieldList{{Name: "FieldA", Value: float}, {Name: "FieldA", Value: float}}, + errMsg: `duplicate numeric field "FieldA"`, + }, + } + for _, tc := range testCases { + _, err := saveDoc(&tc.fields) + if (err == nil) != (tc.errMsg == "") || (err != nil && !strings.Contains(err.Error(), tc.errMsg)) { + t.Errorf("%s: got err %v, wanted %q", tc.desc, err, tc.errMsg) + } + } +} + +func TestLoadErrFieldMismatch(t *testing.T) { + testCases := []struct { + desc string + dst interface{} + src []*pb.Field + err error + }{ + { + desc: "missing", + dst: &struct{ One string }{}, + src: []*pb.Field{newStringValueField("Two", "woop!", pb.FieldValue_TEXT)}, + err: &ErrFieldMismatch{ + FieldName: "Two", + Reason: "no such struct field", + }, + }, + { + desc: "wrong type", + dst: &struct{ Num float64 }{}, + src: []*pb.Field{newStringValueField("Num", "woop!", pb.FieldValue_TEXT)}, + err: &ErrFieldMismatch{ + FieldName: "Num", + Reason: "type mismatch: float64 for string data", + }, + }, + { + desc: "unsettable", + dst: &struct{ lower string }{}, + src: []*pb.Field{newStringValueField("lower", "woop!", pb.FieldValue_TEXT)}, + err: &ErrFieldMismatch{ + FieldName: "lower", + Reason: "cannot set struct field", + }, + }, + } + for _, tc := range testCases { + err := loadDoc(tc.dst, &pb.Document{Field: tc.src}, nil) + if !reflect.DeepEqual(err, tc.err) { + t.Errorf("%s, got err %v, wanted %v", tc.desc, err, tc.err) + } + } +} + +func TestLimit(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, res *pb.SearchResponse) error { + limit := 20 // Default per page. + if req.Params.Limit != nil { + limit = int(*req.Params.Limit) + } + res.Status = &pb.RequestStatus{Code: pb.SearchServiceError_OK.Enum()} + res.MatchedCount = proto.Int64(int64(limit)) + for i := 0; i < limit; i++ { + res.Result = append(res.Result, &pb.SearchResult{Document: &pb.Document{}}) + res.Cursor = proto.String("moreresults") + } + return nil + }) + + const maxDocs = 500 // Limit maximum number of docs. + testCases := []struct { + limit, want int + }{ + {limit: 0, want: maxDocs}, + {limit: 42, want: 42}, + {limit: 100, want: 100}, + {limit: 1000, want: maxDocs}, + } + + for _, tt := range testCases { + it := index.Search(c, "gopher", &SearchOptions{Limit: tt.limit, IDsOnly: true}) + count := 0 + for ; count < maxDocs; count++ { + _, err := it.Next(nil) + if err == Done { + break + } + if err != nil { + t.Fatalf("err after %d: %v", count, err) + } + } + if count != tt.want { + t.Errorf("got %d results, expected %d", count, tt.want) + } + } +} + +func TestPut(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { + expectedIn := &pb.IndexDocumentRequest{ + Params: &pb.IndexDocumentParams{ + Document: []*pb.Document{ + {Field: protoFields, OrderId: proto.Int32(42), OrderIdSource: pb.Document_SUPPLIED.Enum()}, + }, + IndexSpec: &pb.IndexSpec{ + Name: proto.String("Doc"), + }, + }, + } + if !proto.Equal(in, expectedIn) { + return fmt.Errorf("unsupported argument:\ngot %v\nwant %v", in, expectedIn) + } + *out = pb.IndexDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + }, + DocId: []string{ + "doc_id", + }, + } + return nil + }) + + id, err := index.Put(c, "", &FieldListWithMeta{ + Meta: searchMeta, + Fields: searchFields, + }) + if err != nil { + t.Fatal(err) + } + if want := "doc_id"; id != want { + t.Errorf("Got doc ID %q, want %q", id, want) + } +} + +func TestPutAutoOrderID(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { + if len(in.Params.GetDocument()) < 1 { + return fmt.Errorf("expected at least one Document, got %v", in) + } + got, want := in.Params.Document[0].GetOrderId(), int32(time.Since(orderIDEpoch).Seconds()) + if d := got - want; -5 > d || d > 5 { + return fmt.Errorf("got OrderId %d, want near %d", got, want) + } + *out = pb.IndexDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + }, + DocId: []string{ + "doc_id", + }, + } + return nil + }) + + if _, err := index.Put(c, "", &searchFields); err != nil { + t.Fatal(err) + } +} + +func TestPutBadStatus(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(_ *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { + *out = pb.IndexDocumentResponse{ + Status: []*pb.RequestStatus{ + { + Code: pb.SearchServiceError_INVALID_REQUEST.Enum(), + ErrorDetail: proto.String("insufficient gophers"), + }, + }, + } + return nil + }) + + wantErr := "search: INVALID_REQUEST: insufficient gophers" + if _, err := index.Put(c, "", &searchFields); err == nil || err.Error() != wantErr { + t.Fatalf("Put: got %v error, want %q", err, wantErr) + } +} + +func TestPutMultiNilIDSlice(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { + if len(in.Params.GetDocument()) < 1 { + return fmt.Errorf("got %v, want at least 1 document", in) + } + got, want := in.Params.Document[0].GetOrderId(), int32(time.Since(orderIDEpoch).Seconds()) + if d := got - want; -5 > d || d > 5 { + return fmt.Errorf("got OrderId %d, want near %d", got, want) + } + *out = pb.IndexDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + }, + DocId: []string{ + "doc_id", + }, + } + return nil + }) + + if _, err := index.PutMulti(c, nil, []interface{}{&searchFields}); err != nil { + t.Fatal(err) + } +} + +func TestPutMultiError(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { + *out = pb.IndexDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + {Code: pb.SearchServiceError_PERMISSION_DENIED.Enum(), ErrorDetail: proto.String("foo")}, + }, + DocId: []string{ + "id1", + "", + }, + } + return nil + }) + + switch _, err := index.PutMulti(c, nil, []interface{}{&searchFields, &searchFields}); { + case err == nil: + t.Fatalf("got nil, want error") + case err.(appengine.MultiError)[0] != nil: + t.Fatalf("got %v, want nil MultiError[0]", err.(appengine.MultiError)[0]) + case err.(appengine.MultiError)[1] == nil: + t.Fatalf("got nil, want not-nill MultiError[1]") + } +} + +func TestPutMultiWrongNumberOfIDs(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { + return nil + }) + + if _, err := index.PutMulti(c, []string{"a"}, []interface{}{&searchFields, &searchFields}); err == nil { + t.Fatal("got success, want error") + } +} + +func TestPutMultiTooManyDocs(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { + return nil + }) + + srcs := make([]interface{}, 201) + for i, _ := range srcs { + srcs[i] = &searchFields + } + + if _, err := index.PutMulti(c, nil, srcs); err != ErrTooManyDocuments { + t.Fatalf("got %v, want ErrTooManyDocuments", err) + } +} + +func TestSortOptions(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + noErr := errors.New("") // Sentinel err to return to prevent sending request. + + testCases := []struct { + desc string + sort *SortOptions + wantSort []*pb.SortSpec + wantScorer *pb.ScorerSpec + wantErr string + }{ + { + desc: "No SortOptions", + }, + { + desc: "Basic", + sort: &SortOptions{ + Expressions: []SortExpression{ + {Expr: "dog"}, + {Expr: "cat", Reverse: true}, + {Expr: "gopher", Default: "blue"}, + {Expr: "fish", Default: 2.0}, + }, + Limit: 42, + Scorer: MatchScorer, + }, + wantSort: []*pb.SortSpec{ + {SortExpression: proto.String("dog")}, + {SortExpression: proto.String("cat"), SortDescending: proto.Bool(false)}, + {SortExpression: proto.String("gopher"), DefaultValueText: proto.String("blue")}, + {SortExpression: proto.String("fish"), DefaultValueNumeric: proto.Float64(2)}, + }, + wantScorer: &pb.ScorerSpec{ + Limit: proto.Int32(42), + Scorer: pb.ScorerSpec_MATCH_SCORER.Enum(), + }, + }, + { + desc: "Bad expression default", + sort: &SortOptions{ + Expressions: []SortExpression{ + {Expr: "dog", Default: true}, + }, + }, + wantErr: `search: invalid Default type bool for expression "dog"`, + }, + { + desc: "RescoringMatchScorer", + sort: &SortOptions{Scorer: RescoringMatchScorer}, + wantScorer: &pb.ScorerSpec{Scorer: pb.ScorerSpec_RESCORING_MATCH_SCORER.Enum()}, + }, + } + + for _, tt := range testCases { + c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, _ *pb.SearchResponse) error { + params := req.Params + if !reflect.DeepEqual(params.SortSpec, tt.wantSort) { + t.Errorf("%s: params.SortSpec=%v; want %v", tt.desc, params.SortSpec, tt.wantSort) + } + if !reflect.DeepEqual(params.ScorerSpec, tt.wantScorer) { + t.Errorf("%s: params.ScorerSpec=%v; want %v", tt.desc, params.ScorerSpec, tt.wantScorer) + } + return noErr // Always return some error to prevent response parsing. + }) + + it := index.Search(c, "gopher", &SearchOptions{Sort: tt.sort}) + _, err := it.Next(nil) + if err == nil { + t.Fatalf("%s: err==nil; should not happen", tt.desc) + } + if err.Error() != tt.wantErr { + t.Errorf("%s: got error %q, want %q", tt.desc, err, tt.wantErr) + } + } +} + +func TestFieldSpec(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + errFoo := errors.New("foo") // sentinel error when there isn't one. + + testCases := []struct { + desc string + opts *SearchOptions + want *pb.FieldSpec + }{ + { + desc: "No options", + want: &pb.FieldSpec{}, + }, + { + desc: "Fields", + opts: &SearchOptions{ + Fields: []string{"one", "two"}, + }, + want: &pb.FieldSpec{ + Name: []string{"one", "two"}, + }, + }, + { + desc: "Expressions", + opts: &SearchOptions{ + Expressions: []FieldExpression{ + {Name: "one", Expr: "price * quantity"}, + {Name: "two", Expr: "min(daily_use, 10) * rate"}, + }, + }, + want: &pb.FieldSpec{ + Expression: []*pb.FieldSpec_Expression{ + {Name: proto.String("one"), Expression: proto.String("price * quantity")}, + {Name: proto.String("two"), Expression: proto.String("min(daily_use, 10) * rate")}, + }, + }, + }, + } + + for _, tt := range testCases { + c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, _ *pb.SearchResponse) error { + params := req.Params + if !reflect.DeepEqual(params.FieldSpec, tt.want) { + t.Errorf("%s: params.FieldSpec=%v; want %v", tt.desc, params.FieldSpec, tt.want) + } + return errFoo // Always return some error to prevent response parsing. + }) + + it := index.Search(c, "gopher", tt.opts) + if _, err := it.Next(nil); err != errFoo { + t.Fatalf("%s: got error %v; want %v", tt.desc, err, errFoo) + } + } +} + +func TestBasicSearchOpts(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + noErr := errors.New("") // Sentinel err to return to prevent sending request. + + testCases := []struct { + desc string + facetOpts []FacetSearchOption + cursor Cursor + offset int + countAccuracy int + want *pb.SearchParams + wantErr string + }{ + { + desc: "No options", + want: &pb.SearchParams{}, + }, + { + desc: "Default auto discovery", + facetOpts: []FacetSearchOption{ + AutoFacetDiscovery(0, 0), + }, + want: &pb.SearchParams{ + AutoDiscoverFacetCount: proto.Int32(10), + }, + }, + { + desc: "Auto discovery", + facetOpts: []FacetSearchOption{ + AutoFacetDiscovery(7, 12), + }, + want: &pb.SearchParams{ + AutoDiscoverFacetCount: proto.Int32(7), + FacetAutoDetectParam: &pb.FacetAutoDetectParam{ + ValueLimit: proto.Int32(12), + }, + }, + }, + { + desc: "Param Depth", + facetOpts: []FacetSearchOption{ + AutoFacetDiscovery(7, 12), + }, + want: &pb.SearchParams{ + AutoDiscoverFacetCount: proto.Int32(7), + FacetAutoDetectParam: &pb.FacetAutoDetectParam{ + ValueLimit: proto.Int32(12), + }, + }, + }, + { + desc: "Doc depth", + facetOpts: []FacetSearchOption{ + FacetDocumentDepth(123), + }, + want: &pb.SearchParams{ + FacetDepth: proto.Int32(123), + }, + }, + { + desc: "Facet discovery", + facetOpts: []FacetSearchOption{ + FacetDiscovery("colour"), + FacetDiscovery("size", Atom("M"), Atom("L")), + FacetDiscovery("price", LessThan(7), Range{7, 14}, AtLeast(14)), + }, + want: &pb.SearchParams{ + IncludeFacet: []*pb.FacetRequest{ + {Name: proto.String("colour")}, + {Name: proto.String("size"), Params: &pb.FacetRequestParam{ + ValueConstraint: []string{"M", "L"}, + }}, + {Name: proto.String("price"), Params: &pb.FacetRequestParam{ + Range: []*pb.FacetRange{ + {End: proto.String("7e+00")}, + {Start: proto.String("7e+00"), End: proto.String("1.4e+01")}, + {Start: proto.String("1.4e+01")}, + }, + }}, + }, + }, + }, + { + desc: "Facet discovery - bad value", + facetOpts: []FacetSearchOption{ + FacetDiscovery("colour", true), + }, + wantErr: "bad FacetSearchOption: unsupported value type bool", + }, + { + desc: "Facet discovery - mix value types", + facetOpts: []FacetSearchOption{ + FacetDiscovery("colour", Atom("blue"), AtLeast(7)), + }, + wantErr: "bad FacetSearchOption: values must all be Atom, or must all be Range", + }, + { + desc: "Facet discovery - invalid range", + facetOpts: []FacetSearchOption{ + FacetDiscovery("colour", Range{negInf, posInf}), + }, + wantErr: "bad FacetSearchOption: invalid range: either Start or End must be finite", + }, + { + desc: "Cursor", + cursor: Cursor("mycursor"), + want: &pb.SearchParams{ + Cursor: proto.String("mycursor"), + }, + }, + { + desc: "Offset", + offset: 121, + want: &pb.SearchParams{ + Offset: proto.Int32(121), + }, + }, + { + desc: "Cursor and Offset set", + cursor: Cursor("mycursor"), + offset: 121, + wantErr: "at most one of Cursor and Offset may be specified", + }, + { + desc: "Count accuracy", + countAccuracy: 100, + want: &pb.SearchParams{ + MatchedCountAccuracy: proto.Int32(100), + }, + }, + } + + for _, tt := range testCases { + c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, _ *pb.SearchResponse) error { + if tt.want == nil { + t.Errorf("%s: expected call to fail", tt.desc) + return nil + } + // Set default fields. + tt.want.Query = proto.String("gopher") + tt.want.IndexSpec = &pb.IndexSpec{Name: proto.String("Doc")} + tt.want.CursorType = pb.SearchParams_PER_RESULT.Enum() + tt.want.FieldSpec = &pb.FieldSpec{} + if got := req.Params; !reflect.DeepEqual(got, tt.want) { + t.Errorf("%s: params=%v; want %v", tt.desc, got, tt.want) + } + return noErr // Always return some error to prevent response parsing. + }) + + it := index.Search(c, "gopher", &SearchOptions{ + Facets: tt.facetOpts, + Cursor: tt.cursor, + Offset: tt.offset, + CountAccuracy: tt.countAccuracy, + }) + _, err := it.Next(nil) + if err == nil { + t.Fatalf("%s: err==nil; should not happen", tt.desc) + } + if err.Error() != tt.wantErr { + t.Errorf("%s: got error %q, want %q", tt.desc, err, tt.wantErr) + } + } +} + +func TestFacetRefinements(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + noErr := errors.New("") // Sentinel err to return to prevent sending request. + + testCases := []struct { + desc string + refine []Facet + want []*pb.FacetRefinement + wantErr string + }{ + { + desc: "No refinements", + }, + { + desc: "Basic", + refine: []Facet{ + {Name: "fur", Value: Atom("fluffy")}, + {Name: "age", Value: LessThan(123)}, + {Name: "age", Value: AtLeast(0)}, + {Name: "legs", Value: Range{Start: 3, End: 5}}, + }, + want: []*pb.FacetRefinement{ + {Name: proto.String("fur"), Value: proto.String("fluffy")}, + {Name: proto.String("age"), Range: &pb.FacetRefinement_Range{End: proto.String("1.23e+02")}}, + {Name: proto.String("age"), Range: &pb.FacetRefinement_Range{Start: proto.String("0e+00")}}, + {Name: proto.String("legs"), Range: &pb.FacetRefinement_Range{Start: proto.String("3e+00"), End: proto.String("5e+00")}}, + }, + }, + { + desc: "Infinite range", + refine: []Facet{ + {Name: "age", Value: Range{Start: negInf, End: posInf}}, + }, + wantErr: `search: refinement for facet "age": either Start or End must be finite`, + }, + { + desc: "Bad End value in range", + refine: []Facet{ + {Name: "age", Value: LessThan(2147483648)}, + }, + wantErr: `search: refinement for facet "age": invalid value for End`, + }, + { + desc: "Bad Start value in range", + refine: []Facet{ + {Name: "age", Value: AtLeast(-2147483649)}, + }, + wantErr: `search: refinement for facet "age": invalid value for Start`, + }, + { + desc: "Unknown value type", + refine: []Facet{ + {Name: "age", Value: "you can't use strings!"}, + }, + wantErr: `search: unsupported refinement for facet "age" of type string`, + }, + } + + for _, tt := range testCases { + c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, _ *pb.SearchResponse) error { + if got := req.Params.FacetRefinement; !reflect.DeepEqual(got, tt.want) { + t.Errorf("%s: params.FacetRefinement=%v; want %v", tt.desc, got, tt.want) + } + return noErr // Always return some error to prevent response parsing. + }) + + it := index.Search(c, "gopher", &SearchOptions{Refinements: tt.refine}) + _, err := it.Next(nil) + if err == nil { + t.Fatalf("%s: err==nil; should not happen", tt.desc) + } + if err.Error() != tt.wantErr { + t.Errorf("%s: got error %q, want %q", tt.desc, err, tt.wantErr) + } + } +} + +func TestNamespaceResetting(t *testing.T) { + namec := make(chan *string, 1) + c0 := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(req *pb.IndexDocumentRequest, res *pb.IndexDocumentResponse) error { + namec <- req.Params.IndexSpec.Namespace + return fmt.Errorf("RPC error") + }) + + // Check that wrapping c0 in a namespace twice works correctly. + c1, err := appengine.Namespace(c0, "A") + if err != nil { + t.Fatalf("appengine.Namespace: %v", err) + } + c2, err := appengine.Namespace(c1, "") // should act as the original context + if err != nil { + t.Fatalf("appengine.Namespace: %v", err) + } + + i := (&Index{}) + + i.Put(c0, "something", &searchDoc) + if ns := <-namec; ns != nil { + t.Errorf(`Put with c0: ns = %q, want nil`, *ns) + } + + i.Put(c1, "something", &searchDoc) + if ns := <-namec; ns == nil { + t.Error(`Put with c1: ns = nil, want "A"`) + } else if *ns != "A" { + t.Errorf(`Put with c1: ns = %q, want "A"`, *ns) + } + + i.Put(c2, "something", &searchDoc) + if ns := <-namec; ns != nil { + t.Errorf(`Put with c2: ns = %q, want nil`, *ns) + } +} + +func TestDelete(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "DeleteDocument", func(in *pb.DeleteDocumentRequest, out *pb.DeleteDocumentResponse) error { + expectedIn := &pb.DeleteDocumentRequest{ + Params: &pb.DeleteDocumentParams{ + DocId: []string{"id"}, + IndexSpec: &pb.IndexSpec{Name: proto.String("Doc")}, + }, + } + if !proto.Equal(in, expectedIn) { + return fmt.Errorf("unsupported argument:\ngot %v\nwant %v", in, expectedIn) + } + *out = pb.DeleteDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + }, + } + return nil + }) + + if err := index.Delete(c, "id"); err != nil { + t.Fatal(err) + } +} + +func TestDeleteMulti(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "DeleteDocument", func(in *pb.DeleteDocumentRequest, out *pb.DeleteDocumentResponse) error { + expectedIn := &pb.DeleteDocumentRequest{ + Params: &pb.DeleteDocumentParams{ + DocId: []string{"id1", "id2"}, + IndexSpec: &pb.IndexSpec{Name: proto.String("Doc")}, + }, + } + if !proto.Equal(in, expectedIn) { + return fmt.Errorf("unsupported argument:\ngot %v\nwant %v", in, expectedIn) + } + *out = pb.DeleteDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + {Code: pb.SearchServiceError_OK.Enum()}, + }, + } + return nil + }) + + if err := index.DeleteMulti(c, []string{"id1", "id2"}); err != nil { + t.Fatal(err) + } +} + +func TestDeleteWrongNumberOfResults(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "DeleteDocument", func(in *pb.DeleteDocumentRequest, out *pb.DeleteDocumentResponse) error { + expectedIn := &pb.DeleteDocumentRequest{ + Params: &pb.DeleteDocumentParams{ + DocId: []string{"id1", "id2"}, + IndexSpec: &pb.IndexSpec{Name: proto.String("Doc")}, + }, + } + if !proto.Equal(in, expectedIn) { + return fmt.Errorf("unsupported argument:\ngot %v\nwant %v", in, expectedIn) + } + *out = pb.DeleteDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + }, + } + return nil + }) + + if err := index.DeleteMulti(c, []string{"id1", "id2"}); err == nil { + t.Fatalf("got nil, want error") + } +} + +func TestDeleteMultiError(t *testing.T) { + index, err := Open("Doc") + if err != nil { + t.Fatalf("err from Open: %v", err) + } + + c := aetesting.FakeSingleContext(t, "search", "DeleteDocument", func(in *pb.DeleteDocumentRequest, out *pb.DeleteDocumentResponse) error { + expectedIn := &pb.DeleteDocumentRequest{ + Params: &pb.DeleteDocumentParams{ + DocId: []string{"id1", "id2"}, + IndexSpec: &pb.IndexSpec{Name: proto.String("Doc")}, + }, + } + if !proto.Equal(in, expectedIn) { + return fmt.Errorf("unsupported argument:\ngot %v\nwant %v", in, expectedIn) + } + *out = pb.DeleteDocumentResponse{ + Status: []*pb.RequestStatus{ + {Code: pb.SearchServiceError_OK.Enum()}, + {Code: pb.SearchServiceError_PERMISSION_DENIED.Enum(), ErrorDetail: proto.String("foo")}, + }, + } + return nil + }) + + switch err := index.DeleteMulti(c, []string{"id1", "id2"}); { + case err == nil: + t.Fatalf("got nil, want error") + case err.(appengine.MultiError)[0] != nil: + t.Fatalf("got %v, want nil MultiError[0]", err.(appengine.MultiError)[0]) + case err.(appengine.MultiError)[1] == nil: + t.Fatalf("got nil, want not-nill MultiError[1]") + } +} diff --git a/vendor/google.golang.org/appengine/search/struct.go b/vendor/google.golang.org/appengine/search/struct.go new file mode 100644 index 0000000000..e73d2f2ef5 --- /dev/null +++ b/vendor/google.golang.org/appengine/search/struct.go @@ -0,0 +1,251 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package search + +import ( + "fmt" + "reflect" + "strings" + "sync" +) + +// ErrFieldMismatch is returned when a field is to be loaded into a different +// than the one it was stored from, or when a field is missing or unexported in +// the destination struct. +type ErrFieldMismatch struct { + FieldName string + Reason string +} + +func (e *ErrFieldMismatch) Error() string { + return fmt.Sprintf("search: cannot load field %q: %s", e.FieldName, e.Reason) +} + +// ErrFacetMismatch is returned when a facet is to be loaded into a different +// type than the one it was stored from, or when a field is missing or +// unexported in the destination struct. StructType is the type of the struct +// pointed to by the destination argument passed to Iterator.Next. +type ErrFacetMismatch struct { + StructType reflect.Type + FacetName string + Reason string +} + +func (e *ErrFacetMismatch) Error() string { + return fmt.Sprintf("search: cannot load facet %q into a %q: %s", e.FacetName, e.StructType, e.Reason) +} + +// structCodec defines how to convert a given struct to/from a search document. +type structCodec struct { + // byIndex returns the struct tag for the i'th struct field. + byIndex []structTag + + // fieldByName returns the index of the struct field for the given field name. + fieldByName map[string]int + + // facetByName returns the index of the struct field for the given facet name, + facetByName map[string]int +} + +// structTag holds a structured version of each struct field's parsed tag. +type structTag struct { + name string + facet bool + ignore bool +} + +var ( + codecsMu sync.RWMutex + codecs = map[reflect.Type]*structCodec{} +) + +func loadCodec(t reflect.Type) (*structCodec, error) { + codecsMu.RLock() + codec, ok := codecs[t] + codecsMu.RUnlock() + if ok { + return codec, nil + } + + codecsMu.Lock() + defer codecsMu.Unlock() + if codec, ok := codecs[t]; ok { + return codec, nil + } + + codec = &structCodec{ + fieldByName: make(map[string]int), + facetByName: make(map[string]int), + } + + for i, I := 0, t.NumField(); i < I; i++ { + f := t.Field(i) + name, opts := f.Tag.Get("search"), "" + if i := strings.Index(name, ","); i != -1 { + name, opts = name[:i], name[i+1:] + } + ignore := false + if name == "-" { + ignore = true + } else if name == "" { + name = f.Name + } else if !validFieldName(name) { + return nil, fmt.Errorf("search: struct tag has invalid field name: %q", name) + } + facet := opts == "facet" + codec.byIndex = append(codec.byIndex, structTag{name: name, facet: facet, ignore: ignore}) + if facet { + codec.facetByName[name] = i + } else { + codec.fieldByName[name] = i + } + } + + codecs[t] = codec + return codec, nil +} + +// structFLS adapts a struct to be a FieldLoadSaver. +type structFLS struct { + v reflect.Value + codec *structCodec +} + +func (s structFLS) Load(fields []Field, meta *DocumentMetadata) error { + var err error + for _, field := range fields { + i, ok := s.codec.fieldByName[field.Name] + if !ok { + // Note the error, but keep going. + err = &ErrFieldMismatch{ + FieldName: field.Name, + Reason: "no such struct field", + } + continue + + } + f := s.v.Field(i) + if !f.CanSet() { + // Note the error, but keep going. + err = &ErrFieldMismatch{ + FieldName: field.Name, + Reason: "cannot set struct field", + } + continue + } + v := reflect.ValueOf(field.Value) + if ft, vt := f.Type(), v.Type(); ft != vt { + err = &ErrFieldMismatch{ + FieldName: field.Name, + Reason: fmt.Sprintf("type mismatch: %v for %v data", ft, vt), + } + continue + } + f.Set(v) + } + if meta == nil { + return err + } + for _, facet := range meta.Facets { + i, ok := s.codec.facetByName[facet.Name] + if !ok { + // Note the error, but keep going. + if err == nil { + err = &ErrFacetMismatch{ + StructType: s.v.Type(), + FacetName: facet.Name, + Reason: "no matching field found", + } + } + continue + } + f := s.v.Field(i) + if !f.CanSet() { + // Note the error, but keep going. + if err == nil { + err = &ErrFacetMismatch{ + StructType: s.v.Type(), + FacetName: facet.Name, + Reason: "unable to set unexported field of struct", + } + } + continue + } + v := reflect.ValueOf(facet.Value) + if ft, vt := f.Type(), v.Type(); ft != vt { + if err == nil { + err = &ErrFacetMismatch{ + StructType: s.v.Type(), + FacetName: facet.Name, + Reason: fmt.Sprintf("type mismatch: %v for %d data", ft, vt), + } + continue + } + } + f.Set(v) + } + return err +} + +func (s structFLS) Save() ([]Field, *DocumentMetadata, error) { + fields := make([]Field, 0, len(s.codec.fieldByName)) + var facets []Facet + for i, tag := range s.codec.byIndex { + if tag.ignore { + continue + } + f := s.v.Field(i) + if !f.CanSet() { + continue + } + if tag.facet { + facets = append(facets, Facet{Name: tag.name, Value: f.Interface()}) + } else { + fields = append(fields, Field{Name: tag.name, Value: f.Interface()}) + } + } + return fields, &DocumentMetadata{Facets: facets}, nil +} + +// newStructFLS returns a FieldLoadSaver for the struct pointer p. +func newStructFLS(p interface{}) (FieldLoadSaver, error) { + v := reflect.ValueOf(p) + if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct { + return nil, ErrInvalidDocumentType + } + codec, err := loadCodec(v.Elem().Type()) + if err != nil { + return nil, err + } + return structFLS{v.Elem(), codec}, nil +} + +func loadStructWithMeta(dst interface{}, f []Field, meta *DocumentMetadata) error { + x, err := newStructFLS(dst) + if err != nil { + return err + } + return x.Load(f, meta) +} + +func saveStructWithMeta(src interface{}) ([]Field, *DocumentMetadata, error) { + x, err := newStructFLS(src) + if err != nil { + return nil, nil, err + } + return x.Save() +} + +// LoadStruct loads the fields from f to dst. dst must be a struct pointer. +func LoadStruct(dst interface{}, f []Field) error { + return loadStructWithMeta(dst, f, nil) +} + +// SaveStruct returns the fields from src as a slice of Field. +// src must be a struct pointer. +func SaveStruct(src interface{}) ([]Field, error) { + f, _, err := saveStructWithMeta(src) + return f, err +} diff --git a/vendor/google.golang.org/appengine/search/struct_test.go b/vendor/google.golang.org/appengine/search/struct_test.go new file mode 100644 index 0000000000..4e5b5d1b8f --- /dev/null +++ b/vendor/google.golang.org/appengine/search/struct_test.go @@ -0,0 +1,213 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package search + +import ( + "reflect" + "testing" +) + +func TestLoadingStruct(t *testing.T) { + testCases := []struct { + desc string + fields []Field + meta *DocumentMetadata + want interface{} + wantErr bool + }{ + { + desc: "Basic struct", + fields: []Field{ + {Name: "Name", Value: "Gopher"}, + {Name: "Legs", Value: float64(4)}, + }, + want: &struct { + Name string + Legs float64 + }{"Gopher", 4}, + }, + { + desc: "Struct with tags", + fields: []Field{ + {Name: "Name", Value: "Gopher"}, + {Name: "about", Value: "Likes slide rules."}, + }, + meta: &DocumentMetadata{Facets: []Facet{ + {Name: "Legs", Value: float64(4)}, + {Name: "Fur", Value: Atom("furry")}, + }}, + want: &struct { + Name string + Info string `search:"about"` + Legs float64 `search:",facet"` + Fuzz Atom `search:"Fur,facet"` + }{"Gopher", "Likes slide rules.", 4, Atom("furry")}, + }, + { + desc: "Bad field from tag", + want: &struct { + AlphaBeta string `search:"αβ"` + }{}, + wantErr: true, + }, + { + desc: "Ignore missing field", + fields: []Field{ + {Name: "Meaning", Value: float64(42)}, + }, + want: &struct{}{}, + wantErr: true, + }, + { + desc: "Ignore unsettable field", + fields: []Field{ + {Name: "meaning", Value: float64(42)}, + }, + want: &struct{ meaning float64 }{}, // field not populated. + wantErr: true, + }, + { + desc: "Error on missing facet", + meta: &DocumentMetadata{Facets: []Facet{ + {Name: "Set", Value: Atom("yes")}, + {Name: "Missing", Value: Atom("no")}, + }}, + want: &struct { + Set Atom `search:",facet"` + }{Atom("yes")}, + wantErr: true, + }, + { + desc: "Error on unsettable facet", + meta: &DocumentMetadata{Facets: []Facet{ + {Name: "Set", Value: Atom("yes")}, + {Name: "unset", Value: Atom("no")}, + }}, + want: &struct { + Set Atom `search:",facet"` + }{Atom("yes")}, + wantErr: true, + }, + { + desc: "Error setting ignored field", + fields: []Field{ + {Name: "Set", Value: "yes"}, + {Name: "Ignored", Value: "no"}, + }, + want: &struct { + Set string + Ignored string `search:"-"` + }{Set: "yes"}, + wantErr: true, + }, + { + desc: "Error setting ignored facet", + meta: &DocumentMetadata{Facets: []Facet{ + {Name: "Set", Value: Atom("yes")}, + {Name: "Ignored", Value: Atom("no")}, + }}, + want: &struct { + Set Atom `search:",facet"` + Ignored Atom `search:"-,facet"` + }{Set: Atom("yes")}, + wantErr: true, + }, + } + + for _, tt := range testCases { + // Make a pointer to an empty version of what want points to. + dst := reflect.New(reflect.TypeOf(tt.want).Elem()).Interface() + err := loadStructWithMeta(dst, tt.fields, tt.meta) + if err != nil != tt.wantErr { + t.Errorf("%s: got err %v; want err %t", tt.desc, err, tt.wantErr) + continue + } + if !reflect.DeepEqual(dst, tt.want) { + t.Errorf("%s: doesn't match\ngot: %v\nwant: %v", tt.desc, dst, tt.want) + } + } +} + +func TestSavingStruct(t *testing.T) { + testCases := []struct { + desc string + doc interface{} + wantFields []Field + wantFacets []Facet + }{ + { + desc: "Basic struct", + doc: &struct { + Name string + Legs float64 + }{"Gopher", 4}, + wantFields: []Field{ + {Name: "Name", Value: "Gopher"}, + {Name: "Legs", Value: float64(4)}, + }, + }, + { + desc: "Struct with tags", + doc: &struct { + Name string + Info string `search:"about"` + Legs float64 `search:",facet"` + Fuzz Atom `search:"Fur,facet"` + }{"Gopher", "Likes slide rules.", 4, Atom("furry")}, + wantFields: []Field{ + {Name: "Name", Value: "Gopher"}, + {Name: "about", Value: "Likes slide rules."}, + }, + wantFacets: []Facet{ + {Name: "Legs", Value: float64(4)}, + {Name: "Fur", Value: Atom("furry")}, + }, + }, + { + desc: "Ignore unexported struct fields", + doc: &struct { + Name string + info string + Legs float64 `search:",facet"` + fuzz Atom `search:",facet"` + }{"Gopher", "Likes slide rules.", 4, Atom("furry")}, + wantFields: []Field{ + {Name: "Name", Value: "Gopher"}, + }, + wantFacets: []Facet{ + {Name: "Legs", Value: float64(4)}, + }, + }, + { + desc: "Ignore fields marked -", + doc: &struct { + Name string + Info string `search:"-"` + Legs float64 `search:",facet"` + Fuzz Atom `search:"-,facet"` + }{"Gopher", "Likes slide rules.", 4, Atom("furry")}, + wantFields: []Field{ + {Name: "Name", Value: "Gopher"}, + }, + wantFacets: []Facet{ + {Name: "Legs", Value: float64(4)}, + }, + }, + } + + for _, tt := range testCases { + fields, meta, err := saveStructWithMeta(tt.doc) + if err != nil { + t.Errorf("%s: got err %v; want nil", tt.desc, err) + continue + } + if !reflect.DeepEqual(fields, tt.wantFields) { + t.Errorf("%s: fields don't match\ngot: %v\nwant: %v", tt.desc, fields, tt.wantFields) + } + if facets := meta.Facets; !reflect.DeepEqual(facets, tt.wantFacets) { + t.Errorf("%s: facets don't match\ngot: %v\nwant: %v", tt.desc, facets, tt.wantFacets) + } + } +} diff --git a/vendor/google.golang.org/appengine/socket/doc.go b/vendor/google.golang.org/appengine/socket/doc.go new file mode 100644 index 0000000000..3de46df826 --- /dev/null +++ b/vendor/google.golang.org/appengine/socket/doc.go @@ -0,0 +1,10 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package socket provides outbound network sockets. +// +// This package is only required in the classic App Engine environment. +// Applications running only in App Engine "flexible environment" should +// use the standard library's net package. +package socket diff --git a/vendor/google.golang.org/appengine/socket/socket_classic.go b/vendor/google.golang.org/appengine/socket/socket_classic.go new file mode 100644 index 0000000000..0ad50e2d36 --- /dev/null +++ b/vendor/google.golang.org/appengine/socket/socket_classic.go @@ -0,0 +1,290 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build appengine + +package socket + +import ( + "fmt" + "io" + "net" + "strconv" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + "google.golang.org/appengine/internal" + + pb "google.golang.org/appengine/internal/socket" +) + +// Dial connects to the address addr on the network protocol. +// The address format is host:port, where host may be a hostname or an IP address. +// Known protocols are "tcp" and "udp". +// The returned connection satisfies net.Conn, and is valid while ctx is valid; +// if the connection is to be used after ctx becomes invalid, invoke SetContext +// with the new context. +func Dial(ctx context.Context, protocol, addr string) (*Conn, error) { + return DialTimeout(ctx, protocol, addr, 0) +} + +var ipFamilies = []pb.CreateSocketRequest_SocketFamily{ + pb.CreateSocketRequest_IPv4, + pb.CreateSocketRequest_IPv6, +} + +// DialTimeout is like Dial but takes a timeout. +// The timeout includes name resolution, if required. +func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) { + dialCtx := ctx // Used for dialing and name resolution, but not stored in the *Conn. + if timeout > 0 { + var cancel context.CancelFunc + dialCtx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } + + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + port, err := strconv.Atoi(portStr) + if err != nil { + return nil, fmt.Errorf("socket: bad port %q: %v", portStr, err) + } + + var prot pb.CreateSocketRequest_SocketProtocol + switch protocol { + case "tcp": + prot = pb.CreateSocketRequest_TCP + case "udp": + prot = pb.CreateSocketRequest_UDP + default: + return nil, fmt.Errorf("socket: unknown protocol %q", protocol) + } + + packedAddrs, resolved, err := resolve(dialCtx, ipFamilies, host) + if err != nil { + return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err) + } + if len(packedAddrs) == 0 { + return nil, fmt.Errorf("no addresses for %q", host) + } + + packedAddr := packedAddrs[0] // use first address + fam := pb.CreateSocketRequest_IPv4 + if len(packedAddr) == net.IPv6len { + fam = pb.CreateSocketRequest_IPv6 + } + + req := &pb.CreateSocketRequest{ + Family: fam.Enum(), + Protocol: prot.Enum(), + RemoteIp: &pb.AddressPort{ + Port: proto.Int32(int32(port)), + PackedAddress: packedAddr, + }, + } + if resolved { + req.RemoteIp.HostnameHint = &host + } + res := &pb.CreateSocketReply{} + if err := internal.Call(dialCtx, "remote_socket", "CreateSocket", req, res); err != nil { + return nil, err + } + + return &Conn{ + ctx: ctx, + desc: res.GetSocketDescriptor(), + prot: prot, + local: res.ProxyExternalIp, + remote: req.RemoteIp, + }, nil +} + +// LookupIP returns the given host's IP addresses. +func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) { + packedAddrs, _, err := resolve(ctx, ipFamilies, host) + if err != nil { + return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err) + } + addrs = make([]net.IP, len(packedAddrs)) + for i, pa := range packedAddrs { + addrs[i] = net.IP(pa) + } + return addrs, nil +} + +func resolve(ctx context.Context, fams []pb.CreateSocketRequest_SocketFamily, host string) ([][]byte, bool, error) { + // Check if it's an IP address. + if ip := net.ParseIP(host); ip != nil { + if ip := ip.To4(); ip != nil { + return [][]byte{ip}, false, nil + } + return [][]byte{ip}, false, nil + } + + req := &pb.ResolveRequest{ + Name: &host, + AddressFamilies: fams, + } + res := &pb.ResolveReply{} + if err := internal.Call(ctx, "remote_socket", "Resolve", req, res); err != nil { + // XXX: need to map to pb.ResolveReply_ErrorCode? + return nil, false, err + } + return res.PackedAddress, true, nil +} + +// withDeadline is like context.WithDeadline, except it ignores the zero deadline. +func withDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) { + if deadline.IsZero() { + return parent, func() {} + } + return context.WithDeadline(parent, deadline) +} + +// Conn represents a socket connection. +// It implements net.Conn. +type Conn struct { + ctx context.Context + desc string + offset int64 + + prot pb.CreateSocketRequest_SocketProtocol + local, remote *pb.AddressPort + + readDeadline, writeDeadline time.Time // optional +} + +// SetContext sets the context that is used by this Conn. +// It is usually used only when using a Conn that was created in a different context, +// such as when a connection is created during a warmup request but used while +// servicing a user request. +func (cn *Conn) SetContext(ctx context.Context) { + cn.ctx = ctx +} + +func (cn *Conn) Read(b []byte) (n int, err error) { + const maxRead = 1 << 20 + if len(b) > maxRead { + b = b[:maxRead] + } + + req := &pb.ReceiveRequest{ + SocketDescriptor: &cn.desc, + DataSize: proto.Int32(int32(len(b))), + } + res := &pb.ReceiveReply{} + if !cn.readDeadline.IsZero() { + req.TimeoutSeconds = proto.Float64(cn.readDeadline.Sub(time.Now()).Seconds()) + } + ctx, cancel := withDeadline(cn.ctx, cn.readDeadline) + defer cancel() + if err := internal.Call(ctx, "remote_socket", "Receive", req, res); err != nil { + return 0, err + } + if len(res.Data) == 0 { + return 0, io.EOF + } + if len(res.Data) > len(b) { + return 0, fmt.Errorf("socket: internal error: read too much data: %d > %d", len(res.Data), len(b)) + } + return copy(b, res.Data), nil +} + +func (cn *Conn) Write(b []byte) (n int, err error) { + const lim = 1 << 20 // max per chunk + + for n < len(b) { + chunk := b[n:] + if len(chunk) > lim { + chunk = chunk[:lim] + } + + req := &pb.SendRequest{ + SocketDescriptor: &cn.desc, + Data: chunk, + StreamOffset: &cn.offset, + } + res := &pb.SendReply{} + if !cn.writeDeadline.IsZero() { + req.TimeoutSeconds = proto.Float64(cn.writeDeadline.Sub(time.Now()).Seconds()) + } + ctx, cancel := withDeadline(cn.ctx, cn.writeDeadline) + defer cancel() + if err = internal.Call(ctx, "remote_socket", "Send", req, res); err != nil { + // assume zero bytes were sent in this RPC + break + } + n += int(res.GetDataSent()) + cn.offset += int64(res.GetDataSent()) + } + + return +} + +func (cn *Conn) Close() error { + req := &pb.CloseRequest{ + SocketDescriptor: &cn.desc, + } + res := &pb.CloseReply{} + if err := internal.Call(cn.ctx, "remote_socket", "Close", req, res); err != nil { + return err + } + cn.desc = "CLOSED" + return nil +} + +func addr(prot pb.CreateSocketRequest_SocketProtocol, ap *pb.AddressPort) net.Addr { + if ap == nil { + return nil + } + switch prot { + case pb.CreateSocketRequest_TCP: + return &net.TCPAddr{ + IP: net.IP(ap.PackedAddress), + Port: int(*ap.Port), + } + case pb.CreateSocketRequest_UDP: + return &net.UDPAddr{ + IP: net.IP(ap.PackedAddress), + Port: int(*ap.Port), + } + } + panic("unknown protocol " + prot.String()) +} + +func (cn *Conn) LocalAddr() net.Addr { return addr(cn.prot, cn.local) } +func (cn *Conn) RemoteAddr() net.Addr { return addr(cn.prot, cn.remote) } + +func (cn *Conn) SetDeadline(t time.Time) error { + cn.readDeadline = t + cn.writeDeadline = t + return nil +} + +func (cn *Conn) SetReadDeadline(t time.Time) error { + cn.readDeadline = t + return nil +} + +func (cn *Conn) SetWriteDeadline(t time.Time) error { + cn.writeDeadline = t + return nil +} + +// KeepAlive signals that the connection is still in use. +// It may be called to prevent the socket being closed due to inactivity. +func (cn *Conn) KeepAlive() error { + req := &pb.GetSocketNameRequest{ + SocketDescriptor: &cn.desc, + } + res := &pb.GetSocketNameReply{} + return internal.Call(cn.ctx, "remote_socket", "GetSocketName", req, res) +} + +func init() { + internal.RegisterErrorCodeMap("remote_socket", pb.RemoteSocketServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/socket/socket_vm.go b/vendor/google.golang.org/appengine/socket/socket_vm.go new file mode 100644 index 0000000000..c804169a1c --- /dev/null +++ b/vendor/google.golang.org/appengine/socket/socket_vm.go @@ -0,0 +1,64 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package socket + +import ( + "net" + "time" + + "golang.org/x/net/context" +) + +// Dial connects to the address addr on the network protocol. +// The address format is host:port, where host may be a hostname or an IP address. +// Known protocols are "tcp" and "udp". +// The returned connection satisfies net.Conn, and is valid while ctx is valid; +// if the connection is to be used after ctx becomes invalid, invoke SetContext +// with the new context. +func Dial(ctx context.Context, protocol, addr string) (*Conn, error) { + conn, err := net.Dial(protocol, addr) + if err != nil { + return nil, err + } + return &Conn{conn}, nil +} + +// DialTimeout is like Dial but takes a timeout. +// The timeout includes name resolution, if required. +func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) { + conn, err := net.DialTimeout(protocol, addr, timeout) + if err != nil { + return nil, err + } + return &Conn{conn}, nil +} + +// LookupIP returns the given host's IP addresses. +func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) { + return net.LookupIP(host) +} + +// Conn represents a socket connection. +// It implements net.Conn. +type Conn struct { + net.Conn +} + +// SetContext sets the context that is used by this Conn. +// It is usually used only when using a Conn that was created in a different context, +// such as when a connection is created during a warmup request but used while +// servicing a user request. +func (cn *Conn) SetContext(ctx context.Context) { + // This function is not required in App Engine "flexible environment". +} + +// KeepAlive signals that the connection is still in use. +// It may be called to prevent the socket being closed due to inactivity. +func (cn *Conn) KeepAlive() error { + // This function is not required in App Engine "flexible environment". + return nil +} diff --git a/vendor/google.golang.org/appengine/taskqueue/taskqueue.go b/vendor/google.golang.org/appengine/taskqueue/taskqueue.go new file mode 100644 index 0000000000..965c5ab4c4 --- /dev/null +++ b/vendor/google.golang.org/appengine/taskqueue/taskqueue.go @@ -0,0 +1,541 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package taskqueue provides a client for App Engine's taskqueue service. +Using this service, applications may perform work outside a user's request. + +A Task may be constructed manually; alternatively, since the most common +taskqueue operation is to add a single POST task, NewPOSTTask makes it easy. + + t := taskqueue.NewPOSTTask("/worker", url.Values{ + "key": {key}, + }) + taskqueue.Add(c, t, "") // add t to the default queue +*/ +package taskqueue // import "google.golang.org/appengine/taskqueue" + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + dspb "google.golang.org/appengine/internal/datastore" + pb "google.golang.org/appengine/internal/taskqueue" +) + +var ( + // ErrTaskAlreadyAdded is the error returned by Add and AddMulti when a task has already been added with a particular name. + ErrTaskAlreadyAdded = errors.New("taskqueue: task has already been added") +) + +// RetryOptions let you control whether to retry a task and the backoff intervals between tries. +type RetryOptions struct { + // Number of tries/leases after which the task fails permanently and is deleted. + // If AgeLimit is also set, both limits must be exceeded for the task to fail permanently. + RetryLimit int32 + + // Maximum time allowed since the task's first try before the task fails permanently and is deleted (only for push tasks). + // If RetryLimit is also set, both limits must be exceeded for the task to fail permanently. + AgeLimit time.Duration + + // Minimum time between successive tries (only for push tasks). + MinBackoff time.Duration + + // Maximum time between successive tries (only for push tasks). + MaxBackoff time.Duration + + // Maximum number of times to double the interval between successive tries before the intervals increase linearly (only for push tasks). + MaxDoublings int32 + + // If MaxDoublings is zero, set ApplyZeroMaxDoublings to true to override the default non-zero value. + // Otherwise a zero MaxDoublings is ignored and the default is used. + ApplyZeroMaxDoublings bool +} + +// toRetryParameter converts RetryOptions to pb.TaskQueueRetryParameters. +func (opt *RetryOptions) toRetryParameters() *pb.TaskQueueRetryParameters { + params := &pb.TaskQueueRetryParameters{} + if opt.RetryLimit > 0 { + params.RetryLimit = proto.Int32(opt.RetryLimit) + } + if opt.AgeLimit > 0 { + params.AgeLimitSec = proto.Int64(int64(opt.AgeLimit.Seconds())) + } + if opt.MinBackoff > 0 { + params.MinBackoffSec = proto.Float64(opt.MinBackoff.Seconds()) + } + if opt.MaxBackoff > 0 { + params.MaxBackoffSec = proto.Float64(opt.MaxBackoff.Seconds()) + } + if opt.MaxDoublings > 0 || (opt.MaxDoublings == 0 && opt.ApplyZeroMaxDoublings) { + params.MaxDoublings = proto.Int32(opt.MaxDoublings) + } + return params +} + +// A Task represents a task to be executed. +type Task struct { + // Path is the worker URL for the task. + // If unset, it will default to /_ah/queue/. + Path string + + // Payload is the data for the task. + // This will be delivered as the HTTP request body. + // It is only used when Method is POST, PUT or PULL. + // url.Values' Encode method may be used to generate this for POST requests. + Payload []byte + + // Additional HTTP headers to pass at the task's execution time. + // To schedule the task to be run with an alternate app version + // or backend, set the "Host" header. + Header http.Header + + // Method is the HTTP method for the task ("GET", "POST", etc.), + // or "PULL" if this is task is destined for a pull-based queue. + // If empty, this defaults to "POST". + Method string + + // A name for the task. + // If empty, a name will be chosen. + Name string + + // Delay specifies the duration the task queue service must wait + // before executing the task. + // Either Delay or ETA may be set, but not both. + Delay time.Duration + + // ETA specifies the earliest time a task may be executed (push queues) + // or leased (pull queues). + // Either Delay or ETA may be set, but not both. + ETA time.Time + + // The number of times the task has been dispatched or leased. + RetryCount int32 + + // Tag for the task. Only used when Method is PULL. + Tag string + + // Retry options for this task. May be nil. + RetryOptions *RetryOptions +} + +func (t *Task) method() string { + if t.Method == "" { + return "POST" + } + return t.Method +} + +// NewPOSTTask creates a Task that will POST to a path with the given form data. +func NewPOSTTask(path string, params url.Values) *Task { + h := make(http.Header) + h.Set("Content-Type", "application/x-www-form-urlencoded") + return &Task{ + Path: path, + Payload: []byte(params.Encode()), + Header: h, + Method: "POST", + } +} + +// RequestHeaders are the special HTTP request headers available to push task +// HTTP request handlers. These headers are set internally by App Engine. +// See https://cloud.google.com/appengine/docs/standard/go/taskqueue/push/creating-handlers#reading_request_headers +// for a description of the fields. +type RequestHeaders struct { + QueueName string + TaskName string + TaskRetryCount int64 + TaskExecutionCount int64 + TaskETA time.Time + + TaskPreviousResponse int + TaskRetryReason string + FailFast bool +} + +// ParseRequestHeaders parses the special HTTP request headers available to push +// task request handlers. This function silently ignores values of the wrong +// format. +func ParseRequestHeaders(h http.Header) *RequestHeaders { + ret := &RequestHeaders{ + QueueName: h.Get("X-AppEngine-QueueName"), + TaskName: h.Get("X-AppEngine-TaskName"), + } + + ret.TaskRetryCount, _ = strconv.ParseInt(h.Get("X-AppEngine-TaskRetryCount"), 10, 64) + ret.TaskExecutionCount, _ = strconv.ParseInt(h.Get("X-AppEngine-TaskExecutionCount"), 10, 64) + + etaSecs, _ := strconv.ParseInt(h.Get("X-AppEngine-TaskETA"), 10, 64) + if etaSecs != 0 { + ret.TaskETA = time.Unix(etaSecs, 0) + } + + ret.TaskPreviousResponse, _ = strconv.Atoi(h.Get("X-AppEngine-TaskPreviousResponse")) + ret.TaskRetryReason = h.Get("X-AppEngine-TaskRetryReason") + if h.Get("X-AppEngine-FailFast") != "" { + ret.FailFast = true + } + + return ret +} + +var ( + currentNamespace = http.CanonicalHeaderKey("X-AppEngine-Current-Namespace") + defaultNamespace = http.CanonicalHeaderKey("X-AppEngine-Default-Namespace") +) + +func getDefaultNamespace(ctx context.Context) string { + return internal.IncomingHeaders(ctx).Get(defaultNamespace) +} + +func newAddReq(c context.Context, task *Task, queueName string) (*pb.TaskQueueAddRequest, error) { + if queueName == "" { + queueName = "default" + } + path := task.Path + if path == "" { + path = "/_ah/queue/" + queueName + } + eta := task.ETA + if eta.IsZero() { + eta = time.Now().Add(task.Delay) + } else if task.Delay != 0 { + panic("taskqueue: both Delay and ETA are set") + } + req := &pb.TaskQueueAddRequest{ + QueueName: []byte(queueName), + TaskName: []byte(task.Name), + EtaUsec: proto.Int64(eta.UnixNano() / 1e3), + } + method := task.method() + if method == "PULL" { + // Pull-based task + req.Body = task.Payload + req.Mode = pb.TaskQueueMode_PULL.Enum() + if task.Tag != "" { + req.Tag = []byte(task.Tag) + } + } else { + // HTTP-based task + if v, ok := pb.TaskQueueAddRequest_RequestMethod_value[method]; ok { + req.Method = pb.TaskQueueAddRequest_RequestMethod(v).Enum() + } else { + return nil, fmt.Errorf("taskqueue: bad method %q", method) + } + req.Url = []byte(path) + for k, vs := range task.Header { + for _, v := range vs { + req.Header = append(req.Header, &pb.TaskQueueAddRequest_Header{ + Key: []byte(k), + Value: []byte(v), + }) + } + } + if method == "POST" || method == "PUT" { + req.Body = task.Payload + } + + // Namespace headers. + if _, ok := task.Header[currentNamespace]; !ok { + // Fetch the current namespace of this request. + ns := internal.NamespaceFromContext(c) + req.Header = append(req.Header, &pb.TaskQueueAddRequest_Header{ + Key: []byte(currentNamespace), + Value: []byte(ns), + }) + } + if _, ok := task.Header[defaultNamespace]; !ok { + // Fetch the X-AppEngine-Default-Namespace header of this request. + if ns := getDefaultNamespace(c); ns != "" { + req.Header = append(req.Header, &pb.TaskQueueAddRequest_Header{ + Key: []byte(defaultNamespace), + Value: []byte(ns), + }) + } + } + } + + if task.RetryOptions != nil { + req.RetryParameters = task.RetryOptions.toRetryParameters() + } + + return req, nil +} + +var alreadyAddedErrors = map[pb.TaskQueueServiceError_ErrorCode]bool{ + pb.TaskQueueServiceError_TASK_ALREADY_EXISTS: true, + pb.TaskQueueServiceError_TOMBSTONED_TASK: true, +} + +// Add adds the task to a named queue. +// An empty queue name means that the default queue will be used. +// Add returns an equivalent Task with defaults filled in, including setting +// the task's Name field to the chosen name if the original was empty. +func Add(c context.Context, task *Task, queueName string) (*Task, error) { + req, err := newAddReq(c, task, queueName) + if err != nil { + return nil, err + } + res := &pb.TaskQueueAddResponse{} + if err := internal.Call(c, "taskqueue", "Add", req, res); err != nil { + apiErr, ok := err.(*internal.APIError) + if ok && alreadyAddedErrors[pb.TaskQueueServiceError_ErrorCode(apiErr.Code)] { + return nil, ErrTaskAlreadyAdded + } + return nil, err + } + resultTask := *task + resultTask.Method = task.method() + if task.Name == "" { + resultTask.Name = string(res.ChosenTaskName) + } + return &resultTask, nil +} + +// AddMulti adds multiple tasks to a named queue. +// An empty queue name means that the default queue will be used. +// AddMulti returns a slice of equivalent tasks with defaults filled in, including setting +// each task's Name field to the chosen name if the original was empty. +// If a given task is badly formed or could not be added, an appengine.MultiError is returned. +func AddMulti(c context.Context, tasks []*Task, queueName string) ([]*Task, error) { + req := &pb.TaskQueueBulkAddRequest{ + AddRequest: make([]*pb.TaskQueueAddRequest, len(tasks)), + } + me, any := make(appengine.MultiError, len(tasks)), false + for i, t := range tasks { + req.AddRequest[i], me[i] = newAddReq(c, t, queueName) + any = any || me[i] != nil + } + if any { + return nil, me + } + res := &pb.TaskQueueBulkAddResponse{} + if err := internal.Call(c, "taskqueue", "BulkAdd", req, res); err != nil { + return nil, err + } + if len(res.Taskresult) != len(tasks) { + return nil, errors.New("taskqueue: server error") + } + tasksOut := make([]*Task, len(tasks)) + for i, tr := range res.Taskresult { + tasksOut[i] = new(Task) + *tasksOut[i] = *tasks[i] + tasksOut[i].Method = tasksOut[i].method() + if tasksOut[i].Name == "" { + tasksOut[i].Name = string(tr.ChosenTaskName) + } + if *tr.Result != pb.TaskQueueServiceError_OK { + if alreadyAddedErrors[*tr.Result] { + me[i] = ErrTaskAlreadyAdded + } else { + me[i] = &internal.APIError{ + Service: "taskqueue", + Code: int32(*tr.Result), + } + } + any = true + } + } + if any { + return tasksOut, me + } + return tasksOut, nil +} + +// Delete deletes a task from a named queue. +func Delete(c context.Context, task *Task, queueName string) error { + err := DeleteMulti(c, []*Task{task}, queueName) + if me, ok := err.(appengine.MultiError); ok { + return me[0] + } + return err +} + +// DeleteMulti deletes multiple tasks from a named queue. +// If a given task could not be deleted, an appengine.MultiError is returned. +// Each task is deleted independently; one may fail to delete while the others +// are sucessfully deleted. +func DeleteMulti(c context.Context, tasks []*Task, queueName string) error { + taskNames := make([][]byte, len(tasks)) + for i, t := range tasks { + taskNames[i] = []byte(t.Name) + } + if queueName == "" { + queueName = "default" + } + req := &pb.TaskQueueDeleteRequest{ + QueueName: []byte(queueName), + TaskName: taskNames, + } + res := &pb.TaskQueueDeleteResponse{} + if err := internal.Call(c, "taskqueue", "Delete", req, res); err != nil { + return err + } + if a, b := len(req.TaskName), len(res.Result); a != b { + return fmt.Errorf("taskqueue: internal error: requested deletion of %d tasks, got %d results", a, b) + } + me, any := make(appengine.MultiError, len(res.Result)), false + for i, ec := range res.Result { + if ec != pb.TaskQueueServiceError_OK { + me[i] = &internal.APIError{ + Service: "taskqueue", + Code: int32(ec), + } + any = true + } + } + if any { + return me + } + return nil +} + +func lease(c context.Context, maxTasks int, queueName string, leaseTime int, groupByTag bool, tag []byte) ([]*Task, error) { + if queueName == "" { + queueName = "default" + } + req := &pb.TaskQueueQueryAndOwnTasksRequest{ + QueueName: []byte(queueName), + LeaseSeconds: proto.Float64(float64(leaseTime)), + MaxTasks: proto.Int64(int64(maxTasks)), + GroupByTag: proto.Bool(groupByTag), + Tag: tag, + } + res := &pb.TaskQueueQueryAndOwnTasksResponse{} + if err := internal.Call(c, "taskqueue", "QueryAndOwnTasks", req, res); err != nil { + return nil, err + } + tasks := make([]*Task, len(res.Task)) + for i, t := range res.Task { + tasks[i] = &Task{ + Payload: t.Body, + Name: string(t.TaskName), + Method: "PULL", + ETA: time.Unix(0, *t.EtaUsec*1e3), + RetryCount: *t.RetryCount, + Tag: string(t.Tag), + } + } + return tasks, nil +} + +// Lease leases tasks from a queue. +// leaseTime is in seconds. +// The number of tasks fetched will be at most maxTasks. +func Lease(c context.Context, maxTasks int, queueName string, leaseTime int) ([]*Task, error) { + return lease(c, maxTasks, queueName, leaseTime, false, nil) +} + +// LeaseByTag leases tasks from a queue, grouped by tag. +// If tag is empty, then the returned tasks are grouped by the tag of the task with earliest ETA. +// leaseTime is in seconds. +// The number of tasks fetched will be at most maxTasks. +func LeaseByTag(c context.Context, maxTasks int, queueName string, leaseTime int, tag string) ([]*Task, error) { + return lease(c, maxTasks, queueName, leaseTime, true, []byte(tag)) +} + +// Purge removes all tasks from a queue. +func Purge(c context.Context, queueName string) error { + if queueName == "" { + queueName = "default" + } + req := &pb.TaskQueuePurgeQueueRequest{ + QueueName: []byte(queueName), + } + res := &pb.TaskQueuePurgeQueueResponse{} + return internal.Call(c, "taskqueue", "PurgeQueue", req, res) +} + +// ModifyLease modifies the lease of a task. +// Used to request more processing time, or to abandon processing. +// leaseTime is in seconds and must not be negative. +func ModifyLease(c context.Context, task *Task, queueName string, leaseTime int) error { + if queueName == "" { + queueName = "default" + } + req := &pb.TaskQueueModifyTaskLeaseRequest{ + QueueName: []byte(queueName), + TaskName: []byte(task.Name), + EtaUsec: proto.Int64(task.ETA.UnixNano() / 1e3), // Used to verify ownership. + LeaseSeconds: proto.Float64(float64(leaseTime)), + } + res := &pb.TaskQueueModifyTaskLeaseResponse{} + if err := internal.Call(c, "taskqueue", "ModifyTaskLease", req, res); err != nil { + return err + } + task.ETA = time.Unix(0, *res.UpdatedEtaUsec*1e3) + return nil +} + +// QueueStatistics represents statistics about a single task queue. +type QueueStatistics struct { + Tasks int // may be an approximation + OldestETA time.Time // zero if there are no pending tasks + + Executed1Minute int // tasks executed in the last minute + InFlight int // tasks executing now + EnforcedRate float64 // requests per second +} + +// QueueStats retrieves statistics about queues. +func QueueStats(c context.Context, queueNames []string) ([]QueueStatistics, error) { + req := &pb.TaskQueueFetchQueueStatsRequest{ + QueueName: make([][]byte, len(queueNames)), + } + for i, q := range queueNames { + if q == "" { + q = "default" + } + req.QueueName[i] = []byte(q) + } + res := &pb.TaskQueueFetchQueueStatsResponse{} + if err := internal.Call(c, "taskqueue", "FetchQueueStats", req, res); err != nil { + return nil, err + } + qs := make([]QueueStatistics, len(res.Queuestats)) + for i, qsg := range res.Queuestats { + qs[i] = QueueStatistics{ + Tasks: int(*qsg.NumTasks), + } + if eta := *qsg.OldestEtaUsec; eta > -1 { + qs[i].OldestETA = time.Unix(0, eta*1e3) + } + if si := qsg.ScannerInfo; si != nil { + qs[i].Executed1Minute = int(*si.ExecutedLastMinute) + qs[i].InFlight = int(si.GetRequestsInFlight()) + qs[i].EnforcedRate = si.GetEnforcedRate() + } + } + return qs, nil +} + +func setTransaction(x *pb.TaskQueueAddRequest, t *dspb.Transaction) { + x.Transaction = t +} + +func init() { + internal.RegisterErrorCodeMap("taskqueue", pb.TaskQueueServiceError_ErrorCode_name) + + // Datastore error codes are shifted by DATASTORE_ERROR when presented through taskqueue. + dsCode := int32(pb.TaskQueueServiceError_DATASTORE_ERROR) + int32(dspb.Error_TIMEOUT) + internal.RegisterTimeoutErrorCode("taskqueue", dsCode) + + // Transaction registration. + internal.RegisterTransactionSetter(setTransaction) + internal.RegisterTransactionSetter(func(x *pb.TaskQueueBulkAddRequest, t *dspb.Transaction) { + for _, req := range x.AddRequest { + setTransaction(req, t) + } + }) +} diff --git a/vendor/google.golang.org/appengine/taskqueue/taskqueue_test.go b/vendor/google.golang.org/appengine/taskqueue/taskqueue_test.go new file mode 100644 index 0000000000..d9eec50b70 --- /dev/null +++ b/vendor/google.golang.org/appengine/taskqueue/taskqueue_test.go @@ -0,0 +1,173 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package taskqueue + +import ( + "errors" + "fmt" + "net/http" + "reflect" + "testing" + "time" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/taskqueue" +) + +func TestAddErrors(t *testing.T) { + var tests = []struct { + err, want error + sameErr bool // if true, should return err exactly + }{ + { + err: &internal.APIError{ + Service: "taskqueue", + Code: int32(pb.TaskQueueServiceError_TASK_ALREADY_EXISTS), + }, + want: ErrTaskAlreadyAdded, + }, + { + err: &internal.APIError{ + Service: "taskqueue", + Code: int32(pb.TaskQueueServiceError_TOMBSTONED_TASK), + }, + want: ErrTaskAlreadyAdded, + }, + { + err: &internal.APIError{ + Service: "taskqueue", + Code: int32(pb.TaskQueueServiceError_UNKNOWN_QUEUE), + }, + want: errors.New("not used"), + sameErr: true, + }, + } + for _, tc := range tests { + c := aetesting.FakeSingleContext(t, "taskqueue", "Add", func(req *pb.TaskQueueAddRequest, res *pb.TaskQueueAddResponse) error { + // don't fill in any of the response + return tc.err + }) + task := &Task{Path: "/worker", Method: "PULL"} + _, err := Add(c, task, "a-queue") + want := tc.want + if tc.sameErr { + want = tc.err + } + if err != want { + t.Errorf("Add with tc.err = %v, got %#v, want = %#v", tc.err, err, want) + } + } +} + +func TestAddMulti(t *testing.T) { + c := aetesting.FakeSingleContext(t, "taskqueue", "BulkAdd", func(req *pb.TaskQueueBulkAddRequest, res *pb.TaskQueueBulkAddResponse) error { + res.Taskresult = []*pb.TaskQueueBulkAddResponse_TaskResult{ + { + Result: pb.TaskQueueServiceError_OK.Enum(), + }, + { + Result: pb.TaskQueueServiceError_TASK_ALREADY_EXISTS.Enum(), + }, + { + Result: pb.TaskQueueServiceError_TOMBSTONED_TASK.Enum(), + }, + { + Result: pb.TaskQueueServiceError_INTERNAL_ERROR.Enum(), + }, + } + return nil + }) + tasks := []*Task{ + {Path: "/worker", Method: "PULL"}, + {Path: "/worker", Method: "PULL"}, + {Path: "/worker", Method: "PULL"}, + {Path: "/worker", Method: "PULL"}, + } + r, err := AddMulti(c, tasks, "a-queue") + if len(r) != len(tasks) { + t.Fatalf("AddMulti returned %d tasks, want %d", len(r), len(tasks)) + } + want := appengine.MultiError{ + nil, + ErrTaskAlreadyAdded, + ErrTaskAlreadyAdded, + &internal.APIError{ + Service: "taskqueue", + Code: int32(pb.TaskQueueServiceError_INTERNAL_ERROR), + }, + } + if !reflect.DeepEqual(err, want) { + t.Errorf("AddMulti got %v, wanted %v", err, want) + } +} + +func TestAddWithEmptyPath(t *testing.T) { + c := aetesting.FakeSingleContext(t, "taskqueue", "Add", func(req *pb.TaskQueueAddRequest, res *pb.TaskQueueAddResponse) error { + if got, want := string(req.Url), "/_ah/queue/a-queue"; got != want { + return fmt.Errorf("req.Url = %q; want %q", got, want) + } + return nil + }) + if _, err := Add(c, &Task{}, "a-queue"); err != nil { + t.Fatalf("Add: %v", err) + } +} + +func TestParseRequestHeaders(t *testing.T) { + tests := []struct { + Header http.Header + Want RequestHeaders + }{ + { + Header: map[string][]string{ + "X-Appengine-Queuename": []string{"foo"}, + "X-Appengine-Taskname": []string{"bar"}, + "X-Appengine-Taskretrycount": []string{"4294967297"}, // 2^32 + 1 + "X-Appengine-Taskexecutioncount": []string{"4294967298"}, // 2^32 + 2 + "X-Appengine-Tasketa": []string{"1500000000"}, + "X-Appengine-Taskpreviousresponse": []string{"404"}, + "X-Appengine-Taskretryreason": []string{"baz"}, + "X-Appengine-Failfast": []string{"yes"}, + }, + Want: RequestHeaders{ + QueueName: "foo", + TaskName: "bar", + TaskRetryCount: 4294967297, + TaskExecutionCount: 4294967298, + TaskETA: time.Date(2017, time.July, 14, 2, 40, 0, 0, time.UTC), + TaskPreviousResponse: 404, + TaskRetryReason: "baz", + FailFast: true, + }, + }, + { + Header: map[string][]string{}, + Want: RequestHeaders{ + QueueName: "", + TaskName: "", + TaskRetryCount: 0, + TaskExecutionCount: 0, + TaskETA: time.Time{}, + TaskPreviousResponse: 0, + TaskRetryReason: "", + FailFast: false, + }, + }, + } + + for idx, test := range tests { + got := *ParseRequestHeaders(test.Header) + if got.TaskETA.UnixNano() != test.Want.TaskETA.UnixNano() { + t.Errorf("%d. ParseRequestHeaders got TaskETA %v, wanted %v", idx, got.TaskETA, test.Want.TaskETA) + } + got.TaskETA = time.Time{} + test.Want.TaskETA = time.Time{} + if !reflect.DeepEqual(got, test.Want) { + t.Errorf("%d. ParseRequestHeaders got %v, wanted %v", idx, got, test.Want) + } + } +} diff --git a/vendor/google.golang.org/appengine/timeout.go b/vendor/google.golang.org/appengine/timeout.go new file mode 100644 index 0000000000..05642a992a --- /dev/null +++ b/vendor/google.golang.org/appengine/timeout.go @@ -0,0 +1,20 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package appengine + +import "golang.org/x/net/context" + +// IsTimeoutError reports whether err is a timeout error. +func IsTimeoutError(err error) bool { + if err == context.DeadlineExceeded { + return true + } + if t, ok := err.(interface { + IsTimeout() bool + }); ok { + return t.IsTimeout() + } + return false +} diff --git a/vendor/google.golang.org/appengine/urlfetch/urlfetch.go b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go new file mode 100644 index 0000000000..6ffe1e6d90 --- /dev/null +++ b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go @@ -0,0 +1,210 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package urlfetch provides an http.RoundTripper implementation +// for fetching URLs via App Engine's urlfetch service. +package urlfetch // import "google.golang.org/appengine/urlfetch" + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/urlfetch" +) + +// Transport is an implementation of http.RoundTripper for +// App Engine. Users should generally create an http.Client using +// this transport and use the Client rather than using this transport +// directly. +type Transport struct { + Context context.Context + + // Controls whether the application checks the validity of SSL certificates + // over HTTPS connections. A value of false (the default) instructs the + // application to send a request to the server only if the certificate is + // valid and signed by a trusted certificate authority (CA), and also + // includes a hostname that matches the certificate. A value of true + // instructs the application to perform no certificate validation. + AllowInvalidServerCertificate bool +} + +// Verify statically that *Transport implements http.RoundTripper. +var _ http.RoundTripper = (*Transport)(nil) + +// Client returns an *http.Client using a default urlfetch Transport. This +// client will have the default deadline of 5 seconds, and will check the +// validity of SSL certificates. +// +// Any deadline of the provided context will be used for requests through this client; +// if the client does not have a deadline then a 5 second default is used. +func Client(ctx context.Context) *http.Client { + return &http.Client{ + Transport: &Transport{ + Context: ctx, + }, + } +} + +type bodyReader struct { + content []byte + truncated bool + closed bool +} + +// ErrTruncatedBody is the error returned after the final Read() from a +// response's Body if the body has been truncated by App Engine's proxy. +var ErrTruncatedBody = errors.New("urlfetch: truncated body") + +func statusCodeToText(code int) string { + if t := http.StatusText(code); t != "" { + return t + } + return strconv.Itoa(code) +} + +func (br *bodyReader) Read(p []byte) (n int, err error) { + if br.closed { + if br.truncated { + return 0, ErrTruncatedBody + } + return 0, io.EOF + } + n = copy(p, br.content) + if n > 0 { + br.content = br.content[n:] + return + } + if br.truncated { + br.closed = true + return 0, ErrTruncatedBody + } + return 0, io.EOF +} + +func (br *bodyReader) Close() error { + br.closed = true + br.content = nil + return nil +} + +// A map of the URL Fetch-accepted methods that take a request body. +var methodAcceptsRequestBody = map[string]bool{ + "POST": true, + "PUT": true, + "PATCH": true, +} + +// urlString returns a valid string given a URL. This function is necessary because +// the String method of URL doesn't correctly handle URLs with non-empty Opaque values. +// See http://code.google.com/p/go/issues/detail?id=4860. +func urlString(u *url.URL) string { + if u.Opaque == "" || strings.HasPrefix(u.Opaque, "//") { + return u.String() + } + aux := *u + aux.Opaque = "//" + aux.Host + aux.Opaque + return aux.String() +} + +// RoundTrip issues a single HTTP request and returns its response. Per the +// http.RoundTripper interface, RoundTrip only returns an error if there +// was an unsupported request or the URL Fetch proxy fails. +// Note that HTTP response codes such as 5xx, 403, 404, etc are not +// errors as far as the transport is concerned and will be returned +// with err set to nil. +func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) { + methNum, ok := pb.URLFetchRequest_RequestMethod_value[req.Method] + if !ok { + return nil, fmt.Errorf("urlfetch: unsupported HTTP method %q", req.Method) + } + + method := pb.URLFetchRequest_RequestMethod(methNum) + + freq := &pb.URLFetchRequest{ + Method: &method, + Url: proto.String(urlString(req.URL)), + FollowRedirects: proto.Bool(false), // http.Client's responsibility + MustValidateServerCertificate: proto.Bool(!t.AllowInvalidServerCertificate), + } + if deadline, ok := t.Context.Deadline(); ok { + freq.Deadline = proto.Float64(deadline.Sub(time.Now()).Seconds()) + } + + for k, vals := range req.Header { + for _, val := range vals { + freq.Header = append(freq.Header, &pb.URLFetchRequest_Header{ + Key: proto.String(k), + Value: proto.String(val), + }) + } + } + if methodAcceptsRequestBody[req.Method] && req.Body != nil { + // Avoid a []byte copy if req.Body has a Bytes method. + switch b := req.Body.(type) { + case interface { + Bytes() []byte + }: + freq.Payload = b.Bytes() + default: + freq.Payload, err = ioutil.ReadAll(req.Body) + if err != nil { + return nil, err + } + } + } + + fres := &pb.URLFetchResponse{} + if err := internal.Call(t.Context, "urlfetch", "Fetch", freq, fres); err != nil { + return nil, err + } + + res = &http.Response{} + res.StatusCode = int(*fres.StatusCode) + res.Status = fmt.Sprintf("%d %s", res.StatusCode, statusCodeToText(res.StatusCode)) + res.Header = make(http.Header) + res.Request = req + + // Faked: + res.ProtoMajor = 1 + res.ProtoMinor = 1 + res.Proto = "HTTP/1.1" + res.Close = true + + for _, h := range fres.Header { + hkey := http.CanonicalHeaderKey(*h.Key) + hval := *h.Value + if hkey == "Content-Length" { + // Will get filled in below for all but HEAD requests. + if req.Method == "HEAD" { + res.ContentLength, _ = strconv.ParseInt(hval, 10, 64) + } + continue + } + res.Header.Add(hkey, hval) + } + + if req.Method != "HEAD" { + res.ContentLength = int64(len(fres.Content)) + } + + truncated := fres.GetContentWasTruncated() + res.Body = &bodyReader{content: fres.Content, truncated: truncated} + return +} + +func init() { + internal.RegisterErrorCodeMap("urlfetch", pb.URLFetchServiceError_ErrorCode_name) + internal.RegisterTimeoutErrorCode("urlfetch", int32(pb.URLFetchServiceError_DEADLINE_EXCEEDED)) +} diff --git a/vendor/google.golang.org/appengine/user/oauth.go b/vendor/google.golang.org/appengine/user/oauth.go new file mode 100644 index 0000000000..ffad571824 --- /dev/null +++ b/vendor/google.golang.org/appengine/user/oauth.go @@ -0,0 +1,52 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package user + +import ( + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/user" +) + +// CurrentOAuth returns the user associated with the OAuth consumer making this +// request. If the OAuth consumer did not make a valid OAuth request, or the +// scopes is non-empty and the current user does not have at least one of the +// scopes, this method will return an error. +func CurrentOAuth(c context.Context, scopes ...string) (*User, error) { + req := &pb.GetOAuthUserRequest{} + if len(scopes) != 1 || scopes[0] != "" { + // The signature for this function used to be CurrentOAuth(Context, string). + // Ignore the singular "" scope to preserve existing behavior. + req.Scopes = scopes + } + + res := &pb.GetOAuthUserResponse{} + + err := internal.Call(c, "user", "GetOAuthUser", req, res) + if err != nil { + return nil, err + } + return &User{ + Email: *res.Email, + AuthDomain: *res.AuthDomain, + Admin: res.GetIsAdmin(), + ID: *res.UserId, + ClientID: res.GetClientId(), + }, nil +} + +// OAuthConsumerKey returns the OAuth consumer key provided with the current +// request. This method will return an error if the OAuth request was invalid. +func OAuthConsumerKey(c context.Context) (string, error) { + req := &pb.CheckOAuthSignatureRequest{} + res := &pb.CheckOAuthSignatureResponse{} + + err := internal.Call(c, "user", "CheckOAuthSignature", req, res) + if err != nil { + return "", err + } + return *res.OauthConsumerKey, err +} diff --git a/vendor/google.golang.org/appengine/user/user.go b/vendor/google.golang.org/appengine/user/user.go new file mode 100644 index 0000000000..eb76f59b79 --- /dev/null +++ b/vendor/google.golang.org/appengine/user/user.go @@ -0,0 +1,84 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// Package user provides a client for App Engine's user authentication service. +package user // import "google.golang.org/appengine/user" + +import ( + "strings" + + "github.com/golang/protobuf/proto" + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/user" +) + +// User represents a user of the application. +type User struct { + Email string + AuthDomain string + Admin bool + + // ID is the unique permanent ID of the user. + // It is populated if the Email is associated + // with a Google account, or empty otherwise. + ID string + + // ClientID is the ID of the pre-registered client so its identity can be verified. + // See https://developers.google.com/console/help/#generatingoauth2 for more information. + ClientID string + + FederatedIdentity string + FederatedProvider string +} + +// String returns a displayable name for the user. +func (u *User) String() string { + if u.AuthDomain != "" && strings.HasSuffix(u.Email, "@"+u.AuthDomain) { + return u.Email[:len(u.Email)-len("@"+u.AuthDomain)] + } + if u.FederatedIdentity != "" { + return u.FederatedIdentity + } + return u.Email +} + +// LoginURL returns a URL that, when visited, prompts the user to sign in, +// then redirects the user to the URL specified by dest. +func LoginURL(c context.Context, dest string) (string, error) { + return LoginURLFederated(c, dest, "") +} + +// LoginURLFederated is like LoginURL but accepts a user's OpenID identifier. +func LoginURLFederated(c context.Context, dest, identity string) (string, error) { + req := &pb.CreateLoginURLRequest{ + DestinationUrl: proto.String(dest), + } + if identity != "" { + req.FederatedIdentity = proto.String(identity) + } + res := &pb.CreateLoginURLResponse{} + if err := internal.Call(c, "user", "CreateLoginURL", req, res); err != nil { + return "", err + } + return *res.LoginUrl, nil +} + +// LogoutURL returns a URL that, when visited, signs the user out, +// then redirects the user to the URL specified by dest. +func LogoutURL(c context.Context, dest string) (string, error) { + req := &pb.CreateLogoutURLRequest{ + DestinationUrl: proto.String(dest), + } + res := &pb.CreateLogoutURLResponse{} + if err := internal.Call(c, "user", "CreateLogoutURL", req, res); err != nil { + return "", err + } + return *res.LogoutUrl, nil +} + +func init() { + internal.RegisterErrorCodeMap("user", pb.UserServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/user/user_classic.go b/vendor/google.golang.org/appengine/user/user_classic.go new file mode 100644 index 0000000000..81315094c2 --- /dev/null +++ b/vendor/google.golang.org/appengine/user/user_classic.go @@ -0,0 +1,44 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build appengine + +package user + +import ( + "appengine/user" + + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +func Current(ctx context.Context) *User { + c, err := internal.ClassicContextFromContext(ctx) + if err != nil { + panic(err) + } + u := user.Current(c) + if u == nil { + return nil + } + // Map appengine/user.User to this package's User type. + return &User{ + Email: u.Email, + AuthDomain: u.AuthDomain, + Admin: u.Admin, + ID: u.ID, + FederatedIdentity: u.FederatedIdentity, + FederatedProvider: u.FederatedProvider, + } +} + +func IsAdmin(ctx context.Context) bool { + c, err := internal.ClassicContextFromContext(ctx) + if err != nil { + panic(err) + } + + return user.IsAdmin(c) +} diff --git a/vendor/google.golang.org/appengine/user/user_test.go b/vendor/google.golang.org/appengine/user/user_test.go new file mode 100644 index 0000000000..5fc5957a8c --- /dev/null +++ b/vendor/google.golang.org/appengine/user/user_test.go @@ -0,0 +1,99 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package user + +import ( + "fmt" + "net/http" + "testing" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine/internal" + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/user" +) + +func baseReq() *http.Request { + return &http.Request{ + Header: http.Header{}, + } +} + +type basicUserTest struct { + nickname, email, authDomain, admin string + // expectations + isNil, isAdmin bool + displayName string +} + +var basicUserTests = []basicUserTest{ + {"", "", "", "0", true, false, ""}, + {"ken", "ken@example.com", "example.com", "0", false, false, "ken"}, + {"ken", "ken@example.com", "auth_domain.com", "1", false, true, "ken@example.com"}, +} + +func TestBasicUserAPI(t *testing.T) { + for i, tc := range basicUserTests { + req := baseReq() + req.Header.Set("X-AppEngine-User-Nickname", tc.nickname) + req.Header.Set("X-AppEngine-User-Email", tc.email) + req.Header.Set("X-AppEngine-Auth-Domain", tc.authDomain) + req.Header.Set("X-AppEngine-User-Is-Admin", tc.admin) + + c := internal.ContextForTesting(req) + + if ga := IsAdmin(c); ga != tc.isAdmin { + t.Errorf("test %d: expected IsAdmin(c) = %v, got %v", i, tc.isAdmin, ga) + } + + u := Current(c) + if tc.isNil { + if u != nil { + t.Errorf("test %d: expected u == nil, got %+v", i, u) + } + continue + } + if u == nil { + t.Errorf("test %d: expected u != nil, got nil", i) + continue + } + if u.Email != tc.email { + t.Errorf("test %d: expected u.Email = %q, got %q", i, tc.email, u.Email) + } + if gs := u.String(); gs != tc.displayName { + t.Errorf("test %d: expected u.String() = %q, got %q", i, tc.displayName, gs) + } + if u.Admin != tc.isAdmin { + t.Errorf("test %d: expected u.Admin = %v, got %v", i, tc.isAdmin, u.Admin) + } + } +} + +func TestLoginURL(t *testing.T) { + expectedQuery := &pb.CreateLoginURLRequest{ + DestinationUrl: proto.String("/destination"), + } + const expectedDest = "/redir/dest" + c := aetesting.FakeSingleContext(t, "user", "CreateLoginURL", func(req *pb.CreateLoginURLRequest, res *pb.CreateLoginURLResponse) error { + if !proto.Equal(req, expectedQuery) { + return fmt.Errorf("got %v, want %v", req, expectedQuery) + } + res.LoginUrl = proto.String(expectedDest) + return nil + }) + + url, err := LoginURL(c, "/destination") + if err != nil { + t.Fatalf("LoginURL failed: %v", err) + } + if url != expectedDest { + t.Errorf("got %v, want %v", url, expectedDest) + } +} + +// TODO(dsymonds): Add test for LogoutURL. diff --git a/vendor/google.golang.org/appengine/user/user_vm.go b/vendor/google.golang.org/appengine/user/user_vm.go new file mode 100644 index 0000000000..8dc672e922 --- /dev/null +++ b/vendor/google.golang.org/appengine/user/user_vm.go @@ -0,0 +1,38 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +// +build !appengine + +package user + +import ( + "golang.org/x/net/context" + + "google.golang.org/appengine/internal" +) + +// Current returns the currently logged-in user, +// or nil if the user is not signed in. +func Current(c context.Context) *User { + h := internal.IncomingHeaders(c) + u := &User{ + Email: h.Get("X-AppEngine-User-Email"), + AuthDomain: h.Get("X-AppEngine-Auth-Domain"), + ID: h.Get("X-AppEngine-User-Id"), + Admin: h.Get("X-AppEngine-User-Is-Admin") == "1", + FederatedIdentity: h.Get("X-AppEngine-Federated-Identity"), + FederatedProvider: h.Get("X-AppEngine-Federated-Provider"), + } + if u.Email == "" && u.FederatedIdentity == "" { + return nil + } + return u +} + +// IsAdmin returns true if the current user is signed in and +// is currently registered as an administrator of the application. +func IsAdmin(c context.Context) bool { + h := internal.IncomingHeaders(c) + return h.Get("X-AppEngine-User-Is-Admin") == "1" +} diff --git a/vendor/google.golang.org/appengine/xmpp/xmpp.go b/vendor/google.golang.org/appengine/xmpp/xmpp.go new file mode 100644 index 0000000000..3a561fd53f --- /dev/null +++ b/vendor/google.golang.org/appengine/xmpp/xmpp.go @@ -0,0 +1,253 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +/* +Package xmpp provides the means to send and receive instant messages +to and from users of XMPP-compatible services. + +To send a message, + m := &xmpp.Message{ + To: []string{"kaylee@example.com"}, + Body: `Hi! How's the carrot?`, + } + err := m.Send(c) + +To receive messages, + func init() { + xmpp.Handle(handleChat) + } + + func handleChat(c context.Context, m *xmpp.Message) { + // ... + } +*/ +package xmpp // import "google.golang.org/appengine/xmpp" + +import ( + "errors" + "fmt" + "net/http" + + "golang.org/x/net/context" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal" + pb "google.golang.org/appengine/internal/xmpp" +) + +// Message represents an incoming chat message. +type Message struct { + // Sender is the JID of the sender. + // Optional for outgoing messages. + Sender string + + // To is the intended recipients of the message. + // Incoming messages will have exactly one element. + To []string + + // Body is the body of the message. + Body string + + // Type is the message type, per RFC 3921. + // It defaults to "chat". + Type string + + // RawXML is whether the body contains raw XML. + RawXML bool +} + +// Presence represents an outgoing presence update. +type Presence struct { + // Sender is the JID (optional). + Sender string + + // The intended recipient of the presence update. + To string + + // Type, per RFC 3921 (optional). Defaults to "available". + Type string + + // State of presence (optional). + // Valid values: "away", "chat", "xa", "dnd" (RFC 3921). + State string + + // Free text status message (optional). + Status string +} + +var ( + ErrPresenceUnavailable = errors.New("xmpp: presence unavailable") + ErrInvalidJID = errors.New("xmpp: invalid JID") +) + +// Handle arranges for f to be called for incoming XMPP messages. +// Only messages of type "chat" or "normal" will be handled. +func Handle(f func(c context.Context, m *Message)) { + http.HandleFunc("/_ah/xmpp/message/chat/", func(_ http.ResponseWriter, r *http.Request) { + f(appengine.NewContext(r), &Message{ + Sender: r.FormValue("from"), + To: []string{r.FormValue("to")}, + Body: r.FormValue("body"), + }) + }) +} + +// Send sends a message. +// If any failures occur with specific recipients, the error will be an appengine.MultiError. +func (m *Message) Send(c context.Context) error { + req := &pb.XmppMessageRequest{ + Jid: m.To, + Body: &m.Body, + RawXml: &m.RawXML, + } + if m.Type != "" && m.Type != "chat" { + req.Type = &m.Type + } + if m.Sender != "" { + req.FromJid = &m.Sender + } + res := &pb.XmppMessageResponse{} + if err := internal.Call(c, "xmpp", "SendMessage", req, res); err != nil { + return err + } + + if len(res.Status) != len(req.Jid) { + return fmt.Errorf("xmpp: sent message to %d JIDs, but only got %d statuses back", len(req.Jid), len(res.Status)) + } + me, any := make(appengine.MultiError, len(req.Jid)), false + for i, st := range res.Status { + if st != pb.XmppMessageResponse_NO_ERROR { + me[i] = errors.New(st.String()) + any = true + } + } + if any { + return me + } + return nil +} + +// Invite sends an invitation. If the from address is an empty string +// the default (yourapp@appspot.com/bot) will be used. +func Invite(c context.Context, to, from string) error { + req := &pb.XmppInviteRequest{ + Jid: &to, + } + if from != "" { + req.FromJid = &from + } + res := &pb.XmppInviteResponse{} + return internal.Call(c, "xmpp", "SendInvite", req, res) +} + +// Send sends a presence update. +func (p *Presence) Send(c context.Context) error { + req := &pb.XmppSendPresenceRequest{ + Jid: &p.To, + } + if p.State != "" { + req.Show = &p.State + } + if p.Type != "" { + req.Type = &p.Type + } + if p.Sender != "" { + req.FromJid = &p.Sender + } + if p.Status != "" { + req.Status = &p.Status + } + res := &pb.XmppSendPresenceResponse{} + return internal.Call(c, "xmpp", "SendPresence", req, res) +} + +var presenceMap = map[pb.PresenceResponse_SHOW]string{ + pb.PresenceResponse_NORMAL: "", + pb.PresenceResponse_AWAY: "away", + pb.PresenceResponse_DO_NOT_DISTURB: "dnd", + pb.PresenceResponse_CHAT: "chat", + pb.PresenceResponse_EXTENDED_AWAY: "xa", +} + +// GetPresence retrieves a user's presence. +// If the from address is an empty string the default +// (yourapp@appspot.com/bot) will be used. +// Possible return values are "", "away", "dnd", "chat", "xa". +// ErrPresenceUnavailable is returned if the presence is unavailable. +func GetPresence(c context.Context, to string, from string) (string, error) { + req := &pb.PresenceRequest{ + Jid: &to, + } + if from != "" { + req.FromJid = &from + } + res := &pb.PresenceResponse{} + if err := internal.Call(c, "xmpp", "GetPresence", req, res); err != nil { + return "", err + } + if !*res.IsAvailable || res.Presence == nil { + return "", ErrPresenceUnavailable + } + presence, ok := presenceMap[*res.Presence] + if ok { + return presence, nil + } + return "", fmt.Errorf("xmpp: unknown presence %v", *res.Presence) +} + +// GetPresenceMulti retrieves multiple users' presence. +// If the from address is an empty string the default +// (yourapp@appspot.com/bot) will be used. +// Possible return values are "", "away", "dnd", "chat", "xa". +// If any presence is unavailable, an appengine.MultiError is returned +func GetPresenceMulti(c context.Context, to []string, from string) ([]string, error) { + req := &pb.BulkPresenceRequest{ + Jid: to, + } + if from != "" { + req.FromJid = &from + } + res := &pb.BulkPresenceResponse{} + + if err := internal.Call(c, "xmpp", "BulkGetPresence", req, res); err != nil { + return nil, err + } + + presences := make([]string, 0, len(res.PresenceResponse)) + errs := appengine.MultiError{} + + addResult := func(presence string, err error) { + presences = append(presences, presence) + errs = append(errs, err) + } + + anyErr := false + for _, subres := range res.PresenceResponse { + if !subres.GetValid() { + anyErr = true + addResult("", ErrInvalidJID) + continue + } + if !*subres.IsAvailable || subres.Presence == nil { + anyErr = true + addResult("", ErrPresenceUnavailable) + continue + } + presence, ok := presenceMap[*subres.Presence] + if ok { + addResult(presence, nil) + } else { + anyErr = true + addResult("", fmt.Errorf("xmpp: unknown presence %q", *subres.Presence)) + } + } + if anyErr { + return presences, errs + } + return presences, nil +} + +func init() { + internal.RegisterErrorCodeMap("xmpp", pb.XmppServiceError_ErrorCode_name) +} diff --git a/vendor/google.golang.org/appengine/xmpp/xmpp_test.go b/vendor/google.golang.org/appengine/xmpp/xmpp_test.go new file mode 100644 index 0000000000..c3030d36d9 --- /dev/null +++ b/vendor/google.golang.org/appengine/xmpp/xmpp_test.go @@ -0,0 +1,173 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package xmpp + +import ( + "fmt" + "reflect" + "testing" + + "github.com/golang/protobuf/proto" + + "google.golang.org/appengine" + "google.golang.org/appengine/internal/aetesting" + pb "google.golang.org/appengine/internal/xmpp" +) + +func newPresenceResponse(isAvailable bool, presence pb.PresenceResponse_SHOW, valid bool) *pb.PresenceResponse { + return &pb.PresenceResponse{ + IsAvailable: proto.Bool(isAvailable), + Presence: presence.Enum(), + Valid: proto.Bool(valid), + } +} + +func setPresenceResponse(m *pb.PresenceResponse, isAvailable bool, presence pb.PresenceResponse_SHOW, valid bool) { + m.IsAvailable = &isAvailable + m.Presence = presence.Enum() + m.Valid = &valid +} + +func TestGetPresence(t *testing.T) { + c := aetesting.FakeSingleContext(t, "xmpp", "GetPresence", func(in *pb.PresenceRequest, out *pb.PresenceResponse) error { + if jid := in.GetJid(); jid != "user@example.com" { + return fmt.Errorf("bad jid %q", jid) + } + setPresenceResponse(out, true, pb.PresenceResponse_CHAT, true) + return nil + }) + + presence, err := GetPresence(c, "user@example.com", "") + if err != nil { + t.Fatalf("GetPresence: %v", err) + } + + if presence != "chat" { + t.Errorf("GetPresence: got %#v, want %#v", presence, pb.PresenceResponse_CHAT) + } +} + +func TestGetPresenceMultiSingleJID(t *testing.T) { + c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { + if !reflect.DeepEqual(in.Jid, []string{"user@example.com"}) { + return fmt.Errorf("bad request jids %#v", in.Jid) + } + out.PresenceResponse = []*pb.PresenceResponse{ + newPresenceResponse(true, pb.PresenceResponse_NORMAL, true), + } + return nil + }) + + presence, err := GetPresenceMulti(c, []string{"user@example.com"}, "") + if err != nil { + t.Fatalf("GetPresenceMulti: %v", err) + } + if !reflect.DeepEqual(presence, []string{""}) { + t.Errorf("GetPresenceMulti: got %s, want %s", presence, []string{""}) + } +} + +func TestGetPresenceMultiJID(t *testing.T) { + c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { + if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) { + return fmt.Errorf("bad request jids %#v", in.Jid) + } + out.PresenceResponse = []*pb.PresenceResponse{ + newPresenceResponse(true, pb.PresenceResponse_NORMAL, true), + newPresenceResponse(true, pb.PresenceResponse_AWAY, true), + } + return nil + }) + + jids := []string{"user@example.com", "user2@example.com"} + presence, err := GetPresenceMulti(c, jids, "") + if err != nil { + t.Fatalf("GetPresenceMulti: %v", err) + } + want := []string{"", "away"} + if !reflect.DeepEqual(presence, want) { + t.Errorf("GetPresenceMulti: got %v, want %v", presence, want) + } +} + +func TestGetPresenceMultiFromJID(t *testing.T) { + c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { + if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) { + return fmt.Errorf("bad request jids %#v", in.Jid) + } + if jid := in.GetFromJid(); jid != "bot@appspot.com" { + return fmt.Errorf("bad from jid %q", jid) + } + out.PresenceResponse = []*pb.PresenceResponse{ + newPresenceResponse(true, pb.PresenceResponse_NORMAL, true), + newPresenceResponse(true, pb.PresenceResponse_CHAT, true), + } + return nil + }) + + jids := []string{"user@example.com", "user2@example.com"} + presence, err := GetPresenceMulti(c, jids, "bot@appspot.com") + if err != nil { + t.Fatalf("GetPresenceMulti: %v", err) + } + want := []string{"", "chat"} + if !reflect.DeepEqual(presence, want) { + t.Errorf("GetPresenceMulti: got %v, want %v", presence, want) + } +} + +func TestGetPresenceMultiInvalid(t *testing.T) { + c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { + if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) { + return fmt.Errorf("bad request jids %#v", in.Jid) + } + out.PresenceResponse = []*pb.PresenceResponse{ + newPresenceResponse(true, pb.PresenceResponse_EXTENDED_AWAY, true), + newPresenceResponse(true, pb.PresenceResponse_CHAT, false), + } + return nil + }) + + jids := []string{"user@example.com", "user2@example.com"} + presence, err := GetPresenceMulti(c, jids, "") + + wantErr := appengine.MultiError{nil, ErrInvalidJID} + if !reflect.DeepEqual(err, wantErr) { + t.Fatalf("GetPresenceMulti: got %#v, want %#v", err, wantErr) + } + + want := []string{"xa", ""} + if !reflect.DeepEqual(presence, want) { + t.Errorf("GetPresenceMulti: got %#v, want %#v", presence, want) + } +} + +func TestGetPresenceMultiUnavailable(t *testing.T) { + c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { + if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) { + return fmt.Errorf("bad request jids %#v", in.Jid) + } + out.PresenceResponse = []*pb.PresenceResponse{ + newPresenceResponse(false, pb.PresenceResponse_AWAY, true), + newPresenceResponse(false, pb.PresenceResponse_DO_NOT_DISTURB, true), + } + return nil + }) + + jids := []string{"user@example.com", "user2@example.com"} + presence, err := GetPresenceMulti(c, jids, "") + + wantErr := appengine.MultiError{ + ErrPresenceUnavailable, + ErrPresenceUnavailable, + } + if !reflect.DeepEqual(err, wantErr) { + t.Fatalf("GetPresenceMulti: got %#v, want %#v", err, wantErr) + } + want := []string{"", ""} + if !reflect.DeepEqual(presence, want) { + t.Errorf("GetPresenceMulti: got %#v, want %#v", presence, want) + } +}